From ea2ea62fd1577cc67dc61d6b1b40e5f7427ed704 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Wed, 4 Jun 2025 13:40:50 +0200 Subject: [PATCH 001/226] Note that using `enumerate()` will swap the arguments The autofix now: - includes the swapping of index and element in the closure in which the content will be consumed; - notes, with applicability `MaybeIncorrect` (because it will be), that the element and the index will be swapped. --- .../src/methods/range_zip_with_len.rs | 96 +++++++++++++++++-- clippy_utils/src/sym.rs | 4 + tests/ui/range.fixed | 16 +++- tests/ui/range.rs | 16 +++- tests/ui/range.stderr | 29 +++++- tests/ui/range_unfixable.rs | 14 +++ tests/ui/range_unfixable.stderr | 12 +++ 7 files changed, 172 insertions(+), 15 deletions(-) create mode 100644 tests/ui/range_unfixable.rs create mode 100644 tests/ui/range_unfixable.stderr diff --git a/clippy_lints/src/methods/range_zip_with_len.rs b/clippy_lints/src/methods/range_zip_with_len.rs index 3a5e32172086..e13df18333e4 100644 --- a/clippy_lints/src/methods/range_zip_with_len.rs +++ b/clippy_lints/src/methods/range_zip_with_len.rs @@ -1,10 +1,9 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet; -use clippy_utils::{SpanlessEq, higher, is_integer_const, is_trait_method}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::{SpanRangeExt as _, snippet_with_applicability}; +use clippy_utils::{SpanlessEq, get_parent_expr, higher, is_integer_const, is_trait_method, sym}; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, QPath}; +use rustc_hir::{Expr, ExprKind, Node, Pat, PatKind, QPath}; use rustc_lint::LateContext; -use rustc_span::sym; use super::RANGE_ZIP_WITH_LEN; @@ -21,14 +20,93 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &' && let ExprKind::Path(QPath::Resolved(_, len_path)) = len_recv.kind && SpanlessEq::new(cx).eq_path_segments(iter_path.segments, len_path.segments) { - span_lint_and_sugg( + span_lint_and_then( cx, RANGE_ZIP_WITH_LEN, expr.span, "using `.zip()` with a range and `.len()`", - "try", - format!("{}.iter().enumerate()", snippet(cx, recv.span, "_")), - Applicability::MachineApplicable, + |diag| { + // If the iterator content is consumed by a pattern with exactly two elements, swap + // the order of those elements. Otherwise, the suggestion will be marked as + // `Applicability::MaybeIncorrect` (because it will be), and a note will be added + // to the diagnostic to underline the swapping of the index and the content. + let pat = methods_pattern(cx, expr).or_else(|| for_loop_pattern(cx, expr)); + let invert_bindings = if let Some(pat) = pat + && pat.span.eq_ctxt(expr.span) + && let PatKind::Tuple([first, second], _) = pat.kind + { + Some((first.span, second.span)) + } else { + None + }; + let mut app = Applicability::MachineApplicable; + let mut suggestions = vec![( + expr.span, + format!( + "{}.iter().enumerate()", + snippet_with_applicability(cx, recv.span, "_", &mut app) + ), + )]; + if let Some((left, right)) = invert_bindings + && let Some(snip_left) = left.get_source_text(cx) + && let Some(snip_right) = right.get_source_text(cx) + { + suggestions.extend([(left, snip_right.to_string()), (right, snip_left.to_string())]); + } else { + app = Applicability::MaybeIncorrect; + } + diag.multipart_suggestion("use", suggestions, app); + if app != Applicability::MachineApplicable { + diag.note("the order of the element and the index will be swapped"); + } + }, ); } } + +/// If `expr` is the argument of a `for` loop, return the loop pattern. +fn for_loop_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Pat<'tcx>> { + cx.tcx.hir_parent_iter(expr.hir_id).find_map(|(_, node)| { + if let Node::Expr(ancestor_expr) = node + && let Some(for_loop) = higher::ForLoop::hir(ancestor_expr) + && for_loop.arg.hir_id == expr.hir_id + { + Some(for_loop.pat) + } else { + None + } + }) +} + +/// If `expr` is the receiver of an `Iterator` method which consumes the iterator elements and feed +/// them to a closure, return the pattern of the closure. +fn methods_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Pat<'tcx>> { + if let Some(parent_expr) = get_parent_expr(cx, expr) + && is_trait_method(cx, expr, sym::Iterator) + && let ExprKind::MethodCall(method, recv, [arg], _) = parent_expr.kind + && recv.hir_id == expr.hir_id + && matches!( + method.ident.name, + sym::all + | sym::any + | sym::filter_map + | sym::find_map + | sym::flat_map + | sym::for_each + | sym::is_partitioned + | sym::is_sorted_by_key + | sym::map + | sym::map_while + | sym::position + | sym::rposition + | sym::try_for_each + ) + && let ExprKind::Closure(closure) = arg.kind + && let body = cx.tcx.hir_body(closure.body) + && let [param] = body.params + { + Some(param.pat) + } else { + None + } +} diff --git a/clippy_utils/src/sym.rs b/clippy_utils/src/sym.rs index a544954931ba..1fe9e62fae30 100644 --- a/clippy_utils/src/sym.rs +++ b/clippy_utils/src/sym.rs @@ -191,8 +191,10 @@ generate! { is_none, is_none_or, is_ok, + is_partitioned, is_some, is_some_and, + is_sorted_by_key, isqrt, itertools, join, @@ -210,6 +212,7 @@ generate! { map_continue, map_or, map_or_else, + map_while, match_indices, matches, max, @@ -349,6 +352,7 @@ generate! { trim_start, trim_start_matches, truncate, + try_for_each, unreachable_pub, unsafe_removed_from_name, unused, diff --git a/tests/ui/range.fixed b/tests/ui/range.fixed index 0e951d88091b..c8e654600045 100644 --- a/tests/ui/range.fixed +++ b/tests/ui/range.fixed @@ -1,11 +1,23 @@ #![allow(clippy::useless_vec)] #[warn(clippy::range_zip_with_len)] fn main() { - let v1 = vec![1, 2, 3]; - let v2 = vec![4, 5]; + let v1: Vec = vec![1, 2, 3]; + let v2: Vec = vec![4, 5]; let _x = v1.iter().enumerate(); //~^ range_zip_with_len + //~v range_zip_with_len + for (i, e) in v1.iter().enumerate() { + let _: &u64 = e; + let _: usize = i; + } + + //~v range_zip_with_len + v1.iter().enumerate().for_each(|(i, e)| { + let _: &u64 = e; + let _: usize = i; + }); + let _y = v1.iter().zip(0..v2.len()); // No error } diff --git a/tests/ui/range.rs b/tests/ui/range.rs index 534380164743..352d517eabdd 100644 --- a/tests/ui/range.rs +++ b/tests/ui/range.rs @@ -1,11 +1,23 @@ #![allow(clippy::useless_vec)] #[warn(clippy::range_zip_with_len)] fn main() { - let v1 = vec![1, 2, 3]; - let v2 = vec![4, 5]; + let v1: Vec = vec![1, 2, 3]; + let v2: Vec = vec![4, 5]; let _x = v1.iter().zip(0..v1.len()); //~^ range_zip_with_len + //~v range_zip_with_len + for (e, i) in v1.iter().zip(0..v1.len()) { + let _: &u64 = e; + let _: usize = i; + } + + //~v range_zip_with_len + v1.iter().zip(0..v1.len()).for_each(|(e, i)| { + let _: &u64 = e; + let _: usize = i; + }); + let _y = v1.iter().zip(0..v2.len()); // No error } diff --git a/tests/ui/range.stderr b/tests/ui/range.stderr index 798ce1842d8b..b0a852a69fc5 100644 --- a/tests/ui/range.stderr +++ b/tests/ui/range.stderr @@ -2,10 +2,35 @@ error: using `.zip()` with a range and `.len()` --> tests/ui/range.rs:6:14 | LL | let _x = v1.iter().zip(0..v1.len()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `v1.iter().enumerate()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `v1.iter().enumerate()` | + = note: the order of the element and the index will be swapped = note: `-D clippy::range-zip-with-len` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::range_zip_with_len)]` -error: aborting due to 1 previous error +error: using `.zip()` with a range and `.len()` + --> tests/ui/range.rs:10:19 + | +LL | for (e, i) in v1.iter().zip(0..v1.len()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use + | +LL - for (e, i) in v1.iter().zip(0..v1.len()) { +LL + for (i, e) in v1.iter().enumerate() { + | + +error: using `.zip()` with a range and `.len()` + --> tests/ui/range.rs:16:5 + | +LL | v1.iter().zip(0..v1.len()).for_each(|(e, i)| { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use + | +LL - v1.iter().zip(0..v1.len()).for_each(|(e, i)| { +LL + v1.iter().enumerate().for_each(|(i, e)| { + | + +error: aborting due to 3 previous errors diff --git a/tests/ui/range_unfixable.rs b/tests/ui/range_unfixable.rs new file mode 100644 index 000000000000..259be23fa1e5 --- /dev/null +++ b/tests/ui/range_unfixable.rs @@ -0,0 +1,14 @@ +//@no-rustfix +#![allow(clippy::useless_vec)] +#[warn(clippy::range_zip_with_len)] +fn main() { + let v1: Vec = vec![1, 2, 3]; + let v2: Vec = vec![4, 5]; + + // Do not autofix, `filter()` would not consume the iterator. + //~v range_zip_with_len + v1.iter().zip(0..v1.len()).filter(|(_, i)| *i < 2).for_each(|(e, i)| { + let _: &u64 = e; + let _: usize = i; + }); +} diff --git a/tests/ui/range_unfixable.stderr b/tests/ui/range_unfixable.stderr new file mode 100644 index 000000000000..fb03ea2c05f6 --- /dev/null +++ b/tests/ui/range_unfixable.stderr @@ -0,0 +1,12 @@ +error: using `.zip()` with a range and `.len()` + --> tests/ui/range_unfixable.rs:10:5 + | +LL | v1.iter().zip(0..v1.len()).filter(|(_, i)| *i < 2).for_each(|(e, i)| { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `v1.iter().enumerate()` + | + = note: the order of the element and the index will be swapped + = note: `-D clippy::range-zip-with-len` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::range_zip_with_len)]` + +error: aborting due to 1 previous error + From d43c55be3487277783f9a5445ceeaa01fa558b11 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Tue, 26 Aug 2025 15:54:59 +0200 Subject: [PATCH 002/226] Bless clippy tests. --- tests/ui/author/macro_in_closure.stdout | 32 +++++++++++++++---------- tests/ui/author/macro_in_loop.stdout | 28 ++++++++++++++-------- 2 files changed, 38 insertions(+), 22 deletions(-) diff --git a/tests/ui/author/macro_in_closure.stdout b/tests/ui/author/macro_in_closure.stdout index 49595e2fec25..786c61e0c018 100644 --- a/tests/ui/author/macro_in_closure.stdout +++ b/tests/ui/author/macro_in_closure.stdout @@ -10,34 +10,42 @@ if let StmtKind::Let(local) = stmt.kind && paths::STD_IO_STDIO__PRINT.matches_path(cx, func) // Add the path to `clippy_utils::paths` if needed && args.len() == 1 && let ExprKind::Block(block1, None) = args[0].kind - && block1.stmts.len() == 1 + && block1.stmts.len() == 2 && let StmtKind::Let(local1) = block1.stmts[0].kind && let Some(init1) = local1.init - && let ExprKind::Array(elements) = init1.kind + && let ExprKind::Tup(elements) = init1.kind && elements.len() == 1 - && let ExprKind::Call(func1, args1) = elements[0].kind - && paths::CORE_FMT_RT_ARGUMENT_NEW_DISPLAY.matches_path(cx, func1) // Add the path to `clippy_utils::paths` if needed - && args1.len() == 1 - && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = args1[0].kind + && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = elements[0].kind && let PatKind::Binding(BindingMode::NONE, _, name, None) = local1.pat.kind && name.as_str() == "args" + && let StmtKind::Let(local2) = block1.stmts[1].kind + && let Some(init2) = local2.init + && let ExprKind::Array(elements1) = init2.kind + && elements1.len() == 1 + && let ExprKind::Call(func1, args1) = elements1[0].kind + && paths::CORE_FMT_RT_ARGUMENT_NEW_DISPLAY.matches_path(cx, func1) // Add the path to `clippy_utils::paths` if needed + && args1.len() == 1 + && let ExprKind::Field(object, field_name) = args1[0].kind + && field_name.as_str() == "0" + && let PatKind::Binding(BindingMode::NONE, _, name1, None) = local2.pat.kind + && name1.as_str() == "args" && let Some(trailing_expr) = block1.expr && let ExprKind::Call(func2, args2) = trailing_expr.kind && paths::CORE_FMT_RT_NEW_V1.matches_path(cx, func2) // Add the path to `clippy_utils::paths` if needed && args2.len() == 2 && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner1) = args2[0].kind - && let ExprKind::Array(elements1) = inner1.kind - && elements1.len() == 2 - && let ExprKind::Lit(ref lit) = elements1[0].kind + && let ExprKind::Array(elements2) = inner1.kind + && elements2.len() == 2 + && let ExprKind::Lit(ref lit) = elements2[0].kind && let LitKind::Str(s, _) = lit.node && s.as_str() == "" - && let ExprKind::Lit(ref lit1) = elements1[1].kind + && let ExprKind::Lit(ref lit1) = elements2[1].kind && let LitKind::Str(s1, _) = lit1.node && s1.as_str() == "\n" && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner2) = args2[1].kind && block.expr.is_none() - && let PatKind::Binding(BindingMode::NONE, _, name1, None) = local.pat.kind - && name1.as_str() == "print_text" + && let PatKind::Binding(BindingMode::NONE, _, name2, None) = local.pat.kind + && name2.as_str() == "print_text" { // report your lint here } diff --git a/tests/ui/author/macro_in_loop.stdout b/tests/ui/author/macro_in_loop.stdout index 4fc7b49464db..80717900b525 100644 --- a/tests/ui/author/macro_in_loop.stdout +++ b/tests/ui/author/macro_in_loop.stdout @@ -20,28 +20,36 @@ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::Fo && paths::STD_IO_STDIO__PRINT.matches_path(cx, func) // Add the path to `clippy_utils::paths` if needed && args.len() == 1 && let ExprKind::Block(block2, None) = args[0].kind - && block2.stmts.len() == 1 + && block2.stmts.len() == 2 && let StmtKind::Let(local) = block2.stmts[0].kind && let Some(init) = local.init - && let ExprKind::Array(elements) = init.kind + && let ExprKind::Tup(elements) = init.kind && elements.len() == 1 - && let ExprKind::Call(func1, args1) = elements[0].kind - && paths::CORE_FMT_RT_ARGUMENT_NEW_DISPLAY.matches_path(cx, func1) // Add the path to `clippy_utils::paths` if needed - && args1.len() == 1 - && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = args1[0].kind + && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = elements[0].kind && let PatKind::Binding(BindingMode::NONE, _, name1, None) = local.pat.kind && name1.as_str() == "args" + && let StmtKind::Let(local1) = block2.stmts[1].kind + && let Some(init1) = local1.init + && let ExprKind::Array(elements1) = init1.kind + && elements1.len() == 1 + && let ExprKind::Call(func1, args1) = elements1[0].kind + && paths::CORE_FMT_RT_ARGUMENT_NEW_DISPLAY.matches_path(cx, func1) // Add the path to `clippy_utils::paths` if needed + && args1.len() == 1 + && let ExprKind::Field(object, field_name) = args1[0].kind + && field_name.as_str() == "0" + && let PatKind::Binding(BindingMode::NONE, _, name2, None) = local1.pat.kind + && name2.as_str() == "args" && let Some(trailing_expr) = block2.expr && let ExprKind::Call(func2, args2) = trailing_expr.kind && paths::CORE_FMT_RT_NEW_V1.matches_path(cx, func2) // Add the path to `clippy_utils::paths` if needed && args2.len() == 2 && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner1) = args2[0].kind - && let ExprKind::Array(elements1) = inner1.kind - && elements1.len() == 2 - && let ExprKind::Lit(ref lit2) = elements1[0].kind + && let ExprKind::Array(elements2) = inner1.kind + && elements2.len() == 2 + && let ExprKind::Lit(ref lit2) = elements2[0].kind && let LitKind::Str(s, _) = lit2.node && s.as_str() == "" - && let ExprKind::Lit(ref lit3) = elements1[1].kind + && let ExprKind::Lit(ref lit3) = elements2[1].kind && let LitKind::Str(s1, _) = lit3.node && s1.as_str() == "\n" && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner2) = args2[1].kind From ee9986b4a28313429cc50f91ffd30d7491bd59af Mon Sep 17 00:00:00 2001 From: dianne Date: Wed, 27 Aug 2025 12:06:52 -0700 Subject: [PATCH 003/226] document lifetime extension for `format_args!`'s arguments --- library/core/src/macros/mod.rs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index ccf41dfb01dd..c19ce4f90660 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -951,8 +951,9 @@ pub(crate) mod builtin { /// format string in `format_args!`. /// /// ```rust - /// let debug = format!("{:?}", format_args!("{} foo {:?}", 1, 2)); - /// let display = format!("{}", format_args!("{} foo {:?}", 1, 2)); + /// let args = format_args!("{} foo {:?}", 1, 2); + /// let debug = format!("{args:?}"); + /// let display = format!("{args}"); /// assert_eq!("1 foo 2", display); /// assert_eq!(display, debug); /// ``` @@ -976,13 +977,17 @@ pub(crate) mod builtin { /// assert_eq!(s, format!("hello {}", "world")); /// ``` /// - /// # Lifetime limitation + /// # Argument lifetimes /// /// Except when no formatting arguments are used, - /// the produced `fmt::Arguments` value borrows temporary values, - /// which means it can only be used within the same expression - /// and cannot be stored for later use. - /// This is a known limitation, see [#92698](https://github.com/rust-lang/rust/issues/92698). + /// the produced `fmt::Arguments` value borrows temporary values. + /// To allow it to be stored for later use, the arguments' lifetimes, as well as those of + /// temporaries they borrow, may be [extended] when `format_args!` appears in the initializer + /// expression of a `let` statement. The syntactic rules used to determine when temporaries' + /// lifetimes are extended are documented in the [Reference]. + /// + /// [extended]: ../reference/destructors.html#temporary-lifetime-extension + /// [Reference]: ../reference/destructors.html#extending-based-on-expressions #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "format_args_macro"] #[allow_internal_unsafe] From de47decebbbd8645708a47a2a7c11c58a268ac87 Mon Sep 17 00:00:00 2001 From: Ali Nazzal Date: Sun, 31 Aug 2025 09:51:47 +0300 Subject: [PATCH 004/226] docs(rustdoc): document that JS/TS checks run under tidy (eslint, es-check, tsc) --- src/doc/rustc-dev-guide/src/rustdoc.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/doc/rustc-dev-guide/src/rustdoc.md b/src/doc/rustc-dev-guide/src/rustdoc.md index 9290fcd3b41c..4181c2f7f170 100644 --- a/src/doc/rustc-dev-guide/src/rustdoc.md +++ b/src/doc/rustc-dev-guide/src/rustdoc.md @@ -60,6 +60,18 @@ does is call the `main()` that's in this crate's `lib.rs`, though.) * Use `./x test tests/rustdoc*` to run the tests using a stage1 rustdoc. * See [Rustdoc internals] for more information about tests. +* Use `./x.py test tidy --extra-checks=js` to run rustdoc’s JavaScript checks (`eslint`, `es-check`, and `tsc`). +> **Note:** `./x.py test tidy` already runs these checks automatically when JS/TS sources changed; `--extra-checks=js` forces them explicitly. + +### JavaScript CI checks + +Rustdoc’s JavaScript and TypeScript are checked during CI by `eslint`, `es-check`, and `tsc` (not by compiletest). These run as part of the `tidy` job. + +```bash +./x.py test tidy --extra-checks=js +``` + +The `--extra-checks=js` flag enables the frontend linting that runs in CI. [`bootstrap.toml`]: ./building/how-to-build-and-run.md From 9adc741bc4216d9f7c99ba25946372b7124db8b1 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Wed, 3 Sep 2025 21:46:06 +0200 Subject: [PATCH 005/226] rename primary git branch --- src/doc/rustc-dev-guide/.github/workflows/ci.yml | 2 +- src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml | 2 +- src/doc/rustc-dev-guide/book.toml | 2 +- src/doc/rustc-dev-guide/ci/linkcheck.sh | 2 +- src/doc/rustc-dev-guide/src/rustc-driver/intro.md | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/doc/rustc-dev-guide/.github/workflows/ci.yml b/src/doc/rustc-dev-guide/.github/workflows/ci.yml index 2dd695b7a473..79d03080dce3 100644 --- a/src/doc/rustc-dev-guide/.github/workflows/ci.yml +++ b/src/doc/rustc-dev-guide/.github/workflows/ci.yml @@ -3,7 +3,7 @@ name: CI on: push: branches: - - master + - main pull_request: schedule: # Run multiple times a day as the successfull cached links are not checked every time. diff --git a/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml b/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml index 5ff3118960da..fd21b149db8f 100644 --- a/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml +++ b/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml @@ -14,7 +14,7 @@ jobs: github-app-id: ${{ vars.APP_CLIENT_ID }} zulip-stream-id: 196385 zulip-bot-email: "rustc-dev-guide-gha-notif-bot@rust-lang.zulipchat.com" - pr-base-branch: master + pr-base-branch: main branch-name: rustc-pull secrets: zulip-api-token: ${{ secrets.ZULIP_API_TOKEN }} diff --git a/src/doc/rustc-dev-guide/book.toml b/src/doc/rustc-dev-guide/book.toml index daf237ed9081..efb13101c8de 100644 --- a/src/doc/rustc-dev-guide/book.toml +++ b/src/doc/rustc-dev-guide/book.toml @@ -11,7 +11,7 @@ command = "mdbook-mermaid" [output.html] git-repository-url = "https://github.com/rust-lang/rustc-dev-guide" -edit-url-template = "https://github.com/rust-lang/rustc-dev-guide/edit/master/{path}" +edit-url-template = "https://github.com/rust-lang/rustc-dev-guide/edit/main/{path}" additional-js = [ "mermaid.min.js", "mermaid-init.js", diff --git a/src/doc/rustc-dev-guide/ci/linkcheck.sh b/src/doc/rustc-dev-guide/ci/linkcheck.sh index e5184839be02..867b427eafbb 100755 --- a/src/doc/rustc-dev-guide/ci/linkcheck.sh +++ b/src/doc/rustc-dev-guide/ci/linkcheck.sh @@ -32,7 +32,7 @@ elif [ "$GITHUB_EVENT_NAME" = "pull_request" ] ; then # running in PR CI build echo "Checking files changed since $BASE_SHA: $CHANGED_FILES" else # running locally - COMMIT_RANGE=master... + COMMIT_RANGE=main... CHANGED_FILES=$(git diff --name-only $COMMIT_RANGE | sed 's#^src/##' | tr '\n' ' ') FLAGS="-f $CHANGED_FILES" diff --git a/src/doc/rustc-dev-guide/src/rustc-driver/intro.md b/src/doc/rustc-dev-guide/src/rustc-driver/intro.md index a3684397b299..32d27d218197 100644 --- a/src/doc/rustc-dev-guide/src/rustc-driver/intro.md +++ b/src/doc/rustc-dev-guide/src/rustc-driver/intro.md @@ -34,7 +34,7 @@ specifically [`rustc_driver_impl::run_compiler`][rdi_rc] [`rustc_driver`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_driver/ [`rustc_interface`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_interface/index.html [`Callbacks`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_driver/trait.Callbacks.html -[example]: https://github.com/rust-lang/rustc-dev-guide/blob/master/examples/rustc-interface-example.rs +[example]: https://github.com/rust-lang/rustc-dev-guide/blob/main/examples/rustc-interface-example.rs [i_rc]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_interface/interface/fn.run_compiler.html [rd_rc]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_driver/fn.run_compiler.html [rdi_rc]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_driver_impl/fn.run_compiler.html From 400e687598320fbd9690af5acc4af091b18f577d Mon Sep 17 00:00:00 2001 From: Marco Cavenati Date: Mon, 8 Sep 2025 15:55:16 +0200 Subject: [PATCH 006/226] Bump unicode_data to version 17.0.0 --- library/core/src/unicode/unicode_data.rs | 750 ++++++++++++----------- 1 file changed, 383 insertions(+), 367 deletions(-) diff --git a/library/core/src/unicode/unicode_data.rs b/library/core/src/unicode/unicode_data.rs index 2f53de183f62..3c38b44224f8 100644 --- a/library/core/src/unicode/unicode_data.rs +++ b/library/core/src/unicode/unicode_data.rs @@ -1,15 +1,15 @@ //! This file is generated by `./x run src/tools/unicode-table-generator`; do not edit manually! -// Alphabetic : 1723 bytes, 142707 codepoints in 755 ranges (U+0000AA - U+0323B0) using skiplist -// Case_Ignorable : 1043 bytes, 2744 codepoints in 447 ranges (U+0000A8 - U+0E01F0) using skiplist -// Cased : 403 bytes, 4526 codepoints in 157 ranges (U+0000AA - U+01F18A) using skiplist -// Grapheme_Extend : 887 bytes, 2193 codepoints in 375 ranges (U+000300 - U+0E01F0) using skiplist -// Lowercase : 933 bytes, 2543 codepoints in 674 ranges (U+0000AA - U+01E944) using bitset -// N : 455 bytes, 1901 codepoints in 143 ranges (U+0000B2 - U+01FBFA) using skiplist -// Uppercase : 797 bytes, 1952 codepoints in 655 ranges (U+0000C0 - U+01F18A) using bitset +// Alphabetic : 1723 bytes, 147369 codepoints in 759 ranges (U+0000AA - U+03347A) using skiplist +// Case_Ignorable : 1063 bytes, 2789 codepoints in 459 ranges (U+0000A8 - U+0E01F0) using skiplist +// Cased : 401 bytes, 4580 codepoints in 156 ranges (U+0000AA - U+01F18A) using skiplist +// Grapheme_Extend : 899 bytes, 2232 codepoints in 383 ranges (U+000300 - U+0E01F0) using skiplist +// Lowercase : 943 bytes, 2569 codepoints in 676 ranges (U+0000AA - U+01E944) using bitset +// N : 463 bytes, 1914 codepoints in 145 ranges (U+0000B2 - U+01FBFA) using skiplist +// Uppercase : 799 bytes, 1980 codepoints in 659 ranges (U+0000C0 - U+01F18A) using bitset // White_Space : 256 bytes, 19 codepoints in 8 ranges (U+000085 - U+003001) using cascading -// to_lower : 11484 bytes -// to_upper : 13432 bytes -// Total : 31413 bytes +// to_lower : 11708 bytes +// to_upper : 13656 bytes +// Total : 31911 bytes #[inline(always)] const fn bitset_search< @@ -140,53 +140,52 @@ unsafe fn skip_search( offset_idx % 2 == 1 } -pub const UNICODE_VERSION: (u8, u8, u8) = (16, 0, 0); +pub const UNICODE_VERSION: (u8, u8, u8) = (17, 0, 0); #[rustfmt::skip] pub mod alphabetic { use super::ShortOffsetRunHeader; - static SHORT_OFFSET_RUNS: [ShortOffsetRunHeader; 53] = [ + static SHORT_OFFSET_RUNS: [ShortOffsetRunHeader; 51] = [ ShortOffsetRunHeader::new(0, 706), ShortOffsetRunHeader::new(12, 4681), ShortOffsetRunHeader::new(414, 5741), ShortOffsetRunHeader::new(452, 7958), ShortOffsetRunHeader::new(552, 9398), ShortOffsetRunHeader::new(623, 11264), ShortOffsetRunHeader::new(625, 12293), ShortOffsetRunHeader::new(663, 13312), ShortOffsetRunHeader::new(687, 19904), ShortOffsetRunHeader::new(688, 42125), ShortOffsetRunHeader::new(690, 42509), ShortOffsetRunHeader::new(694, 55204), - ShortOffsetRunHeader::new(784, 63744), ShortOffsetRunHeader::new(789, 64110), - ShortOffsetRunHeader::new(790, 64830), ShortOffsetRunHeader::new(812, 66176), - ShortOffsetRunHeader::new(853, 67383), ShortOffsetRunHeader::new(900, 73440), + ShortOffsetRunHeader::new(778, 63744), ShortOffsetRunHeader::new(783, 64110), + ShortOffsetRunHeader::new(784, 64830), ShortOffsetRunHeader::new(806, 66176), + ShortOffsetRunHeader::new(847, 67383), ShortOffsetRunHeader::new(894, 73440), ShortOffsetRunHeader::new(1217, 74650), ShortOffsetRunHeader::new(1228, 77712), ShortOffsetRunHeader::new(1233, 78896), ShortOffsetRunHeader::new(1236, 82939), ShortOffsetRunHeader::new(1240, 83527), ShortOffsetRunHeader::new(1242, 90368), ShortOffsetRunHeader::new(1243, 92160), ShortOffsetRunHeader::new(1245, 92729), - ShortOffsetRunHeader::new(1246, 93504), ShortOffsetRunHeader::new(1261, 100344), - ShortOffsetRunHeader::new(1278, 101590), ShortOffsetRunHeader::new(1280, 110576), - ShortOffsetRunHeader::new(1283, 110883), ShortOffsetRunHeader::new(1290, 111356), - ShortOffsetRunHeader::new(1300, 113664), ShortOffsetRunHeader::new(1301, 119808), - ShortOffsetRunHeader::new(1311, 120486), ShortOffsetRunHeader::new(1348, 122624), - ShortOffsetRunHeader::new(1371, 123536), ShortOffsetRunHeader::new(1395, 124112), - ShortOffsetRunHeader::new(1399, 124896), ShortOffsetRunHeader::new(1405, 126464), - ShortOffsetRunHeader::new(1421, 127280), ShortOffsetRunHeader::new(1487, 131072), - ShortOffsetRunHeader::new(1493, 173792), ShortOffsetRunHeader::new(1494, 177978), - ShortOffsetRunHeader::new(1496, 183970), ShortOffsetRunHeader::new(1500, 191457), - ShortOffsetRunHeader::new(1502, 192094), ShortOffsetRunHeader::new(1504, 194560), - ShortOffsetRunHeader::new(1505, 195102), ShortOffsetRunHeader::new(1506, 196608), - ShortOffsetRunHeader::new(1507, 201547), ShortOffsetRunHeader::new(1508, 205744), - ShortOffsetRunHeader::new(1510, 1319856), + ShortOffsetRunHeader::new(1246, 93504), ShortOffsetRunHeader::new(1261, 101590), + ShortOffsetRunHeader::new(1282, 110576), ShortOffsetRunHeader::new(1287, 110883), + ShortOffsetRunHeader::new(1294, 111356), ShortOffsetRunHeader::new(1304, 113664), + ShortOffsetRunHeader::new(1305, 119808), ShortOffsetRunHeader::new(1315, 120486), + ShortOffsetRunHeader::new(1352, 122624), ShortOffsetRunHeader::new(1375, 123536), + ShortOffsetRunHeader::new(1399, 124112), ShortOffsetRunHeader::new(1403, 126464), + ShortOffsetRunHeader::new(1431, 127280), ShortOffsetRunHeader::new(1497, 131072), + ShortOffsetRunHeader::new(1503, 173792), ShortOffsetRunHeader::new(1504, 178206), + ShortOffsetRunHeader::new(1506, 183982), ShortOffsetRunHeader::new(1508, 191457), + ShortOffsetRunHeader::new(1510, 192094), ShortOffsetRunHeader::new(1512, 194560), + ShortOffsetRunHeader::new(1513, 195102), ShortOffsetRunHeader::new(1514, 196608), + ShortOffsetRunHeader::new(1515, 201547), ShortOffsetRunHeader::new(1516, 210042), + ShortOffsetRunHeader::new(1518, 1324154), ]; - static OFFSETS: [u8; 1511] = [ + static OFFSETS: [u8; 1519] = [ 170, 1, 10, 1, 4, 1, 5, 23, 1, 31, 1, 0, 4, 12, 14, 5, 7, 1, 1, 1, 86, 1, 29, 18, 1, 2, 2, 4, 1, 1, 6, 1, 1, 3, 1, 1, 1, 20, 1, 83, 1, 139, 8, 166, 1, 38, 2, 1, 6, 41, 39, 14, 1, 1, 1, 2, 1, 2, 1, 1, 8, 27, 4, 4, 29, 11, 5, 56, 1, 7, 14, 102, 1, 8, 4, 8, 4, 3, 10, 3, 2, 1, - 16, 48, 13, 101, 24, 33, 9, 2, 4, 1, 5, 24, 2, 19, 19, 25, 7, 11, 5, 24, 1, 6, 8, 1, 8, 42, + 16, 48, 13, 101, 24, 33, 9, 2, 4, 1, 5, 24, 2, 19, 19, 25, 7, 11, 5, 24, 1, 7, 7, 1, 8, 42, 10, 12, 3, 7, 6, 76, 1, 16, 1, 3, 4, 15, 13, 19, 1, 8, 2, 2, 2, 22, 1, 7, 1, 1, 3, 4, 3, 8, 2, 2, 2, 2, 1, 1, 8, 1, 4, 2, 1, 5, 12, 2, 10, 1, 4, 3, 1, 6, 4, 2, 2, 22, 1, 7, 1, 2, 1, 2, 1, 2, 4, 5, 4, 2, 2, 2, 4, 1, 7, 4, 1, 1, 17, 6, 11, 3, 1, 9, 1, 3, 1, 22, 1, 7, 1, 2, 1, 5, 3, 9, 1, 3, 1, 2, 3, 1, 15, 4, 21, 4, 4, 3, 1, 8, 2, 2, 2, 22, 1, 7, 1, 2, 1, 5, 3, 8, 2, 2, 2, 2, 9, 2, 4, 2, 1, 5, 13, 1, 16, 2, 1, 6, 3, 3, 1, 4, 3, 2, 1, 1, 1, 2, 3, 2, 3, 3, 3, 12, - 4, 5, 3, 3, 1, 3, 3, 1, 6, 1, 40, 13, 1, 3, 1, 23, 1, 16, 3, 8, 1, 3, 1, 3, 8, 2, 1, 3, 2, - 1, 2, 4, 28, 4, 1, 8, 1, 3, 1, 23, 1, 10, 1, 5, 3, 8, 1, 3, 1, 3, 8, 2, 6, 2, 1, 4, 13, 3, + 4, 5, 3, 3, 1, 3, 3, 1, 6, 1, 40, 13, 1, 3, 1, 23, 1, 16, 3, 8, 1, 3, 1, 3, 8, 2, 1, 3, 1, + 2, 2, 4, 28, 4, 1, 8, 1, 3, 1, 23, 1, 10, 1, 5, 3, 8, 1, 3, 1, 3, 8, 2, 5, 3, 1, 4, 13, 3, 12, 13, 1, 3, 1, 41, 2, 8, 1, 3, 1, 3, 1, 1, 5, 4, 7, 5, 22, 6, 1, 3, 1, 18, 3, 24, 1, 9, 1, 1, 2, 7, 8, 6, 1, 1, 1, 8, 18, 2, 13, 58, 5, 7, 6, 1, 51, 2, 1, 1, 1, 5, 1, 24, 1, 1, 1, 19, 1, 3, 2, 5, 1, 1, 6, 1, 14, 4, 32, 1, 63, 8, 1, 36, 4, 19, 4, 16, 1, 36, 67, 55, 1, 1, 2, 5, @@ -201,37 +200,37 @@ pub mod alphabetic { 4, 1, 17, 41, 0, 52, 0, 229, 6, 4, 3, 2, 12, 38, 1, 1, 5, 1, 2, 56, 7, 1, 16, 23, 9, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 32, 47, 1, 0, 3, 25, 9, 7, 5, 2, 5, 4, 86, 6, 3, 1, 90, 1, 4, 5, 43, 1, 94, 17, 32, 48, 16, 0, 0, 64, 0, 67, 46, 2, 0, 3, 16, 10, 2, 20, 47, - 5, 8, 3, 113, 39, 9, 2, 103, 2, 67, 2, 2, 1, 1, 1, 8, 21, 20, 1, 33, 24, 52, 12, 68, 1, 1, - 44, 6, 3, 1, 1, 3, 10, 33, 5, 35, 13, 29, 3, 51, 1, 12, 15, 1, 16, 16, 10, 5, 1, 55, 9, 14, - 18, 23, 3, 69, 1, 1, 1, 1, 24, 3, 2, 16, 2, 4, 11, 6, 2, 6, 2, 6, 9, 7, 1, 7, 1, 43, 1, 14, - 6, 123, 21, 0, 12, 23, 4, 49, 0, 0, 2, 106, 38, 7, 12, 5, 5, 12, 1, 13, 1, 5, 1, 1, 1, 2, 1, - 2, 1, 108, 33, 0, 18, 64, 2, 54, 40, 12, 116, 5, 1, 135, 36, 26, 6, 26, 11, 89, 3, 6, 2, 6, - 2, 6, 2, 3, 35, 12, 1, 26, 1, 19, 1, 2, 1, 15, 2, 14, 34, 123, 69, 53, 0, 29, 3, 49, 47, 32, - 13, 30, 5, 43, 5, 30, 2, 36, 4, 8, 1, 5, 42, 158, 18, 36, 4, 36, 4, 40, 8, 52, 12, 11, 1, - 15, 1, 7, 1, 2, 1, 11, 1, 15, 1, 7, 1, 2, 3, 52, 12, 0, 9, 22, 10, 8, 24, 6, 1, 42, 1, 9, - 69, 6, 2, 1, 1, 44, 1, 2, 3, 1, 2, 23, 10, 23, 9, 31, 65, 19, 1, 2, 10, 22, 10, 26, 70, 56, - 6, 2, 64, 4, 1, 2, 5, 8, 1, 3, 1, 29, 42, 29, 3, 29, 35, 8, 1, 28, 27, 54, 10, 22, 10, 19, - 13, 18, 110, 73, 55, 51, 13, 51, 13, 40, 34, 28, 3, 1, 5, 23, 250, 42, 1, 2, 3, 2, 16, 3, - 55, 1, 3, 29, 10, 1, 8, 22, 42, 18, 46, 21, 27, 23, 9, 70, 43, 5, 10, 57, 9, 1, 13, 25, 23, - 51, 17, 4, 8, 35, 3, 1, 9, 64, 1, 4, 9, 2, 10, 1, 1, 1, 35, 18, 1, 34, 2, 1, 6, 4, 62, 7, 1, - 1, 1, 4, 1, 15, 1, 10, 7, 57, 23, 4, 1, 8, 2, 2, 2, 22, 1, 7, 1, 2, 1, 5, 3, 8, 2, 2, 2, 2, - 3, 1, 6, 1, 5, 7, 28, 10, 1, 1, 2, 1, 1, 38, 1, 10, 1, 1, 2, 1, 1, 4, 1, 2, 3, 1, 1, 1, 44, - 66, 1, 3, 1, 4, 20, 3, 30, 66, 2, 2, 1, 1, 184, 54, 2, 7, 25, 6, 34, 63, 1, 1, 3, 1, 59, 54, - 2, 1, 71, 27, 2, 14, 21, 7, 185, 57, 103, 64, 31, 8, 2, 1, 2, 8, 1, 2, 1, 30, 1, 2, 2, 2, 2, - 4, 93, 8, 2, 46, 2, 6, 1, 1, 1, 2, 27, 51, 2, 10, 17, 72, 5, 1, 18, 73, 199, 33, 31, 9, 1, - 45, 1, 7, 1, 1, 49, 30, 2, 22, 1, 14, 73, 7, 1, 2, 1, 44, 3, 1, 1, 2, 1, 3, 1, 1, 2, 2, 24, - 6, 1, 2, 1, 37, 1, 2, 1, 4, 1, 1, 0, 23, 9, 17, 1, 41, 3, 3, 111, 1, 79, 0, 102, 111, 17, - 196, 0, 97, 15, 0, 17, 6, 25, 0, 5, 0, 0, 47, 0, 0, 7, 31, 17, 79, 17, 30, 18, 48, 16, 4, - 31, 21, 5, 19, 0, 45, 211, 64, 128, 75, 4, 57, 7, 17, 64, 2, 1, 1, 12, 2, 14, 0, 8, 0, 41, - 10, 0, 4, 1, 7, 1, 2, 1, 0, 15, 1, 29, 3, 2, 1, 14, 4, 8, 0, 0, 107, 5, 13, 3, 9, 7, 10, 4, - 1, 0, 85, 1, 71, 1, 2, 2, 1, 2, 2, 2, 4, 1, 12, 1, 1, 1, 7, 1, 65, 1, 4, 2, 8, 1, 7, 1, 28, - 1, 4, 1, 5, 1, 1, 3, 7, 1, 0, 2, 25, 1, 25, 1, 31, 1, 25, 1, 31, 1, 25, 1, 31, 1, 25, 1, 31, - 1, 25, 1, 8, 0, 31, 6, 6, 213, 7, 1, 17, 2, 7, 1, 2, 1, 5, 5, 62, 33, 1, 112, 45, 10, 7, 16, - 1, 0, 30, 18, 44, 0, 28, 228, 30, 2, 1, 0, 7, 1, 4, 1, 2, 1, 15, 1, 197, 59, 68, 3, 1, 3, 1, - 0, 4, 1, 27, 1, 2, 1, 1, 2, 1, 1, 10, 1, 4, 1, 1, 1, 1, 6, 1, 4, 1, 1, 1, 1, 1, 1, 3, 1, 2, - 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 2, 4, 1, 7, 1, 4, 1, 4, 1, 1, 1, 10, 1, 17, - 5, 3, 1, 5, 1, 17, 0, 26, 6, 26, 6, 26, 0, 0, 32, 0, 6, 222, 2, 0, 14, 0, 15, 0, 0, 0, 0, 0, - 5, 0, 0, + 5, 8, 3, 113, 39, 9, 2, 103, 2, 82, 20, 21, 1, 33, 24, 52, 12, 68, 1, 1, 44, 6, 3, 1, 1, 3, + 10, 33, 5, 35, 13, 29, 3, 51, 1, 12, 15, 1, 16, 16, 10, 5, 1, 55, 9, 14, 18, 23, 3, 69, 1, + 1, 1, 1, 24, 3, 2, 16, 2, 4, 11, 6, 2, 6, 2, 6, 9, 7, 1, 7, 1, 43, 1, 14, 6, 123, 21, 0, 12, + 23, 4, 49, 0, 0, 2, 106, 38, 7, 12, 5, 5, 12, 1, 13, 1, 5, 1, 1, 1, 2, 1, 2, 1, 108, 33, 0, + 18, 64, 2, 54, 40, 12, 116, 5, 1, 135, 36, 26, 6, 26, 11, 89, 3, 6, 2, 6, 2, 6, 2, 3, 35, + 12, 1, 26, 1, 19, 1, 2, 1, 15, 2, 14, 34, 123, 69, 53, 0, 29, 3, 49, 47, 32, 13, 30, 5, 43, + 5, 30, 2, 36, 4, 8, 1, 5, 42, 158, 18, 36, 4, 36, 4, 40, 8, 52, 12, 11, 1, 15, 1, 7, 1, 2, + 1, 11, 1, 15, 1, 7, 1, 2, 3, 52, 12, 0, 9, 22, 10, 8, 24, 6, 1, 42, 1, 9, 69, 6, 2, 1, 1, + 44, 1, 2, 3, 1, 2, 23, 10, 23, 9, 31, 65, 19, 1, 2, 10, 22, 10, 26, 6, 26, 38, 56, 6, 2, 64, + 4, 1, 2, 5, 8, 1, 3, 1, 29, 42, 29, 3, 29, 35, 8, 1, 28, 27, 54, 10, 22, 10, 19, 13, 18, + 110, 73, 55, 51, 13, 51, 13, 40, 34, 28, 3, 1, 5, 23, 250, 42, 1, 2, 3, 2, 16, 6, 50, 3, 3, + 29, 10, 1, 8, 22, 42, 18, 46, 21, 27, 23, 9, 70, 43, 5, 10, 57, 9, 1, 13, 25, 23, 51, 17, 4, + 8, 35, 3, 1, 9, 64, 1, 4, 9, 2, 10, 1, 1, 1, 35, 18, 1, 34, 2, 1, 6, 4, 62, 7, 1, 1, 1, 4, + 1, 15, 1, 10, 7, 57, 23, 4, 1, 8, 2, 2, 2, 22, 1, 7, 1, 2, 1, 5, 3, 8, 2, 2, 2, 2, 3, 1, 6, + 1, 5, 7, 28, 10, 1, 1, 2, 1, 1, 38, 1, 10, 1, 1, 2, 1, 1, 4, 1, 2, 3, 1, 1, 1, 44, 66, 1, 3, + 1, 4, 20, 3, 30, 66, 2, 2, 1, 1, 184, 54, 2, 7, 25, 6, 34, 63, 1, 1, 3, 1, 59, 54, 2, 1, 71, + 27, 2, 14, 21, 7, 185, 57, 103, 64, 31, 8, 2, 1, 2, 8, 1, 2, 1, 30, 1, 2, 2, 2, 2, 4, 93, 8, + 2, 46, 2, 6, 1, 1, 1, 2, 27, 51, 2, 10, 17, 72, 5, 1, 18, 73, 103, 8, 88, 33, 31, 9, 1, 45, + 1, 7, 1, 1, 49, 30, 2, 22, 1, 14, 73, 7, 1, 2, 1, 44, 3, 1, 1, 2, 1, 3, 1, 1, 2, 2, 24, 6, + 1, 2, 1, 37, 1, 2, 1, 4, 1, 1, 23, 44, 0, 23, 9, 17, 1, 41, 3, 3, 111, 1, 79, 0, 102, 111, + 17, 196, 0, 97, 15, 0, 17, 6, 25, 0, 5, 0, 0, 47, 0, 0, 7, 31, 17, 79, 17, 30, 18, 48, 16, + 4, 31, 21, 5, 19, 0, 45, 211, 64, 32, 25, 2, 25, 44, 75, 4, 57, 7, 17, 64, 2, 1, 1, 12, 7, + 9, 0, 41, 32, 97, 115, 0, 4, 1, 7, 1, 2, 1, 0, 15, 1, 29, 3, 2, 1, 14, 4, 8, 0, 0, 107, 5, + 13, 3, 9, 7, 10, 4, 1, 0, 85, 1, 71, 1, 2, 2, 1, 2, 2, 2, 4, 1, 12, 1, 1, 1, 7, 1, 65, 1, 4, + 2, 8, 1, 7, 1, 28, 1, 4, 1, 5, 1, 1, 3, 7, 1, 0, 2, 25, 1, 25, 1, 31, 1, 25, 1, 31, 1, 25, + 1, 31, 1, 25, 1, 31, 1, 25, 1, 8, 0, 31, 6, 6, 213, 7, 1, 17, 2, 7, 1, 2, 1, 5, 5, 62, 33, + 1, 112, 45, 10, 7, 16, 1, 0, 30, 18, 44, 0, 28, 228, 30, 2, 1, 207, 31, 1, 22, 8, 2, 224, 7, + 1, 4, 1, 2, 1, 15, 1, 197, 59, 68, 3, 1, 3, 1, 0, 4, 1, 27, 1, 2, 1, 1, 2, 1, 1, 10, 1, 4, + 1, 1, 1, 1, 6, 1, 4, 1, 1, 1, 1, 1, 1, 3, 1, 2, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, + 1, 2, 4, 1, 7, 1, 4, 1, 4, 1, 1, 1, 10, 1, 17, 5, 3, 1, 5, 1, 17, 0, 26, 6, 26, 6, 26, 0, 0, + 32, 0, 2, 0, 2, 0, 15, 0, 0, 0, 0, 0, 5, 0, 0, ]; #[inline] pub fn lookup(c: char) -> bool { @@ -259,28 +258,27 @@ pub mod alphabetic { pub mod case_ignorable { use super::ShortOffsetRunHeader; - static SHORT_OFFSET_RUNS: [ShortOffsetRunHeader; 37] = [ + static SHORT_OFFSET_RUNS: [ShortOffsetRunHeader; 36] = [ ShortOffsetRunHeader::new(0, 688), ShortOffsetRunHeader::new(11, 4957), ShortOffsetRunHeader::new(263, 5906), ShortOffsetRunHeader::new(265, 8125), - ShortOffsetRunHeader::new(375, 11388), ShortOffsetRunHeader::new(409, 12293), - ShortOffsetRunHeader::new(421, 40981), ShortOffsetRunHeader::new(433, 42232), - ShortOffsetRunHeader::new(435, 42508), ShortOffsetRunHeader::new(437, 64286), - ShortOffsetRunHeader::new(533, 65024), ShortOffsetRunHeader::new(537, 66045), - ShortOffsetRunHeader::new(567, 67456), ShortOffsetRunHeader::new(573, 68097), - ShortOffsetRunHeader::new(579, 68900), ShortOffsetRunHeader::new(591, 69291), - ShortOffsetRunHeader::new(599, 71727), ShortOffsetRunHeader::new(723, 71995), - ShortOffsetRunHeader::new(727, 72752), ShortOffsetRunHeader::new(755, 73459), - ShortOffsetRunHeader::new(785, 78896), ShortOffsetRunHeader::new(797, 90398), - ShortOffsetRunHeader::new(801, 92912), ShortOffsetRunHeader::new(805, 93504), - ShortOffsetRunHeader::new(811, 94031), ShortOffsetRunHeader::new(815, 110576), - ShortOffsetRunHeader::new(823, 113821), ShortOffsetRunHeader::new(829, 118528), - ShortOffsetRunHeader::new(833, 119143), ShortOffsetRunHeader::new(837, 121344), - ShortOffsetRunHeader::new(847, 122880), ShortOffsetRunHeader::new(859, 123566), - ShortOffsetRunHeader::new(875, 124139), ShortOffsetRunHeader::new(879, 125136), - ShortOffsetRunHeader::new(883, 127995), ShortOffsetRunHeader::new(887, 917505), - ShortOffsetRunHeader::new(889, 2032112), + ShortOffsetRunHeader::new(377, 11388), ShortOffsetRunHeader::new(411, 12293), + ShortOffsetRunHeader::new(423, 40981), ShortOffsetRunHeader::new(435, 42232), + ShortOffsetRunHeader::new(437, 42508), ShortOffsetRunHeader::new(439, 64286), + ShortOffsetRunHeader::new(535, 65024), ShortOffsetRunHeader::new(539, 66045), + ShortOffsetRunHeader::new(569, 67456), ShortOffsetRunHeader::new(575, 68097), + ShortOffsetRunHeader::new(581, 68900), ShortOffsetRunHeader::new(593, 69291), + ShortOffsetRunHeader::new(601, 71727), ShortOffsetRunHeader::new(727, 71995), + ShortOffsetRunHeader::new(731, 73459), ShortOffsetRunHeader::new(797, 78896), + ShortOffsetRunHeader::new(809, 90398), ShortOffsetRunHeader::new(813, 92912), + ShortOffsetRunHeader::new(817, 93504), ShortOffsetRunHeader::new(823, 94031), + ShortOffsetRunHeader::new(827, 110576), ShortOffsetRunHeader::new(837, 113821), + ShortOffsetRunHeader::new(843, 118528), ShortOffsetRunHeader::new(847, 119143), + ShortOffsetRunHeader::new(851, 121344), ShortOffsetRunHeader::new(861, 122880), + ShortOffsetRunHeader::new(873, 123566), ShortOffsetRunHeader::new(889, 124139), + ShortOffsetRunHeader::new(893, 125136), ShortOffsetRunHeader::new(907, 127995), + ShortOffsetRunHeader::new(911, 917505), ShortOffsetRunHeader::new(913, 2032112), ]; - static OFFSETS: [u8; 895] = [ + static OFFSETS: [u8; 919] = [ 168, 1, 4, 1, 1, 1, 4, 1, 2, 2, 0, 192, 4, 2, 4, 1, 9, 2, 1, 1, 251, 7, 207, 1, 5, 1, 49, 45, 1, 1, 1, 2, 1, 2, 1, 1, 44, 1, 11, 6, 10, 11, 1, 1, 35, 1, 10, 21, 16, 1, 101, 8, 1, 10, 1, 4, 33, 1, 1, 1, 30, 27, 91, 11, 58, 11, 4, 1, 2, 1, 24, 24, 43, 3, 44, 1, 7, 2, 5, 9, 41, @@ -292,28 +290,29 @@ pub mod case_ignorable { 1, 1, 1, 1, 55, 14, 1, 5, 1, 2, 5, 11, 1, 36, 9, 1, 102, 4, 1, 6, 1, 2, 2, 2, 25, 2, 4, 3, 16, 4, 13, 1, 2, 2, 6, 1, 15, 1, 94, 1, 0, 3, 0, 3, 29, 2, 30, 2, 30, 2, 64, 2, 1, 7, 8, 1, 2, 11, 3, 1, 5, 1, 45, 5, 51, 1, 65, 2, 34, 1, 118, 3, 4, 2, 9, 1, 6, 3, 219, 2, 2, 1, 58, - 1, 1, 7, 1, 1, 1, 1, 2, 8, 6, 10, 2, 1, 39, 1, 8, 31, 49, 4, 48, 1, 1, 5, 1, 1, 5, 1, 40, 9, - 12, 2, 32, 4, 2, 2, 1, 3, 56, 1, 1, 2, 3, 1, 1, 3, 58, 8, 2, 2, 64, 6, 82, 3, 1, 13, 1, 7, - 4, 1, 6, 1, 3, 2, 50, 63, 13, 1, 34, 101, 0, 1, 1, 3, 11, 3, 13, 3, 13, 3, 13, 2, 12, 5, 8, - 2, 10, 1, 2, 1, 2, 5, 49, 5, 1, 10, 1, 1, 13, 1, 16, 13, 51, 33, 0, 2, 113, 3, 125, 1, 15, - 1, 96, 32, 47, 1, 0, 1, 36, 4, 3, 5, 5, 1, 93, 6, 93, 3, 0, 1, 0, 6, 0, 1, 98, 4, 1, 10, 1, - 1, 28, 4, 80, 2, 14, 34, 78, 1, 23, 3, 103, 3, 3, 2, 8, 1, 3, 1, 4, 1, 25, 2, 5, 1, 151, 2, - 26, 18, 13, 1, 38, 8, 25, 11, 46, 3, 48, 1, 2, 4, 2, 2, 17, 1, 21, 2, 66, 6, 2, 2, 2, 2, 12, - 1, 8, 1, 35, 1, 11, 1, 51, 1, 1, 3, 2, 2, 5, 2, 1, 1, 27, 1, 14, 2, 5, 2, 1, 1, 100, 5, 9, - 3, 121, 1, 2, 1, 4, 1, 0, 1, 147, 17, 0, 16, 3, 1, 12, 16, 34, 1, 2, 1, 169, 1, 7, 1, 6, 1, - 11, 1, 35, 1, 1, 1, 47, 1, 45, 2, 67, 1, 21, 3, 0, 1, 226, 1, 149, 5, 0, 6, 1, 42, 1, 9, 0, - 3, 1, 2, 5, 4, 40, 3, 4, 1, 165, 2, 0, 4, 38, 1, 26, 5, 1, 1, 0, 2, 79, 4, 70, 11, 49, 4, - 123, 1, 54, 15, 41, 1, 2, 2, 10, 3, 49, 4, 2, 2, 2, 1, 4, 1, 10, 1, 50, 3, 36, 5, 1, 8, 62, - 1, 12, 2, 52, 9, 10, 4, 2, 1, 95, 3, 2, 1, 1, 2, 6, 1, 2, 1, 157, 1, 3, 8, 21, 2, 57, 2, 3, - 1, 37, 7, 3, 5, 70, 6, 13, 1, 1, 1, 1, 1, 14, 2, 85, 8, 2, 3, 1, 1, 23, 1, 84, 6, 1, 1, 4, - 2, 1, 2, 238, 4, 6, 2, 1, 2, 27, 2, 85, 8, 2, 1, 1, 2, 106, 1, 1, 1, 2, 6, 1, 1, 101, 1, 1, - 1, 2, 4, 1, 5, 0, 9, 1, 2, 0, 2, 1, 1, 4, 1, 144, 4, 2, 2, 4, 1, 32, 10, 40, 6, 2, 4, 8, 1, - 9, 6, 2, 3, 46, 13, 1, 2, 0, 7, 1, 6, 1, 1, 82, 22, 2, 7, 1, 2, 1, 2, 122, 6, 3, 1, 1, 2, 1, - 7, 1, 1, 72, 2, 3, 1, 1, 1, 0, 2, 11, 2, 52, 5, 5, 1, 1, 1, 23, 1, 0, 17, 6, 15, 0, 12, 3, - 3, 0, 5, 59, 7, 9, 4, 0, 3, 40, 2, 0, 1, 63, 17, 64, 2, 1, 2, 0, 4, 1, 7, 1, 2, 0, 2, 1, 4, - 0, 46, 2, 23, 0, 3, 9, 16, 2, 7, 30, 4, 148, 3, 0, 55, 4, 50, 8, 1, 14, 1, 22, 5, 1, 15, 0, - 7, 1, 17, 2, 7, 1, 2, 1, 5, 5, 62, 33, 1, 160, 14, 0, 1, 61, 4, 0, 5, 254, 2, 0, 7, 109, 8, - 0, 5, 0, 1, 30, 96, 128, 240, 0, + 1, 1, 7, 1, 1, 1, 1, 2, 8, 6, 10, 2, 1, 39, 1, 8, 46, 2, 12, 20, 4, 48, 1, 1, 5, 1, 1, 5, 1, + 40, 9, 12, 2, 32, 4, 2, 2, 1, 3, 56, 1, 1, 2, 3, 1, 1, 3, 58, 8, 2, 2, 64, 6, 82, 3, 1, 13, + 1, 7, 4, 1, 6, 1, 3, 2, 50, 63, 13, 1, 34, 101, 0, 1, 1, 3, 11, 3, 13, 3, 13, 3, 13, 2, 12, + 5, 8, 2, 10, 1, 2, 1, 2, 5, 49, 5, 1, 10, 1, 1, 13, 1, 16, 13, 51, 33, 0, 2, 113, 3, 125, 1, + 15, 1, 96, 32, 47, 1, 0, 1, 36, 4, 3, 5, 5, 1, 93, 6, 93, 3, 0, 1, 0, 6, 0, 1, 98, 4, 1, 10, + 1, 1, 28, 4, 80, 2, 14, 34, 78, 1, 23, 3, 102, 4, 3, 2, 8, 1, 3, 1, 4, 1, 25, 2, 5, 1, 151, + 2, 26, 18, 13, 1, 38, 8, 25, 11, 46, 3, 48, 1, 2, 4, 2, 2, 17, 1, 21, 2, 66, 6, 2, 2, 2, 2, + 12, 1, 8, 1, 35, 1, 11, 1, 51, 1, 1, 3, 2, 2, 5, 2, 1, 1, 27, 1, 14, 2, 5, 2, 1, 1, 100, 5, + 9, 3, 121, 1, 2, 1, 4, 1, 0, 1, 147, 17, 0, 16, 3, 1, 12, 16, 34, 1, 2, 1, 169, 1, 7, 1, 6, + 1, 11, 1, 35, 1, 1, 1, 47, 1, 45, 2, 67, 1, 21, 3, 0, 1, 226, 1, 149, 5, 0, 6, 1, 42, 1, 9, + 0, 3, 1, 2, 5, 4, 40, 3, 4, 1, 165, 2, 0, 4, 38, 1, 26, 5, 1, 1, 0, 2, 24, 1, 52, 6, 70, 11, + 49, 4, 123, 1, 54, 15, 41, 1, 2, 2, 10, 3, 49, 4, 2, 2, 2, 1, 4, 1, 10, 1, 50, 3, 36, 5, 1, + 8, 62, 1, 12, 2, 52, 9, 10, 4, 2, 1, 95, 3, 2, 1, 1, 2, 6, 1, 2, 1, 157, 1, 3, 8, 21, 2, 57, + 2, 3, 1, 37, 7, 3, 5, 70, 6, 13, 1, 1, 1, 1, 1, 14, 2, 85, 8, 2, 3, 1, 1, 23, 1, 84, 6, 1, + 1, 4, 2, 1, 2, 238, 4, 6, 2, 1, 2, 27, 2, 85, 8, 2, 1, 1, 2, 106, 1, 1, 1, 2, 6, 1, 1, 101, + 1, 1, 1, 2, 4, 1, 5, 0, 9, 1, 2, 0, 2, 1, 1, 4, 1, 144, 4, 2, 2, 4, 1, 32, 10, 40, 6, 2, 4, + 8, 1, 9, 6, 2, 3, 46, 13, 1, 2, 198, 1, 1, 3, 1, 1, 201, 7, 1, 6, 1, 1, 82, 22, 2, 7, 1, 2, + 1, 2, 122, 6, 3, 1, 1, 2, 1, 7, 1, 1, 72, 2, 3, 1, 1, 1, 65, 1, 0, 2, 11, 2, 52, 5, 5, 1, 1, + 1, 23, 1, 0, 17, 6, 15, 0, 12, 3, 3, 0, 5, 59, 7, 9, 4, 0, 3, 40, 2, 0, 1, 63, 17, 64, 2, 1, + 2, 13, 2, 0, 4, 1, 7, 1, 2, 0, 2, 1, 4, 0, 46, 2, 23, 0, 3, 9, 16, 2, 7, 30, 4, 148, 3, 0, + 55, 4, 50, 8, 1, 14, 1, 22, 5, 1, 15, 0, 7, 1, 17, 2, 7, 1, 2, 1, 5, 5, 62, 33, 1, 160, 14, + 0, 1, 61, 4, 0, 5, 254, 2, 243, 1, 2, 1, 7, 2, 5, 1, 9, 1, 0, 7, 109, 8, 0, 5, 0, 1, 30, 96, + 128, 240, 0, ]; #[inline] pub fn lookup(c: char) -> bool { @@ -346,24 +345,24 @@ pub mod cased { ShortOffsetRunHeader::new(61, 7296), ShortOffsetRunHeader::new(65, 7958), ShortOffsetRunHeader::new(74, 9398), ShortOffsetRunHeader::new(149, 11264), ShortOffsetRunHeader::new(151, 42560), ShortOffsetRunHeader::new(163, 43824), - ShortOffsetRunHeader::new(183, 64256), ShortOffsetRunHeader::new(189, 65313), - ShortOffsetRunHeader::new(193, 66560), ShortOffsetRunHeader::new(197, 67456), - ShortOffsetRunHeader::new(219, 68736), ShortOffsetRunHeader::new(227, 71840), - ShortOffsetRunHeader::new(235, 93760), ShortOffsetRunHeader::new(237, 119808), - ShortOffsetRunHeader::new(239, 120486), ShortOffsetRunHeader::new(276, 122624), - ShortOffsetRunHeader::new(299, 122928), ShortOffsetRunHeader::new(305, 125184), - ShortOffsetRunHeader::new(307, 127280), ShortOffsetRunHeader::new(309, 1241482), + ShortOffsetRunHeader::new(177, 64256), ShortOffsetRunHeader::new(183, 65313), + ShortOffsetRunHeader::new(187, 66560), ShortOffsetRunHeader::new(191, 67456), + ShortOffsetRunHeader::new(213, 68736), ShortOffsetRunHeader::new(221, 71840), + ShortOffsetRunHeader::new(229, 93760), ShortOffsetRunHeader::new(231, 119808), + ShortOffsetRunHeader::new(237, 120486), ShortOffsetRunHeader::new(274, 122624), + ShortOffsetRunHeader::new(297, 122928), ShortOffsetRunHeader::new(303, 125184), + ShortOffsetRunHeader::new(305, 127280), ShortOffsetRunHeader::new(307, 1241482), ]; - static OFFSETS: [u8; 315] = [ - 170, 1, 10, 1, 4, 1, 5, 23, 1, 31, 1, 195, 1, 4, 4, 208, 1, 36, 7, 2, 30, 5, 96, 1, 42, 4, + static OFFSETS: [u8; 313] = [ + 170, 1, 10, 1, 4, 1, 5, 23, 1, 31, 1, 195, 1, 4, 4, 208, 2, 35, 7, 2, 30, 5, 96, 1, 42, 4, 2, 2, 2, 4, 1, 1, 6, 1, 1, 3, 1, 1, 1, 20, 1, 83, 1, 139, 8, 166, 1, 38, 9, 41, 0, 38, 1, 1, 5, 1, 2, 43, 1, 4, 0, 86, 2, 6, 0, 11, 5, 43, 2, 3, 64, 192, 64, 0, 2, 6, 2, 38, 2, 6, 2, 8, 1, 1, 1, 1, 1, 1, 1, 31, 2, 53, 1, 7, 1, 1, 3, 3, 1, 7, 3, 4, 2, 6, 4, 13, 5, 3, 1, 7, 116, 1, 13, 1, 16, 13, 101, 1, 4, 1, 2, 10, 1, 1, 3, 5, 6, 1, 1, 1, 1, 1, 1, 4, 1, 6, 4, 1, 2, 4, 5, 5, 4, 1, 17, 32, 3, 2, 0, 52, 0, 229, 6, 4, 3, 2, 12, 38, 1, 1, 5, 1, 0, 46, 18, 30, 132, - 102, 3, 4, 1, 62, 2, 2, 1, 1, 1, 8, 21, 5, 1, 3, 0, 43, 1, 14, 6, 80, 0, 7, 12, 5, 0, 26, 6, - 26, 0, 80, 96, 36, 4, 36, 116, 11, 1, 15, 1, 7, 1, 2, 1, 11, 1, 15, 1, 7, 1, 2, 0, 1, 2, 3, - 1, 42, 1, 9, 0, 51, 13, 51, 93, 22, 10, 22, 0, 64, 0, 64, 0, 85, 1, 71, 1, 2, 2, 1, 2, 2, 2, + 102, 3, 4, 1, 77, 20, 6, 1, 3, 0, 43, 1, 14, 6, 80, 0, 7, 12, 5, 0, 26, 6, 26, 0, 80, 96, + 36, 4, 36, 116, 11, 1, 15, 1, 7, 1, 2, 1, 11, 1, 15, 1, 7, 1, 2, 0, 1, 2, 3, 1, 42, 1, 9, 0, + 51, 13, 51, 93, 22, 10, 22, 0, 64, 0, 64, 32, 25, 2, 25, 0, 85, 1, 71, 1, 2, 2, 1, 2, 2, 2, 4, 1, 12, 1, 1, 1, 7, 1, 65, 1, 4, 2, 8, 1, 7, 1, 28, 1, 4, 1, 5, 1, 1, 3, 7, 1, 0, 2, 25, 1, 25, 1, 31, 1, 25, 1, 31, 1, 25, 1, 31, 1, 25, 1, 31, 1, 25, 1, 8, 0, 10, 1, 20, 6, 6, 0, 62, 0, 68, 0, 26, 6, 26, 6, 26, 0, @@ -394,26 +393,26 @@ pub mod cased { pub mod grapheme_extend { use super::ShortOffsetRunHeader; - static SHORT_OFFSET_RUNS: [ShortOffsetRunHeader; 34] = [ + static SHORT_OFFSET_RUNS: [ShortOffsetRunHeader; 33] = [ ShortOffsetRunHeader::new(0, 768), ShortOffsetRunHeader::new(1, 1155), ShortOffsetRunHeader::new(3, 1425), ShortOffsetRunHeader::new(5, 4957), ShortOffsetRunHeader::new(249, 5906), ShortOffsetRunHeader::new(251, 8204), - ShortOffsetRunHeader::new(345, 11503), ShortOffsetRunHeader::new(349, 12330), - ShortOffsetRunHeader::new(355, 42607), ShortOffsetRunHeader::new(359, 43010), - ShortOffsetRunHeader::new(367, 64286), ShortOffsetRunHeader::new(433, 65024), - ShortOffsetRunHeader::new(435, 65438), ShortOffsetRunHeader::new(439, 66045), - ShortOffsetRunHeader::new(441, 68097), ShortOffsetRunHeader::new(447, 68900), - ShortOffsetRunHeader::new(459, 69291), ShortOffsetRunHeader::new(463, 71727), - ShortOffsetRunHeader::new(599, 72752), ShortOffsetRunHeader::new(631, 73459), - ShortOffsetRunHeader::new(661, 78912), ShortOffsetRunHeader::new(671, 90398), - ShortOffsetRunHeader::new(675, 92912), ShortOffsetRunHeader::new(679, 94031), - ShortOffsetRunHeader::new(683, 113821), ShortOffsetRunHeader::new(691, 118528), - ShortOffsetRunHeader::new(693, 119141), ShortOffsetRunHeader::new(697, 121344), - ShortOffsetRunHeader::new(709, 122880), ShortOffsetRunHeader::new(721, 123566), - ShortOffsetRunHeader::new(735, 124140), ShortOffsetRunHeader::new(739, 125136), - ShortOffsetRunHeader::new(743, 917536), ShortOffsetRunHeader::new(747, 2032112), + ShortOffsetRunHeader::new(347, 11503), ShortOffsetRunHeader::new(351, 12330), + ShortOffsetRunHeader::new(357, 42607), ShortOffsetRunHeader::new(361, 43010), + ShortOffsetRunHeader::new(369, 64286), ShortOffsetRunHeader::new(435, 65024), + ShortOffsetRunHeader::new(437, 65438), ShortOffsetRunHeader::new(441, 66045), + ShortOffsetRunHeader::new(443, 68097), ShortOffsetRunHeader::new(449, 68900), + ShortOffsetRunHeader::new(461, 69291), ShortOffsetRunHeader::new(465, 71727), + ShortOffsetRunHeader::new(601, 73459), ShortOffsetRunHeader::new(669, 78912), + ShortOffsetRunHeader::new(679, 90398), ShortOffsetRunHeader::new(683, 92912), + ShortOffsetRunHeader::new(687, 94031), ShortOffsetRunHeader::new(691, 113821), + ShortOffsetRunHeader::new(699, 118528), ShortOffsetRunHeader::new(701, 119141), + ShortOffsetRunHeader::new(705, 121344), ShortOffsetRunHeader::new(717, 122880), + ShortOffsetRunHeader::new(729, 123566), ShortOffsetRunHeader::new(743, 124140), + ShortOffsetRunHeader::new(747, 125136), ShortOffsetRunHeader::new(759, 917536), + ShortOffsetRunHeader::new(763, 2032112), ]; - static OFFSETS: [u8; 751] = [ + static OFFSETS: [u8; 767] = [ 0, 112, 0, 7, 0, 45, 1, 1, 1, 2, 1, 2, 1, 1, 72, 11, 48, 21, 16, 1, 101, 7, 2, 6, 2, 2, 1, 4, 35, 1, 30, 27, 91, 11, 58, 9, 9, 1, 24, 4, 1, 9, 1, 3, 1, 5, 43, 3, 59, 9, 42, 24, 1, 32, 55, 1, 1, 1, 4, 8, 4, 1, 3, 7, 10, 2, 29, 1, 58, 1, 1, 1, 2, 4, 8, 1, 9, 1, 10, 2, 26, 1, 2, @@ -424,23 +423,24 @@ pub mod grapheme_extend { 12, 8, 98, 1, 2, 9, 11, 7, 73, 2, 27, 1, 1, 1, 1, 1, 55, 14, 1, 5, 1, 2, 5, 11, 1, 36, 9, 1, 102, 4, 1, 6, 1, 2, 2, 2, 25, 2, 4, 3, 16, 4, 13, 1, 2, 2, 6, 1, 15, 1, 0, 3, 0, 4, 28, 3, 29, 2, 30, 2, 64, 2, 1, 7, 8, 1, 2, 11, 9, 1, 45, 3, 1, 1, 117, 2, 34, 1, 118, 3, 4, 2, 9, - 1, 6, 3, 219, 2, 2, 1, 58, 1, 1, 7, 1, 1, 1, 1, 2, 8, 6, 10, 2, 1, 48, 31, 49, 4, 48, 10, 4, - 3, 38, 9, 12, 2, 32, 4, 2, 6, 56, 1, 1, 2, 3, 1, 1, 5, 56, 8, 2, 2, 152, 3, 1, 13, 1, 7, 4, - 1, 6, 1, 3, 2, 198, 64, 0, 1, 195, 33, 0, 3, 141, 1, 96, 32, 0, 6, 105, 2, 0, 4, 1, 10, 32, - 2, 80, 2, 0, 1, 3, 1, 4, 1, 25, 2, 5, 1, 151, 2, 26, 18, 13, 1, 38, 8, 25, 11, 1, 1, 44, 3, - 48, 1, 2, 4, 2, 2, 2, 1, 36, 1, 67, 6, 2, 2, 2, 2, 12, 1, 8, 1, 47, 1, 51, 1, 1, 3, 2, 2, 5, - 2, 1, 1, 42, 2, 8, 1, 238, 1, 2, 1, 4, 1, 0, 1, 0, 16, 16, 16, 0, 2, 0, 1, 226, 1, 149, 5, - 0, 3, 1, 2, 5, 4, 40, 3, 4, 1, 165, 2, 0, 4, 65, 5, 0, 2, 79, 4, 70, 11, 49, 4, 123, 1, 54, - 15, 41, 1, 2, 2, 10, 3, 49, 4, 2, 2, 7, 1, 61, 3, 36, 5, 1, 8, 62, 1, 12, 2, 52, 9, 1, 1, 8, - 4, 2, 1, 95, 3, 2, 4, 6, 1, 2, 1, 157, 1, 3, 8, 21, 2, 57, 2, 1, 1, 1, 1, 12, 1, 9, 1, 14, - 7, 3, 5, 67, 1, 2, 6, 1, 1, 2, 1, 1, 3, 4, 3, 1, 1, 14, 2, 85, 8, 2, 3, 1, 1, 23, 1, 81, 1, - 2, 6, 1, 1, 2, 1, 1, 2, 1, 2, 235, 1, 2, 4, 6, 2, 1, 2, 27, 2, 85, 8, 2, 1, 1, 2, 106, 1, 1, - 1, 2, 8, 101, 1, 1, 1, 2, 4, 1, 5, 0, 9, 1, 2, 245, 1, 10, 4, 4, 1, 144, 4, 2, 2, 4, 1, 32, - 10, 40, 6, 2, 4, 8, 1, 9, 6, 2, 3, 46, 13, 1, 2, 0, 7, 1, 6, 1, 1, 82, 22, 2, 7, 1, 2, 1, 2, - 122, 6, 3, 1, 1, 2, 1, 7, 1, 1, 72, 2, 3, 1, 1, 1, 0, 2, 11, 2, 52, 5, 5, 3, 23, 1, 0, 1, 6, - 15, 0, 12, 3, 3, 0, 5, 59, 7, 0, 1, 63, 4, 81, 1, 11, 2, 0, 2, 0, 46, 2, 23, 0, 5, 3, 6, 8, - 8, 2, 7, 30, 4, 148, 3, 0, 55, 4, 50, 8, 1, 14, 1, 22, 5, 1, 15, 0, 7, 1, 17, 2, 7, 1, 2, 1, - 5, 100, 1, 160, 7, 0, 1, 61, 4, 0, 4, 254, 2, 0, 7, 109, 7, 0, 96, 128, 240, 0, + 1, 6, 3, 219, 2, 2, 1, 58, 1, 1, 7, 1, 1, 1, 1, 2, 8, 6, 10, 2, 1, 48, 46, 2, 12, 20, 4, 48, + 10, 4, 3, 38, 9, 12, 2, 32, 4, 2, 6, 56, 1, 1, 2, 3, 1, 1, 5, 56, 8, 2, 2, 152, 3, 1, 13, 1, + 7, 4, 1, 6, 1, 3, 2, 198, 64, 0, 1, 195, 33, 0, 3, 141, 1, 96, 32, 0, 6, 105, 2, 0, 4, 1, + 10, 32, 2, 80, 2, 0, 1, 3, 1, 4, 1, 25, 2, 5, 1, 151, 2, 26, 18, 13, 1, 38, 8, 25, 11, 1, 1, + 44, 3, 48, 1, 2, 4, 2, 2, 2, 1, 36, 1, 67, 6, 2, 2, 2, 2, 12, 1, 8, 1, 47, 1, 51, 1, 1, 3, + 2, 2, 5, 2, 1, 1, 42, 2, 8, 1, 238, 1, 2, 1, 4, 1, 0, 1, 0, 16, 16, 16, 0, 2, 0, 1, 226, 1, + 149, 5, 0, 3, 1, 2, 5, 4, 40, 3, 4, 1, 165, 2, 0, 4, 65, 5, 0, 2, 77, 6, 70, 11, 49, 4, 123, + 1, 54, 15, 41, 1, 2, 2, 10, 3, 49, 4, 2, 2, 7, 1, 61, 3, 36, 5, 1, 8, 62, 1, 12, 2, 52, 9, + 1, 1, 8, 4, 2, 1, 95, 3, 2, 4, 6, 1, 2, 1, 157, 1, 3, 8, 21, 2, 57, 2, 1, 1, 1, 1, 12, 1, 9, + 1, 14, 7, 3, 5, 67, 1, 2, 6, 1, 1, 2, 1, 1, 3, 4, 3, 1, 1, 14, 2, 85, 8, 2, 3, 1, 1, 23, 1, + 81, 1, 2, 6, 1, 1, 2, 1, 1, 2, 1, 2, 235, 1, 2, 4, 6, 2, 1, 2, 27, 2, 85, 8, 2, 1, 1, 2, + 106, 1, 1, 1, 2, 8, 101, 1, 1, 1, 2, 4, 1, 5, 0, 9, 1, 2, 245, 1, 10, 4, 4, 1, 144, 4, 2, 2, + 4, 1, 32, 10, 40, 6, 2, 4, 8, 1, 9, 6, 2, 3, 46, 13, 1, 2, 198, 1, 1, 3, 1, 1, 201, 7, 1, 6, + 1, 1, 82, 22, 2, 7, 1, 2, 1, 2, 122, 6, 3, 1, 1, 2, 1, 7, 1, 1, 72, 2, 3, 1, 1, 1, 0, 2, 11, + 2, 52, 5, 5, 3, 23, 1, 0, 1, 6, 15, 0, 12, 3, 3, 0, 5, 59, 7, 0, 1, 63, 4, 81, 1, 11, 2, 0, + 2, 0, 46, 2, 23, 0, 5, 3, 6, 8, 8, 2, 7, 30, 4, 148, 3, 0, 55, 4, 50, 8, 1, 14, 1, 22, 5, 1, + 15, 0, 7, 1, 17, 2, 7, 1, 2, 1, 5, 100, 1, 160, 7, 0, 1, 61, 4, 0, 4, 254, 2, 243, 1, 2, 1, + 7, 2, 5, 1, 0, 7, 109, 7, 0, 96, 128, 240, 0, ]; #[inline] pub fn lookup(c: char) -> bool { @@ -475,27 +475,27 @@ pub mod lowercase { ]; static BITSET_INDEX_CHUNKS: [[u8; 16]; 20] = [ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 14, 56, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 43, 0, 52, 48, 50, 33], - [0, 0, 0, 0, 9, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 3, 0, 16, 58, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27], - [0, 0, 0, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 71, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 34, 17, 23, 53, 54, 49, 47, 7, 35, 42, 0, 28, 12, 31], - [0, 0, 46, 0, 56, 56, 56, 0, 22, 22, 69, 22, 36, 25, 24, 37], - [0, 5, 70, 0, 29, 15, 75, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [10, 60, 0, 6, 0, 0, 30, 0, 0, 0, 0, 0, 0, 0, 32, 0], - [16, 26, 22, 38, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [16, 51, 2, 21, 68, 8, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [16, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [65, 41, 55, 11, 66, 63, 18, 13, 1, 64, 76, 20, 73, 74, 4, 45], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 14, 57, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 19, 62, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 69, 44, 0, 53, 49, 51, 34], + [0, 0, 0, 0, 9, 58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 3, 0, 16, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28], + [0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 73, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 35, 17, 24, 54, 55, 50, 48, 7, 36, 43, 0, 29, 12, 32], + [0, 0, 47, 0, 57, 57, 57, 0, 23, 23, 71, 23, 37, 26, 25, 38], + [0, 5, 72, 0, 30, 15, 77, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [10, 61, 0, 6, 0, 0, 31, 0, 0, 0, 0, 0, 0, 0, 33, 0], + [16, 27, 23, 39, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [16, 52, 2, 22, 70, 8, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [16, 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [67, 42, 56, 11, 68, 65, 18, 13, 1, 66, 78, 21, 75, 76, 4, 46], ]; - static BITSET_CANONICAL: [u64; 56] = [ + static BITSET_CANONICAL: [u64; 57] = [ 0b0000000000000000000000000000000000000000000000000000000000000000, 0b0000111111111111111111111111110000000000000000000000000011111111, 0b1010101010101010101010101010101010101010101010101010100000000010, @@ -515,6 +515,7 @@ pub mod lowercase { 0b1111111111111111000000000000000000000000000000000000000000000000, 0b1111111101111111111111111111111110000000000000000000000000000000, 0b1111110000000000000000000000000011111111111111111111111111000000, + 0b1111100000000000000000000000000000000000000000000000000000000000, 0b1111011111111111111111111111111111111111111111110000000000000000, 0b1111000000000000000000000000001111110111111111111111111111111100, 0b1010101010101010101010101010101010101010101010101101010101010100, @@ -529,9 +530,9 @@ pub mod lowercase { 0b0001101111111011111111111111101111111111100000000000000000000000, 0b0001100100101111101010101010101010101010111000110111111111111111, 0b0000011111111101111111111111111111111111111111111111111110111001, - 0b0000011101011100000000000000000000001010101010100010010100001010, + 0b0000011101011110000000000000000000001010101010101010010100001010, 0b0000010000100000000001000000000000000000000000000000000000000000, - 0b0000000111111111111111111111111111111111111011111111111111111111, + 0b0000000111111111111111111111111111111111110011111111111111111111, 0b0000000011111111000000001111111100000000001111110000000011111111, 0b0000000011011100000000001111111100000000110011110000000011011100, 0b0000000000001000010100000001101010101010101010101010101010101010, @@ -553,10 +554,10 @@ pub mod lowercase { 0b1110011001010001001011010010101001001110001001000011000100101001, 0b1110101111000000000000000000000000001111111111111111111111111100, ]; - static BITSET_MAPPING: [(u8, u8); 21] = [ - (0, 64), (1, 184), (1, 182), (1, 179), (1, 172), (1, 161), (1, 146), (1, 144), (1, 140), - (1, 136), (1, 132), (2, 146), (2, 144), (2, 83), (3, 93), (3, 147), (3, 133), (4, 12), - (4, 6), (5, 187), (6, 78), + static BITSET_MAPPING: [(u8, u8); 22] = [ + (0, 64), (1, 184), (1, 182), (1, 179), (1, 172), (1, 168), (1, 161), (1, 146), (1, 144), + (1, 140), (1, 136), (1, 132), (2, 146), (2, 144), (2, 83), (3, 93), (3, 147), (3, 133), + (4, 12), (4, 6), (5, 187), (6, 78), ]; pub const fn lookup(c: char) -> bool { @@ -576,7 +577,7 @@ pub mod lowercase { pub mod n { use super::ShortOffsetRunHeader; - static SHORT_OFFSET_RUNS: [ShortOffsetRunHeader; 42] = [ + static SHORT_OFFSET_RUNS: [ShortOffsetRunHeader; 43] = [ ShortOffsetRunHeader::new(0, 1632), ShortOffsetRunHeader::new(7, 2406), ShortOffsetRunHeader::new(13, 4160), ShortOffsetRunHeader::new(47, 4969), ShortOffsetRunHeader::new(51, 5870), ShortOffsetRunHeader::new(53, 6470), @@ -590,16 +591,17 @@ pub mod n { ShortOffsetRunHeader::new(181, 69216), ShortOffsetRunHeader::new(187, 70736), ShortOffsetRunHeader::new(207, 71248), ShortOffsetRunHeader::new(211, 71904), ShortOffsetRunHeader::new(219, 72688), ShortOffsetRunHeader::new(223, 73552), - ShortOffsetRunHeader::new(231, 74752), ShortOffsetRunHeader::new(235, 90416), - ShortOffsetRunHeader::new(237, 92768), ShortOffsetRunHeader::new(239, 93552), - ShortOffsetRunHeader::new(247, 93824), ShortOffsetRunHeader::new(249, 118000), - ShortOffsetRunHeader::new(251, 119488), ShortOffsetRunHeader::new(253, 120782), - ShortOffsetRunHeader::new(259, 123200), ShortOffsetRunHeader::new(261, 123632), - ShortOffsetRunHeader::new(263, 124144), ShortOffsetRunHeader::new(265, 125127), - ShortOffsetRunHeader::new(269, 126065), ShortOffsetRunHeader::new(273, 127232), - ShortOffsetRunHeader::new(283, 130032), ShortOffsetRunHeader::new(285, 1244154), + ShortOffsetRunHeader::new(233, 74752), ShortOffsetRunHeader::new(237, 90416), + ShortOffsetRunHeader::new(239, 92768), ShortOffsetRunHeader::new(241, 93552), + ShortOffsetRunHeader::new(249, 93824), ShortOffsetRunHeader::new(251, 94196), + ShortOffsetRunHeader::new(253, 118000), ShortOffsetRunHeader::new(255, 119488), + ShortOffsetRunHeader::new(257, 120782), ShortOffsetRunHeader::new(263, 123200), + ShortOffsetRunHeader::new(265, 123632), ShortOffsetRunHeader::new(267, 124144), + ShortOffsetRunHeader::new(269, 125127), ShortOffsetRunHeader::new(273, 126065), + ShortOffsetRunHeader::new(277, 127232), ShortOffsetRunHeader::new(287, 130032), + ShortOffsetRunHeader::new(289, 1244154), ]; - static OFFSETS: [u8; 287] = [ + static OFFSETS: [u8; 291] = [ 178, 2, 5, 1, 2, 3, 0, 10, 134, 10, 198, 10, 0, 10, 118, 10, 4, 6, 108, 10, 118, 10, 118, 10, 2, 6, 110, 13, 115, 10, 8, 7, 103, 10, 104, 7, 7, 19, 109, 10, 96, 10, 118, 10, 70, 20, 0, 10, 70, 10, 0, 20, 0, 3, 239, 10, 6, 10, 22, 10, 0, 10, 128, 11, 165, 10, 6, 10, 182, 10, @@ -609,9 +611,9 @@ pub mod n { 1, 134, 5, 202, 10, 0, 8, 25, 7, 39, 9, 75, 5, 22, 6, 160, 2, 2, 16, 2, 46, 64, 9, 52, 2, 30, 3, 75, 5, 104, 8, 24, 8, 41, 7, 0, 6, 48, 10, 6, 10, 0, 31, 158, 10, 42, 4, 112, 7, 134, 30, 128, 10, 60, 10, 144, 10, 7, 20, 251, 10, 0, 10, 118, 10, 0, 10, 102, 10, 6, 20, 76, 12, - 0, 19, 93, 10, 0, 10, 86, 29, 227, 10, 70, 10, 0, 10, 102, 21, 0, 111, 0, 10, 0, 10, 86, 10, - 134, 10, 1, 7, 0, 10, 0, 23, 0, 10, 0, 20, 12, 20, 108, 25, 0, 50, 0, 10, 0, 10, 0, 10, 247, - 10, 0, 9, 128, 10, 0, 59, 1, 3, 1, 4, 76, 45, 1, 15, 0, 13, 0, 10, 0, + 0, 19, 93, 10, 0, 10, 86, 29, 227, 10, 70, 10, 54, 10, 0, 10, 102, 21, 0, 111, 0, 10, 0, 10, + 86, 10, 134, 10, 1, 7, 0, 10, 0, 23, 0, 3, 0, 10, 0, 20, 12, 20, 108, 25, 0, 50, 0, 10, 0, + 10, 0, 10, 247, 10, 0, 9, 128, 10, 0, 59, 1, 3, 1, 4, 76, 45, 1, 15, 0, 13, 0, 10, 0, ]; #[inline] pub fn lookup(c: char) -> bool { @@ -647,28 +649,28 @@ pub mod uppercase { static BITSET_INDEX_CHUNKS: [[u8; 16]; 17] = [ [44, 44, 5, 35, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 5, 0], [44, 44, 5, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44], - [44, 44, 40, 44, 44, 44, 44, 44, 17, 17, 62, 17, 43, 29, 24, 23], + [44, 44, 40, 44, 44, 44, 44, 44, 17, 17, 66, 17, 43, 29, 24, 23], [44, 44, 44, 32, 36, 21, 22, 15, 13, 34, 44, 44, 44, 11, 30, 39], [44, 44, 44, 44, 9, 8, 45, 44, 44, 44, 44, 44, 44, 44, 44, 44], - [44, 44, 44, 44, 37, 28, 66, 44, 44, 44, 44, 44, 44, 44, 44, 44], + [44, 44, 44, 44, 37, 28, 67, 44, 44, 44, 44, 44, 44, 44, 44, 44], [44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44], [44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 57, 44, 44, 44], - [44, 44, 44, 44, 44, 44, 44, 44, 44, 49, 44, 44, 44, 44, 44, 44], - [44, 44, 44, 44, 44, 44, 44, 44, 44, 61, 60, 44, 20, 14, 16, 4], + [44, 44, 44, 44, 44, 44, 44, 44, 44, 49, 63, 44, 44, 44, 44, 44], + [44, 44, 44, 44, 44, 44, 44, 44, 44, 65, 64, 44, 20, 14, 16, 4], [44, 44, 44, 44, 50, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44], [44, 44, 53, 44, 44, 31, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44], [44, 44, 54, 46, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44], [51, 44, 9, 47, 44, 42, 33, 44, 44, 44, 44, 44, 44, 44, 44, 44], - [52, 19, 2, 18, 10, 48, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44], + [52, 19, 3, 18, 10, 48, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44], [52, 38, 17, 27, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44], - [58, 1, 26, 55, 12, 7, 25, 56, 41, 59, 6, 3, 65, 64, 63, 67], + [58, 1, 26, 55, 12, 7, 25, 56, 41, 59, 6, 2, 62, 61, 60, 68], ]; static BITSET_CANONICAL: [u64; 44] = [ 0b0000000000111111111111111111111111111111111111111111111111111111, 0b1111111111111111111111110000000000000000000000000011111111111111, - 0b0101010101010101010101010101010101010101010101010101010000000001, 0b0000011111111111111111111111110000000000000000000000000000000001, - 0b0000000000100000000000000000000000010101010000010001101011110101, + 0b0101010101010101010101010101010101010101010101010101010000000001, + 0b0000000000100000000000000000000000010101010101010101101011110101, 0b1111111111111111111111111111111100000000000000000000000000000000, 0b1111111111111111111111110000000000000000000000000000001111111111, 0b1111111111111111111100000000000000000000000000011111110001011111, @@ -709,10 +711,10 @@ pub mod uppercase { 0b1111011111111111000000000000000000000000000000000000000000000000, 0b1111111100000000111111110000000000111111000000001111111100000000, ]; - static BITSET_MAPPING: [(u8, u8); 24] = [ + static BITSET_MAPPING: [(u8, u8); 25] = [ (0, 182), (0, 74), (0, 166), (0, 162), (0, 159), (0, 150), (0, 148), (0, 142), (0, 134), - (0, 131), (0, 64), (1, 66), (1, 70), (1, 83), (1, 12), (1, 8), (2, 164), (2, 146), (2, 20), - (3, 146), (3, 140), (3, 134), (4, 178), (4, 171), + (0, 131), (0, 64), (1, 66), (1, 70), (1, 83), (1, 12), (1, 8), (2, 146), (2, 140), (2, 134), + (2, 130), (3, 164), (3, 146), (3, 20), (4, 178), (4, 171), ]; pub const fn lookup(c: char) -> bool { @@ -792,7 +794,7 @@ pub mod conversions { } } - static LOWERCASE_TABLE: &[(char, u32); 1434] = &[ + static LOWERCASE_TABLE: &[(char, u32); 1462] = &[ ('\u{c0}', 224), ('\u{c1}', 225), ('\u{c2}', 226), ('\u{c3}', 227), ('\u{c4}', 228), ('\u{c5}', 229), ('\u{c6}', 230), ('\u{c7}', 231), ('\u{c8}', 232), ('\u{c9}', 233), ('\u{ca}', 234), ('\u{cb}', 235), ('\u{cc}', 236), ('\u{cd}', 237), ('\u{ce}', 238), @@ -1060,77 +1062,84 @@ pub mod conversions { ('\u{a7b4}', 42933), ('\u{a7b6}', 42935), ('\u{a7b8}', 42937), ('\u{a7ba}', 42939), ('\u{a7bc}', 42941), ('\u{a7be}', 42943), ('\u{a7c0}', 42945), ('\u{a7c2}', 42947), ('\u{a7c4}', 42900), ('\u{a7c5}', 642), ('\u{a7c6}', 7566), ('\u{a7c7}', 42952), - ('\u{a7c9}', 42954), ('\u{a7cb}', 612), ('\u{a7cc}', 42957), ('\u{a7d0}', 42961), - ('\u{a7d6}', 42967), ('\u{a7d8}', 42969), ('\u{a7da}', 42971), ('\u{a7dc}', 411), - ('\u{a7f5}', 42998), ('\u{ff21}', 65345), ('\u{ff22}', 65346), ('\u{ff23}', 65347), - ('\u{ff24}', 65348), ('\u{ff25}', 65349), ('\u{ff26}', 65350), ('\u{ff27}', 65351), - ('\u{ff28}', 65352), ('\u{ff29}', 65353), ('\u{ff2a}', 65354), ('\u{ff2b}', 65355), - ('\u{ff2c}', 65356), ('\u{ff2d}', 65357), ('\u{ff2e}', 65358), ('\u{ff2f}', 65359), - ('\u{ff30}', 65360), ('\u{ff31}', 65361), ('\u{ff32}', 65362), ('\u{ff33}', 65363), - ('\u{ff34}', 65364), ('\u{ff35}', 65365), ('\u{ff36}', 65366), ('\u{ff37}', 65367), - ('\u{ff38}', 65368), ('\u{ff39}', 65369), ('\u{ff3a}', 65370), ('\u{10400}', 66600), - ('\u{10401}', 66601), ('\u{10402}', 66602), ('\u{10403}', 66603), ('\u{10404}', 66604), - ('\u{10405}', 66605), ('\u{10406}', 66606), ('\u{10407}', 66607), ('\u{10408}', 66608), - ('\u{10409}', 66609), ('\u{1040a}', 66610), ('\u{1040b}', 66611), ('\u{1040c}', 66612), - ('\u{1040d}', 66613), ('\u{1040e}', 66614), ('\u{1040f}', 66615), ('\u{10410}', 66616), - ('\u{10411}', 66617), ('\u{10412}', 66618), ('\u{10413}', 66619), ('\u{10414}', 66620), - ('\u{10415}', 66621), ('\u{10416}', 66622), ('\u{10417}', 66623), ('\u{10418}', 66624), - ('\u{10419}', 66625), ('\u{1041a}', 66626), ('\u{1041b}', 66627), ('\u{1041c}', 66628), - ('\u{1041d}', 66629), ('\u{1041e}', 66630), ('\u{1041f}', 66631), ('\u{10420}', 66632), - ('\u{10421}', 66633), ('\u{10422}', 66634), ('\u{10423}', 66635), ('\u{10424}', 66636), - ('\u{10425}', 66637), ('\u{10426}', 66638), ('\u{10427}', 66639), ('\u{104b0}', 66776), - ('\u{104b1}', 66777), ('\u{104b2}', 66778), ('\u{104b3}', 66779), ('\u{104b4}', 66780), - ('\u{104b5}', 66781), ('\u{104b6}', 66782), ('\u{104b7}', 66783), ('\u{104b8}', 66784), - ('\u{104b9}', 66785), ('\u{104ba}', 66786), ('\u{104bb}', 66787), ('\u{104bc}', 66788), - ('\u{104bd}', 66789), ('\u{104be}', 66790), ('\u{104bf}', 66791), ('\u{104c0}', 66792), - ('\u{104c1}', 66793), ('\u{104c2}', 66794), ('\u{104c3}', 66795), ('\u{104c4}', 66796), - ('\u{104c5}', 66797), ('\u{104c6}', 66798), ('\u{104c7}', 66799), ('\u{104c8}', 66800), - ('\u{104c9}', 66801), ('\u{104ca}', 66802), ('\u{104cb}', 66803), ('\u{104cc}', 66804), - ('\u{104cd}', 66805), ('\u{104ce}', 66806), ('\u{104cf}', 66807), ('\u{104d0}', 66808), - ('\u{104d1}', 66809), ('\u{104d2}', 66810), ('\u{104d3}', 66811), ('\u{10570}', 66967), - ('\u{10571}', 66968), ('\u{10572}', 66969), ('\u{10573}', 66970), ('\u{10574}', 66971), - ('\u{10575}', 66972), ('\u{10576}', 66973), ('\u{10577}', 66974), ('\u{10578}', 66975), - ('\u{10579}', 66976), ('\u{1057a}', 66977), ('\u{1057c}', 66979), ('\u{1057d}', 66980), - ('\u{1057e}', 66981), ('\u{1057f}', 66982), ('\u{10580}', 66983), ('\u{10581}', 66984), - ('\u{10582}', 66985), ('\u{10583}', 66986), ('\u{10584}', 66987), ('\u{10585}', 66988), - ('\u{10586}', 66989), ('\u{10587}', 66990), ('\u{10588}', 66991), ('\u{10589}', 66992), - ('\u{1058a}', 66993), ('\u{1058c}', 66995), ('\u{1058d}', 66996), ('\u{1058e}', 66997), - ('\u{1058f}', 66998), ('\u{10590}', 66999), ('\u{10591}', 67000), ('\u{10592}', 67001), - ('\u{10594}', 67003), ('\u{10595}', 67004), ('\u{10c80}', 68800), ('\u{10c81}', 68801), - ('\u{10c82}', 68802), ('\u{10c83}', 68803), ('\u{10c84}', 68804), ('\u{10c85}', 68805), - ('\u{10c86}', 68806), ('\u{10c87}', 68807), ('\u{10c88}', 68808), ('\u{10c89}', 68809), - ('\u{10c8a}', 68810), ('\u{10c8b}', 68811), ('\u{10c8c}', 68812), ('\u{10c8d}', 68813), - ('\u{10c8e}', 68814), ('\u{10c8f}', 68815), ('\u{10c90}', 68816), ('\u{10c91}', 68817), - ('\u{10c92}', 68818), ('\u{10c93}', 68819), ('\u{10c94}', 68820), ('\u{10c95}', 68821), - ('\u{10c96}', 68822), ('\u{10c97}', 68823), ('\u{10c98}', 68824), ('\u{10c99}', 68825), - ('\u{10c9a}', 68826), ('\u{10c9b}', 68827), ('\u{10c9c}', 68828), ('\u{10c9d}', 68829), - ('\u{10c9e}', 68830), ('\u{10c9f}', 68831), ('\u{10ca0}', 68832), ('\u{10ca1}', 68833), - ('\u{10ca2}', 68834), ('\u{10ca3}', 68835), ('\u{10ca4}', 68836), ('\u{10ca5}', 68837), - ('\u{10ca6}', 68838), ('\u{10ca7}', 68839), ('\u{10ca8}', 68840), ('\u{10ca9}', 68841), - ('\u{10caa}', 68842), ('\u{10cab}', 68843), ('\u{10cac}', 68844), ('\u{10cad}', 68845), - ('\u{10cae}', 68846), ('\u{10caf}', 68847), ('\u{10cb0}', 68848), ('\u{10cb1}', 68849), - ('\u{10cb2}', 68850), ('\u{10d50}', 68976), ('\u{10d51}', 68977), ('\u{10d52}', 68978), - ('\u{10d53}', 68979), ('\u{10d54}', 68980), ('\u{10d55}', 68981), ('\u{10d56}', 68982), - ('\u{10d57}', 68983), ('\u{10d58}', 68984), ('\u{10d59}', 68985), ('\u{10d5a}', 68986), - ('\u{10d5b}', 68987), ('\u{10d5c}', 68988), ('\u{10d5d}', 68989), ('\u{10d5e}', 68990), - ('\u{10d5f}', 68991), ('\u{10d60}', 68992), ('\u{10d61}', 68993), ('\u{10d62}', 68994), - ('\u{10d63}', 68995), ('\u{10d64}', 68996), ('\u{10d65}', 68997), ('\u{118a0}', 71872), - ('\u{118a1}', 71873), ('\u{118a2}', 71874), ('\u{118a3}', 71875), ('\u{118a4}', 71876), - ('\u{118a5}', 71877), ('\u{118a6}', 71878), ('\u{118a7}', 71879), ('\u{118a8}', 71880), - ('\u{118a9}', 71881), ('\u{118aa}', 71882), ('\u{118ab}', 71883), ('\u{118ac}', 71884), - ('\u{118ad}', 71885), ('\u{118ae}', 71886), ('\u{118af}', 71887), ('\u{118b0}', 71888), - ('\u{118b1}', 71889), ('\u{118b2}', 71890), ('\u{118b3}', 71891), ('\u{118b4}', 71892), - ('\u{118b5}', 71893), ('\u{118b6}', 71894), ('\u{118b7}', 71895), ('\u{118b8}', 71896), - ('\u{118b9}', 71897), ('\u{118ba}', 71898), ('\u{118bb}', 71899), ('\u{118bc}', 71900), - ('\u{118bd}', 71901), ('\u{118be}', 71902), ('\u{118bf}', 71903), ('\u{16e40}', 93792), - ('\u{16e41}', 93793), ('\u{16e42}', 93794), ('\u{16e43}', 93795), ('\u{16e44}', 93796), - ('\u{16e45}', 93797), ('\u{16e46}', 93798), ('\u{16e47}', 93799), ('\u{16e48}', 93800), - ('\u{16e49}', 93801), ('\u{16e4a}', 93802), ('\u{16e4b}', 93803), ('\u{16e4c}', 93804), - ('\u{16e4d}', 93805), ('\u{16e4e}', 93806), ('\u{16e4f}', 93807), ('\u{16e50}', 93808), - ('\u{16e51}', 93809), ('\u{16e52}', 93810), ('\u{16e53}', 93811), ('\u{16e54}', 93812), - ('\u{16e55}', 93813), ('\u{16e56}', 93814), ('\u{16e57}', 93815), ('\u{16e58}', 93816), - ('\u{16e59}', 93817), ('\u{16e5a}', 93818), ('\u{16e5b}', 93819), ('\u{16e5c}', 93820), - ('\u{16e5d}', 93821), ('\u{16e5e}', 93822), ('\u{16e5f}', 93823), ('\u{1e900}', 125218), + ('\u{a7c9}', 42954), ('\u{a7cb}', 612), ('\u{a7cc}', 42957), ('\u{a7ce}', 42959), + ('\u{a7d0}', 42961), ('\u{a7d2}', 42963), ('\u{a7d4}', 42965), ('\u{a7d6}', 42967), + ('\u{a7d8}', 42969), ('\u{a7da}', 42971), ('\u{a7dc}', 411), ('\u{a7f5}', 42998), + ('\u{ff21}', 65345), ('\u{ff22}', 65346), ('\u{ff23}', 65347), ('\u{ff24}', 65348), + ('\u{ff25}', 65349), ('\u{ff26}', 65350), ('\u{ff27}', 65351), ('\u{ff28}', 65352), + ('\u{ff29}', 65353), ('\u{ff2a}', 65354), ('\u{ff2b}', 65355), ('\u{ff2c}', 65356), + ('\u{ff2d}', 65357), ('\u{ff2e}', 65358), ('\u{ff2f}', 65359), ('\u{ff30}', 65360), + ('\u{ff31}', 65361), ('\u{ff32}', 65362), ('\u{ff33}', 65363), ('\u{ff34}', 65364), + ('\u{ff35}', 65365), ('\u{ff36}', 65366), ('\u{ff37}', 65367), ('\u{ff38}', 65368), + ('\u{ff39}', 65369), ('\u{ff3a}', 65370), ('\u{10400}', 66600), ('\u{10401}', 66601), + ('\u{10402}', 66602), ('\u{10403}', 66603), ('\u{10404}', 66604), ('\u{10405}', 66605), + ('\u{10406}', 66606), ('\u{10407}', 66607), ('\u{10408}', 66608), ('\u{10409}', 66609), + ('\u{1040a}', 66610), ('\u{1040b}', 66611), ('\u{1040c}', 66612), ('\u{1040d}', 66613), + ('\u{1040e}', 66614), ('\u{1040f}', 66615), ('\u{10410}', 66616), ('\u{10411}', 66617), + ('\u{10412}', 66618), ('\u{10413}', 66619), ('\u{10414}', 66620), ('\u{10415}', 66621), + ('\u{10416}', 66622), ('\u{10417}', 66623), ('\u{10418}', 66624), ('\u{10419}', 66625), + ('\u{1041a}', 66626), ('\u{1041b}', 66627), ('\u{1041c}', 66628), ('\u{1041d}', 66629), + ('\u{1041e}', 66630), ('\u{1041f}', 66631), ('\u{10420}', 66632), ('\u{10421}', 66633), + ('\u{10422}', 66634), ('\u{10423}', 66635), ('\u{10424}', 66636), ('\u{10425}', 66637), + ('\u{10426}', 66638), ('\u{10427}', 66639), ('\u{104b0}', 66776), ('\u{104b1}', 66777), + ('\u{104b2}', 66778), ('\u{104b3}', 66779), ('\u{104b4}', 66780), ('\u{104b5}', 66781), + ('\u{104b6}', 66782), ('\u{104b7}', 66783), ('\u{104b8}', 66784), ('\u{104b9}', 66785), + ('\u{104ba}', 66786), ('\u{104bb}', 66787), ('\u{104bc}', 66788), ('\u{104bd}', 66789), + ('\u{104be}', 66790), ('\u{104bf}', 66791), ('\u{104c0}', 66792), ('\u{104c1}', 66793), + ('\u{104c2}', 66794), ('\u{104c3}', 66795), ('\u{104c4}', 66796), ('\u{104c5}', 66797), + ('\u{104c6}', 66798), ('\u{104c7}', 66799), ('\u{104c8}', 66800), ('\u{104c9}', 66801), + ('\u{104ca}', 66802), ('\u{104cb}', 66803), ('\u{104cc}', 66804), ('\u{104cd}', 66805), + ('\u{104ce}', 66806), ('\u{104cf}', 66807), ('\u{104d0}', 66808), ('\u{104d1}', 66809), + ('\u{104d2}', 66810), ('\u{104d3}', 66811), ('\u{10570}', 66967), ('\u{10571}', 66968), + ('\u{10572}', 66969), ('\u{10573}', 66970), ('\u{10574}', 66971), ('\u{10575}', 66972), + ('\u{10576}', 66973), ('\u{10577}', 66974), ('\u{10578}', 66975), ('\u{10579}', 66976), + ('\u{1057a}', 66977), ('\u{1057c}', 66979), ('\u{1057d}', 66980), ('\u{1057e}', 66981), + ('\u{1057f}', 66982), ('\u{10580}', 66983), ('\u{10581}', 66984), ('\u{10582}', 66985), + ('\u{10583}', 66986), ('\u{10584}', 66987), ('\u{10585}', 66988), ('\u{10586}', 66989), + ('\u{10587}', 66990), ('\u{10588}', 66991), ('\u{10589}', 66992), ('\u{1058a}', 66993), + ('\u{1058c}', 66995), ('\u{1058d}', 66996), ('\u{1058e}', 66997), ('\u{1058f}', 66998), + ('\u{10590}', 66999), ('\u{10591}', 67000), ('\u{10592}', 67001), ('\u{10594}', 67003), + ('\u{10595}', 67004), ('\u{10c80}', 68800), ('\u{10c81}', 68801), ('\u{10c82}', 68802), + ('\u{10c83}', 68803), ('\u{10c84}', 68804), ('\u{10c85}', 68805), ('\u{10c86}', 68806), + ('\u{10c87}', 68807), ('\u{10c88}', 68808), ('\u{10c89}', 68809), ('\u{10c8a}', 68810), + ('\u{10c8b}', 68811), ('\u{10c8c}', 68812), ('\u{10c8d}', 68813), ('\u{10c8e}', 68814), + ('\u{10c8f}', 68815), ('\u{10c90}', 68816), ('\u{10c91}', 68817), ('\u{10c92}', 68818), + ('\u{10c93}', 68819), ('\u{10c94}', 68820), ('\u{10c95}', 68821), ('\u{10c96}', 68822), + ('\u{10c97}', 68823), ('\u{10c98}', 68824), ('\u{10c99}', 68825), ('\u{10c9a}', 68826), + ('\u{10c9b}', 68827), ('\u{10c9c}', 68828), ('\u{10c9d}', 68829), ('\u{10c9e}', 68830), + ('\u{10c9f}', 68831), ('\u{10ca0}', 68832), ('\u{10ca1}', 68833), ('\u{10ca2}', 68834), + ('\u{10ca3}', 68835), ('\u{10ca4}', 68836), ('\u{10ca5}', 68837), ('\u{10ca6}', 68838), + ('\u{10ca7}', 68839), ('\u{10ca8}', 68840), ('\u{10ca9}', 68841), ('\u{10caa}', 68842), + ('\u{10cab}', 68843), ('\u{10cac}', 68844), ('\u{10cad}', 68845), ('\u{10cae}', 68846), + ('\u{10caf}', 68847), ('\u{10cb0}', 68848), ('\u{10cb1}', 68849), ('\u{10cb2}', 68850), + ('\u{10d50}', 68976), ('\u{10d51}', 68977), ('\u{10d52}', 68978), ('\u{10d53}', 68979), + ('\u{10d54}', 68980), ('\u{10d55}', 68981), ('\u{10d56}', 68982), ('\u{10d57}', 68983), + ('\u{10d58}', 68984), ('\u{10d59}', 68985), ('\u{10d5a}', 68986), ('\u{10d5b}', 68987), + ('\u{10d5c}', 68988), ('\u{10d5d}', 68989), ('\u{10d5e}', 68990), ('\u{10d5f}', 68991), + ('\u{10d60}', 68992), ('\u{10d61}', 68993), ('\u{10d62}', 68994), ('\u{10d63}', 68995), + ('\u{10d64}', 68996), ('\u{10d65}', 68997), ('\u{118a0}', 71872), ('\u{118a1}', 71873), + ('\u{118a2}', 71874), ('\u{118a3}', 71875), ('\u{118a4}', 71876), ('\u{118a5}', 71877), + ('\u{118a6}', 71878), ('\u{118a7}', 71879), ('\u{118a8}', 71880), ('\u{118a9}', 71881), + ('\u{118aa}', 71882), ('\u{118ab}', 71883), ('\u{118ac}', 71884), ('\u{118ad}', 71885), + ('\u{118ae}', 71886), ('\u{118af}', 71887), ('\u{118b0}', 71888), ('\u{118b1}', 71889), + ('\u{118b2}', 71890), ('\u{118b3}', 71891), ('\u{118b4}', 71892), ('\u{118b5}', 71893), + ('\u{118b6}', 71894), ('\u{118b7}', 71895), ('\u{118b8}', 71896), ('\u{118b9}', 71897), + ('\u{118ba}', 71898), ('\u{118bb}', 71899), ('\u{118bc}', 71900), ('\u{118bd}', 71901), + ('\u{118be}', 71902), ('\u{118bf}', 71903), ('\u{16e40}', 93792), ('\u{16e41}', 93793), + ('\u{16e42}', 93794), ('\u{16e43}', 93795), ('\u{16e44}', 93796), ('\u{16e45}', 93797), + ('\u{16e46}', 93798), ('\u{16e47}', 93799), ('\u{16e48}', 93800), ('\u{16e49}', 93801), + ('\u{16e4a}', 93802), ('\u{16e4b}', 93803), ('\u{16e4c}', 93804), ('\u{16e4d}', 93805), + ('\u{16e4e}', 93806), ('\u{16e4f}', 93807), ('\u{16e50}', 93808), ('\u{16e51}', 93809), + ('\u{16e52}', 93810), ('\u{16e53}', 93811), ('\u{16e54}', 93812), ('\u{16e55}', 93813), + ('\u{16e56}', 93814), ('\u{16e57}', 93815), ('\u{16e58}', 93816), ('\u{16e59}', 93817), + ('\u{16e5a}', 93818), ('\u{16e5b}', 93819), ('\u{16e5c}', 93820), ('\u{16e5d}', 93821), + ('\u{16e5e}', 93822), ('\u{16e5f}', 93823), ('\u{16ea0}', 93883), ('\u{16ea1}', 93884), + ('\u{16ea2}', 93885), ('\u{16ea3}', 93886), ('\u{16ea4}', 93887), ('\u{16ea5}', 93888), + ('\u{16ea6}', 93889), ('\u{16ea7}', 93890), ('\u{16ea8}', 93891), ('\u{16ea9}', 93892), + ('\u{16eaa}', 93893), ('\u{16eab}', 93894), ('\u{16eac}', 93895), ('\u{16ead}', 93896), + ('\u{16eae}', 93897), ('\u{16eaf}', 93898), ('\u{16eb0}', 93899), ('\u{16eb1}', 93900), + ('\u{16eb2}', 93901), ('\u{16eb3}', 93902), ('\u{16eb4}', 93903), ('\u{16eb5}', 93904), + ('\u{16eb6}', 93905), ('\u{16eb7}', 93906), ('\u{16eb8}', 93907), ('\u{1e900}', 125218), ('\u{1e901}', 125219), ('\u{1e902}', 125220), ('\u{1e903}', 125221), ('\u{1e904}', 125222), ('\u{1e905}', 125223), ('\u{1e906}', 125224), ('\u{1e907}', 125225), ('\u{1e908}', 125226), ('\u{1e909}', 125227), ('\u{1e90a}', 125228), ('\u{1e90b}', 125229), ('\u{1e90c}', 125230), @@ -1146,7 +1155,7 @@ pub mod conversions { ['i', '\u{307}', '\u{0}'], ]; - static UPPERCASE_TABLE: &[(char, u32); 1526] = &[ + static UPPERCASE_TABLE: &[(char, u32); 1554] = &[ ('\u{b5}', 924), ('\u{df}', 4194304), ('\u{e0}', 192), ('\u{e1}', 193), ('\u{e2}', 194), ('\u{e3}', 195), ('\u{e4}', 196), ('\u{e5}', 197), ('\u{e6}', 198), ('\u{e7}', 199), ('\u{e8}', 200), ('\u{e9}', 201), ('\u{ea}', 202), ('\u{eb}', 203), ('\u{ec}', 204), @@ -1415,100 +1424,107 @@ pub mod conversions { ('\u{a7a7}', 42918), ('\u{a7a9}', 42920), ('\u{a7b5}', 42932), ('\u{a7b7}', 42934), ('\u{a7b9}', 42936), ('\u{a7bb}', 42938), ('\u{a7bd}', 42940), ('\u{a7bf}', 42942), ('\u{a7c1}', 42944), ('\u{a7c3}', 42946), ('\u{a7c8}', 42951), ('\u{a7ca}', 42953), - ('\u{a7cd}', 42956), ('\u{a7d1}', 42960), ('\u{a7d7}', 42966), ('\u{a7d9}', 42968), - ('\u{a7db}', 42970), ('\u{a7f6}', 42997), ('\u{ab53}', 42931), ('\u{ab70}', 5024), - ('\u{ab71}', 5025), ('\u{ab72}', 5026), ('\u{ab73}', 5027), ('\u{ab74}', 5028), - ('\u{ab75}', 5029), ('\u{ab76}', 5030), ('\u{ab77}', 5031), ('\u{ab78}', 5032), - ('\u{ab79}', 5033), ('\u{ab7a}', 5034), ('\u{ab7b}', 5035), ('\u{ab7c}', 5036), - ('\u{ab7d}', 5037), ('\u{ab7e}', 5038), ('\u{ab7f}', 5039), ('\u{ab80}', 5040), - ('\u{ab81}', 5041), ('\u{ab82}', 5042), ('\u{ab83}', 5043), ('\u{ab84}', 5044), - ('\u{ab85}', 5045), ('\u{ab86}', 5046), ('\u{ab87}', 5047), ('\u{ab88}', 5048), - ('\u{ab89}', 5049), ('\u{ab8a}', 5050), ('\u{ab8b}', 5051), ('\u{ab8c}', 5052), - ('\u{ab8d}', 5053), ('\u{ab8e}', 5054), ('\u{ab8f}', 5055), ('\u{ab90}', 5056), - ('\u{ab91}', 5057), ('\u{ab92}', 5058), ('\u{ab93}', 5059), ('\u{ab94}', 5060), - ('\u{ab95}', 5061), ('\u{ab96}', 5062), ('\u{ab97}', 5063), ('\u{ab98}', 5064), - ('\u{ab99}', 5065), ('\u{ab9a}', 5066), ('\u{ab9b}', 5067), ('\u{ab9c}', 5068), - ('\u{ab9d}', 5069), ('\u{ab9e}', 5070), ('\u{ab9f}', 5071), ('\u{aba0}', 5072), - ('\u{aba1}', 5073), ('\u{aba2}', 5074), ('\u{aba3}', 5075), ('\u{aba4}', 5076), - ('\u{aba5}', 5077), ('\u{aba6}', 5078), ('\u{aba7}', 5079), ('\u{aba8}', 5080), - ('\u{aba9}', 5081), ('\u{abaa}', 5082), ('\u{abab}', 5083), ('\u{abac}', 5084), - ('\u{abad}', 5085), ('\u{abae}', 5086), ('\u{abaf}', 5087), ('\u{abb0}', 5088), - ('\u{abb1}', 5089), ('\u{abb2}', 5090), ('\u{abb3}', 5091), ('\u{abb4}', 5092), - ('\u{abb5}', 5093), ('\u{abb6}', 5094), ('\u{abb7}', 5095), ('\u{abb8}', 5096), - ('\u{abb9}', 5097), ('\u{abba}', 5098), ('\u{abbb}', 5099), ('\u{abbc}', 5100), - ('\u{abbd}', 5101), ('\u{abbe}', 5102), ('\u{abbf}', 5103), ('\u{fb00}', 4194394), - ('\u{fb01}', 4194395), ('\u{fb02}', 4194396), ('\u{fb03}', 4194397), ('\u{fb04}', 4194398), - ('\u{fb05}', 4194399), ('\u{fb06}', 4194400), ('\u{fb13}', 4194401), ('\u{fb14}', 4194402), - ('\u{fb15}', 4194403), ('\u{fb16}', 4194404), ('\u{fb17}', 4194405), ('\u{ff41}', 65313), - ('\u{ff42}', 65314), ('\u{ff43}', 65315), ('\u{ff44}', 65316), ('\u{ff45}', 65317), - ('\u{ff46}', 65318), ('\u{ff47}', 65319), ('\u{ff48}', 65320), ('\u{ff49}', 65321), - ('\u{ff4a}', 65322), ('\u{ff4b}', 65323), ('\u{ff4c}', 65324), ('\u{ff4d}', 65325), - ('\u{ff4e}', 65326), ('\u{ff4f}', 65327), ('\u{ff50}', 65328), ('\u{ff51}', 65329), - ('\u{ff52}', 65330), ('\u{ff53}', 65331), ('\u{ff54}', 65332), ('\u{ff55}', 65333), - ('\u{ff56}', 65334), ('\u{ff57}', 65335), ('\u{ff58}', 65336), ('\u{ff59}', 65337), - ('\u{ff5a}', 65338), ('\u{10428}', 66560), ('\u{10429}', 66561), ('\u{1042a}', 66562), - ('\u{1042b}', 66563), ('\u{1042c}', 66564), ('\u{1042d}', 66565), ('\u{1042e}', 66566), - ('\u{1042f}', 66567), ('\u{10430}', 66568), ('\u{10431}', 66569), ('\u{10432}', 66570), - ('\u{10433}', 66571), ('\u{10434}', 66572), ('\u{10435}', 66573), ('\u{10436}', 66574), - ('\u{10437}', 66575), ('\u{10438}', 66576), ('\u{10439}', 66577), ('\u{1043a}', 66578), - ('\u{1043b}', 66579), ('\u{1043c}', 66580), ('\u{1043d}', 66581), ('\u{1043e}', 66582), - ('\u{1043f}', 66583), ('\u{10440}', 66584), ('\u{10441}', 66585), ('\u{10442}', 66586), - ('\u{10443}', 66587), ('\u{10444}', 66588), ('\u{10445}', 66589), ('\u{10446}', 66590), - ('\u{10447}', 66591), ('\u{10448}', 66592), ('\u{10449}', 66593), ('\u{1044a}', 66594), - ('\u{1044b}', 66595), ('\u{1044c}', 66596), ('\u{1044d}', 66597), ('\u{1044e}', 66598), - ('\u{1044f}', 66599), ('\u{104d8}', 66736), ('\u{104d9}', 66737), ('\u{104da}', 66738), - ('\u{104db}', 66739), ('\u{104dc}', 66740), ('\u{104dd}', 66741), ('\u{104de}', 66742), - ('\u{104df}', 66743), ('\u{104e0}', 66744), ('\u{104e1}', 66745), ('\u{104e2}', 66746), - ('\u{104e3}', 66747), ('\u{104e4}', 66748), ('\u{104e5}', 66749), ('\u{104e6}', 66750), - ('\u{104e7}', 66751), ('\u{104e8}', 66752), ('\u{104e9}', 66753), ('\u{104ea}', 66754), - ('\u{104eb}', 66755), ('\u{104ec}', 66756), ('\u{104ed}', 66757), ('\u{104ee}', 66758), - ('\u{104ef}', 66759), ('\u{104f0}', 66760), ('\u{104f1}', 66761), ('\u{104f2}', 66762), - ('\u{104f3}', 66763), ('\u{104f4}', 66764), ('\u{104f5}', 66765), ('\u{104f6}', 66766), - ('\u{104f7}', 66767), ('\u{104f8}', 66768), ('\u{104f9}', 66769), ('\u{104fa}', 66770), - ('\u{104fb}', 66771), ('\u{10597}', 66928), ('\u{10598}', 66929), ('\u{10599}', 66930), - ('\u{1059a}', 66931), ('\u{1059b}', 66932), ('\u{1059c}', 66933), ('\u{1059d}', 66934), - ('\u{1059e}', 66935), ('\u{1059f}', 66936), ('\u{105a0}', 66937), ('\u{105a1}', 66938), - ('\u{105a3}', 66940), ('\u{105a4}', 66941), ('\u{105a5}', 66942), ('\u{105a6}', 66943), - ('\u{105a7}', 66944), ('\u{105a8}', 66945), ('\u{105a9}', 66946), ('\u{105aa}', 66947), - ('\u{105ab}', 66948), ('\u{105ac}', 66949), ('\u{105ad}', 66950), ('\u{105ae}', 66951), - ('\u{105af}', 66952), ('\u{105b0}', 66953), ('\u{105b1}', 66954), ('\u{105b3}', 66956), - ('\u{105b4}', 66957), ('\u{105b5}', 66958), ('\u{105b6}', 66959), ('\u{105b7}', 66960), - ('\u{105b8}', 66961), ('\u{105b9}', 66962), ('\u{105bb}', 66964), ('\u{105bc}', 66965), - ('\u{10cc0}', 68736), ('\u{10cc1}', 68737), ('\u{10cc2}', 68738), ('\u{10cc3}', 68739), - ('\u{10cc4}', 68740), ('\u{10cc5}', 68741), ('\u{10cc6}', 68742), ('\u{10cc7}', 68743), - ('\u{10cc8}', 68744), ('\u{10cc9}', 68745), ('\u{10cca}', 68746), ('\u{10ccb}', 68747), - ('\u{10ccc}', 68748), ('\u{10ccd}', 68749), ('\u{10cce}', 68750), ('\u{10ccf}', 68751), - ('\u{10cd0}', 68752), ('\u{10cd1}', 68753), ('\u{10cd2}', 68754), ('\u{10cd3}', 68755), - ('\u{10cd4}', 68756), ('\u{10cd5}', 68757), ('\u{10cd6}', 68758), ('\u{10cd7}', 68759), - ('\u{10cd8}', 68760), ('\u{10cd9}', 68761), ('\u{10cda}', 68762), ('\u{10cdb}', 68763), - ('\u{10cdc}', 68764), ('\u{10cdd}', 68765), ('\u{10cde}', 68766), ('\u{10cdf}', 68767), - ('\u{10ce0}', 68768), ('\u{10ce1}', 68769), ('\u{10ce2}', 68770), ('\u{10ce3}', 68771), - ('\u{10ce4}', 68772), ('\u{10ce5}', 68773), ('\u{10ce6}', 68774), ('\u{10ce7}', 68775), - ('\u{10ce8}', 68776), ('\u{10ce9}', 68777), ('\u{10cea}', 68778), ('\u{10ceb}', 68779), - ('\u{10cec}', 68780), ('\u{10ced}', 68781), ('\u{10cee}', 68782), ('\u{10cef}', 68783), - ('\u{10cf0}', 68784), ('\u{10cf1}', 68785), ('\u{10cf2}', 68786), ('\u{10d70}', 68944), - ('\u{10d71}', 68945), ('\u{10d72}', 68946), ('\u{10d73}', 68947), ('\u{10d74}', 68948), - ('\u{10d75}', 68949), ('\u{10d76}', 68950), ('\u{10d77}', 68951), ('\u{10d78}', 68952), - ('\u{10d79}', 68953), ('\u{10d7a}', 68954), ('\u{10d7b}', 68955), ('\u{10d7c}', 68956), - ('\u{10d7d}', 68957), ('\u{10d7e}', 68958), ('\u{10d7f}', 68959), ('\u{10d80}', 68960), - ('\u{10d81}', 68961), ('\u{10d82}', 68962), ('\u{10d83}', 68963), ('\u{10d84}', 68964), - ('\u{10d85}', 68965), ('\u{118c0}', 71840), ('\u{118c1}', 71841), ('\u{118c2}', 71842), - ('\u{118c3}', 71843), ('\u{118c4}', 71844), ('\u{118c5}', 71845), ('\u{118c6}', 71846), - ('\u{118c7}', 71847), ('\u{118c8}', 71848), ('\u{118c9}', 71849), ('\u{118ca}', 71850), - ('\u{118cb}', 71851), ('\u{118cc}', 71852), ('\u{118cd}', 71853), ('\u{118ce}', 71854), - ('\u{118cf}', 71855), ('\u{118d0}', 71856), ('\u{118d1}', 71857), ('\u{118d2}', 71858), - ('\u{118d3}', 71859), ('\u{118d4}', 71860), ('\u{118d5}', 71861), ('\u{118d6}', 71862), - ('\u{118d7}', 71863), ('\u{118d8}', 71864), ('\u{118d9}', 71865), ('\u{118da}', 71866), - ('\u{118db}', 71867), ('\u{118dc}', 71868), ('\u{118dd}', 71869), ('\u{118de}', 71870), - ('\u{118df}', 71871), ('\u{16e60}', 93760), ('\u{16e61}', 93761), ('\u{16e62}', 93762), - ('\u{16e63}', 93763), ('\u{16e64}', 93764), ('\u{16e65}', 93765), ('\u{16e66}', 93766), - ('\u{16e67}', 93767), ('\u{16e68}', 93768), ('\u{16e69}', 93769), ('\u{16e6a}', 93770), - ('\u{16e6b}', 93771), ('\u{16e6c}', 93772), ('\u{16e6d}', 93773), ('\u{16e6e}', 93774), - ('\u{16e6f}', 93775), ('\u{16e70}', 93776), ('\u{16e71}', 93777), ('\u{16e72}', 93778), - ('\u{16e73}', 93779), ('\u{16e74}', 93780), ('\u{16e75}', 93781), ('\u{16e76}', 93782), - ('\u{16e77}', 93783), ('\u{16e78}', 93784), ('\u{16e79}', 93785), ('\u{16e7a}', 93786), - ('\u{16e7b}', 93787), ('\u{16e7c}', 93788), ('\u{16e7d}', 93789), ('\u{16e7e}', 93790), - ('\u{16e7f}', 93791), ('\u{1e922}', 125184), ('\u{1e923}', 125185), ('\u{1e924}', 125186), + ('\u{a7cd}', 42956), ('\u{a7cf}', 42958), ('\u{a7d1}', 42960), ('\u{a7d3}', 42962), + ('\u{a7d5}', 42964), ('\u{a7d7}', 42966), ('\u{a7d9}', 42968), ('\u{a7db}', 42970), + ('\u{a7f6}', 42997), ('\u{ab53}', 42931), ('\u{ab70}', 5024), ('\u{ab71}', 5025), + ('\u{ab72}', 5026), ('\u{ab73}', 5027), ('\u{ab74}', 5028), ('\u{ab75}', 5029), + ('\u{ab76}', 5030), ('\u{ab77}', 5031), ('\u{ab78}', 5032), ('\u{ab79}', 5033), + ('\u{ab7a}', 5034), ('\u{ab7b}', 5035), ('\u{ab7c}', 5036), ('\u{ab7d}', 5037), + ('\u{ab7e}', 5038), ('\u{ab7f}', 5039), ('\u{ab80}', 5040), ('\u{ab81}', 5041), + ('\u{ab82}', 5042), ('\u{ab83}', 5043), ('\u{ab84}', 5044), ('\u{ab85}', 5045), + ('\u{ab86}', 5046), ('\u{ab87}', 5047), ('\u{ab88}', 5048), ('\u{ab89}', 5049), + ('\u{ab8a}', 5050), ('\u{ab8b}', 5051), ('\u{ab8c}', 5052), ('\u{ab8d}', 5053), + ('\u{ab8e}', 5054), ('\u{ab8f}', 5055), ('\u{ab90}', 5056), ('\u{ab91}', 5057), + ('\u{ab92}', 5058), ('\u{ab93}', 5059), ('\u{ab94}', 5060), ('\u{ab95}', 5061), + ('\u{ab96}', 5062), ('\u{ab97}', 5063), ('\u{ab98}', 5064), ('\u{ab99}', 5065), + ('\u{ab9a}', 5066), ('\u{ab9b}', 5067), ('\u{ab9c}', 5068), ('\u{ab9d}', 5069), + ('\u{ab9e}', 5070), ('\u{ab9f}', 5071), ('\u{aba0}', 5072), ('\u{aba1}', 5073), + ('\u{aba2}', 5074), ('\u{aba3}', 5075), ('\u{aba4}', 5076), ('\u{aba5}', 5077), + ('\u{aba6}', 5078), ('\u{aba7}', 5079), ('\u{aba8}', 5080), ('\u{aba9}', 5081), + ('\u{abaa}', 5082), ('\u{abab}', 5083), ('\u{abac}', 5084), ('\u{abad}', 5085), + ('\u{abae}', 5086), ('\u{abaf}', 5087), ('\u{abb0}', 5088), ('\u{abb1}', 5089), + ('\u{abb2}', 5090), ('\u{abb3}', 5091), ('\u{abb4}', 5092), ('\u{abb5}', 5093), + ('\u{abb6}', 5094), ('\u{abb7}', 5095), ('\u{abb8}', 5096), ('\u{abb9}', 5097), + ('\u{abba}', 5098), ('\u{abbb}', 5099), ('\u{abbc}', 5100), ('\u{abbd}', 5101), + ('\u{abbe}', 5102), ('\u{abbf}', 5103), ('\u{fb00}', 4194394), ('\u{fb01}', 4194395), + ('\u{fb02}', 4194396), ('\u{fb03}', 4194397), ('\u{fb04}', 4194398), ('\u{fb05}', 4194399), + ('\u{fb06}', 4194400), ('\u{fb13}', 4194401), ('\u{fb14}', 4194402), ('\u{fb15}', 4194403), + ('\u{fb16}', 4194404), ('\u{fb17}', 4194405), ('\u{ff41}', 65313), ('\u{ff42}', 65314), + ('\u{ff43}', 65315), ('\u{ff44}', 65316), ('\u{ff45}', 65317), ('\u{ff46}', 65318), + ('\u{ff47}', 65319), ('\u{ff48}', 65320), ('\u{ff49}', 65321), ('\u{ff4a}', 65322), + ('\u{ff4b}', 65323), ('\u{ff4c}', 65324), ('\u{ff4d}', 65325), ('\u{ff4e}', 65326), + ('\u{ff4f}', 65327), ('\u{ff50}', 65328), ('\u{ff51}', 65329), ('\u{ff52}', 65330), + ('\u{ff53}', 65331), ('\u{ff54}', 65332), ('\u{ff55}', 65333), ('\u{ff56}', 65334), + ('\u{ff57}', 65335), ('\u{ff58}', 65336), ('\u{ff59}', 65337), ('\u{ff5a}', 65338), + ('\u{10428}', 66560), ('\u{10429}', 66561), ('\u{1042a}', 66562), ('\u{1042b}', 66563), + ('\u{1042c}', 66564), ('\u{1042d}', 66565), ('\u{1042e}', 66566), ('\u{1042f}', 66567), + ('\u{10430}', 66568), ('\u{10431}', 66569), ('\u{10432}', 66570), ('\u{10433}', 66571), + ('\u{10434}', 66572), ('\u{10435}', 66573), ('\u{10436}', 66574), ('\u{10437}', 66575), + ('\u{10438}', 66576), ('\u{10439}', 66577), ('\u{1043a}', 66578), ('\u{1043b}', 66579), + ('\u{1043c}', 66580), ('\u{1043d}', 66581), ('\u{1043e}', 66582), ('\u{1043f}', 66583), + ('\u{10440}', 66584), ('\u{10441}', 66585), ('\u{10442}', 66586), ('\u{10443}', 66587), + ('\u{10444}', 66588), ('\u{10445}', 66589), ('\u{10446}', 66590), ('\u{10447}', 66591), + ('\u{10448}', 66592), ('\u{10449}', 66593), ('\u{1044a}', 66594), ('\u{1044b}', 66595), + ('\u{1044c}', 66596), ('\u{1044d}', 66597), ('\u{1044e}', 66598), ('\u{1044f}', 66599), + ('\u{104d8}', 66736), ('\u{104d9}', 66737), ('\u{104da}', 66738), ('\u{104db}', 66739), + ('\u{104dc}', 66740), ('\u{104dd}', 66741), ('\u{104de}', 66742), ('\u{104df}', 66743), + ('\u{104e0}', 66744), ('\u{104e1}', 66745), ('\u{104e2}', 66746), ('\u{104e3}', 66747), + ('\u{104e4}', 66748), ('\u{104e5}', 66749), ('\u{104e6}', 66750), ('\u{104e7}', 66751), + ('\u{104e8}', 66752), ('\u{104e9}', 66753), ('\u{104ea}', 66754), ('\u{104eb}', 66755), + ('\u{104ec}', 66756), ('\u{104ed}', 66757), ('\u{104ee}', 66758), ('\u{104ef}', 66759), + ('\u{104f0}', 66760), ('\u{104f1}', 66761), ('\u{104f2}', 66762), ('\u{104f3}', 66763), + ('\u{104f4}', 66764), ('\u{104f5}', 66765), ('\u{104f6}', 66766), ('\u{104f7}', 66767), + ('\u{104f8}', 66768), ('\u{104f9}', 66769), ('\u{104fa}', 66770), ('\u{104fb}', 66771), + ('\u{10597}', 66928), ('\u{10598}', 66929), ('\u{10599}', 66930), ('\u{1059a}', 66931), + ('\u{1059b}', 66932), ('\u{1059c}', 66933), ('\u{1059d}', 66934), ('\u{1059e}', 66935), + ('\u{1059f}', 66936), ('\u{105a0}', 66937), ('\u{105a1}', 66938), ('\u{105a3}', 66940), + ('\u{105a4}', 66941), ('\u{105a5}', 66942), ('\u{105a6}', 66943), ('\u{105a7}', 66944), + ('\u{105a8}', 66945), ('\u{105a9}', 66946), ('\u{105aa}', 66947), ('\u{105ab}', 66948), + ('\u{105ac}', 66949), ('\u{105ad}', 66950), ('\u{105ae}', 66951), ('\u{105af}', 66952), + ('\u{105b0}', 66953), ('\u{105b1}', 66954), ('\u{105b3}', 66956), ('\u{105b4}', 66957), + ('\u{105b5}', 66958), ('\u{105b6}', 66959), ('\u{105b7}', 66960), ('\u{105b8}', 66961), + ('\u{105b9}', 66962), ('\u{105bb}', 66964), ('\u{105bc}', 66965), ('\u{10cc0}', 68736), + ('\u{10cc1}', 68737), ('\u{10cc2}', 68738), ('\u{10cc3}', 68739), ('\u{10cc4}', 68740), + ('\u{10cc5}', 68741), ('\u{10cc6}', 68742), ('\u{10cc7}', 68743), ('\u{10cc8}', 68744), + ('\u{10cc9}', 68745), ('\u{10cca}', 68746), ('\u{10ccb}', 68747), ('\u{10ccc}', 68748), + ('\u{10ccd}', 68749), ('\u{10cce}', 68750), ('\u{10ccf}', 68751), ('\u{10cd0}', 68752), + ('\u{10cd1}', 68753), ('\u{10cd2}', 68754), ('\u{10cd3}', 68755), ('\u{10cd4}', 68756), + ('\u{10cd5}', 68757), ('\u{10cd6}', 68758), ('\u{10cd7}', 68759), ('\u{10cd8}', 68760), + ('\u{10cd9}', 68761), ('\u{10cda}', 68762), ('\u{10cdb}', 68763), ('\u{10cdc}', 68764), + ('\u{10cdd}', 68765), ('\u{10cde}', 68766), ('\u{10cdf}', 68767), ('\u{10ce0}', 68768), + ('\u{10ce1}', 68769), ('\u{10ce2}', 68770), ('\u{10ce3}', 68771), ('\u{10ce4}', 68772), + ('\u{10ce5}', 68773), ('\u{10ce6}', 68774), ('\u{10ce7}', 68775), ('\u{10ce8}', 68776), + ('\u{10ce9}', 68777), ('\u{10cea}', 68778), ('\u{10ceb}', 68779), ('\u{10cec}', 68780), + ('\u{10ced}', 68781), ('\u{10cee}', 68782), ('\u{10cef}', 68783), ('\u{10cf0}', 68784), + ('\u{10cf1}', 68785), ('\u{10cf2}', 68786), ('\u{10d70}', 68944), ('\u{10d71}', 68945), + ('\u{10d72}', 68946), ('\u{10d73}', 68947), ('\u{10d74}', 68948), ('\u{10d75}', 68949), + ('\u{10d76}', 68950), ('\u{10d77}', 68951), ('\u{10d78}', 68952), ('\u{10d79}', 68953), + ('\u{10d7a}', 68954), ('\u{10d7b}', 68955), ('\u{10d7c}', 68956), ('\u{10d7d}', 68957), + ('\u{10d7e}', 68958), ('\u{10d7f}', 68959), ('\u{10d80}', 68960), ('\u{10d81}', 68961), + ('\u{10d82}', 68962), ('\u{10d83}', 68963), ('\u{10d84}', 68964), ('\u{10d85}', 68965), + ('\u{118c0}', 71840), ('\u{118c1}', 71841), ('\u{118c2}', 71842), ('\u{118c3}', 71843), + ('\u{118c4}', 71844), ('\u{118c5}', 71845), ('\u{118c6}', 71846), ('\u{118c7}', 71847), + ('\u{118c8}', 71848), ('\u{118c9}', 71849), ('\u{118ca}', 71850), ('\u{118cb}', 71851), + ('\u{118cc}', 71852), ('\u{118cd}', 71853), ('\u{118ce}', 71854), ('\u{118cf}', 71855), + ('\u{118d0}', 71856), ('\u{118d1}', 71857), ('\u{118d2}', 71858), ('\u{118d3}', 71859), + ('\u{118d4}', 71860), ('\u{118d5}', 71861), ('\u{118d6}', 71862), ('\u{118d7}', 71863), + ('\u{118d8}', 71864), ('\u{118d9}', 71865), ('\u{118da}', 71866), ('\u{118db}', 71867), + ('\u{118dc}', 71868), ('\u{118dd}', 71869), ('\u{118de}', 71870), ('\u{118df}', 71871), + ('\u{16e60}', 93760), ('\u{16e61}', 93761), ('\u{16e62}', 93762), ('\u{16e63}', 93763), + ('\u{16e64}', 93764), ('\u{16e65}', 93765), ('\u{16e66}', 93766), ('\u{16e67}', 93767), + ('\u{16e68}', 93768), ('\u{16e69}', 93769), ('\u{16e6a}', 93770), ('\u{16e6b}', 93771), + ('\u{16e6c}', 93772), ('\u{16e6d}', 93773), ('\u{16e6e}', 93774), ('\u{16e6f}', 93775), + ('\u{16e70}', 93776), ('\u{16e71}', 93777), ('\u{16e72}', 93778), ('\u{16e73}', 93779), + ('\u{16e74}', 93780), ('\u{16e75}', 93781), ('\u{16e76}', 93782), ('\u{16e77}', 93783), + ('\u{16e78}', 93784), ('\u{16e79}', 93785), ('\u{16e7a}', 93786), ('\u{16e7b}', 93787), + ('\u{16e7c}', 93788), ('\u{16e7d}', 93789), ('\u{16e7e}', 93790), ('\u{16e7f}', 93791), + ('\u{16ebb}', 93856), ('\u{16ebc}', 93857), ('\u{16ebd}', 93858), ('\u{16ebe}', 93859), + ('\u{16ebf}', 93860), ('\u{16ec0}', 93861), ('\u{16ec1}', 93862), ('\u{16ec2}', 93863), + ('\u{16ec3}', 93864), ('\u{16ec4}', 93865), ('\u{16ec5}', 93866), ('\u{16ec6}', 93867), + ('\u{16ec7}', 93868), ('\u{16ec8}', 93869), ('\u{16ec9}', 93870), ('\u{16eca}', 93871), + ('\u{16ecb}', 93872), ('\u{16ecc}', 93873), ('\u{16ecd}', 93874), ('\u{16ece}', 93875), + ('\u{16ecf}', 93876), ('\u{16ed0}', 93877), ('\u{16ed1}', 93878), ('\u{16ed2}', 93879), + ('\u{16ed3}', 93880), ('\u{1e922}', 125184), ('\u{1e923}', 125185), ('\u{1e924}', 125186), ('\u{1e925}', 125187), ('\u{1e926}', 125188), ('\u{1e927}', 125189), ('\u{1e928}', 125190), ('\u{1e929}', 125191), ('\u{1e92a}', 125192), ('\u{1e92b}', 125193), ('\u{1e92c}', 125194), ('\u{1e92d}', 125195), ('\u{1e92e}', 125196), ('\u{1e92f}', 125197), ('\u{1e930}', 125198), From 83a99aadcff60482552fc81e0dc2992403c5652e Mon Sep 17 00:00:00 2001 From: Marco Cavenati Date: Tue, 9 Sep 2025 15:01:47 +0200 Subject: [PATCH 007/226] Bump unicode printable to version 17.0.0 --- library/core/src/unicode/printable.rs | 160 +++++++++++++------------- 1 file changed, 82 insertions(+), 78 deletions(-) diff --git a/library/core/src/unicode/printable.rs b/library/core/src/unicode/printable.rs index d8fb50e4ed29..68e1c8ae31c0 100644 --- a/library/core/src/unicode/printable.rs +++ b/library/core/src/unicode/printable.rs @@ -54,13 +54,10 @@ pub(crate) fn is_printable(x: char) -> bool { if 0x2a6e0 <= x && x < 0x2a700 { return false; } - if 0x2b73a <= x && x < 0x2b740 { - return false; - } if 0x2b81e <= x && x < 0x2b820 { return false; } - if 0x2cea2 <= x && x < 0x2ceb0 { + if 0x2ceae <= x && x < 0x2ceb0 { return false; } if 0x2ebe1 <= x && x < 0x2ebf0 { @@ -75,7 +72,7 @@ pub(crate) fn is_printable(x: char) -> bool { if 0x3134b <= x && x < 0x31350 { return false; } - if 0x323b0 <= x && x < 0xe0100 { + if 0x3347a <= x && x < 0xe0100 { return false; } if 0xe01f0 <= x && x < 0x110000 { @@ -96,7 +93,7 @@ const SINGLETONS0U: &[(u8, u8)] = &[ (0x09, 17), (0x0a, 28), (0x0b, 25), - (0x0c, 26), + (0x0c, 25), (0x0d, 16), (0x0e, 12), (0x0f, 4), @@ -107,24 +104,22 @@ const SINGLETONS0U: &[(u8, u8)] = &[ (0x17, 4), (0x18, 1), (0x19, 3), - (0x1a, 7), + (0x1a, 9), (0x1b, 1), (0x1c, 2), (0x1f, 22), (0x20, 3), - (0x2b, 3), + (0x2b, 2), (0x2d, 11), (0x2e, 1), (0x30, 4), (0x31, 2), (0x32, 1), - (0xa7, 4), (0xa9, 2), (0xaa, 4), (0xab, 8), (0xfa, 2), (0xfb, 5), - (0xfd, 2), (0xfe, 3), (0xff, 9), ]; @@ -143,30 +138,29 @@ const SINGLETONS0L: &[u8] = &[ 0x34, 0x3a, 0x3b, 0x45, 0x46, 0x49, 0x4a, 0x5e, 0x64, 0x65, 0x84, 0x91, 0x9b, 0x9d, 0xc9, 0xce, 0xcf, 0x0d, 0x11, 0x29, 0x3a, 0x3b, 0x45, 0x49, - 0x57, 0x5b, 0x5c, 0x5e, 0x5f, 0x64, 0x65, 0x8d, - 0x91, 0xa9, 0xb4, 0xba, 0xbb, 0xc5, 0xc9, 0xdf, - 0xe4, 0xe5, 0xf0, 0x0d, 0x11, 0x45, 0x49, 0x64, - 0x65, 0x80, 0x84, 0xb2, 0xbc, 0xbe, 0xbf, 0xd5, - 0xd7, 0xf0, 0xf1, 0x83, 0x85, 0x8b, 0xa4, 0xa6, - 0xbe, 0xbf, 0xc5, 0xc7, 0xcf, 0xda, 0xdb, 0x48, - 0x98, 0xbd, 0xcd, 0xc6, 0xce, 0xcf, 0x49, 0x4e, - 0x4f, 0x57, 0x59, 0x5e, 0x5f, 0x89, 0x8e, 0x8f, - 0xb1, 0xb6, 0xb7, 0xbf, 0xc1, 0xc6, 0xc7, 0xd7, - 0x11, 0x16, 0x17, 0x5b, 0x5c, 0xf6, 0xf7, 0xfe, - 0xff, 0x80, 0x6d, 0x71, 0xde, 0xdf, 0x0e, 0x1f, - 0x6e, 0x6f, 0x1c, 0x1d, 0x5f, 0x7d, 0x7e, 0xae, - 0xaf, 0x4d, 0xbb, 0xbc, 0x16, 0x17, 0x1e, 0x1f, - 0x46, 0x47, 0x4e, 0x4f, 0x58, 0x5a, 0x5c, 0x5e, - 0x7e, 0x7f, 0xb5, 0xc5, 0xd4, 0xd5, 0xdc, 0xf0, - 0xf1, 0xf5, 0x72, 0x73, 0x8f, 0x74, 0x75, 0x96, + 0x57, 0x5b, 0x5e, 0x5f, 0x64, 0x65, 0x8d, 0x91, + 0xa9, 0xb4, 0xba, 0xbb, 0xc5, 0xc9, 0xdf, 0xe4, + 0xe5, 0xf0, 0x0d, 0x11, 0x45, 0x49, 0x64, 0x65, + 0x80, 0x84, 0xb2, 0xbc, 0xbe, 0xbf, 0xd5, 0xd7, + 0xf0, 0xf1, 0x83, 0x85, 0x8b, 0xa4, 0xa6, 0xbe, + 0xbf, 0xc5, 0xc7, 0xcf, 0xda, 0xdb, 0x48, 0x98, + 0xbd, 0xcd, 0xc6, 0xce, 0xcf, 0x49, 0x4e, 0x4f, + 0x57, 0x59, 0x5e, 0x5f, 0x89, 0x8e, 0x8f, 0xb1, + 0xb6, 0xb7, 0xbf, 0xc1, 0xc6, 0xc7, 0xd7, 0x11, + 0x16, 0x17, 0x5b, 0x5c, 0xf6, 0xf7, 0xfe, 0xff, + 0x80, 0x6d, 0x71, 0xde, 0xdf, 0x0e, 0x1f, 0x6e, + 0x6f, 0x1c, 0x1d, 0x5f, 0x7d, 0x7e, 0xae, 0xaf, + 0xde, 0xdf, 0x4d, 0xbb, 0xbc, 0x16, 0x17, 0x1e, + 0x1f, 0x46, 0x47, 0x4e, 0x4f, 0x58, 0x5a, 0x5c, + 0x5e, 0x7e, 0x7f, 0xb5, 0xc5, 0xd4, 0xd5, 0xdc, + 0xf0, 0xf1, 0xf5, 0x72, 0x73, 0x8f, 0x74, 0x75, 0x26, 0x2e, 0x2f, 0xa7, 0xaf, 0xb7, 0xbf, 0xc7, 0xcf, 0xd7, 0xdf, 0x9a, 0x00, 0x40, 0x97, 0x98, - 0x30, 0x8f, 0x1f, 0xce, 0xcf, 0xd2, 0xd4, 0xce, - 0xff, 0x4e, 0x4f, 0x5a, 0x5b, 0x07, 0x08, 0x0f, - 0x10, 0x27, 0x2f, 0xee, 0xef, 0x6e, 0x6f, 0x37, - 0x3d, 0x3f, 0x42, 0x45, 0x90, 0x91, 0x53, 0x67, - 0x75, 0xc8, 0xc9, 0xd0, 0xd1, 0xd8, 0xd9, 0xe7, - 0xfe, 0xff, + 0x30, 0x8f, 0x1f, 0xce, 0xff, 0x4e, 0x4f, 0x5a, + 0x5b, 0x07, 0x08, 0x0f, 0x10, 0x27, 0x2f, 0xee, + 0xef, 0x6e, 0x6f, 0x37, 0x3d, 0x3f, 0x42, 0x45, + 0x53, 0x67, 0x75, 0xc8, 0xc9, 0xd0, 0xd1, 0xd8, + 0xd9, 0xe7, 0xfe, 0xff, ]; #[rustfmt::skip] const SINGLETONS1U: &[(u8, u8)] = &[ @@ -195,6 +189,7 @@ const SINGLETONS1U: &[(u8, u8)] = &[ (0x24, 1), (0x6a, 4), (0x6b, 2), + (0x6e, 2), (0xaf, 3), (0xb1, 2), (0xbc, 2), @@ -207,12 +202,13 @@ const SINGLETONS1U: &[(u8, u8)] = &[ (0xda, 1), (0xe0, 5), (0xe1, 2), + (0xe6, 1), (0xe7, 4), (0xe8, 2), (0xee, 32), (0xf0, 4), (0xf8, 2), - (0xfa, 4), + (0xfa, 5), (0xfb, 1), ]; #[rustfmt::skip] @@ -231,18 +227,19 @@ const SINGLETONS1L: &[u8] = &[ 0x39, 0x3a, 0xa8, 0xa9, 0xd8, 0xd9, 0x09, 0x37, 0x90, 0x91, 0xa8, 0x07, 0x0a, 0x3b, 0x3e, 0x66, 0x69, 0x8f, 0x92, 0x11, 0x6f, 0x5f, 0xbf, 0xee, - 0xef, 0x5a, 0x62, 0xf4, 0xfc, 0xff, 0x53, 0x54, - 0x9a, 0x9b, 0x2e, 0x2f, 0x27, 0x28, 0x55, 0x9d, - 0xa0, 0xa1, 0xa3, 0xa4, 0xa7, 0xa8, 0xad, 0xba, - 0xbc, 0xc4, 0x06, 0x0b, 0x0c, 0x15, 0x1d, 0x3a, - 0x3f, 0x45, 0x51, 0xa6, 0xa7, 0xcc, 0xcd, 0xa0, - 0x07, 0x19, 0x1a, 0x22, 0x25, 0x3e, 0x3f, 0xe7, - 0xec, 0xef, 0xff, 0xc5, 0xc6, 0x04, 0x20, 0x23, - 0x25, 0x26, 0x28, 0x33, 0x38, 0x3a, 0x48, 0x4a, - 0x4c, 0x50, 0x53, 0x55, 0x56, 0x58, 0x5a, 0x5c, - 0x5e, 0x60, 0x63, 0x65, 0x66, 0x6b, 0x73, 0x78, - 0x7d, 0x7f, 0x8a, 0xa4, 0xaa, 0xaf, 0xb0, 0xc0, - 0xd0, 0xae, 0xaf, 0x6e, 0x6f, 0xdd, 0xde, 0x93, + 0xef, 0x5a, 0x62, 0xb9, 0xba, 0xf4, 0xfc, 0xff, + 0x53, 0x54, 0x9a, 0x9b, 0x2e, 0x2f, 0x27, 0x28, + 0x55, 0x9d, 0xa0, 0xa1, 0xa3, 0xa4, 0xa7, 0xa8, + 0xad, 0xba, 0xbc, 0xc4, 0x06, 0x0b, 0x0c, 0x15, + 0x1d, 0x3a, 0x3f, 0x45, 0x51, 0xa6, 0xa7, 0xcc, + 0xcd, 0xa0, 0x07, 0x19, 0x1a, 0x22, 0x25, 0x3e, + 0x3f, 0xdf, 0xe7, 0xec, 0xef, 0xff, 0xc5, 0xc6, + 0x04, 0x20, 0x23, 0x25, 0x26, 0x28, 0x33, 0x38, + 0x3a, 0x48, 0x4a, 0x4c, 0x50, 0x53, 0x55, 0x56, + 0x58, 0x5a, 0x5c, 0x5e, 0x60, 0x63, 0x65, 0x66, + 0x6b, 0x73, 0x78, 0x7d, 0x7f, 0x8a, 0xa4, 0xaa, + 0xaf, 0xb0, 0xc0, 0xd0, 0xae, 0xaf, 0x6e, 0x6f, + 0xc7, 0xdd, 0xde, 0x93, ]; #[rustfmt::skip] const NORMAL0: &[u8] = &[ @@ -254,7 +251,7 @@ const NORMAL0: &[u8] = &[ 0x06, 0x11, 0x81, 0xac, 0x0e, 0x80, 0xab, 0x05, - 0x1f, 0x08, + 0x20, 0x07, 0x81, 0x1c, 0x03, 0x19, 0x08, 0x01, 0x04, @@ -282,8 +279,8 @@ const NORMAL0: &[u8] = &[ 0x4e, 0x07, 0x1b, 0x07, 0x57, 0x07, - 0x02, 0x06, - 0x17, 0x0c, + 0x02, 0x05, + 0x18, 0x0c, 0x50, 0x04, 0x43, 0x03, 0x2d, 0x03, @@ -319,7 +316,7 @@ const NORMAL0: &[u8] = &[ 0x0b, 0x03, 0x80, 0xac, 0x06, 0x0a, 0x06, - 0x2f, 0x31, + 0x4c, 0x14, 0x80, 0xf4, 0x08, 0x3c, 0x03, 0x0f, 0x03, @@ -330,7 +327,7 @@ const NORMAL0: &[u8] = &[ 0x18, 0x08, 0x2f, 0x11, 0x2d, 0x03, - 0x21, 0x0f, + 0x22, 0x0e, 0x21, 0x0f, 0x80, 0x8c, 0x04, 0x82, 0x9a, 0x16, @@ -349,8 +346,8 @@ const NORMAL0: &[u8] = &[ 0x37, 0x09, 0x81, 0x5c, 0x14, 0x80, 0xb8, 0x08, - 0x80, 0xdd, 0x15, - 0x3b, 0x03, + 0x80, 0xdd, 0x14, + 0x3c, 0x03, 0x0a, 0x06, 0x38, 0x08, 0x46, 0x08, @@ -370,9 +367,7 @@ const NORMAL0: &[u8] = &[ 0x81, 0xda, 0x26, 0x07, 0x0c, 0x05, 0x05, - 0x80, 0xa6, 0x10, - 0x81, 0xf5, 0x07, - 0x01, 0x20, + 0x82, 0xb3, 0x20, 0x2a, 0x06, 0x4c, 0x04, 0x80, 0x8d, 0x04, @@ -414,7 +409,7 @@ const NORMAL1: &[u8] = &[ 0x16, 0x05, 0x21, 0x03, 0x1b, 0x05, - 0x01, 0x40, + 0x1b, 0x26, 0x38, 0x04, 0x4b, 0x05, 0x2f, 0x04, @@ -437,8 +432,9 @@ const NORMAL1: &[u8] = &[ 0x1d, 0x08, 0x02, 0x80, 0xd0, 0x52, 0x10, - 0x03, 0x37, - 0x2c, 0x08, + 0x06, 0x08, + 0x09, 0x21, + 0x2e, 0x08, 0x2a, 0x16, 0x1a, 0x26, 0x1c, 0x14, @@ -481,7 +477,8 @@ const NORMAL1: &[u8] = &[ 0x48, 0x08, 0x53, 0x0d, 0x49, 0x07, - 0x0a, 0x80, 0xb6, + 0x0a, 0x56, + 0x08, 0x58, 0x22, 0x0e, 0x0a, 0x06, 0x46, 0x0a, @@ -491,7 +488,9 @@ const NORMAL1: &[u8] = &[ 0x0e, 0x08, 0x0a, 0x06, 0x39, 0x07, - 0x0a, 0x81, 0x36, + 0x0a, 0x06, + 0x2c, 0x04, + 0x0a, 0x80, 0xf6, 0x19, 0x07, 0x3b, 0x03, 0x1d, 0x55, @@ -514,15 +513,16 @@ const NORMAL1: &[u8] = &[ 0x28, 0x05, 0x13, 0x81, 0xb0, 0x3a, 0x80, 0xc6, - 0x5b, 0x65, + 0x5b, 0x05, + 0x34, 0x2c, 0x4b, 0x04, 0x39, 0x07, 0x11, 0x40, 0x05, 0x0b, - 0x02, 0x0e, - 0x97, 0xf8, 0x08, - 0x84, 0xd6, 0x29, - 0x0a, 0xa2, 0xe7, + 0x07, 0x09, + 0x9c, 0xd6, 0x29, + 0x20, 0x61, + 0x73, 0xa1, 0xfd, 0x81, 0x33, 0x0f, 0x01, 0x1d, 0x06, 0x0e, @@ -532,8 +532,10 @@ const NORMAL1: &[u8] = &[ 0x0d, 0x03, 0x09, 0x07, 0x10, 0x8f, 0x60, - 0x80, 0xfa, 0x06, - 0x81, 0xb4, 0x4c, + 0x80, 0xfd, 0x03, + 0x81, 0xb4, 0x06, + 0x17, 0x0f, + 0x11, 0x0f, 0x47, 0x09, 0x74, 0x3c, 0x80, 0xf6, 0x0a, @@ -560,7 +562,9 @@ const NORMAL1: &[u8] = &[ 0x01, 0x81, 0xd0, 0x2a, 0x80, 0xd6, 0x2b, 0x04, - 0x01, 0x81, 0xe0, + 0x01, 0x80, 0xc0, + 0x36, 0x08, + 0x02, 0x80, 0xe0, 0x80, 0xf7, 0x29, 0x4c, 0x04, 0x0a, 0x04, @@ -581,11 +585,10 @@ const NORMAL1: &[u8] = &[ 0x09, 0x07, 0x02, 0x0e, 0x06, 0x80, 0x9a, - 0x83, 0xd8, 0x04, + 0x83, 0xd9, 0x03, 0x11, 0x03, 0x0d, 0x03, - 0x77, 0x04, - 0x5f, 0x06, + 0x80, 0xda, 0x06, 0x0c, 0x04, 0x01, 0x0f, 0x0c, 0x04, @@ -593,12 +596,13 @@ const NORMAL1: &[u8] = &[ 0x0a, 0x06, 0x28, 0x08, 0x2c, 0x04, - 0x02, 0x3e, - 0x81, 0x54, 0x0c, + 0x02, 0x0e, + 0x09, 0x27, + 0x81, 0x58, 0x08, 0x1d, 0x03, - 0x0a, 0x05, - 0x38, 0x07, - 0x1c, 0x06, - 0x09, 0x07, - 0x80, 0xfa, 0x84, 0x06, + 0x0b, 0x03, + 0x3b, 0x04, + 0x1e, 0x04, + 0x0a, 0x07, + 0x80, 0xfb, 0x84, 0x05, ]; From e513ecc939b61f9020c511e4fc4daff892725cc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Justus=20Fl=C3=BCgel?= Date: Sun, 7 Sep 2025 15:18:47 +0200 Subject: [PATCH 008/226] Fix `explicit_deref_methods` triggering from within proc macros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Justus Flügel --- clippy_lints/src/dereference.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 9337816c80d9..f82ea9844ed6 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -3,7 +3,8 @@ use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; use clippy_utils::ty::{adjust_derefs_manually_drop, implements_trait, is_manually_drop, peel_and_count_ty_refs}; use clippy_utils::{ - DefinedTy, ExprUseNode, expr_use_ctxt, get_parent_expr, is_block_like, is_lint_allowed, path_to_local, + DefinedTy, ExprUseNode, expr_use_ctxt, get_parent_expr, is_block_like, is_from_proc_macro, is_lint_allowed, + path_to_local, }; use rustc_ast::util::parser::ExprPrecedence; use rustc_data_structures::fx::FxIndexMap; @@ -260,6 +261,13 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { }; self.skip_expr = skip_expr; + if is_from_proc_macro(cx, expr) { + if let Some((state, data)) = self.state.take() { + report(cx, expr, state, data, cx.typeck_results()); + } + return; + } + match (self.state.take(), kind) { (None, kind) => { let expr_ty = typeck.expr_ty(expr); From b92330b7b87ba86abccd6b4f4298652a9cbed606 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sat, 30 Aug 2025 18:08:50 +0200 Subject: [PATCH 009/226] fix `nonstandard_macro_braces`: suggest trailing semicolon when needed --- clippy_lints/src/nonstandard_macro_braces.rs | 91 +++++++++++++------ .../conf_nonstandard_macro_braces.fixed | 8 ++ .../conf_nonstandard_macro_braces.rs | 8 ++ .../conf_nonstandard_macro_braces.stderr | 8 +- 4 files changed, 87 insertions(+), 28 deletions(-) diff --git a/clippy_lints/src/nonstandard_macro_braces.rs b/clippy_lints/src/nonstandard_macro_braces.rs index 83f7d9319697..3a8a4dd0c713 100644 --- a/clippy_lints/src/nonstandard_macro_braces.rs +++ b/clippy_lints/src/nonstandard_macro_braces.rs @@ -16,8 +16,8 @@ declare_clippy_lint! { /// Checks that common macros are used with consistent bracing. /// /// ### Why is this bad? - /// This is mostly a consistency lint although using () or [] - /// doesn't give you a semicolon in item position, which can be unexpected. + /// Having non-conventional braces on well-stablished macros can be confusing + /// when debugging, and they bring incosistencies with the rest of the ecosystem. /// /// ### Example /// ```no_run @@ -33,8 +33,12 @@ declare_clippy_lint! { "check consistent use of braces in macro" } -/// The (callsite span, (open brace, close brace), source snippet) -type MacroInfo = (Span, (char, char), SourceText); +struct MacroInfo { + callsite_span: Span, + callsite_snippet: SourceText, + old_open_brace: char, + braces: (char, char), +} pub struct MacroBraces { macro_braces: FxHashMap, @@ -54,30 +58,58 @@ impl_lint_pass!(MacroBraces => [NONSTANDARD_MACRO_BRACES]); impl EarlyLintPass for MacroBraces { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { - if let Some((span, braces, snip)) = is_offending_macro(cx, item.span, self) { - emit_help(cx, &snip, braces, span); - self.done.insert(span); + if let Some(MacroInfo { + callsite_span, + callsite_snippet, + braces, + .. + }) = is_offending_macro(cx, item.span, self) + { + emit_help(cx, &callsite_snippet, braces, callsite_span, false); + self.done.insert(callsite_span); } } fn check_stmt(&mut self, cx: &EarlyContext<'_>, stmt: &ast::Stmt) { - if let Some((span, braces, snip)) = is_offending_macro(cx, stmt.span, self) { - emit_help(cx, &snip, braces, span); - self.done.insert(span); + if let Some(MacroInfo { + callsite_span, + callsite_snippet, + braces, + old_open_brace, + }) = is_offending_macro(cx, stmt.span, self) + { + // if we turn `macro!{}` into `macro!()`/`macro![]`, we'll no longer get the implicit + // trailing semicolon, see #9913 + // NOTE: `stmt.kind != StmtKind::MacCall` because `EarlyLintPass` happens after macro expansion + let add_semi = matches!(stmt.kind, ast::StmtKind::Expr(..)) && old_open_brace == '{'; + emit_help(cx, &callsite_snippet, braces, callsite_span, add_semi); + self.done.insert(callsite_span); } } fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { - if let Some((span, braces, snip)) = is_offending_macro(cx, expr.span, self) { - emit_help(cx, &snip, braces, span); - self.done.insert(span); + if let Some(MacroInfo { + callsite_span, + callsite_snippet, + braces, + .. + }) = is_offending_macro(cx, expr.span, self) + { + emit_help(cx, &callsite_snippet, braces, callsite_span, false); + self.done.insert(callsite_span); } } fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) { - if let Some((span, braces, snip)) = is_offending_macro(cx, ty.span, self) { - emit_help(cx, &snip, braces, span); - self.done.insert(span); + if let Some(MacroInfo { + callsite_span, + braces, + callsite_snippet, + .. + }) = is_offending_macro(cx, ty.span, self) + { + emit_help(cx, &callsite_snippet, braces, callsite_span, false); + self.done.insert(callsite_span); } } } @@ -90,39 +122,44 @@ fn is_offending_macro(cx: &EarlyContext<'_>, span: Span, mac_braces: &MacroBrace .last() .is_some_and(|e| e.macro_def_id.is_some_and(DefId::is_local)) }; - let span_call_site = span.ctxt().outer_expn_data().call_site; + let callsite_span = span.ctxt().outer_expn_data().call_site; if let ExpnKind::Macro(MacroKind::Bang, mac_name) = span.ctxt().outer_expn_data().kind && let name = mac_name.as_str() && let Some(&braces) = mac_braces.macro_braces.get(name) - && let Some(snip) = span_call_site.get_source_text(cx) + && let Some(snip) = callsite_span.get_source_text(cx) // we must check only invocation sites // https://github.com/rust-lang/rust-clippy/issues/7422 - && snip.starts_with(&format!("{name}!")) + && let Some(macro_args_str) = snip.strip_prefix(name).and_then(|snip| snip.strip_prefix('!')) + && let Some(old_open_brace @ ('{' | '(' | '[')) = macro_args_str.trim_start().chars().next() + && old_open_brace != braces.0 && unnested_or_local() - // make formatting consistent - && let c = snip.replace(' ', "") - && !c.starts_with(&format!("{name}!{}", braces.0)) - && !mac_braces.done.contains(&span_call_site) + && !mac_braces.done.contains(&callsite_span) { - Some((span_call_site, braces, snip)) + Some(MacroInfo { + callsite_span, + callsite_snippet: snip, + old_open_brace, + braces, + }) } else { None } } -fn emit_help(cx: &EarlyContext<'_>, snip: &str, (open, close): (char, char), span: Span) { +fn emit_help(cx: &EarlyContext<'_>, snip: &str, (open, close): (char, char), span: Span, add_semi: bool) { + let semi = if add_semi { ";" } else { "" }; if let Some((macro_name, macro_args_str)) = snip.split_once('!') { let mut macro_args = macro_args_str.trim().to_string(); // now remove the wrong braces - macro_args.remove(0); macro_args.pop(); + macro_args.remove(0); span_lint_and_sugg( cx, NONSTANDARD_MACRO_BRACES, span, format!("use of irregular braces for `{macro_name}!` macro"), "consider writing", - format!("{macro_name}!{open}{macro_args}{close}"), + format!("{macro_name}!{open}{macro_args}{close}{semi}"), Applicability::MachineApplicable, ); } diff --git a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.fixed b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.fixed index 8da607ec6584..419e62f92f46 100644 --- a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.fixed +++ b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.fixed @@ -67,3 +67,11 @@ fn main() { printlnfoo!["test if printlnfoo is triggered by println"]; } + +#[rustfmt::skip] +#[expect(clippy::no_effect)] +fn issue9913() { + println!("hello world"); + [0]; // separate statement, not indexing into the result of println. + //~^^ nonstandard_macro_braces +} diff --git a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs index e35844a209fa..b0bbced4ea3c 100644 --- a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs +++ b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs @@ -67,3 +67,11 @@ fn main() { printlnfoo!["test if printlnfoo is triggered by println"]; } + +#[rustfmt::skip] +#[expect(clippy::no_effect)] +fn issue9913() { + println! {"hello world"} + [0]; // separate statement, not indexing into the result of println. + //~^^ nonstandard_macro_braces +} diff --git a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr index fda6addc7aa3..87325f05c9bc 100644 --- a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr +++ b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr @@ -54,5 +54,11 @@ error: use of irregular braces for `eprint!` macro LL | eprint!("test if user config overrides defaults"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `eprint!["test if user config overrides defaults"]` -error: aborting due to 8 previous errors +error: use of irregular braces for `println!` macro + --> tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs:74:5 + | +LL | println! {"hello world"} + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `println!("hello world");` + +error: aborting due to 9 previous errors From 05156f4e8b1c0e0ef6eedcf99cfedf142c8f56fb Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Mon, 8 Sep 2025 21:59:03 +0200 Subject: [PATCH 010/226] clean-up `single_match` a bit --- clippy_lints/src/matches/single_match.rs | 27 +++++++++++++----------- tests/ui/single_match.fixed | 5 +++-- tests/ui/single_match.rs | 5 +++-- tests/ui/single_match.stderr | 20 +++++++++--------- 4 files changed, 31 insertions(+), 26 deletions(-) diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index bcf079b70070..0470b5443d18 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -24,17 +24,16 @@ use super::{MATCH_BOOL, SINGLE_MATCH, SINGLE_MATCH_ELSE}; /// span, e.g. a string literal `"//"`, but we know that this isn't the case for empty /// match arms. fn empty_arm_has_comment(cx: &LateContext<'_>, span: Span) -> bool { - if let Some(ff) = span.get_source_range(cx) - && let Some(text) = ff.as_str() - { - text.as_bytes().windows(2).any(|w| w == b"//" || w == b"/*") - } else { - false - } + span.check_source_text(cx, |text| text.as_bytes().windows(2).any(|w| w == b"//" || w == b"/*")) } -#[rustfmt::skip] -pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>], expr: &'tcx Expr<'_>, contains_comments: bool) { +pub(crate) fn check<'tcx>( + cx: &LateContext<'tcx>, + ex: &'tcx Expr<'_>, + arms: &'tcx [Arm<'_>], + expr: &'tcx Expr<'_>, + contains_comments: bool, +) { if let [arm1, arm2] = arms && !arms.iter().any(|arm| arm.guard.is_some() || arm.pat.span.from_expansion()) && !expr.span.from_expansion() @@ -226,13 +225,13 @@ enum PatState<'a> { Wild, /// A std enum we know won't be extended. Tracks the states of each variant separately. /// - /// This is not used for `Option` since it uses the current pattern to track it's state. + /// This is not used for `Option` since it uses the current pattern to track its state. StdEnum(&'a mut [PatState<'a>]), /// Either the initial state for a pattern or a non-std enum. There is currently no need to /// distinguish these cases. /// /// For non-std enums there's no need to track the state of sub-patterns as the state of just - /// this pattern on it's own is enough for linting. Consider two cases: + /// this pattern on its own is enough for linting. Consider two cases: /// * This enum has no wild match. This case alone is enough to determine we can lint. /// * This enum has a wild match and therefore all sub-patterns also have a wild match. /// @@ -380,7 +379,11 @@ impl<'a> PatState<'a> { self.add_pat(cx, pat) }, PatKind::Tuple([sub_pat], pos) - if pos.as_opt_usize().is_none() || cx.typeck.pat_ty(pat).tuple_fields().len() == 1 => + // `pat` looks like `(sub_pat)`, without a `..` -- has only one sub-pattern + if pos.as_opt_usize().is_none() + // `pat` looks like `(sub_pat, ..)` or `(.., sub_pat)`, but its type is a unary tuple, + // so it still only has one sub-pattern + || cx.typeck.pat_ty(pat).tuple_fields().len() == 1 => { self.add_pat(cx, sub_pat) }, diff --git a/tests/ui/single_match.fixed b/tests/ui/single_match.fixed index db5107600ee6..03982b069e67 100644 --- a/tests/ui/single_match.fixed +++ b/tests/ui/single_match.fixed @@ -218,7 +218,7 @@ fn main() { }; } -fn issue_10808(bar: Option) { +fn issue10808(bar: Option) { if let Some(v) = bar { unsafe { let r = &v as *const i32; println!("{}", *r); @@ -330,6 +330,7 @@ pub struct Data([u8; 4]); const DATA: Data = Data([1, 2, 3, 4]); const CONST_I32: i32 = 1; +// https://github.com/rust-lang/rust-clippy/issues/13012 fn irrefutable_match() { println!(); //~^^^^ single_match @@ -367,7 +368,7 @@ fn irrefutable_match() { //~| NOTE: you might want to preserve the comments from inside the `match` } -fn issue_14493() { +fn issue14493() { macro_rules! mac { (some) => { Some(42) diff --git a/tests/ui/single_match.rs b/tests/ui/single_match.rs index a367b94c4ca6..e28128e35ada 100644 --- a/tests/ui/single_match.rs +++ b/tests/ui/single_match.rs @@ -269,7 +269,7 @@ fn main() { }; } -fn issue_10808(bar: Option) { +fn issue10808(bar: Option) { match bar { Some(v) => unsafe { let r = &v as *const i32; @@ -397,6 +397,7 @@ pub struct Data([u8; 4]); const DATA: Data = Data([1, 2, 3, 4]); const CONST_I32: i32 = 1; +// https://github.com/rust-lang/rust-clippy/issues/13012 fn irrefutable_match() { match DATA { DATA => println!(), @@ -462,7 +463,7 @@ fn irrefutable_match() { //~| NOTE: you might want to preserve the comments from inside the `match` } -fn issue_14493() { +fn issue14493() { macro_rules! mac { (some) => { Some(42) diff --git a/tests/ui/single_match.stderr b/tests/ui/single_match.stderr index 1a4edc45c928..ba8bc5af5a60 100644 --- a/tests/ui/single_match.stderr +++ b/tests/ui/single_match.stderr @@ -225,7 +225,7 @@ LL | | } | |_____^ help: try: `if &s[0..3] == b"foo" { println!() }` error: this pattern is irrefutable, `match` is useless - --> tests/ui/single_match.rs:401:5 + --> tests/ui/single_match.rs:402:5 | LL | / match DATA { LL | | DATA => println!(), @@ -234,7 +234,7 @@ LL | | } | |_____^ help: try: `println!();` error: this pattern is irrefutable, `match` is useless - --> tests/ui/single_match.rs:407:5 + --> tests/ui/single_match.rs:408:5 | LL | / match CONST_I32 { LL | | CONST_I32 => println!(), @@ -243,7 +243,7 @@ LL | | } | |_____^ help: try: `println!();` error: this pattern is irrefutable, `match` is useless - --> tests/ui/single_match.rs:414:5 + --> tests/ui/single_match.rs:415:5 | LL | / match i { LL | | i => { @@ -263,7 +263,7 @@ LL + } | error: this pattern is irrefutable, `match` is useless - --> tests/ui/single_match.rs:423:5 + --> tests/ui/single_match.rs:424:5 | LL | / match i { LL | | i => {}, @@ -272,7 +272,7 @@ LL | | } | |_____^ help: `match` expression can be removed error: this pattern is irrefutable, `match` is useless - --> tests/ui/single_match.rs:429:5 + --> tests/ui/single_match.rs:430:5 | LL | / match i { LL | | i => (), @@ -281,7 +281,7 @@ LL | | } | |_____^ help: `match` expression can be removed error: this pattern is irrefutable, `match` is useless - --> tests/ui/single_match.rs:435:5 + --> tests/ui/single_match.rs:436:5 | LL | / match CONST_I32 { LL | | CONST_I32 => println!(), @@ -290,7 +290,7 @@ LL | | } | |_____^ help: try: `println!();` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:443:5 + --> tests/ui/single_match.rs:444:5 | LL | / match x.pop() { LL | | // bla @@ -302,7 +302,7 @@ LL | | } = note: you might want to preserve the comments from inside the `match` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:452:5 + --> tests/ui/single_match.rs:453:5 | LL | / match x.pop() { LL | | // bla @@ -322,7 +322,7 @@ LL + } | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:478:5 + --> tests/ui/single_match.rs:479:5 | LL | / match mac!(some) { LL | | Some(u) => println!("{u}"), @@ -331,7 +331,7 @@ LL | | } | |_____^ help: try: `if let Some(u) = mac!(some) { println!("{u}") }` error: you seem to be trying to use `match` for an equality check. Consider using `if` - --> tests/ui/single_match.rs:486:5 + --> tests/ui/single_match.rs:487:5 | LL | / match mac!(str) { LL | | "foo" => println!("eq"), From 593a9c98aef3819f727f2fc9fa8abe58ea784d07 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 9 Sep 2025 22:45:29 +0200 Subject: [PATCH 011/226] fix(collapsible{,_else}_if): respect `#[expect]` on inner `if` --- clippy_lints/src/collapsible_if.rs | 73 +++++++++++++------ clippy_utils/src/sym.rs | 2 + .../collapsible_if/collapsible_else_if.fixed | 19 ++++- .../collapsible_if/collapsible_else_if.rs | 19 ++++- tests/ui/collapsible_else_if.fixed | 27 +++++++ tests/ui/collapsible_else_if.rs | 27 +++++++ tests/ui/collapsible_else_if.stderr | 2 +- tests/ui/collapsible_else_if_unfixable.rs | 22 ++++++ tests/ui/collapsible_else_if_unfixable.stderr | 17 +++++ tests/ui/collapsible_if.fixed | 18 +++++ tests/ui/collapsible_if.rs | 18 +++++ tests/ui/collapsible_if.stderr | 2 +- tests/ui/collapsible_if_unfixable.rs | 20 +++++ tests/ui/collapsible_if_unfixable.stderr | 17 +++++ 14 files changed, 256 insertions(+), 27 deletions(-) create mode 100644 tests/ui/collapsible_else_if_unfixable.rs create mode 100644 tests/ui/collapsible_else_if_unfixable.stderr create mode 100644 tests/ui/collapsible_if_unfixable.rs create mode 100644 tests/ui/collapsible_if_unfixable.stderr diff --git a/clippy_lints/src/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs index ad610fbd8d2c..b239c20ab4d8 100644 --- a/clippy_lints/src/collapsible_if.rs +++ b/clippy_lints/src/collapsible_if.rs @@ -1,16 +1,16 @@ use clippy_config::Conf; -use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{IntoSpan as _, SpanRangeExt, snippet, snippet_block_with_applicability}; -use clippy_utils::{span_contains_non_whitespace, tokenize_with_text}; -use rustc_ast::BinOpKind; +use clippy_utils::{span_contains_non_whitespace, sym, tokenize_with_text}; +use rustc_ast::{BinOpKind, MetaItemInner}; use rustc_errors::Applicability; use rustc_hir::{Block, Expr, ExprKind, StmtKind}; use rustc_lexer::TokenKind; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, Level}; use rustc_session::impl_lint_pass; use rustc_span::source_map::SourceMap; -use rustc_span::{BytePos, Span}; +use rustc_span::{BytePos, Span, Symbol}; declare_clippy_lint! { /// ### What it does @@ -95,14 +95,14 @@ impl CollapsibleIf { fn check_collapsible_else_if(&self, cx: &LateContext<'_>, then_span: Span, else_block: &Block<'_>) { if let Some(else_) = expr_block(else_block) - && cx.tcx.hir_attrs(else_.hir_id).is_empty() && !else_.span.from_expansion() && let ExprKind::If(else_if_cond, ..) = else_.kind - && !block_starts_with_significant_tokens(cx, else_block, else_, self.lint_commented_code) + && self.check_significant_tokens_and_expect_attrs(cx, else_block, else_, sym::collapsible_else_if) { - span_lint_and_then( + span_lint_hir_and_then( cx, COLLAPSIBLE_ELSE_IF, + else_.hir_id, else_block.span, "this `else { if .. }` block can be collapsed", |diag| { @@ -166,15 +166,15 @@ impl CollapsibleIf { fn check_collapsible_if_if(&self, cx: &LateContext<'_>, expr: &Expr<'_>, check: &Expr<'_>, then: &Block<'_>) { if let Some(inner) = expr_block(then) - && cx.tcx.hir_attrs(inner.hir_id).is_empty() && let ExprKind::If(check_inner, _, None) = &inner.kind && self.eligible_condition(cx, check_inner) && expr.span.eq_ctxt(inner.span) - && !block_starts_with_significant_tokens(cx, then, inner, self.lint_commented_code) + && self.check_significant_tokens_and_expect_attrs(cx, then, inner, sym::collapsible_if) { - span_lint_and_then( + span_lint_hir_and_then( cx, COLLAPSIBLE_IF, + inner.hir_id, expr.span, "this `if` statement can be collapsed", |diag| { @@ -219,6 +219,45 @@ impl CollapsibleIf { !matches!(cond.kind, ExprKind::Let(..)) || (cx.tcx.sess.edition().at_least_rust_2024() && self.msrv.meets(cx, msrvs::LET_CHAINS)) } + + // Check that nothing significant can be found between the initial `{` of `inner_if` and + // the beginning of `inner_if_expr`... + // + // Unless it's only an `#[expect(clippy::collapsible{,_else}_if)]` attribute, in which case we + // _do_ need to lint, in order to actually fulfill its expectation (#13365) + fn check_significant_tokens_and_expect_attrs( + &self, + cx: &LateContext<'_>, + inner_if: &Block<'_>, + inner_if_expr: &Expr<'_>, + expected_lint_name: Symbol, + ) -> bool { + match cx.tcx.hir_attrs(inner_if_expr.hir_id) { + [] => { + // There aren't any attributes, so just check for significant tokens + let span = inner_if.span.split_at(1).1.until(inner_if_expr.span); + !span_contains_non_whitespace(cx, span, self.lint_commented_code) + }, + + [attr] + if matches!(Level::from_attr(attr), Some((Level::Expect, _))) + && let Some(metas) = attr.meta_item_list() + && let Some(MetaItemInner::MetaItem(meta_item)) = metas.first() + && let [tool, lint_name] = meta_item.path.segments.as_slice() + && tool.ident.name == sym::clippy + && [expected_lint_name, sym::style, sym::all].contains(&lint_name.ident.name) => + { + // There is an `expect` attribute -- check that there is no _other_ significant text + let span_before_attr = inner_if.span.split_at(1).1.until(attr.span()); + let span_after_attr = attr.span().between(inner_if_expr.span); + !span_contains_non_whitespace(cx, span_before_attr, self.lint_commented_code) + && !span_contains_non_whitespace(cx, span_after_attr, self.lint_commented_code) + }, + + // There are other attributes, which are significant tokens -- check failed + _ => false, + } + } } impl_lint_pass!(CollapsibleIf => [COLLAPSIBLE_IF, COLLAPSIBLE_ELSE_IF]); @@ -242,18 +281,6 @@ impl LateLintPass<'_> for CollapsibleIf { } } -// Check that nothing significant can be found but whitespaces between the initial `{` of `block` -// and the beginning of `stop_at`. -fn block_starts_with_significant_tokens( - cx: &LateContext<'_>, - block: &Block<'_>, - stop_at: &Expr<'_>, - lint_commented_code: bool, -) -> bool { - let span = block.span.split_at(1).1.until(stop_at.span); - span_contains_non_whitespace(cx, span, lint_commented_code) -} - /// If `block` is a block with either one expression or a statement containing an expression, /// return the expression. We don't peel blocks recursively, as extra blocks might be intentional. fn expr_block<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> { diff --git a/clippy_utils/src/sym.rs b/clippy_utils/src/sym.rs index 278101ac27f9..463711195bb0 100644 --- a/clippy_utils/src/sym.rs +++ b/clippy_utils/src/sym.rs @@ -111,6 +111,8 @@ generate! { clone_into, cloned, cognitive_complexity, + collapsible_else_if, + collapsible_if, collect, const_ptr, contains, diff --git a/tests/ui-toml/collapsible_if/collapsible_else_if.fixed b/tests/ui-toml/collapsible_if/collapsible_else_if.fixed index 0dc0fc230c8d..ec45dfd2033a 100644 --- a/tests/ui-toml/collapsible_if/collapsible_else_if.fixed +++ b/tests/ui-toml/collapsible_if/collapsible_else_if.fixed @@ -1,7 +1,7 @@ #![allow(clippy::eq_op, clippy::nonminimal_bool)] +#![warn(clippy::collapsible_if)] #[rustfmt::skip] -#[warn(clippy::collapsible_if)] fn main() { let (x, y) = ("hello", "world"); @@ -48,3 +48,20 @@ fn main() { } //~^^^^^^ collapsible_else_if } + +fn issue_13365() { + // the comments don't stop us from linting, so the the `expect` *will* be fulfilled + if true { + } else { + // some other text before + #[expect(clippy::collapsible_else_if)] + if false {} + } + + if true { + } else { + #[expect(clippy::collapsible_else_if)] + // some other text after + if false {} + } +} diff --git a/tests/ui-toml/collapsible_if/collapsible_else_if.rs b/tests/ui-toml/collapsible_if/collapsible_else_if.rs index 8344c122f16c..54315a3c32bf 100644 --- a/tests/ui-toml/collapsible_if/collapsible_else_if.rs +++ b/tests/ui-toml/collapsible_if/collapsible_else_if.rs @@ -1,7 +1,7 @@ #![allow(clippy::eq_op, clippy::nonminimal_bool)] +#![warn(clippy::collapsible_if)] #[rustfmt::skip] -#[warn(clippy::collapsible_if)] fn main() { let (x, y) = ("hello", "world"); @@ -53,3 +53,20 @@ fn main() { } //~^^^^^^ collapsible_else_if } + +fn issue_13365() { + // the comments don't stop us from linting, so the the `expect` *will* be fulfilled + if true { + } else { + // some other text before + #[expect(clippy::collapsible_else_if)] + if false {} + } + + if true { + } else { + #[expect(clippy::collapsible_else_if)] + // some other text after + if false {} + } +} diff --git a/tests/ui/collapsible_else_if.fixed b/tests/ui/collapsible_else_if.fixed index 3d709fe9b8e0..da958f76a5ca 100644 --- a/tests/ui/collapsible_else_if.fixed +++ b/tests/ui/collapsible_else_if.fixed @@ -87,6 +87,33 @@ fn issue_7318() { //~^^^ collapsible_else_if } +fn issue_13365() { + // all the `expect`s that we should fulfill + if true { + } else { + #[expect(clippy::collapsible_else_if)] + if false {} + } + + if true { + } else { + #[expect(clippy::style)] + if false {} + } + + if true { + } else { + #[expect(clippy::all)] + if false {} + } + + if true { + } else { + #[expect(warnings)] + if false {} + } +} + fn issue14799() { use std::ops::ControlFlow; diff --git a/tests/ui/collapsible_else_if.rs b/tests/ui/collapsible_else_if.rs index 51868e039086..06af49f2f6f3 100644 --- a/tests/ui/collapsible_else_if.rs +++ b/tests/ui/collapsible_else_if.rs @@ -103,6 +103,33 @@ fn issue_7318() { //~^^^ collapsible_else_if } +fn issue_13365() { + // all the `expect`s that we should fulfill + if true { + } else { + #[expect(clippy::collapsible_else_if)] + if false {} + } + + if true { + } else { + #[expect(clippy::style)] + if false {} + } + + if true { + } else { + #[expect(clippy::all)] + if false {} + } + + if true { + } else { + #[expect(warnings)] + if false {} + } +} + fn issue14799() { use std::ops::ControlFlow; diff --git a/tests/ui/collapsible_else_if.stderr b/tests/ui/collapsible_else_if.stderr index 1a7bcec7fd5d..ce1da593a8e9 100644 --- a/tests/ui/collapsible_else_if.stderr +++ b/tests/ui/collapsible_else_if.stderr @@ -151,7 +151,7 @@ LL | | } | |_____^ help: collapse nested if block: `if false {}` error: this `else { if .. }` block can be collapsed - --> tests/ui/collapsible_else_if.rs:130:12 + --> tests/ui/collapsible_else_if.rs:157:12 | LL | } else { | ____________^ diff --git a/tests/ui/collapsible_else_if_unfixable.rs b/tests/ui/collapsible_else_if_unfixable.rs new file mode 100644 index 000000000000..e5c18cf3ba90 --- /dev/null +++ b/tests/ui/collapsible_else_if_unfixable.rs @@ -0,0 +1,22 @@ +//@no-rustfix +#![warn(clippy::collapsible_else_if)] + +fn issue_13365() { + // in the following examples, we won't lint because of the comments, + // so the the `expect` will be unfulfilled + if true { + } else { + // some other text before + #[expect(clippy::collapsible_else_if)] + if false {} + } + //~^^^ ERROR: this lint expectation is unfulfilled + + if true { + } else { + #[expect(clippy::collapsible_else_if)] + // some other text after + if false {} + } + //~^^^^ ERROR: this lint expectation is unfulfilled +} diff --git a/tests/ui/collapsible_else_if_unfixable.stderr b/tests/ui/collapsible_else_if_unfixable.stderr new file mode 100644 index 000000000000..b461ceba6de7 --- /dev/null +++ b/tests/ui/collapsible_else_if_unfixable.stderr @@ -0,0 +1,17 @@ +error: this lint expectation is unfulfilled + --> tests/ui/collapsible_else_if_unfixable.rs:10:18 + | +LL | #[expect(clippy::collapsible_else_if)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D unfulfilled-lint-expectations` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(unfulfilled_lint_expectations)]` + +error: this lint expectation is unfulfilled + --> tests/ui/collapsible_else_if_unfixable.rs:17:18 + | +LL | #[expect(clippy::collapsible_else_if)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/collapsible_if.fixed b/tests/ui/collapsible_if.fixed index 78354c2d7cf8..ca9d02ff2d4f 100644 --- a/tests/ui/collapsible_if.fixed +++ b/tests/ui/collapsible_if.fixed @@ -144,6 +144,24 @@ fn layout_check() -> u32 { //~^^^^^ collapsible_if } +fn issue13365() { + // all the `expect`s that we should fulfill + if true { + #[expect(clippy::collapsible_if)] + if true {} + } + + if true { + #[expect(clippy::style)] + if true {} + } + + if true { + #[expect(clippy::all)] + if true {} + } +} + fn issue14722() { let x = if true { Some(1) diff --git a/tests/ui/collapsible_if.rs b/tests/ui/collapsible_if.rs index 5d9afa109569..9ac68ecd4cac 100644 --- a/tests/ui/collapsible_if.rs +++ b/tests/ui/collapsible_if.rs @@ -154,6 +154,24 @@ fn layout_check() -> u32 { //~^^^^^ collapsible_if } +fn issue13365() { + // all the `expect`s that we should fulfill + if true { + #[expect(clippy::collapsible_if)] + if true {} + } + + if true { + #[expect(clippy::style)] + if true {} + } + + if true { + #[expect(clippy::all)] + if true {} + } +} + fn issue14722() { let x = if true { Some(1) diff --git a/tests/ui/collapsible_if.stderr b/tests/ui/collapsible_if.stderr index a685cc2e9291..b1f26630f529 100644 --- a/tests/ui/collapsible_if.stderr +++ b/tests/ui/collapsible_if.stderr @@ -191,7 +191,7 @@ LL ~ ; 3 | error: this `if` statement can be collapsed - --> tests/ui/collapsible_if.rs:178:5 + --> tests/ui/collapsible_if.rs:196:5 | LL | / if true { LL | | (if true { diff --git a/tests/ui/collapsible_if_unfixable.rs b/tests/ui/collapsible_if_unfixable.rs new file mode 100644 index 000000000000..643520ac0f5d --- /dev/null +++ b/tests/ui/collapsible_if_unfixable.rs @@ -0,0 +1,20 @@ +//@ no-rustfix +#![warn(clippy::collapsible_if)] + +fn issue13365() { + // in the following examples, we won't lint because of the comments, + // so the the `expect` will be unfulfilled + if true { + // don't collapsible because of this comment + #[expect(clippy::collapsible_if)] + if true {} + } + //~^^^ ERROR: this lint expectation is unfulfilled + + if true { + #[expect(clippy::collapsible_if)] + // don't collapsible because of this comment + if true {} + } + //~^^^^ ERROR: this lint expectation is unfulfilled +} diff --git a/tests/ui/collapsible_if_unfixable.stderr b/tests/ui/collapsible_if_unfixable.stderr new file mode 100644 index 000000000000..64c8fb8da2b4 --- /dev/null +++ b/tests/ui/collapsible_if_unfixable.stderr @@ -0,0 +1,17 @@ +error: this lint expectation is unfulfilled + --> tests/ui/collapsible_if_unfixable.rs:9:18 + | +LL | #[expect(clippy::collapsible_if)] + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D unfulfilled-lint-expectations` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(unfulfilled_lint_expectations)]` + +error: this lint expectation is unfulfilled + --> tests/ui/collapsible_if_unfixable.rs:15:18 + | +LL | #[expect(clippy::collapsible_if)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + From 4d931e643af306eeabc417957975271879b68c50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Wed, 17 Sep 2025 04:16:47 +0200 Subject: [PATCH 012/226] Remove `DynKind` --- clippy_utils/src/qualify_min_const_fn.rs | 2 +- clippy_utils/src/ty/mod.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index 68f0b5ea2558..ac87e21b82f1 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -86,7 +86,7 @@ fn check_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, msrv: Msrv) ty::FnPtr(..) => { return Err((span, "function pointers in const fn are unstable".into())); }, - ty::Dynamic(preds, _, _) => { + ty::Dynamic(preds, _) => { for pred in *preds { match pred.skip_binder() { ty::ExistentialPredicate::AutoTrait(_) | ty::ExistentialPredicate::Projection(_) => { diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index 9f77a1c4d9b6..3e41bce1dc4c 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -349,7 +349,7 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { } false }, - ty::Dynamic(binder, _, _) => { + ty::Dynamic(binder, _) => { for predicate in *binder { if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() && find_attr!(cx.tcx.get_all_attrs(trait_ref.def_id), AttributeKind::MustUse { .. }) @@ -673,7 +673,7 @@ pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option Some(ExprFnSig::Sig(sig_tys.with(hdr), None)), - ty::Dynamic(bounds, _, _) => { + ty::Dynamic(bounds, _) => { let lang_items = cx.tcx.lang_items(); match bounds.principal() { Some(bound) From 2972ae5e539162778f83529599040d5bbc4adca4 Mon Sep 17 00:00:00 2001 From: Emmanuel Thompson Date: Sat, 28 Jun 2025 19:39:43 -0400 Subject: [PATCH 013/226] rename_lint: unchecked_duration_subtraction to unchecked_time_subtraction --- CHANGELOG.md | 1 + book/src/lint_configuration.md | 2 +- clippy_config/src/conf.rs | 2 +- clippy_lints/src/declared_lints.rs | 2 +- clippy_lints/src/deprecated_lints.rs | 2 + clippy_lints/src/instant_subtraction.rs | 6 +- tests/ui/manual_instant_elapsed.fixed | 2 +- tests/ui/manual_instant_elapsed.rs | 2 +- tests/ui/rename.fixed | 2 + tests/ui/rename.rs | 2 + tests/ui/rename.stderr | 154 +++++++++--------- ...fixed => unchecked_time_subtraction.fixed} | 10 +- ...ction.rs => unchecked_time_subtraction.rs} | 10 +- ...derr => unchecked_time_subtraction.stderr} | 12 +- 14 files changed, 111 insertions(+), 98 deletions(-) rename tests/ui/{unchecked_duration_subtraction.fixed => unchecked_time_subtraction.fixed} (64%) rename tests/ui/{unchecked_duration_subtraction.rs => unchecked_time_subtraction.rs} (58%) rename tests/ui/{unchecked_duration_subtraction.stderr => unchecked_time_subtraction.stderr} (75%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a9be0214372..2144d68ae152 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6702,6 +6702,7 @@ Released 2018-09-13 [`type_repetition_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds [`unbuffered_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#unbuffered_bytes [`unchecked_duration_subtraction`]: https://rust-lang.github.io/rust-clippy/master/index.html#unchecked_duration_subtraction +[`unchecked_time_subtraction`]: https://rust-lang.github.io/rust-clippy/master/index.html#unchecked_time_subtraction [`unconditional_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#unconditional_recursion [`undocumented_unsafe_blocks`]: https://rust-lang.github.io/rust-clippy/master/index.html#undocumented_unsafe_blocks [`undropped_manually_drops`]: https://rust-lang.github.io/rust-clippy/master/index.html#undropped_manually_drops diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index c2d080cd96a1..5502e6ff5e83 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -894,7 +894,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`transmute_ptr_to_ref`](https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref) * [`tuple_array_conversions`](https://rust-lang.github.io/rust-clippy/master/index.html#tuple_array_conversions) * [`type_repetition_in_bounds`](https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds) -* [`unchecked_duration_subtraction`](https://rust-lang.github.io/rust-clippy/master/index.html#unchecked_duration_subtraction) +* [`unchecked_time_subtraction`](https://rust-lang.github.io/rust-clippy/master/index.html#unchecked_time_subtraction) * [`uninlined_format_args`](https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args) * [`unnecessary_lazy_evaluations`](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations) * [`unnested_or_patterns`](https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns) diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 2f28f6175ad8..ae994815f63a 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -790,7 +790,7 @@ define_Conf! { transmute_ptr_to_ref, tuple_array_conversions, type_repetition_in_bounds, - unchecked_duration_subtraction, + unchecked_time_subtraction, uninlined_format_args, unnecessary_lazy_evaluations, unnested_or_patterns, diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 1b19a8851edd..7734f2266308 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -227,7 +227,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::init_numbered_fields::INIT_NUMBERED_FIELDS_INFO, crate::inline_fn_without_body::INLINE_FN_WITHOUT_BODY_INFO, crate::instant_subtraction::MANUAL_INSTANT_ELAPSED_INFO, - crate::instant_subtraction::UNCHECKED_DURATION_SUBTRACTION_INFO, + crate::instant_subtraction::UNCHECKED_TIME_SUBTRACTION_INFO, crate::int_plus_one::INT_PLUS_ONE_INFO, crate::integer_division_remainder_used::INTEGER_DIVISION_REMAINDER_USED_INFO, crate::invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS_INFO, diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index 88aebc3e6a16..80b74f50223a 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -184,6 +184,8 @@ declare_with_version! { RENAMED(RENAMED_VERSION) = [ ("clippy::transmute_int_to_float", "unnecessary_transmutes"), #[clippy::version = "1.88.0"] ("clippy::transmute_num_to_bytes", "unnecessary_transmutes"), + #[clippy::version = "1.90.0"] + ("clippy::unchecked_duration_subtraction", "clippy::unchecked_time_subtraction"), #[clippy::version = ""] ("clippy::undropped_manually_drops", "undropped_manually_drops"), #[clippy::version = ""] diff --git a/clippy_lints/src/instant_subtraction.rs b/clippy_lints/src/instant_subtraction.rs index 13117f60abd5..5fe26e3e3e0c 100644 --- a/clippy_lints/src/instant_subtraction.rs +++ b/clippy_lints/src/instant_subtraction.rs @@ -59,7 +59,7 @@ declare_clippy_lint! { /// let time_passed = Instant::now().checked_sub(Duration::from_secs(5)); /// ``` #[clippy::version = "1.67.0"] - pub UNCHECKED_DURATION_SUBTRACTION, + pub UNCHECKED_TIME_SUBTRACTION, pedantic, "finds unchecked subtraction of a 'Duration' from an 'Instant'" } @@ -74,7 +74,7 @@ impl InstantSubtraction { } } -impl_lint_pass!(InstantSubtraction => [MANUAL_INSTANT_ELAPSED, UNCHECKED_DURATION_SUBTRACTION]); +impl_lint_pass!(InstantSubtraction => [MANUAL_INSTANT_ELAPSED, UNCHECKED_TIME_SUBTRACTION]); impl LateLintPass<'_> for InstantSubtraction { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { @@ -141,7 +141,7 @@ fn print_unchecked_duration_subtraction_sugg( span_lint_and_sugg( cx, - UNCHECKED_DURATION_SUBTRACTION, + UNCHECKED_TIME_SUBTRACTION, expr.span, "unchecked subtraction of a 'Duration' from an 'Instant'", "try", diff --git a/tests/ui/manual_instant_elapsed.fixed b/tests/ui/manual_instant_elapsed.fixed index 187802bb76c9..a04c601e08c1 100644 --- a/tests/ui/manual_instant_elapsed.fixed +++ b/tests/ui/manual_instant_elapsed.fixed @@ -1,6 +1,6 @@ #![warn(clippy::manual_instant_elapsed)] #![allow(clippy::unnecessary_operation)] -#![allow(clippy::unchecked_duration_subtraction)] +#![allow(clippy::unchecked_time_subtraction)] #![allow(unused_variables)] #![allow(unused_must_use)] diff --git a/tests/ui/manual_instant_elapsed.rs b/tests/ui/manual_instant_elapsed.rs index 61e14e5a3d9d..7c67f6acf85d 100644 --- a/tests/ui/manual_instant_elapsed.rs +++ b/tests/ui/manual_instant_elapsed.rs @@ -1,6 +1,6 @@ #![warn(clippy::manual_instant_elapsed)] #![allow(clippy::unnecessary_operation)] -#![allow(clippy::unchecked_duration_subtraction)] +#![allow(clippy::unchecked_time_subtraction)] #![allow(unused_variables)] #![allow(unused_must_use)] diff --git a/tests/ui/rename.fixed b/tests/ui/rename.fixed index ff81c6426027..fdd851414746 100644 --- a/tests/ui/rename.fixed +++ b/tests/ui/rename.fixed @@ -58,6 +58,7 @@ #![allow(clippy::missing_const_for_thread_local)] #![allow(clippy::recursive_format_impl)] #![allow(unnecessary_transmutes)] +#![allow(clippy::unchecked_time_subtraction)] #![allow(undropped_manually_drops)] #![allow(unknown_lints)] #![allow(unused_labels)] @@ -131,6 +132,7 @@ #![warn(unnecessary_transmutes)] //~ ERROR: lint `clippy::transmute_int_to_char` #![warn(unnecessary_transmutes)] //~ ERROR: lint `clippy::transmute_int_to_float` #![warn(unnecessary_transmutes)] //~ ERROR: lint `clippy::transmute_num_to_bytes` +#![warn(clippy::unchecked_time_subtraction)] //~ ERROR: lint `clippy::unchecked_duration_subtraction` #![warn(undropped_manually_drops)] //~ ERROR: lint `clippy::undropped_manually_drops` #![warn(unknown_lints)] //~ ERROR: lint `clippy::unknown_clippy_lints` #![warn(unused_labels)] //~ ERROR: lint `clippy::unused_label` diff --git a/tests/ui/rename.rs b/tests/ui/rename.rs index b5d5d07e639a..591c8ca53ac2 100644 --- a/tests/ui/rename.rs +++ b/tests/ui/rename.rs @@ -58,6 +58,7 @@ #![allow(clippy::missing_const_for_thread_local)] #![allow(clippy::recursive_format_impl)] #![allow(unnecessary_transmutes)] +#![allow(clippy::unchecked_time_subtraction)] #![allow(undropped_manually_drops)] #![allow(unknown_lints)] #![allow(unused_labels)] @@ -131,6 +132,7 @@ #![warn(clippy::transmute_int_to_char)] //~ ERROR: lint `clippy::transmute_int_to_char` #![warn(clippy::transmute_int_to_float)] //~ ERROR: lint `clippy::transmute_int_to_float` #![warn(clippy::transmute_num_to_bytes)] //~ ERROR: lint `clippy::transmute_num_to_bytes` +#![warn(clippy::unchecked_duration_subtraction)] //~ ERROR: lint `clippy::unchecked_duration_subtraction` #![warn(clippy::undropped_manually_drops)] //~ ERROR: lint `clippy::undropped_manually_drops` #![warn(clippy::unknown_clippy_lints)] //~ ERROR: lint `clippy::unknown_clippy_lints` #![warn(clippy::unused_label)] //~ ERROR: lint `clippy::unused_label` diff --git a/tests/ui/rename.stderr b/tests/ui/rename.stderr index 2487dfc8eba4..b54fec8c5794 100644 --- a/tests/ui/rename.stderr +++ b/tests/ui/rename.stderr @@ -1,5 +1,5 @@ error: lint `clippy::almost_complete_letter_range` has been renamed to `clippy::almost_complete_range` - --> tests/ui/rename.rs:67:9 + --> tests/ui/rename.rs:68:9 | LL | #![warn(clippy::almost_complete_letter_range)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::almost_complete_range` @@ -8,436 +8,442 @@ LL | #![warn(clippy::almost_complete_letter_range)] = help: to override `-D warnings` add `#[allow(renamed_and_removed_lints)]` error: lint `clippy::blacklisted_name` has been renamed to `clippy::disallowed_names` - --> tests/ui/rename.rs:68:9 + --> tests/ui/rename.rs:69:9 | LL | #![warn(clippy::blacklisted_name)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_names` error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_conditions` - --> tests/ui/rename.rs:69:9 + --> tests/ui/rename.rs:70:9 | LL | #![warn(clippy::block_in_if_condition_expr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions` error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_conditions` - --> tests/ui/rename.rs:70:9 + --> tests/ui/rename.rs:71:9 | LL | #![warn(clippy::block_in_if_condition_stmt)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions` error: lint `clippy::blocks_in_if_conditions` has been renamed to `clippy::blocks_in_conditions` - --> tests/ui/rename.rs:71:9 + --> tests/ui/rename.rs:72:9 | LL | #![warn(clippy::blocks_in_if_conditions)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions` error: lint `clippy::box_vec` has been renamed to `clippy::box_collection` - --> tests/ui/rename.rs:72:9 + --> tests/ui/rename.rs:73:9 | LL | #![warn(clippy::box_vec)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection` error: lint `clippy::cast_ref_to_mut` has been renamed to `invalid_reference_casting` - --> tests/ui/rename.rs:73:9 + --> tests/ui/rename.rs:74:9 | LL | #![warn(clippy::cast_ref_to_mut)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_reference_casting` error: lint `clippy::clone_double_ref` has been renamed to `suspicious_double_ref_op` - --> tests/ui/rename.rs:74:9 + --> tests/ui/rename.rs:75:9 | LL | #![warn(clippy::clone_double_ref)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `suspicious_double_ref_op` error: lint `clippy::cmp_nan` has been renamed to `invalid_nan_comparisons` - --> tests/ui/rename.rs:75:9 + --> tests/ui/rename.rs:76:9 | LL | #![warn(clippy::cmp_nan)] | ^^^^^^^^^^^^^^^ help: use the new name: `invalid_nan_comparisons` error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` - --> tests/ui/rename.rs:76:9 + --> tests/ui/rename.rs:77:9 | LL | #![warn(clippy::const_static_lifetime)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes` error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity` - --> tests/ui/rename.rs:77:9 + --> tests/ui/rename.rs:78:9 | LL | #![warn(clippy::cyclomatic_complexity)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity` error: lint `clippy::derive_hash_xor_eq` has been renamed to `clippy::derived_hash_with_manual_eq` - --> tests/ui/rename.rs:78:9 + --> tests/ui/rename.rs:79:9 | LL | #![warn(clippy::derive_hash_xor_eq)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::derived_hash_with_manual_eq` error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods` - --> tests/ui/rename.rs:79:9 + --> tests/ui/rename.rs:80:9 | LL | #![warn(clippy::disallowed_method)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods` error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types` - --> tests/ui/rename.rs:80:9 + --> tests/ui/rename.rs:81:9 | LL | #![warn(clippy::disallowed_type)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types` error: lint `clippy::double_neg` has been renamed to `double_negations` - --> tests/ui/rename.rs:81:9 + --> tests/ui/rename.rs:82: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:82:9 + --> tests/ui/rename.rs:83: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:83:9 + --> tests/ui/rename.rs:84: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:84:9 + --> tests/ui/rename.rs:85:9 | LL | #![warn(clippy::drop_ref)] | ^^^^^^^^^^^^^^^^ help: use the new name: `dropping_references` error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression` - --> tests/ui/rename.rs:85:9 + --> tests/ui/rename.rs:86:9 | LL | #![warn(clippy::eval_order_dependence)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression` error: lint `clippy::filter_map` has been renamed to `clippy::manual_filter_map` - --> tests/ui/rename.rs:86:9 + --> tests/ui/rename.rs:87:9 | LL | #![warn(clippy::filter_map)] | ^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::manual_filter_map` error: lint `clippy::find_map` has been renamed to `clippy::manual_find_map` - --> tests/ui/rename.rs:87:9 + --> tests/ui/rename.rs:88:9 | LL | #![warn(clippy::find_map)] | ^^^^^^^^^^^^^^^^ help: use the new name: `clippy::manual_find_map` error: lint `clippy::fn_address_comparisons` has been renamed to `unpredictable_function_pointer_comparisons` - --> tests/ui/rename.rs:88:9 + --> tests/ui/rename.rs:89: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:89:9 + --> tests/ui/rename.rs:90: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:90:9 + --> tests/ui/rename.rs:91: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:91:9 + --> tests/ui/rename.rs:92: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:92:9 + --> tests/ui/rename.rs:93: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:93:9 + --> tests/ui/rename.rs:94: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:94:9 + --> tests/ui/rename.rs:95:9 | LL | #![warn(clippy::forget_ref)] | ^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_references` error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` - --> tests/ui/rename.rs:95:9 + --> tests/ui/rename.rs:96: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:96:9 + --> tests/ui/rename.rs:97: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:97:9 + --> tests/ui/rename.rs:98: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:98:9 + --> tests/ui/rename.rs:99: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:99:9 + --> tests/ui/rename.rs:100: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:100:9 + --> tests/ui/rename.rs:101:9 | LL | #![warn(clippy::integer_arithmetic)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::arithmetic_side_effects` error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` - --> tests/ui/rename.rs:101:9 + --> tests/ui/rename.rs:102: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:102:9 + --> tests/ui/rename.rs:103:9 | LL | #![warn(clippy::invalid_atomic_ordering)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` error: lint `clippy::invalid_null_ptr_usage` has been renamed to `invalid_null_arguments` - --> tests/ui/rename.rs:103:9 + --> tests/ui/rename.rs:104:9 | LL | #![warn(clippy::invalid_null_ptr_usage)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_null_arguments` error: lint `clippy::invalid_ref` has been renamed to `invalid_value` - --> tests/ui/rename.rs:104:9 + --> tests/ui/rename.rs:105: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:105:9 + --> tests/ui/rename.rs:106: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:106:9 + --> tests/ui/rename.rs:107:9 | LL | #![warn(clippy::let_underscore_drop)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop` error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr` - --> tests/ui/rename.rs:107:9 + --> tests/ui/rename.rs:108:9 | LL | #![warn(clippy::logic_bug)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr` error: lint `clippy::maybe_misused_cfg` has been renamed to `unexpected_cfgs` - --> tests/ui/rename.rs:108:9 + --> tests/ui/rename.rs:109: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:109:9 + --> tests/ui/rename.rs:110: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:110:9 + --> tests/ui/rename.rs:111:9 | LL | #![warn(clippy::mismatched_target_os)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unexpected_cfgs` error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` - --> tests/ui/rename.rs:111:9 + --> tests/ui/rename.rs:112: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:112:9 + --> tests/ui/rename.rs:113: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:113:9 + --> tests/ui/rename.rs:114: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:114:9 + --> tests/ui/rename.rs:115: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:115:9 + --> tests/ui/rename.rs:116: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:116:9 + --> tests/ui/rename.rs:117: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:117:9 + --> tests/ui/rename.rs:118:9 | LL | #![warn(clippy::overflow_check_conditional)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::panicking_overflow_checks` error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` - --> tests/ui/rename.rs:118:9 + --> tests/ui/rename.rs:119: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:119:9 + --> tests/ui/rename.rs:120:9 | LL | #![warn(clippy::positional_named_format_parameters)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally` error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` - --> tests/ui/rename.rs:120:9 + --> tests/ui/rename.rs:121: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:121:9 + --> tests/ui/rename.rs:122: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:122:9 + --> tests/ui/rename.rs:123: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:123:9 + --> tests/ui/rename.rs:124: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:124:9 + --> tests/ui/rename.rs:125: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:125:9 + --> tests/ui/rename.rs:126:9 | LL | #![warn(clippy::single_char_push_str)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str` error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` - --> tests/ui/rename.rs:126:9 + --> tests/ui/rename.rs:127:9 | LL | #![warn(clippy::stutter)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `dangling_pointers_from_temporaries` - --> tests/ui/rename.rs:127: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::thread_local_initializer_can_be_made_const` has been renamed to `clippy::missing_const_for_thread_local` - --> tests/ui/rename.rs:128:9 + --> tests/ui/rename.rs:129:9 | LL | #![warn(clippy::thread_local_initializer_can_be_made_const)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::missing_const_for_thread_local` error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl` - --> tests/ui/rename.rs:129:9 + --> tests/ui/rename.rs:130:9 | LL | #![warn(clippy::to_string_in_display)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` error: lint `clippy::transmute_float_to_int` has been renamed to `unnecessary_transmutes` - --> tests/ui/rename.rs:130:9 + --> tests/ui/rename.rs:131:9 | LL | #![warn(clippy::transmute_float_to_int)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` error: lint `clippy::transmute_int_to_char` has been renamed to `unnecessary_transmutes` - --> tests/ui/rename.rs:131:9 + --> tests/ui/rename.rs:132:9 | LL | #![warn(clippy::transmute_int_to_char)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` error: lint `clippy::transmute_int_to_float` has been renamed to `unnecessary_transmutes` - --> tests/ui/rename.rs:132:9 + --> tests/ui/rename.rs:133:9 | LL | #![warn(clippy::transmute_int_to_float)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` error: lint `clippy::transmute_num_to_bytes` has been renamed to `unnecessary_transmutes` - --> tests/ui/rename.rs:133:9 + --> tests/ui/rename.rs:134:9 | LL | #![warn(clippy::transmute_num_to_bytes)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` +error: lint `clippy::unchecked_duration_subtraction` has been renamed to `clippy::unchecked_time_subtraction` + --> tests/ui/rename.rs:135:9 + | +LL | #![warn(clippy::unchecked_duration_subtraction)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unchecked_time_subtraction` + error: lint `clippy::undropped_manually_drops` has been renamed to `undropped_manually_drops` - --> tests/ui/rename.rs:134:9 + --> tests/ui/rename.rs:136: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:135:9 + --> tests/ui/rename.rs:137: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:136:9 + --> tests/ui/rename.rs:138:9 | LL | #![warn(clippy::unused_label)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` error: lint `clippy::unwrap_or_else_default` has been renamed to `clippy::unwrap_or_default` - --> tests/ui/rename.rs:137:9 + --> tests/ui/rename.rs:139:9 | LL | #![warn(clippy::unwrap_or_else_default)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_or_default` error: lint `clippy::vtable_address_comparisons` has been renamed to `ambiguous_wide_pointer_comparisons` - --> tests/ui/rename.rs:138:9 + --> tests/ui/rename.rs:140:9 | LL | #![warn(clippy::vtable_address_comparisons)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `ambiguous_wide_pointer_comparisons` error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` - --> tests/ui/rename.rs:139:9 + --> tests/ui/rename.rs:141:9 | LL | #![warn(clippy::zero_width_space)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` -error: aborting due to 73 previous errors +error: aborting due to 74 previous errors diff --git a/tests/ui/unchecked_duration_subtraction.fixed b/tests/ui/unchecked_time_subtraction.fixed similarity index 64% rename from tests/ui/unchecked_duration_subtraction.fixed rename to tests/ui/unchecked_time_subtraction.fixed index bddffe44ac4d..0850226eeea7 100644 --- a/tests/ui/unchecked_duration_subtraction.fixed +++ b/tests/ui/unchecked_time_subtraction.fixed @@ -1,4 +1,4 @@ -#![warn(clippy::unchecked_duration_subtraction)] +#![warn(clippy::unchecked_time_subtraction)] use std::time::{Duration, Instant}; @@ -7,14 +7,14 @@ fn main() { let second = Duration::from_secs(3); let _ = _first.checked_sub(second).unwrap(); - //~^ unchecked_duration_subtraction + //~^ unchecked_time_subtraction let _ = Instant::now().checked_sub(Duration::from_secs(5)).unwrap(); - //~^ unchecked_duration_subtraction + //~^ unchecked_time_subtraction let _ = _first.checked_sub(Duration::from_secs(5)).unwrap(); - //~^ unchecked_duration_subtraction + //~^ unchecked_time_subtraction let _ = Instant::now().checked_sub(second).unwrap(); - //~^ unchecked_duration_subtraction + //~^ unchecked_time_subtraction } diff --git a/tests/ui/unchecked_duration_subtraction.rs b/tests/ui/unchecked_time_subtraction.rs similarity index 58% rename from tests/ui/unchecked_duration_subtraction.rs rename to tests/ui/unchecked_time_subtraction.rs index bb0f71239642..a2dbfd1c1fad 100644 --- a/tests/ui/unchecked_duration_subtraction.rs +++ b/tests/ui/unchecked_time_subtraction.rs @@ -1,4 +1,4 @@ -#![warn(clippy::unchecked_duration_subtraction)] +#![warn(clippy::unchecked_time_subtraction)] use std::time::{Duration, Instant}; @@ -7,14 +7,14 @@ fn main() { let second = Duration::from_secs(3); let _ = _first - second; - //~^ unchecked_duration_subtraction + //~^ unchecked_time_subtraction let _ = Instant::now() - Duration::from_secs(5); - //~^ unchecked_duration_subtraction + //~^ unchecked_time_subtraction let _ = _first - Duration::from_secs(5); - //~^ unchecked_duration_subtraction + //~^ unchecked_time_subtraction let _ = Instant::now() - second; - //~^ unchecked_duration_subtraction + //~^ unchecked_time_subtraction } diff --git a/tests/ui/unchecked_duration_subtraction.stderr b/tests/ui/unchecked_time_subtraction.stderr similarity index 75% rename from tests/ui/unchecked_duration_subtraction.stderr rename to tests/ui/unchecked_time_subtraction.stderr index be291c320e68..da0d51dbaef1 100644 --- a/tests/ui/unchecked_duration_subtraction.stderr +++ b/tests/ui/unchecked_time_subtraction.stderr @@ -1,26 +1,26 @@ error: unchecked subtraction of a 'Duration' from an 'Instant' - --> tests/ui/unchecked_duration_subtraction.rs:9:13 + --> tests/ui/unchecked_time_subtraction.rs:9:13 | LL | let _ = _first - second; | ^^^^^^^^^^^^^^^ help: try: `_first.checked_sub(second).unwrap()` | - = note: `-D clippy::unchecked-duration-subtraction` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::unchecked_duration_subtraction)]` + = note: `-D clippy::unchecked-time-subtraction` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unchecked_time_subtraction)]` error: unchecked subtraction of a 'Duration' from an 'Instant' - --> tests/ui/unchecked_duration_subtraction.rs:12:13 + --> tests/ui/unchecked_time_subtraction.rs:12:13 | LL | let _ = Instant::now() - Duration::from_secs(5); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(Duration::from_secs(5)).unwrap()` error: unchecked subtraction of a 'Duration' from an 'Instant' - --> tests/ui/unchecked_duration_subtraction.rs:15:13 + --> tests/ui/unchecked_time_subtraction.rs:15:13 | LL | let _ = _first - Duration::from_secs(5); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `_first.checked_sub(Duration::from_secs(5)).unwrap()` error: unchecked subtraction of a 'Duration' from an 'Instant' - --> tests/ui/unchecked_duration_subtraction.rs:18:13 + --> tests/ui/unchecked_time_subtraction.rs:18:13 | LL | let _ = Instant::now() - second; | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(second).unwrap()` From b0566b949b1e68319ed9852e637c5a77aae45186 Mon Sep 17 00:00:00 2001 From: Emmanuel Thompson Date: Sat, 28 Jun 2025 19:41:55 -0400 Subject: [PATCH 014/226] rename module: instant_substraction to time_substraction --- clippy_lints/src/declared_lints.rs | 4 ++-- clippy_lints/src/lib.rs | 4 ++-- .../src/{instant_subtraction.rs => time_subtraction.rs} | 0 3 files changed, 4 insertions(+), 4 deletions(-) rename clippy_lints/src/{instant_subtraction.rs => time_subtraction.rs} (100%) diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 7734f2266308..6a2833e5f86e 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -226,8 +226,8 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY_INFO, crate::init_numbered_fields::INIT_NUMBERED_FIELDS_INFO, crate::inline_fn_without_body::INLINE_FN_WITHOUT_BODY_INFO, - crate::instant_subtraction::MANUAL_INSTANT_ELAPSED_INFO, - crate::instant_subtraction::UNCHECKED_TIME_SUBTRACTION_INFO, + crate::time_subtraction::MANUAL_INSTANT_ELAPSED_INFO, + crate::time_subtraction::UNCHECKED_TIME_SUBTRACTION_INFO, crate::int_plus_one::INT_PLUS_ONE_INFO, crate::integer_division_remainder_used::INTEGER_DIVISION_REMAINDER_USED_INFO, crate::invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c56fa257b068..1ab6eef5e57d 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -176,7 +176,6 @@ mod inherent_impl; mod inherent_to_string; mod init_numbered_fields; mod inline_fn_without_body; -mod instant_subtraction; mod int_plus_one; mod integer_division_remainder_used; mod invalid_upcast_comparisons; @@ -357,6 +356,7 @@ mod swap_ptr_to_ref; mod tabs_in_doc_comments; mod temporary_assignment; mod tests_outside_test_module; +mod time_subtraction; mod to_digit_is_some; mod to_string_trait_impl; mod toplevel_ref_arg; @@ -718,7 +718,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(move |_| Box::new(manual_rotate::ManualRotate)); store.register_late_pass(move |_| Box::new(operators::Operators::new(conf))); store.register_late_pass(move |_| Box::new(std_instead_of_core::StdReexports::new(conf))); - store.register_late_pass(move |_| Box::new(instant_subtraction::InstantSubtraction::new(conf))); + store.register_late_pass(move |_| Box::new(time_subtraction::InstantSubtraction::new(conf))); store.register_late_pass(|_| Box::new(partialeq_to_none::PartialeqToNone)); store.register_late_pass(move |_| Box::new(manual_abs_diff::ManualAbsDiff::new(conf))); store.register_late_pass(move |_| Box::new(manual_clamp::ManualClamp::new(conf))); diff --git a/clippy_lints/src/instant_subtraction.rs b/clippy_lints/src/time_subtraction.rs similarity index 100% rename from clippy_lints/src/instant_subtraction.rs rename to clippy_lints/src/time_subtraction.rs From f296de4665cec2f1200496a0432667a150322f9b Mon Sep 17 00:00:00 2001 From: Emmanuel Thompson Date: Sat, 28 Jun 2025 19:47:24 -0400 Subject: [PATCH 015/226] Implement Duration - Duration Renames InstantSubtraction to UncheckedTimeSubtraction --- clippy_lints/src/declared_lints.rs | 4 +- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/time_subtraction.rs | 111 ++++++++++++++---- tests/ui/unchecked_time_subtraction.fixed | 17 +++ tests/ui/unchecked_time_subtraction.rs | 17 +++ tests/ui/unchecked_time_subtraction.stderr | 26 +++- .../unchecked_time_subtraction_unfixable.rs | 22 ++++ ...nchecked_time_subtraction_unfixable.stderr | 29 +++++ 8 files changed, 201 insertions(+), 27 deletions(-) create mode 100644 tests/ui/unchecked_time_subtraction_unfixable.rs create mode 100644 tests/ui/unchecked_time_subtraction_unfixable.stderr diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 6a2833e5f86e..e1b6ee41967a 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -226,8 +226,6 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY_INFO, crate::init_numbered_fields::INIT_NUMBERED_FIELDS_INFO, crate::inline_fn_without_body::INLINE_FN_WITHOUT_BODY_INFO, - crate::time_subtraction::MANUAL_INSTANT_ELAPSED_INFO, - crate::time_subtraction::UNCHECKED_TIME_SUBTRACTION_INFO, crate::int_plus_one::INT_PLUS_ONE_INFO, crate::integer_division_remainder_used::INTEGER_DIVISION_REMAINDER_USED_INFO, crate::invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS_INFO, @@ -703,6 +701,8 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::tabs_in_doc_comments::TABS_IN_DOC_COMMENTS_INFO, crate::temporary_assignment::TEMPORARY_ASSIGNMENT_INFO, crate::tests_outside_test_module::TESTS_OUTSIDE_TEST_MODULE_INFO, + crate::time_subtraction::MANUAL_INSTANT_ELAPSED_INFO, + crate::time_subtraction::UNCHECKED_TIME_SUBTRACTION_INFO, crate::to_digit_is_some::TO_DIGIT_IS_SOME_INFO, crate::to_string_trait_impl::TO_STRING_TRAIT_IMPL_INFO, crate::toplevel_ref_arg::TOPLEVEL_REF_ARG_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 1ab6eef5e57d..65c9a947b578 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -718,7 +718,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(move |_| Box::new(manual_rotate::ManualRotate)); store.register_late_pass(move |_| Box::new(operators::Operators::new(conf))); store.register_late_pass(move |_| Box::new(std_instead_of_core::StdReexports::new(conf))); - store.register_late_pass(move |_| Box::new(time_subtraction::InstantSubtraction::new(conf))); + store.register_late_pass(move |_| Box::new(time_subtraction::UncheckedTimeSubtraction::new(conf))); store.register_late_pass(|_| Box::new(partialeq_to_none::PartialeqToNone)); store.register_late_pass(move |_| Box::new(manual_abs_diff::ManualAbsDiff::new(conf))); store.register_late_pass(move |_| Box::new(manual_clamp::ManualClamp::new(conf))); diff --git a/clippy_lints/src/time_subtraction.rs b/clippy_lints/src/time_subtraction.rs index 5fe26e3e3e0c..fde8c3d9a9a7 100644 --- a/clippy_lints/src/time_subtraction.rs +++ b/clippy_lints/src/time_subtraction.rs @@ -1,12 +1,12 @@ use clippy_config::Conf; -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::snippet_with_context; use clippy_utils::sugg::Sugg; use clippy_utils::{is_path_diagnostic_item, ty}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::Ty; use rustc_session::impl_lint_pass; use rustc_span::source_map::Spanned; use rustc_span::sym; @@ -41,7 +41,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Lints subtraction between an `Instant` and a `Duration`. + /// Lints subtraction between an `Instant` and a `Duration`, or between two `Duration` values. /// /// ### Why is this bad? /// Unchecked subtraction could cause underflow on certain platforms, leading to @@ -51,32 +51,38 @@ declare_clippy_lint! { /// ```no_run /// # use std::time::{Instant, Duration}; /// let time_passed = Instant::now() - Duration::from_secs(5); + /// let dur1 = Duration::from_secs(3); + /// let dur2 = Duration::from_secs(5); + /// let diff = dur1 - dur2; /// ``` /// /// Use instead: /// ```no_run /// # use std::time::{Instant, Duration}; /// let time_passed = Instant::now().checked_sub(Duration::from_secs(5)); + /// let dur1 = Duration::from_secs(3); + /// let dur2 = Duration::from_secs(5); + /// let diff = dur1.checked_sub(dur2); /// ``` #[clippy::version = "1.67.0"] pub UNCHECKED_TIME_SUBTRACTION, pedantic, - "finds unchecked subtraction of a 'Duration' from an 'Instant'" + "finds unchecked subtraction involving 'Duration' or 'Instant'" } -pub struct InstantSubtraction { +pub struct UncheckedTimeSubtraction { msrv: Msrv, } -impl InstantSubtraction { +impl UncheckedTimeSubtraction { pub fn new(conf: &'static Conf) -> Self { Self { msrv: conf.msrv } } } -impl_lint_pass!(InstantSubtraction => [MANUAL_INSTANT_ELAPSED, UNCHECKED_TIME_SUBTRACTION]); +impl_lint_pass!(UncheckedTimeSubtraction => [MANUAL_INSTANT_ELAPSED, UNCHECKED_TIME_SUBTRACTION]); -impl LateLintPass<'_> for InstantSubtraction { +impl LateLintPass<'_> for UncheckedTimeSubtraction { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { if let ExprKind::Binary( Spanned { @@ -85,21 +91,54 @@ impl LateLintPass<'_> for InstantSubtraction { lhs, rhs, ) = expr.kind - && let typeck = cx.typeck_results() - && ty::is_type_diagnostic_item(cx, typeck.expr_ty(lhs), sym::Instant) { + let typeck = cx.typeck_results(); + let lhs_ty = typeck.expr_ty(lhs); let rhs_ty = typeck.expr_ty(rhs); - if is_instant_now_call(cx, lhs) - && ty::is_type_diagnostic_item(cx, rhs_ty, sym::Instant) - && let Some(sugg) = Sugg::hir_opt(cx, rhs) - { - print_manual_instant_elapsed_sugg(cx, expr, sugg); - } else if ty::is_type_diagnostic_item(cx, rhs_ty, sym::Duration) + if ty::is_type_diagnostic_item(cx, lhs_ty, sym::Instant) { + // Instant::now() - instant + if is_instant_now_call(cx, lhs) + && ty::is_type_diagnostic_item(cx, rhs_ty, sym::Instant) + && let Some(sugg) = Sugg::hir_opt(cx, rhs) + { + print_manual_instant_elapsed_sugg(cx, expr, sugg); + } + // instant - duration + else if ty::is_type_diagnostic_item(cx, rhs_ty, sym::Duration) + && !expr.span.from_expansion() + && self.msrv.meets(cx, msrvs::TRY_FROM) + { + // For chained subtraction like (instant - dur1) - dur2, avoid suggestions + if is_chained_time_subtraction(cx, lhs) { + span_lint( + cx, + UNCHECKED_TIME_SUBTRACTION, + expr.span, + "unchecked subtraction of a 'Duration' from an 'Instant'", + ); + } else { + // instant - duration + print_unchecked_duration_subtraction_sugg(cx, lhs, rhs, expr); + } + } + } else if ty::is_type_diagnostic_item(cx, lhs_ty, sym::Duration) + && ty::is_type_diagnostic_item(cx, rhs_ty, sym::Duration) && !expr.span.from_expansion() && self.msrv.meets(cx, msrvs::TRY_FROM) { - print_unchecked_duration_subtraction_sugg(cx, lhs, rhs, expr); + // For chained subtraction like (dur1 - dur2) - dur3, avoid suggestions + if is_chained_time_subtraction(cx, lhs) { + span_lint( + cx, + UNCHECKED_TIME_SUBTRACTION, + expr.span, + "unchecked subtraction between 'Duration' values", + ); + } else { + // duration - duration + print_unchecked_duration_subtraction_sugg(cx, lhs, rhs, expr); + } } } } @@ -115,6 +154,25 @@ fn is_instant_now_call(cx: &LateContext<'_>, expr_block: &'_ Expr<'_>) -> bool { } } +/// Returns true if this subtraction is part of a chain like `(a - b) - c` +fn is_chained_time_subtraction(cx: &LateContext<'_>, lhs: &Expr<'_>) -> bool { + if let ExprKind::Binary(op, inner_lhs, inner_rhs) = &lhs.kind + && matches!(op.node, BinOpKind::Sub) + { + let typeck = cx.typeck_results(); + let left_ty = typeck.expr_ty(inner_lhs); + let right_ty = typeck.expr_ty(inner_rhs); + is_time_type(cx, left_ty) && is_time_type(cx, right_ty) + } else { + false + } +} + +/// Returns true if the type is Duration or Instant +fn is_time_type(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { + ty::is_type_diagnostic_item(cx, ty, sym::Duration) || ty::is_type_diagnostic_item(cx, ty, sym::Instant) +} + fn print_manual_instant_elapsed_sugg(cx: &LateContext<'_>, expr: &Expr<'_>, sugg: Sugg<'_>) { span_lint_and_sugg( cx, @@ -133,19 +191,26 @@ fn print_unchecked_duration_subtraction_sugg( right_expr: &Expr<'_>, expr: &Expr<'_>, ) { - let mut applicability = Applicability::MachineApplicable; + let typeck = cx.typeck_results(); + let left_ty = typeck.expr_ty(left_expr); - let ctxt = expr.span.ctxt(); - let left_expr = snippet_with_context(cx, left_expr.span, ctxt, "", &mut applicability).0; - let right_expr = snippet_with_context(cx, right_expr.span, ctxt, "", &mut applicability).0; + let lint_msg = if ty::is_type_diagnostic_item(cx, left_ty, sym::Instant) { + "unchecked subtraction of a 'Duration' from an 'Instant'" + } else { + "unchecked subtraction between 'Duration' values" + }; + + let mut applicability = Applicability::MachineApplicable; + let left_sugg = Sugg::hir_with_applicability(cx, left_expr, "", &mut applicability); + let right_sugg = Sugg::hir_with_applicability(cx, right_expr, "", &mut applicability); span_lint_and_sugg( cx, UNCHECKED_TIME_SUBTRACTION, expr.span, - "unchecked subtraction of a 'Duration' from an 'Instant'", + lint_msg, "try", - format!("{left_expr}.checked_sub({right_expr}).unwrap()"), + format!("{}.checked_sub({}).unwrap()", left_sugg.maybe_paren(), right_sugg), applicability, ); } diff --git a/tests/ui/unchecked_time_subtraction.fixed b/tests/ui/unchecked_time_subtraction.fixed index 0850226eeea7..2f923fef4c25 100644 --- a/tests/ui/unchecked_time_subtraction.fixed +++ b/tests/ui/unchecked_time_subtraction.fixed @@ -17,4 +17,21 @@ fn main() { let _ = Instant::now().checked_sub(second).unwrap(); //~^ unchecked_time_subtraction + + // Duration - Duration cases + let dur1 = Duration::from_secs(5); + let dur2 = Duration::from_secs(3); + + let _ = dur1.checked_sub(dur2).unwrap(); + //~^ unchecked_time_subtraction + + let _ = Duration::from_secs(10).checked_sub(Duration::from_secs(5)).unwrap(); + //~^ unchecked_time_subtraction + + let _ = second.checked_sub(dur1).unwrap(); + //~^ unchecked_time_subtraction + + // Duration multiplication and subtraction + let _ = (2 * dur1).checked_sub(dur2).unwrap(); + //~^ unchecked_time_subtraction } diff --git a/tests/ui/unchecked_time_subtraction.rs b/tests/ui/unchecked_time_subtraction.rs index a2dbfd1c1fad..cf727f62aafa 100644 --- a/tests/ui/unchecked_time_subtraction.rs +++ b/tests/ui/unchecked_time_subtraction.rs @@ -17,4 +17,21 @@ fn main() { let _ = Instant::now() - second; //~^ unchecked_time_subtraction + + // Duration - Duration cases + let dur1 = Duration::from_secs(5); + let dur2 = Duration::from_secs(3); + + let _ = dur1 - dur2; + //~^ unchecked_time_subtraction + + let _ = Duration::from_secs(10) - Duration::from_secs(5); + //~^ unchecked_time_subtraction + + let _ = second - dur1; + //~^ unchecked_time_subtraction + + // Duration multiplication and subtraction + let _ = 2 * dur1 - dur2; + //~^ unchecked_time_subtraction } diff --git a/tests/ui/unchecked_time_subtraction.stderr b/tests/ui/unchecked_time_subtraction.stderr index da0d51dbaef1..7a39712269cf 100644 --- a/tests/ui/unchecked_time_subtraction.stderr +++ b/tests/ui/unchecked_time_subtraction.stderr @@ -25,5 +25,29 @@ error: unchecked subtraction of a 'Duration' from an 'Instant' LL | let _ = Instant::now() - second; | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(second).unwrap()` -error: aborting due to 4 previous errors +error: unchecked subtraction between 'Duration' values + --> tests/ui/unchecked_time_subtraction.rs:25:13 + | +LL | let _ = dur1 - dur2; + | ^^^^^^^^^^^ help: try: `dur1.checked_sub(dur2).unwrap()` + +error: unchecked subtraction between 'Duration' values + --> tests/ui/unchecked_time_subtraction.rs:28:13 + | +LL | let _ = Duration::from_secs(10) - Duration::from_secs(5); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Duration::from_secs(10).checked_sub(Duration::from_secs(5)).unwrap()` + +error: unchecked subtraction between 'Duration' values + --> tests/ui/unchecked_time_subtraction.rs:31:13 + | +LL | let _ = second - dur1; + | ^^^^^^^^^^^^^ help: try: `second.checked_sub(dur1).unwrap()` + +error: unchecked subtraction between 'Duration' values + --> tests/ui/unchecked_time_subtraction.rs:35:13 + | +LL | let _ = 2 * dur1 - dur2; + | ^^^^^^^^^^^^^^^ help: try: `(2 * dur1).checked_sub(dur2).unwrap()` + +error: aborting due to 8 previous errors diff --git a/tests/ui/unchecked_time_subtraction_unfixable.rs b/tests/ui/unchecked_time_subtraction_unfixable.rs new file mode 100644 index 000000000000..4b6a5ca15620 --- /dev/null +++ b/tests/ui/unchecked_time_subtraction_unfixable.rs @@ -0,0 +1,22 @@ +#![warn(clippy::unchecked_time_subtraction)] +//@no-rustfix + +use std::time::{Duration, Instant}; + +fn main() { + let dur1 = Duration::from_secs(5); + let dur2 = Duration::from_secs(3); + let dur3 = Duration::from_secs(1); + + // Chained Duration subtraction - should lint without suggestion due to complexity + let _ = dur1 - dur2 - dur3; + //~^ unchecked_time_subtraction + //~| unchecked_time_subtraction + + // Chained Instant - Duration subtraction - should lint without suggestion due to complexity + let instant1 = Instant::now(); + + let _ = instant1 - dur2 - dur3; + //~^ unchecked_time_subtraction + //~| unchecked_time_subtraction +} diff --git a/tests/ui/unchecked_time_subtraction_unfixable.stderr b/tests/ui/unchecked_time_subtraction_unfixable.stderr new file mode 100644 index 000000000000..c25c112b06ce --- /dev/null +++ b/tests/ui/unchecked_time_subtraction_unfixable.stderr @@ -0,0 +1,29 @@ +error: unchecked subtraction between 'Duration' values + --> tests/ui/unchecked_time_subtraction_unfixable.rs:12:13 + | +LL | let _ = dur1 - dur2 - dur3; + | ^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::unchecked-time-subtraction` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unchecked_time_subtraction)]` + +error: unchecked subtraction between 'Duration' values + --> tests/ui/unchecked_time_subtraction_unfixable.rs:12:13 + | +LL | let _ = dur1 - dur2 - dur3; + | ^^^^^^^^^^^ help: try: `dur1.checked_sub(dur2).unwrap()` + +error: unchecked subtraction of a 'Duration' from an 'Instant' + --> tests/ui/unchecked_time_subtraction_unfixable.rs:19:13 + | +LL | let _ = instant1 - dur2 - dur3; + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: unchecked subtraction of a 'Duration' from an 'Instant' + --> tests/ui/unchecked_time_subtraction_unfixable.rs:19:13 + | +LL | let _ = instant1 - dur2 - dur3; + | ^^^^^^^^^^^^^^^ help: try: `instant1.checked_sub(dur2).unwrap()` + +error: aborting due to 4 previous errors + From 26cf3eee16d982ca66da4d103513f08da82f7ca5 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 8 May 2025 02:53:00 -0400 Subject: [PATCH 016/226] Rework `module_inception` * Don't check for repetition if the previous module crosses a body boundary. * Don't take a snippet of the entire item. * Check each item's visibility once. * Use `is_from_proc_macro` before linting --- clippy_lints/src/item_name_repetitions.rs | 180 ++++++++++++++-------- tests/ui/module_inception.rs | 9 ++ 2 files changed, 121 insertions(+), 68 deletions(-) diff --git a/clippy_lints/src/item_name_repetitions.rs b/clippy_lints/src/item_name_repetitions.rs index 945bb84708f8..76f5fdfaa8dc 100644 --- a/clippy_lints/src/item_name_repetitions.rs +++ b/clippy_lints/src/item_name_repetitions.rs @@ -1,11 +1,9 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_hir}; -use clippy_utils::is_bool; -use clippy_utils::macros::span_is_local; -use clippy_utils::source::is_present_in_source; use clippy_utils::str_utils::{camel_case_split, count_match_end, count_match_start, to_camel_case, to_snake_case}; +use clippy_utils::{is_bool, is_from_proc_macro}; use rustc_data_structures::fx::FxHashSet; -use rustc_hir::{EnumDef, FieldDef, Item, ItemKind, OwnerId, QPath, TyKind, Variant, VariantData}; +use rustc_hir::{Body, EnumDef, FieldDef, Item, ItemKind, QPath, TyKind, UseKind, Variant, VariantData}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; use rustc_span::symbol::Symbol; @@ -158,7 +156,8 @@ declare_clippy_lint! { } pub struct ItemNameRepetitions { - modules: Vec<(Symbol, String, OwnerId)>, + /// The module path the lint pass is in. + modules: Vec, enum_threshold: u64, struct_threshold: u64, avoid_breaking_exported_api: bool, @@ -167,6 +166,17 @@ pub struct ItemNameRepetitions { allowed_prefixes: FxHashSet, } +struct ModInfo { + name: Symbol, + name_camel: String, + /// Does this module have the `pub` visibility modifier. + is_public: bool, + /// How many bodies are between this module and the current lint pass position. + /// + /// Only the most recently seen module is updated when entering/exiting a body. + in_body_count: u32, +} + impl ItemNameRepetitions { pub fn new(conf: &'static Conf) -> Self { Self { @@ -458,71 +468,109 @@ fn check_enum_tuple_path_match(variant_name: &str, variant_data: VariantData<'_> } impl LateLintPass<'_> for ItemNameRepetitions { - fn check_item_post(&mut self, _cx: &LateContext<'_>, item: &Item<'_>) { - let Some(_ident) = item.kind.ident() else { return }; + fn check_item_post(&mut self, _: &LateContext<'_>, item: &Item<'_>) { + if matches!(item.kind, ItemKind::Mod(..)) { + let prev = self.modules.pop(); + debug_assert!(prev.is_some()); + } + } - let last = self.modules.pop(); - assert!(last.is_some()); + fn check_body(&mut self, _: &LateContext<'_>, _: &Body<'_>) { + if let [.., last] = &mut *self.modules { + last.in_body_count += 1; + } + } + + fn check_body_post(&mut self, _: &LateContext<'_>, _: &Body<'_>) { + if let [.., last] = &mut *self.modules { + last.in_body_count -= 1; + } } fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { - let Some(ident) = item.kind.ident() else { return }; + let ident = match item.kind { + ItemKind::Mod(ident, _) => { + if let [.., prev] = &*self.modules + && prev.name == ident.name + && prev.in_body_count == 0 + && (!self.allow_private_module_inception || prev.is_public) + && !item.span.from_expansion() + && !is_from_proc_macro(cx, item) + { + span_lint( + cx, + MODULE_INCEPTION, + item.span, + "module has the same name as its containing module", + ); + } + ident + }, + + ItemKind::Enum(ident, _, def) => { + if !ident.span.in_external_macro(cx.tcx.sess.source_map()) { + self.check_variants(cx, item, &def); + } + ident + }, + ItemKind::Struct(ident, _, data) => { + if let VariantData::Struct { fields, .. } = data + && !ident.span.in_external_macro(cx.tcx.sess.source_map()) + { + self.check_fields(cx, item, fields); + } + ident + }, + + ItemKind::Const(ident, ..) + | ItemKind::ExternCrate(_, ident) + | ItemKind::Fn { ident, .. } + | ItemKind::Macro(ident, ..) + | ItemKind::Static(_, ident, ..) + | ItemKind::Trait(_, _, _, ident, ..) + | ItemKind::TraitAlias(ident, ..) + | ItemKind::TyAlias(ident, ..) + | ItemKind::Union(ident, ..) + | ItemKind::Use(_, UseKind::Single(ident)) => ident, + + ItemKind::ForeignMod { .. } | ItemKind::GlobalAsm { .. } | ItemKind::Impl(_) | ItemKind::Use(..) => return, + }; let item_name = ident.name.as_str(); let item_camel = to_camel_case(item_name); - if !item.span.from_expansion() && is_present_in_source(cx, item.span) - && let [.., (mod_name, mod_camel, mod_owner_id)] = &*self.modules - // constants don't have surrounding modules - && !mod_camel.is_empty() + + if let [.., prev] = &*self.modules + && prev.is_public + && prev.in_body_count == 0 + && !item.span.from_expansion() + && !matches!(item.kind, ItemKind::Macro(..)) + && cx.tcx.visibility(item.owner_id).is_public() { - if mod_name == &ident.name - && let ItemKind::Mod(..) = item.kind - && (!self.allow_private_module_inception || cx.tcx.visibility(mod_owner_id.def_id).is_public()) - { - span_lint( - cx, - MODULE_INCEPTION, - item.span, - "module has the same name as its containing module", - ); - } - - // The `module_name_repetitions` lint should only trigger if the item has the module in its - // name. Having the same name is only accepted if `allow_exact_repetition` is set to `true`. - - let both_are_public = - cx.tcx.visibility(item.owner_id).is_public() && cx.tcx.visibility(mod_owner_id.def_id).is_public(); - - if both_are_public && !self.allow_exact_repetitions && item_camel == *mod_camel { - span_lint( - cx, - MODULE_NAME_REPETITIONS, - ident.span, - "item name is the same as its containing module's name", - ); - } - - let is_macro = matches!(item.kind, ItemKind::Macro(_, _, _)); - if both_are_public && item_camel.len() > mod_camel.len() && !is_macro { - let matching = count_match_start(mod_camel, &item_camel); - let rmatching = count_match_end(mod_camel, &item_camel); - let nchars = mod_camel.chars().count(); - - let is_word_beginning = |c: char| c == '_' || c.is_uppercase() || c.is_numeric(); - - if matching.char_count == nchars { - match item_camel.chars().nth(nchars) { - Some(c) if is_word_beginning(c) => span_lint( + if !self.allow_exact_repetitions && item_camel == prev.name_camel { + if !is_from_proc_macro(cx, item) { + span_lint( + cx, + MODULE_NAME_REPETITIONS, + ident.span, + "item name is the same as its containing module's name", + ); + } + } else if item_camel.len() > prev.name_camel.len() { + if let Some(s) = item_camel.strip_prefix(&prev.name_camel) + && let Some(c) = s.chars().next() + && (c == '_' || c.is_uppercase() || c.is_numeric()) + { + if !is_from_proc_macro(cx, item) { + span_lint( cx, MODULE_NAME_REPETITIONS, ident.span, "item name starts with its containing module's name", - ), - _ => (), + ); } - } - if rmatching.char_count == nchars - && !self.is_allowed_prefix(&item_camel[..item_camel.len() - rmatching.byte_count]) + } else if let Some(s) = item_camel.strip_suffix(&prev.name_camel) + && !self.is_allowed_prefix(s) + && !is_from_proc_macro(cx, item) { span_lint( cx, @@ -534,17 +582,13 @@ impl LateLintPass<'_> for ItemNameRepetitions { } } - if span_is_local(item.span) { - match item.kind { - ItemKind::Enum(_, _, def) => { - self.check_variants(cx, item, &def); - }, - ItemKind::Struct(_, _, VariantData::Struct { fields, .. }) => { - self.check_fields(cx, item, fields); - }, - _ => (), - } + if matches!(item.kind, ItemKind::Mod(..)) { + self.modules.push(ModInfo { + name: ident.name, + name_camel: item_camel, + is_public: cx.tcx.visibility(item.owner_id).is_public(), + in_body_count: 0, + }); } - self.modules.push((ident.name, item_camel, item.owner_id)); } } diff --git a/tests/ui/module_inception.rs b/tests/ui/module_inception.rs index 15b7fb877770..5735dd5867d5 100644 --- a/tests/ui/module_inception.rs +++ b/tests/ui/module_inception.rs @@ -38,4 +38,13 @@ mod bar { mod bar {} } +mod with_inner_impl { + struct S; + impl S { + fn f() { + mod with_inner_impl {} + } + } +} + fn main() {} From b3e056f19882e1d60a2a67ad9485546890108b44 Mon Sep 17 00:00:00 2001 From: Zihan Date: Mon, 11 Aug 2025 11:06:28 -0400 Subject: [PATCH 017/226] `module_style`: refactor and don't lint mod tagged with path attr refactored with `check_item` and `check_item_post`. this refactoring also fixed a few bugs related to mod path with shared segment name caused by preivous approach. changelog: [`self_named_module_files`]: don't lint mod tagged with path attribute changelog: [`mod_module_files`]: don't lint mod tagged with path attribute Signed-off-by: Zihan --- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/module_style.rs | 161 +++++++++--------- .../duplicated_mod_names_14697/Cargo.stderr | 11 ++ .../duplicated_mod_names_14697/Cargo.toml | 11 ++ .../duplicated_mod_names_14697/src/foo.rs | 1 + .../duplicated_mod_names_14697/src/foo/bar.rs | 1 + .../duplicated_mod_names_14697/src/lib.rs | 4 + .../src/other/foo/mod.rs | 1 + .../src/other/mod.rs | 1 + .../module_style/fail_mod/Cargo.stderr | 20 +-- .../Cargo.toml | 10 ++ .../foo/bar/Cargo.toml | 5 + .../foo/bar/src/foo.rs | 1 + .../foo/bar/src/lib.rs | 1 + .../src/lib.rs | 1 + .../with_path_attr_mod/Cargo.toml | 10 ++ .../with_path_attr_mod/src/bar/mod.rs | 1 + .../with_path_attr_mod/src/lib.rs | 4 + .../with_path_attr_no_mod/Cargo.toml | 10 ++ .../with_path_attr_no_mod/src/bar.rs | 1 + .../with_path_attr_no_mod/src/foo.rs | 2 + .../with_path_attr_no_mod/src/lib.rs | 3 + 22 files changed, 172 insertions(+), 90 deletions(-) create mode 100644 tests/ui-cargo/module_style/duplicated_mod_names_14697/Cargo.stderr create mode 100644 tests/ui-cargo/module_style/duplicated_mod_names_14697/Cargo.toml create mode 100644 tests/ui-cargo/module_style/duplicated_mod_names_14697/src/foo.rs create mode 100644 tests/ui-cargo/module_style/duplicated_mod_names_14697/src/foo/bar.rs create mode 100644 tests/ui-cargo/module_style/duplicated_mod_names_14697/src/lib.rs create mode 100644 tests/ui-cargo/module_style/duplicated_mod_names_14697/src/other/foo/mod.rs create mode 100644 tests/ui-cargo/module_style/duplicated_mod_names_14697/src/other/mod.rs create mode 100644 tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/Cargo.toml create mode 100644 tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/foo/bar/Cargo.toml create mode 100644 tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/foo/bar/src/foo.rs create mode 100644 tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/foo/bar/src/lib.rs create mode 100644 tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/src/lib.rs create mode 100644 tests/ui-cargo/module_style/with_path_attr_mod/Cargo.toml create mode 100644 tests/ui-cargo/module_style/with_path_attr_mod/src/bar/mod.rs create mode 100644 tests/ui-cargo/module_style/with_path_attr_mod/src/lib.rs create mode 100644 tests/ui-cargo/module_style/with_path_attr_no_mod/Cargo.toml create mode 100644 tests/ui-cargo/module_style/with_path_attr_no_mod/src/bar.rs create mode 100644 tests/ui-cargo/module_style/with_path_attr_no_mod/src/foo.rs create mode 100644 tests/ui-cargo/module_style/with_path_attr_no_mod/src/lib.rs diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c56fa257b068..2b8f09d33405 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -671,7 +671,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::new(from_str_radix_10::FromStrRadix10)); store.register_late_pass(move |_| Box::new(if_then_some_else_none::IfThenSomeElseNone::new(conf))); store.register_late_pass(|_| Box::new(bool_assert_comparison::BoolAssertComparison)); - store.register_early_pass(move || Box::new(module_style::ModStyle)); + store.register_early_pass(move || Box::new(module_style::ModStyle::default())); store.register_late_pass(|_| Box::::default()); store.register_late_pass(move |tcx| Box::new(disallowed_types::DisallowedTypes::new(tcx, conf))); store.register_late_pass(move |tcx| Box::new(missing_enforced_import_rename::ImportRename::new(tcx, conf))); diff --git a/clippy_lints/src/module_style.rs b/clippy_lints/src/module_style.rs index 98614baffcea..f132b90ac4f2 100644 --- a/clippy_lints/src/module_style.rs +++ b/clippy_lints/src/module_style.rs @@ -1,12 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_then; -use rustc_ast::ast; -use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; +use rustc_ast::ast::{self, Inline, ItemKind, ModKind}; use rustc_lint::{EarlyContext, EarlyLintPass, Level, LintContext}; use rustc_session::impl_lint_pass; use rustc_span::def_id::LOCAL_CRATE; -use rustc_span::{FileName, SourceFile, Span, SyntaxContext}; -use std::ffi::OsStr; -use std::path::{Component, Path}; +use rustc_span::{FileName, SourceFile, Span, SyntaxContext, sym}; +use std::path::{Path, PathBuf}; +use std::sync::Arc; declare_clippy_lint! { /// ### What it does @@ -60,107 +59,97 @@ declare_clippy_lint! { /// mod.rs /// lib.rs /// ``` - #[clippy::version = "1.57.0"] pub SELF_NAMED_MODULE_FILES, restriction, "checks that module layout is consistent" } -pub struct ModStyle; - impl_lint_pass!(ModStyle => [MOD_MODULE_FILES, SELF_NAMED_MODULE_FILES]); +pub struct ModState { + contains_external: bool, + has_path_attr: bool, + mod_file: Arc, +} + +#[derive(Default)] +pub struct ModStyle { + working_dir: Option, + module_stack: Vec, +} + impl EarlyLintPass for ModStyle { fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) { + self.working_dir = cx.sess().opts.working_dir.local_path().map(Path::to_path_buf); + } + + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { + if cx.builder.lint_level(MOD_MODULE_FILES).level == Level::Allow + && cx.builder.lint_level(SELF_NAMED_MODULE_FILES).level == Level::Allow + { + return; + } + if let ItemKind::Mod(.., ModKind::Loaded(_, Inline::No { .. }, mod_spans, ..)) = &item.kind { + let has_path_attr = item.attrs.iter().any(|attr| attr.has_name(sym::path)); + if !has_path_attr && let Some(current) = self.module_stack.last_mut() { + current.contains_external = true; + } + let mod_file = cx.sess().source_map().lookup_source_file(mod_spans.inner_span.lo()); + self.module_stack.push(ModState { + contains_external: false, + has_path_attr, + mod_file, + }); + } + } + + fn check_item_post(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { if cx.builder.lint_level(MOD_MODULE_FILES).level == Level::Allow && cx.builder.lint_level(SELF_NAMED_MODULE_FILES).level == Level::Allow { return; } - let files = cx.sess().source_map().files(); - - let Some(trim_to_src) = cx.sess().opts.working_dir.local_path() else { - return; - }; - - // `folder_segments` is all unique folder path segments `path/to/foo.rs` gives - // `[path, to]` but not foo - let mut folder_segments = FxIndexSet::default(); - // `mod_folders` is all the unique folder names that contain a mod.rs file - let mut mod_folders = FxHashSet::default(); - // `file_map` maps file names to the full path including the file name - // `{ foo => path/to/foo.rs, .. } - let mut file_map = FxHashMap::default(); - for file in files.iter() { - if let FileName::Real(name) = &file.name - && let Some(lp) = name.local_path() - && file.cnum == LOCAL_CRATE - { - // [#8887](https://github.com/rust-lang/rust-clippy/issues/8887) - // Only check files in the current crate. - // Fix false positive that crate dependency in workspace sub directory - // is checked unintentionally. - let path = if lp.is_relative() { - lp - } else if let Ok(relative) = lp.strip_prefix(trim_to_src) { - relative - } else { - continue; - }; - - if let Some(stem) = path.file_stem() { - file_map.insert(stem, (file, path)); - } - process_paths_for_mod_files(path, &mut folder_segments, &mut mod_folders); - check_self_named_mod_exists(cx, path, file); - } - } - - for folder in &folder_segments { - if !mod_folders.contains(folder) - && let Some((file, path)) = file_map.get(folder) - { - span_lint_and_then( - cx, - SELF_NAMED_MODULE_FILES, - Span::new(file.start_pos, file.start_pos, SyntaxContext::root(), None), - format!("`mod.rs` files are required, found `{}`", path.display()), - |diag| { - let mut correct = path.to_path_buf(); - correct.pop(); - correct.push(folder); - correct.push("mod.rs"); - diag.help(format!("move `{}` to `{}`", path.display(), correct.display(),)); - }, - ); + if let ItemKind::Mod(.., ModKind::Loaded(_, Inline::No { .. }, ..)) = &item.kind + && let Some(current) = self.module_stack.pop() + && !current.has_path_attr + { + let Some(path) = self + .working_dir + .as_ref() + .and_then(|src| try_trim_file_path_prefix(¤t.mod_file, src)) + else { + return; + }; + if current.contains_external { + check_self_named_module(cx, path, ¤t.mod_file); } + check_mod_module(cx, path, ¤t.mod_file); } } } -/// For each `path` we add each folder component to `folder_segments` and if the file name -/// is `mod.rs` we add it's parent folder to `mod_folders`. -fn process_paths_for_mod_files<'a>( - path: &'a Path, - folder_segments: &mut FxIndexSet<&'a OsStr>, - mod_folders: &mut FxHashSet<&'a OsStr>, -) { - let mut comp = path.components().rev().peekable(); - let _: Option<_> = comp.next(); - if path.ends_with("mod.rs") { - mod_folders.insert(comp.peek().map(|c| c.as_os_str()).unwrap_or_default()); +fn check_self_named_module(cx: &EarlyContext<'_>, path: &Path, file: &SourceFile) { + if !path.ends_with("mod.rs") { + let mut mod_folder = path.with_extension(""); + span_lint_and_then( + cx, + SELF_NAMED_MODULE_FILES, + Span::new(file.start_pos, file.start_pos, SyntaxContext::root(), None), + format!("`mod.rs` files are required, found `{}`", path.display()), + |diag| { + mod_folder.push("mod.rs"); + diag.help(format!("move `{}` to `{}`", path.display(), mod_folder.display())); + }, + ); } - let folders = comp.filter_map(|c| if let Component::Normal(s) = c { Some(s) } else { None }); - folder_segments.extend(folders); } -/// Checks every path for the presence of `mod.rs` files and emits the lint if found. /// We should not emit a lint for test modules in the presence of `mod.rs`. /// Using `mod.rs` in integration tests is a [common pattern](https://doc.rust-lang.org/book/ch11-03-test-organization.html#submodules-in-integration-test) /// for code-sharing between tests. -fn check_self_named_mod_exists(cx: &EarlyContext<'_>, path: &Path, file: &SourceFile) { +fn check_mod_module(cx: &EarlyContext<'_>, path: &Path, file: &SourceFile) { if path.ends_with("mod.rs") && !path.starts_with("tests") { span_lint_and_then( cx, @@ -177,3 +166,17 @@ fn check_self_named_mod_exists(cx: &EarlyContext<'_>, path: &Path, file: &Source ); } } + +fn try_trim_file_path_prefix<'a>(file: &'a SourceFile, prefix: &'a Path) -> Option<&'a Path> { + if let FileName::Real(name) = &file.name + && let Some(mut path) = name.local_path() + && file.cnum == LOCAL_CRATE + { + if !path.is_relative() { + path = path.strip_prefix(prefix).ok()?; + } + Some(path) + } else { + None + } +} diff --git a/tests/ui-cargo/module_style/duplicated_mod_names_14697/Cargo.stderr b/tests/ui-cargo/module_style/duplicated_mod_names_14697/Cargo.stderr new file mode 100644 index 000000000000..c7490c5da027 --- /dev/null +++ b/tests/ui-cargo/module_style/duplicated_mod_names_14697/Cargo.stderr @@ -0,0 +1,11 @@ +error: `mod.rs` files are required, found `src/foo.rs` + --> src/foo.rs:1:1 + | +1 | pub mod bar; + | ^ + | + = help: move `src/foo.rs` to `src/foo/mod.rs` + = note: `-D clippy::self-named-module-files` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::self_named_module_files)]` + +error: could not compile `duplicated-mod-names-14697` (lib) due to 1 previous error diff --git a/tests/ui-cargo/module_style/duplicated_mod_names_14697/Cargo.toml b/tests/ui-cargo/module_style/duplicated_mod_names_14697/Cargo.toml new file mode 100644 index 000000000000..569082f2f659 --- /dev/null +++ b/tests/ui-cargo/module_style/duplicated_mod_names_14697/Cargo.toml @@ -0,0 +1,11 @@ +# Should trigger when multiple mods with the same name exist and not all of them follow self-named convention. +# See issue #14697. +[package] +name = "duplicated-mod-names-14697" +version = "0.1.0" +edition = "2024" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/foo.rs b/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/foo.rs new file mode 100644 index 000000000000..46f285ca47d6 --- /dev/null +++ b/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/foo.rs @@ -0,0 +1 @@ +pub mod bar; diff --git a/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/foo/bar.rs b/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/foo/bar.rs new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/foo/bar.rs @@ -0,0 +1 @@ + diff --git a/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/lib.rs b/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/lib.rs new file mode 100644 index 000000000000..a85dae574816 --- /dev/null +++ b/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/lib.rs @@ -0,0 +1,4 @@ +#![warn(clippy::self_named_module_files)] + +pub mod foo; +pub mod other; diff --git a/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/other/foo/mod.rs b/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/other/foo/mod.rs new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/other/foo/mod.rs @@ -0,0 +1 @@ + diff --git a/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/other/mod.rs b/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/other/mod.rs new file mode 100644 index 000000000000..b52703b25740 --- /dev/null +++ b/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/other/mod.rs @@ -0,0 +1 @@ +pub mod foo; diff --git a/tests/ui-cargo/module_style/fail_mod/Cargo.stderr b/tests/ui-cargo/module_style/fail_mod/Cargo.stderr index 902330e17853..f134943e69bf 100644 --- a/tests/ui-cargo/module_style/fail_mod/Cargo.stderr +++ b/tests/ui-cargo/module_style/fail_mod/Cargo.stderr @@ -1,13 +1,3 @@ -error: `mod.rs` files are required, found `src/bad/inner.rs` - --> src/bad/inner.rs:1:1 - | -1 | pub mod stuff; - | ^ - | - = help: move `src/bad/inner.rs` to `src/bad/inner/mod.rs` - = note: `-D clippy::self-named-module-files` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::self_named_module_files)]` - error: `mod.rs` files are required, found `src/bad/inner/stuff.rs` --> src/bad/inner/stuff.rs:1:1 | @@ -15,5 +5,15 @@ error: `mod.rs` files are required, found `src/bad/inner/stuff.rs` | ^ | = help: move `src/bad/inner/stuff.rs` to `src/bad/inner/stuff/mod.rs` + = note: `-D clippy::self-named-module-files` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::self_named_module_files)]` + +error: `mod.rs` files are required, found `src/bad/inner.rs` + --> src/bad/inner.rs:1:1 + | +1 | pub mod stuff; + | ^ + | + = help: move `src/bad/inner.rs` to `src/bad/inner/mod.rs` error: could not compile `fail-mod` (bin "fail-mod") due to 2 previous errors diff --git a/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/Cargo.toml b/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/Cargo.toml new file mode 100644 index 000000000000..5c2fabd2283d --- /dev/null +++ b/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/Cargo.toml @@ -0,0 +1,10 @@ +# Should not produce FP when irrelavant path segment shares the same name with module. +# See issue #10271 and #11916. +[package] +name = "segment-with-mod-name-10271-11916" +version = "0.1.0" +edition = "2024" +publish = false + +[workspace] +members = ["foo/bar"] \ No newline at end of file diff --git a/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/foo/bar/Cargo.toml b/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/foo/bar/Cargo.toml new file mode 100644 index 000000000000..1f68c0dccac5 --- /dev/null +++ b/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/foo/bar/Cargo.toml @@ -0,0 +1,5 @@ +[package] +name = "bar" +version = "0.1.0" +edition = "2024" +publish = false diff --git a/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/foo/bar/src/foo.rs b/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/foo/bar/src/foo.rs new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/foo/bar/src/foo.rs @@ -0,0 +1 @@ + diff --git a/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/foo/bar/src/lib.rs b/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/foo/bar/src/lib.rs new file mode 100644 index 000000000000..b52703b25740 --- /dev/null +++ b/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/foo/bar/src/lib.rs @@ -0,0 +1 @@ +pub mod foo; diff --git a/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/src/lib.rs b/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/src/lib.rs new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/src/lib.rs @@ -0,0 +1 @@ + diff --git a/tests/ui-cargo/module_style/with_path_attr_mod/Cargo.toml b/tests/ui-cargo/module_style/with_path_attr_mod/Cargo.toml new file mode 100644 index 000000000000..d867377545e1 --- /dev/null +++ b/tests/ui-cargo/module_style/with_path_attr_mod/Cargo.toml @@ -0,0 +1,10 @@ +# Should not lint mod tagged with `#[path = ...]` +[package] +name = "with-path-attr-mod" +version = "0.1.0" +edition = "2024" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/tests/ui-cargo/module_style/with_path_attr_mod/src/bar/mod.rs b/tests/ui-cargo/module_style/with_path_attr_mod/src/bar/mod.rs new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/tests/ui-cargo/module_style/with_path_attr_mod/src/bar/mod.rs @@ -0,0 +1 @@ + diff --git a/tests/ui-cargo/module_style/with_path_attr_mod/src/lib.rs b/tests/ui-cargo/module_style/with_path_attr_mod/src/lib.rs new file mode 100644 index 000000000000..a5c2109ece7d --- /dev/null +++ b/tests/ui-cargo/module_style/with_path_attr_mod/src/lib.rs @@ -0,0 +1,4 @@ +#![warn(clippy::mod_module_files)] + +#[path = "bar/mod.rs"] +pub mod foo; diff --git a/tests/ui-cargo/module_style/with_path_attr_no_mod/Cargo.toml b/tests/ui-cargo/module_style/with_path_attr_no_mod/Cargo.toml new file mode 100644 index 000000000000..ddf2ac394cdd --- /dev/null +++ b/tests/ui-cargo/module_style/with_path_attr_no_mod/Cargo.toml @@ -0,0 +1,10 @@ +# Should not lint mod tagged with `#[path = ...]` +[package] +name = "with-path-attr-no-mod" +version = "0.1.0" +edition = "2024" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/tests/ui-cargo/module_style/with_path_attr_no_mod/src/bar.rs b/tests/ui-cargo/module_style/with_path_attr_no_mod/src/bar.rs new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/tests/ui-cargo/module_style/with_path_attr_no_mod/src/bar.rs @@ -0,0 +1 @@ + diff --git a/tests/ui-cargo/module_style/with_path_attr_no_mod/src/foo.rs b/tests/ui-cargo/module_style/with_path_attr_no_mod/src/foo.rs new file mode 100644 index 000000000000..3b12aefa3d5f --- /dev/null +++ b/tests/ui-cargo/module_style/with_path_attr_no_mod/src/foo.rs @@ -0,0 +1,2 @@ +#[path = "bar.rs"] +mod bar; diff --git a/tests/ui-cargo/module_style/with_path_attr_no_mod/src/lib.rs b/tests/ui-cargo/module_style/with_path_attr_no_mod/src/lib.rs new file mode 100644 index 000000000000..bf2a5d019335 --- /dev/null +++ b/tests/ui-cargo/module_style/with_path_attr_no_mod/src/lib.rs @@ -0,0 +1,3 @@ +#![warn(clippy::self_named_module_files)] + +pub mod foo; From 427774af0719cdf2214825cfa0a634e550839169 Mon Sep 17 00:00:00 2001 From: Kevin Reid Date: Thu, 18 Sep 2025 07:46:17 -0700 Subject: [PATCH 018/226] Changelog: fix heading level in 1.90 section --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a9be0214372..fb7cbbed6542 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,7 @@ Current stable, released 2025-09-18 Note: This Clippy release does not introduce many new lints and is focused entirely on bug fixes — see [#15086](https://github.com/rust-lang/rust-clippy/issues/15086) for more details. -## New Lints +### New Lints * Added [`manual_is_multiple_of`] to `complexity` [#14292](https://github.com/rust-lang/rust-clippy/pull/14292) * Added [`doc_broken_link`] to `pedantic` [#13696](https://github.com/rust-lang/rust-clippy/pull/13696) From 47bcee4ee26cc03aa19df2331559686d6a719bd1 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 18 Sep 2025 17:21:44 +0200 Subject: [PATCH 019/226] Merge commit '20ce69b9a63bcd2756cd906fe0964d1e901e042a' into clippy-subtree-update --- .github/ISSUE_TEMPLATE/new_lint.yml | 4 +- .github/PULL_REQUEST_TEMPLATE.md | 4 - .github/workflows/feature_freeze.yml | 45 -- .gitignore | 3 + CHANGELOG.md | 148 ++++- Cargo.toml | 2 +- book/src/README.md | 4 - book/src/SUMMARY.md | 1 - book/src/development/adding_lints.md | 3 - book/src/development/feature_freeze.md | 55 -- clippy_config/Cargo.toml | 2 +- clippy_lints/Cargo.toml | 2 +- clippy_lints/src/attrs/useless_attribute.rs | 1 + clippy_lints/src/casts/as_underscore.rs | 8 +- clippy_lints/src/casts/cast_possible_wrap.rs | 30 +- clippy_lints/src/casts/cast_sign_loss.rs | 29 +- clippy_lints/src/casts/mod.rs | 4 +- clippy_lints/src/casts/utils.rs | 15 + clippy_lints/src/declared_lints.rs | 5 +- clippy_lints/src/dereference.rs | 7 +- clippy_lints/src/derive.rs | 527 ------------------ .../src/derive/derive_ord_xor_partial_ord.rs | 50 ++ .../derive/derive_partial_eq_without_eq.rs | 87 +++ .../src/derive/derived_hash_with_manual_eq.rs | 49 ++ .../src/derive/expl_impl_clone_on_copy.rs | 65 +++ clippy_lints/src/derive/mod.rs | 215 +++++++ .../src/derive/unsafe_derive_deserialize.rs | 93 ++++ clippy_lints/src/disallowed_macros.rs | 4 +- clippy_lints/src/eta_reduction.rs | 27 +- .../src/functions/not_unsafe_ptr_arg_deref.rs | 4 +- clippy_lints/src/functions/ref_option.rs | 48 +- clippy_lints/src/future_not_send.rs | 111 ++-- .../src/invalid_upcast_comparisons.rs | 13 +- clippy_lints/src/len_zero.rs | 6 +- clippy_lints/src/let_with_type_underscore.rs | 16 +- clippy_lints/src/lib.rs | 6 +- clippy_lints/src/lifetimes.rs | 105 +++- clippy_lints/src/loops/explicit_iter_loop.rs | 6 +- clippy_lints/src/loops/never_loop.rs | 59 +- clippy_lints/src/manual_abs_diff.rs | 6 +- clippy_lints/src/manual_option_as_slice.rs | 6 +- .../src/manual_slice_size_calculation.rs | 3 +- clippy_lints/src/matches/manual_ok_err.rs | 16 +- clippy_lints/src/matches/manual_utils.rs | 9 +- clippy_lints/src/matches/needless_match.rs | 6 +- .../matches/rest_pat_in_fully_bound_struct.rs | 10 +- clippy_lints/src/matches/single_match.rs | 8 +- clippy_lints/src/methods/implicit_clone.rs | 6 +- .../src/methods/inefficient_to_string.rs | 4 +- .../src/methods/iter_overeager_cloned.rs | 5 +- clippy_lints/src/methods/mod.rs | 75 ++- clippy_lints/src/methods/mut_mutex_lock.rs | 5 +- clippy_lints/src/methods/or_fun_call.rs | 7 + .../src/methods/ptr_offset_with_cast.rs | 82 +++ .../src/methods/unnecessary_to_owned.rs | 13 +- clippy_lints/src/methods/useless_asref.rs | 6 +- clippy_lints/src/min_ident_chars.rs | 6 +- clippy_lints/src/misc.rs | 118 +--- clippy_lints/src/missing_doc.rs | 2 +- .../src/multiple_unsafe_ops_per_block.rs | 8 + clippy_lints/src/only_used_in_recursion.rs | 4 +- clippy_lints/src/operators/erasing_op.rs | 4 +- clippy_lints/src/ptr_offset_with_cast.rs | 151 ----- clippy_lints/src/question_mark.rs | 21 +- clippy_lints/src/read_zero_byte_vec.rs | 27 +- clippy_lints/src/redundant_clone.rs | 4 +- clippy_lints/src/redundant_slicing.rs | 8 +- clippy_lints/src/returns.rs | 513 ----------------- clippy_lints/src/returns/let_and_return.rs | 86 +++ clippy_lints/src/returns/mod.rs | 140 +++++ clippy_lints/src/returns/needless_return.rs | 269 +++++++++ .../needless_return_with_question_mark.rs | 60 ++ clippy_lints/src/semicolon_block.rs | 14 + clippy_lints/src/size_of_ref.rs | 5 +- clippy_lints/src/toplevel_ref_arg.rs | 119 ++++ .../src/transmute/transmute_ptr_to_ref.rs | 44 +- .../src/transmute/transmute_ref_to_ref.rs | 4 +- clippy_lints/src/uninit_vec.rs | 11 +- clippy_lints/src/unused_peekable.rs | 6 +- clippy_lints/src/unwrap.rs | 1 + clippy_lints/src/use_self.rs | 81 +-- clippy_lints/src/useless_conversion.rs | 14 +- clippy_utils/Cargo.toml | 2 +- clippy_utils/README.md | 2 +- clippy_utils/src/check_proc_macro.rs | 146 ++++- clippy_utils/src/lib.rs | 29 +- clippy_utils/src/msrvs.rs | 4 +- clippy_utils/src/source.rs | 5 + clippy_utils/src/sym.rs | 1 + clippy_utils/src/ty/mod.rs | 88 ++- declare_clippy_lint/Cargo.toml | 2 +- rust-toolchain.toml | 2 +- tests/no-profile-in-cargo-toml.rs | 3 + .../ref_option/all/clippy.toml | 0 .../ref_option/private/clippy.toml | 0 tests/ui-toml/ref_option/ref_option.all.fixed | 114 ++++ .../ref_option/ref_option.all.stderr | 68 +-- .../ref_option/ref_option.private.fixed | 114 ++++ .../ref_option/ref_option.private.stderr | 44 +- tests/ui-toml/ref_option/ref_option.rs | 114 ++++ .../ref_option/ref_option_traits.all.stderr | 8 +- .../ref_option_traits.private.stderr | 4 +- tests/ui-toml/ref_option/ref_option_traits.rs | 71 +++ tests/ui/as_underscore_unfixable.rs | 14 + tests/ui/as_underscore_unfixable.stderr | 20 + tests/ui/cast.rs | 13 + tests/ui/cast.stderr | 96 ++-- .../ui/checked_unwrap/simple_conditionals.rs | 22 + .../checked_unwrap/simple_conditionals.stderr | 36 +- tests/ui/crashes/ice-15657.rs | 11 + tests/ui/crashes/ice-15666.fixed | 6 + tests/ui/crashes/ice-15666.rs | 6 + tests/ui/crashes/ice-15666.stderr | 16 + tests/ui/elidable_lifetime_names.fixed | 82 +++ tests/ui/elidable_lifetime_names.rs | 82 +++ tests/ui/elidable_lifetime_names.stderr | 146 ++++- tests/ui/eta.fixed | 8 + tests/ui/eta.rs | 8 + tests/ui/invalid_upcast_comparisons.rs | 12 + tests/ui/invalid_upcast_comparisons.stderr | 8 +- tests/ui/iter_overeager_cloned.fixed | 12 +- tests/ui/iter_overeager_cloned.rs | 12 +- tests/ui/iter_overeager_cloned.stderr | 4 +- tests/ui/match_as_ref.fixed | 30 + tests/ui/match_as_ref.rs | 30 + tests/ui/multiple_unsafe_ops_per_block.rs | 129 +++-- tests/ui/multiple_unsafe_ops_per_block.stderr | 161 ++++-- tests/ui/needless_return.fixed | 7 + tests/ui/needless_return.rs | 7 + tests/ui/never_loop.rs | 24 + tests/ui/never_loop.stderr | 97 +++- tests/ui/option_if_let_else.fixed | 10 + tests/ui/option_if_let_else.rs | 16 + tests/ui/option_if_let_else.stderr | 50 +- tests/ui/or_fun_call.fixed | 16 + tests/ui/or_fun_call.rs | 22 +- tests/ui/or_fun_call.stderr | 88 +-- tests/ui/ptr_offset_with_cast.fixed | 22 +- tests/ui/ptr_offset_with_cast.rs | 22 +- tests/ui/ptr_offset_with_cast.stderr | 41 +- tests/ui/question_mark.fixed | 14 +- tests/ui/question_mark.rs | 26 +- tests/ui/question_mark.stderr | 92 +-- tests/ui/read_zero_byte_vec.rs | 27 + tests/ui/read_zero_byte_vec.stderr | 65 ++- tests/ui/ref_option/ref_option.all.fixed | 79 --- tests/ui/ref_option/ref_option.private.fixed | 79 --- tests/ui/ref_option/ref_option.rs | 79 --- tests/ui/ref_option/ref_option_traits.rs | 40 -- .../ui/rest_pat_in_fully_bound_structs.fixed | 61 ++ .../ui/rest_pat_in_fully_bound_structs.stderr | 18 +- tests/ui/semicolon_inside_block.fixed | 17 +- tests/ui/semicolon_inside_block.rs | 17 +- tests/ui/semicolon_inside_block.stderr | 8 +- tests/ui/track-diagnostics-clippy.stderr | 2 +- tests/ui/transmute_ptr_to_ref.fixed | 39 +- tests/ui/transmute_ptr_to_ref.rs | 39 +- tests/ui/transmute_ptr_to_ref.stderr | 54 +- tests/ui/use_self.fixed | 18 +- tests/ui/use_self.rs | 16 +- tests/ui/use_self.stderr | 8 +- tests/ui/useless_attribute.fixed | 10 + tests/ui/useless_attribute.rs | 10 + 163 files changed, 4388 insertions(+), 2487 deletions(-) delete mode 100644 .github/workflows/feature_freeze.yml delete mode 100644 book/src/development/feature_freeze.md delete mode 100644 clippy_lints/src/derive.rs create mode 100644 clippy_lints/src/derive/derive_ord_xor_partial_ord.rs create mode 100644 clippy_lints/src/derive/derive_partial_eq_without_eq.rs create mode 100644 clippy_lints/src/derive/derived_hash_with_manual_eq.rs create mode 100644 clippy_lints/src/derive/expl_impl_clone_on_copy.rs create mode 100644 clippy_lints/src/derive/mod.rs create mode 100644 clippy_lints/src/derive/unsafe_derive_deserialize.rs create mode 100644 clippy_lints/src/methods/ptr_offset_with_cast.rs delete mode 100644 clippy_lints/src/ptr_offset_with_cast.rs delete mode 100644 clippy_lints/src/returns.rs create mode 100644 clippy_lints/src/returns/let_and_return.rs create mode 100644 clippy_lints/src/returns/mod.rs create mode 100644 clippy_lints/src/returns/needless_return.rs create mode 100644 clippy_lints/src/returns/needless_return_with_question_mark.rs create mode 100644 clippy_lints/src/toplevel_ref_arg.rs rename tests/{ui => ui-toml}/ref_option/all/clippy.toml (100%) rename tests/{ui => ui-toml}/ref_option/private/clippy.toml (100%) create mode 100644 tests/ui-toml/ref_option/ref_option.all.fixed rename tests/{ui => ui-toml}/ref_option/ref_option.all.stderr (62%) create mode 100644 tests/ui-toml/ref_option/ref_option.private.fixed rename tests/{ui => ui-toml}/ref_option/ref_option.private.stderr (63%) create mode 100644 tests/ui-toml/ref_option/ref_option.rs rename tests/{ui => ui-toml}/ref_option/ref_option_traits.all.stderr (85%) rename tests/{ui => ui-toml}/ref_option/ref_option_traits.private.stderr (86%) create mode 100644 tests/ui-toml/ref_option/ref_option_traits.rs create mode 100644 tests/ui/as_underscore_unfixable.rs create mode 100644 tests/ui/as_underscore_unfixable.stderr create mode 100644 tests/ui/crashes/ice-15657.rs create mode 100644 tests/ui/crashes/ice-15666.fixed create mode 100644 tests/ui/crashes/ice-15666.rs create mode 100644 tests/ui/crashes/ice-15666.stderr delete mode 100644 tests/ui/ref_option/ref_option.all.fixed delete mode 100644 tests/ui/ref_option/ref_option.private.fixed delete mode 100644 tests/ui/ref_option/ref_option.rs delete mode 100644 tests/ui/ref_option/ref_option_traits.rs create mode 100644 tests/ui/rest_pat_in_fully_bound_structs.fixed diff --git a/.github/ISSUE_TEMPLATE/new_lint.yml b/.github/ISSUE_TEMPLATE/new_lint.yml index a8202f6378fd..6ad16aead601 100644 --- a/.github/ISSUE_TEMPLATE/new_lint.yml +++ b/.github/ISSUE_TEMPLATE/new_lint.yml @@ -1,7 +1,5 @@ name: New lint suggestion -description: | - Suggest a new Clippy lint (currently not accepting new lints) - Check out the Clippy book for more information about the feature freeze. +description: Suggest a new Clippy lint. labels: ["A-lint"] body: - type: markdown diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 83bfd8e9c686..9e49f60892d2 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -32,10 +32,6 @@ order to get feedback. Delete this line and everything above before opening your PR. -Note that we are currently not taking in new PRs that add new lints. We are in a -feature freeze. Check out the book for more information. If you open a -feature-adding pull request, its review will be delayed. - --- *Please write a short comment explaining your change (or "none" for internal only changes)* diff --git a/.github/workflows/feature_freeze.yml b/.github/workflows/feature_freeze.yml deleted file mode 100644 index 5b139e767007..000000000000 --- a/.github/workflows/feature_freeze.yml +++ /dev/null @@ -1,45 +0,0 @@ -name: Feature freeze check - -on: - pull_request_target: - types: - - opened - branches: - - master - paths: - - 'clippy_lints/src/declared_lints.rs' - -jobs: - auto-comment: - runs-on: ubuntu-latest - - permissions: - pull-requests: write - - # Do not in any case add code that runs anything coming from the content - # of the pull request, as malicious code would be able to access the private - # GitHub token. - steps: - - name: Add freeze warning comment - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - GITHUB_REPOSITORY: ${{ github.repository }} - PR_NUMBER: ${{ github.event.pull_request.number }} - run: | - COMMENT=$(echo "**Seems that you are trying to add a new lint!**\n\ - \n\ - We are currently in a [feature freeze](https://doc.rust-lang.org/nightly/clippy/development/feature_freeze.html), so we are delaying all lint-adding PRs to September 18 and [focusing on bugfixes](https://github.com/rust-lang/rust-clippy/issues/15086).\n\ - \n\ - Thanks a lot for your contribution, and sorry for the inconvenience.\n\ - \n\ - With ❤ from the Clippy team.\n\ - \n\ - @rustbot note Feature-freeze\n\ - @rustbot blocked\n\ - @rustbot label +A-lint" - ) - curl -s -H "Authorization: Bearer $GITHUB_TOKEN" \ - -H "Content-Type: application/vnd.github.raw+json" \ - -X POST \ - --data "{\"body\":\"${COMMENT}\"}" \ - "https://api.github.com/repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments" diff --git a/.gitignore b/.gitignore index 36a4cdc1c352..666c4ceac4db 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,6 @@ helper.txt # mdbook generated output /book/book + +# Remove jujutsu directory from search tools +.jj diff --git a/CHANGELOG.md b/CHANGELOG.md index eb2a76a81836..3f26b9470e82 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,152 @@ document. ## Unreleased / Beta / In Rust Nightly -[4ef75291...master](https://github.com/rust-lang/rust-clippy/compare/4ef75291...master) +[e9b7045...master](https://github.com/rust-lang/rust-clippy/compare/e9b7045...master) + +## Rust 1.90 + +Current stable, released 2025-09-18 + +[View all 118 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2025-06-13T15%3A55%3A04Z..2025-07-25T13%3A24%3A00Z+base%3Amaster) + +Note: This Clippy release does not introduce many new lints and is focused entirely on bug fixes — see +[#15086](https://github.com/rust-lang/rust-clippy/issues/15086) for more details. + +## New Lints + +* Added [`manual_is_multiple_of`] to `complexity` [#14292](https://github.com/rust-lang/rust-clippy/pull/14292) +* Added [`doc_broken_link`] to `pedantic` [#13696](https://github.com/rust-lang/rust-clippy/pull/13696) + +### Moves and Deprecations + +* Move [`uninlined_format_args`] to `pedantic` (from `style`, now allow-by-default) [#15287](https://github.com/rust-lang/rust-clippy/pull/15287) + +### Enhancements + +* [`or_fun_call`] now lints `Option::get_or_insert`, `Result::map_or`, `Option/Result::and` methods + [#15071](https://github.com/rust-lang/rust-clippy/pull/15071) + [#15073](https://github.com/rust-lang/rust-clippy/pull/15073) + [#15074](https://github.com/rust-lang/rust-clippy/pull/15074) +* [`incompatible_msrv`] now recognizes types exceeding MSRV + [#15296](https://github.com/rust-lang/rust-clippy/pull/15296) +* [`incompatible_msrv`] now checks the right MSRV when in a `const` context + [#15297](https://github.com/rust-lang/rust-clippy/pull/15297) +* [`zero_ptr`] now lints in `const` context as well + [#15152](https://github.com/rust-lang/rust-clippy/pull/15152) +* [`map_identity`],[`flat_map_identity`] now recognizes `|[x, y]| [x, y]` as an identity function + [#15229](https://github.com/rust-lang/rust-clippy/pull/15229) +* [`exit`] no longer fails on the main function when using `--test` or `--all-targets` flag + [#15222](https://github.com/rust-lang/rust-clippy/pull/15222) + +### False Positive Fixes + +* [`if_then_some_else_none`] fixed FP when require type coercion + [#15267](https://github.com/rust-lang/rust-clippy/pull/15267) +* [`module_name_repetitions`] fixed FP on exported macros + [#15319](https://github.com/rust-lang/rust-clippy/pull/15319) +* [`unused_async`] fixed FP on function with `todo!` + [#15308](https://github.com/rust-lang/rust-clippy/pull/15308) +* [`useless_attribute`] fixed FP when using `#[expect(redundant_imports)]` and similar lint attributes + on `use` statements + [#15318](https://github.com/rust-lang/rust-clippy/pull/15318) +* [`pattern_type_mismatch`] fixed FP in external macro + [#15306](https://github.com/rust-lang/rust-clippy/pull/15306) +* [`large_enum_variant`] fixed FP by not suggesting `Box` in `no_std` mode + [#15241](https://github.com/rust-lang/rust-clippy/pull/15241) +* [`missing_inline_in_public_items`] fixed FP on functions with `extern` + [#15313](https://github.com/rust-lang/rust-clippy/pull/15313) +* [`needless_range_loop`] fixed FP on array literals + [#15314](https://github.com/rust-lang/rust-clippy/pull/15314) +* [`ptr_as_ptr`] fixed wrong suggestions with turbo fish + [#15289](https://github.com/rust-lang/rust-clippy/pull/15289) +* [`range_plus_one`], [`range_minus_one`] fixed FP by restricting lint to cases where it is safe + to switch the range type + [#14432](https://github.com/rust-lang/rust-clippy/pull/14432) +* [`ptr_arg`] fixed FP with underscore binding to `&T` or `&mut T` argument + [#15105](https://github.com/rust-lang/rust-clippy/pull/15105) +* [`unsafe_derive_deserialize`] fixed FP caused by the standard library's `pin!()` macro + [#15137](https://github.com/rust-lang/rust-clippy/pull/15137) +* [`unused_trait_names`] fixed FP in macros + [#14947](https://github.com/rust-lang/rust-clippy/pull/14947) +* [`expect_fun_call`] fixed invalid suggestions + [#15122](https://github.com/rust-lang/rust-clippy/pull/15122) +* [`manual_is_multiple_of`] fixed various false positives + [#15205](https://github.com/rust-lang/rust-clippy/pull/15205) +* [`manual_assert`] fixed wrong suggestions for macros + [#15264](https://github.com/rust-lang/rust-clippy/pull/15264) +* [`expect_used`] fixed false negative when meeting `Option::ok().expect` + [#15253](https://github.com/rust-lang/rust-clippy/pull/15253) +* [`manual_abs_diff`] fixed wrong suggestions behind refs + [#15265](https://github.com/rust-lang/rust-clippy/pull/15265) +* [`approx_constant`] fixed FP with overly precise literals and non trivially formatted numerals + [#15236](https://github.com/rust-lang/rust-clippy/pull/15236) +* [`arithmetic_side_effects`] fixed FP on `NonZeroU*.get() - 1` + [#15238](https://github.com/rust-lang/rust-clippy/pull/15238) +* [`legacy_numeric_constants`] fixed suggestion when call is inside parentheses + [#15191](https://github.com/rust-lang/rust-clippy/pull/15191) +* [`manual_is_variant_and`] fixed inverted suggestions that could lead to code with different semantics + [#15206](https://github.com/rust-lang/rust-clippy/pull/15206) +* [`unnecessary_map_or`] fixed FP by not proposing to replace `map_or` call when types wouldn't be correct + [#15181](https://github.com/rust-lang/rust-clippy/pull/15181) +* [`return_and_then`] fixed FP in case of a partially used expression + [#15115](https://github.com/rust-lang/rust-clippy/pull/15115) +* [`manual_let_else`] fixed FP with binding subpattern with unused name + [#15118](https://github.com/rust-lang/rust-clippy/pull/15118) +* [`suboptimal_flops`] fixed FP with ambiguous float types in mul_add suggestions + [#15133](https://github.com/rust-lang/rust-clippy/pull/15133) +* [`borrow_as_ptr`] fixed FP by not removing cast to trait object pointer + [#15145](https://github.com/rust-lang/rust-clippy/pull/15145) +* [`unnecessary_operation`] fixed FP by not removing casts if they are useful to type the expression + [#15182](https://github.com/rust-lang/rust-clippy/pull/15182) +* [`empty_loop`] fixed FP on intrinsic function declaration + [#15201](https://github.com/rust-lang/rust-clippy/pull/15201) +* [`doc_nested_refdefs`] fixed FP where task lists are reported as refdefs + [#15146](https://github.com/rust-lang/rust-clippy/pull/15146) +* [`std_instead_of_core`] fixed FP when not all items come from the new crate + [#15165](https://github.com/rust-lang/rust-clippy/pull/15165) +* [`swap_with_temporary`] fixed FP leading to different semantics being suggested + [#15172](https://github.com/rust-lang/rust-clippy/pull/15172) +* [`cast_possible_truncation`] fixed FP by not giving suggestions inside const context + [#15164](https://github.com/rust-lang/rust-clippy/pull/15164) +* [`missing_panics_doc`] fixed FP by allowing unwrap() and expect()s in const-only contexts + [#15170](https://github.com/rust-lang/rust-clippy/pull/15170) +* [`disallowed_script_idents`] fixed FP on identifiers with `_` + [#15123](https://github.com/rust-lang/rust-clippy/pull/15123) +* [`wildcard_enum_match_arm`] fixed wrong suggestions with raw identifiers + [#15093](https://github.com/rust-lang/rust-clippy/pull/15093) +* [`borrow_deref_ref`] fixed FP by not proposing replacing a reborrow when the reborrow is itself mutably borrowed + [#14967](https://github.com/rust-lang/rust-clippy/pull/14967) +* [`manual_ok_err`] fixed wrong suggestions with references + [#15053](https://github.com/rust-lang/rust-clippy/pull/15053) +* [`identity_op`] fixed FP when encountering `Default::default()` + [#15028](https://github.com/rust-lang/rust-clippy/pull/15028) +* [`exhaustive_structs`] fixed FP on structs with default valued field + [#15022](https://github.com/rust-lang/rust-clippy/pull/15022) +* [`collapsible_else_if`] fixed FP on conditionally compiled stmt + [#14906](https://github.com/rust-lang/rust-clippy/pull/14906) +* [`missing_const_for_fn`] fixed FP by checking MSRV before emitting lint on function containing + non-`Sized` trait bounds + [#15080](https://github.com/rust-lang/rust-clippy/pull/15080) +* [`question_mark`] fixed FP when else branch of let-else contains `#[cfg]` + [#15082](https://github.com/rust-lang/rust-clippy/pull/15082) + +### ICE Fixes + +* [`single_match`] fixed ICE with deref patterns and string literals + [#15124](https://github.com/rust-lang/rust-clippy/pull/15124) +* [`needless_doctest_main`] fixed panic when doctest is invalid + [#15052](https://github.com/rust-lang/rust-clippy/pull/15052) +* [`zero_sized_map_values`] do not attempt to compute size of a type with escaping lifetimes + [#15434](https://github.com/rust-lang/rust-clippy/pull/15434) + +### Documentation Improvements + +* [`manual_is_variant_and`] improved documentation to include equality comparison patterns + [#15239](https://github.com/rust-lang/rust-clippy/pull/15239) +* [`uninlined_format_args`] improved documentation with example of how to fix a `{:?}` parameter + [#15228](https://github.com/rust-lang/rust-clippy/pull/15228) +* [`undocumented_unsafe_blocks`] improved documentation wording + [#15213](https://github.com/rust-lang/rust-clippy/pull/15213) ## Rust 1.89 @@ -6408,6 +6553,7 @@ Released 2018-09-13 [`redundant_feature_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_feature_names [`redundant_field_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names [`redundant_guards`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_guards +[`redundant_iter_cloned`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_iter_cloned [`redundant_locals`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_locals [`redundant_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern [`redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern_matching diff --git a/Cargo.toml b/Cargo.toml index b3618932ded7..e06383499893 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.91" +version = "0.1.92" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/book/src/README.md b/book/src/README.md index db73b49ecc24..5d2c3972b060 100644 --- a/book/src/README.md +++ b/book/src/README.md @@ -1,9 +1,5 @@ # Clippy -[### IMPORTANT NOTE FOR CONTRIBUTORS ================](development/feature_freeze.md) - ----- - [![License: MIT OR Apache-2.0](https://img.shields.io/crates/l/clippy.svg)](https://github.com/rust-lang/rust-clippy#license) A collection of lints to catch common mistakes and improve your diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index b66c3481e493..39fe7358ed87 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -13,7 +13,6 @@ - [GitLab CI](continuous_integration/gitlab.md) - [Travis CI](continuous_integration/travis.md) - [Development](development/README.md) - - [IMPORTANT: FEATURE FREEZE](development/feature_freeze.md) - [Basics](development/basics.md) - [Adding Lints](development/adding_lints.md) - [Defining Lints](development/defining_lints.md) diff --git a/book/src/development/adding_lints.md b/book/src/development/adding_lints.md index a42a29837446..2b89e94cf8f4 100644 --- a/book/src/development/adding_lints.md +++ b/book/src/development/adding_lints.md @@ -1,8 +1,5 @@ # Adding a new lint -[### IMPORTANT NOTE FOR CONTRIBUTORS ================](feature_freeze.md) - - You are probably here because you want to add a new lint to Clippy. If this is the first time you're contributing to Clippy, this document guides you through creating an example lint from scratch. diff --git a/book/src/development/feature_freeze.md b/book/src/development/feature_freeze.md deleted file mode 100644 index 260cb136cc07..000000000000 --- a/book/src/development/feature_freeze.md +++ /dev/null @@ -1,55 +0,0 @@ -# IMPORTANT: FEATURE FREEZE - -This is a temporary notice. - -From the 26th of June until the 18th of September we will perform a feature freeze. Only bugfix PRs will be reviewed -except already open ones. Every feature-adding PR opened in between those dates will be moved into a -milestone to be reviewed separately at another time. - -We do this because of the long backlog of bugs that need to be addressed -in order to continue being the state-of-the-art linter that Clippy has become known for being. - -## For contributors - -If you are a contributor or are planning to become one, **please do not open a lint-adding PR**, we have lots of open -bugs of all levels of difficulty that you can address instead! - -We currently have about 800 lints, each one posing a maintainability challenge that needs to account to every possible -use case of the whole ecosystem. Bugs are natural in every software, but the Clippy team considers that Clippy needs a -refinement period. - -If you open a PR at this time, we will not review it but push it into a milestone until the refinement period ends, -adding additional load into our reviewing schedules. - -## I want to help, what can I do - -Thanks a lot to everyone who wants to help Clippy become better software in this feature freeze period! -If you'd like to help, making a bugfix, making sure that it works, and opening a PR is a great step! - -To find things to fix, go to the [tracking issue][tracking_issue], find an issue that you like, go there and claim that -issue with `@rustbot claim`. - -As a general metric and always taking into account your skill and knowledge level, you can use this guide: - -- 🟥 [ICEs][search_ice], these are compiler errors that causes Clippy to panic and crash. Usually involves high-level -debugging, sometimes interacting directly with the upstream compiler. Difficult to fix but a great challenge that -improves a lot developer workflows! - -- 🟧 [Suggestion causes bug][sugg_causes_bug], Clippy suggested code that changed logic in some silent way. -Unacceptable, as this may have disastrous consequences. Easier to fix than ICEs - -- 🟨 [Suggestion causes error][sugg_causes_error], Clippy suggested code snippet that caused a compiler error -when applied. We need to make sure that Clippy doesn't suggest using a variable twice at the same time or similar -easy-to-happen occurrences. - -- 🟩 [False positives][false_positive], a lint should not have fired, the easiest of them all, as this is "just" -identifying the root of a false positive and making an exception for those cases. - -Note that false negatives do not have priority unless the case is very clear, as they are a feature-request in a -trench coat. - -[search_ice]: https://github.com/rust-lang/rust-clippy/issues?q=sort%3Aupdated-desc+state%3Aopen+label%3A%22I-ICE%22 -[sugg_causes_bug]: https://github.com/rust-lang/rust-clippy/issues?q=sort%3Aupdated-desc%20state%3Aopen%20label%3AI-suggestion-causes-bug -[sugg_causes_error]: https://github.com/rust-lang/rust-clippy/issues?q=sort%3Aupdated-desc%20state%3Aopen%20label%3AI-suggestion-causes-error%20 -[false_positive]: https://github.com/rust-lang/rust-clippy/issues?q=sort%3Aupdated-desc%20state%3Aopen%20label%3AI-false-positive -[tracking_issue]: https://github.com/rust-lang/rust-clippy/issues/15086 diff --git a/clippy_config/Cargo.toml b/clippy_config/Cargo.toml index 6ad2cf0d0b10..f8c748290e41 100644 --- a/clippy_config/Cargo.toml +++ b/clippy_config/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_config" -version = "0.1.91" +version = "0.1.92" edition = "2024" publish = false diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 70184ee2ca59..51e59ae20507 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_lints" -version = "0.1.91" +version = "0.1.92" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/clippy_lints/src/attrs/useless_attribute.rs b/clippy_lints/src/attrs/useless_attribute.rs index b9b5cedb5aa7..1cebc18edc90 100644 --- a/clippy_lints/src/attrs/useless_attribute.rs +++ b/clippy_lints/src/attrs/useless_attribute.rs @@ -30,6 +30,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, item: &Item, attrs: &[Attribute]) { sym::ambiguous_glob_reexports | sym::dead_code | sym::deprecated + | sym::deprecated_in_future | sym::hidden_glob_reexports | sym::unreachable_pub | sym::unused diff --git a/clippy_lints/src/casts/as_underscore.rs b/clippy_lints/src/casts/as_underscore.rs index 3ac486dd63fb..a73e48e5fd5d 100644 --- a/clippy_lints/src/casts/as_underscore.rs +++ b/clippy_lints/src/casts/as_underscore.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use rustc_errors::Applicability; use rustc_hir::{Expr, Ty, TyKind}; use rustc_lint::LateContext; -use rustc_middle::ty; +use rustc_middle::ty::IsSuggestable; use super::AS_UNDERSCORE; @@ -10,15 +10,15 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ty: &'tc 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() { - diag.help("consider giving the type explicitly"); - } else { + if ty_resolved.is_suggestable(cx.tcx, true) { diag.span_suggestion( ty.span, "consider giving the type explicitly", ty_resolved, Applicability::MachineApplicable, ); + } else { + diag.help("consider giving the type explicitly"); } }); } diff --git a/clippy_lints/src/casts/cast_possible_wrap.rs b/clippy_lints/src/casts/cast_possible_wrap.rs index e26c03ccda93..9eaa6e4cf26e 100644 --- a/clippy_lints/src/casts/cast_possible_wrap.rs +++ b/clippy_lints/src/casts/cast_possible_wrap.rs @@ -1,4 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::sugg::Sugg; +use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::Ty; @@ -16,7 +19,14 @@ enum EmitState { LintOnPtrSize(u64), } -pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) { +pub(super) fn check( + cx: &LateContext<'_>, + expr: &Expr<'_>, + cast_op: &Expr<'_>, + cast_from: Ty<'_>, + cast_to: Ty<'_>, + msrv: Msrv, +) { let (Some(from_nbits), Some(to_nbits)) = ( utils::int_ty_to_nbits(cx.tcx, cast_from), utils::int_ty_to_nbits(cx.tcx, cast_to), @@ -85,5 +95,23 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, ca .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"); } + + if msrv.meets(cx, msrvs::INTEGER_SIGN_CAST) + && let Some(cast) = utils::is_signedness_cast(cast_from, cast_to) + { + let method = match cast { + utils::CastTo::Signed => "cast_signed()", + utils::CastTo::Unsigned => "cast_unsigned()", + }; + let mut app = Applicability::MaybeIncorrect; + let sugg = Sugg::hir_with_context(cx, cast_op, expr.span.ctxt(), "..", &mut app); + + diag.span_suggestion( + expr.span, + format!("if this is intentional, use `{method}` instead"), + format!("{}.{method}", sugg.maybe_paren()), + app, + ); + } }); } diff --git a/clippy_lints/src/casts/cast_sign_loss.rs b/clippy_lints/src/casts/cast_sign_loss.rs index a70bd8861919..f870d27b796e 100644 --- a/clippy_lints/src/casts/cast_sign_loss.rs +++ b/clippy_lints/src/casts/cast_sign_loss.rs @@ -2,15 +2,18 @@ use std::convert::Infallible; use std::ops::ControlFlow; use clippy_utils::consts::{ConstEvalCtxt, Constant}; -use clippy_utils::diagnostics::span_lint; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::sugg::Sugg; use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; use clippy_utils::{method_chain_args, sext, sym}; +use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; use rustc_span::Symbol; -use super::CAST_SIGN_LOSS; +use super::{CAST_SIGN_LOSS, utils}; /// A list of methods that can never return a negative value. /// Includes methods that panic rather than returning a negative value. @@ -42,13 +45,33 @@ pub(super) fn check<'cx>( cast_op: &Expr<'_>, cast_from: Ty<'cx>, cast_to: Ty<'_>, + msrv: Msrv, ) { if should_lint(cx, cast_op, cast_from, cast_to) { - span_lint( + span_lint_and_then( cx, CAST_SIGN_LOSS, expr.span, format!("casting `{cast_from}` to `{cast_to}` may lose the sign of the value"), + |diag| { + if msrv.meets(cx, msrvs::INTEGER_SIGN_CAST) + && let Some(cast) = utils::is_signedness_cast(cast_from, cast_to) + { + let method = match cast { + utils::CastTo::Signed => "cast_signed()", + utils::CastTo::Unsigned => "cast_unsigned()", + }; + let mut app = Applicability::MaybeIncorrect; + let sugg = Sugg::hir_with_context(cx, cast_op, expr.span.ctxt(), "..", &mut app); + + diag.span_suggestion( + expr.span, + format!("if this is intentional, use `{method}` instead"), + format!("{}.{method}", sugg.maybe_paren()), + app, + ); + } + }, ); } } diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index d2e62ee56e43..47cc1da0a6e9 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -890,9 +890,9 @@ impl<'tcx> LateLintPass<'tcx> for Casts { if cast_to.is_numeric() { cast_possible_truncation::check(cx, expr, cast_from_expr, cast_from, cast_to, cast_to_hir.span); if cast_from.is_numeric() { - cast_possible_wrap::check(cx, expr, cast_from, cast_to); + cast_possible_wrap::check(cx, expr, cast_from_expr, cast_from, cast_to, self.msrv); cast_precision_loss::check(cx, expr, cast_from, cast_to); - cast_sign_loss::check(cx, expr, cast_from_expr, cast_from, cast_to); + cast_sign_loss::check(cx, expr, cast_from_expr, cast_from, cast_to, self.msrv); cast_abs_to_unsigned::check(cx, expr, cast_from_expr, cast_from, cast_to, self.msrv); cast_nan_to_int::check(cx, expr, cast_from_expr, cast_from, cast_to); } diff --git a/clippy_lints/src/casts/utils.rs b/clippy_lints/src/casts/utils.rs index d846d78b9ee7..707fc2a8eed3 100644 --- a/clippy_lints/src/casts/utils.rs +++ b/clippy_lints/src/casts/utils.rs @@ -60,3 +60,18 @@ pub(super) fn enum_ty_to_nbits(adt: AdtDef<'_>, tcx: TyCtxt<'_>) -> u64 { neg_bits.max(pos_bits).into() } } + +pub(super) enum CastTo { + Signed, + Unsigned, +} +/// Returns `Some` if the type cast is between 2 integral types that differ +/// only in signedness, otherwise `None`. The value of `Some` is which +/// signedness is casted to. +pub(super) fn is_signedness_cast(cast_from: Ty<'_>, cast_to: Ty<'_>) -> Option { + match (cast_from.kind(), cast_to.kind()) { + (ty::Int(from), ty::Uint(to)) if from.to_unsigned() == *to => Some(CastTo::Unsigned), + (ty::Uint(from), ty::Int(to)) if *from == to.to_unsigned() => Some(CastTo::Signed), + _ => None, + } +} diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index d0c7443a4a4b..2a4bedc18455 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -448,10 +448,12 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::methods::OR_THEN_UNWRAP_INFO, crate::methods::PATH_BUF_PUSH_OVERWRITE_INFO, crate::methods::PATH_ENDS_WITH_EXT_INFO, + crate::methods::PTR_OFFSET_WITH_CAST_INFO, crate::methods::RANGE_ZIP_WITH_LEN_INFO, crate::methods::READONLY_WRITE_LOCK_INFO, crate::methods::READ_LINE_WITHOUT_TRIM_INFO, crate::methods::REDUNDANT_AS_STR_INFO, + crate::methods::REDUNDANT_ITER_CLONED_INFO, crate::methods::REPEAT_ONCE_INFO, crate::methods::RESULT_FILTER_MAP_INFO, crate::methods::RESULT_MAP_OR_INTO_OPTION_INFO, @@ -503,7 +505,6 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::min_ident_chars::MIN_IDENT_CHARS_INFO, crate::minmax::MIN_MAX_INFO, crate::misc::SHORT_CIRCUIT_STATEMENT_INFO, - crate::misc::TOPLEVEL_REF_ARG_INFO, crate::misc::USED_UNDERSCORE_BINDING_INFO, crate::misc::USED_UNDERSCORE_ITEMS_INFO, crate::misc_early::BUILTIN_TYPE_SHADOW_INFO, @@ -625,7 +626,6 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::ptr::MUT_FROM_REF_INFO, crate::ptr::PTR_ARG_INFO, crate::ptr::PTR_EQ_INFO, - crate::ptr_offset_with_cast::PTR_OFFSET_WITH_CAST_INFO, crate::pub_underscore_fields::PUB_UNDERSCORE_FIELDS_INFO, crate::pub_use::PUB_USE_INFO, crate::question_mark::QUESTION_MARK_INFO, @@ -706,6 +706,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::tests_outside_test_module::TESTS_OUTSIDE_TEST_MODULE_INFO, crate::to_digit_is_some::TO_DIGIT_IS_SOME_INFO, crate::to_string_trait_impl::TO_STRING_TRAIT_IMPL_INFO, + crate::toplevel_ref_arg::TOPLEVEL_REF_ARG_INFO, crate::trailing_empty_array::TRAILING_EMPTY_ARRAY_INFO, crate::trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS_INFO, crate::trait_bounds::TYPE_REPETITION_IN_BOUNDS_INFO, diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 9645a26a68ae..a70105db1949 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1,10 +1,9 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; -use clippy_utils::ty::{adjust_derefs_manually_drop, implements_trait, is_manually_drop}; +use clippy_utils::ty::{adjust_derefs_manually_drop, implements_trait, is_manually_drop, peel_and_count_ty_refs}; use clippy_utils::{ DefinedTy, ExprUseNode, expr_use_ctxt, get_parent_expr, is_block_like, is_lint_allowed, path_to_local, - peel_middle_ty_refs, }; use rustc_ast::util::parser::ExprPrecedence; use rustc_data_structures::fx::FxIndexMap; @@ -942,7 +941,7 @@ fn report<'tcx>( let (expr_str, expr_is_macro_call) = snippet_with_context(cx, expr.span, data.first_expr.span.ctxt(), "..", &mut app); let ty = typeck.expr_ty(expr); - let (_, ref_count) = peel_middle_ty_refs(ty); + let (_, ref_count, _) = peel_and_count_ty_refs(ty); let deref_str = if ty_changed_count >= ref_count && ref_count != 0 { // a deref call changing &T -> &U requires two deref operators the first time // this occurs. One to remove the reference, a second to call the deref impl. @@ -1045,7 +1044,7 @@ fn report<'tcx>( if let ty::Ref(_, dst, _) = data.adjusted_ty.kind() && dst.is_slice() { - let (src, n_src_refs) = peel_middle_ty_refs(ty); + let (src, n_src_refs, _) = peel_and_count_ty_refs(ty); if n_src_refs >= 2 && src.is_array() { return; } diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs deleted file mode 100644 index c53a957f6a8b..000000000000 --- a/clippy_lints/src/derive.rs +++ /dev/null @@ -1,527 +0,0 @@ -use std::ops::ControlFlow; - -use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then, span_lint_hir_and_then}; -use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy}; -use clippy_utils::{has_non_exhaustive_attr, is_lint_allowed, paths}; -use rustc_errors::Applicability; -use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn, walk_item}; -use rustc_hir::{self as hir, BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, Impl, Item, ItemKind, UnsafeSource}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::hir::nested_filter; -use rustc_middle::ty::{ - self, ClauseKind, GenericArgKind, GenericParamDefKind, ParamEnv, TraitPredicate, Ty, TyCtxt, Upcast, -}; -use rustc_session::declare_lint_pass; -use rustc_span::def_id::LocalDefId; -use rustc_span::{Span, sym}; - -declare_clippy_lint! { - /// ### What it does - /// Lints against manual `PartialEq` implementations for types with a derived `Hash` - /// implementation. - /// - /// ### Why is this bad? - /// The implementation of these traits must agree (for - /// example for use with `HashMap`) so it’s probably a bad idea to use a - /// default-generated `Hash` implementation with an explicitly defined - /// `PartialEq`. In particular, the following must hold for any type: - /// - /// ```text - /// k1 == k2 ⇒ hash(k1) == hash(k2) - /// ``` - /// - /// ### Example - /// ```ignore - /// #[derive(Hash)] - /// struct Foo; - /// - /// impl PartialEq for Foo { - /// ... - /// } - /// ``` - #[clippy::version = "pre 1.29.0"] - pub DERIVED_HASH_WITH_MANUAL_EQ, - correctness, - "deriving `Hash` but implementing `PartialEq` explicitly" -} - -declare_clippy_lint! { - /// ### What it does - /// Lints against manual `PartialOrd` and `Ord` implementations for types with a derived `Ord` - /// or `PartialOrd` implementation. - /// - /// ### Why is this bad? - /// The implementation of these traits must agree (for - /// example for use with `sort`) so it’s probably a bad idea to use a - /// default-generated `Ord` implementation with an explicitly defined - /// `PartialOrd`. In particular, the following must hold for any type - /// implementing `Ord`: - /// - /// ```text - /// k1.cmp(&k2) == k1.partial_cmp(&k2).unwrap() - /// ``` - /// - /// ### Example - /// ```rust,ignore - /// #[derive(Ord, PartialEq, Eq)] - /// struct Foo; - /// - /// impl PartialOrd for Foo { - /// ... - /// } - /// ``` - /// Use instead: - /// ```rust,ignore - /// #[derive(PartialEq, Eq)] - /// struct Foo; - /// - /// impl PartialOrd for Foo { - /// fn partial_cmp(&self, other: &Foo) -> Option { - /// Some(self.cmp(other)) - /// } - /// } - /// - /// impl Ord for Foo { - /// ... - /// } - /// ``` - /// or, if you don't need a custom ordering: - /// ```rust,ignore - /// #[derive(Ord, PartialOrd, PartialEq, Eq)] - /// struct Foo; - /// ``` - #[clippy::version = "1.47.0"] - pub DERIVE_ORD_XOR_PARTIAL_ORD, - correctness, - "deriving `Ord` but implementing `PartialOrd` explicitly" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for explicit `Clone` implementations for `Copy` - /// types. - /// - /// ### Why is this bad? - /// To avoid surprising behavior, these traits should - /// agree and the behavior of `Copy` cannot be overridden. In almost all - /// situations a `Copy` type should have a `Clone` implementation that does - /// nothing more than copy the object, which is what `#[derive(Copy, Clone)]` - /// gets you. - /// - /// ### Example - /// ```rust,ignore - /// #[derive(Copy)] - /// struct Foo; - /// - /// impl Clone for Foo { - /// // .. - /// } - /// ``` - #[clippy::version = "pre 1.29.0"] - pub EXPL_IMPL_CLONE_ON_COPY, - pedantic, - "implementing `Clone` explicitly on `Copy` types" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for deriving `serde::Deserialize` on a type that - /// has methods using `unsafe`. - /// - /// ### Why is this bad? - /// Deriving `serde::Deserialize` will create a constructor - /// that may violate invariants held by another constructor. - /// - /// ### Example - /// ```rust,ignore - /// use serde::Deserialize; - /// - /// #[derive(Deserialize)] - /// pub struct Foo { - /// // .. - /// } - /// - /// impl Foo { - /// pub fn new() -> Self { - /// // setup here .. - /// } - /// - /// pub unsafe fn parts() -> (&str, &str) { - /// // assumes invariants hold - /// } - /// } - /// ``` - #[clippy::version = "1.45.0"] - pub UNSAFE_DERIVE_DESERIALIZE, - pedantic, - "deriving `serde::Deserialize` on a type that has methods using `unsafe`" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for types that derive `PartialEq` and could implement `Eq`. - /// - /// ### Why is this bad? - /// If a type `T` derives `PartialEq` and all of its members implement `Eq`, - /// then `T` can always implement `Eq`. Implementing `Eq` allows `T` to be used - /// in APIs that require `Eq` types. It also allows structs containing `T` to derive - /// `Eq` themselves. - /// - /// ### Example - /// ```no_run - /// #[derive(PartialEq)] - /// struct Foo { - /// i_am_eq: i32, - /// i_am_eq_too: Vec, - /// } - /// ``` - /// Use instead: - /// ```no_run - /// #[derive(PartialEq, Eq)] - /// struct Foo { - /// i_am_eq: i32, - /// i_am_eq_too: Vec, - /// } - /// ``` - #[clippy::version = "1.63.0"] - pub DERIVE_PARTIAL_EQ_WITHOUT_EQ, - nursery, - "deriving `PartialEq` on a type that can implement `Eq`, without implementing `Eq`" -} - -declare_lint_pass!(Derive => [ - EXPL_IMPL_CLONE_ON_COPY, - DERIVED_HASH_WITH_MANUAL_EQ, - DERIVE_ORD_XOR_PARTIAL_ORD, - UNSAFE_DERIVE_DESERIALIZE, - DERIVE_PARTIAL_EQ_WITHOUT_EQ -]); - -impl<'tcx> LateLintPass<'tcx> for Derive { - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - if let ItemKind::Impl(Impl { - of_trait: Some(of_trait), - .. - }) = item.kind - { - let trait_ref = &of_trait.trait_ref; - let ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); - let is_automatically_derived = cx.tcx.is_automatically_derived(item.owner_id.to_def_id()); - - check_hash_peq(cx, item.span, trait_ref, ty, is_automatically_derived); - check_ord_partial_ord(cx, item.span, trait_ref, ty, is_automatically_derived); - - if is_automatically_derived { - check_unsafe_derive_deserialize(cx, item, trait_ref, ty); - check_partial_eq_without_eq(cx, item.span, trait_ref, ty); - } else { - check_copy_clone(cx, item, trait_ref, ty); - } - } - } -} - -/// Implementation of the `DERIVED_HASH_WITH_MANUAL_EQ` lint. -fn check_hash_peq<'tcx>( - cx: &LateContext<'tcx>, - span: Span, - trait_ref: &hir::TraitRef<'_>, - ty: Ty<'tcx>, - hash_is_automatically_derived: bool, -) { - if let Some(peq_trait_def_id) = cx.tcx.lang_items().eq_trait() - && let Some(def_id) = trait_ref.trait_def_id() - && cx.tcx.is_diagnostic_item(sym::Hash, def_id) - { - // Look for the PartialEq implementations for `ty` - cx.tcx.for_each_relevant_impl(peq_trait_def_id, ty, |impl_id| { - let peq_is_automatically_derived = cx.tcx.is_automatically_derived(impl_id); - - if !hash_is_automatically_derived || peq_is_automatically_derived { - return; - } - - let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation"); - - // Only care about `impl PartialEq for Foo` - // For `impl PartialEq for A, input_types is [A, B] - if trait_ref.instantiate_identity().args.type_at(1) == ty { - span_lint_and_then( - cx, - DERIVED_HASH_WITH_MANUAL_EQ, - span, - "you are deriving `Hash` but have implemented `PartialEq` explicitly", - |diag| { - if let Some(local_def_id) = impl_id.as_local() { - let hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id); - diag.span_note(cx.tcx.hir_span(hir_id), "`PartialEq` implemented here"); - } - }, - ); - } - }); - } -} - -/// Implementation of the `DERIVE_ORD_XOR_PARTIAL_ORD` lint. -fn check_ord_partial_ord<'tcx>( - cx: &LateContext<'tcx>, - span: Span, - trait_ref: &hir::TraitRef<'_>, - ty: Ty<'tcx>, - ord_is_automatically_derived: bool, -) { - if let Some(ord_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Ord) - && let Some(partial_ord_trait_def_id) = cx.tcx.lang_items().partial_ord_trait() - && let Some(def_id) = &trait_ref.trait_def_id() - && *def_id == ord_trait_def_id - { - // Look for the PartialOrd implementations for `ty` - cx.tcx.for_each_relevant_impl(partial_ord_trait_def_id, ty, |impl_id| { - let partial_ord_is_automatically_derived = cx.tcx.is_automatically_derived(impl_id); - - if partial_ord_is_automatically_derived == ord_is_automatically_derived { - return; - } - - let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation"); - - // Only care about `impl PartialOrd for Foo` - // For `impl PartialOrd for A, input_types is [A, B] - if trait_ref.instantiate_identity().args.type_at(1) == ty { - let mess = if partial_ord_is_automatically_derived { - "you are implementing `Ord` explicitly but have derived `PartialOrd`" - } else { - "you are deriving `Ord` but have implemented `PartialOrd` explicitly" - }; - - span_lint_and_then(cx, DERIVE_ORD_XOR_PARTIAL_ORD, span, mess, |diag| { - if let Some(local_def_id) = impl_id.as_local() { - let hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id); - diag.span_note(cx.tcx.hir_span(hir_id), "`PartialOrd` implemented here"); - } - }); - } - }); - } -} - -/// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint. -fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) { - let clone_id = match cx.tcx.lang_items().clone_trait() { - Some(id) if trait_ref.trait_def_id() == Some(id) => id, - _ => return, - }; - let Some(copy_id) = cx.tcx.lang_items().copy_trait() else { - return; - }; - let (ty_adt, ty_subs) = match *ty.kind() { - // Unions can't derive clone. - ty::Adt(adt, subs) if !adt.is_union() => (adt, subs), - _ => return, - }; - // If the current self type doesn't implement Copy (due to generic constraints), search to see if - // there's a Copy impl for any instance of the adt. - if !is_copy(cx, ty) { - if ty_subs.non_erasable_generics().next().is_some() { - let has_copy_impl = cx.tcx.local_trait_impls(copy_id).iter().any(|&id| { - matches!(cx.tcx.type_of(id).instantiate_identity().kind(), ty::Adt(adt, _) - if ty_adt.did() == adt.did()) - }); - if !has_copy_impl { - return; - } - } else { - return; - } - } - // Derive constrains all generic types to requiring Clone. Check if any type is not constrained for - // this impl. - if ty_subs.types().any(|ty| !implements_trait(cx, ty, clone_id, &[])) { - return; - } - // `#[repr(packed)]` structs with type/const parameters can't derive `Clone`. - // https://github.com/rust-lang/rust-clippy/issues/10188 - if ty_adt.repr().packed() - && ty_subs - .iter() - .any(|arg| matches!(arg.kind(), GenericArgKind::Type(_) | GenericArgKind::Const(_))) - { - return; - } - // The presence of `unsafe` fields prevents deriving `Clone` automatically - if ty_adt.all_fields().any(|f| f.safety.is_unsafe()) { - return; - } - - span_lint_and_note( - cx, - EXPL_IMPL_CLONE_ON_COPY, - item.span, - "you are implementing `Clone` explicitly on a `Copy` type", - Some(item.span), - "consider deriving `Clone` or removing `Copy`", - ); -} - -/// Implementation of the `UNSAFE_DERIVE_DESERIALIZE` lint. -fn check_unsafe_derive_deserialize<'tcx>( - cx: &LateContext<'tcx>, - item: &Item<'_>, - trait_ref: &hir::TraitRef<'_>, - ty: Ty<'tcx>, -) { - fn has_unsafe<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>) -> bool { - let mut visitor = UnsafeVisitor { cx }; - walk_item(&mut visitor, item).is_break() - } - - if let Some(trait_def_id) = trait_ref.trait_def_id() - && paths::SERDE_DESERIALIZE.matches(cx, trait_def_id) - && let ty::Adt(def, _) = ty.kind() - && let Some(local_def_id) = def.did().as_local() - && let adt_hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id) - && !is_lint_allowed(cx, UNSAFE_DERIVE_DESERIALIZE, adt_hir_id) - && cx - .tcx - .inherent_impls(def.did()) - .iter() - .map(|imp_did| cx.tcx.hir_expect_item(imp_did.expect_local())) - .any(|imp| has_unsafe(cx, imp)) - { - span_lint_hir_and_then( - cx, - UNSAFE_DERIVE_DESERIALIZE, - adt_hir_id, - item.span, - "you are deriving `serde::Deserialize` on a type that has methods using `unsafe`", - |diag| { - diag.help( - "consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html", - ); - }, - ); - } -} - -struct UnsafeVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, -} - -impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> { - type Result = ControlFlow<()>; - type NestedFilter = nested_filter::All; - - fn visit_fn( - &mut self, - kind: FnKind<'tcx>, - decl: &'tcx FnDecl<'_>, - body_id: BodyId, - _: Span, - id: LocalDefId, - ) -> Self::Result { - if let Some(header) = kind.header() - && header.is_unsafe() - { - ControlFlow::Break(()) - } else { - walk_fn(self, kind, decl, body_id, id) - } - } - - fn visit_expr(&mut self, expr: &'tcx Expr<'_>) -> Self::Result { - if let ExprKind::Block(block, _) = expr.kind - && block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) - && block - .span - .source_callee() - .and_then(|expr| expr.macro_def_id) - .is_none_or(|did| !self.cx.tcx.is_diagnostic_item(sym::pin_macro, did)) - { - return ControlFlow::Break(()); - } - - walk_expr(self, expr) - } - - fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt { - self.cx.tcx - } -} - -/// Implementation of the `DERIVE_PARTIAL_EQ_WITHOUT_EQ` lint. -fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) { - if let ty::Adt(adt, args) = ty.kind() - && cx.tcx.visibility(adt.did()).is_public() - && let Some(eq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Eq) - && let Some(def_id) = trait_ref.trait_def_id() - && cx.tcx.is_diagnostic_item(sym::PartialEq, def_id) - && !has_non_exhaustive_attr(cx.tcx, *adt) - && !ty_implements_eq_trait(cx.tcx, ty, eq_trait_def_id) - && let typing_env = typing_env_for_derived_eq(cx.tcx, adt.did(), eq_trait_def_id) - && let Some(local_def_id) = adt.did().as_local() - // If all of our fields implement `Eq`, we can implement `Eq` too - && adt - .all_fields() - .map(|f| f.ty(cx.tcx, args)) - .all(|ty| implements_trait_with_env(cx.tcx, typing_env, ty, eq_trait_def_id, None, &[])) - { - span_lint_hir_and_then( - cx, - DERIVE_PARTIAL_EQ_WITHOUT_EQ, - cx.tcx.local_def_id_to_hir_id(local_def_id), - span.ctxt().outer_expn_data().call_site, - "you are deriving `PartialEq` and can implement `Eq`", - |diag| { - diag.span_suggestion( - span.ctxt().outer_expn_data().call_site, - "consider deriving `Eq` as well", - "PartialEq, Eq", - Applicability::MachineApplicable, - ); - }, - ); - } -} - -fn ty_implements_eq_trait<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, eq_trait_id: DefId) -> bool { - tcx.non_blanket_impls_for_ty(eq_trait_id, ty).next().is_some() -} - -/// Creates the `ParamEnv` used for the given type's derived `Eq` impl. -fn typing_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) -> ty::TypingEnv<'_> { - // Initial map from generic index to param def. - // Vec<(param_def, needs_eq)> - let mut params = tcx - .generics_of(did) - .own_params - .iter() - .map(|p| (p, matches!(p.kind, GenericParamDefKind::Type { .. }))) - .collect::>(); - - let ty_predicates = tcx.predicates_of(did).predicates; - for (p, _) in ty_predicates { - if let ClauseKind::Trait(p) = p.kind().skip_binder() - && p.trait_ref.def_id == eq_trait_id - && let ty::Param(self_ty) = p.trait_ref.self_ty().kind() - { - // Flag types which already have an `Eq` bound. - params[self_ty.index as usize].1 = false; - } - } - - let param_env = ParamEnv::new(tcx.mk_clauses_from_iter(ty_predicates.iter().map(|&(p, _)| p).chain( - params.iter().filter(|&&(_, needs_eq)| needs_eq).map(|&(param, _)| { - ClauseKind::Trait(TraitPredicate { - trait_ref: ty::TraitRef::new(tcx, eq_trait_id, [tcx.mk_param_from_def(param)]), - polarity: ty::PredicatePolarity::Positive, - }) - .upcast(tcx) - }), - ))); - ty::TypingEnv { - typing_mode: ty::TypingMode::non_body_analysis(), - param_env, - } -} diff --git a/clippy_lints/src/derive/derive_ord_xor_partial_ord.rs b/clippy_lints/src/derive/derive_ord_xor_partial_ord.rs new file mode 100644 index 000000000000..cbbcb2f7a3ba --- /dev/null +++ b/clippy_lints/src/derive/derive_ord_xor_partial_ord.rs @@ -0,0 +1,50 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_middle::ty::Ty; +use rustc_span::{Span, sym}; + +use super::DERIVE_ORD_XOR_PARTIAL_ORD; + +/// Implementation of the `DERIVE_ORD_XOR_PARTIAL_ORD` lint. +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + span: Span, + trait_ref: &hir::TraitRef<'_>, + ty: Ty<'tcx>, + ord_is_automatically_derived: bool, +) { + if let Some(ord_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Ord) + && let Some(partial_ord_trait_def_id) = cx.tcx.lang_items().partial_ord_trait() + && let Some(def_id) = &trait_ref.trait_def_id() + && *def_id == ord_trait_def_id + { + // Look for the PartialOrd implementations for `ty` + cx.tcx.for_each_relevant_impl(partial_ord_trait_def_id, ty, |impl_id| { + let partial_ord_is_automatically_derived = cx.tcx.is_automatically_derived(impl_id); + + if partial_ord_is_automatically_derived == ord_is_automatically_derived { + return; + } + + let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation"); + + // Only care about `impl PartialOrd for Foo` + // For `impl PartialOrd for A, input_types is [A, B] + if trait_ref.instantiate_identity().args.type_at(1) == ty { + let mess = if partial_ord_is_automatically_derived { + "you are implementing `Ord` explicitly but have derived `PartialOrd`" + } else { + "you are deriving `Ord` but have implemented `PartialOrd` explicitly" + }; + + span_lint_and_then(cx, DERIVE_ORD_XOR_PARTIAL_ORD, span, mess, |diag| { + if let Some(local_def_id) = impl_id.as_local() { + let hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id); + diag.span_note(cx.tcx.hir_span(hir_id), "`PartialOrd` implemented here"); + } + }); + } + }); + } +} diff --git a/clippy_lints/src/derive/derive_partial_eq_without_eq.rs b/clippy_lints/src/derive/derive_partial_eq_without_eq.rs new file mode 100644 index 000000000000..ed7881c461ff --- /dev/null +++ b/clippy_lints/src/derive/derive_partial_eq_without_eq.rs @@ -0,0 +1,87 @@ +use clippy_utils::diagnostics::span_lint_hir_and_then; +use clippy_utils::has_non_exhaustive_attr; +use clippy_utils::ty::implements_trait_with_env; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, ClauseKind, GenericParamDefKind, ParamEnv, TraitPredicate, Ty, TyCtxt, Upcast}; +use rustc_span::{Span, sym}; + +use super::DERIVE_PARTIAL_EQ_WITHOUT_EQ; + +/// Implementation of the `DERIVE_PARTIAL_EQ_WITHOUT_EQ` lint. +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) { + if let ty::Adt(adt, args) = ty.kind() + && cx.tcx.visibility(adt.did()).is_public() + && let Some(eq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Eq) + && let Some(def_id) = trait_ref.trait_def_id() + && cx.tcx.is_diagnostic_item(sym::PartialEq, def_id) + && !has_non_exhaustive_attr(cx.tcx, *adt) + && !ty_implements_eq_trait(cx.tcx, ty, eq_trait_def_id) + && let typing_env = typing_env_for_derived_eq(cx.tcx, adt.did(), eq_trait_def_id) + && let Some(local_def_id) = adt.did().as_local() + // If all of our fields implement `Eq`, we can implement `Eq` too + && adt + .all_fields() + .map(|f| f.ty(cx.tcx, args)) + .all(|ty| implements_trait_with_env(cx.tcx, typing_env, ty, eq_trait_def_id, None, &[])) + { + span_lint_hir_and_then( + cx, + DERIVE_PARTIAL_EQ_WITHOUT_EQ, + cx.tcx.local_def_id_to_hir_id(local_def_id), + span.ctxt().outer_expn_data().call_site, + "you are deriving `PartialEq` and can implement `Eq`", + |diag| { + diag.span_suggestion( + span.ctxt().outer_expn_data().call_site, + "consider deriving `Eq` as well", + "PartialEq, Eq", + Applicability::MachineApplicable, + ); + }, + ); + } +} + +fn ty_implements_eq_trait<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, eq_trait_id: DefId) -> bool { + tcx.non_blanket_impls_for_ty(eq_trait_id, ty).next().is_some() +} + +/// Creates the `ParamEnv` used for the given type's derived `Eq` impl. +fn typing_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) -> ty::TypingEnv<'_> { + // Initial map from generic index to param def. + // Vec<(param_def, needs_eq)> + let mut params = tcx + .generics_of(did) + .own_params + .iter() + .map(|p| (p, matches!(p.kind, GenericParamDefKind::Type { .. }))) + .collect::>(); + + let ty_predicates = tcx.predicates_of(did).predicates; + for (p, _) in ty_predicates { + if let ClauseKind::Trait(p) = p.kind().skip_binder() + && p.trait_ref.def_id == eq_trait_id + && let ty::Param(self_ty) = p.trait_ref.self_ty().kind() + { + // Flag types which already have an `Eq` bound. + params[self_ty.index as usize].1 = false; + } + } + + let param_env = ParamEnv::new(tcx.mk_clauses_from_iter(ty_predicates.iter().map(|&(p, _)| p).chain( + params.iter().filter(|&&(_, needs_eq)| needs_eq).map(|&(param, _)| { + ClauseKind::Trait(TraitPredicate { + trait_ref: ty::TraitRef::new(tcx, eq_trait_id, [tcx.mk_param_from_def(param)]), + polarity: ty::PredicatePolarity::Positive, + }) + .upcast(tcx) + }), + ))); + ty::TypingEnv { + typing_mode: ty::TypingMode::non_body_analysis(), + param_env, + } +} diff --git a/clippy_lints/src/derive/derived_hash_with_manual_eq.rs b/clippy_lints/src/derive/derived_hash_with_manual_eq.rs new file mode 100644 index 000000000000..6f36a58025a2 --- /dev/null +++ b/clippy_lints/src/derive/derived_hash_with_manual_eq.rs @@ -0,0 +1,49 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_middle::ty::Ty; +use rustc_span::{Span, sym}; + +use super::DERIVED_HASH_WITH_MANUAL_EQ; + +/// Implementation of the `DERIVED_HASH_WITH_MANUAL_EQ` lint. +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + span: Span, + trait_ref: &hir::TraitRef<'_>, + ty: Ty<'tcx>, + hash_is_automatically_derived: bool, +) { + if let Some(peq_trait_def_id) = cx.tcx.lang_items().eq_trait() + && let Some(def_id) = trait_ref.trait_def_id() + && cx.tcx.is_diagnostic_item(sym::Hash, def_id) + { + // Look for the PartialEq implementations for `ty` + cx.tcx.for_each_relevant_impl(peq_trait_def_id, ty, |impl_id| { + let peq_is_automatically_derived = cx.tcx.is_automatically_derived(impl_id); + + if !hash_is_automatically_derived || peq_is_automatically_derived { + return; + } + + let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation"); + + // Only care about `impl PartialEq for Foo` + // For `impl PartialEq for A, input_types is [A, B] + if trait_ref.instantiate_identity().args.type_at(1) == ty { + span_lint_and_then( + cx, + DERIVED_HASH_WITH_MANUAL_EQ, + span, + "you are deriving `Hash` but have implemented `PartialEq` explicitly", + |diag| { + if let Some(local_def_id) = impl_id.as_local() { + let hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id); + diag.span_note(cx.tcx.hir_span(hir_id), "`PartialEq` implemented here"); + } + }, + ); + } + }); + } +} diff --git a/clippy_lints/src/derive/expl_impl_clone_on_copy.rs b/clippy_lints/src/derive/expl_impl_clone_on_copy.rs new file mode 100644 index 000000000000..6b97b4bd6b4d --- /dev/null +++ b/clippy_lints/src/derive/expl_impl_clone_on_copy.rs @@ -0,0 +1,65 @@ +use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::ty::{implements_trait, is_copy}; +use rustc_hir::{self as hir, Item}; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, GenericArgKind, Ty}; + +use super::EXPL_IMPL_CLONE_ON_COPY; + +/// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint. +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) { + let clone_id = match cx.tcx.lang_items().clone_trait() { + Some(id) if trait_ref.trait_def_id() == Some(id) => id, + _ => return, + }; + let Some(copy_id) = cx.tcx.lang_items().copy_trait() else { + return; + }; + let (ty_adt, ty_subs) = match *ty.kind() { + // Unions can't derive clone. + ty::Adt(adt, subs) if !adt.is_union() => (adt, subs), + _ => return, + }; + // If the current self type doesn't implement Copy (due to generic constraints), search to see if + // there's a Copy impl for any instance of the adt. + if !is_copy(cx, ty) { + if ty_subs.non_erasable_generics().next().is_some() { + let has_copy_impl = cx.tcx.local_trait_impls(copy_id).iter().any(|&id| { + matches!(cx.tcx.type_of(id).instantiate_identity().kind(), ty::Adt(adt, _) + if ty_adt.did() == adt.did()) + }); + if !has_copy_impl { + return; + } + } else { + return; + } + } + // Derive constrains all generic types to requiring Clone. Check if any type is not constrained for + // this impl. + if ty_subs.types().any(|ty| !implements_trait(cx, ty, clone_id, &[])) { + return; + } + // `#[repr(packed)]` structs with type/const parameters can't derive `Clone`. + // https://github.com/rust-lang/rust-clippy/issues/10188 + if ty_adt.repr().packed() + && ty_subs + .iter() + .any(|arg| matches!(arg.kind(), GenericArgKind::Type(_) | GenericArgKind::Const(_))) + { + return; + } + // The presence of `unsafe` fields prevents deriving `Clone` automatically + if ty_adt.all_fields().any(|f| f.safety.is_unsafe()) { + return; + } + + span_lint_and_note( + cx, + EXPL_IMPL_CLONE_ON_COPY, + item.span, + "you are implementing `Clone` explicitly on a `Copy` type", + Some(item.span), + "consider deriving `Clone` or removing `Copy`", + ); +} diff --git a/clippy_lints/src/derive/mod.rs b/clippy_lints/src/derive/mod.rs new file mode 100644 index 000000000000..1d63394ce37d --- /dev/null +++ b/clippy_lints/src/derive/mod.rs @@ -0,0 +1,215 @@ +use rustc_hir::{Impl, Item, ItemKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::declare_lint_pass; + +mod derive_ord_xor_partial_ord; +mod derive_partial_eq_without_eq; +mod derived_hash_with_manual_eq; +mod expl_impl_clone_on_copy; +mod unsafe_derive_deserialize; + +declare_clippy_lint! { + /// ### What it does + /// Lints against manual `PartialEq` implementations for types with a derived `Hash` + /// implementation. + /// + /// ### Why is this bad? + /// The implementation of these traits must agree (for + /// example for use with `HashMap`) so it’s probably a bad idea to use a + /// default-generated `Hash` implementation with an explicitly defined + /// `PartialEq`. In particular, the following must hold for any type: + /// + /// ```text + /// k1 == k2 ⇒ hash(k1) == hash(k2) + /// ``` + /// + /// ### Example + /// ```ignore + /// #[derive(Hash)] + /// struct Foo; + /// + /// impl PartialEq for Foo { + /// ... + /// } + /// ``` + #[clippy::version = "pre 1.29.0"] + pub DERIVED_HASH_WITH_MANUAL_EQ, + correctness, + "deriving `Hash` but implementing `PartialEq` explicitly" +} + +declare_clippy_lint! { + /// ### What it does + /// Lints against manual `PartialOrd` and `Ord` implementations for types with a derived `Ord` + /// or `PartialOrd` implementation. + /// + /// ### Why is this bad? + /// The implementation of these traits must agree (for + /// example for use with `sort`) so it’s probably a bad idea to use a + /// default-generated `Ord` implementation with an explicitly defined + /// `PartialOrd`. In particular, the following must hold for any type + /// implementing `Ord`: + /// + /// ```text + /// k1.cmp(&k2) == k1.partial_cmp(&k2).unwrap() + /// ``` + /// + /// ### Example + /// ```rust,ignore + /// #[derive(Ord, PartialEq, Eq)] + /// struct Foo; + /// + /// impl PartialOrd for Foo { + /// ... + /// } + /// ``` + /// Use instead: + /// ```rust,ignore + /// #[derive(PartialEq, Eq)] + /// struct Foo; + /// + /// impl PartialOrd for Foo { + /// fn partial_cmp(&self, other: &Foo) -> Option { + /// Some(self.cmp(other)) + /// } + /// } + /// + /// impl Ord for Foo { + /// ... + /// } + /// ``` + /// or, if you don't need a custom ordering: + /// ```rust,ignore + /// #[derive(Ord, PartialOrd, PartialEq, Eq)] + /// struct Foo; + /// ``` + #[clippy::version = "1.47.0"] + pub DERIVE_ORD_XOR_PARTIAL_ORD, + correctness, + "deriving `Ord` but implementing `PartialOrd` explicitly" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for explicit `Clone` implementations for `Copy` + /// types. + /// + /// ### Why is this bad? + /// To avoid surprising behavior, these traits should + /// agree and the behavior of `Copy` cannot be overridden. In almost all + /// situations a `Copy` type should have a `Clone` implementation that does + /// nothing more than copy the object, which is what `#[derive(Copy, Clone)]` + /// gets you. + /// + /// ### Example + /// ```rust,ignore + /// #[derive(Copy)] + /// struct Foo; + /// + /// impl Clone for Foo { + /// // .. + /// } + /// ``` + #[clippy::version = "pre 1.29.0"] + pub EXPL_IMPL_CLONE_ON_COPY, + pedantic, + "implementing `Clone` explicitly on `Copy` types" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for deriving `serde::Deserialize` on a type that + /// has methods using `unsafe`. + /// + /// ### Why is this bad? + /// Deriving `serde::Deserialize` will create a constructor + /// that may violate invariants held by another constructor. + /// + /// ### Example + /// ```rust,ignore + /// use serde::Deserialize; + /// + /// #[derive(Deserialize)] + /// pub struct Foo { + /// // .. + /// } + /// + /// impl Foo { + /// pub fn new() -> Self { + /// // setup here .. + /// } + /// + /// pub unsafe fn parts() -> (&str, &str) { + /// // assumes invariants hold + /// } + /// } + /// ``` + #[clippy::version = "1.45.0"] + pub UNSAFE_DERIVE_DESERIALIZE, + pedantic, + "deriving `serde::Deserialize` on a type that has methods using `unsafe`" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for types that derive `PartialEq` and could implement `Eq`. + /// + /// ### Why is this bad? + /// If a type `T` derives `PartialEq` and all of its members implement `Eq`, + /// then `T` can always implement `Eq`. Implementing `Eq` allows `T` to be used + /// in APIs that require `Eq` types. It also allows structs containing `T` to derive + /// `Eq` themselves. + /// + /// ### Example + /// ```no_run + /// #[derive(PartialEq)] + /// struct Foo { + /// i_am_eq: i32, + /// i_am_eq_too: Vec, + /// } + /// ``` + /// Use instead: + /// ```no_run + /// #[derive(PartialEq, Eq)] + /// struct Foo { + /// i_am_eq: i32, + /// i_am_eq_too: Vec, + /// } + /// ``` + #[clippy::version = "1.63.0"] + pub DERIVE_PARTIAL_EQ_WITHOUT_EQ, + nursery, + "deriving `PartialEq` on a type that can implement `Eq`, without implementing `Eq`" +} + +declare_lint_pass!(Derive => [ + EXPL_IMPL_CLONE_ON_COPY, + DERIVED_HASH_WITH_MANUAL_EQ, + DERIVE_ORD_XOR_PARTIAL_ORD, + UNSAFE_DERIVE_DESERIALIZE, + DERIVE_PARTIAL_EQ_WITHOUT_EQ +]); + +impl<'tcx> LateLintPass<'tcx> for Derive { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + if let ItemKind::Impl(Impl { + of_trait: Some(of_trait), + .. + }) = item.kind + { + let trait_ref = &of_trait.trait_ref; + let ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); + let is_automatically_derived = cx.tcx.is_automatically_derived(item.owner_id.to_def_id()); + + derived_hash_with_manual_eq::check(cx, item.span, trait_ref, ty, is_automatically_derived); + derive_ord_xor_partial_ord::check(cx, item.span, trait_ref, ty, is_automatically_derived); + + if is_automatically_derived { + unsafe_derive_deserialize::check(cx, item, trait_ref, ty); + derive_partial_eq_without_eq::check(cx, item.span, trait_ref, ty); + } else { + expl_impl_clone_on_copy::check(cx, item, trait_ref, ty); + } + } + } +} diff --git a/clippy_lints/src/derive/unsafe_derive_deserialize.rs b/clippy_lints/src/derive/unsafe_derive_deserialize.rs new file mode 100644 index 000000000000..c391e7b62289 --- /dev/null +++ b/clippy_lints/src/derive/unsafe_derive_deserialize.rs @@ -0,0 +1,93 @@ +use std::ops::ControlFlow; + +use clippy_utils::diagnostics::span_lint_hir_and_then; +use clippy_utils::{is_lint_allowed, paths}; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn, walk_item}; +use rustc_hir::{self as hir, BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, Item, UnsafeSource}; +use rustc_lint::LateContext; +use rustc_middle::hir::nested_filter; +use rustc_middle::ty::{self, Ty}; +use rustc_span::{Span, sym}; + +use super::UNSAFE_DERIVE_DESERIALIZE; + +/// Implementation of the `UNSAFE_DERIVE_DESERIALIZE` lint. +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) { + fn has_unsafe<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>) -> bool { + let mut visitor = UnsafeVisitor { cx }; + walk_item(&mut visitor, item).is_break() + } + + if let Some(trait_def_id) = trait_ref.trait_def_id() + && paths::SERDE_DESERIALIZE.matches(cx, trait_def_id) + && let ty::Adt(def, _) = ty.kind() + && let Some(local_def_id) = def.did().as_local() + && let adt_hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id) + && !is_lint_allowed(cx, UNSAFE_DERIVE_DESERIALIZE, adt_hir_id) + && cx + .tcx + .inherent_impls(def.did()) + .iter() + .map(|imp_did| cx.tcx.hir_expect_item(imp_did.expect_local())) + .any(|imp| has_unsafe(cx, imp)) + { + span_lint_hir_and_then( + cx, + UNSAFE_DERIVE_DESERIALIZE, + adt_hir_id, + item.span, + "you are deriving `serde::Deserialize` on a type that has methods using `unsafe`", + |diag| { + diag.help( + "consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html", + ); + }, + ); + } +} + +struct UnsafeVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, +} + +impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> { + type Result = ControlFlow<()>; + type NestedFilter = nested_filter::All; + + fn visit_fn( + &mut self, + kind: FnKind<'tcx>, + decl: &'tcx FnDecl<'_>, + body_id: BodyId, + _: Span, + id: LocalDefId, + ) -> Self::Result { + if let Some(header) = kind.header() + && header.is_unsafe() + { + ControlFlow::Break(()) + } else { + walk_fn(self, kind, decl, body_id, id) + } + } + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) -> Self::Result { + if let ExprKind::Block(block, _) = expr.kind + && block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) + && block + .span + .source_callee() + .and_then(|expr| expr.macro_def_id) + .is_none_or(|did| !self.cx.tcx.is_diagnostic_item(sym::pin_macro, did)) + { + return ControlFlow::Break(()); + } + + walk_expr(self, expr) + } + + fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt { + self.cx.tcx + } +} diff --git a/clippy_lints/src/disallowed_macros.rs b/clippy_lints/src/disallowed_macros.rs index 49cd2671dc00..1c9c971730f6 100644 --- a/clippy_lints/src/disallowed_macros.rs +++ b/clippy_lints/src/disallowed_macros.rs @@ -1,4 +1,3 @@ - use clippy_config::Conf; use clippy_config::types::{DisallowedPath, create_disallowed_map}; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; @@ -8,7 +7,8 @@ use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefIdMap; use rustc_hir::{ - AmbigArg, Expr, ExprKind, ForeignItem, HirId, ImplItem, ImplItemImplKind, Item, ItemKind, OwnerId, Pat, Path, Stmt, TraitItem, Ty, + AmbigArg, Expr, ExprKind, ForeignItem, HirId, ImplItem, ImplItemImplKind, Item, ItemKind, OwnerId, Pat, Path, Stmt, + TraitItem, Ty, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::TyCtxt; diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 2da1c2bad117..752f39b4e6dc 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -197,6 +197,18 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx // in a type which is `'static`. // For now ignore all callee types which reference a type parameter. && !generic_args.types().any(|t| matches!(t.kind(), ty::Param(_))) + // Rule out `AsyncFn*`s, because while they can be called as `|x| f(x)`, + // they can't be passed directly into a place expecting an `Fn*` (#13892) + && let Ok((closure_kind, _)) = cx + .tcx + .infer_ctxt() + .build(cx.typing_mode()) + .err_ctxt() + .type_implements_fn_trait( + cx.param_env, + Binder::bind_with_vars(callee_ty_adjusted, List::empty()), + ty::PredicatePolarity::Positive, + ) { span_lint_hir_and_then( cx, @@ -213,19 +225,10 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx // 'cuz currently nothing changes after deleting this check. local_used_in(cx, l, args) || local_used_after_expr(cx, l, expr) }) { - match cx - .tcx - .infer_ctxt() - .build(cx.typing_mode()) - .err_ctxt() - .type_implements_fn_trait( - cx.param_env, - Binder::bind_with_vars(callee_ty_adjusted, List::empty()), - ty::PredicatePolarity::Positive, - ) { + match closure_kind { // Mutable closure is used after current expr; we cannot consume it. - Ok((ClosureKind::FnMut, _)) => snippet = format!("&mut {snippet}"), - Ok((ClosureKind::Fn, _)) if !callee_ty_raw.is_ref() => { + ClosureKind::FnMut => snippet = format!("&mut {snippet}"), + ClosureKind::Fn if !callee_ty_raw.is_ref() => { snippet = format!("&{snippet}"); }, _ => (), 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 906bbd006d46..72f879530405 100644 --- a/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs +++ b/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs @@ -4,7 +4,7 @@ use rustc_middle::ty; use rustc_span::def_id::LocalDefId; use clippy_utils::diagnostics::span_lint; -use clippy_utils::ty::type_is_unsafe_function; +use clippy_utils::ty::is_unsafe_fn; use clippy_utils::visitors::for_each_expr; use clippy_utils::{iter_input_pats, path_to_local}; @@ -51,7 +51,7 @@ fn check_raw_ptr<'tcx>( let typeck = cx.tcx.typeck_body(body.id()); let _: Option = for_each_expr(cx, body.value, |e| { match e.kind { - hir::ExprKind::Call(f, args) if type_is_unsafe_function(cx, typeck.expr_ty(f)) => { + hir::ExprKind::Call(f, args) if is_unsafe_fn(cx, typeck.expr_ty(f)) => { for arg in args { check_arg(cx, &raw_ptrs, arg); } diff --git a/clippy_lints/src/functions/ref_option.rs b/clippy_lints/src/functions/ref_option.rs index 106202d00d40..5dc1b7269b76 100644 --- a/clippy_lints/src/functions/ref_option.rs +++ b/clippy_lints/src/functions/ref_option.rs @@ -1,23 +1,20 @@ use crate::functions::REF_OPTION; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::is_trait_impl_item; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::option_arg_ty; +use clippy_utils::{is_from_proc_macro, is_trait_impl_item}; use rustc_errors::Applicability; -use rustc_hir as hir; use rustc_hir::intravisit::FnKind; -use rustc_hir::{FnDecl, HirId}; -use rustc_lint::LateContext; -use rustc_middle::ty::{self, GenericArgKind, Mutability, Ty}; +use rustc_hir::{self as hir, FnDecl, HirId}; +use rustc_lint::{LateContext, LintContext}; +use rustc_middle::ty::{self, Mutability, Ty}; +use rustc_span::Span; use rustc_span::def_id::LocalDefId; -use rustc_span::{Span, sym}; -fn check_ty<'a>(cx: &LateContext<'a>, param: &rustc_hir::Ty<'a>, param_ty: Ty<'a>, fixes: &mut Vec<(Span, String)>) { - if let ty::Ref(_, opt_ty, Mutability::Not) = param_ty.kind() - && is_type_diagnostic_item(cx, *opt_ty, sym::Option) - && let ty::Adt(_, opt_gen_args) = opt_ty.kind() - && let [gen_arg] = opt_gen_args.as_slice() - && let GenericArgKind::Type(gen_ty) = gen_arg.kind() +fn check_ty<'a>(cx: &LateContext<'a>, param: &hir::Ty<'a>, param_ty: Ty<'a>, fixes: &mut Vec<(Span, String)>) { + if !param.span.in_external_macro(cx.sess().source_map()) + && let ty::Ref(_, opt_ty, Mutability::Not) = param_ty.kind() + && let Some(gen_ty) = option_arg_ty(cx, *opt_ty) && !gen_ty.is_ref() // Need to gen the original spans, so first parsing mid, and hir parsing afterward && let hir::TyKind::Ref(lifetime, hir::MutTy { ty, .. }) = param.kind @@ -27,6 +24,7 @@ fn check_ty<'a>(cx: &LateContext<'a>, param: &rustc_hir::Ty<'a>, param_ty: Ty<'a args: [hir::GenericArg::Type(opt_ty)], .. }) = last.args + && !is_from_proc_macro(cx, param) { let lifetime = snippet(cx, lifetime.ident.span, ".."); fixes.push(( @@ -67,21 +65,24 @@ fn check_fn_sig<'a>(cx: &LateContext<'a>, decl: &FnDecl<'a>, span: Span, sig: ty #[allow(clippy::too_many_arguments)] pub(crate) fn check_fn<'a>( cx: &LateContext<'a>, - kind: FnKind<'_>, + kind: FnKind<'a>, decl: &FnDecl<'a>, span: Span, hir_id: HirId, def_id: LocalDefId, - body: &hir::Body<'_>, + body: &hir::Body<'a>, avoid_breaking_exported_api: bool, ) { if avoid_breaking_exported_api && cx.effective_visibilities.is_exported(def_id) { return; } + if span.in_external_macro(cx.sess().source_map()) { + return; + } if let FnKind::Closure = kind { // Compute the span of the closure parameters + return type if set - let span = if let hir::FnRetTy::Return(out_ty) = &decl.output { + let inputs_output_span = if let hir::FnRetTy::Return(out_ty) = &decl.output { if decl.inputs.is_empty() { out_ty.span } else { @@ -100,9 +101,18 @@ pub(crate) fn check_fn<'a>( }; let sig = args.as_closure().sig().skip_binder(); - check_fn_sig(cx, decl, span, sig); + if is_from_proc_macro(cx, &(&kind, body, hir_id, span)) { + return; + } + + check_fn_sig(cx, decl, inputs_output_span, sig); } else if !is_trait_impl_item(cx, hir_id) { let sig = cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder(); + + if is_from_proc_macro(cx, &(&kind, body, hir_id, span)) { + return; + } + check_fn_sig(cx, decl, span, sig); } } @@ -112,8 +122,10 @@ pub(super) fn check_trait_item<'a>( trait_item: &hir::TraitItem<'a>, avoid_breaking_exported_api: bool, ) { - if let hir::TraitItemKind::Fn(ref sig, _) = trait_item.kind + if !trait_item.span.in_external_macro(cx.sess().source_map()) + && let hir::TraitItemKind::Fn(ref sig, _) = trait_item.kind && !(avoid_breaking_exported_api && cx.effective_visibilities.is_exported(trait_item.owner_id.def_id)) + && !is_from_proc_macro(cx, trait_item) { let def_id = trait_item.owner_id.def_id; let ty_sig = cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder(); diff --git a/clippy_lints/src/future_not_send.rs b/clippy_lints/src/future_not_send.rs index 3ccfa51ab70b..596047977a9b 100644 --- a/clippy_lints/src/future_not_send.rs +++ b/clippy_lints/src/future_not_send.rs @@ -78,66 +78,65 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { if let ty::Alias(ty::Opaque, AliasTy { def_id, args, .. }) = *ret_ty.kind() && 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_self_bounds(def_id) + // If is a Future + && preds + .iter_instantiated_copied(cx.tcx, args) + .filter_map(|(p, _)| p.as_trait_clause()) + .any(|trait_pred| trait_pred.skip_binder().trait_ref.def_id == future_trait) { - 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) + let span = decl.output.span(); + let infcx = cx.tcx.infer_ctxt().build(cx.typing_mode()); + let ocx = ObligationCtxt::new_with_diagnostics(&infcx); + let cause = traits::ObligationCause::misc(span, fn_def_id); + ocx.register_bound(cause, cx.param_env, ret_ty, send_trait); + let send_errors = ocx.select_all_or_error(); + + // Allow errors that try to prove `Send` for types that "mention" a generic parameter at the "top + // level". + // For example, allow errors that `T: Send` can't be proven, but reject `Rc: Send` errors, + // which is always unconditionally `!Send` for any possible type `T`. + // + // We also allow associated type projections if the self type is either itself a projection or a + // type parameter. + // This is to prevent emitting warnings for e.g. holding a `::Output` across await + // points, where `Fut` is a type parameter. + + let is_send = send_errors.iter().all(|err| { + err.obligation + .predicate + .as_trait_clause() + .map(Binder::skip_binder) + .is_some_and(|pred| { + pred.def_id() == send_trait + && pred.self_ty().has_param() + && TyParamAtTopLevelVisitor.visit_ty(pred.self_ty()) == ControlFlow::Break(true) + }) }); - if is_future { - let span = decl.output.span(); - let infcx = cx.tcx.infer_ctxt().build(cx.typing_mode()); - let ocx = ObligationCtxt::new_with_diagnostics(&infcx); - let cause = traits::ObligationCause::misc(span, fn_def_id); - ocx.register_bound(cause, cx.param_env, ret_ty, send_trait); - let send_errors = ocx.select_all_or_error(); - // Allow errors that try to prove `Send` for types that "mention" a generic parameter at the "top - // level". - // For example, allow errors that `T: Send` can't be proven, but reject `Rc: Send` errors, - // which is always unconditionally `!Send` for any possible type `T`. - // - // We also allow associated type projections if the self type is either itself a projection or a - // type parameter. - // This is to prevent emitting warnings for e.g. holding a `::Output` across await - // points, where `Fut` is a type parameter. - - let is_send = send_errors.iter().all(|err| { - err.obligation - .predicate - .as_trait_clause() - .map(Binder::skip_binder) - .is_some_and(|pred| { - pred.def_id() == send_trait - && pred.self_ty().has_param() - && TyParamAtTopLevelVisitor.visit_ty(pred.self_ty()) == ControlFlow::Break(true) - }) - }); - - if !is_send { - span_lint_and_then( - cx, - FUTURE_NOT_SEND, - span, - "future cannot be sent between threads safely", - |db| { - for FulfillmentError { obligation, .. } in send_errors { - infcx - .err_ctxt() - .maybe_note_obligation_cause_for_async_await(db, &obligation); - if let PredicateKind::Clause(ClauseKind::Trait(trait_pred)) = - obligation.predicate.kind().skip_binder() - { - db.note(format!( - "`{}` doesn't implement `{}`", - trait_pred.self_ty(), - trait_pred.trait_ref.print_only_trait_path(), - )); - } + if !is_send { + span_lint_and_then( + cx, + FUTURE_NOT_SEND, + span, + "future cannot be sent between threads safely", + |db| { + for FulfillmentError { obligation, .. } in send_errors { + infcx + .err_ctxt() + .maybe_note_obligation_cause_for_async_await(db, &obligation); + if let PredicateKind::Clause(ClauseKind::Trait(trait_pred)) = + obligation.predicate.kind().skip_binder() + { + db.note(format!( + "`{}` doesn't implement `{}`", + trait_pred.self_ty(), + trait_pred.trait_ref.print_only_trait_path(), + )); } - }, - ); - } + } + }, + ); } } } diff --git a/clippy_lints/src/invalid_upcast_comparisons.rs b/clippy_lints/src/invalid_upcast_comparisons.rs index b0ecc5d52ddb..1666e8e5ae32 100644 --- a/clippy_lints/src/invalid_upcast_comparisons.rs +++ b/clippy_lints/src/invalid_upcast_comparisons.rs @@ -1,3 +1,4 @@ +use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::layout::LayoutOf; @@ -9,7 +10,7 @@ use clippy_utils::comparisons; use clippy_utils::comparisons::Rel; use clippy_utils::consts::{ConstEvalCtxt, FullInt}; use clippy_utils::diagnostics::span_lint; -use clippy_utils::source::snippet; +use clippy_utils::source::snippet_with_context; declare_clippy_lint! { /// ### What it does @@ -69,13 +70,21 @@ fn numeric_cast_precast_bounds(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option< fn err_upcast_comparison(cx: &LateContext<'_>, span: Span, expr: &Expr<'_>, always: bool) { if let ExprKind::Cast(cast_val, _) = expr.kind { + let mut applicability = Applicability::MachineApplicable; + let (cast_val_snip, _) = snippet_with_context( + cx, + cast_val.span, + expr.span.ctxt(), + "the expression", + &mut applicability, + ); span_lint( cx, INVALID_UPCAST_COMPARISONS, span, format!( "because of the numeric bounds on `{}` prior to casting, this expression is always {}", - snippet(cx, cast_val.span, "the expression"), + cast_val_snip, if always { "true" } else { "false" }, ), ); diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index f44a5fdf715e..28a0fbc05115 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -371,9 +371,9 @@ fn parse_len_output<'tcx>(cx: &LateContext<'tcx>, sig: FnSig<'tcx>) -> Option Some(LenOutput::Integral), - ty::Adt(adt, subs) if subs.type_at(0).is_integral() => match cx.tcx.get_diagnostic_name(adt.did()) { - Some(sym::Option) => Some(LenOutput::Option(adt.did())), - Some(sym::Result) => Some(LenOutput::Result(adt.did())), + ty::Adt(adt, subs) => match cx.tcx.get_diagnostic_name(adt.did()) { + Some(sym::Option) => subs.type_at(0).is_integral().then(|| LenOutput::Option(adt.did())), + Some(sym::Result) => subs.type_at(0).is_integral().then(|| LenOutput::Result(adt.did())), _ => None, }, _ => None, diff --git a/clippy_lints/src/let_with_type_underscore.rs b/clippy_lints/src/let_with_type_underscore.rs index 5b0f95ffc377..24a4c321bdab 100644 --- a/clippy_lints/src/let_with_type_underscore.rs +++ b/clippy_lints/src/let_with_type_underscore.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; use clippy_utils::source::{IntoSpan, SpanRangeExt}; +use rustc_ast::{Local, TyKind}; use rustc_errors::Applicability; -use rustc_hir::{LetStmt, TyKind}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_session::declare_lint_pass; declare_clippy_lint! { @@ -26,14 +26,14 @@ declare_clippy_lint! { } 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 '_' +impl EarlyLintPass for UnderscoreTyped { + fn check_local(&mut self, cx: &EarlyContext<'_>, local: &Local) { + if let Some(ty) = &local.ty // Ensure that it has a type defined + && let TyKind::Infer = ty.kind // that type is '_' && local.span.eq_ctxt(ty.span) - && let sm = cx.tcx.sess.source_map() + && let sm = cx.sess().source_map() && !local.span.in_external_macro(sm) - && !is_from_proc_macro(cx, ty) + && !is_from_proc_macro(cx, &**ty) { let span_to_remove = sm .span_extend_to_prev_char_before(ty.span, ':', true) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index a89cf3fdc1ee..c56fa257b068 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -302,7 +302,6 @@ mod permissions_set_readonly_false; mod pointers_in_nomem_asm_block; mod precedence; mod ptr; -mod ptr_offset_with_cast; mod pub_underscore_fields; mod pub_use; mod question_mark; @@ -360,6 +359,7 @@ mod temporary_assignment; mod tests_outside_test_module; mod to_digit_is_some; mod to_string_trait_impl; +mod toplevel_ref_arg; mod trailing_empty_array; mod trait_bounds; mod transmute; @@ -592,7 +592,6 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::new(unwrap::Unwrap)); store.register_late_pass(move |_| Box::new(indexing_slicing::IndexingSlicing::new(conf))); store.register_late_pass(move |tcx| Box::new(non_copy_const::NonCopyConst::new(tcx, conf))); - store.register_late_pass(|_| Box::new(ptr_offset_with_cast::PtrOffsetWithCast)); store.register_late_pass(|_| Box::new(redundant_clone::RedundantClone)); store.register_late_pass(|_| Box::new(slow_vector_initialization::SlowVectorInit)); store.register_late_pass(move |_| Box::new(unnecessary_wraps::UnnecessaryWraps::new(conf))); @@ -744,7 +743,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::new(missing_assert_message::MissingAssertMessage)); store.register_late_pass(|_| Box::new(needless_maybe_sized::NeedlessMaybeSized)); store.register_late_pass(|_| Box::new(redundant_async_block::RedundantAsyncBlock)); - store.register_late_pass(|_| Box::new(let_with_type_underscore::UnderscoreTyped)); + store.register_early_pass(|| Box::new(let_with_type_underscore::UnderscoreTyped)); store.register_late_pass(move |_| Box::new(manual_main_separator_str::ManualMainSeparatorStr::new(conf))); store.register_late_pass(|_| Box::new(unnecessary_struct_initialization::UnnecessaryStruct)); store.register_late_pass(move |_| Box::new(unnecessary_box_returns::UnnecessaryBoxReturns::new(conf))); @@ -831,5 +830,6 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::new(cloned_ref_to_slice_refs::ClonedRefToSliceRefs::new(conf))); store.register_late_pass(|_| Box::new(infallible_try_from::InfallibleTryFrom)); store.register_late_pass(|_| Box::new(coerce_container_to_any::CoerceContainerToAny)); + store.register_late_pass(|_| Box::new(toplevel_ref_arg::ToplevelRefArg)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 149ae5e710c1..d8b186b6787d 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -745,7 +745,7 @@ fn report_elidable_impl_lifetimes<'tcx>( impl_: &'tcx Impl<'_>, map: &FxIndexMap>, ) { - let single_usages = map + let (elidable_lts, usages): (Vec<_>, Vec<_>) = map .iter() .filter_map(|(def_id, usages)| { if let [ @@ -762,14 +762,12 @@ fn report_elidable_impl_lifetimes<'tcx>( None } }) - .collect::>(); + .unzip(); - if single_usages.is_empty() { + if elidable_lts.is_empty() { return; } - let (elidable_lts, usages): (Vec<_>, Vec<_>) = single_usages.into_iter().unzip(); - report_elidable_lifetimes(cx, impl_.generics, &elidable_lts, &usages, true); } @@ -795,9 +793,7 @@ fn report_elidable_lifetimes( // In principle, the result of the call to `Node::ident` could be `unwrap`ped, as `DefId` should refer to a // `Node::GenericParam`. .filter_map(|&def_id| cx.tcx.hir_node_by_def_id(def_id).ident()) - .map(|ident| ident.to_string()) - .collect::>() - .join(", "); + .format(", "); let elidable_usages: Vec = usages .iter() @@ -860,36 +856,89 @@ fn elision_suggestions( .filter(|param| !param.is_elided_lifetime() && !param.is_impl_trait()) .collect::>(); - let mut suggestions = if elidable_lts.len() == explicit_params.len() { + if !elidable_lts + .iter() + .all(|lt| explicit_params.iter().any(|param| param.def_id == *lt)) + { + return None; + } + + let mut suggestions = if elidable_lts.is_empty() { + vec![] + } else if elidable_lts.len() == explicit_params.len() { // if all the params are elided remove the whole generic block // // fn x<'a>() {} // ^^^^ vec![(generics.span, String::new())] } else { - elidable_lts - .iter() - .map(|&id| { - let pos = explicit_params.iter().position(|param| param.def_id == id)?; - let param = explicit_params.get(pos)?; - - let span = if let Some(next) = explicit_params.get(pos + 1) { - // fn x<'prev, 'a, 'next>() {} - // ^^^^ - param.span.until(next.span) + match &explicit_params[..] { + // no params, nothing to elide + [] => unreachable!("handled by `elidable_lts.is_empty()`"), + [param] => { + if elidable_lts.contains(¶m.def_id) { + unreachable!("handled by `elidable_lts.len() == explicit_params.len()`") } else { - // `pos` should be at least 1 here, because the param in position 0 would either have a `next` - // param or would have taken the `elidable_lts.len() == explicit_params.len()` branch. - let prev = explicit_params.get(pos - 1)?; + unreachable!("handled by `elidable_lts.is_empty()`") + } + }, + [_, _, ..] => { + // Given a list like `<'a, 'b, 'c, 'd, ..>`, + // + // If there is a cluster of elidable lifetimes at the beginning, say `'a` and `'b`, we should + // suggest removing them _and_ the trailing comma. The span for that is `a.span.until(c.span)`: + // <'a, 'b, 'c, 'd, ..> => <'a, 'b, 'c, 'd, ..> + // ^^ ^^ ^^^^^^^^ + // + // And since we know that `'c` isn't elidable--otherwise it would've been in the cluster--we can go + // over all the lifetimes after it, and for each elidable one, add a suggestion spanning the + // lifetime itself and the comma before, because each individual suggestion is guaranteed to leave + // the list valid: + // <.., 'c, 'd, 'e, 'f, 'g, ..> => <.., 'c, 'd, 'e, 'f, 'g, ..> + // ^^ ^^ ^^ ^^^^ ^^^^^^^^ + // + // In case there is no such starting cluster, we only need to do the second part of the algorithm: + // <'a, 'b, 'c, 'd, 'e, 'f, 'g, ..> => <'a, 'b , 'c, 'd, 'e, 'f, 'g, ..> + // ^^ ^^ ^^ ^^ ^^^^^^^^^ ^^^^^^^^ - // fn x<'prev, 'a>() {} - // ^^^^ - param.span.with_lo(prev.span.hi()) + // Split off the starting cluster + // TODO: use `slice::split_once` once stabilized (github.com/rust-lang/rust/issues/112811): + // ``` + // let Some(split) = explicit_params.split_once(|param| !elidable_lts.contains(¶m.def_id)) else { + // // there were no lifetime param that couldn't be elided + // unreachable!("handled by `elidable_lts.len() == explicit_params.len()`") + // }; + // match split { /* .. */ } + // ``` + let Some(split_pos) = explicit_params + .iter() + .position(|param| !elidable_lts.contains(¶m.def_id)) + else { + // there were no lifetime param that couldn't be elided + unreachable!("handled by `elidable_lts.len() == explicit_params.len()`") }; + let split = explicit_params + .split_at_checked(split_pos) + .expect("got `split_pos` from `position` on the same Vec"); - Some((span, String::new())) - }) - .collect::>>()? + match split { + ([..], []) => unreachable!("handled by `elidable_lts.len() == explicit_params.len()`"), + ([], [_]) => unreachable!("handled by `explicit_params.len() == 1`"), + (cluster, rest @ [rest_first, ..]) => { + // the span for the cluster + (cluster.first().map(|fw| fw.span.until(rest_first.span)).into_iter()) + // the span for the remaining lifetimes (calculations independent of the cluster) + .chain( + rest.array_windows() + .filter(|[_, curr]| elidable_lts.contains(&curr.def_id)) + .map(|[prev, curr]| curr.span.with_lo(prev.span.hi())), + ) + .map(|sp| (sp, String::new())) + .collect() + }, + } + }, + } }; suggestions.extend(usages.iter().map(|&usage| { diff --git a/clippy_lints/src/loops/explicit_iter_loop.rs b/clippy_lints/src/loops/explicit_iter_loop.rs index 6ce7f0b1f0bc..af475c40586f 100644 --- a/clippy_lints/src/loops/explicit_iter_loop.rs +++ b/clippy_lints/src/loops/explicit_iter_loop.rs @@ -138,9 +138,9 @@ fn is_ref_iterable<'tcx>( return Some((AdjustKind::None, self_ty)); } - let res_ty = cx - .tcx - .erase_and_anonymize_regions(EarlyBinder::bind(req_res_ty).instantiate(cx.tcx, typeck.node_args(call_expr.hir_id))); + let res_ty = cx.tcx.erase_and_anonymize_regions( + EarlyBinder::bind(req_res_ty).instantiate(cx.tcx, typeck.node_args(call_expr.hir_id)), + ); let mutbl = if let ty::Ref(_, _, mutbl) = *req_self_ty.kind() { Some(mutbl) } else { diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index 2ccff7680976..544c3c34d029 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -22,7 +22,10 @@ pub(super) fn check<'tcx>( for_loop: Option<&ForLoop<'_>>, ) { match never_loop_block(cx, block, &mut Vec::new(), loop_id) { - NeverLoopResult::Diverging { ref break_spans } => { + NeverLoopResult::Diverging { + ref break_spans, + ref never_spans, + } => { span_lint_and_then(cx, NEVER_LOOP, span, "this loop never actually loops", |diag| { if let Some(ForLoop { arg: iterator, @@ -34,12 +37,16 @@ pub(super) fn check<'tcx>( { // If the block contains a break or continue, or if the loop has a label, `MachineApplicable` is not // appropriate. - let app = if !contains_any_break_or_continue(block) && label.is_none() { + let mut app = if !contains_any_break_or_continue(block) && label.is_none() { Applicability::MachineApplicable } else { Applicability::Unspecified }; + if !never_spans.is_empty() { + app = Applicability::HasPlaceholders; + } + let mut suggestions = vec![( for_span.with_hi(iterator.span.hi()), for_to_if_let_sugg(cx, iterator, pat), @@ -51,6 +58,13 @@ pub(super) fn check<'tcx>( suggestions, app, ); + + for span in never_spans { + diag.span_help( + *span, + "this code is unreachable. Consider moving the reachable parts out", + ); + } } }); }, @@ -77,13 +91,16 @@ fn contains_any_break_or_continue(block: &Block<'_>) -> bool { /// The first two bits of information are in this enum, and the last part is in the /// `local_labels` variable, which contains a list of `(block_id, reachable)` pairs ordered by /// scope. -#[derive(Clone)] +#[derive(Clone, Debug)] enum NeverLoopResult { /// A continue may occur for the main loop. MayContinueMainLoop, /// We have not encountered any main loop continue, /// but we are diverging (subsequent control flow is not reachable) - Diverging { break_spans: Vec }, + Diverging { + break_spans: Vec, + never_spans: Vec, + }, /// We have not encountered any main loop continue, /// and subsequent control flow is (possibly) reachable Normal, @@ -128,14 +145,18 @@ fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult) -> NeverLoopResult ( NeverLoopResult::Diverging { break_spans: mut break_spans1, + never_spans: mut never_spans1, }, NeverLoopResult::Diverging { break_spans: mut break_spans2, + never_spans: mut never_spans2, }, ) => { break_spans1.append(&mut break_spans2); + never_spans1.append(&mut never_spans2); NeverLoopResult::Diverging { break_spans: break_spans1, + never_spans: never_spans1, } }, } @@ -207,6 +228,8 @@ fn all_spans_after_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> Vec { } return vec![stmt.span]; + } else if let Node::Block(_) = cx.tcx.parent_hir_node(expr.hir_id) { + return vec![expr.span]; } vec![] @@ -270,10 +293,13 @@ fn never_loop_expr<'tcx>( ExprKind::Match(e, arms, _) => { let e = never_loop_expr(cx, e, local_labels, main_loop_id); combine_seq(e, || { - arms.iter() - .fold(NeverLoopResult::Diverging { break_spans: vec![] }, |a, b| { - combine_branches(a, never_loop_expr(cx, b.body, local_labels, main_loop_id)) - }) + arms.iter().fold( + NeverLoopResult::Diverging { + break_spans: vec![], + never_spans: vec![], + }, + |a, b| combine_branches(a, never_loop_expr(cx, b.body, local_labels, main_loop_id)), + ) }) }, ExprKind::Block(b, _) => { @@ -296,6 +322,7 @@ fn never_loop_expr<'tcx>( } else { NeverLoopResult::Diverging { break_spans: all_spans_after_expr(cx, expr), + never_spans: vec![], } } }, @@ -306,7 +333,10 @@ fn never_loop_expr<'tcx>( combine_seq(first, || { // checks if break targets a block instead of a loop mark_block_as_reachable(expr, local_labels); - NeverLoopResult::Diverging { break_spans: vec![] } + NeverLoopResult::Diverging { + break_spans: vec![], + never_spans: vec![], + } }) }, ExprKind::Break(dest, e) => { @@ -322,11 +352,15 @@ fn never_loop_expr<'tcx>( } else { all_spans_after_expr(cx, expr) }, + never_spans: vec![], } }) }, ExprKind::Become(e) => combine_seq(never_loop_expr(cx, e, local_labels, main_loop_id), || { - NeverLoopResult::Diverging { break_spans: vec![] } + NeverLoopResult::Diverging { + break_spans: vec![], + never_spans: vec![], + } }), ExprKind::InlineAsm(asm) => combine_seq_many(asm.operands.iter().map(|(o, _)| match o { InlineAsmOperand::In { expr, .. } | InlineAsmOperand::InOut { expr, .. } => { @@ -356,7 +390,10 @@ fn never_loop_expr<'tcx>( }; let result = combine_seq(result, || { if cx.typeck_results().expr_ty(expr).is_never() { - NeverLoopResult::Diverging { break_spans: vec![] } + NeverLoopResult::Diverging { + break_spans: vec![], + never_spans: all_spans_after_expr(cx, expr), + } } else { NeverLoopResult::Normal } diff --git a/clippy_lints/src/manual_abs_diff.rs b/clippy_lints/src/manual_abs_diff.rs index 288f27db8ca2..5814b6815a1e 100644 --- a/clippy_lints/src/manual_abs_diff.rs +++ b/clippy_lints/src/manual_abs_diff.rs @@ -4,8 +4,8 @@ use clippy_utils::higher::If; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::HasSession as _; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{eq_expr_value, peel_blocks, peel_middle_ty_refs, span_contains_comment}; +use clippy_utils::ty::{is_type_diagnostic_item, peel_and_count_ty_refs}; +use clippy_utils::{eq_expr_value, peel_blocks, span_contains_comment}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -107,7 +107,7 @@ impl ManualAbsDiff { |ty| is_type_diagnostic_item(cx, ty, sym::Duration) && self.msrv.meets(cx, msrvs::DURATION_ABS_DIFF); let a_ty = cx.typeck_results().expr_ty(a).peel_refs(); - let (b_ty, b_n_refs) = peel_middle_ty_refs(cx.typeck_results().expr_ty(b)); + let (b_ty, b_n_refs, _) = peel_and_count_ty_refs(cx.typeck_results().expr_ty(b)); (a_ty == b_ty && (is_int(a_ty) || is_duration(a_ty))).then_some((a_ty, b_n_refs)) } diff --git a/clippy_lints/src/manual_option_as_slice.rs b/clippy_lints/src/manual_option_as_slice.rs index 922db174e3d4..b036e78cdedc 100644 --- a/clippy_lints/src/manual_option_as_slice.rs +++ b/clippy_lints/src/manual_option_as_slice.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::msrvs::Msrv; -use clippy_utils::{is_none_arm, msrvs, peel_hir_expr_refs, sym}; +use clippy_utils::{is_none_pattern, msrvs, peel_hir_expr_refs, sym}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Arm, Expr, ExprKind, LangItem, Pat, PatKind, QPath, is_range_literal}; @@ -60,8 +60,8 @@ impl LateLintPass<'_> for ManualOptionAsSlice { } match expr.kind { ExprKind::Match(scrutinee, [arm1, arm2], _) => { - if is_none_arm(cx, arm2) && check_arms(cx, arm2, arm1) - || is_none_arm(cx, arm1) && check_arms(cx, arm1, arm2) + if is_none_pattern(cx, arm2.pat) && check_arms(cx, arm2, arm1) + || is_none_pattern(cx, arm1.pat) && check_arms(cx, arm1, arm2) { check_as_ref(cx, scrutinee, span, self.msrv); } diff --git a/clippy_lints/src/manual_slice_size_calculation.rs b/clippy_lints/src/manual_slice_size_calculation.rs index 0c09a47c9651..de12fa29d02c 100644 --- a/clippy_lints/src/manual_slice_size_calculation.rs +++ b/clippy_lints/src/manual_slice_size_calculation.rs @@ -2,6 +2,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_context; +use clippy_utils::ty::peel_and_count_ty_refs; use clippy_utils::{expr_or_init, is_in_const_context, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; @@ -102,7 +103,7 @@ fn simplify_half<'tcx>( && let ExprKind::MethodCall(method_path, receiver, [], _) = expr1.kind && method_path.ident.name == sym::len && let receiver_ty = cx.typeck_results().expr_ty(receiver) - && let (receiver_ty, refs_count) = clippy_utils::ty::walk_ptrs_ty_depth(receiver_ty) + && let (receiver_ty, refs_count, _) = peel_and_count_ty_refs(receiver_ty) && let ty::Slice(ty1) = receiver_ty.kind() // expr2 is `size_of::()`? && let ExprKind::Call(func, []) = expr2.kind diff --git a/clippy_lints/src/matches/manual_ok_err.rs b/clippy_lints/src/matches/manual_ok_err.rs index edbb556fd976..a8490d6aa7d8 100644 --- a/clippy_lints/src/matches/manual_ok_err.rs +++ b/clippy_lints/src/matches/manual_ok_err.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, reindent_multiline}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{option_arg_ty, peel_mid_ty_refs_is_mutable}; +use clippy_utils::ty::{option_arg_ty, peel_and_count_ty_refs}; use clippy_utils::{get_parent_expr, is_res_lang_ctor, path_res, peel_blocks, span_contains_comment}; use rustc_ast::{BindingMode, Mutability}; use rustc_errors::Applicability; @@ -135,15 +135,11 @@ fn apply_lint(cx: &LateContext<'_>, expr: &Expr<'_>, scrutinee: &Expr<'_>, is_ok let scrut = Sugg::hir_with_applicability(cx, scrutinee, "..", &mut app).maybe_paren(); let scrutinee_ty = cx.typeck_results().expr_ty(scrutinee); - let (_, n_ref, mutability) = peel_mid_ty_refs_is_mutable(scrutinee_ty); - let prefix = if n_ref > 0 { - if mutability == Mutability::Mut { - ".as_mut()" - } else { - ".as_ref()" - } - } else { - "" + let (_, _, mutability) = peel_and_count_ty_refs(scrutinee_ty); + let prefix = match mutability { + Some(Mutability::Mut) => ".as_mut()", + Some(Mutability::Not) => ".as_ref()", + None => "", }; let sugg = format!("{scrut}{prefix}.{method}()"); diff --git a/clippy_lints/src/matches/manual_utils.rs b/clippy_lints/src/matches/manual_utils.rs index dbae71bbb1b0..d4bfdb7e440d 100644 --- a/clippy_lints/src/matches/manual_utils.rs +++ b/clippy_lints/src/matches/manual_utils.rs @@ -2,7 +2,7 @@ use crate::map_unit_fn::OPTION_MAP_UNIT_FN; use crate::matches::MATCH_AS_REF; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{is_copy, is_type_diagnostic_item, peel_mid_ty_refs_is_mutable, type_is_unsafe_function}; +use clippy_utils::ty::{is_copy, is_type_diagnostic_item, is_unsafe_fn, peel_and_count_ty_refs}; use clippy_utils::{ CaptureKind, can_move_expr_to_closure, expr_requires_coercion, is_else_clause, is_lint_allowed, is_res_lang_ctor, path_res, path_to_local_id, peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, @@ -30,8 +30,9 @@ pub(super) fn check_with<'tcx, F>( where F: Fn(&LateContext<'tcx>, &'tcx Pat<'_>, &'tcx Expr<'_>, SyntaxContext) -> Option>, { - let (scrutinee_ty, ty_ref_count, ty_mutability) = - peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(scrutinee)); + let (scrutinee_ty, ty_ref_count, ty_mutability) = peel_and_count_ty_refs(cx.typeck_results().expr_ty(scrutinee)); + let ty_mutability = ty_mutability.unwrap_or(Mutability::Mut); + if !(is_type_diagnostic_item(cx, scrutinee_ty, sym::Option) && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Option)) { @@ -191,7 +192,7 @@ fn can_pass_as_func<'tcx>(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Ex ExprKind::Call(func, [arg]) if path_to_local_id(arg, binding) && cx.typeck_results().expr_adjustments(arg).is_empty() - && !type_is_unsafe_function(cx, cx.typeck_results().expr_ty(func).peel_refs()) => + && !is_unsafe_fn(cx, cx.typeck_results().expr_ty(func).peel_refs()) => { Some(func) }, diff --git a/clippy_lints/src/matches/needless_match.rs b/clippy_lints/src/matches/needless_match.rs index b04db03f8d2e..3a2097c3df26 100644 --- a/clippy_lints/src/matches/needless_match.rs +++ b/clippy_lints/src/matches/needless_match.rs @@ -1,7 +1,7 @@ use super::NEEDLESS_MATCH; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::{is_type_diagnostic_item, same_type_and_consts}; +use clippy_utils::ty::{is_type_diagnostic_item, same_type_modulo_regions}; use clippy_utils::{ SpanlessEq, eq_expr_value, get_parent_expr_for_hir, higher, is_else_clause, is_res_lang_ctor, over, path_res, peel_blocks_with_stmt, @@ -122,7 +122,7 @@ fn expr_ty_matches_p_ty(cx: &LateContext<'_>, expr: &Expr<'_>, p_expr: &Expr<'_> // Compare match_expr ty with local in `let local = match match_expr {..}` Node::LetStmt(local) => { let results = cx.typeck_results(); - return same_type_and_consts(results.node_type(local.hir_id), results.expr_ty(expr)); + return same_type_modulo_regions(results.node_type(local.hir_id), results.expr_ty(expr)); }, // compare match_expr ty with RetTy in `fn foo() -> RetTy` Node::Item(item) => { @@ -133,7 +133,7 @@ fn expr_ty_matches_p_ty(cx: &LateContext<'_>, expr: &Expr<'_>, p_expr: &Expr<'_> .instantiate_identity() .output() .skip_binder(); - return same_type_and_consts(output, cx.typeck_results().expr_ty(expr)); + return same_type_modulo_regions(output, cx.typeck_results().expr_ty(expr)); } }, // check the parent expr for this whole block `{ match match_expr {..} }` diff --git a/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs b/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs index ae09c2e87d6b..1130d82ab78f 100644 --- a/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs +++ b/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs @@ -7,7 +7,7 @@ use super::REST_PAT_IN_FULLY_BOUND_STRUCTS; pub(crate) fn check(cx: &LateContext<'_>, pat: &Pat<'_>) { if !pat.span.from_expansion() - && let PatKind::Struct(QPath::Resolved(_, path), fields, Some(_)) = pat.kind + && let PatKind::Struct(QPath::Resolved(_, path), fields, Some(dotdot)) = pat.kind && let Some(def_id) = path.res.opt_def_id() && let ty = cx.tcx.type_of(def_id).instantiate_identity() && let ty::Adt(def, _) = ty.kind() @@ -15,14 +15,18 @@ pub(crate) fn check(cx: &LateContext<'_>, pat: &Pat<'_>) { && fields.len() == def.non_enum_variant().fields.len() && !def.non_enum_variant().is_field_list_non_exhaustive() { - #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] span_lint_and_then( cx, REST_PAT_IN_FULLY_BOUND_STRUCTS, pat.span, "unnecessary use of `..` pattern in struct binding. All fields were already bound", |diag| { - diag.help("consider removing `..` from this binding"); + diag.span_suggestion_verbose( + dotdot, + "consider removing `..` from this binding", + "", + rustc_errors::Applicability::MachineApplicable, + ); }, ); } diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index bcf079b70070..83939d325794 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -2,10 +2,8 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{ SpanRangeExt, expr_block, snippet, snippet_block_with_context, snippet_with_applicability, snippet_with_context, }; -use clippy_utils::ty::implements_trait; -use clippy_utils::{ - is_lint_allowed, is_unit_expr, peel_blocks, peel_hir_pat_refs, peel_middle_ty_refs, peel_n_hir_expr_refs, -}; +use clippy_utils::ty::{implements_trait, peel_and_count_ty_refs}; +use clippy_utils::{is_lint_allowed, is_unit_expr, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs}; use core::ops::ControlFlow; use rustc_arena::DroplessArena; use rustc_errors::{Applicability, Diag}; @@ -133,7 +131,7 @@ fn report_single_pattern( let (pat, pat_ref_count) = peel_hir_pat_refs(arm.pat); 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 (ty, ty_ref_count, _) = peel_and_count_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() && (ty.is_integral() diff --git a/clippy_lints/src/methods/implicit_clone.rs b/clippy_lints/src/methods/implicit_clone.rs index 8a976d1b4dc0..0ba84919395c 100644 --- a/clippy_lints/src/methods/implicit_clone.rs +++ b/clippy_lints/src/methods/implicit_clone.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; -use clippy_utils::ty::implements_trait; -use clippy_utils::{is_diag_item_method, is_diag_trait_item, peel_middle_ty_refs, sym}; +use clippy_utils::ty::{implements_trait, peel_and_count_ty_refs}; +use clippy_utils::{is_diag_item_method, is_diag_trait_item, sym}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -14,7 +14,7 @@ pub fn check(cx: &LateContext<'_>, method_name: Symbol, expr: &hir::Expr<'_>, re && is_clone_like(cx, method_name, method_def_id) && let return_type = cx.typeck_results().expr_ty(expr) && let input_type = cx.typeck_results().expr_ty(recv) - && let (input_type, ref_count) = peel_middle_ty_refs(input_type) + && let (input_type, ref_count, _) = peel_and_count_ty_refs(input_type) && !(ref_count > 0 && is_diag_trait_item(cx, method_def_id, sym::ToOwned)) && let Some(ty_name) = input_type.ty_adt_def().map(|adt_def| cx.tcx.item_name(adt_def.did())) && return_type == input_type diff --git a/clippy_lints/src/methods/inefficient_to_string.rs b/clippy_lints/src/methods/inefficient_to_string.rs index 4ed7de81ea3d..47195fdd65f5 100644 --- a/clippy_lints/src/methods/inefficient_to_string.rs +++ b/clippy_lints/src/methods/inefficient_to_string.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::{is_type_lang_item, walk_ptrs_ty_depth}; +use clippy_utils::ty::{is_type_lang_item, peel_and_count_ty_refs}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -24,7 +24,7 @@ pub fn check( && let Some(args) = cx.typeck_results().node_args_opt(expr.hir_id) && let arg_ty = cx.typeck_results().expr_ty_adjusted(receiver) && let self_ty = args.type_at(0) - && let (deref_self_ty, deref_count) = walk_ptrs_ty_depth(self_ty) + && let (deref_self_ty, deref_count, _) = peel_and_count_ty_refs(self_ty) && deref_count >= 1 && specializes_tostring(cx, deref_self_ty) { diff --git a/clippy_lints/src/methods/iter_overeager_cloned.rs b/clippy_lints/src/methods/iter_overeager_cloned.rs index f851ebe91f37..d43dc23a86b2 100644 --- a/clippy_lints/src/methods/iter_overeager_cloned.rs +++ b/clippy_lints/src/methods/iter_overeager_cloned.rs @@ -10,8 +10,7 @@ use rustc_middle::mir::{FakeReadCause, Mutability}; use rustc_middle::ty::{self, BorrowKind}; use rustc_span::{Symbol, sym}; -use super::ITER_OVEREAGER_CLONED; -use crate::redundant_clone::REDUNDANT_CLONE; +use super::{ITER_OVEREAGER_CLONED, REDUNDANT_ITER_CLONED}; #[derive(Clone, Copy)] pub(super) enum Op<'a> { @@ -96,7 +95,7 @@ pub(super) fn check<'tcx>( } let (lint, msg, trailing_clone) = match op { - Op::RmCloned | Op::NeedlessMove(_) => (REDUNDANT_CLONE, "unneeded cloning of iterator items", ""), + Op::RmCloned | Op::NeedlessMove(_) => (REDUNDANT_ITER_CLONED, "unneeded cloning of iterator items", ""), Op::LaterCloned | Op::FixClosure(_, _) => ( ITER_OVEREAGER_CLONED, "unnecessarily eager cloning of iterator items", diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 49ca81dafc28..8679689c8ad4 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -91,6 +91,7 @@ mod or_fun_call; mod or_then_unwrap; mod path_buf_push_overwrite; mod path_ends_with_ext; +mod ptr_offset_with_cast; mod range_zip_with_len; mod read_line_without_trim; mod readonly_write_lock; @@ -1725,6 +1726,43 @@ declare_clippy_lint! { "Check for offset calculations on raw pointers to zero-sized types" } +declare_clippy_lint! { + /// ### What it does + /// Checks for usage of the `offset` pointer method with a `usize` casted to an + /// `isize`. + /// + /// ### Why is this bad? + /// If we’re always increasing the pointer address, we can avoid the numeric + /// cast by using the `add` method instead. + /// + /// ### Example + /// ```no_run + /// let vec = vec![b'a', b'b', b'c']; + /// let ptr = vec.as_ptr(); + /// let offset = 1_usize; + /// + /// unsafe { + /// ptr.offset(offset as isize); + /// } + /// ``` + /// + /// Could be written: + /// + /// ```no_run + /// let vec = vec![b'a', b'b', b'c']; + /// let ptr = vec.as_ptr(); + /// let offset = 1_usize; + /// + /// unsafe { + /// ptr.add(offset); + /// } + /// ``` + #[clippy::version = "1.30.0"] + pub PTR_OFFSET_WITH_CAST, + complexity, + "unneeded pointer offset cast" +} + declare_clippy_lint! { /// ### What it does /// Checks for `FileType::is_file()`. @@ -4576,6 +4614,31 @@ declare_clippy_lint! { "hardcoded localhost IP address" } +declare_clippy_lint! { + /// ### What it does + /// Checks for calls to `Iterator::cloned` where the original value could be used + /// instead. + /// + /// ### Why is this bad? + /// It is not always possible for the compiler to eliminate useless allocations and + /// deallocations generated by redundant `clone()`s. + /// + /// ### Example + /// ```no_run + /// let x = vec![String::new()]; + /// let _ = x.iter().cloned().map(|x| x.len()); + /// ``` + /// Use instead: + /// ```no_run + /// let x = vec![String::new()]; + /// let _ = x.iter().map(|x| x.len()); + /// ``` + #[clippy::version = "1.90.0"] + pub REDUNDANT_ITER_CLONED, + perf, + "detects redundant calls to `Iterator::cloned`" +} + #[expect(clippy::struct_excessive_bools)] pub struct Methods { avoid_breaking_exported_api: bool, @@ -4665,6 +4728,7 @@ impl_lint_pass!(Methods => [ UNINIT_ASSUMED_INIT, MANUAL_SATURATING_ARITHMETIC, ZST_OFFSET, + PTR_OFFSET_WITH_CAST, FILETYPE_IS_FILE, OPTION_AS_REF_DEREF, UNNECESSARY_LAZY_EVALUATIONS, @@ -4755,6 +4819,7 @@ impl_lint_pass!(Methods => [ IO_OTHER_ERROR, SWAP_WITH_TEMPORARY, IP_CONSTANT, + REDUNDANT_ITER_CLONED, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -4960,10 +5025,7 @@ impl Methods { // Handle method calls whose receiver and arguments may not come from expansion if let Some((name, recv, args, span, call_span)) = method_call(expr) { match (name, args) { - ( - sym::add | sym::offset | sym::sub | sym::wrapping_offset | sym::wrapping_add | sym::wrapping_sub, - [_arg], - ) => { + (sym::add | sym::sub | sym::wrapping_add | sym::wrapping_sub, [_arg]) => { zst_offset::check(cx, expr, recv); }, (sym::all, [arg]) => { @@ -5334,6 +5396,11 @@ impl Methods { }, _ => iter_nth_zero::check(cx, expr, recv, n_arg), }, + (sym::offset | sym::wrapping_offset, [arg]) => { + zst_offset::check(cx, expr, recv); + + ptr_offset_with_cast::check(cx, name, expr, recv, arg, self.msrv); + }, (sym::ok_or_else, [arg]) => { unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"); }, diff --git a/clippy_lints/src/methods/mut_mutex_lock.rs b/clippy_lints/src/methods/mut_mutex_lock.rs index 4235af882b0c..9d2c5e6232d6 100644 --- a/clippy_lints/src/methods/mut_mutex_lock.rs +++ b/clippy_lints/src/methods/mut_mutex_lock.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::expr_custom_deref_adjustment; -use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable}; +use clippy_utils::ty::{is_type_diagnostic_item, peel_and_count_ty_refs}; use rustc_errors::Applicability; use rustc_hir::{Expr, Mutability}; use rustc_lint::LateContext; @@ -10,8 +10,7 @@ use super::MUT_MUTEX_LOCK; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>, recv: &'tcx Expr<'tcx>, name_span: Span) { if matches!(expr_custom_deref_adjustment(cx, recv), None | Some(Mutability::Mut)) - && let (_, ref_depth, Mutability::Mut) = peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(recv)) - && ref_depth >= 1 + && let (_, _, Some(Mutability::Mut)) = peel_and_count_ty_refs(cx.typeck_results().expr_ty(recv)) && let Some(method_id) = cx.typeck_results().type_dependent_def_id(ex.hir_id) && let Some(impl_id) = cx.tcx.impl_of_assoc(method_id) && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::Mutex) diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index 04f0e3c0479e..71b2f251eded 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -2,6 +2,7 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::eager_or_lazy::switch_to_lazy_eval; +use clippy_utils::higher::VecArgs; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{expr_type_is_certain, implements_trait, is_type_diagnostic_item}; use clippy_utils::visitors::for_each_expr; @@ -97,6 +98,12 @@ pub(super) fn check<'tcx>( return false; } + // `.unwrap_or(vec![])` is as readable as `.unwrap_or_default()`. And if the expression is a + // non-empty `Vec`, then it will not be a default value anyway. Bail out in all cases. + if call_expr.and_then(|call_expr| VecArgs::hir(cx, call_expr)).is_some() { + return false; + } + // needs to target Default::default in particular or be *::new and have a Default impl // available if (is_new(fun) && output_type_implements_default(fun)) diff --git a/clippy_lints/src/methods/ptr_offset_with_cast.rs b/clippy_lints/src/methods/ptr_offset_with_cast.rs new file mode 100644 index 000000000000..d19d3b8eb89d --- /dev/null +++ b/clippy_lints/src/methods/ptr_offset_with_cast.rs @@ -0,0 +1,82 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::sym; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_span::Symbol; +use std::fmt; + +use super::PTR_OFFSET_WITH_CAST; + +pub(super) fn check( + cx: &LateContext<'_>, + method: Symbol, + expr: &Expr<'_>, + recv: &Expr<'_>, + arg: &Expr<'_>, + msrv: Msrv, +) { + // `pointer::add` and `pointer::wrapping_add` are only stable since 1.26.0. These functions + // became const-stable in 1.61.0, the same version that `pointer::offset` became const-stable. + if !msrv.meets(cx, msrvs::POINTER_ADD_SUB_METHODS) { + return; + } + + let method = match method { + sym::offset => Method::Offset, + sym::wrapping_offset => Method::WrappingOffset, + _ => return, + }; + + if !cx.typeck_results().expr_ty_adjusted(recv).is_raw_ptr() { + return; + } + + // Check if the argument to the method call is a cast from usize. + let cast_lhs_expr = match arg.kind { + ExprKind::Cast(lhs, _) if cx.typeck_results().expr_ty(lhs).is_usize() => lhs, + _ => return, + }; + + let ExprKind::MethodCall(method_name, _, _, _) = expr.kind else { + return; + }; + + let msg = format!("use of `{method}` with a `usize` casted to an `isize`"); + span_lint_and_then(cx, PTR_OFFSET_WITH_CAST, expr.span, msg, |diag| { + diag.multipart_suggestion( + format!("use `{}` instead", method.suggestion()), + vec![ + (method_name.ident.span, method.suggestion().to_string()), + (arg.span.with_lo(cast_lhs_expr.span.hi()), String::new()), + ], + Applicability::MachineApplicable, + ); + }); +} + +#[derive(Copy, Clone)] +enum Method { + Offset, + WrappingOffset, +} + +impl Method { + #[must_use] + fn suggestion(self) -> &'static str { + match self { + Self::Offset => "add", + Self::WrappingOffset => "wrapping_add", + } + } +} + +impl fmt::Display for Method { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Offset => write!(f, "offset"), + Self::WrappingOffset => write!(f, "wrapping_offset"), + } + } +} diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index c1f4904af7c4..640931a82899 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -3,11 +3,12 @@ use super::unnecessary_iter_cloned::{self, is_into_iter}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{SpanRangeExt, snippet}; -use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, is_type_diagnostic_item, is_type_lang_item}; +use clippy_utils::ty::{ + get_iterator_item_ty, implements_trait, is_copy, is_type_diagnostic_item, is_type_lang_item, peel_and_count_ty_refs, +}; use clippy_utils::visitors::find_all_ret_expressions; use clippy_utils::{ - fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, is_expr_temporary_value, peel_middle_ty_refs, - return_ty, sym, + fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, is_expr_temporary_value, return_ty, sym, }; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -119,8 +120,8 @@ fn check_addr_of_expr( }, ] = adjustments[..] && let receiver_ty = cx.typeck_results().expr_ty(receiver) - && let (target_ty, n_target_refs) = peel_middle_ty_refs(*target_ty) - && let (receiver_ty, n_receiver_refs) = peel_middle_ty_refs(receiver_ty) + && let (target_ty, n_target_refs, _) = peel_and_count_ty_refs(*target_ty) + && let (receiver_ty, n_receiver_refs, _) = peel_and_count_ty_refs(receiver_ty) // Only flag cases satisfying at least one of the following three conditions: // * the referent and receiver types are distinct // * the referent/receiver type is a copyable array @@ -385,7 +386,7 @@ fn check_other_call_arg<'tcx>( && let fn_sig = cx.tcx.fn_sig(callee_def_id).instantiate_identity().skip_binder() && let Some(i) = recv.into_iter().chain(call_args).position(|arg| arg.hir_id == maybe_arg.hir_id) && let Some(input) = fn_sig.inputs().get(i) - && let (input, n_refs) = peel_middle_ty_refs(*input) + && let (input, n_refs, _) = peel_and_count_ty_refs(*input) && let (trait_predicates, _) = get_input_traits_and_projections(cx, callee_def_id, input) && let Some(sized_def_id) = cx.tcx.lang_items().sized_trait() && let Some(meta_sized_def_id) = cx.tcx.lang_items().meta_sized_trait() diff --git a/clippy_lints/src/methods/useless_asref.rs b/clippy_lints/src/methods/useless_asref.rs index 38fad239f679..e56f4b80d017 100644 --- a/clippy_lints/src/methods/useless_asref.rs +++ b/clippy_lints/src/methods/useless_asref.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::{implements_trait, should_call_clone_as_function, walk_ptrs_ty_depth}; +use clippy_utils::ty::{implements_trait, peel_and_count_ty_refs, should_call_clone_as_function}; use clippy_utils::{get_parent_expr, is_diag_trait_item, path_to_local_id, peel_blocks, strip_pat_refs}; use rustc_errors::Applicability; use rustc_hir::{self as hir, LangItem}; @@ -50,8 +50,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: Symbo // check if the type after `as_ref` or `as_mut` is the same as before let rcv_ty = cx.typeck_results().expr_ty(recvr); let res_ty = cx.typeck_results().expr_ty(expr); - let (base_res_ty, res_depth) = walk_ptrs_ty_depth(res_ty); - let (base_rcv_ty, rcv_depth) = walk_ptrs_ty_depth(rcv_ty); + let (base_res_ty, res_depth, _) = peel_and_count_ty_refs(res_ty); + let (base_rcv_ty, rcv_depth, _) = peel_and_count_ty_refs(rcv_ty); if base_rcv_ty == base_res_ty && rcv_depth >= res_depth { if let Some(parent) = get_parent_expr(cx, expr) { // allow the `as_ref` or `as_mut` if it is followed by another method call diff --git a/clippy_lints/src/min_ident_chars.rs b/clippy_lints/src/min_ident_chars.rs index 6258e408217f..1ceee836732a 100644 --- a/clippy_lints/src/min_ident_chars.rs +++ b/clippy_lints/src/min_ident_chars.rs @@ -256,7 +256,11 @@ fn is_not_in_trait_impl(cx: &LateContext<'_>, pat: &Pat<'_>, ident: Ident) -> bo } fn get_param_name(impl_item: &ImplItem<'_>, cx: &LateContext<'_>, ident: Ident) -> Option { - if let ImplItemImplKind::Trait { trait_item_def_id: Ok(trait_item_def_id), .. } = impl_item.impl_kind { + if let ImplItemImplKind::Trait { + trait_item_def_id: Ok(trait_item_def_id), + .. + } = impl_item.impl_kind + { let trait_param_names = cx.tcx.fn_arg_idents(trait_item_def_id); let ImplItemKind::Fn(_, body_id) = impl_item.kind else { diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 09ee6f7037c6..19e9910dfe9d 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -1,57 +1,11 @@ -use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir, span_lint_hir_and_then}; -use clippy_utils::source::{snippet, snippet_with_context}; +use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::sugg::Sugg; -use clippy_utils::{ - SpanlessEq, fulfill_or_allowed, get_parent_expr, in_automatically_derived, is_lint_allowed, iter_input_pats, - last_path_segment, -}; +use clippy_utils::{SpanlessEq, fulfill_or_allowed, get_parent_expr, in_automatically_derived, last_path_segment}; use rustc_errors::Applicability; use rustc_hir::def::Res; -use rustc_hir::intravisit::FnKind; -use rustc_hir::{ - BinOpKind, BindingMode, Body, ByRef, Expr, ExprKind, FnDecl, Mutability, PatKind, QPath, Stmt, StmtKind, -}; +use rustc_hir::{BinOpKind, Expr, ExprKind, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::declare_lint_pass; -use rustc_span::Span; -use rustc_span::def_id::LocalDefId; - -use crate::ref_patterns::REF_PATTERNS; - -declare_clippy_lint! { - /// ### What it does - /// Checks for function arguments and let bindings denoted as - /// `ref`. - /// - /// ### Why is this bad? - /// The `ref` declaration makes the function take an owned - /// value, but turns the argument into a reference (which means that the value - /// is destroyed when exiting the function). This adds not much value: either - /// take a reference type, or take an owned value and create references in the - /// body. - /// - /// For let bindings, `let x = &foo;` is preferred over `let ref x = foo`. The - /// type of `x` is more obvious with the former. - /// - /// ### Known problems - /// If the argument is dereferenced within the function, - /// removing the `ref` will lead to errors. This can be fixed by removing the - /// dereferences, e.g., changing `*x` to `x` within the function. - /// - /// ### Example - /// ```no_run - /// fn foo(ref _x: u8) {} - /// ``` - /// - /// Use instead: - /// ```no_run - /// fn foo(_x: &u8) {} - /// ``` - #[clippy::version = "pre 1.29.0"] - pub TOPLEVEL_REF_ARG, - style, - "an entire binding declared as `ref`, in a function argument or a `let` statement" -} declare_clippy_lint! { /// ### What it does @@ -140,79 +94,13 @@ declare_clippy_lint! { } declare_lint_pass!(LintPass => [ - TOPLEVEL_REF_ARG, USED_UNDERSCORE_BINDING, USED_UNDERSCORE_ITEMS, SHORT_CIRCUIT_STATEMENT, ]); impl<'tcx> LateLintPass<'tcx> for LintPass { - fn check_fn( - &mut self, - cx: &LateContext<'tcx>, - k: FnKind<'tcx>, - decl: &'tcx FnDecl<'_>, - body: &'tcx Body<'_>, - _: Span, - _: LocalDefId, - ) { - if !matches!(k, FnKind::Closure) { - for arg in iter_input_pats(decl, body) { - if let PatKind::Binding(BindingMode(ByRef::Yes(_), _), ..) = arg.pat.kind - && is_lint_allowed(cx, REF_PATTERNS, arg.pat.hir_id) - && !arg.span.in_external_macro(cx.tcx.sess.source_map()) - { - span_lint_hir( - cx, - TOPLEVEL_REF_ARG, - arg.hir_id, - arg.pat.span, - "`ref` directly on a function parameter does not prevent taking ownership of the passed argument. \ - Consider using a reference type instead", - ); - } - } - } - } - fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { - if let StmtKind::Let(local) = stmt.kind - && let PatKind::Binding(BindingMode(ByRef::Yes(mutabl), _), .., name, None) = local.pat.kind - && let Some(init) = local.init - // Do not emit if clippy::ref_patterns is not allowed to avoid having two lints for the same issue. - && is_lint_allowed(cx, REF_PATTERNS, local.pat.hir_id) - && !stmt.span.in_external_macro(cx.tcx.sess.source_map()) - { - let ctxt = local.span.ctxt(); - let mut app = Applicability::MachineApplicable; - let sugg_init = Sugg::hir_with_context(cx, init, ctxt, "..", &mut app); - let (mutopt, initref) = if mutabl == Mutability::Mut { - ("mut ", sugg_init.mut_addr()) - } else { - ("", sugg_init.addr()) - }; - let tyopt = if let Some(ty) = local.ty { - let ty_snip = snippet_with_context(cx, ty.span, ctxt, "_", &mut app).0; - format!(": &{mutopt}{ty_snip}") - } else { - String::new() - }; - span_lint_hir_and_then( - cx, - TOPLEVEL_REF_ARG, - init.hir_id, - local.pat.span, - "`ref` on an entire `let` pattern is discouraged, take a reference with `&` instead", - |diag| { - diag.span_suggestion( - stmt.span, - "try", - format!("let {name}{tyopt} = {initref};", name = snippet(cx, name.span, ".."),), - app, - ); - }, - ); - } if let StmtKind::Semi(expr) = stmt.kind && let ExprKind::Binary(binop, a, b) = &expr.kind && matches!(binop.node, BinOpKind::And | BinOpKind::Or) diff --git a/clippy_lints/src/missing_doc.rs b/clippy_lints/src/missing_doc.rs index 39b5964bd87b..1c62caa1c827 100644 --- a/clippy_lints/src/missing_doc.rs +++ b/clippy_lints/src/missing_doc.rs @@ -250,7 +250,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { AssocContainer::Trait | AssocContainer::TraitImpl(_) => { note_prev_span_then_ret!(self.prev_span, impl_item.span); }, - AssocContainer::InherentImpl => {} + AssocContainer::InherentImpl => {}, } let (article, desc) = cx.tcx.article_and_description(impl_item.owner_id.to_def_id()); diff --git a/clippy_lints/src/multiple_unsafe_ops_per_block.rs b/clippy_lints/src/multiple_unsafe_ops_per_block.rs index c6c27e22b90e..bc5e72270f4e 100644 --- a/clippy_lints/src/multiple_unsafe_ops_per_block.rs +++ b/clippy_lints/src/multiple_unsafe_ops_per_block.rs @@ -1,3 +1,4 @@ +use clippy_utils::desugar_await; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::visitors::{Descend, Visitable, for_each_expr}; use core::ops::ControlFlow::Continue; @@ -97,6 +98,13 @@ fn collect_unsafe_exprs<'tcx>( ) { for_each_expr(cx, node, |expr| { match expr.kind { + // The `await` itself will desugar to two unsafe calls, but we should ignore those. + // Instead, check the expression that is `await`ed + _ if let Some(e) = desugar_await(expr) => { + collect_unsafe_exprs(cx, e, unsafe_ops); + return Continue(Descend::No); + }, + ExprKind::InlineAsm(_) => unsafe_ops.push(("inline assembly used here", expr.span)), ExprKind::Field(e, _) => { diff --git a/clippy_lints/src/only_used_in_recursion.rs b/clippy_lints/src/only_used_in_recursion.rs index c4cad592e367..a21c361356e8 100644 --- a/clippy_lints/src/only_used_in_recursion.rs +++ b/clippy_lints/src/only_used_in_recursion.rs @@ -252,7 +252,9 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { { ( trait_item_id, - FnKind::ImplTraitFn(std::ptr::from_ref(cx.tcx.erase_and_anonymize_regions(trait_ref.args)) as usize), + FnKind::ImplTraitFn( + std::ptr::from_ref(cx.tcx.erase_and_anonymize_regions(trait_ref.args)) as usize + ), usize::from(sig.decl.implicit_self.has_implicit_self()), ) } else { diff --git a/clippy_lints/src/operators/erasing_op.rs b/clippy_lints/src/operators/erasing_op.rs index e3fc8d8fea7d..8f5ee390f722 100644 --- a/clippy_lints/src/operators/erasing_op.rs +++ b/clippy_lints/src/operators/erasing_op.rs @@ -1,6 +1,6 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; -use clippy_utils::ty::same_type_and_consts; +use clippy_utils::ty::same_type_modulo_regions; use rustc_hir::{BinOpKind, Expr}; use rustc_lint::LateContext; @@ -29,7 +29,7 @@ pub(super) fn check<'tcx>( fn different_types(tck: &TypeckResults<'_>, input: &Expr<'_>, output: &Expr<'_>) -> bool { let input_ty = tck.expr_ty(input).peel_refs(); let output_ty = tck.expr_ty(output).peel_refs(); - !same_type_and_consts(input_ty, output_ty) + !same_type_modulo_regions(input_ty, output_ty) } fn check_op<'tcx>( diff --git a/clippy_lints/src/ptr_offset_with_cast.rs b/clippy_lints/src/ptr_offset_with_cast.rs deleted file mode 100644 index d8d813f9846d..000000000000 --- a/clippy_lints/src/ptr_offset_with_cast.rs +++ /dev/null @@ -1,151 +0,0 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; -use clippy_utils::source::SpanRangeExt; -use clippy_utils::sym; -use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::declare_lint_pass; -use std::fmt; - -declare_clippy_lint! { - /// ### What it does - /// Checks for usage of the `offset` pointer method with a `usize` casted to an - /// `isize`. - /// - /// ### Why is this bad? - /// If we’re always increasing the pointer address, we can avoid the numeric - /// cast by using the `add` method instead. - /// - /// ### Example - /// ```no_run - /// let vec = vec![b'a', b'b', b'c']; - /// let ptr = vec.as_ptr(); - /// let offset = 1_usize; - /// - /// unsafe { - /// ptr.offset(offset as isize); - /// } - /// ``` - /// - /// Could be written: - /// - /// ```no_run - /// let vec = vec![b'a', b'b', b'c']; - /// let ptr = vec.as_ptr(); - /// let offset = 1_usize; - /// - /// unsafe { - /// ptr.add(offset); - /// } - /// ``` - #[clippy::version = "1.30.0"] - pub PTR_OFFSET_WITH_CAST, - complexity, - "unneeded pointer offset cast" -} - -declare_lint_pass!(PtrOffsetWithCast => [PTR_OFFSET_WITH_CAST]); - -impl<'tcx> LateLintPass<'tcx> for PtrOffsetWithCast { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - // Check if the expressions is a ptr.offset or ptr.wrapping_offset method call - let Some((receiver_expr, arg_expr, method)) = expr_as_ptr_offset_call(cx, expr) else { - return; - }; - - // Check if the argument to the method call is a cast from usize - let Some(cast_lhs_expr) = expr_as_cast_from_usize(cx, arg_expr) else { - return; - }; - - let msg = format!("use of `{method}` with a `usize` casted to an `isize`"); - if let Some(sugg) = build_suggestion(cx, method, receiver_expr, cast_lhs_expr) { - span_lint_and_sugg( - cx, - PTR_OFFSET_WITH_CAST, - expr.span, - msg, - "try", - sugg, - Applicability::MachineApplicable, - ); - } else { - span_lint(cx, PTR_OFFSET_WITH_CAST, expr.span, msg); - } - } -} - -// If the given expression is a cast from a usize, return the lhs of the cast -fn expr_as_cast_from_usize<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { - if let ExprKind::Cast(cast_lhs_expr, _) = expr.kind - && is_expr_ty_usize(cx, cast_lhs_expr) - { - return Some(cast_lhs_expr); - } - None -} - -// If the given expression is a ptr::offset or ptr::wrapping_offset method call, return the -// receiver, the arg of the method call, and the method. -fn expr_as_ptr_offset_call<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'_>, -) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Method)> { - if let ExprKind::MethodCall(path_segment, arg_0, [arg_1], _) = &expr.kind - && is_expr_ty_raw_ptr(cx, arg_0) - { - if path_segment.ident.name == sym::offset { - return Some((arg_0, arg_1, Method::Offset)); - } - if path_segment.ident.name == sym::wrapping_offset { - return Some((arg_0, arg_1, Method::WrappingOffset)); - } - } - None -} - -// Is the type of the expression a usize? -fn is_expr_ty_usize(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - cx.typeck_results().expr_ty(expr) == cx.tcx.types.usize -} - -// Is the type of the expression a raw pointer? -fn is_expr_ty_raw_ptr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - cx.typeck_results().expr_ty(expr).is_raw_ptr() -} - -fn build_suggestion( - cx: &LateContext<'_>, - method: Method, - receiver_expr: &Expr<'_>, - cast_lhs_expr: &Expr<'_>, -) -> Option { - let receiver = receiver_expr.span.get_source_text(cx)?; - let cast_lhs = cast_lhs_expr.span.get_source_text(cx)?; - Some(format!("{receiver}.{}({cast_lhs})", method.suggestion())) -} - -#[derive(Copy, Clone)] -enum Method { - Offset, - WrappingOffset, -} - -impl Method { - #[must_use] - fn suggestion(self) -> &'static str { - match self { - Self::Offset => "add", - Self::WrappingOffset => "wrapping_add", - } - } -} - -impl fmt::Display for Method { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Offset => write!(f, "offset"), - Self::WrappingOffset => write!(f, "wrapping_offset"), - } - } -} diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index de12a25b03df..4aa100a50e05 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -8,9 +8,9 @@ use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use clippy_utils::{ - eq_expr_value, higher, is_else_clause, is_in_const_context, is_lint_allowed, is_path_lang_item, is_res_lang_ctor, - pat_and_expr_can_be_question_mark, path_res, path_to_local, path_to_local_id, peel_blocks, peel_blocks_with_stmt, - span_contains_cfg, span_contains_comment, sym, + eq_expr_value, fn_def_id_with_node_args, higher, is_else_clause, is_in_const_context, is_lint_allowed, + is_path_lang_item, is_res_lang_ctor, pat_and_expr_can_be_question_mark, path_res, path_to_local, path_to_local_id, + peel_blocks, peel_blocks_with_stmt, span_contains_cfg, span_contains_comment, sym, }; use rustc_errors::Applicability; use rustc_hir::LangItem::{self, OptionNone, OptionSome, ResultErr, ResultOk}; @@ -393,8 +393,8 @@ fn check_arm_is_none_or_err<'tcx>(cx: &LateContext<'tcx>, mode: TryMode, arm: &A && let ExprKind::Ret(Some(wrapped_ret_expr)) = arm_body.kind && let ExprKind::Call(ok_ctor, [ret_expr]) = wrapped_ret_expr.kind && is_res_lang_ctor(cx, path_res(cx, ok_ctor), ResultErr) - // check `...` is `val` from binding - && path_to_local_id(ret_expr, ok_val) + // check if `...` is `val` from binding or `val.into()` + && is_local_or_local_into(cx, ret_expr, ok_val) { true } else { @@ -417,6 +417,17 @@ fn check_arm_is_none_or_err<'tcx>(cx: &LateContext<'tcx>, mode: TryMode, arm: &A } } +/// Check if `expr` is `val` or `val.into()` +fn is_local_or_local_into(cx: &LateContext<'_>, expr: &Expr<'_>, val: HirId) -> bool { + let is_into_call = fn_def_id_with_node_args(cx, expr) + .and_then(|(fn_def_id, _)| cx.tcx.trait_of_assoc(fn_def_id)) + .is_some_and(|trait_def_id| cx.tcx.is_diagnostic_item(sym::Into, trait_def_id)); + match expr.kind { + ExprKind::MethodCall(_, recv, [], _) | ExprKind::Call(_, [recv]) => is_into_call && path_to_local_id(recv, val), + _ => path_to_local_id(expr, val), + } +} + fn check_arms_are_try<'tcx>(cx: &LateContext<'tcx>, mode: TryMode, arm1: &Arm<'tcx>, arm2: &Arm<'tcx>) -> bool { (check_arm_is_some_or_ok(cx, mode, arm1) && check_arm_is_none_or_err(cx, mode, arm2)) || (check_arm_is_some_or_ok(cx, mode, arm2) && check_arm_is_none_or_err(cx, mode, arm1)) diff --git a/clippy_lints/src/read_zero_byte_vec.rs b/clippy_lints/src/read_zero_byte_vec.rs index acd840401c6b..b8d4e7c4651d 100644 --- a/clippy_lints/src/read_zero_byte_vec.rs +++ b/clippy_lints/src/read_zero_byte_vec.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; use clippy_utils::higher::{VecInitKind, get_vec_init_kind}; -use clippy_utils::source::snippet; +use clippy_utils::source::{indent_of, snippet}; use clippy_utils::{get_enclosing_block, sym}; use rustc_errors::Applicability; @@ -83,10 +83,12 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec { expr.span, "reading zero byte data to `Vec`", |diag| { + let span = first_stmt_containing_expr(cx, expr).map_or(expr.span, |stmt| stmt.span); + let indent = indent_of(cx, span).unwrap_or(0); diag.span_suggestion( - expr.span, + span.shrink_to_lo(), "try", - format!("{}.resize({len}, 0); {}", ident, snippet(cx, expr.span, "..")), + format!("{ident}.resize({len}, 0);\n{}", " ".repeat(indent)), applicability, ); }, @@ -100,14 +102,15 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec { expr.span, "reading zero byte data to `Vec`", |diag| { + let span = first_stmt_containing_expr(cx, expr).map_or(expr.span, |stmt| stmt.span); + let indent = indent_of(cx, span).unwrap_or(0); diag.span_suggestion( - expr.span, + span.shrink_to_lo(), "try", format!( - "{}.resize({}, 0); {}", - ident, + "{ident}.resize({}, 0);\n{}", snippet(cx, e.span, ".."), - snippet(cx, expr.span, "..") + " ".repeat(indent) ), applicability, ); @@ -130,6 +133,16 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec { } } +fn first_stmt_containing_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx hir::Stmt<'tcx>> { + cx.tcx.hir_parent_iter(expr.hir_id).find_map(|(_, node)| { + if let hir::Node::Stmt(stmt) = node { + Some(stmt) + } else { + None + } + }) +} + struct ReadVecVisitor<'tcx> { local_id: HirId, read_zero_expr: Option<&'tcx Expr<'tcx>>, diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index 1d58cdd26d88..de6766cbe94a 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; use clippy_utils::fn_has_unsatisfiable_preds; use clippy_utils::mir::{LocalUsage, PossibleBorrowerMap, visit_local_usage}; use clippy_utils::source::SpanRangeExt; -use clippy_utils::ty::{has_drop, is_copy, is_type_lang_item, walk_ptrs_ty_depth}; +use clippy_utils::ty::{has_drop, is_copy, is_type_lang_item, peel_and_count_ty_refs}; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, FnDecl, LangItem, def_id}; @@ -263,7 +263,7 @@ fn is_call_with_ref_arg<'tcx>( && args.len() == 1 && let mir::Operand::Move(mir::Place { local, .. }) = &args[0].node && let ty::FnDef(def_id, _) = *func.ty(mir, cx.tcx).kind() - && let (inner_ty, 1) = walk_ptrs_ty_depth(args[0].node.ty(mir, cx.tcx)) + && let (inner_ty, 1, _) = peel_and_count_ty_refs(args[0].node.ty(mir, cx.tcx)) && !is_copy(cx, inner_ty) { Some((def_id, *local, inner_ty, destination.as_local()?)) diff --git a/clippy_lints/src/redundant_slicing.rs b/clippy_lints/src/redundant_slicing.rs index 324a05cdcc0c..a358eff2ce55 100644 --- a/clippy_lints/src/redundant_slicing.rs +++ b/clippy_lints/src/redundant_slicing.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::get_parent_expr; use clippy_utils::source::snippet_with_context; -use clippy_utils::ty::is_type_lang_item; -use clippy_utils::{get_parent_expr, peel_middle_ty_refs}; +use clippy_utils::ty::{is_type_lang_item, peel_and_count_ty_refs}; use rustc_ast::util::parser::ExprPrecedence; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability}; @@ -82,8 +82,8 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing { && let ExprKind::Index(indexed, range, _) = addressee.kind && is_type_lang_item(cx, cx.typeck_results().expr_ty_adjusted(range), LangItem::RangeFull) { - let (expr_ty, expr_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(expr)); - let (indexed_ty, indexed_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(indexed)); + let (expr_ty, expr_ref_count, _) = peel_and_count_ty_refs(cx.typeck_results().expr_ty(expr)); + let (indexed_ty, indexed_ref_count, _) = peel_and_count_ty_refs(cx.typeck_results().expr_ty(indexed)); let parent_expr = get_parent_expr(cx, expr); let needs_parens_for_prefix = parent_expr.is_some_and(|parent| cx.precedence(parent) > ExprPrecedence::Prefix); diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs deleted file mode 100644 index e0c93153a77a..000000000000 --- a/clippy_lints/src/returns.rs +++ /dev/null @@ -1,513 +0,0 @@ -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::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, - leaks_droppable_temporary_with_limited_lifetime, path_res, path_to_local_id, span_contains_cfg, - span_find_starting_semi, sym, -}; -use core::ops::ControlFlow; -use rustc_ast::MetaItemInner; -use rustc_errors::Applicability; -use rustc_hir::LangItem::ResultErr; -use rustc_hir::intravisit::FnKind; -use rustc_hir::{ - Block, Body, Expr, ExprKind, FnDecl, HirId, ItemKind, LangItem, MatchSource, Node, OwnerNode, PatKind, QPath, Stmt, - StmtKind, -}; -use rustc_lint::{LateContext, LateLintPass, Level, LintContext}; -use rustc_middle::ty::adjustment::Adjust; -use rustc_middle::ty::{self, GenericArgKind, Ty}; -use rustc_session::declare_lint_pass; -use rustc_span::def_id::LocalDefId; -use rustc_span::edition::Edition; -use rustc_span::{BytePos, Pos, Span}; -use std::borrow::Cow; -use std::fmt::Display; - -declare_clippy_lint! { - /// ### What it does - /// Checks for `let`-bindings, which are subsequently - /// returned. - /// - /// ### Why is this bad? - /// It is just extraneous code. Remove it to make your code - /// more rusty. - /// - /// ### Known problems - /// In the case of some temporaries, e.g. locks, eliding the variable binding could lead - /// to deadlocks. See [this issue](https://github.com/rust-lang/rust/issues/37612). - /// This could become relevant if the code is later changed to use the code that would have been - /// bound without first assigning it to a let-binding. - /// - /// ### Example - /// ```no_run - /// fn foo() -> String { - /// let x = String::new(); - /// x - /// } - /// ``` - /// instead, use - /// ```no_run - /// fn foo() -> String { - /// String::new() - /// } - /// ``` - #[clippy::version = "pre 1.29.0"] - pub LET_AND_RETURN, - style, - "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for return statements at the end of a block. - /// - /// ### Why is this bad? - /// Removing the `return` and semicolon will make the code - /// more rusty. - /// - /// ### Example - /// ```no_run - /// fn foo(x: usize) -> usize { - /// return x; - /// } - /// ``` - /// simplify to - /// ```no_run - /// fn foo(x: usize) -> usize { - /// x - /// } - /// ``` - #[clippy::version = "pre 1.29.0"] - pub NEEDLESS_RETURN, - // This lint requires some special handling in `check_final_expr` for `#[expect]`. - // This handling needs to be updated if the group gets changed. This should also - // be caught by tests. - style, - "using a return statement like `return expr;` where an expression would suffice" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for return statements on `Err` paired with the `?` operator. - /// - /// ### Why is this bad? - /// The `return` is unnecessary. - /// - /// Returns may be used to add attributes to the return expression. Return - /// statements with attributes are therefore be accepted by this lint. - /// - /// ### Example - /// ```rust,ignore - /// fn foo(x: usize) -> Result<(), Box> { - /// if x == 0 { - /// return Err(...)?; - /// } - /// Ok(()) - /// } - /// ``` - /// simplify to - /// ```rust,ignore - /// fn foo(x: usize) -> Result<(), Box> { - /// if x == 0 { - /// Err(...)?; - /// } - /// Ok(()) - /// } - /// ``` - /// if paired with `try_err`, use instead: - /// ```rust,ignore - /// fn foo(x: usize) -> Result<(), Box> { - /// if x == 0 { - /// return Err(...); - /// } - /// Ok(()) - /// } - /// ``` - #[clippy::version = "1.73.0"] - pub NEEDLESS_RETURN_WITH_QUESTION_MARK, - style, - "using a return statement like `return Err(expr)?;` where removing it would suffice" -} - -#[derive(PartialEq, Eq)] -enum RetReplacement<'tcx> { - Empty, - Block, - Unit, - NeedsPar(Cow<'tcx, str>, Applicability), - Expr(Cow<'tcx, str>, Applicability), -} - -impl RetReplacement<'_> { - fn sugg_help(&self) -> &'static str { - match self { - Self::Empty | Self::Expr(..) => "remove `return`", - Self::Block => "replace `return` with an empty block", - Self::Unit => "replace `return` with a unit value", - Self::NeedsPar(..) => "remove `return` and wrap the sequence with parentheses", - } - } - - fn applicability(&self) -> Applicability { - match self { - Self::Expr(_, ap) | Self::NeedsPar(_, ap) => *ap, - _ => Applicability::MachineApplicable, - } - } -} - -impl Display for RetReplacement<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Empty => write!(f, ""), - Self::Block => write!(f, "{{}}"), - Self::Unit => write!(f, "()"), - Self::NeedsPar(inner, _) => write!(f, "({inner})"), - Self::Expr(inner, _) => write!(f, "{inner}"), - } - } -} - -declare_lint_pass!(Return => [LET_AND_RETURN, NEEDLESS_RETURN, NEEDLESS_RETURN_WITH_QUESTION_MARK]); - -/// Checks if a return statement is "needed" in the middle of a block, or if it can be removed. This -/// is the case when the enclosing block expression is coerced to some other type, which only works -/// because of the never-ness of `return` expressions -fn stmt_needs_never_type(cx: &LateContext<'_>, stmt_hir_id: HirId) -> bool { - cx.tcx - .hir_parent_iter(stmt_hir_id) - .find_map(|(_, node)| if let Node::Expr(expr) = node { Some(expr) } else { None }) - .is_some_and(|e| { - cx.typeck_results() - .expr_adjustments(e) - .iter() - .any(|adjust| adjust.target != cx.tcx.types.unit && matches!(adjust.kind, Adjust::NeverToAny)) - }) -} - -impl<'tcx> LateLintPass<'tcx> for Return { - fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { - if !stmt.span.in_external_macro(cx.sess().source_map()) - && let StmtKind::Semi(expr) = stmt.kind - && let ExprKind::Ret(Some(ret)) = expr.kind - // return Err(...)? desugars to a match - // over a Err(...).branch() - // which breaks down to a branch call, with the callee being - // the constructor of the Err variant - && let ExprKind::Match(maybe_cons, _, MatchSource::TryDesugar(_)) = ret.kind - && let ExprKind::Call(_, [maybe_result_err]) = maybe_cons.kind - && let ExprKind::Call(maybe_constr, _) = maybe_result_err.kind - && is_res_lang_ctor(cx, path_res(cx, maybe_constr), ResultErr) - - // Ensure this is not the final stmt, otherwise removing it would cause a compile error - && let OwnerNode::Item(item) = cx.tcx.hir_owner_node(cx.tcx.hir_get_parent_item(expr.hir_id)) - && let ItemKind::Fn { body, .. } = item.kind - && let block = cx.tcx.hir_body(body).value - && let ExprKind::Block(block, _) = block.kind - && !is_inside_let_else(cx.tcx, expr) - && let [.., final_stmt] = block.stmts - && final_stmt.hir_id != stmt.hir_id - && !is_from_proc_macro(cx, expr) - && !stmt_needs_never_type(cx, stmt.hir_id) - { - span_lint_and_sugg( - cx, - NEEDLESS_RETURN_WITH_QUESTION_MARK, - expr.span.until(ret.span), - "unneeded `return` statement with `?` operator", - "remove it", - String::new(), - Applicability::MachineApplicable, - ); - } - } - - fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) { - // we need both a let-binding stmt and an expr - if let Some(retexpr) = block.expr - && let Some(stmt) = block.stmts.iter().last() - && let StmtKind::Let(local) = &stmt.kind - && local.ty.is_none() - && cx.tcx.hir_attrs(local.hir_id).is_empty() - && let Some(initexpr) = &local.init - && let PatKind::Binding(_, local_id, _, _) = local.pat.kind - && path_to_local_id(retexpr, local_id) - && (cx.sess().edition() >= Edition::Edition2024 || !last_statement_borrows(cx, initexpr)) - && !initexpr.span.in_external_macro(cx.sess().source_map()) - && !retexpr.span.in_external_macro(cx.sess().source_map()) - && !local.span.from_expansion() - && !span_contains_cfg(cx, stmt.span.between(retexpr.span)) - { - span_lint_hir_and_then( - cx, - LET_AND_RETURN, - retexpr.hir_id, - retexpr.span, - "returning the result of a `let` binding from a block", - |err| { - err.span_label(local.span, "unnecessary `let` binding"); - - if let Some(src) = initexpr.span.get_source_text(cx) { - let sugg = if binary_expr_needs_parentheses(initexpr) { - if has_enclosing_paren(&src) { - src.to_owned() - } else { - format!("({src})") - } - } else if !cx.typeck_results().expr_adjustments(retexpr).is_empty() { - if has_enclosing_paren(&src) { - format!("{src} as _") - } else { - format!("({src}) as _") - } - } else { - src.to_owned() - }; - err.multipart_suggestion( - "return the expression directly", - vec![(local.span, String::new()), (retexpr.span, sugg)], - Applicability::MachineApplicable, - ); - } else { - err.span_help(initexpr.span, "this expression can be directly returned"); - } - }, - ); - } - } - - fn check_fn( - &mut self, - cx: &LateContext<'tcx>, - kind: FnKind<'tcx>, - _: &'tcx FnDecl<'tcx>, - body: &'tcx Body<'tcx>, - sp: Span, - _: LocalDefId, - ) { - if sp.from_expansion() { - return; - } - - match kind { - FnKind::Closure => { - // when returning without value in closure, replace this `return` - // with an empty block to prevent invalid suggestion (see #6501) - let replacement = if let ExprKind::Ret(None) = &body.value.kind { - RetReplacement::Block - } else { - RetReplacement::Empty - }; - check_final_expr(cx, body.value, vec![], replacement, None); - }, - FnKind::ItemFn(..) | FnKind::Method(..) => { - check_block_return(cx, &body.value.kind, sp, vec![]); - }, - } - } -} - -// if `expr` is a block, check if there are needless returns in it -fn check_block_return<'tcx>(cx: &LateContext<'tcx>, expr_kind: &ExprKind<'tcx>, sp: Span, mut semi_spans: Vec) { - if let ExprKind::Block(block, _) = expr_kind { - if let Some(block_expr) = block.expr { - check_final_expr(cx, block_expr, semi_spans, RetReplacement::Empty, None); - } else if let Some(stmt) = block.stmts.iter().last() { - match stmt.kind { - StmtKind::Expr(expr) => { - check_final_expr(cx, expr, semi_spans, RetReplacement::Empty, None); - }, - StmtKind::Semi(semi_expr) => { - // Remove ending semicolons and any whitespace ' ' in between. - // Without `return`, the suggestion might not compile if the semicolon is retained - if let Some(semi_span) = stmt.span.trim_start(semi_expr.span) { - let semi_span_to_remove = - span_find_starting_semi(cx.sess().source_map(), semi_span.with_hi(sp.hi())); - semi_spans.push(semi_span_to_remove); - } - check_final_expr(cx, semi_expr, semi_spans, RetReplacement::Empty, None); - }, - _ => (), - } - } - } -} - -fn check_final_expr<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'tcx>, - semi_spans: Vec, /* containing all the places where we would need to remove semicolons if finding an - * needless return */ - replacement: RetReplacement<'tcx>, - match_ty_opt: Option>, -) { - let peeled_drop_expr = expr.peel_drop_temps(); - match &peeled_drop_expr.kind { - // simple return is always "bad" - ExprKind::Ret(inner) => { - // check if expr return nothing - let ret_span = if inner.is_none() && replacement == RetReplacement::Empty { - extend_span_to_previous_non_ws(cx, peeled_drop_expr.span) - } else { - peeled_drop_expr.span - }; - - let replacement = if let Some(inner_expr) = inner { - // if desugar of `do yeet`, don't lint - if let ExprKind::Call(path_expr, [_]) = inner_expr.kind - && let ExprKind::Path(QPath::LangItem(LangItem::TryTraitFromYeet, ..)) = path_expr.kind - { - return; - } - - let mut applicability = Applicability::MachineApplicable; - let (snippet, _) = snippet_with_context(cx, inner_expr.span, ret_span.ctxt(), "..", &mut applicability); - if binary_expr_needs_parentheses(inner_expr) { - RetReplacement::NeedsPar(snippet, applicability) - } else { - RetReplacement::Expr(snippet, applicability) - } - } else { - match match_ty_opt { - Some(match_ty) => { - match match_ty.kind() { - // If the code got till here with - // tuple not getting detected before it, - // then we are sure it's going to be Unit - // type - ty::Tuple(_) => RetReplacement::Unit, - // We don't want to anything in this case - // cause we can't predict what the user would - // want here - _ => return, - } - }, - None => replacement, - } - }; - - 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) { - return; - } - - // Returns may be used to turn an expression into a statement in rustc's AST. - // This allows the addition of attributes, like `#[allow]` (See: clippy#9361) - // `#[expect(clippy::needless_return)]` needs to be handled separately to - // actually fulfill the expectation (clippy::#12998) - match cx.tcx.hir_attrs(expr.hir_id) { - [] => {}, - [attr] => { - if matches!(Level::from_attr(attr), Some((Level::Expect, _))) - && let metas = attr.meta_item_list() - && let Some(lst) = metas - && let [MetaItemInner::MetaItem(meta_item), ..] = lst.as_slice() - && let [tool, lint_name] = meta_item.path.segments.as_slice() - && tool.ident.name == sym::clippy - && matches!( - lint_name.ident.name, - sym::needless_return | sym::style | sym::all | sym::warnings - ) - { - // This is an expectation of the `needless_return` lint - } else { - return; - } - }, - _ => return, - } - - emit_return_lint( - cx, - peeled_drop_expr.span, - ret_span, - semi_spans, - &replacement, - expr.hir_id, - ); - }, - ExprKind::If(_, then, else_clause_opt) => { - check_block_return(cx, &then.kind, peeled_drop_expr.span, semi_spans.clone()); - if let Some(else_clause) = else_clause_opt { - // The `RetReplacement` won't be used there as `else_clause` will be either a block or - // a `if` expression. - check_final_expr(cx, else_clause, semi_spans, RetReplacement::Empty, match_ty_opt); - } - }, - // a match expr, check all arms - // an if/if let expr, check both exprs - // note, if without else is going to be a type checking error anyways - // (except for unit type functions) so we don't match it - ExprKind::Match(_, arms, MatchSource::Normal) => { - let match_ty = cx.typeck_results().expr_ty(peeled_drop_expr); - for arm in *arms { - check_final_expr(cx, arm.body, semi_spans.clone(), RetReplacement::Unit, Some(match_ty)); - } - }, - // if it's a whole block, check it - other_expr_kind => check_block_return(cx, other_expr_kind, peeled_drop_expr.span, semi_spans), - } -} - -fn emit_return_lint( - cx: &LateContext<'_>, - lint_span: Span, - ret_span: Span, - semi_spans: Vec, - replacement: &RetReplacement<'_>, - at: HirId, -) { - span_lint_hir_and_then( - cx, - NEEDLESS_RETURN, - at, - lint_span, - "unneeded `return` statement", - |diag| { - let suggestions = std::iter::once((ret_span, replacement.to_string())) - .chain(semi_spans.into_iter().map(|span| (span, String::new()))) - .collect(); - - diag.multipart_suggestion_verbose(replacement.sugg_help(), suggestions, replacement.applicability()); - }, - ); -} - -fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { - for_each_expr(cx, expr, |e| { - if let Some(def_id) = fn_def_id(cx, e) - && cx - .tcx - .fn_sig(def_id) - .instantiate_identity() - .skip_binder() - .output() - .walk() - .any(|arg| matches!(arg.kind(), GenericArgKind::Lifetime(re) if !re.is_static())) - { - ControlFlow::Break(()) - } else { - ControlFlow::Continue(()) - } - }) - .is_some() -} - -// Go backwards while encountering whitespace and extend the given Span to that point. -fn extend_span_to_previous_non_ws(cx: &LateContext<'_>, sp: Span) -> Span { - if let Ok(prev_source) = cx.sess().source_map().span_to_prev_source(sp) { - let ws = [b' ', b'\t', b'\n']; - if let Some(non_ws_pos) = prev_source.bytes().rposition(|c| !ws.contains(&c)) { - let len = prev_source.len() - non_ws_pos - 1; - return sp.with_lo(sp.lo() - BytePos::from_usize(len)); - } - } - - sp -} diff --git a/clippy_lints/src/returns/let_and_return.rs b/clippy_lints/src/returns/let_and_return.rs new file mode 100644 index 000000000000..e2002fb36e5a --- /dev/null +++ b/clippy_lints/src/returns/let_and_return.rs @@ -0,0 +1,86 @@ +use clippy_utils::diagnostics::span_lint_hir_and_then; +use clippy_utils::source::SpanRangeExt; +use clippy_utils::sugg::has_enclosing_paren; +use clippy_utils::visitors::for_each_expr; +use clippy_utils::{binary_expr_needs_parentheses, fn_def_id, path_to_local_id, span_contains_cfg}; +use core::ops::ControlFlow; +use rustc_errors::Applicability; +use rustc_hir::{Block, Expr, PatKind, StmtKind}; +use rustc_lint::{LateContext, LintContext}; +use rustc_middle::ty::GenericArgKind; +use rustc_span::edition::Edition; + +use super::LET_AND_RETURN; + +pub(super) fn check_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'_>) { + // we need both a let-binding stmt and an expr + if let Some(retexpr) = block.expr + && let Some(stmt) = block.stmts.last() + && let StmtKind::Let(local) = &stmt.kind + && local.ty.is_none() + && cx.tcx.hir_attrs(local.hir_id).is_empty() + && let Some(initexpr) = &local.init + && let PatKind::Binding(_, local_id, _, _) = local.pat.kind + && path_to_local_id(retexpr, local_id) + && (cx.sess().edition() >= Edition::Edition2024 || !last_statement_borrows(cx, initexpr)) + && !initexpr.span.in_external_macro(cx.sess().source_map()) + && !retexpr.span.in_external_macro(cx.sess().source_map()) + && !local.span.from_expansion() + && !span_contains_cfg(cx, stmt.span.between(retexpr.span)) + { + span_lint_hir_and_then( + cx, + LET_AND_RETURN, + retexpr.hir_id, + retexpr.span, + "returning the result of a `let` binding from a block", + |err| { + err.span_label(local.span, "unnecessary `let` binding"); + + if let Some(src) = initexpr.span.get_source_text(cx) { + let sugg = if binary_expr_needs_parentheses(initexpr) { + if has_enclosing_paren(&src) { + src.to_owned() + } else { + format!("({src})") + } + } else if !cx.typeck_results().expr_adjustments(retexpr).is_empty() { + if has_enclosing_paren(&src) { + format!("{src} as _") + } else { + format!("({src}) as _") + } + } else { + src.to_owned() + }; + err.multipart_suggestion( + "return the expression directly", + vec![(local.span, String::new()), (retexpr.span, sugg)], + Applicability::MachineApplicable, + ); + } else { + err.span_help(initexpr.span, "this expression can be directly returned"); + } + }, + ); + } +} +fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { + for_each_expr(cx, expr, |e| { + if let Some(def_id) = fn_def_id(cx, e) + && cx + .tcx + .fn_sig(def_id) + .instantiate_identity() + .skip_binder() + .output() + .walk() + .any(|arg| matches!(arg.kind(), GenericArgKind::Lifetime(re) if !re.is_static())) + { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) + } + }) + .is_some() +} diff --git a/clippy_lints/src/returns/mod.rs b/clippy_lints/src/returns/mod.rs new file mode 100644 index 000000000000..47c6332b9b81 --- /dev/null +++ b/clippy_lints/src/returns/mod.rs @@ -0,0 +1,140 @@ +use rustc_hir::intravisit::FnKind; +use rustc_hir::{Block, Body, FnDecl, Stmt}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::declare_lint_pass; +use rustc_span::Span; +use rustc_span::def_id::LocalDefId; + +mod let_and_return; +mod needless_return; +mod needless_return_with_question_mark; + +declare_clippy_lint! { + /// ### What it does + /// Checks for `let`-bindings, which are subsequently + /// returned. + /// + /// ### Why is this bad? + /// It is just extraneous code. Remove it to make your code + /// more rusty. + /// + /// ### Known problems + /// In the case of some temporaries, e.g. locks, eliding the variable binding could lead + /// to deadlocks. See [this issue](https://github.com/rust-lang/rust/issues/37612). + /// This could become relevant if the code is later changed to use the code that would have been + /// bound without first assigning it to a let-binding. + /// + /// ### Example + /// ```no_run + /// fn foo() -> String { + /// let x = String::new(); + /// x + /// } + /// ``` + /// instead, use + /// ```no_run + /// fn foo() -> String { + /// String::new() + /// } + /// ``` + #[clippy::version = "pre 1.29.0"] + pub LET_AND_RETURN, + style, + "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for return statements at the end of a block. + /// + /// ### Why is this bad? + /// Removing the `return` and semicolon will make the code + /// more rusty. + /// + /// ### Example + /// ```no_run + /// fn foo(x: usize) -> usize { + /// return x; + /// } + /// ``` + /// simplify to + /// ```no_run + /// fn foo(x: usize) -> usize { + /// x + /// } + /// ``` + #[clippy::version = "pre 1.29.0"] + pub NEEDLESS_RETURN, + // This lint requires some special handling in `check_final_expr` for `#[expect]`. + // This handling needs to be updated if the group gets changed. This should also + // be caught by tests. + style, + "using a return statement like `return expr;` where an expression would suffice" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for return statements on `Err` paired with the `?` operator. + /// + /// ### Why is this bad? + /// The `return` is unnecessary. + /// + /// Returns may be used to add attributes to the return expression. Return + /// statements with attributes are therefore be accepted by this lint. + /// + /// ### Example + /// ```rust,ignore + /// fn foo(x: usize) -> Result<(), Box> { + /// if x == 0 { + /// return Err(...)?; + /// } + /// Ok(()) + /// } + /// ``` + /// simplify to + /// ```rust,ignore + /// fn foo(x: usize) -> Result<(), Box> { + /// if x == 0 { + /// Err(...)?; + /// } + /// Ok(()) + /// } + /// ``` + /// if paired with `try_err`, use instead: + /// ```rust,ignore + /// fn foo(x: usize) -> Result<(), Box> { + /// if x == 0 { + /// return Err(...); + /// } + /// Ok(()) + /// } + /// ``` + #[clippy::version = "1.73.0"] + pub NEEDLESS_RETURN_WITH_QUESTION_MARK, + style, + "using a return statement like `return Err(expr)?;` where removing it would suffice" +} + +declare_lint_pass!(Return => [LET_AND_RETURN, NEEDLESS_RETURN, NEEDLESS_RETURN_WITH_QUESTION_MARK]); + +impl<'tcx> LateLintPass<'tcx> for Return { + fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { + needless_return_with_question_mark::check_stmt(cx, stmt); + } + + fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) { + let_and_return::check_block(cx, block); + } + + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + kind: FnKind<'tcx>, + _: &'tcx FnDecl<'tcx>, + body: &'tcx Body<'tcx>, + sp: Span, + _: LocalDefId, + ) { + needless_return::check_fn(cx, kind, body, sp); + } +} diff --git a/clippy_lints/src/returns/needless_return.rs b/clippy_lints/src/returns/needless_return.rs new file mode 100644 index 000000000000..04739fc1b22a --- /dev/null +++ b/clippy_lints/src/returns/needless_return.rs @@ -0,0 +1,269 @@ +use clippy_utils::diagnostics::span_lint_hir_and_then; +use clippy_utils::source::snippet_with_context; +use clippy_utils::{ + binary_expr_needs_parentheses, is_from_proc_macro, leaks_droppable_temporary_with_limited_lifetime, + span_contains_cfg, span_find_starting_semi, sym, +}; +use rustc_ast::MetaItemInner; +use rustc_errors::Applicability; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{Body, Expr, ExprKind, HirId, LangItem, MatchSource, QPath, StmtKind}; +use rustc_lint::{LateContext, Level, LintContext}; +use rustc_middle::ty::{self, Ty}; +use rustc_span::{BytePos, Pos, Span}; +use std::borrow::Cow; +use std::fmt::Display; + +use super::NEEDLESS_RETURN; + +#[derive(PartialEq, Eq)] +enum RetReplacement<'tcx> { + Empty, + Block, + Unit, + NeedsPar(Cow<'tcx, str>, Applicability), + Expr(Cow<'tcx, str>, Applicability), +} + +impl RetReplacement<'_> { + fn sugg_help(&self) -> &'static str { + match self { + Self::Empty | Self::Expr(..) => "remove `return`", + Self::Block => "replace `return` with an empty block", + Self::Unit => "replace `return` with a unit value", + Self::NeedsPar(..) => "remove `return` and wrap the sequence with parentheses", + } + } + + fn applicability(&self) -> Applicability { + match self { + Self::Expr(_, ap) | Self::NeedsPar(_, ap) => *ap, + _ => Applicability::MachineApplicable, + } + } +} + +impl Display for RetReplacement<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Empty => f.write_str(""), + Self::Block => f.write_str("{}"), + Self::Unit => f.write_str("()"), + Self::NeedsPar(inner, _) => write!(f, "({inner})"), + Self::Expr(inner, _) => write!(f, "{inner}"), + } + } +} + +pub(super) fn check_fn<'tcx>(cx: &LateContext<'tcx>, kind: FnKind<'tcx>, body: &'tcx Body<'tcx>, sp: Span) { + if sp.from_expansion() { + return; + } + + match kind { + FnKind::Closure => { + // when returning without value in closure, replace this `return` + // with an empty block to prevent invalid suggestion (see #6501) + let replacement = if let ExprKind::Ret(None) = &body.value.kind { + RetReplacement::Block + } else { + RetReplacement::Empty + }; + check_final_expr(cx, body.value, vec![], replacement, None); + }, + FnKind::ItemFn(..) | FnKind::Method(..) => { + check_block_return(cx, &body.value.kind, sp, vec![]); + }, + } +} + +// if `expr` is a block, check if there are needless returns in it +fn check_block_return<'tcx>(cx: &LateContext<'tcx>, expr_kind: &ExprKind<'tcx>, sp: Span, mut semi_spans: Vec) { + if let ExprKind::Block(block, _) = expr_kind { + if let Some(block_expr) = block.expr { + check_final_expr(cx, block_expr, semi_spans, RetReplacement::Empty, None); + } else if let Some(stmt) = block.stmts.last() { + if span_contains_cfg( + cx, + Span::between( + stmt.span, + cx.sess().source_map().end_point(block.span), // the closing brace of the block + ), + ) { + return; + } + match stmt.kind { + StmtKind::Expr(expr) => { + check_final_expr(cx, expr, semi_spans, RetReplacement::Empty, None); + }, + StmtKind::Semi(semi_expr) => { + // Remove ending semicolons and any whitespace ' ' in between. + // Without `return`, the suggestion might not compile if the semicolon is retained + if let Some(semi_span) = stmt.span.trim_start(semi_expr.span) { + let semi_span_to_remove = + span_find_starting_semi(cx.sess().source_map(), semi_span.with_hi(sp.hi())); + semi_spans.push(semi_span_to_remove); + } + check_final_expr(cx, semi_expr, semi_spans, RetReplacement::Empty, None); + }, + _ => (), + } + } + } +} + +fn check_final_expr<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, + semi_spans: Vec, /* containing all the places where we would need to remove semicolons if finding an + * needless return */ + replacement: RetReplacement<'tcx>, + match_ty_opt: Option>, +) { + let peeled_drop_expr = expr.peel_drop_temps(); + match &peeled_drop_expr.kind { + // simple return is always "bad" + ExprKind::Ret(inner) => { + // check if expr return nothing + let ret_span = if inner.is_none() && replacement == RetReplacement::Empty { + extend_span_to_previous_non_ws(cx, peeled_drop_expr.span) + } else { + peeled_drop_expr.span + }; + + let replacement = if let Some(inner_expr) = inner { + // if desugar of `do yeet`, don't lint + if let ExprKind::Call(path_expr, [_]) = inner_expr.kind + && let ExprKind::Path(QPath::LangItem(LangItem::TryTraitFromYeet, ..)) = path_expr.kind + { + return; + } + + let mut applicability = Applicability::MachineApplicable; + let (snippet, _) = snippet_with_context(cx, inner_expr.span, ret_span.ctxt(), "..", &mut applicability); + if binary_expr_needs_parentheses(inner_expr) { + RetReplacement::NeedsPar(snippet, applicability) + } else { + RetReplacement::Expr(snippet, applicability) + } + } else { + match match_ty_opt { + Some(match_ty) => { + match match_ty.kind() { + // If the code got till here with + // tuple not getting detected before it, + // then we are sure it's going to be Unit + // type + ty::Tuple(_) => RetReplacement::Unit, + // We don't want to anything in this case + // cause we can't predict what the user would + // want here + _ => return, + } + }, + None => replacement, + } + }; + + 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) { + return; + } + + // Returns may be used to turn an expression into a statement in rustc's AST. + // This allows the addition of attributes, like `#[allow]` (See: clippy#9361) + // `#[expect(clippy::needless_return)]` needs to be handled separately to + // actually fulfill the expectation (clippy::#12998) + match cx.tcx.hir_attrs(expr.hir_id) { + [] => {}, + [attr] => { + if matches!(Level::from_attr(attr), Some((Level::Expect, _))) + && let metas = attr.meta_item_list() + && let Some(lst) = metas + && let [MetaItemInner::MetaItem(meta_item), ..] = lst.as_slice() + && let [tool, lint_name] = meta_item.path.segments.as_slice() + && tool.ident.name == sym::clippy + && matches!( + lint_name.ident.name, + sym::needless_return | sym::style | sym::all | sym::warnings + ) + { + // This is an expectation of the `needless_return` lint + } else { + return; + } + }, + _ => return, + } + + emit_return_lint( + cx, + peeled_drop_expr.span, + ret_span, + semi_spans, + &replacement, + expr.hir_id, + ); + }, + ExprKind::If(_, then, else_clause_opt) => { + check_block_return(cx, &then.kind, peeled_drop_expr.span, semi_spans.clone()); + if let Some(else_clause) = else_clause_opt { + // The `RetReplacement` won't be used there as `else_clause` will be either a block or + // a `if` expression. + check_final_expr(cx, else_clause, semi_spans, RetReplacement::Empty, match_ty_opt); + } + }, + // a match expr, check all arms + // an if/if let expr, check both exprs + // note, if without else is going to be a type checking error anyways + // (except for unit type functions) so we don't match it + ExprKind::Match(_, arms, MatchSource::Normal) => { + let match_ty = cx.typeck_results().expr_ty(peeled_drop_expr); + for arm in *arms { + check_final_expr(cx, arm.body, semi_spans.clone(), RetReplacement::Unit, Some(match_ty)); + } + }, + // if it's a whole block, check it + other_expr_kind => check_block_return(cx, other_expr_kind, peeled_drop_expr.span, semi_spans), + } +} + +fn emit_return_lint( + cx: &LateContext<'_>, + lint_span: Span, + ret_span: Span, + semi_spans: Vec, + replacement: &RetReplacement<'_>, + at: HirId, +) { + span_lint_hir_and_then( + cx, + NEEDLESS_RETURN, + at, + lint_span, + "unneeded `return` statement", + |diag| { + let suggestions = std::iter::once((ret_span, replacement.to_string())) + .chain(semi_spans.into_iter().map(|span| (span, String::new()))) + .collect(); + + diag.multipart_suggestion_verbose(replacement.sugg_help(), suggestions, replacement.applicability()); + }, + ); +} + +// Go backwards while encountering whitespace and extend the given Span to that point. +fn extend_span_to_previous_non_ws(cx: &LateContext<'_>, sp: Span) -> Span { + if let Ok(prev_source) = cx.sess().source_map().span_to_prev_source(sp) { + let ws = [b' ', b'\t', b'\n']; + if let Some(non_ws_pos) = prev_source.bytes().rposition(|c| !ws.contains(&c)) { + let len = prev_source.len() - non_ws_pos - 1; + return sp.with_lo(sp.lo() - BytePos::from_usize(len)); + } + } + + sp +} diff --git a/clippy_lints/src/returns/needless_return_with_question_mark.rs b/clippy_lints/src/returns/needless_return_with_question_mark.rs new file mode 100644 index 000000000000..c05038cd1e50 --- /dev/null +++ b/clippy_lints/src/returns/needless_return_with_question_mark.rs @@ -0,0 +1,60 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::{is_from_proc_macro, is_inside_let_else, is_res_lang_ctor, path_res}; +use rustc_errors::Applicability; +use rustc_hir::LangItem::ResultErr; +use rustc_hir::{ExprKind, HirId, ItemKind, MatchSource, Node, OwnerNode, Stmt, StmtKind}; +use rustc_lint::{LateContext, LintContext}; +use rustc_middle::ty::adjustment::Adjust; + +use super::NEEDLESS_RETURN_WITH_QUESTION_MARK; + +pub(super) fn check_stmt<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { + if !stmt.span.in_external_macro(cx.sess().source_map()) + && let StmtKind::Semi(expr) = stmt.kind + && let ExprKind::Ret(Some(ret)) = expr.kind + // return Err(...)? desugars to a match + // over a Err(...).branch() + // which breaks down to a branch call, with the callee being + // the constructor of the Err variant + && let ExprKind::Match(maybe_cons, _, MatchSource::TryDesugar(_)) = ret.kind + && let ExprKind::Call(_, [maybe_result_err]) = maybe_cons.kind + && let ExprKind::Call(maybe_constr, _) = maybe_result_err.kind + && is_res_lang_ctor(cx, path_res(cx, maybe_constr), ResultErr) + + // Ensure this is not the final stmt, otherwise removing it would cause a compile error + && let OwnerNode::Item(item) = cx.tcx.hir_owner_node(cx.tcx.hir_get_parent_item(expr.hir_id)) + && let ItemKind::Fn { body, .. } = item.kind + && let block = cx.tcx.hir_body(body).value + && let ExprKind::Block(block, _) = block.kind + && !is_inside_let_else(cx.tcx, expr) + && let [.., final_stmt] = block.stmts + && final_stmt.hir_id != stmt.hir_id + && !is_from_proc_macro(cx, expr) + && !stmt_needs_never_type(cx, stmt.hir_id) + { + span_lint_and_sugg( + cx, + NEEDLESS_RETURN_WITH_QUESTION_MARK, + expr.span.until(ret.span), + "unneeded `return` statement with `?` operator", + "remove it", + String::new(), + Applicability::MachineApplicable, + ); + } +} + +/// Checks if a return statement is "needed" in the middle of a block, or if it can be removed. +/// This is the case when the enclosing block expression is coerced to some other type, +/// which only works because of the never-ness of `return` expressions +fn stmt_needs_never_type(cx: &LateContext<'_>, stmt_hir_id: HirId) -> bool { + cx.tcx + .hir_parent_iter(stmt_hir_id) + .find_map(|(_, node)| if let Node::Expr(expr) = node { Some(expr) } else { None }) + .is_some_and(|e| { + cx.typeck_results() + .expr_adjustments(e) + .iter() + .any(|adjust| adjust.target != cx.tcx.types.unit && matches!(adjust.kind, Adjust::NeverToAny)) + }) +} diff --git a/clippy_lints/src/semicolon_block.rs b/clippy_lints/src/semicolon_block.rs index 1dea8f17c34b..371d62a06849 100644 --- a/clippy_lints/src/semicolon_block.rs +++ b/clippy_lints/src/semicolon_block.rs @@ -1,5 +1,6 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::SpanRangeExt; use rustc_errors::Applicability; use rustc_hir::{Block, Expr, ExprKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -82,6 +83,19 @@ impl SemicolonBlock { let insert_span = tail.span.source_callsite().shrink_to_hi(); let remove_span = semi_span.with_lo(block.span.hi()); + // If the block is surrounded by parens (`({ 0 });`), the author probably knows what + // they're doing and why, so don't get in their way. + // + // This has the additional benefit of stopping the block being parsed as a function call: + // ``` + // fn foo() { + // ({ 0 }); // if we remove this `;`, this will parse as a `({ 0 })(5);` function call + // (5); + // } + if remove_span.check_source_text(cx, |src| src.contains(')')) { + return; + } + if self.semicolon_inside_block_ignore_singleline && get_line(cx, remove_span) == get_line(cx, insert_span) { return; } diff --git a/clippy_lints/src/size_of_ref.rs b/clippy_lints/src/size_of_ref.rs index 60d923bcd77e..606e852aae9e 100644 --- a/clippy_lints/src/size_of_ref.rs +++ b/clippy_lints/src/size_of_ref.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::{path_def_id, peel_middle_ty_refs}; +use clippy_utils::path_def_id; +use clippy_utils::ty::peel_and_count_ty_refs; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -59,7 +60,7 @@ impl LateLintPass<'_> for SizeOfRef { && let Some(def_id) = path_def_id(cx, path) && cx.tcx.is_diagnostic_item(sym::mem_size_of_val, def_id) && let arg_ty = cx.typeck_results().expr_ty(arg) - && peel_middle_ty_refs(arg_ty).1 > 1 + && peel_and_count_ty_refs(arg_ty).1 > 1 { span_lint_and_help( cx, diff --git a/clippy_lints/src/toplevel_ref_arg.rs b/clippy_lints/src/toplevel_ref_arg.rs new file mode 100644 index 000000000000..074b79263d37 --- /dev/null +++ b/clippy_lints/src/toplevel_ref_arg.rs @@ -0,0 +1,119 @@ +use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; +use clippy_utils::source::{snippet, snippet_with_context}; +use clippy_utils::sugg::Sugg; +use clippy_utils::{is_lint_allowed, iter_input_pats}; +use rustc_errors::Applicability; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{BindingMode, Body, ByRef, FnDecl, Mutability, PatKind, Stmt, StmtKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::declare_lint_pass; +use rustc_span::Span; +use rustc_span::def_id::LocalDefId; + +use crate::ref_patterns::REF_PATTERNS; + +declare_clippy_lint! { + /// ### What it does + /// Checks for function arguments and let bindings denoted as + /// `ref`. + /// + /// ### Why is this bad? + /// The `ref` declaration makes the function take an owned + /// value, but turns the argument into a reference (which means that the value + /// is destroyed when exiting the function). This adds not much value: either + /// take a reference type, or take an owned value and create references in the + /// body. + /// + /// For let bindings, `let x = &foo;` is preferred over `let ref x = foo`. The + /// type of `x` is more obvious with the former. + /// + /// ### Known problems + /// If the argument is dereferenced within the function, + /// removing the `ref` will lead to errors. This can be fixed by removing the + /// dereferences, e.g., changing `*x` to `x` within the function. + /// + /// ### Example + /// ```no_run + /// fn foo(ref _x: u8) {} + /// ``` + /// + /// Use instead: + /// ```no_run + /// fn foo(_x: &u8) {} + /// ``` + #[clippy::version = "pre 1.29.0"] + pub TOPLEVEL_REF_ARG, + style, + "an entire binding declared as `ref`, in a function argument or a `let` statement" +} + +declare_lint_pass!(ToplevelRefArg => [TOPLEVEL_REF_ARG]); + +impl<'tcx> LateLintPass<'tcx> for ToplevelRefArg { + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + k: FnKind<'tcx>, + decl: &'tcx FnDecl<'_>, + body: &'tcx Body<'_>, + _: Span, + _: LocalDefId, + ) { + if !matches!(k, FnKind::Closure) { + for arg in iter_input_pats(decl, body) { + if let PatKind::Binding(BindingMode(ByRef::Yes(_), _), ..) = arg.pat.kind + && is_lint_allowed(cx, REF_PATTERNS, arg.pat.hir_id) + && !arg.span.in_external_macro(cx.tcx.sess.source_map()) + { + span_lint_hir( + cx, + TOPLEVEL_REF_ARG, + arg.hir_id, + arg.pat.span, + "`ref` directly on a function parameter does not prevent taking ownership of the passed argument. \ + Consider using a reference type instead", + ); + } + } + } + } + + fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { + if let StmtKind::Let(local) = stmt.kind + && let PatKind::Binding(BindingMode(ByRef::Yes(mutabl), _), .., name, None) = local.pat.kind + && let Some(init) = local.init + // Do not emit if clippy::ref_patterns is not allowed to avoid having two lints for the same issue. + && is_lint_allowed(cx, REF_PATTERNS, local.pat.hir_id) + && !stmt.span.in_external_macro(cx.tcx.sess.source_map()) + { + let ctxt = local.span.ctxt(); + let mut app = Applicability::MachineApplicable; + let sugg_init = Sugg::hir_with_context(cx, init, ctxt, "..", &mut app); + let (mutopt, initref) = match mutabl { + Mutability::Mut => ("mut ", sugg_init.mut_addr()), + Mutability::Not => ("", sugg_init.addr()), + }; + let tyopt = if let Some(ty) = local.ty { + let ty_snip = snippet_with_context(cx, ty.span, ctxt, "_", &mut app).0; + format!(": &{mutopt}{ty_snip}") + } else { + String::new() + }; + span_lint_hir_and_then( + cx, + TOPLEVEL_REF_ARG, + init.hir_id, + local.pat.span, + "`ref` on an entire `let` pattern is discouraged, take a reference with `&` instead", + |diag| { + diag.span_suggestion( + stmt.span, + "try", + format!("let {name}{tyopt} = {initref};", name = snippet(cx, name.span, ".."),), + app, + ); + }, + ); + } + } +} diff --git a/clippy_lints/src/transmute/transmute_ptr_to_ref.rs b/clippy_lints/src/transmute/transmute_ptr_to_ref.rs index e58212fae15c..e67ab6a73d26 100644 --- a/clippy_lints/src/transmute/transmute_ptr_to_ref.rs +++ b/clippy_lints/src/transmute/transmute_ptr_to_ref.rs @@ -28,16 +28,27 @@ pub(super) fn check<'tcx>( format!("transmute from a pointer type (`{from_ty}`) to a reference type (`{to_ty}`)"), |diag| { let arg = sugg::Sugg::hir(cx, arg, ".."); - let (deref, cast) = if *mutbl == Mutability::Mut { - ("&mut *", "*mut") - } else { - ("&*", "*const") + let (deref, cast) = match mutbl { + Mutability::Mut => ("&mut *", "*mut"), + Mutability::Not => ("&*", "*const"), }; let mut app = Applicability::MachineApplicable; let sugg = if let Some(ty) = get_explicit_type(path) { let ty_snip = snippet_with_applicability(cx, ty.span, "..", &mut app); - if msrv.meets(cx, msrvs::POINTER_CAST) { + if !to_ref_ty.is_sized(cx.tcx, cx.typing_env()) { + // We can't suggest `.cast()`, because that requires `to_ref_ty` to be Sized. + if from_ptr_ty.has_erased_regions() { + // We can't suggest `as *mut/const () as *mut/const to_ref_ty`, because the former is a + // thin pointer, whereas the latter is a wide pointer, due of its pointee, `to_ref_ty`, + // being !Sized. + // + // The only remaining option is be to skip `*mut/const ()`, but that might not be safe + // to do because of the erased regions in `from_ptr_ty`, so reduce the applicability. + app = Applicability::MaybeIncorrect; + } + sugg::make_unop(deref, arg.as_ty(format!("{cast} {ty_snip}"))).to_string() + } else if msrv.meets(cx, msrvs::POINTER_CAST) { format!("{deref}{}.cast::<{ty_snip}>()", arg.maybe_paren()) } else if from_ptr_ty.has_erased_regions() { sugg::make_unop(deref, arg.as_ty(format!("{cast} () as {cast} {ty_snip}"))).to_string() @@ -45,15 +56,22 @@ pub(super) fn check<'tcx>( sugg::make_unop(deref, arg.as_ty(format!("{cast} {ty_snip}"))).to_string() } } else if *from_ptr_ty == *to_ref_ty { - if from_ptr_ty.has_erased_regions() { - if msrv.meets(cx, msrvs::POINTER_CAST) { - format!("{deref}{}.cast::<{to_ref_ty}>()", arg.maybe_paren()) - } else { - sugg::make_unop(deref, arg.as_ty(format!("{cast} () as {cast} {to_ref_ty}"))) - .to_string() - } - } else { + if !from_ptr_ty.has_erased_regions() { sugg::make_unop(deref, arg).to_string() + } else if !to_ref_ty.is_sized(cx.tcx, cx.typing_env()) { + // 1. We can't suggest `.cast()`, because that requires `to_ref_ty` to be Sized. + // 2. We can't suggest `as *mut/const () as *mut/const to_ref_ty`, because the former is a + // thin pointer, whereas the latter is a wide pointer, due of its pointee, `to_ref_ty`, + // being !Sized. + // + // The only remaining option is be to skip `*mut/const ()`, but that might not be safe to do + // because of the erased regions in `from_ptr_ty`, so reduce the applicability. + app = Applicability::MaybeIncorrect; + sugg::make_unop(deref, arg.as_ty(format!("{cast} {to_ref_ty}"))).to_string() + } else if msrv.meets(cx, msrvs::POINTER_CAST) { + format!("{deref}{}.cast::<{to_ref_ty}>()", arg.maybe_paren()) + } else { + sugg::make_unop(deref, arg.as_ty(format!("{cast} () as {cast} {to_ref_ty}"))).to_string() } } else { sugg::make_unop(deref, arg.as_ty(format!("{cast} {to_ref_ty}"))).to_string() diff --git a/clippy_lints/src/transmute/transmute_ref_to_ref.rs b/clippy_lints/src/transmute/transmute_ref_to_ref.rs index 6aeb22d41a7d..70c2a73ce6ef 100644 --- a/clippy_lints/src/transmute/transmute_ref_to_ref.rs +++ b/clippy_lints/src/transmute/transmute_ref_to_ref.rs @@ -45,7 +45,9 @@ pub(super) fn check<'tcx>( Applicability::MaybeIncorrect, ); triggered = true; - } else if (cx.tcx.erase_and_anonymize_regions(from_ty) != cx.tcx.erase_and_anonymize_regions(to_ty)) && !const_context { + } else if (cx.tcx.erase_and_anonymize_regions(from_ty) != cx.tcx.erase_and_anonymize_regions(to_ty)) + && !const_context + { span_lint_and_then( cx, TRANSMUTE_PTR_TO_PTR, diff --git a/clippy_lints/src/uninit_vec.rs b/clippy_lints/src/uninit_vec.rs index cee4a53f03cb..51116b5eba9e 100644 --- a/clippy_lints/src/uninit_vec.rs +++ b/clippy_lints/src/uninit_vec.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::higher::{VecInitKind, get_vec_init_kind}; use clippy_utils::ty::{is_type_diagnostic_item, is_uninit_value_valid_for_ty}; use clippy_utils::{SpanlessEq, is_integer_literal, is_lint_allowed, path_to_local_id, peel_hir_expr_while, sym}; @@ -95,16 +95,13 @@ fn handle_uninit_vec_pair<'tcx>( // Check T of Vec if !is_uninit_value_valid_for_ty(cx, args.type_at(0)) { - // FIXME: #7698, false positive of the internal lints - #[expect(clippy::collapsible_span_lint_calls)] - span_lint_and_then( + span_lint_and_help( cx, UNINIT_VEC, vec![call_span, maybe_init_or_reserve.span], "calling `set_len()` immediately after reserving a buffer creates uninitialized values", - |diag| { - diag.help("initialize the buffer or wrap the content in `MaybeUninit`"); - }, + None, + "initialize the buffer or wrap the content in `MaybeUninit`", ); } } else { diff --git a/clippy_lints/src/unused_peekable.rs b/clippy_lints/src/unused_peekable.rs index 1f5351e32aac..5224b62e9fc7 100644 --- a/clippy_lints/src/unused_peekable.rs +++ b/clippy_lints/src/unused_peekable.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable}; +use clippy_utils::ty::{is_type_diagnostic_item, peel_and_count_ty_refs}; use clippy_utils::{fn_def_id, is_trait_method, path_to_local_id, peel_ref_operators, sym}; use rustc_ast::Mutability; use rustc_hir::intravisit::{Visitor, walk_expr}; @@ -61,7 +61,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedPeekable { && let Some(init) = local.init && !init.span.from_expansion() && let Some(ty) = cx.typeck_results().expr_ty_opt(init) - && let (ty, _, Mutability::Mut) = peel_mid_ty_refs_is_mutable(ty) + && let (ty, _, None | Some(Mutability::Mut)) = peel_and_count_ty_refs(ty) && is_type_diagnostic_item(cx, ty, sym::IterPeekable) { let mut vis = PeekableVisitor::new(cx, binding); @@ -211,7 +211,7 @@ impl<'tcx> Visitor<'tcx> for PeekableVisitor<'_, 'tcx> { fn arg_is_mut_peekable(cx: &LateContext<'_>, arg: &Expr<'_>) -> bool { if let Some(ty) = cx.typeck_results().expr_ty_opt(arg) - && let (ty, _, Mutability::Mut) = peel_mid_ty_refs_is_mutable(ty) + && let (ty, _, None | Some(Mutability::Mut)) = peel_and_count_ty_refs(ty) && is_type_diagnostic_item(cx, ty, sym::IterPeekable) { true diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index 490da4f1e037..34dfe5b6546f 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -292,6 +292,7 @@ impl<'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'_, 'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { // Shouldn't lint when `expr` is in macro. if expr.span.in_external_macro(self.cx.tcx.sess.source_map()) { + walk_expr(self, expr); return; } // Skip checking inside closures since they are visited through `Unwrap::check_fn()` already. diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 8252e6d48694..9d5be922f43f 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -2,20 +2,21 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::is_from_proc_macro; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::ty::{same_type_and_consts, ty_from_hir_ty}; +use clippy_utils::ty::{same_type_modulo_regions, ty_from_hir_ty}; 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::{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, PatExpr, PatExprKind, PatKind, Path, QPath, Ty, TyKind, + self as hir, AmbigArg, Expr, ExprKind, FnRetTy, FnSig, GenericArgsParentheses, GenericParamKind, HirId, Impl, + ImplItemKind, Item, ItemKind, Pat, PatExpr, PatExprKind, PatKind, Path, QPath, Ty, TyKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::Ty as MiddleTy; use rustc_session::impl_lint_pass; use rustc_span::Span; +use std::iter; declare_clippy_lint! { /// ### What it does @@ -101,17 +102,11 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { let types_to_skip = generics .params .iter() - .filter_map(|param| match param { - GenericParam { - kind: - GenericParamKind::Const { - ty: Ty { hir_id, .. }, .. - }, - .. - } => Some(*hir_id), + .filter_map(|param| match param.kind { + GenericParamKind::Const { ty, .. } => Some(ty.hir_id), _ => None, }) - .chain(std::iter::once(self_ty.hir_id)) + .chain([self_ty.hir_id]) .collect(); StackItem::Check { impl_id: item.owner_id.def_id, @@ -209,11 +204,11 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { && !types_to_skip.contains(&hir_ty.hir_id) && let ty = ty_from_hir_ty(cx, hir_ty.as_unambig_ty()) && let impl_ty = cx.tcx.type_of(impl_id).instantiate_identity() - && same_type_and_consts(ty, impl_ty) + && same_type_modulo_regions(ty, impl_ty) // Ensure the type we encounter and the one from the impl have the same lifetime parameters. It may be that - // the lifetime parameters of `ty` are elided (`impl<'a> Foo<'a> { fn new() -> Self { Foo{..} } }`, in + // the lifetime parameters of `ty` are elided (`impl<'a> Foo<'a> { fn new() -> Self { Foo{..} } }`), in // which case we must still trigger the lint. - && (has_no_lifetime(ty) || same_lifetimes(ty, impl_ty)) + && same_lifetimes(ty, impl_ty) && self.msrv.meets(cx, msrvs::TYPE_ALIAS_ENUM_VARIANTS) { span_lint(cx, hir_ty.span); @@ -226,18 +221,16 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { && cx.typeck_results().expr_ty(expr) == cx.tcx.type_of(impl_id).instantiate_identity() && self.msrv.meets(cx, msrvs::TYPE_ALIAS_ENUM_VARIANTS) { - } else { - return; - } - match expr.kind { - ExprKind::Struct(QPath::Resolved(_, path), ..) => check_path(cx, path), - ExprKind::Call(fun, _) => { - if let ExprKind::Path(QPath::Resolved(_, path)) = fun.kind { - check_path(cx, path); - } - }, - ExprKind::Path(QPath::Resolved(_, path)) => check_path(cx, path), - _ => (), + match expr.kind { + ExprKind::Struct(QPath::Resolved(_, path), ..) => check_path(cx, path), + ExprKind::Call(fun, _) => { + if let ExprKind::Path(QPath::Resolved(_, path)) = fun.kind { + check_path(cx, path); + } + }, + ExprKind::Path(QPath::Resolved(_, path)) => check_path(cx, path), + _ => (), + } } } @@ -307,36 +300,20 @@ fn lint_path_to_variant(cx: &LateContext<'_>, path: &Path<'_>) { } } -/// Returns `true` if types `a` and `b` have the same lifetime parameters, otherwise returns -/// `false`. +/// Checks whether types `a` and `b` have the same lifetime parameters. /// /// This function does not check that types `a` and `b` are the same types. fn same_lifetimes<'tcx>(a: MiddleTy<'tcx>, b: MiddleTy<'tcx>) -> bool { use rustc_middle::ty::{Adt, GenericArgKind}; - match (&a.kind(), &b.kind()) { - (&Adt(_, args_a), &Adt(_, args_b)) => { - args_a - .iter() - .zip(args_b.iter()) - .all(|(arg_a, arg_b)| match (arg_a.kind(), arg_b.kind()) { - // TODO: Handle inferred lifetimes - (GenericArgKind::Lifetime(inner_a), GenericArgKind::Lifetime(inner_b)) => inner_a == inner_b, - (GenericArgKind::Type(type_a), GenericArgKind::Type(type_b)) => same_lifetimes(type_a, type_b), - _ => true, - }) + match (a.kind(), b.kind()) { + (Adt(_, args_a), Adt(_, args_b)) => { + iter::zip(*args_a, *args_b).all(|(arg_a, arg_b)| match (arg_a.kind(), arg_b.kind()) { + // TODO: Handle inferred lifetimes + (GenericArgKind::Lifetime(inner_a), GenericArgKind::Lifetime(inner_b)) => inner_a == inner_b, + (GenericArgKind::Type(type_a), GenericArgKind::Type(type_b)) => same_lifetimes(type_a, type_b), + _ => true, + }) }, _ => a == b, } } - -/// Returns `true` if `ty` has no lifetime parameter, otherwise returns `false`. -fn has_no_lifetime(ty: MiddleTy<'_>) -> bool { - use rustc_middle::ty::{Adt, GenericArgKind}; - match ty.kind() { - &Adt(_, args) => !args - .iter() - // TODO: Handle inferred lifetimes - .any(|arg| matches!(arg.kind(), GenericArgKind::Lifetime(..))), - _ => true, - } -} diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index e45f884cfcbc..1b137017ecb4 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, snippet_with_context}; use clippy_utils::sugg::{DiagExt as _, Sugg}; -use clippy_utils::ty::{get_type_diagnostic_name, is_copy, is_type_diagnostic_item, same_type_and_consts}; +use clippy_utils::ty::{get_type_diagnostic_name, is_copy, is_type_diagnostic_item, same_type_modulo_regions}; use clippy_utils::{ get_parent_expr, is_inherent_method_call, is_trait_item, is_trait_method, is_ty_alias, path_to_local, sym, }; @@ -184,7 +184,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { && (is_trait_item(cx, arg, sym::Into) || is_trait_item(cx, arg, sym::From)) && let ty::FnDef(_, args) = cx.typeck_results().expr_ty(arg).kind() && let &[from_ty, to_ty] = args.into_type_list(cx.tcx).as_slice() - && same_type_and_consts(from_ty, to_ty) + && same_type_modulo_regions(from_ty, to_ty) { span_lint_and_then( cx, @@ -207,7 +207,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { if is_trait_method(cx, e, sym::Into) && name.ident.name == sym::into { let a = cx.typeck_results().expr_ty(e); let b = cx.typeck_results().expr_ty(recv); - if same_type_and_consts(a, b) { + if same_type_modulo_regions(a, b) { let mut app = Applicability::MachineApplicable; let sugg = snippet_with_context(cx, recv.span, e.span.ctxt(), "", &mut app).0; span_lint_and_sugg( @@ -324,7 +324,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { // If the types are identical then .into_iter() can be removed, unless the type // implements Copy, in which case .into_iter() returns a copy of the receiver and // cannot be safely omitted. - if same_type_and_consts(a, b) && !is_copy(cx, b) { + if same_type_modulo_regions(a, b) && !is_copy(cx, b) { // Below we check if the parent method call meets the following conditions: // 1. First parameter is `&mut self` (requires mutable reference) // 2. Second parameter implements the `FnMut` trait (e.g., Iterator::any) @@ -371,7 +371,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { && is_type_diagnostic_item(cx, a, sym::Result) && let ty::Adt(_, args) = a.kind() && let Some(a_type) = args.types().next() - && same_type_and_consts(a_type, b) + && same_type_modulo_regions(a_type, b) { span_lint_and_help( cx, @@ -396,7 +396,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { && is_type_diagnostic_item(cx, a, sym::Result) && let ty::Adt(_, args) = a.kind() && let Some(a_type) = args.types().next() - && same_type_and_consts(a_type, b) + && same_type_modulo_regions(a_type, b) { let hint = format!("consider removing `{}()`", snippet(cx, path.span, "TryFrom::try_from")); span_lint_and_help( @@ -407,7 +407,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { None, hint, ); - } else if name == sym::from_fn && same_type_and_consts(a, b) { + } else if name == sym::from_fn && same_type_modulo_regions(a, b) { let mut app = Applicability::MachineApplicable; let sugg = Sugg::hir_with_context(cx, arg, e.span.ctxt(), "", &mut app).maybe_paren(); let sugg_msg = format!("consider removing `{}()`", snippet(cx, path.span, "From::from")); diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index bdf7431f29f2..d58b47bf6deb 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_utils" -version = "0.1.91" +version = "0.1.92" edition = "2024" description = "Helpful tools for writing lints, provided as they are used in Clippy" repository = "https://github.com/rust-lang/rust-clippy" diff --git a/clippy_utils/README.md b/clippy_utils/README.md index e01f563c49e7..2c66fdc73f53 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-09-04 +nightly-2025-09-18 ``` diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index 1a25c90d735e..948a7203402d 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -13,20 +13,24 @@ //! if the span is not from a `macro_rules` based macro. use rustc_abi::ExternAbi; +use rustc_ast as ast; use rustc_ast::AttrStyle; -use rustc_ast::ast::{AttrKind, Attribute, IntTy, LitIntType, LitKind, StrStyle, TraitObjectSyntax, UintTy}; +use rustc_ast::ast::{ + AttrKind, Attribute, GenericArgs, IntTy, LitIntType, LitKind, StrStyle, TraitObjectSyntax, UintTy, +}; use rustc_ast::token::CommentKind; use rustc_hir::intravisit::FnKind; use rustc_hir::{ Block, BlockCheckMode, Body, Closure, Destination, Expr, ExprKind, FieldDef, FnHeader, FnRetTy, HirId, Impl, - ImplItem, ImplItemImplKind, ImplItemKind, IsAuto, Item, ItemKind, Lit, LoopSource, MatchSource, MutTy, Node, Path, QPath, Safety, - TraitImplHeader, TraitItem, TraitItemKind, Ty, TyKind, UnOp, UnsafeSource, Variant, VariantData, YieldSource, + ImplItem, ImplItemImplKind, ImplItemKind, IsAuto, Item, ItemKind, Lit, LoopSource, MatchSource, MutTy, Node, Path, + QPath, Safety, TraitImplHeader, TraitItem, TraitItemKind, Ty, TyKind, UnOp, UnsafeSource, Variant, VariantData, + YieldSource, }; use rustc_lint::{EarlyContext, LateContext, LintContext}; use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_span::symbol::{Ident, kw}; -use rustc_span::{Span, Symbol}; +use rustc_span::{Span, Symbol, sym}; /// The search pattern to look for. Used by `span_matches_pat` #[derive(Clone)] @@ -289,7 +293,7 @@ fn impl_item_search_pat(item: &ImplItem<'_>) -> (Pat, Pat) { && !vis_span.is_empty() { start_pat = Pat::Str("pub"); - }; + } (start_pat, end_pat) } @@ -321,14 +325,17 @@ fn fn_kind_pat(tcx: TyCtxt<'_>, kind: &FnKind<'_>, body: &Body<'_>, hir_id: HirI }; match tcx.hir_node(hir_id) { Node::Item(Item { vis_span, .. }) - | Node::ImplItem(ImplItem { impl_kind: ImplItemImplKind::Inherent { vis_span, .. }, .. }) => { + | Node::ImplItem(ImplItem { + impl_kind: ImplItemImplKind::Inherent { vis_span, .. }, + .. + }) => { if !vis_span.is_empty() { - start_pat = Pat::Str("pub") + start_pat = Pat::Str("pub"); } }, Node::ImplItem(_) | Node::TraitItem(_) => {}, _ => start_pat = Pat::Str(""), - }; + } (start_pat, end_pat) } @@ -403,6 +410,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::UnsafeBinder(binder_ty) => (Pat::Str("unsafe"), ty_search_pat(binder_ty.inner_ty).1), TyKind::TraitObject(_, tagged_ptr) if let TraitObjectSyntax::Dyn = tagged_ptr.tag() => { (Pat::Str("dyn"), Pat::Str("")) }, @@ -411,6 +419,127 @@ fn ty_search_pat(ty: &Ty<'_>) -> (Pat, Pat) { } } +fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) { + use ast::{Extern, FnRetTy, MutTy, Safety, TraitObjectSyntax, TyKind}; + + match &ty.kind { + TyKind::Slice(..) | TyKind::Array(..) => (Pat::Str("["), Pat::Str("]")), + TyKind::Ptr(MutTy { ty, .. }) => (Pat::Str("*"), ast_ty_search_pat(ty).1), + TyKind::Ref(_, MutTy { ty, .. }) | TyKind::PinnedRef(_, MutTy { ty, .. }) => { + (Pat::Str("&"), ast_ty_search_pat(ty).1) + }, + TyKind::FnPtr(fn_ptr) => ( + if let Safety::Unsafe(_) = fn_ptr.safety { + Pat::Str("unsafe") + } else if let Extern::Explicit(strlit, _) = fn_ptr.ext + && strlit.symbol == sym::rust + { + Pat::MultiStr(&["fn", "extern"]) + } else { + Pat::Str("extern") + }, + match &fn_ptr.decl.output { + FnRetTy::Default(_) => { + if let [.., param] = &*fn_ptr.decl.inputs { + ast_ty_search_pat(¶m.ty).1 + } else { + Pat::Str("(") + } + }, + FnRetTy::Ty(ty) => ast_ty_search_pat(ty).1, + }, + ), + TyKind::Never => (Pat::Str("!"), Pat::Str("!")), + // Parenthesis are trimmed from the text before the search patterns are matched. + // See: `span_matches_pat` + TyKind::Tup(tup) => match &**tup { + [] => (Pat::Str(")"), Pat::Str("(")), + [ty] => ast_ty_search_pat(ty), + [head, .., tail] => (ast_ty_search_pat(head).0, ast_ty_search_pat(tail).1), + }, + TyKind::ImplTrait(..) => (Pat::Str("impl"), Pat::Str("")), + TyKind::Path(qself_path, path) => { + let start = if qself_path.is_some() { + Pat::Str("<") + } else if let Some(first) = path.segments.first() { + ident_search_pat(first.ident).0 + } else { + // this shouldn't be possible, but sure + Pat::Str("") + }; + let end = if let Some(last) = path.segments.last() { + match last.args.as_deref() { + // last `>` in `std::foo::Bar` + Some(GenericArgs::AngleBracketed(_)) => Pat::Str(">"), + Some(GenericArgs::Parenthesized(par_args)) => match &par_args.output { + FnRetTy::Default(_) => { + if let Some(last) = par_args.inputs.last() { + // `B` in `(A, B)` -- `)` gets stripped + ast_ty_search_pat(last).1 + } else { + // `(` in `()` -- `)` gets stripped + Pat::Str("(") + } + }, + // `C` in `(A, B) -> C` + FnRetTy::Ty(ty) => ast_ty_search_pat(ty).1, + }, + // last `..` in `(..)` -- `)` gets stripped + Some(GenericArgs::ParenthesizedElided(_)) => Pat::Str(".."), + // `bar` in `std::foo::bar` + None => ident_search_pat(last.ident).1, + } + } else { + // this shouldn't be possible, but sure + #[allow( + clippy::collapsible_else_if, + reason = "we want to keep these cases together, since they are both impossible" + )] + if qself_path.is_some() { + // last `>` in `` + Pat::Str(">") + } else { + Pat::Str("") + } + }; + (start, end) + }, + TyKind::Infer => (Pat::Str("_"), Pat::Str("_")), + TyKind::Paren(ty) => ast_ty_search_pat(ty), + TyKind::UnsafeBinder(binder_ty) => (Pat::Str("unsafe"), ast_ty_search_pat(&binder_ty.inner_ty).1), + TyKind::TraitObject(_, trait_obj_syntax) => { + if let TraitObjectSyntax::Dyn = trait_obj_syntax { + (Pat::Str("dyn"), Pat::Str("")) + } else { + // NOTE: `TraitObject` is incomplete. It will always return true then. + (Pat::Str(""), Pat::Str("")) + } + }, + TyKind::MacCall(mac_call) => { + let start = if let Some(first) = mac_call.path.segments.first() { + ident_search_pat(first.ident).0 + } else { + Pat::Str("") + }; + (start, Pat::Str("")) + }, + + // implicit, so has no contents to match against + TyKind::ImplicitSelf + + // experimental + |TyKind::Pat(..) + + // unused + | TyKind::CVarArgs + | TyKind::Typeof(_) + + // placeholder + | TyKind::Dummy + | TyKind::Err(_) => (Pat::Str(""), Pat::Str("")), + } +} + fn ident_search_pat(ident: Ident) -> (Pat, Pat) { (Pat::Sym(ident.name), Pat::Sym(ident.name)) } @@ -445,6 +574,7 @@ impl_with_search_pat!((_cx: LateContext<'tcx>, self: Lit) => lit_search_pat(&sel impl_with_search_pat!((_cx: LateContext<'tcx>, self: Path<'_>) => path_search_pat(self)); impl_with_search_pat!((_cx: EarlyContext<'tcx>, self: Attribute) => attr_search_pat(self)); +impl_with_search_pat!((_cx: EarlyContext<'tcx>, self: ast::Ty) => ast_ty_search_pat(self)); impl<'cx> WithSearchPat<'cx> for (&FnKind<'cx>, &Body<'cx>, HirId, Span) { type Context = LateContext<'cx>; diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 120ab2e717db..feadc0ecf659 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -329,13 +329,17 @@ pub fn is_wild(pat: &Pat<'_>) -> bool { matches!(pat.kind, PatKind::Wild) } -// Checks if arm has the form `None => None` -pub fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { - matches!( - arm.pat.kind, +/// Checks if the `pat` is `None`. +pub fn is_none_pattern(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { + matches!(pat.kind, PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), .. }) - if is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), OptionNone) - ) + if is_res_lang_ctor(cx, cx.qpath_res(qpath, pat.hir_id), OptionNone)) +} + +/// Checks if `arm` has the form `None => None`. +pub fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { + is_none_pattern(cx, arm.pat) + && matches!(peel_blocks(arm.body).kind, ExprKind::Path(qpath) if is_res_lang_ctor(cx, cx.qpath_res(&qpath, arm.body.hir_id), OptionNone)) } /// Checks if the given `QPath` belongs to a type alias. @@ -2374,15 +2378,12 @@ pub fn peel_hir_ty_refs<'a>(mut ty: &'a hir::Ty<'a>) -> (&'a hir::Ty<'a>, usize) } } -/// Peels off all references on the type. Returns the underlying type and the number of references -/// removed. -pub fn peel_middle_ty_refs(mut ty: Ty<'_>) -> (Ty<'_>, usize) { - let mut count = 0; - while let rustc_ty::Ref(_, dest_ty, _) = ty.kind() { - ty = *dest_ty; - count += 1; +/// Returns the base type for HIR references and pointers. +pub fn peel_hir_ty_refs_and_ptrs<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> { + match &ty.kind { + TyKind::Ptr(mut_ty) | TyKind::Ref(_, mut_ty) => peel_hir_ty_refs_and_ptrs(mut_ty.ty), + _ => ty, } - (ty, count) } /// Removes `AddrOf` operators (`&`) or deref operators (`*`), but only if a reference type is diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 896d607fbcdd..6e07ed9ffcc4 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -24,7 +24,7 @@ macro_rules! msrv_aliases { // names may refer to stabilized feature flags or library items msrv_aliases! { 1,88,0 { LET_CHAINS } - 1,87,0 { OS_STR_DISPLAY, INT_MIDPOINT, CONST_CHAR_IS_DIGIT, UNSIGNED_IS_MULTIPLE_OF } + 1,87,0 { OS_STR_DISPLAY, INT_MIDPOINT, CONST_CHAR_IS_DIGIT, UNSIGNED_IS_MULTIPLE_OF, INTEGER_SIGN_CAST } 1,85,0 { UINT_FLOAT_MIDPOINT, CONST_SIZE_OF_VAL } 1,84,0 { CONST_OPTION_AS_SLICE, MANUAL_DANGLING_PTR } 1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY, CONST_MUT_REFS, CONST_UNWRAP } @@ -73,7 +73,7 @@ msrv_aliases! { 1,29,0 { ITER_FLATTEN } 1,28,0 { FROM_BOOL, REPEAT_WITH, SLICE_FROM_REF } 1,27,0 { ITERATOR_TRY_FOLD } - 1,26,0 { RANGE_INCLUSIVE, STRING_RETAIN } + 1,26,0 { RANGE_INCLUSIVE, STRING_RETAIN, POINTER_ADD_SUB_METHODS } 1,24,0 { IS_ASCII_DIGIT, PTR_NULL } 1,18,0 { HASH_MAP_RETAIN, HASH_SET_RETAIN } 1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR } diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index e675291b6f3a..638d32903123 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -215,6 +215,11 @@ impl fmt::Display for SourceText { self.as_str().fmt(f) } } +impl fmt::Debug for SourceText { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.as_str().fmt(f) + } +} fn get_source_range(sm: &SourceMap, sp: Range) -> Option { let start = sm.lookup_byte_offset(sp.start); diff --git a/clippy_utils/src/sym.rs b/clippy_utils/src/sym.rs index 8033b74a8d25..7530d3bc7157 100644 --- a/clippy_utils/src/sym.rs +++ b/clippy_utils/src/sym.rs @@ -125,6 +125,7 @@ generate! { cycle, cyclomatic_complexity, de, + deprecated_in_future, diagnostics, disallowed_types, drain, diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index 3e41bce1dc4c..e4bc3b768294 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -11,7 +11,7 @@ use rustc_hir as hir; use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::{Expr, FnDecl, LangItem, TyKind, find_attr}; +use rustc_hir::{Expr, FnDecl, LangItem, find_attr}; use rustc_hir_analysis::lower_ty; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; @@ -43,13 +43,8 @@ pub use type_certainty::expr_type_is_certain; /// Lower a [`hir::Ty`] to a [`rustc_middle::ty::Ty`]. pub fn ty_from_hir_ty<'tcx>(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'tcx>) -> Ty<'tcx> { cx.maybe_typeck_results() - .and_then(|results| { - if results.hir_owner == hir_ty.hir_id.owner { - results.node_type_opt(hir_ty.hir_id) - } else { - None - } - }) + .filter(|results| results.hir_owner == hir_ty.hir_id.owner) + .and_then(|results| results.node_type_opt(hir_ty.hir_id)) .unwrap_or_else(|| lower_ty(cx.tcx, hir_ty)) } @@ -475,63 +470,50 @@ pub fn needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { needs_ordered_drop_inner(cx, ty, &mut FxHashSet::default()) } -/// Peels off all references on the type. Returns the underlying type, the number of references -/// removed, and whether the pointer is ultimately mutable or not. -pub fn peel_mid_ty_refs_is_mutable(ty: Ty<'_>) -> (Ty<'_>, usize, Mutability) { - fn f(ty: Ty<'_>, count: usize, mutability: Mutability) -> (Ty<'_>, usize, Mutability) { - match ty.kind() { - ty::Ref(_, ty, Mutability::Mut) => f(*ty, count + 1, mutability), - ty::Ref(_, ty, Mutability::Not) => f(*ty, count + 1, Mutability::Not), - _ => (ty, count, mutability), - } - } - f(ty, 0, Mutability::Mut) -} - -/// Returns `true` if the given type is an `unsafe` function. -pub fn type_is_unsafe_function<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { +/// Returns `true` if `ty` denotes an `unsafe fn`. +pub fn is_unsafe_fn<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { ty.is_fn() && ty.fn_sig(cx.tcx).safety().is_unsafe() } -/// Returns the base type for HIR references and pointers. -pub fn walk_ptrs_hir_ty<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> { - match ty.kind { - TyKind::Ptr(ref mut_ty) | TyKind::Ref(_, ref mut_ty) => walk_ptrs_hir_ty(mut_ty.ty), - _ => ty, +/// Peels off all references on the type. Returns the underlying type, the number of references +/// removed, and, if there were any such references, whether the pointer is ultimately mutable or +/// not. +pub fn peel_and_count_ty_refs(mut ty: Ty<'_>) -> (Ty<'_>, usize, Option) { + let mut count = 0; + let mut mutbl = None; + while let ty::Ref(_, dest_ty, m) = ty.kind() { + ty = *dest_ty; + count += 1; + mutbl.replace(mutbl.map_or(*m, |mutbl: Mutability| mutbl.min(*m))); } + (ty, count, mutbl) } -/// Returns the base type for references and raw pointers, and count reference -/// depth. -pub fn walk_ptrs_ty_depth(ty: Ty<'_>) -> (Ty<'_>, usize) { - fn inner(ty: Ty<'_>, depth: usize) -> (Ty<'_>, usize) { - match ty.kind() { - ty::Ref(_, ty, _) => inner(*ty, depth + 1), - _ => (ty, depth), - } - } - inner(ty, 0) -} - -/// Returns `true` if types `a` and `b` are same types having same `Const` generic args, -/// otherwise returns `false` -pub fn same_type_and_consts<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool { +/// Checks whether `a` and `b` are same types having same `Const` generic args, but ignores +/// lifetimes. +/// +/// For example, the function would return `true` for +/// - `u32` and `u32` +/// - `[u8; N]` and `[u8; M]`, if `N=M` +/// - `Option` and `Option`, if `same_type_modulo_regions(T, U)` holds +/// - `&'a str` and `&'b str` +/// +/// and `false` for: +/// - `Result` and `Result` +pub fn same_type_modulo_regions<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool { match (&a.kind(), &b.kind()) { (&ty::Adt(did_a, args_a), &ty::Adt(did_b, args_b)) => { if did_a != did_b { return false; } - args_a - .iter() - .zip(args_b.iter()) - .all(|(arg_a, arg_b)| match (arg_a.kind(), arg_b.kind()) { - (GenericArgKind::Const(inner_a), GenericArgKind::Const(inner_b)) => inner_a == inner_b, - (GenericArgKind::Type(type_a), GenericArgKind::Type(type_b)) => { - same_type_and_consts(type_a, type_b) - }, - _ => true, - }) + iter::zip(*args_a, *args_b).all(|(arg_a, arg_b)| match (arg_a.kind(), arg_b.kind()) { + (GenericArgKind::Const(inner_a), GenericArgKind::Const(inner_b)) => inner_a == inner_b, + (GenericArgKind::Type(type_a), GenericArgKind::Type(type_b)) => { + same_type_modulo_regions(type_a, type_b) + }, + _ => true, + }) }, _ => a == b, } diff --git a/declare_clippy_lint/Cargo.toml b/declare_clippy_lint/Cargo.toml index ec0e59e70549..4de7b5fb5924 100644 --- a/declare_clippy_lint/Cargo.toml +++ b/declare_clippy_lint/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "declare_clippy_lint" -version = "0.1.91" +version = "0.1.92" edition = "2024" repository = "https://github.com/rust-lang/rust-clippy" license = "MIT OR Apache-2.0" diff --git a/rust-toolchain.toml b/rust-toolchain.toml index ec2f24a0a6d8..9c102de44820 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2025-09-04" +channel = "nightly-2025-09-18" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" diff --git a/tests/no-profile-in-cargo-toml.rs b/tests/no-profile-in-cargo-toml.rs index 2ad9bfb75dee..1f8c4fae9b31 100644 --- a/tests/no-profile-in-cargo-toml.rs +++ b/tests/no-profile-in-cargo-toml.rs @@ -17,6 +17,9 @@ fn no_profile_in_cargo_toml() { // keep it fast and simple. for entry in WalkDir::new(".") .into_iter() + // Do not recurse into `target` as lintcheck might put some sources (and their + // `Cargo.toml`) there. + .filter_entry(|e| e.file_name() != "target") .filter_map(Result::ok) .filter(|e| e.file_name().to_str() == Some("Cargo.toml")) { diff --git a/tests/ui/ref_option/all/clippy.toml b/tests/ui-toml/ref_option/all/clippy.toml similarity index 100% rename from tests/ui/ref_option/all/clippy.toml rename to tests/ui-toml/ref_option/all/clippy.toml diff --git a/tests/ui/ref_option/private/clippy.toml b/tests/ui-toml/ref_option/private/clippy.toml similarity index 100% rename from tests/ui/ref_option/private/clippy.toml rename to tests/ui-toml/ref_option/private/clippy.toml diff --git a/tests/ui-toml/ref_option/ref_option.all.fixed b/tests/ui-toml/ref_option/ref_option.all.fixed new file mode 100644 index 000000000000..f8f097e9a75e --- /dev/null +++ b/tests/ui-toml/ref_option/ref_option.all.fixed @@ -0,0 +1,114 @@ +//@aux-build:../../ui/auxiliary/proc_macros.rs +//@revisions: private all +//@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/private +//@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/all + +#![allow(unused, clippy::needless_lifetimes, clippy::borrowed_box)] +#![warn(clippy::ref_option)] + +fn opt_u8(a: Option<&u8>) {} +//~^ ref_option +fn opt_gen(a: Option<&T>) {} +//~^ ref_option +fn opt_string(a: std::option::Option<&String>) {} +//~^ ref_option +fn ret_u8<'a>(p: &'a str) -> Option<&'a u8> { + //~^ ref_option + panic!() +} +fn ret_u8_static() -> Option<&'static u8> { + //~^ ref_option + panic!() +} +fn mult_string(a: Option<&String>, b: Option<&Vec>) {} +//~^ ref_option +fn ret_box<'a>() -> Option<&'a Box> { + //~^ ref_option + panic!() +} + +pub fn pub_opt_string(a: Option<&String>) {} +//~[all]^ ref_option +pub fn pub_mult_string(a: Option<&String>, b: Option<&Vec>) {} +//~[all]^ ref_option + +pub struct PubStruct; + +impl PubStruct { + pub fn pub_opt_params(&self, a: Option<&()>) {} + //~[all]^ ref_option + pub fn pub_opt_ret(&self) -> Option<&String> { + //~[all]^ ref_option + panic!() + } + + fn private_opt_params(&self, a: Option<&()>) {} + //~^ ref_option + fn private_opt_ret(&self) -> Option<&String> { + //~^ ref_option + panic!() + } +} + +// valid, don't change +fn mut_u8(a: &mut Option) {} +pub fn pub_mut_u8(a: &mut Option) {} + +// might be good to catch in the future +fn mut_u8_ref(a: &mut &Option) {} +pub fn pub_mut_u8_ref(a: &mut &Option) {} +fn lambdas() { + // Not handled for now, not sure if we should + let x = |a: &Option| {}; + let x = |a: &Option| -> &Option { panic!() }; +} + +pub mod external { + proc_macros::external!( + fn opt_u8(a: &Option) {} + fn ret_u8<'a>(p: &'a str) -> &'a Option { + panic!() + } + pub fn pub_opt_u8(a: &Option) {} + + pub struct PubStruct; + impl PubStruct { + pub fn pub_opt_params(&self, a: &Option<()>) {} + pub fn pub_opt_ret(&self) -> &Option { + panic!() + } + + fn private_opt_params(&self, a: &Option<()>) {} + fn private_opt_ret(&self) -> &Option { + panic!() + } + } + ); +} + +pub mod proc_macros { + proc_macros::with_span!( + span + + fn opt_u8(a: &Option) {} + fn ret_u8<'a>(p: &'a str) -> &'a Option { + panic!() + } + pub fn pub_opt_u8(a: &Option) {} + + pub struct PubStruct; + impl PubStruct { + pub fn pub_opt_params(&self, a: &Option<()>) {} + pub fn pub_opt_ret(&self) -> &Option { + panic!() + } + + fn private_opt_params(&self, a: &Option<()>) {} + fn private_opt_ret(&self) -> &Option { + panic!() + } + } + ); +} + +fn main() {} diff --git a/tests/ui/ref_option/ref_option.all.stderr b/tests/ui-toml/ref_option/ref_option.all.stderr similarity index 62% rename from tests/ui/ref_option/ref_option.all.stderr rename to tests/ui-toml/ref_option/ref_option.all.stderr index bd43c28336eb..45ce105e0308 100644 --- a/tests/ui/ref_option/ref_option.all.stderr +++ b/tests/ui-toml/ref_option/ref_option.all.stderr @@ -1,5 +1,5 @@ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:8:1 + --> tests/ui-toml/ref_option/ref_option.rs:9:1 | LL | fn opt_u8(a: &Option) {} | ^^^^^^^^^^^^^-----------^^^^ @@ -10,7 +10,7 @@ LL | fn opt_u8(a: &Option) {} = help: to override `-D warnings` add `#[allow(clippy::ref_option)]` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:10:1 + --> tests/ui-toml/ref_option/ref_option.rs:11:1 | LL | fn opt_gen(a: &Option) {} | ^^^^^^^^^^^^^^^^^----------^^^^ @@ -18,7 +18,7 @@ LL | fn opt_gen(a: &Option) {} | help: change this to: `Option<&T>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:12:1 + --> tests/ui-toml/ref_option/ref_option.rs:13:1 | LL | fn opt_string(a: &std::option::Option) {} | ^^^^^^^^^^^^^^^^^----------------------------^^^^ @@ -26,10 +26,10 @@ LL | fn opt_string(a: &std::option::Option) {} | help: change this to: `std::option::Option<&String>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:14:1 + --> tests/ui-toml/ref_option/ref_option.rs:15:1 | -LL | fn ret_string<'a>(p: &'a str) -> &'a Option { - | ^ -------------- help: change this to: `Option<&'a u8>` +LL | fn ret_u8<'a>(p: &'a str) -> &'a Option { + | ^ -------------- help: change this to: `Option<&'a u8>` | _| | | LL | | @@ -38,10 +38,10 @@ LL | | } | |_^ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:18:1 + --> tests/ui-toml/ref_option/ref_option.rs:19:1 | -LL | fn ret_string_static() -> &'static Option { - | ^ ------------------- help: change this to: `Option<&'static u8>` +LL | fn ret_u8_static() -> &'static Option { + | ^ ------------------- help: change this to: `Option<&'static u8>` | _| | | LL | | @@ -50,7 +50,7 @@ LL | | } | |_^ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:22:1 + --> tests/ui-toml/ref_option/ref_option.rs:23:1 | LL | fn mult_string(a: &Option, b: &Option>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -62,7 +62,7 @@ LL + fn mult_string(a: Option<&String>, b: Option<&Vec>) {} | error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:24:1 + --> tests/ui-toml/ref_option/ref_option.rs:25:1 | LL | fn ret_box<'a>() -> &'a Option> { | ^ ------------------- help: change this to: `Option<&'a Box>` @@ -74,7 +74,7 @@ LL | | } | |_^ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:29:1 + --> tests/ui-toml/ref_option/ref_option.rs:30:1 | LL | pub fn pub_opt_string(a: &Option) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^---------------^^^^ @@ -82,7 +82,7 @@ LL | pub fn pub_opt_string(a: &Option) {} | help: change this to: `Option<&String>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:31:1 + --> tests/ui-toml/ref_option/ref_option.rs:32:1 | LL | pub fn pub_mult_string(a: &Option, b: &Option>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -94,39 +94,7 @@ LL + pub fn pub_mult_string(a: Option<&String>, b: Option<&Vec>) {} | error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:35:5 - | -LL | fn pub_trait_opt(&self, a: &Option>); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^----------------^^ - | | - | help: change this to: `Option<&Vec>` - -error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:37:5 - | -LL | fn pub_trait_ret(&self) -> &Option>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^----------------^ - | | - | help: change this to: `Option<&Vec>` - -error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:42:5 - | -LL | fn trait_opt(&self, a: &Option); - | ^^^^^^^^^^^^^^^^^^^^^^^---------------^^ - | | - | help: change this to: `Option<&String>` - -error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:44:5 - | -LL | fn trait_ret(&self) -> &Option; - | ^^^^^^^^^^^^^^^^^^^^^^^---------------^ - | | - | help: change this to: `Option<&String>` - -error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:51:5 + --> tests/ui-toml/ref_option/ref_option.rs:38:5 | LL | pub fn pub_opt_params(&self, a: &Option<()>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-----------^^^^ @@ -134,7 +102,7 @@ LL | pub fn pub_opt_params(&self, a: &Option<()>) {} | help: change this to: `Option<&()>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:53:5 + --> tests/ui-toml/ref_option/ref_option.rs:40:5 | LL | pub fn pub_opt_ret(&self) -> &Option { | ^ --------------- help: change this to: `Option<&String>` @@ -146,7 +114,7 @@ LL | | } | |_____^ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:58:5 + --> tests/ui-toml/ref_option/ref_option.rs:45:5 | LL | fn private_opt_params(&self, a: &Option<()>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-----------^^^^ @@ -154,7 +122,7 @@ LL | fn private_opt_params(&self, a: &Option<()>) {} | help: change this to: `Option<&()>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:60:5 + --> tests/ui-toml/ref_option/ref_option.rs:47:5 | LL | fn private_opt_ret(&self) -> &Option { | ^ --------------- help: change this to: `Option<&String>` @@ -165,5 +133,5 @@ LL | | panic!() LL | | } | |_____^ -error: aborting due to 17 previous errors +error: aborting due to 13 previous errors diff --git a/tests/ui-toml/ref_option/ref_option.private.fixed b/tests/ui-toml/ref_option/ref_option.private.fixed new file mode 100644 index 000000000000..4dd14a822067 --- /dev/null +++ b/tests/ui-toml/ref_option/ref_option.private.fixed @@ -0,0 +1,114 @@ +//@aux-build:../../ui/auxiliary/proc_macros.rs +//@revisions: private all +//@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/private +//@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/all + +#![allow(unused, clippy::needless_lifetimes, clippy::borrowed_box)] +#![warn(clippy::ref_option)] + +fn opt_u8(a: Option<&u8>) {} +//~^ ref_option +fn opt_gen(a: Option<&T>) {} +//~^ ref_option +fn opt_string(a: std::option::Option<&String>) {} +//~^ ref_option +fn ret_u8<'a>(p: &'a str) -> Option<&'a u8> { + //~^ ref_option + panic!() +} +fn ret_u8_static() -> Option<&'static u8> { + //~^ ref_option + panic!() +} +fn mult_string(a: Option<&String>, b: Option<&Vec>) {} +//~^ ref_option +fn ret_box<'a>() -> Option<&'a Box> { + //~^ ref_option + panic!() +} + +pub fn pub_opt_string(a: &Option) {} +//~[all]^ ref_option +pub fn pub_mult_string(a: &Option, b: &Option>) {} +//~[all]^ ref_option + +pub struct PubStruct; + +impl PubStruct { + pub fn pub_opt_params(&self, a: &Option<()>) {} + //~[all]^ ref_option + pub fn pub_opt_ret(&self) -> &Option { + //~[all]^ ref_option + panic!() + } + + fn private_opt_params(&self, a: Option<&()>) {} + //~^ ref_option + fn private_opt_ret(&self) -> Option<&String> { + //~^ ref_option + panic!() + } +} + +// valid, don't change +fn mut_u8(a: &mut Option) {} +pub fn pub_mut_u8(a: &mut Option) {} + +// might be good to catch in the future +fn mut_u8_ref(a: &mut &Option) {} +pub fn pub_mut_u8_ref(a: &mut &Option) {} +fn lambdas() { + // Not handled for now, not sure if we should + let x = |a: &Option| {}; + let x = |a: &Option| -> &Option { panic!() }; +} + +pub mod external { + proc_macros::external!( + fn opt_u8(a: &Option) {} + fn ret_u8<'a>(p: &'a str) -> &'a Option { + panic!() + } + pub fn pub_opt_u8(a: &Option) {} + + pub struct PubStruct; + impl PubStruct { + pub fn pub_opt_params(&self, a: &Option<()>) {} + pub fn pub_opt_ret(&self) -> &Option { + panic!() + } + + fn private_opt_params(&self, a: &Option<()>) {} + fn private_opt_ret(&self) -> &Option { + panic!() + } + } + ); +} + +pub mod proc_macros { + proc_macros::with_span!( + span + + fn opt_u8(a: &Option) {} + fn ret_u8<'a>(p: &'a str) -> &'a Option { + panic!() + } + pub fn pub_opt_u8(a: &Option) {} + + pub struct PubStruct; + impl PubStruct { + pub fn pub_opt_params(&self, a: &Option<()>) {} + pub fn pub_opt_ret(&self) -> &Option { + panic!() + } + + fn private_opt_params(&self, a: &Option<()>) {} + fn private_opt_ret(&self) -> &Option { + panic!() + } + } + ); +} + +fn main() {} diff --git a/tests/ui/ref_option/ref_option.private.stderr b/tests/ui-toml/ref_option/ref_option.private.stderr similarity index 63% rename from tests/ui/ref_option/ref_option.private.stderr rename to tests/ui-toml/ref_option/ref_option.private.stderr index 88c65e429d87..a63efd60a036 100644 --- a/tests/ui/ref_option/ref_option.private.stderr +++ b/tests/ui-toml/ref_option/ref_option.private.stderr @@ -1,5 +1,5 @@ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:8:1 + --> tests/ui-toml/ref_option/ref_option.rs:9:1 | LL | fn opt_u8(a: &Option) {} | ^^^^^^^^^^^^^-----------^^^^ @@ -10,7 +10,7 @@ LL | fn opt_u8(a: &Option) {} = help: to override `-D warnings` add `#[allow(clippy::ref_option)]` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:10:1 + --> tests/ui-toml/ref_option/ref_option.rs:11:1 | LL | fn opt_gen(a: &Option) {} | ^^^^^^^^^^^^^^^^^----------^^^^ @@ -18,7 +18,7 @@ LL | fn opt_gen(a: &Option) {} | help: change this to: `Option<&T>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:12:1 + --> tests/ui-toml/ref_option/ref_option.rs:13:1 | LL | fn opt_string(a: &std::option::Option) {} | ^^^^^^^^^^^^^^^^^----------------------------^^^^ @@ -26,10 +26,10 @@ LL | fn opt_string(a: &std::option::Option) {} | help: change this to: `std::option::Option<&String>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:14:1 + --> tests/ui-toml/ref_option/ref_option.rs:15:1 | -LL | fn ret_string<'a>(p: &'a str) -> &'a Option { - | ^ -------------- help: change this to: `Option<&'a u8>` +LL | fn ret_u8<'a>(p: &'a str) -> &'a Option { + | ^ -------------- help: change this to: `Option<&'a u8>` | _| | | LL | | @@ -38,10 +38,10 @@ LL | | } | |_^ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:18:1 + --> tests/ui-toml/ref_option/ref_option.rs:19:1 | -LL | fn ret_string_static() -> &'static Option { - | ^ ------------------- help: change this to: `Option<&'static u8>` +LL | fn ret_u8_static() -> &'static Option { + | ^ ------------------- help: change this to: `Option<&'static u8>` | _| | | LL | | @@ -50,7 +50,7 @@ LL | | } | |_^ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:22:1 + --> tests/ui-toml/ref_option/ref_option.rs:23:1 | LL | fn mult_string(a: &Option, b: &Option>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -62,7 +62,7 @@ LL + fn mult_string(a: Option<&String>, b: Option<&Vec>) {} | error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:24:1 + --> tests/ui-toml/ref_option/ref_option.rs:25:1 | LL | fn ret_box<'a>() -> &'a Option> { | ^ ------------------- help: change this to: `Option<&'a Box>` @@ -74,23 +74,7 @@ LL | | } | |_^ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:42:5 - | -LL | fn trait_opt(&self, a: &Option); - | ^^^^^^^^^^^^^^^^^^^^^^^---------------^^ - | | - | help: change this to: `Option<&String>` - -error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:44:5 - | -LL | fn trait_ret(&self) -> &Option; - | ^^^^^^^^^^^^^^^^^^^^^^^---------------^ - | | - | help: change this to: `Option<&String>` - -error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:58:5 + --> tests/ui-toml/ref_option/ref_option.rs:45:5 | LL | fn private_opt_params(&self, a: &Option<()>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-----------^^^^ @@ -98,7 +82,7 @@ LL | fn private_opt_params(&self, a: &Option<()>) {} | help: change this to: `Option<&()>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:60:5 + --> tests/ui-toml/ref_option/ref_option.rs:47:5 | LL | fn private_opt_ret(&self) -> &Option { | ^ --------------- help: change this to: `Option<&String>` @@ -109,5 +93,5 @@ LL | | panic!() LL | | } | |_____^ -error: aborting due to 11 previous errors +error: aborting due to 9 previous errors diff --git a/tests/ui-toml/ref_option/ref_option.rs b/tests/ui-toml/ref_option/ref_option.rs new file mode 100644 index 000000000000..8397c2213d17 --- /dev/null +++ b/tests/ui-toml/ref_option/ref_option.rs @@ -0,0 +1,114 @@ +//@aux-build:../../ui/auxiliary/proc_macros.rs +//@revisions: private all +//@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/private +//@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/all + +#![allow(unused, clippy::needless_lifetimes, clippy::borrowed_box)] +#![warn(clippy::ref_option)] + +fn opt_u8(a: &Option) {} +//~^ ref_option +fn opt_gen(a: &Option) {} +//~^ ref_option +fn opt_string(a: &std::option::Option) {} +//~^ ref_option +fn ret_u8<'a>(p: &'a str) -> &'a Option { + //~^ ref_option + panic!() +} +fn ret_u8_static() -> &'static Option { + //~^ ref_option + panic!() +} +fn mult_string(a: &Option, b: &Option>) {} +//~^ ref_option +fn ret_box<'a>() -> &'a Option> { + //~^ ref_option + panic!() +} + +pub fn pub_opt_string(a: &Option) {} +//~[all]^ ref_option +pub fn pub_mult_string(a: &Option, b: &Option>) {} +//~[all]^ ref_option + +pub struct PubStruct; + +impl PubStruct { + pub fn pub_opt_params(&self, a: &Option<()>) {} + //~[all]^ ref_option + pub fn pub_opt_ret(&self) -> &Option { + //~[all]^ ref_option + panic!() + } + + fn private_opt_params(&self, a: &Option<()>) {} + //~^ ref_option + fn private_opt_ret(&self) -> &Option { + //~^ ref_option + panic!() + } +} + +// valid, don't change +fn mut_u8(a: &mut Option) {} +pub fn pub_mut_u8(a: &mut Option) {} + +// might be good to catch in the future +fn mut_u8_ref(a: &mut &Option) {} +pub fn pub_mut_u8_ref(a: &mut &Option) {} +fn lambdas() { + // Not handled for now, not sure if we should + let x = |a: &Option| {}; + let x = |a: &Option| -> &Option { panic!() }; +} + +pub mod external { + proc_macros::external!( + fn opt_u8(a: &Option) {} + fn ret_u8<'a>(p: &'a str) -> &'a Option { + panic!() + } + pub fn pub_opt_u8(a: &Option) {} + + pub struct PubStruct; + impl PubStruct { + pub fn pub_opt_params(&self, a: &Option<()>) {} + pub fn pub_opt_ret(&self) -> &Option { + panic!() + } + + fn private_opt_params(&self, a: &Option<()>) {} + fn private_opt_ret(&self) -> &Option { + panic!() + } + } + ); +} + +pub mod proc_macros { + proc_macros::with_span!( + span + + fn opt_u8(a: &Option) {} + fn ret_u8<'a>(p: &'a str) -> &'a Option { + panic!() + } + pub fn pub_opt_u8(a: &Option) {} + + pub struct PubStruct; + impl PubStruct { + pub fn pub_opt_params(&self, a: &Option<()>) {} + pub fn pub_opt_ret(&self) -> &Option { + panic!() + } + + fn private_opt_params(&self, a: &Option<()>) {} + fn private_opt_ret(&self) -> &Option { + panic!() + } + } + ); +} + +fn main() {} diff --git a/tests/ui/ref_option/ref_option_traits.all.stderr b/tests/ui-toml/ref_option/ref_option_traits.all.stderr similarity index 85% rename from tests/ui/ref_option/ref_option_traits.all.stderr rename to tests/ui-toml/ref_option/ref_option_traits.all.stderr index 886bf2b03498..602e148be601 100644 --- a/tests/ui/ref_option/ref_option_traits.all.stderr +++ b/tests/ui-toml/ref_option/ref_option_traits.all.stderr @@ -1,5 +1,5 @@ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option_traits.rs:9:5 + --> tests/ui-toml/ref_option/ref_option_traits.rs:10:5 | LL | fn pub_trait_opt(&self, a: &Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^----------------^^ @@ -10,7 +10,7 @@ LL | fn pub_trait_opt(&self, a: &Option>); = help: to override `-D warnings` add `#[allow(clippy::ref_option)]` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option_traits.rs:11:5 + --> tests/ui-toml/ref_option/ref_option_traits.rs:12:5 | LL | fn pub_trait_ret(&self) -> &Option>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^----------------^ @@ -18,7 +18,7 @@ LL | fn pub_trait_ret(&self) -> &Option>; | help: change this to: `Option<&Vec>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option_traits.rs:16:5 + --> tests/ui-toml/ref_option/ref_option_traits.rs:17:5 | LL | fn trait_opt(&self, a: &Option); | ^^^^^^^^^^^^^^^^^^^^^^^---------------^^ @@ -26,7 +26,7 @@ LL | fn trait_opt(&self, a: &Option); | help: change this to: `Option<&String>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option_traits.rs:18:5 + --> tests/ui-toml/ref_option/ref_option_traits.rs:19:5 | LL | fn trait_ret(&self) -> &Option; | ^^^^^^^^^^^^^^^^^^^^^^^---------------^ diff --git a/tests/ui/ref_option/ref_option_traits.private.stderr b/tests/ui-toml/ref_option/ref_option_traits.private.stderr similarity index 86% rename from tests/ui/ref_option/ref_option_traits.private.stderr rename to tests/ui-toml/ref_option/ref_option_traits.private.stderr index cfab7fa5734c..20bea400edfe 100644 --- a/tests/ui/ref_option/ref_option_traits.private.stderr +++ b/tests/ui-toml/ref_option/ref_option_traits.private.stderr @@ -1,5 +1,5 @@ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option_traits.rs:16:5 + --> tests/ui-toml/ref_option/ref_option_traits.rs:17:5 | LL | fn trait_opt(&self, a: &Option); | ^^^^^^^^^^^^^^^^^^^^^^^---------------^^ @@ -10,7 +10,7 @@ LL | fn trait_opt(&self, a: &Option); = help: to override `-D warnings` add `#[allow(clippy::ref_option)]` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option_traits.rs:18:5 + --> tests/ui-toml/ref_option/ref_option_traits.rs:19:5 | LL | fn trait_ret(&self) -> &Option; | ^^^^^^^^^^^^^^^^^^^^^^^---------------^ diff --git a/tests/ui-toml/ref_option/ref_option_traits.rs b/tests/ui-toml/ref_option/ref_option_traits.rs new file mode 100644 index 000000000000..e1477d7f8463 --- /dev/null +++ b/tests/ui-toml/ref_option/ref_option_traits.rs @@ -0,0 +1,71 @@ +//@no-rustfix: fixes are only done to traits, not the impls +//@aux-build:../../ui/auxiliary/proc_macros.rs +//@revisions: private all +//@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/private +//@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/all + +#![warn(clippy::ref_option)] + +pub trait PubTrait { + fn pub_trait_opt(&self, a: &Option>); + //~[all]^ ref_option + fn pub_trait_ret(&self) -> &Option>; + //~[all]^ ref_option +} + +trait PrivateTrait { + fn trait_opt(&self, a: &Option); + //~^ ref_option + fn trait_ret(&self) -> &Option; + //~^ ref_option +} + +pub struct PubStruct; + +impl PubTrait for PubStruct { + fn pub_trait_opt(&self, a: &Option>) {} + fn pub_trait_ret(&self) -> &Option> { + panic!() + } +} + +struct PrivateStruct; + +impl PrivateTrait for PrivateStruct { + fn trait_opt(&self, a: &Option) {} + fn trait_ret(&self) -> &Option { + panic!() + } +} + +pub mod external { + proc_macros::external!( + pub trait PubTrait { + fn pub_trait_opt(&self, a: &Option>); + fn pub_trait_ret(&self) -> &Option>; + } + + trait PrivateTrait { + fn trait_opt(&self, a: &Option); + fn trait_ret(&self) -> &Option; + } + ); +} + +pub mod proc_macros { + proc_macros::with_span!( + span + + pub trait PubTrait { + fn pub_trait_opt(&self, a: &Option>); + fn pub_trait_ret(&self) -> &Option>; + } + + trait PrivateTrait { + fn trait_opt(&self, a: &Option); + fn trait_ret(&self) -> &Option; + } + ); +} + +fn main() {} diff --git a/tests/ui/as_underscore_unfixable.rs b/tests/ui/as_underscore_unfixable.rs new file mode 100644 index 000000000000..854feca7174f --- /dev/null +++ b/tests/ui/as_underscore_unfixable.rs @@ -0,0 +1,14 @@ +//@no-rustfix + +#![warn(clippy::as_underscore)] + +fn main() { + // From issue #15282 + let f = async || (); + let _: Box _> = Box::new(f) as _; + //~^ as_underscore + + let barr = || (|| ()); + let _: Box _> = Box::new(barr) as _; + //~^ as_underscore +} diff --git a/tests/ui/as_underscore_unfixable.stderr b/tests/ui/as_underscore_unfixable.stderr new file mode 100644 index 000000000000..7385bea5c650 --- /dev/null +++ b/tests/ui/as_underscore_unfixable.stderr @@ -0,0 +1,20 @@ +error: using `as _` conversion + --> tests/ui/as_underscore_unfixable.rs:8:37 + | +LL | let _: Box _> = Box::new(f) as _; + | ^^^^^^^^^^^^^^^^ + | + = help: consider giving the type explicitly + = note: `-D clippy::as-underscore` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::as_underscore)]` + +error: using `as _` conversion + --> tests/ui/as_underscore_unfixable.rs:12:33 + | +LL | let _: Box _> = Box::new(barr) as _; + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider giving the type explicitly + +error: aborting due to 2 previous errors + diff --git a/tests/ui/cast.rs b/tests/ui/cast.rs index 525be8216500..fab02bf7b24e 100644 --- a/tests/ui/cast.rs +++ b/tests/ui/cast.rs @@ -569,3 +569,16 @@ fn issue12721() { (255 % 999999u64) as u8; //~^ cast_possible_truncation } + +mod issue14150 { + #[clippy::msrv = "1.87"] + fn msrv_supports_cast_signed() { + _ = 1u8 as i8; + //~^ cast_possible_wrap + } + #[clippy::msrv = "1.86"] + fn msrv_doesnt_supports_cast_signed() { + _ = 1u8 as i8; + //~^ cast_possible_wrap + } +} diff --git a/tests/ui/cast.stderr b/tests/ui/cast.stderr index 1cb30d956679..8c48855123f9 100644 --- a/tests/ui/cast.stderr +++ b/tests/ui/cast.stderr @@ -194,7 +194,7 @@ error: casting `u8` to `i8` may wrap around the value --> tests/ui/cast.rs:88:5 | LL | 1u8 as i8; - | ^^^^^^^^^ + | ^^^^^^^^^ help: if this is intentional, use `cast_signed()` instead: `1u8.cast_signed()` | = note: `-D clippy::cast-possible-wrap` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::cast_possible_wrap)]` @@ -203,25 +203,25 @@ error: casting `u16` to `i16` may wrap around the value --> tests/ui/cast.rs:91:5 | LL | 1u16 as i16; - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ help: if this is intentional, use `cast_signed()` instead: `1u16.cast_signed()` error: casting `u32` to `i32` may wrap around the value --> tests/ui/cast.rs:94:5 | LL | 1u32 as i32; - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ help: if this is intentional, use `cast_signed()` instead: `1u32.cast_signed()` error: casting `u64` to `i64` may wrap around the value --> tests/ui/cast.rs:97:5 | LL | 1u64 as i64; - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ help: if this is intentional, use `cast_signed()` instead: `1u64.cast_signed()` error: casting `usize` to `isize` may wrap around the value --> tests/ui/cast.rs:100:5 | LL | 1usize as isize; - | ^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_signed()` instead: `1usize.cast_signed()` error: casting `usize` to `i8` may truncate the value --> tests/ui/cast.rs:104:5 @@ -321,43 +321,43 @@ error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:138:5 | LL | -1i32 as u32; - | ^^^^^^^^^^^^ + | ^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(-1i32).cast_unsigned()` error: casting `isize` to `usize` may lose the sign of the value --> tests/ui/cast.rs:142:5 | LL | -1isize as usize; - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(-1isize).cast_unsigned()` error: casting `i8` to `u8` may lose the sign of the value --> tests/ui/cast.rs:154:5 | LL | (i8::MIN).abs() as u8; - | ^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(i8::MIN).abs().cast_unsigned()` error: casting `i64` to `u64` may lose the sign of the value --> tests/ui/cast.rs:159:5 | LL | (-1i64).abs() as u64; - | ^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(-1i64).abs().cast_unsigned()` error: casting `isize` to `usize` may lose the sign of the value --> tests/ui/cast.rs:161:5 | LL | (-1isize).abs() as usize; - | ^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(-1isize).abs().cast_unsigned()` error: casting `i64` to `u64` may lose the sign of the value --> tests/ui/cast.rs:169:5 | LL | (unsafe { (-1i64).checked_abs().unwrap_unchecked() }) as u64; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(unsafe { (-1i64).checked_abs().unwrap_unchecked() }).cast_unsigned()` error: casting `i64` to `u64` may lose the sign of the value --> tests/ui/cast.rs:185:5 | LL | (unsafe { (-1i64).checked_isqrt().unwrap_unchecked() }) as u64; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(unsafe { (-1i64).checked_isqrt().unwrap_unchecked() }).cast_unsigned()` error: casting `i64` to `i8` may truncate the value --> tests/ui/cast.rs:237:5 @@ -495,79 +495,79 @@ error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:438:9 | LL | (x * x) as u32; - | ^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(x * x).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:444:32 | LL | let _a = |x: i32| -> u32 { (x * x * x * x) as u32 }; - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(x * x * x * x).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:447:5 | LL | (2_i32).checked_pow(3).unwrap() as u32; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(2_i32).checked_pow(3).unwrap().cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:449:5 | LL | (-2_i32).pow(3) as u32; - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(-2_i32).pow(3).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:454:5 | LL | (-5_i32 % 2) as u32; - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(-5_i32 % 2).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:457:5 | LL | (-5_i32 % -2) as u32; - | ^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(-5_i32 % -2).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:461:5 | LL | (-2_i32 >> 1) as u32; - | ^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(-2_i32 >> 1).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:465:5 | LL | (x * x) as u32; - | ^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(x * x).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:467:5 | LL | (x * x * x) as u32; - | ^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(x * x * x).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:471:5 | LL | (y * y * y * y * -2) as u16; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(y * y * y * y * -2).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:474:5 | LL | (y * y * y / y * 2) as u16; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(y * y * y / y * 2).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:476:5 | LL | (y * y / y * 2) as u16; - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(y * y / y * 2).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:479:5 | LL | (y / y * y * -2) as u16; - | ^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(y / y * y * -2).cast_unsigned()` error: equal expressions as operands to `/` --> tests/ui/cast.rs:479:6 @@ -581,97 +581,97 @@ error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:483:5 | LL | (y + y + y + -2) as u16; - | ^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(y + y + y + -2).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:486:5 | LL | (y + y + y + 2) as u16; - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(y + y + y + 2).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:490:5 | LL | (z + -2) as u16; - | ^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(z + -2).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:493:5 | LL | (z + z + 2) as u16; - | ^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(z + z + 2).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:497:9 | LL | (a * a * b * b * c * c) as u32; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a * a * b * b * c * c).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:499:9 | LL | (a * b * c) as u32; - | ^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a * b * c).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:502:9 | LL | (a * -b * c) as u32; - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a * -b * c).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:505:9 | LL | (a * b * c * c) as u32; - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a * b * c * c).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:507:9 | LL | (a * -2) as u32; - | ^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a * -2).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:510:9 | LL | (a * b * c * -2) as u32; - | ^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a * b * c * -2).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:513:9 | LL | (a / b) as u32; - | ^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a / b).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:515:9 | LL | (a / b * c) as u32; - | ^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a / b * c).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:518:9 | LL | (a / b + b * c) as u32; - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a / b + b * c).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:521:9 | LL | a.saturating_pow(3) as u32; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `a.saturating_pow(3).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:524:9 | LL | (a.abs() * b.pow(2) / c.abs()) as u32 - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a.abs() * b.pow(2) / c.abs()).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:532:21 | LL | let _ = i32::MIN as u32; // cast_sign_loss - | ^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `i32::MIN.cast_unsigned()` ... LL | m!(); | ---- in this macro invocation @@ -752,5 +752,17 @@ LL - (255 % 999999u64) as u8; LL + u8::try_from(255 % 999999u64); | -error: aborting due to 92 previous errors +error: casting `u8` to `i8` may wrap around the value + --> tests/ui/cast.rs:576:13 + | +LL | _ = 1u8 as i8; + | ^^^^^^^^^ help: if this is intentional, use `cast_signed()` instead: `1u8.cast_signed()` + +error: casting `u8` to `i8` may wrap around the value + --> tests/ui/cast.rs:581:13 + | +LL | _ = 1u8 as i8; + | ^^^^^^^^^ + +error: aborting due to 94 previous errors diff --git a/tests/ui/checked_unwrap/simple_conditionals.rs b/tests/ui/checked_unwrap/simple_conditionals.rs index 785b2473c053..bba264080b40 100644 --- a/tests/ui/checked_unwrap/simple_conditionals.rs +++ b/tests/ui/checked_unwrap/simple_conditionals.rs @@ -273,6 +273,28 @@ const ISSUE14763: fn(Option) = |x| { } }; +fn issue12295() { + let option = Some(()); + + if option.is_some() { + println!("{:?}", option.unwrap()); + //~^ unnecessary_unwrap + } else { + println!("{:?}", option.unwrap()); + //~^ panicking_unwrap + } + + let result = Ok::<(), ()>(()); + + if result.is_ok() { + println!("{:?}", result.unwrap()); + //~^ unnecessary_unwrap + } else { + println!("{:?}", result.unwrap()); + //~^ panicking_unwrap + } +} + fn check_expect() { let x = Some(()); if x.is_some() { diff --git a/tests/ui/checked_unwrap/simple_conditionals.stderr b/tests/ui/checked_unwrap/simple_conditionals.stderr index 939b509d85c9..2007a8595413 100644 --- a/tests/ui/checked_unwrap/simple_conditionals.stderr +++ b/tests/ui/checked_unwrap/simple_conditionals.stderr @@ -322,6 +322,40 @@ LL | if x.is_some() { LL | _ = x.unwrap(); | ^^^^^^^^^^ +error: called `unwrap` on `option` after checking its variant with `is_some` + --> tests/ui/checked_unwrap/simple_conditionals.rs:280:26 + | +LL | if option.is_some() { + | ------------------- help: try: `if let Some() = option` +LL | println!("{:?}", option.unwrap()); + | ^^^^^^^^^^^^^^^ + +error: this call to `unwrap()` will always panic + --> tests/ui/checked_unwrap/simple_conditionals.rs:283:26 + | +LL | if option.is_some() { + | ---------------- because of this check +... +LL | println!("{:?}", option.unwrap()); + | ^^^^^^^^^^^^^^^ + +error: called `unwrap` on `result` after checking its variant with `is_ok` + --> tests/ui/checked_unwrap/simple_conditionals.rs:290:26 + | +LL | if result.is_ok() { + | ----------------- help: try: `if let Ok() = result` +LL | println!("{:?}", result.unwrap()); + | ^^^^^^^^^^^^^^^ + +error: this call to `unwrap()` will always panic + --> tests/ui/checked_unwrap/simple_conditionals.rs:293:26 + | +LL | if result.is_ok() { + | -------------- because of this check +... +LL | println!("{:?}", result.unwrap()); + | ^^^^^^^^^^^^^^^ + error: creating a shared reference to mutable static --> tests/ui/checked_unwrap/simple_conditionals.rs:183:12 | @@ -332,5 +366,5 @@ LL | if X.is_some() { = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives = note: `#[deny(static_mut_refs)]` (part of `#[deny(rust_2024_compatibility)]`) on by default -error: aborting due to 36 previous errors +error: aborting due to 40 previous errors diff --git a/tests/ui/crashes/ice-15657.rs b/tests/ui/crashes/ice-15657.rs new file mode 100644 index 000000000000..c6f6506cd8d0 --- /dev/null +++ b/tests/ui/crashes/ice-15657.rs @@ -0,0 +1,11 @@ +//@check-pass +#![warn(clippy::len_zero)] + +pub struct S1; +pub struct S2; + +impl S1 { + pub fn len(&self) -> S2 { + S2 + } +} diff --git a/tests/ui/crashes/ice-15666.fixed b/tests/ui/crashes/ice-15666.fixed new file mode 100644 index 000000000000..53b618765c0f --- /dev/null +++ b/tests/ui/crashes/ice-15666.fixed @@ -0,0 +1,6 @@ +#![warn(clippy::elidable_lifetime_names)] + +struct UnitVariantAccess<'a, 'b, 's>(&'a &'b &'s ()); +trait Trait<'de> {} +impl<'de> Trait<'de> for UnitVariantAccess<'_, 'de, '_> {} +//~^ elidable_lifetime_names diff --git a/tests/ui/crashes/ice-15666.rs b/tests/ui/crashes/ice-15666.rs new file mode 100644 index 000000000000..1414b3d2035e --- /dev/null +++ b/tests/ui/crashes/ice-15666.rs @@ -0,0 +1,6 @@ +#![warn(clippy::elidable_lifetime_names)] + +struct UnitVariantAccess<'a, 'b, 's>(&'a &'b &'s ()); +trait Trait<'de> {} +impl<'de, 'a, 's> Trait<'de> for UnitVariantAccess<'a, 'de, 's> {} +//~^ elidable_lifetime_names diff --git a/tests/ui/crashes/ice-15666.stderr b/tests/ui/crashes/ice-15666.stderr new file mode 100644 index 000000000000..b417c09b5c65 --- /dev/null +++ b/tests/ui/crashes/ice-15666.stderr @@ -0,0 +1,16 @@ +error: the following explicit lifetimes could be elided: 'a, 's + --> tests/ui/crashes/ice-15666.rs:5:11 + | +LL | impl<'de, 'a, 's> Trait<'de> for UnitVariantAccess<'a, 'de, 's> {} + | ^^ ^^ ^^ ^^ + | + = note: `-D clippy::elidable-lifetime-names` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::elidable_lifetime_names)]` +help: elide the lifetimes + | +LL - impl<'de, 'a, 's> Trait<'de> for UnitVariantAccess<'a, 'de, 's> {} +LL + impl<'de> Trait<'de> for UnitVariantAccess<'_, 'de, '_> {} + | + +error: aborting due to 1 previous error + diff --git a/tests/ui/elidable_lifetime_names.fixed b/tests/ui/elidable_lifetime_names.fixed index abeee5c4cef3..a6c4cb7a36a8 100644 --- a/tests/ui/elidable_lifetime_names.fixed +++ b/tests/ui/elidable_lifetime_names.fixed @@ -192,3 +192,85 @@ mod issue13923 { x.b } } + +fn issue15666_original() { + struct UnitVariantAccess<'a, 'b, 's>(&'a &'b &'s ()); + + trait Trait<'de> {} + + //~v elidable_lifetime_names + impl<'de> Trait<'de> for UnitVariantAccess<'_, 'de, '_> {} + // ^^ ^^ ^^ ^^ +} + +#[allow(clippy::upper_case_acronyms)] +fn issue15666() { + struct S1<'a>(&'a ()); + struct S2<'a, 'b>(&'a &'b ()); + struct S3<'a, 'b, 'c>(&'a &'b &'c ()); + + trait T {} + trait TA<'a> {} + trait TB<'b> {} + trait TC<'c> {} + trait TAB<'a, 'b> {} + trait TAC<'a, 'c> {} + trait TBC<'b, 'c> {} + trait TABC<'a, 'b, 'c> {} + + // 1 lifetime + + impl<'a> TA<'a> for S1<'a> {} + + //~v elidable_lifetime_names + impl T for S1<'_> {} + // ^^ + + // 2 lifetimes + + impl<'a, 'b> TAB<'a, 'b> for S2<'a, 'b> {} + + //~v elidable_lifetime_names + impl<'a> TA<'a> for S2<'a, '_> {} + // ^^ + + //~v elidable_lifetime_names + impl<'b> TB<'b> for S2<'_, 'b> {} + // ^^ + + //~v elidable_lifetime_names + impl T for S2<'_, '_> {} + // ^^ ^^ + + // 3 lifetimes + + impl<'a, 'b, 'c> TABC<'a, 'b, 'c> for S3<'a, 'b, 'c> {} + + //~v elidable_lifetime_names + impl<'a, 'b> TAB<'a, 'b> for S3<'a, 'b, '_> {} + // ^^ + + //~v elidable_lifetime_names + impl<'a, 'c> TAC<'a, 'c> for S3<'a, '_, 'c> {} + // ^^ + + //~v elidable_lifetime_names + impl<'a> TA<'a> for S3<'a, '_, '_> {} + // ^^ ^^ + + //~v elidable_lifetime_names + impl<'b, 'c> TBC<'b, 'c> for S3<'_, 'b, 'c> {} + // ^^ + + //~v elidable_lifetime_names + impl<'b> TB<'b> for S3<'_, 'b, '_> {} + // ^^ ^^ + + //~v elidable_lifetime_names + impl<'c> TC<'c> for S3<'_, '_, 'c> {} + // ^^ ^^ + + //~v elidable_lifetime_names + impl T for S3<'_, '_, '_> {} + // ^^ ^^ ^^ +} diff --git a/tests/ui/elidable_lifetime_names.rs b/tests/ui/elidable_lifetime_names.rs index fae3577a8e96..e08056b2fb56 100644 --- a/tests/ui/elidable_lifetime_names.rs +++ b/tests/ui/elidable_lifetime_names.rs @@ -192,3 +192,85 @@ mod issue13923 { x.b } } + +fn issue15666_original() { + struct UnitVariantAccess<'a, 'b, 's>(&'a &'b &'s ()); + + trait Trait<'de> {} + + //~v elidable_lifetime_names + impl<'de, 'a, 's> Trait<'de> for UnitVariantAccess<'a, 'de, 's> {} + // ^^ ^^ ^^ ^^ +} + +#[allow(clippy::upper_case_acronyms)] +fn issue15666() { + struct S1<'a>(&'a ()); + struct S2<'a, 'b>(&'a &'b ()); + struct S3<'a, 'b, 'c>(&'a &'b &'c ()); + + trait T {} + trait TA<'a> {} + trait TB<'b> {} + trait TC<'c> {} + trait TAB<'a, 'b> {} + trait TAC<'a, 'c> {} + trait TBC<'b, 'c> {} + trait TABC<'a, 'b, 'c> {} + + // 1 lifetime + + impl<'a> TA<'a> for S1<'a> {} + + //~v elidable_lifetime_names + impl<'a> T for S1<'a> {} + // ^^ + + // 2 lifetimes + + impl<'a, 'b> TAB<'a, 'b> for S2<'a, 'b> {} + + //~v elidable_lifetime_names + impl<'a, 'b> TA<'a> for S2<'a, 'b> {} + // ^^ + + //~v elidable_lifetime_names + impl<'a, 'b> TB<'b> for S2<'a, 'b> {} + // ^^ + + //~v elidable_lifetime_names + impl<'a, 'b> T for S2<'a, 'b> {} + // ^^ ^^ + + // 3 lifetimes + + impl<'a, 'b, 'c> TABC<'a, 'b, 'c> for S3<'a, 'b, 'c> {} + + //~v elidable_lifetime_names + impl<'a, 'b, 'c> TAB<'a, 'b> for S3<'a, 'b, 'c> {} + // ^^ + + //~v elidable_lifetime_names + impl<'a, 'b, 'c> TAC<'a, 'c> for S3<'a, 'b, 'c> {} + // ^^ + + //~v elidable_lifetime_names + impl<'a, 'b, 'c> TA<'a> for S3<'a, 'b, 'c> {} + // ^^ ^^ + + //~v elidable_lifetime_names + impl<'a, 'b, 'c> TBC<'b, 'c> for S3<'a, 'b, 'c> {} + // ^^ + + //~v elidable_lifetime_names + impl<'a, 'b, 'c> TB<'b> for S3<'a, 'b, 'c> {} + // ^^ ^^ + + //~v elidable_lifetime_names + impl<'a, 'b, 'c> TC<'c> for S3<'a, 'b, 'c> {} + // ^^ ^^ + + //~v elidable_lifetime_names + impl<'a, 'b, 'c> T for S3<'a, 'b, 'c> {} + // ^^ ^^ ^^ +} diff --git a/tests/ui/elidable_lifetime_names.stderr b/tests/ui/elidable_lifetime_names.stderr index a60dfc697564..03fe383b8f67 100644 --- a/tests/ui/elidable_lifetime_names.stderr +++ b/tests/ui/elidable_lifetime_names.stderr @@ -158,5 +158,149 @@ LL | o: &'t str, LL ~ ) -> Content<'t, '_> { | -error: aborting due to 12 previous errors +error: the following explicit lifetimes could be elided: 'a, 's + --> tests/ui/elidable_lifetime_names.rs:202:15 + | +LL | impl<'de, 'a, 's> Trait<'de> for UnitVariantAccess<'a, 'de, 's> {} + | ^^ ^^ ^^ ^^ + | +help: elide the lifetimes + | +LL - impl<'de, 'a, 's> Trait<'de> for UnitVariantAccess<'a, 'de, 's> {} +LL + impl<'de> Trait<'de> for UnitVariantAccess<'_, 'de, '_> {} + | + +error: the following explicit lifetimes could be elided: 'a + --> tests/ui/elidable_lifetime_names.rs:226:10 + | +LL | impl<'a> T for S1<'a> {} + | ^^ ^^ + | +help: elide the lifetimes + | +LL - impl<'a> T for S1<'a> {} +LL + impl T for S1<'_> {} + | + +error: the following explicit lifetimes could be elided: 'b + --> tests/ui/elidable_lifetime_names.rs:234:14 + | +LL | impl<'a, 'b> TA<'a> for S2<'a, 'b> {} + | ^^ ^^ + | +help: elide the lifetimes + | +LL - impl<'a, 'b> TA<'a> for S2<'a, 'b> {} +LL + impl<'a> TA<'a> for S2<'a, '_> {} + | + +error: the following explicit lifetimes could be elided: 'a + --> tests/ui/elidable_lifetime_names.rs:238:10 + | +LL | impl<'a, 'b> TB<'b> for S2<'a, 'b> {} + | ^^ ^^ + | +help: elide the lifetimes + | +LL - impl<'a, 'b> TB<'b> for S2<'a, 'b> {} +LL + impl<'b> TB<'b> for S2<'_, 'b> {} + | + +error: the following explicit lifetimes could be elided: 'a, 'b + --> tests/ui/elidable_lifetime_names.rs:242:10 + | +LL | impl<'a, 'b> T for S2<'a, 'b> {} + | ^^ ^^ ^^ ^^ + | +help: elide the lifetimes + | +LL - impl<'a, 'b> T for S2<'a, 'b> {} +LL + impl T for S2<'_, '_> {} + | + +error: the following explicit lifetimes could be elided: 'c + --> tests/ui/elidable_lifetime_names.rs:250:18 + | +LL | impl<'a, 'b, 'c> TAB<'a, 'b> for S3<'a, 'b, 'c> {} + | ^^ ^^ + | +help: elide the lifetimes + | +LL - impl<'a, 'b, 'c> TAB<'a, 'b> for S3<'a, 'b, 'c> {} +LL + impl<'a, 'b> TAB<'a, 'b> for S3<'a, 'b, '_> {} + | + +error: the following explicit lifetimes could be elided: 'b + --> tests/ui/elidable_lifetime_names.rs:254:14 + | +LL | impl<'a, 'b, 'c> TAC<'a, 'c> for S3<'a, 'b, 'c> {} + | ^^ ^^ + | +help: elide the lifetimes + | +LL - impl<'a, 'b, 'c> TAC<'a, 'c> for S3<'a, 'b, 'c> {} +LL + impl<'a, 'c> TAC<'a, 'c> for S3<'a, '_, 'c> {} + | + +error: the following explicit lifetimes could be elided: 'b, 'c + --> tests/ui/elidable_lifetime_names.rs:258:14 + | +LL | impl<'a, 'b, 'c> TA<'a> for S3<'a, 'b, 'c> {} + | ^^ ^^ ^^ ^^ + | +help: elide the lifetimes + | +LL - impl<'a, 'b, 'c> TA<'a> for S3<'a, 'b, 'c> {} +LL + impl<'a> TA<'a> for S3<'a, '_, '_> {} + | + +error: the following explicit lifetimes could be elided: 'a + --> tests/ui/elidable_lifetime_names.rs:262:10 + | +LL | impl<'a, 'b, 'c> TBC<'b, 'c> for S3<'a, 'b, 'c> {} + | ^^ ^^ + | +help: elide the lifetimes + | +LL - impl<'a, 'b, 'c> TBC<'b, 'c> for S3<'a, 'b, 'c> {} +LL + impl<'b, 'c> TBC<'b, 'c> for S3<'_, 'b, 'c> {} + | + +error: the following explicit lifetimes could be elided: 'a, 'c + --> tests/ui/elidable_lifetime_names.rs:266:10 + | +LL | impl<'a, 'b, 'c> TB<'b> for S3<'a, 'b, 'c> {} + | ^^ ^^ ^^ ^^ + | +help: elide the lifetimes + | +LL - impl<'a, 'b, 'c> TB<'b> for S3<'a, 'b, 'c> {} +LL + impl<'b> TB<'b> for S3<'_, 'b, '_> {} + | + +error: the following explicit lifetimes could be elided: 'a, 'b + --> tests/ui/elidable_lifetime_names.rs:270:10 + | +LL | impl<'a, 'b, 'c> TC<'c> for S3<'a, 'b, 'c> {} + | ^^ ^^ ^^ ^^ + | +help: elide the lifetimes + | +LL - impl<'a, 'b, 'c> TC<'c> for S3<'a, 'b, 'c> {} +LL + impl<'c> TC<'c> for S3<'_, '_, 'c> {} + | + +error: the following explicit lifetimes could be elided: 'a, 'b, 'c + --> tests/ui/elidable_lifetime_names.rs:274:10 + | +LL | impl<'a, 'b, 'c> T for S3<'a, 'b, 'c> {} + | ^^ ^^ ^^ ^^ ^^ ^^ + | +help: elide the lifetimes + | +LL - impl<'a, 'b, 'c> T for S3<'a, 'b, 'c> {} +LL + impl T for S3<'_, '_, '_> {} + | + +error: aborting due to 24 previous errors diff --git a/tests/ui/eta.fixed b/tests/ui/eta.fixed index 6944a979c05e..107318e5323d 100644 --- a/tests/ui/eta.fixed +++ b/tests/ui/eta.fixed @@ -635,3 +635,11 @@ fn issue8817() { //~| HELP: replace the closure with the tuple variant itself .unwrap(); // just for nicer formatting } + +async fn issue13892<'a, T, F>(maybe: Option<&'a T>, visitor: F) +where + F: AsyncFn(&'a T), + T: 'a, +{ + maybe.map(|x| visitor(x)); +} diff --git a/tests/ui/eta.rs b/tests/ui/eta.rs index 5bcc1cb26fd7..b85e8e75153a 100644 --- a/tests/ui/eta.rs +++ b/tests/ui/eta.rs @@ -635,3 +635,11 @@ fn issue8817() { //~| HELP: replace the closure with the tuple variant itself .unwrap(); // just for nicer formatting } + +async fn issue13892<'a, T, F>(maybe: Option<&'a T>, visitor: F) +where + F: AsyncFn(&'a T), + T: 'a, +{ + maybe.map(|x| visitor(x)); +} diff --git a/tests/ui/invalid_upcast_comparisons.rs b/tests/ui/invalid_upcast_comparisons.rs index 4f3194869f43..88ecca65e150 100644 --- a/tests/ui/invalid_upcast_comparisons.rs +++ b/tests/ui/invalid_upcast_comparisons.rs @@ -133,3 +133,15 @@ fn main() { -5 == (u32 as i32); } + +fn issue15662() { + macro_rules! add_one { + ($x:expr) => { + $x + 1 + }; + } + + let x: u8 = 1; + (add_one!(x) as u32) > 300; + //~^ invalid_upcast_comparisons +} diff --git a/tests/ui/invalid_upcast_comparisons.stderr b/tests/ui/invalid_upcast_comparisons.stderr index ef36f18eabc9..cc042a7c4b01 100644 --- a/tests/ui/invalid_upcast_comparisons.stderr +++ b/tests/ui/invalid_upcast_comparisons.stderr @@ -163,5 +163,11 @@ error: because of the numeric bounds on `u8` prior to casting, this expression i LL | -5 >= (u8 as i32); | ^^^^^^^^^^^^^^^^^ -error: aborting due to 27 previous errors +error: because of the numeric bounds on `add_one!(x)` prior to casting, this expression is always false + --> tests/ui/invalid_upcast_comparisons.rs:145:5 + | +LL | (add_one!(x) as u32) > 300; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 28 previous errors diff --git a/tests/ui/iter_overeager_cloned.fixed b/tests/ui/iter_overeager_cloned.fixed index b0e548f17909..4171f19469a4 100644 --- a/tests/ui/iter_overeager_cloned.fixed +++ b/tests/ui/iter_overeager_cloned.fixed @@ -1,4 +1,4 @@ -#![warn(clippy::iter_overeager_cloned, clippy::redundant_clone, clippy::filter_next)] +#![warn(clippy::iter_overeager_cloned, clippy::redundant_iter_cloned, clippy::filter_next)] #![allow( dead_code, clippy::let_unit_value, @@ -16,7 +16,7 @@ fn main() { //~^ iter_overeager_cloned let _: usize = vec.iter().filter(|x| x == &"2").count(); - //~^ redundant_clone + //~^ redundant_iter_cloned let _: Vec<_> = vec.iter().take(2).cloned().collect(); //~^ iter_overeager_cloned @@ -77,19 +77,19 @@ fn main() { } let _ = vec.iter().map(|x| x.len()); - //~^ redundant_clone + //~^ redundant_iter_cloned // This would fail if changed. let _ = vec.iter().cloned().map(|x| x + "2"); let _ = vec.iter().for_each(|x| assert!(!x.is_empty())); - //~^ redundant_clone + //~^ redundant_iter_cloned let _ = vec.iter().all(|x| x.len() == 1); - //~^ redundant_clone + //~^ redundant_iter_cloned let _ = vec.iter().any(|x| x.len() == 1); - //~^ redundant_clone + //~^ redundant_iter_cloned // Should probably stay as it is. let _ = [0, 1, 2, 3, 4].iter().cloned().take(10); diff --git a/tests/ui/iter_overeager_cloned.rs b/tests/ui/iter_overeager_cloned.rs index cedf62a6b473..fe6aba24dd3e 100644 --- a/tests/ui/iter_overeager_cloned.rs +++ b/tests/ui/iter_overeager_cloned.rs @@ -1,4 +1,4 @@ -#![warn(clippy::iter_overeager_cloned, clippy::redundant_clone, clippy::filter_next)] +#![warn(clippy::iter_overeager_cloned, clippy::redundant_iter_cloned, clippy::filter_next)] #![allow( dead_code, clippy::let_unit_value, @@ -16,7 +16,7 @@ fn main() { //~^ iter_overeager_cloned let _: usize = vec.iter().filter(|x| x == &"2").cloned().count(); - //~^ redundant_clone + //~^ redundant_iter_cloned let _: Vec<_> = vec.iter().cloned().take(2).collect(); //~^ iter_overeager_cloned @@ -78,19 +78,19 @@ fn main() { } let _ = vec.iter().cloned().map(|x| x.len()); - //~^ redundant_clone + //~^ redundant_iter_cloned // This would fail if changed. let _ = vec.iter().cloned().map(|x| x + "2"); let _ = vec.iter().cloned().for_each(|x| assert!(!x.is_empty())); - //~^ redundant_clone + //~^ redundant_iter_cloned let _ = vec.iter().cloned().all(|x| x.len() == 1); - //~^ redundant_clone + //~^ redundant_iter_cloned let _ = vec.iter().cloned().any(|x| x.len() == 1); - //~^ redundant_clone + //~^ redundant_iter_cloned // Should probably stay as it is. let _ = [0, 1, 2, 3, 4].iter().cloned().take(10); diff --git a/tests/ui/iter_overeager_cloned.stderr b/tests/ui/iter_overeager_cloned.stderr index 1616dec95b79..f234d19e4aaa 100644 --- a/tests/ui/iter_overeager_cloned.stderr +++ b/tests/ui/iter_overeager_cloned.stderr @@ -25,8 +25,8 @@ LL | let _: usize = vec.iter().filter(|x| x == &"2").cloned().count(); | | | help: try: `.count()` | - = note: `-D clippy::redundant-clone` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::redundant_clone)]` + = note: `-D clippy::redundant-iter-cloned` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::redundant_iter_cloned)]` error: unnecessarily eager cloning of iterator items --> tests/ui/iter_overeager_cloned.rs:21:21 diff --git a/tests/ui/match_as_ref.fixed b/tests/ui/match_as_ref.fixed index 8c07076af4a4..a39f0c9299bd 100644 --- a/tests/ui/match_as_ref.fixed +++ b/tests/ui/match_as_ref.fixed @@ -41,3 +41,33 @@ fn main() { None => None, }; } + +mod issue15691 { + use std::ops::{Deref, DerefMut}; + + struct A(B); + struct B; + + impl Deref for A { + type Target = B; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + impl DerefMut for A { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } + + fn func() { + let mut a = Some(A(B)); + let mut b = Some(B); + // Do not lint, we don't have `None => None` + let _ = match b { + Some(ref mut x) => Some(x), + None => a.as_deref_mut(), + }; + } +} diff --git a/tests/ui/match_as_ref.rs b/tests/ui/match_as_ref.rs index 3a5b1227331e..049928167901 100644 --- a/tests/ui/match_as_ref.rs +++ b/tests/ui/match_as_ref.rs @@ -53,3 +53,33 @@ fn main() { None => None, }; } + +mod issue15691 { + use std::ops::{Deref, DerefMut}; + + struct A(B); + struct B; + + impl Deref for A { + type Target = B; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + impl DerefMut for A { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } + + fn func() { + let mut a = Some(A(B)); + let mut b = Some(B); + // Do not lint, we don't have `None => None` + let _ = match b { + Some(ref mut x) => Some(x), + None => a.as_deref_mut(), + }; + } +} diff --git a/tests/ui/multiple_unsafe_ops_per_block.rs b/tests/ui/multiple_unsafe_ops_per_block.rs index 016fd89a7b7a..132673d5164a 100644 --- a/tests/ui/multiple_unsafe_ops_per_block.rs +++ b/tests/ui/multiple_unsafe_ops_per_block.rs @@ -1,10 +1,10 @@ //@needs-asm-support //@aux-build:proc_macros.rs -#![allow(unused)] -#![allow(deref_nullptr)] -#![allow(clippy::unnecessary_operation)] -#![allow(dropping_copy_types)] -#![allow(clippy::assign_op_pattern)] +#![expect( + dropping_copy_types, + clippy::unnecessary_operation, + clippy::unnecessary_literal_unwrap +)] #![warn(clippy::multiple_unsafe_ops_per_block)] extern crate proc_macros; @@ -105,17 +105,17 @@ fn correct3() { } } -// tests from the issue (https://github.com/rust-lang/rust-clippy/issues/10064) +fn issue10064() { + unsafe fn read_char_bad(ptr: *const u8) -> char { + unsafe { char::from_u32_unchecked(*ptr.cast::()) } + //~^ multiple_unsafe_ops_per_block + } -unsafe fn read_char_bad(ptr: *const u8) -> char { - unsafe { char::from_u32_unchecked(*ptr.cast::()) } - //~^ multiple_unsafe_ops_per_block -} - -// no lint -unsafe fn read_char_good(ptr: *const u8) -> char { - let int_value = unsafe { *ptr.cast::() }; - unsafe { core::char::from_u32_unchecked(int_value) } + // no lint + unsafe fn read_char_good(ptr: *const u8) -> char { + let int_value = unsafe { *ptr.cast::() }; + unsafe { core::char::from_u32_unchecked(int_value) } + } } // no lint @@ -126,42 +126,87 @@ fn issue10259() { }); } -fn _fn_ptr(x: unsafe fn()) { - unsafe { - //~^ multiple_unsafe_ops_per_block - x(); - x(); - } -} - -fn _assoc_const() { - trait X { - const X: unsafe fn(); - } - fn _f() { +fn issue10367() { + fn fn_ptr(x: unsafe fn()) { unsafe { //~^ multiple_unsafe_ops_per_block - T::X(); - T::X(); + x(); + x(); + } + } + + fn assoc_const() { + trait X { + const X: unsafe fn(); + } + fn _f() { + unsafe { + //~^ multiple_unsafe_ops_per_block + T::X(); + T::X(); + } + } + } + + fn field_fn_ptr(x: unsafe fn()) { + struct X(unsafe fn()); + let x = X(x); + unsafe { + //~^ multiple_unsafe_ops_per_block + x.0(); + x.0(); } } } -fn _field_fn_ptr(x: unsafe fn()) { - struct X(unsafe fn()); - let x = X(x); - unsafe { - //~^ multiple_unsafe_ops_per_block - x.0(); - x.0(); - } -} - -// await expands to an unsafe block with several operations, but this is fine.: #11312 -async fn await_desugaring_silent() { +// await expands to an unsafe block with several operations, but this is fine. +async fn issue11312() { async fn helper() {} helper().await; } +async fn issue13879() { + async fn foo() {} + + // no lint: nothing unsafe beyond the `await` which we ignore + unsafe { + foo().await; + } + + // no lint: only one unsafe call beyond the `await` + unsafe { + not_very_safe(); + foo().await; + } + + // lint: two unsafe calls beyond the `await` + unsafe { + //~^ multiple_unsafe_ops_per_block + not_very_safe(); + STATIC += 1; + foo().await; + } + + async unsafe fn foo_unchecked() {} + + // no lint: only one unsafe call in the `await`ed expr + unsafe { + foo_unchecked().await; + } + + // lint: one unsafe call in the `await`ed expr, and one outside + unsafe { + //~^ multiple_unsafe_ops_per_block + not_very_safe(); + foo_unchecked().await; + } + + // lint: two unsafe calls in the `await`ed expr + unsafe { + //~^ multiple_unsafe_ops_per_block + Some(foo_unchecked()).unwrap_unchecked().await; + } +} + fn main() {} diff --git a/tests/ui/multiple_unsafe_ops_per_block.stderr b/tests/ui/multiple_unsafe_ops_per_block.stderr index 3130cecc2525..922a464c6b6e 100644 --- a/tests/ui/multiple_unsafe_ops_per_block.stderr +++ b/tests/ui/multiple_unsafe_ops_per_block.stderr @@ -113,84 +113,147 @@ LL | asm!("nop"); | ^^^^^^^^^^^ error: this `unsafe` block contains 2 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:111:5 + --> tests/ui/multiple_unsafe_ops_per_block.rs:110:9 | -LL | unsafe { char::from_u32_unchecked(*ptr.cast::()) } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | unsafe { char::from_u32_unchecked(*ptr.cast::()) } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:111:14 + --> tests/ui/multiple_unsafe_ops_per_block.rs:110:18 | -LL | unsafe { char::from_u32_unchecked(*ptr.cast::()) } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | unsafe { char::from_u32_unchecked(*ptr.cast::()) } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: raw pointer dereference occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:111:39 + --> tests/ui/multiple_unsafe_ops_per_block.rs:110:43 | -LL | unsafe { char::from_u32_unchecked(*ptr.cast::()) } - | ^^^^^^^^^^^^^^^^^^ +LL | unsafe { char::from_u32_unchecked(*ptr.cast::()) } + | ^^^^^^^^^^^^^^^^^^ error: this `unsafe` block contains 2 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:130:5 - | -LL | / unsafe { -LL | | -LL | | x(); -LL | | x(); -LL | | } - | |_____^ - | -note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:132:9 - | -LL | x(); - | ^^^ -note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:133:9 - | -LL | x(); - | ^^^ - -error: this `unsafe` block contains 2 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:142:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:131:9 | LL | / unsafe { LL | | -LL | | T::X(); -LL | | T::X(); +LL | | x(); +LL | | x(); LL | | } | |_________^ | note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:144:13 + --> tests/ui/multiple_unsafe_ops_per_block.rs:133:13 | -LL | T::X(); - | ^^^^^^ +LL | x(); + | ^^^ note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:145:13 + --> tests/ui/multiple_unsafe_ops_per_block.rs:134:13 | -LL | T::X(); - | ^^^^^^ +LL | x(); + | ^^^ error: this `unsafe` block contains 2 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:153:5 + --> tests/ui/multiple_unsafe_ops_per_block.rs:143:13 + | +LL | / unsafe { +LL | | +LL | | T::X(); +LL | | T::X(); +LL | | } + | |_____________^ + | +note: unsafe function call occurs here + --> tests/ui/multiple_unsafe_ops_per_block.rs:145:17 + | +LL | T::X(); + | ^^^^^^ +note: unsafe function call occurs here + --> tests/ui/multiple_unsafe_ops_per_block.rs:146:17 + | +LL | T::X(); + | ^^^^^^ + +error: this `unsafe` block contains 2 unsafe operations, expected only one + --> tests/ui/multiple_unsafe_ops_per_block.rs:154:9 + | +LL | / unsafe { +LL | | +LL | | x.0(); +LL | | x.0(); +LL | | } + | |_________^ + | +note: unsafe function call occurs here + --> tests/ui/multiple_unsafe_ops_per_block.rs:156:13 + | +LL | x.0(); + | ^^^^^ +note: unsafe function call occurs here + --> tests/ui/multiple_unsafe_ops_per_block.rs:157:13 + | +LL | x.0(); + | ^^^^^ + +error: this `unsafe` block contains 2 unsafe operations, expected only one + --> tests/ui/multiple_unsafe_ops_per_block.rs:184:5 | LL | / unsafe { LL | | -LL | | x.0(); -LL | | x.0(); +LL | | not_very_safe(); +LL | | STATIC += 1; +LL | | foo().await; LL | | } | |_____^ | note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:155:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:186:9 + | +LL | not_very_safe(); + | ^^^^^^^^^^^^^^^ +note: modification of a mutable static occurs here + --> tests/ui/multiple_unsafe_ops_per_block.rs:187:9 + | +LL | STATIC += 1; + | ^^^^^^^^^^^ + +error: this `unsafe` block contains 2 unsafe operations, expected only one + --> tests/ui/multiple_unsafe_ops_per_block.rs:199:5 + | +LL | / unsafe { +LL | | +LL | | not_very_safe(); +LL | | foo_unchecked().await; +LL | | } + | |_____^ | -LL | x.0(); - | ^^^^^ note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:156:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:201:9 | -LL | x.0(); - | ^^^^^ +LL | not_very_safe(); + | ^^^^^^^^^^^^^^^ +note: unsafe function call occurs here + --> tests/ui/multiple_unsafe_ops_per_block.rs:202:9 + | +LL | foo_unchecked().await; + | ^^^^^^^^^^^^^^^ -error: aborting due to 8 previous errors +error: this `unsafe` block contains 2 unsafe operations, expected only one + --> tests/ui/multiple_unsafe_ops_per_block.rs:206:5 + | +LL | / unsafe { +LL | | +LL | | Some(foo_unchecked()).unwrap_unchecked().await; +LL | | } + | |_____^ + | +note: unsafe method call occurs here + --> tests/ui/multiple_unsafe_ops_per_block.rs:208:9 + | +LL | Some(foo_unchecked()).unwrap_unchecked().await; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: unsafe function call occurs here + --> tests/ui/multiple_unsafe_ops_per_block.rs:208:14 + | +LL | Some(foo_unchecked()).unwrap_unchecked().await; + | ^^^^^^^^^^^^^^^ + +error: aborting due to 11 previous errors diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed index d571b97f5194..f5f8bb21e815 100644 --- a/tests/ui/needless_return.fixed +++ b/tests/ui/needless_return.fixed @@ -517,3 +517,10 @@ mod else_ifs { } } } + +fn issue14474() -> u64 { + return 456; + + #[cfg(false)] + 123 +} diff --git a/tests/ui/needless_return.rs b/tests/ui/needless_return.rs index 2e4348ea338c..495516c1c2e5 100644 --- a/tests/ui/needless_return.rs +++ b/tests/ui/needless_return.rs @@ -526,3 +526,10 @@ mod else_ifs { } } } + +fn issue14474() -> u64 { + return 456; + + #[cfg(false)] + 123 +} diff --git a/tests/ui/never_loop.rs b/tests/ui/never_loop.rs index 48d4b8ad1519..01db64a446c0 100644 --- a/tests/ui/never_loop.rs +++ b/tests/ui/never_loop.rs @@ -498,3 +498,27 @@ fn issue15059() { () } } + +fn issue15350() { + 'bar: for _ in 0..100 { + //~^ never_loop + loop { + //~^ never_loop + println!("This will still run"); + break 'bar; + } + } + + 'foo: for _ in 0..100 { + //~^ never_loop + loop { + //~^ never_loop + println!("This will still run"); + loop { + //~^ never_loop + println!("This will still run"); + break 'foo; + } + } + } +} diff --git a/tests/ui/never_loop.stderr b/tests/ui/never_loop.stderr index 54b463266a3a..4fda06cff4ab 100644 --- a/tests/ui/never_loop.stderr +++ b/tests/ui/never_loop.stderr @@ -193,6 +193,19 @@ LL | | return; LL | | } | |_____^ | +help: this code is unreachable. Consider moving the reachable parts out + --> tests/ui/never_loop.rs:436:9 + | +LL | / loop { +LL | | +LL | | break 'outer; +LL | | } + | |_________^ +help: this code is unreachable. Consider moving the reachable parts out + --> tests/ui/never_loop.rs:440:9 + | +LL | return; + | ^^^^^^^ help: if you need the first element of the iterator, try writing | LL - 'outer: for v in 0..10 { @@ -297,5 +310,87 @@ LL ~ LL ~ | -error: aborting due to 24 previous errors +error: this loop never actually loops + --> tests/ui/never_loop.rs:503:5 + | +LL | / 'bar: for _ in 0..100 { +LL | | +LL | | loop { +... | +LL | | } + | |_____^ + | +help: this code is unreachable. Consider moving the reachable parts out + --> tests/ui/never_loop.rs:505:9 + | +LL | / loop { +LL | | +LL | | println!("This will still run"); +LL | | break 'bar; +LL | | } + | |_________^ +help: if you need the first element of the iterator, try writing + | +LL - 'bar: for _ in 0..100 { +LL + if let Some(_) = (0..100).next() { + | + +error: this loop never actually loops + --> tests/ui/never_loop.rs:505:9 + | +LL | / loop { +LL | | +LL | | println!("This will still run"); +LL | | break 'bar; +LL | | } + | |_________^ + +error: this loop never actually loops + --> tests/ui/never_loop.rs:512:5 + | +LL | / 'foo: for _ in 0..100 { +LL | | +LL | | loop { +... | +LL | | } + | |_____^ + | +help: this code is unreachable. Consider moving the reachable parts out + --> tests/ui/never_loop.rs:514:9 + | +LL | / loop { +LL | | +LL | | println!("This will still run"); +LL | | loop { +... | +LL | | } + | |_________^ +help: if you need the first element of the iterator, try writing + | +LL - 'foo: for _ in 0..100 { +LL + if let Some(_) = (0..100).next() { + | + +error: this loop never actually loops + --> tests/ui/never_loop.rs:514:9 + | +LL | / loop { +LL | | +LL | | println!("This will still run"); +LL | | loop { +... | +LL | | } + | |_________^ + +error: this loop never actually loops + --> tests/ui/never_loop.rs:517:13 + | +LL | / loop { +LL | | +LL | | println!("This will still run"); +LL | | break 'foo; +LL | | } + | |_____________^ + +error: aborting due to 29 previous errors diff --git a/tests/ui/option_if_let_else.fixed b/tests/ui/option_if_let_else.fixed index 0f86de5646cd..6ce067f5c246 100644 --- a/tests/ui/option_if_let_else.fixed +++ b/tests/ui/option_if_let_else.fixed @@ -125,6 +125,16 @@ fn complex_subpat() -> DummyEnum { DummyEnum::Two } +// #10335 +pub fn test_result_err_ignored_1(r: Result<&[u8], &[u8]>) -> Vec { + r.map_or_else(|_| Vec::new(), |s| s.to_owned()) +} + +// #10335 +pub fn test_result_err_ignored_2(r: Result<&[u8], &[u8]>) -> Vec { + r.map_or_else(|_| Vec::new(), |s| s.to_owned()) +} + fn main() { let optional = Some(5); let _ = optional.map_or(5, |x| x + 2); diff --git a/tests/ui/option_if_let_else.rs b/tests/ui/option_if_let_else.rs index 7aabd778f87e..096d3aabf28d 100644 --- a/tests/ui/option_if_let_else.rs +++ b/tests/ui/option_if_let_else.rs @@ -152,6 +152,22 @@ fn complex_subpat() -> DummyEnum { DummyEnum::Two } +// #10335 +pub fn test_result_err_ignored_1(r: Result<&[u8], &[u8]>) -> Vec { + match r { + //~^ option_if_let_else + Ok(s) => s.to_owned(), + Err(_) => Vec::new(), + } +} + +// #10335 +pub fn test_result_err_ignored_2(r: Result<&[u8], &[u8]>) -> Vec { + if let Ok(s) = r { s.to_owned() } + //~^ option_if_let_else + else { Vec::new() } +} + fn main() { let optional = Some(5); let _ = if let Some(x) = optional { x + 2 } else { 5 }; diff --git a/tests/ui/option_if_let_else.stderr b/tests/ui/option_if_let_else.stderr index 2e2fe6f20492..21a80ae038d8 100644 --- a/tests/ui/option_if_let_else.stderr +++ b/tests/ui/option_if_let_else.stderr @@ -188,14 +188,32 @@ LL + true LL + }) | +error: use Option::map_or_else instead of an if let/else + --> tests/ui/option_if_let_else.rs:157:5 + | +LL | / match r { +LL | | +LL | | Ok(s) => s.to_owned(), +LL | | Err(_) => Vec::new(), +LL | | } + | |_____^ help: try: `r.map_or_else(|_| Vec::new(), |s| s.to_owned())` + +error: use Option::map_or_else instead of an if let/else + --> tests/ui/option_if_let_else.rs:166:5 + | +LL | / if let Ok(s) = r { s.to_owned() } +LL | | +LL | | else { Vec::new() } + | |_______________________^ help: try: `r.map_or_else(|_| Vec::new(), |s| s.to_owned())` + error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:157:13 + --> tests/ui/option_if_let_else.rs:173:13 | LL | let _ = if let Some(x) = optional { x + 2 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:168:13 + --> tests/ui/option_if_let_else.rs:184:13 | LL | let _ = if let Some(x) = Some(0) { | _____________^ @@ -217,13 +235,13 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:197:13 + --> tests/ui/option_if_let_else.rs:213:13 | LL | let _ = if let Some(x) = Some(0) { s.len() + x } else { s.len() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(0).map_or(s.len(), |x| s.len() + x)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:202:13 + --> tests/ui/option_if_let_else.rs:218:13 | LL | let _ = if let Some(x) = Some(0) { | _____________^ @@ -245,7 +263,7 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:242:13 + --> tests/ui/option_if_let_else.rs:258:13 | LL | let _ = match s { | _____________^ @@ -256,7 +274,7 @@ LL | | }; | |_____^ help: try: `s.map_or(1, |string| string.len())` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:247:13 + --> tests/ui/option_if_let_else.rs:263:13 | LL | let _ = match Some(10) { | _____________^ @@ -267,7 +285,7 @@ LL | | }; | |_____^ help: try: `Some(10).map_or(5, |a| a + 1)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:254:13 + --> tests/ui/option_if_let_else.rs:270:13 | LL | let _ = match res { | _____________^ @@ -278,7 +296,7 @@ LL | | }; | |_____^ help: try: `res.map_or(1, |a| a + 1)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:259:13 + --> tests/ui/option_if_let_else.rs:275:13 | LL | let _ = match res { | _____________^ @@ -289,13 +307,13 @@ LL | | }; | |_____^ help: try: `res.map_or(1, |a| a + 1)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:264:13 + --> tests/ui/option_if_let_else.rs:280:13 | LL | let _ = if let Ok(a) = res { a + 1 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `res.map_or(5, |a| a + 1)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:282:17 + --> tests/ui/option_if_let_else.rs:298:17 | LL | let _ = match initial { | _________________^ @@ -306,7 +324,7 @@ LL | | }; | |_________^ help: try: `initial.as_ref().map_or(42, |value| do_something(value))` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:290:17 + --> tests/ui/option_if_let_else.rs:306:17 | LL | let _ = match initial { | _________________^ @@ -317,7 +335,7 @@ LL | | }; | |_________^ help: try: `initial.as_mut().map_or(42, |value| do_something2(value))` error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:314:24 + --> tests/ui/option_if_let_else.rs:330:24 | LL | let mut _hashmap = if let Some(hm) = &opt { | ________________________^ @@ -329,19 +347,19 @@ LL | | }; | |_____^ help: try: `opt.as_ref().map_or_else(HashMap::new, |hm| hm.clone())` error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:321:19 + --> tests/ui/option_if_let_else.rs:337:19 | LL | let mut _hm = if let Some(hm) = &opt { hm.clone() } else { new_map!() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.as_ref().map_or_else(|| new_map!(), |hm| hm.clone())` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:372:22 + --> tests/ui/option_if_let_else.rs:388:22 | LL | let _ = unsafe { if let Some(o) = *opt_raw_ptr { o } else { 1 } }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(*opt_raw_ptr).map_or(1, |o| o)` error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:378:13 + --> tests/ui/option_if_let_else.rs:394:13 | LL | let _ = match res { | _____________^ @@ -351,5 +369,5 @@ LL | | Err(_) => String::new(), LL | | }; | |_____^ help: try: `res.map_or_else(|_| String::new(), |s| s.clone())` -error: aborting due to 27 previous errors +error: aborting due to 29 previous errors diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 0a8525a12f5e..7a0be97017eb 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -77,6 +77,22 @@ fn or_fun_call() { with_default_type.unwrap_or_default(); //~^ unwrap_or_default + let with_default_literal = Some(1); + with_default_literal.unwrap_or(0); + // Do not lint because `.unwrap_or_default()` wouldn't be simpler + + let with_default_literal = Some(1.0); + with_default_literal.unwrap_or(0.0); + // Do not lint because `.unwrap_or_default()` wouldn't be simpler + + let with_default_literal = Some("foo"); + with_default_literal.unwrap_or(""); + // Do not lint because `.unwrap_or_default()` wouldn't be simpler + + let with_default_vec_macro = Some(vec![1, 2, 3]); + with_default_vec_macro.unwrap_or(vec![]); + // Do not lint because `.unwrap_or_default()` wouldn't be simpler + let self_default = None::; self_default.unwrap_or_else(::default); //~^ or_fun_call diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index b4f9b950a7fe..724af606de9c 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -77,6 +77,22 @@ fn or_fun_call() { with_default_type.unwrap_or(u64::default()); //~^ unwrap_or_default + let with_default_literal = Some(1); + with_default_literal.unwrap_or(0); + // Do not lint because `.unwrap_or_default()` wouldn't be simpler + + let with_default_literal = Some(1.0); + with_default_literal.unwrap_or(0.0); + // Do not lint because `.unwrap_or_default()` wouldn't be simpler + + let with_default_literal = Some("foo"); + with_default_literal.unwrap_or(""); + // Do not lint because `.unwrap_or_default()` wouldn't be simpler + + let with_default_vec_macro = Some(vec![1, 2, 3]); + with_default_vec_macro.unwrap_or(vec![]); + // Do not lint because `.unwrap_or_default()` wouldn't be simpler + let self_default = None::; self_default.unwrap_or(::default()); //~^ or_fun_call @@ -86,7 +102,7 @@ fn or_fun_call() { //~^ unwrap_or_default let with_vec = Some(vec![1]); - with_vec.unwrap_or(vec![]); + with_vec.unwrap_or(Vec::new()); //~^ unwrap_or_default let without_default = Some(Foo); @@ -98,7 +114,7 @@ fn or_fun_call() { //~^ unwrap_or_default let mut map_vec = HashMap::>::new(); - map_vec.entry(42).or_insert(vec![]); + map_vec.entry(42).or_insert(Vec::new()); //~^ unwrap_or_default let mut btree = BTreeMap::::new(); @@ -106,7 +122,7 @@ fn or_fun_call() { //~^ unwrap_or_default let mut btree_vec = BTreeMap::>::new(); - btree_vec.entry(42).or_insert(vec![]); + btree_vec.entry(42).or_insert(Vec::new()); //~^ unwrap_or_default let stringy = Some(String::new()); diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index 3e4df772668d..40b25f91154d 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -47,175 +47,175 @@ LL | with_default_type.unwrap_or(u64::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:81:18 + --> tests/ui/or_fun_call.rs:97:18 | LL | self_default.unwrap_or(::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(::default)` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:85:18 + --> tests/ui/or_fun_call.rs:101:18 | LL | real_default.unwrap_or(::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:89:14 + --> tests/ui/or_fun_call.rs:105:14 | -LL | with_vec.unwrap_or(vec![]); - | ^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` +LL | with_vec.unwrap_or(Vec::new()); + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:93:21 + --> tests/ui/or_fun_call.rs:109:21 | LL | without_default.unwrap_or(Foo::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(Foo::new)` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:97:19 + --> tests/ui/or_fun_call.rs:113:19 | LL | map.entry(42).or_insert(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:101:23 + --> tests/ui/or_fun_call.rs:117:23 | -LL | map_vec.entry(42).or_insert(vec![]); - | ^^^^^^^^^^^^^^^^^ help: try: `or_default()` +LL | map_vec.entry(42).or_insert(Vec::new()); + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:105:21 + --> tests/ui/or_fun_call.rs:121:21 | LL | btree.entry(42).or_insert(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:109:25 + --> tests/ui/or_fun_call.rs:125:25 | -LL | btree_vec.entry(42).or_insert(vec![]); - | ^^^^^^^^^^^^^^^^^ help: try: `or_default()` +LL | btree_vec.entry(42).or_insert(Vec::new()); + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:113:21 + --> tests/ui/or_fun_call.rs:129:21 | LL | let _ = stringy.unwrap_or(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `ok_or` - --> tests/ui/or_fun_call.rs:118:17 + --> tests/ui/or_fun_call.rs:134:17 | LL | let _ = opt.ok_or(format!("{} world.", hello)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ok_or_else(|| format!("{} world.", hello))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:123:21 + --> tests/ui/or_fun_call.rs:139:21 | LL | let _ = Some(1).unwrap_or(map[&1]); | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| map[&1])` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:126:21 + --> tests/ui/or_fun_call.rs:142:21 | LL | let _ = Some(1).unwrap_or(map[&1]); | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| map[&1])` error: function call inside of `or` - --> tests/ui/or_fun_call.rs:151:35 + --> tests/ui/or_fun_call.rs:167:35 | LL | let _ = Some("a".to_string()).or(Some("b".to_string())); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_else(|| Some("b".to_string()))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:194:18 + --> tests/ui/or_fun_call.rs:210:18 | LL | None.unwrap_or(ptr_to_ref(s)); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| ptr_to_ref(s))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:202:14 + --> tests/ui/or_fun_call.rs:218:14 | LL | None.unwrap_or(unsafe { ptr_to_ref(s) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:205:14 + --> tests/ui/or_fun_call.rs:221:14 | LL | None.unwrap_or( unsafe { ptr_to_ref(s) } ); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:281:25 + --> tests/ui/or_fun_call.rs:297:25 | LL | let _ = Some(4).map_or(g(), |v| v); | ^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(g, |v| v)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:283:25 + --> tests/ui/or_fun_call.rs:299:25 | LL | let _ = Some(4).map_or(g(), f); | ^^^^^^^^^^^^^^ help: try: `map_or_else(g, f)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:286:25 + --> tests/ui/or_fun_call.rs:302:25 | LL | let _ = Some(4).map_or("asd".to_string().len() as i32, f); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|| "asd".to_string().len() as i32, f)` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:317:18 + --> tests/ui/or_fun_call.rs:333:18 | LL | with_new.unwrap_or_else(Vec::new); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:321:28 + --> tests/ui/or_fun_call.rs:337:28 | LL | with_default_trait.unwrap_or_else(Default::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:325:27 + --> tests/ui/or_fun_call.rs:341:27 | LL | with_default_type.unwrap_or_else(u64::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:329:22 + --> tests/ui/or_fun_call.rs:345:22 | LL | real_default.unwrap_or_else(::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `or_insert_with` to construct default value - --> tests/ui/or_fun_call.rs:333:23 + --> tests/ui/or_fun_call.rs:349:23 | LL | map.entry(42).or_insert_with(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert_with` to construct default value - --> tests/ui/or_fun_call.rs:337:25 + --> tests/ui/or_fun_call.rs:353:25 | LL | btree.entry(42).or_insert_with(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:341:25 + --> tests/ui/or_fun_call.rs:357:25 | LL | let _ = stringy.unwrap_or_else(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:383:17 + --> tests/ui/or_fun_call.rs:399:17 | LL | let _ = opt.unwrap_or({ f() }); // suggest `.unwrap_or_else(f)` | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(f)` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:388:17 + --> tests/ui/or_fun_call.rs:404:17 | LL | let _ = opt.unwrap_or(f() + 1); // suggest `.unwrap_or_else(|| f() + 1)` | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| f() + 1)` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:393:17 + --> tests/ui/or_fun_call.rs:409:17 | LL | let _ = opt.unwrap_or({ | _________________^ @@ -235,55 +235,55 @@ LL ~ }); | error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:399:17 + --> tests/ui/or_fun_call.rs:415:17 | LL | let _ = opt.map_or(f() + 1, |v| v); // suggest `.map_or_else(|| f() + 1, |v| v)` | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|| f() + 1, |v| v)` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:404:17 + --> tests/ui/or_fun_call.rs:420:17 | LL | let _ = opt.unwrap_or({ i32::default() }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:411:21 + --> tests/ui/or_fun_call.rs:427:21 | LL | let _ = opt_foo.unwrap_or(Foo { val: String::default() }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| Foo { val: String::default() })` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:426:19 + --> tests/ui/or_fun_call.rs:442:19 | LL | let _ = x.map_or(g(), |v| v); | ^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|_| g(), |v| v)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:428:19 + --> tests/ui/or_fun_call.rs:444:19 | LL | let _ = x.map_or(g(), f); | ^^^^^^^^^^^^^^ help: try: `map_or_else(|_| g(), f)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:431:19 + --> tests/ui/or_fun_call.rs:447:19 | LL | let _ = x.map_or("asd".to_string().len() as i32, f); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|_| "asd".to_string().len() as i32, f)` error: function call inside of `get_or_insert` - --> tests/ui/or_fun_call.rs:442:15 + --> tests/ui/or_fun_call.rs:458:15 | LL | let _ = x.get_or_insert(g()); | ^^^^^^^^^^^^^^^^^^ help: try: `get_or_insert_with(g)` error: function call inside of `and` - --> tests/ui/or_fun_call.rs:452:15 + --> tests/ui/or_fun_call.rs:468:15 | LL | let _ = x.and(g()); | ^^^^^^^^ help: try: `and_then(|_| g())` error: function call inside of `and` - --> tests/ui/or_fun_call.rs:462:15 + --> tests/ui/or_fun_call.rs:478:15 | LL | let _ = x.and(g()); | ^^^^^^^^ help: try: `and_then(|_| g())` diff --git a/tests/ui/ptr_offset_with_cast.fixed b/tests/ui/ptr_offset_with_cast.fixed index 4fe9dcf46c35..42d1abeaa05a 100644 --- a/tests/ui/ptr_offset_with_cast.fixed +++ b/tests/ui/ptr_offset_with_cast.fixed @@ -1,4 +1,4 @@ -#![allow(clippy::unnecessary_cast, clippy::useless_vec)] +#![expect(clippy::unnecessary_cast, clippy::useless_vec, clippy::needless_borrow)] fn main() { let vec = vec![b'a', b'b', b'c']; @@ -18,5 +18,25 @@ fn main() { //~^ ptr_offset_with_cast let _ = ptr.wrapping_offset(offset_isize as isize); let _ = ptr.wrapping_offset(offset_u8 as isize); + + let _ = S.offset(offset_usize as isize); + let _ = S.wrapping_offset(offset_usize as isize); + + let _ = (&ptr).add(offset_usize); + //~^ ptr_offset_with_cast + let _ = (&ptr).wrapping_add(offset_usize); + //~^ ptr_offset_with_cast + } +} + +#[derive(Clone, Copy)] +struct S; + +impl S { + fn offset(self, _: isize) -> Self { + self + } + fn wrapping_offset(self, _: isize) -> Self { + self } } diff --git a/tests/ui/ptr_offset_with_cast.rs b/tests/ui/ptr_offset_with_cast.rs index a1fb892733d3..6d06a6af1fa2 100644 --- a/tests/ui/ptr_offset_with_cast.rs +++ b/tests/ui/ptr_offset_with_cast.rs @@ -1,4 +1,4 @@ -#![allow(clippy::unnecessary_cast, clippy::useless_vec)] +#![expect(clippy::unnecessary_cast, clippy::useless_vec, clippy::needless_borrow)] fn main() { let vec = vec![b'a', b'b', b'c']; @@ -18,5 +18,25 @@ fn main() { //~^ ptr_offset_with_cast let _ = ptr.wrapping_offset(offset_isize as isize); let _ = ptr.wrapping_offset(offset_u8 as isize); + + let _ = S.offset(offset_usize as isize); + let _ = S.wrapping_offset(offset_usize as isize); + + let _ = (&ptr).offset(offset_usize as isize); + //~^ ptr_offset_with_cast + let _ = (&ptr).wrapping_offset(offset_usize as isize); + //~^ ptr_offset_with_cast + } +} + +#[derive(Clone, Copy)] +struct S; + +impl S { + fn offset(self, _: isize) -> Self { + self + } + fn wrapping_offset(self, _: isize) -> Self { + self } } diff --git a/tests/ui/ptr_offset_with_cast.stderr b/tests/ui/ptr_offset_with_cast.stderr index dcd5e027d182..022b3286c93b 100644 --- a/tests/ui/ptr_offset_with_cast.stderr +++ b/tests/ui/ptr_offset_with_cast.stderr @@ -2,16 +2,51 @@ error: use of `offset` with a `usize` casted to an `isize` --> tests/ui/ptr_offset_with_cast.rs:12:17 | LL | let _ = ptr.offset(offset_usize as isize); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr.add(offset_usize)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::ptr-offset-with-cast` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::ptr_offset_with_cast)]` +help: use `add` instead + | +LL - let _ = ptr.offset(offset_usize as isize); +LL + let _ = ptr.add(offset_usize); + | error: use of `wrapping_offset` with a `usize` casted to an `isize` --> tests/ui/ptr_offset_with_cast.rs:17:17 | LL | let _ = ptr.wrapping_offset(offset_usize as isize); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr.wrapping_add(offset_usize)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `wrapping_add` instead + | +LL - let _ = ptr.wrapping_offset(offset_usize as isize); +LL + let _ = ptr.wrapping_add(offset_usize); + | -error: aborting due to 2 previous errors +error: use of `offset` with a `usize` casted to an `isize` + --> tests/ui/ptr_offset_with_cast.rs:25:17 + | +LL | let _ = (&ptr).offset(offset_usize as isize); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `add` instead + | +LL - let _ = (&ptr).offset(offset_usize as isize); +LL + let _ = (&ptr).add(offset_usize); + | + +error: use of `wrapping_offset` with a `usize` casted to an `isize` + --> tests/ui/ptr_offset_with_cast.rs:27:17 + | +LL | let _ = (&ptr).wrapping_offset(offset_usize as isize); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `wrapping_add` instead + | +LL - let _ = (&ptr).wrapping_offset(offset_usize as isize); +LL + let _ = (&ptr).wrapping_add(offset_usize); + | + +error: aborting due to 4 previous errors diff --git a/tests/ui/question_mark.fixed b/tests/ui/question_mark.fixed index 8d6f5fbadca5..ac81b324c204 100644 --- a/tests/ui/question_mark.fixed +++ b/tests/ui/question_mark.fixed @@ -1,6 +1,4 @@ #![feature(try_blocks)] -#![allow(unreachable_code)] -#![allow(dead_code)] #![allow(clippy::unnecessary_wraps)] use std::sync::MutexGuard; @@ -465,3 +463,15 @@ fn issue_13642(x: Option) -> Option<()> { None } + +fn issue_15679() -> Result { + let some_result: Result = todo!(); + + some_result?; + + some_result?; + + some_result?; + + Ok(0) +} diff --git a/tests/ui/question_mark.rs b/tests/ui/question_mark.rs index f13eee29c113..b5866dac6b8f 100644 --- a/tests/ui/question_mark.rs +++ b/tests/ui/question_mark.rs @@ -1,6 +1,4 @@ #![feature(try_blocks)] -#![allow(unreachable_code)] -#![allow(dead_code)] #![allow(clippy::unnecessary_wraps)] use std::sync::MutexGuard; @@ -561,3 +559,27 @@ fn issue_13642(x: Option) -> Option<()> { None } + +fn issue_15679() -> Result { + let some_result: Result = todo!(); + + match some_result { + //~^ question_mark + Ok(val) => val, + Err(err) => return Err(err.into()), + }; + + match some_result { + //~^ question_mark + Ok(val) => val, + Err(err) => return Err(Into::into(err)), + }; + + match some_result { + //~^ question_mark + Ok(val) => val, + Err(err) => return Err(<&str as Into>::into(err)), + }; + + Ok(0) +} diff --git a/tests/ui/question_mark.stderr b/tests/ui/question_mark.stderr index d8ce4420aeeb..1ecd936292e5 100644 --- a/tests/ui/question_mark.stderr +++ b/tests/ui/question_mark.stderr @@ -1,5 +1,5 @@ error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:9:5 + --> tests/ui/question_mark.rs:7:5 | LL | / if a.is_none() { LL | | @@ -11,7 +11,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::question_mark)]` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:55:9 + --> tests/ui/question_mark.rs:53:9 | LL | / if (self.opt).is_none() { LL | | @@ -20,7 +20,7 @@ LL | | } | |_________^ help: replace it with: `(self.opt)?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:60:9 + --> tests/ui/question_mark.rs:58:9 | LL | / if self.opt.is_none() { LL | | @@ -29,7 +29,7 @@ LL | | } | |_________^ help: replace it with: `self.opt?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:65:17 + --> tests/ui/question_mark.rs:63:17 | LL | let _ = if self.opt.is_none() { | _________________^ @@ -41,7 +41,7 @@ LL | | }; | |_________^ help: replace it with: `Some(self.opt?)` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:72:17 + --> tests/ui/question_mark.rs:70:17 | LL | let _ = if let Some(x) = self.opt { | _________________^ @@ -53,7 +53,7 @@ LL | | }; | |_________^ help: replace it with: `self.opt?` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:90:9 + --> tests/ui/question_mark.rs:88:9 | LL | / if self.opt.is_none() { LL | | @@ -62,7 +62,7 @@ LL | | } | |_________^ help: replace it with: `self.opt.as_ref()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:99:9 + --> tests/ui/question_mark.rs:97:9 | LL | / if self.opt.is_none() { LL | | @@ -71,7 +71,7 @@ LL | | } | |_________^ help: replace it with: `self.opt.as_ref()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:108:9 + --> tests/ui/question_mark.rs:106:9 | LL | / if self.opt.is_none() { LL | | @@ -80,7 +80,7 @@ LL | | } | |_________^ help: replace it with: `self.opt.as_ref()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:116:26 + --> tests/ui/question_mark.rs:114:26 | LL | let v: &Vec<_> = if let Some(ref v) = self.opt { | __________________________^ @@ -92,7 +92,7 @@ LL | | }; | |_________^ help: replace it with: `self.opt.as_ref()?` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:127:17 + --> tests/ui/question_mark.rs:125:17 | LL | let v = if let Some(v) = self.opt { | _________________^ @@ -104,7 +104,7 @@ LL | | }; | |_________^ help: replace it with: `self.opt?` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:149:5 + --> tests/ui/question_mark.rs:147:5 | LL | / if f().is_none() { LL | | @@ -113,7 +113,7 @@ LL | | } | |_____^ help: replace it with: `f()?;` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:154:16 + --> tests/ui/question_mark.rs:152:16 | LL | let _val = match f() { | ________________^ @@ -124,7 +124,7 @@ LL | | }; | |_____^ help: try instead: `f()?` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:165:5 + --> tests/ui/question_mark.rs:163:5 | LL | / match f() { LL | | @@ -134,7 +134,7 @@ LL | | }; | |_____^ help: try instead: `f()?` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:171:5 + --> tests/ui/question_mark.rs:169:5 | LL | / match opt_none!() { LL | | @@ -144,13 +144,13 @@ LL | | }; | |_____^ help: try instead: `opt_none!()?` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:198:13 + --> tests/ui/question_mark.rs:196:13 | LL | let _ = if let Ok(x) = x { x } else { return x }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x?` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:201:5 + --> tests/ui/question_mark.rs:199:5 | LL | / if x.is_err() { LL | | @@ -159,7 +159,7 @@ LL | | } | |_____^ help: replace it with: `x?;` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:206:16 + --> tests/ui/question_mark.rs:204:16 | LL | let _val = match func_returning_result() { | ________________^ @@ -170,7 +170,7 @@ LL | | }; | |_____^ help: try instead: `func_returning_result()?` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:212:5 + --> tests/ui/question_mark.rs:210:5 | LL | / match func_returning_result() { LL | | @@ -180,7 +180,7 @@ LL | | }; | |_____^ help: try instead: `func_returning_result()?` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:304:5 + --> tests/ui/question_mark.rs:302:5 | LL | / if let Err(err) = func_returning_result() { LL | | @@ -189,7 +189,7 @@ LL | | } | |_____^ help: replace it with: `func_returning_result()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:312:5 + --> tests/ui/question_mark.rs:310:5 | LL | / if let Err(err) = func_returning_result() { LL | | @@ -198,7 +198,7 @@ LL | | } | |_____^ help: replace it with: `func_returning_result()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:395:13 + --> tests/ui/question_mark.rs:393:13 | LL | / if a.is_none() { LL | | @@ -208,7 +208,7 @@ LL | | } | |_____________^ help: replace it with: `a?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:456:5 + --> tests/ui/question_mark.rs:454:5 | LL | / let Some(v) = bar.foo.owned.clone() else { LL | | return None; @@ -216,7 +216,7 @@ LL | | }; | |______^ help: replace it with: `let v = bar.foo.owned.clone()?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:471:5 + --> tests/ui/question_mark.rs:469:5 | LL | / let Some(ref x) = foo.opt_x else { LL | | return None; @@ -224,7 +224,7 @@ LL | | }; | |______^ help: replace it with: `let x = foo.opt_x.as_ref()?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:481:5 + --> tests/ui/question_mark.rs:479:5 | LL | / let Some(ref mut x) = foo.opt_x else { LL | | return None; @@ -232,7 +232,7 @@ LL | | }; | |______^ help: replace it with: `let x = foo.opt_x.as_mut()?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:492:5 + --> tests/ui/question_mark.rs:490:5 | LL | / let Some(ref x @ ref y) = foo.opt_x else { LL | | return None; @@ -240,7 +240,7 @@ LL | | }; | |______^ help: replace it with: `let x @ y = foo.opt_x.as_ref()?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:496:5 + --> tests/ui/question_mark.rs:494:5 | LL | / let Some(ref x @ WrapperStructWithString(_)) = bar else { LL | | return None; @@ -248,7 +248,7 @@ LL | | }; | |______^ help: replace it with: `let x @ &WrapperStructWithString(_) = bar.as_ref()?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:500:5 + --> tests/ui/question_mark.rs:498:5 | LL | / let Some(ref mut x @ WrapperStructWithString(_)) = bar else { LL | | return None; @@ -256,7 +256,7 @@ LL | | }; | |______^ help: replace it with: `let x @ &mut WrapperStructWithString(_) = bar.as_mut()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:522:5 + --> tests/ui/question_mark.rs:520:5 | LL | / if arg.is_none() { LL | | @@ -265,7 +265,7 @@ LL | | } | |_____^ help: replace it with: `arg?;` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:526:15 + --> tests/ui/question_mark.rs:524:15 | LL | let val = match arg { | _______________^ @@ -276,12 +276,42 @@ LL | | }; | |_____^ help: try instead: `arg?` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:536:5 + --> tests/ui/question_mark.rs:534:5 | LL | / let Some(a) = *a else { LL | | return None; LL | | }; | |______^ help: replace it with: `let a = (*a)?;` -error: aborting due to 30 previous errors +error: this `match` expression can be replaced with `?` + --> tests/ui/question_mark.rs:566:5 + | +LL | / match some_result { +LL | | +LL | | Ok(val) => val, +LL | | Err(err) => return Err(err.into()), +LL | | }; + | |_____^ help: try instead: `some_result?` + +error: this `match` expression can be replaced with `?` + --> tests/ui/question_mark.rs:572:5 + | +LL | / match some_result { +LL | | +LL | | Ok(val) => val, +LL | | Err(err) => return Err(Into::into(err)), +LL | | }; + | |_____^ help: try instead: `some_result?` + +error: this `match` expression can be replaced with `?` + --> tests/ui/question_mark.rs:578:5 + | +LL | / match some_result { +LL | | +LL | | Ok(val) => val, +LL | | Err(err) => return Err(<&str as Into>::into(err)), +LL | | }; + | |_____^ help: try instead: `some_result?` + +error: aborting due to 33 previous errors diff --git a/tests/ui/read_zero_byte_vec.rs b/tests/ui/read_zero_byte_vec.rs index 938d61b68607..720276cb5548 100644 --- a/tests/ui/read_zero_byte_vec.rs +++ b/tests/ui/read_zero_byte_vec.rs @@ -120,3 +120,30 @@ fn allow_works(mut f: F) { } fn main() {} + +fn issue15575() -> usize { + use std::io::Read; + use std::net::TcpListener; + + let listener = TcpListener::bind("127.0.0.1:9010").unwrap(); + let mut stream_and_addr = listener.accept().unwrap(); + let mut buf = Vec::with_capacity(32); + let num_bytes_received = stream_and_addr.0.read(&mut buf).unwrap(); + //~^ read_zero_byte_vec + + let cap = 1000; + let mut buf = Vec::with_capacity(cap); + let num_bytes_received = stream_and_addr.0.read(&mut buf).unwrap(); + //~^ read_zero_byte_vec + + let cap = 1000; + let mut buf = Vec::with_capacity(cap); + let num_bytes_received = { stream_and_addr.0.read(&mut buf) }.unwrap(); + //~^ read_zero_byte_vec + + use std::fs::File; + let mut f = File::open("foo.txt").unwrap(); + let mut data = Vec::with_capacity(100); + f.read(&mut data).unwrap() + //~^ read_zero_byte_vec +} diff --git a/tests/ui/read_zero_byte_vec.stderr b/tests/ui/read_zero_byte_vec.stderr index 8f255bc87ab8..8dd74592e4c1 100644 --- a/tests/ui/read_zero_byte_vec.stderr +++ b/tests/ui/read_zero_byte_vec.stderr @@ -2,16 +2,27 @@ error: reading zero byte data to `Vec` --> tests/ui/read_zero_byte_vec.rs:22:5 | LL | f.read_exact(&mut data).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `data.resize(20, 0); f.read_exact(&mut data)` + | ^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::read-zero-byte-vec` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::read_zero_byte_vec)]` +help: try + | +LL ~ data.resize(20, 0); +LL ~ f.read_exact(&mut data).unwrap(); + | error: reading zero byte data to `Vec` --> tests/ui/read_zero_byte_vec.rs:28:5 | LL | f.read_exact(&mut data2)?; - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `data2.resize(cap, 0); f.read_exact(&mut data2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ data2.resize(cap, 0); +LL ~ f.read_exact(&mut data2)?; + | error: reading zero byte data to `Vec` --> tests/ui/read_zero_byte_vec.rs:33:5 @@ -67,5 +78,53 @@ error: reading zero byte data to `Vec` LL | r.read_exact(&mut data2).await.unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 11 previous errors +error: reading zero byte data to `Vec` + --> tests/ui/read_zero_byte_vec.rs:131:30 + | +LL | let num_bytes_received = stream_and_addr.0.read(&mut buf).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ buf.resize(32, 0); +LL ~ let num_bytes_received = stream_and_addr.0.read(&mut buf).unwrap(); + | + +error: reading zero byte data to `Vec` + --> tests/ui/read_zero_byte_vec.rs:136:30 + | +LL | let num_bytes_received = stream_and_addr.0.read(&mut buf).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ buf.resize(cap, 0); +LL ~ let num_bytes_received = stream_and_addr.0.read(&mut buf).unwrap(); + | + +error: reading zero byte data to `Vec` + --> tests/ui/read_zero_byte_vec.rs:141:32 + | +LL | let num_bytes_received = { stream_and_addr.0.read(&mut buf) }.unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ buf.resize(cap, 0); +LL ~ let num_bytes_received = { stream_and_addr.0.read(&mut buf) }.unwrap(); + | + +error: reading zero byte data to `Vec` + --> tests/ui/read_zero_byte_vec.rs:147:5 + | +LL | f.read(&mut data).unwrap() + | ^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ data.resize(100, 0); +LL ~ f.read(&mut data).unwrap() + | + +error: aborting due to 15 previous errors diff --git a/tests/ui/ref_option/ref_option.all.fixed b/tests/ui/ref_option/ref_option.all.fixed deleted file mode 100644 index 4159e916f199..000000000000 --- a/tests/ui/ref_option/ref_option.all.fixed +++ /dev/null @@ -1,79 +0,0 @@ -//@revisions: private all -//@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui/ref_option/private -//@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui/ref_option/all - -#![allow(unused, clippy::needless_lifetimes, clippy::borrowed_box)] -#![warn(clippy::ref_option)] - -fn opt_u8(a: Option<&u8>) {} -//~^ ref_option -fn opt_gen(a: Option<&T>) {} -//~^ ref_option -fn opt_string(a: std::option::Option<&String>) {} -//~^ ref_option -fn ret_string<'a>(p: &'a str) -> Option<&'a u8> { - //~^ ref_option - panic!() -} -fn ret_string_static() -> Option<&'static u8> { - //~^ ref_option - panic!() -} -fn mult_string(a: Option<&String>, b: Option<&Vec>) {} -//~^ ref_option -fn ret_box<'a>() -> Option<&'a Box> { - //~^ ref_option - panic!() -} - -pub fn pub_opt_string(a: Option<&String>) {} -//~[all]^ ref_option -pub fn pub_mult_string(a: Option<&String>, b: Option<&Vec>) {} -//~[all]^ ref_option - -pub trait PubTrait { - fn pub_trait_opt(&self, a: Option<&Vec>); - //~[all]^ ref_option - fn pub_trait_ret(&self) -> Option<&Vec>; - //~[all]^ ref_option -} - -trait PrivateTrait { - fn trait_opt(&self, a: Option<&String>); - //~^ ref_option - fn trait_ret(&self) -> Option<&String>; - //~^ ref_option -} - -pub struct PubStruct; - -impl PubStruct { - pub fn pub_opt_params(&self, a: Option<&()>) {} - //~[all]^ ref_option - pub fn pub_opt_ret(&self) -> Option<&String> { - //~[all]^ ref_option - panic!() - } - - fn private_opt_params(&self, a: Option<&()>) {} - //~^ ref_option - fn private_opt_ret(&self) -> Option<&String> { - //~^ ref_option - panic!() - } -} - -// valid, don't change -fn mut_u8(a: &mut Option) {} -pub fn pub_mut_u8(a: &mut Option) {} - -// might be good to catch in the future -fn mut_u8_ref(a: &mut &Option) {} -pub fn pub_mut_u8_ref(a: &mut &Option) {} -fn lambdas() { - // Not handled for now, not sure if we should - let x = |a: &Option| {}; - let x = |a: &Option| -> &Option { panic!() }; -} - -fn main() {} diff --git a/tests/ui/ref_option/ref_option.private.fixed b/tests/ui/ref_option/ref_option.private.fixed deleted file mode 100644 index 3b158befb926..000000000000 --- a/tests/ui/ref_option/ref_option.private.fixed +++ /dev/null @@ -1,79 +0,0 @@ -//@revisions: private all -//@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui/ref_option/private -//@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui/ref_option/all - -#![allow(unused, clippy::needless_lifetimes, clippy::borrowed_box)] -#![warn(clippy::ref_option)] - -fn opt_u8(a: Option<&u8>) {} -//~^ ref_option -fn opt_gen(a: Option<&T>) {} -//~^ ref_option -fn opt_string(a: std::option::Option<&String>) {} -//~^ ref_option -fn ret_string<'a>(p: &'a str) -> Option<&'a u8> { - //~^ ref_option - panic!() -} -fn ret_string_static() -> Option<&'static u8> { - //~^ ref_option - panic!() -} -fn mult_string(a: Option<&String>, b: Option<&Vec>) {} -//~^ ref_option -fn ret_box<'a>() -> Option<&'a Box> { - //~^ ref_option - panic!() -} - -pub fn pub_opt_string(a: &Option) {} -//~[all]^ ref_option -pub fn pub_mult_string(a: &Option, b: &Option>) {} -//~[all]^ ref_option - -pub trait PubTrait { - fn pub_trait_opt(&self, a: &Option>); - //~[all]^ ref_option - fn pub_trait_ret(&self) -> &Option>; - //~[all]^ ref_option -} - -trait PrivateTrait { - fn trait_opt(&self, a: Option<&String>); - //~^ ref_option - fn trait_ret(&self) -> Option<&String>; - //~^ ref_option -} - -pub struct PubStruct; - -impl PubStruct { - pub fn pub_opt_params(&self, a: &Option<()>) {} - //~[all]^ ref_option - pub fn pub_opt_ret(&self) -> &Option { - //~[all]^ ref_option - panic!() - } - - fn private_opt_params(&self, a: Option<&()>) {} - //~^ ref_option - fn private_opt_ret(&self) -> Option<&String> { - //~^ ref_option - panic!() - } -} - -// valid, don't change -fn mut_u8(a: &mut Option) {} -pub fn pub_mut_u8(a: &mut Option) {} - -// might be good to catch in the future -fn mut_u8_ref(a: &mut &Option) {} -pub fn pub_mut_u8_ref(a: &mut &Option) {} -fn lambdas() { - // Not handled for now, not sure if we should - let x = |a: &Option| {}; - let x = |a: &Option| -> &Option { panic!() }; -} - -fn main() {} diff --git a/tests/ui/ref_option/ref_option.rs b/tests/ui/ref_option/ref_option.rs deleted file mode 100644 index 35cd94174f8b..000000000000 --- a/tests/ui/ref_option/ref_option.rs +++ /dev/null @@ -1,79 +0,0 @@ -//@revisions: private all -//@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui/ref_option/private -//@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui/ref_option/all - -#![allow(unused, clippy::needless_lifetimes, clippy::borrowed_box)] -#![warn(clippy::ref_option)] - -fn opt_u8(a: &Option) {} -//~^ ref_option -fn opt_gen(a: &Option) {} -//~^ ref_option -fn opt_string(a: &std::option::Option) {} -//~^ ref_option -fn ret_string<'a>(p: &'a str) -> &'a Option { - //~^ ref_option - panic!() -} -fn ret_string_static() -> &'static Option { - //~^ ref_option - panic!() -} -fn mult_string(a: &Option, b: &Option>) {} -//~^ ref_option -fn ret_box<'a>() -> &'a Option> { - //~^ ref_option - panic!() -} - -pub fn pub_opt_string(a: &Option) {} -//~[all]^ ref_option -pub fn pub_mult_string(a: &Option, b: &Option>) {} -//~[all]^ ref_option - -pub trait PubTrait { - fn pub_trait_opt(&self, a: &Option>); - //~[all]^ ref_option - fn pub_trait_ret(&self) -> &Option>; - //~[all]^ ref_option -} - -trait PrivateTrait { - fn trait_opt(&self, a: &Option); - //~^ ref_option - fn trait_ret(&self) -> &Option; - //~^ ref_option -} - -pub struct PubStruct; - -impl PubStruct { - pub fn pub_opt_params(&self, a: &Option<()>) {} - //~[all]^ ref_option - pub fn pub_opt_ret(&self) -> &Option { - //~[all]^ ref_option - panic!() - } - - fn private_opt_params(&self, a: &Option<()>) {} - //~^ ref_option - fn private_opt_ret(&self) -> &Option { - //~^ ref_option - panic!() - } -} - -// valid, don't change -fn mut_u8(a: &mut Option) {} -pub fn pub_mut_u8(a: &mut Option) {} - -// might be good to catch in the future -fn mut_u8_ref(a: &mut &Option) {} -pub fn pub_mut_u8_ref(a: &mut &Option) {} -fn lambdas() { - // Not handled for now, not sure if we should - let x = |a: &Option| {}; - let x = |a: &Option| -> &Option { panic!() }; -} - -fn main() {} diff --git a/tests/ui/ref_option/ref_option_traits.rs b/tests/ui/ref_option/ref_option_traits.rs deleted file mode 100644 index 4c773e84f8da..000000000000 --- a/tests/ui/ref_option/ref_option_traits.rs +++ /dev/null @@ -1,40 +0,0 @@ -//@no-rustfix: fixes are only done to traits, not the impls -//@revisions: private all -//@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui/ref_option/private -//@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui/ref_option/all - -#![warn(clippy::ref_option)] - -pub trait PubTrait { - fn pub_trait_opt(&self, a: &Option>); - //~[all]^ ref_option - fn pub_trait_ret(&self) -> &Option>; - //~[all]^ ref_option -} - -trait PrivateTrait { - fn trait_opt(&self, a: &Option); - //~^ ref_option - fn trait_ret(&self) -> &Option; - //~^ ref_option -} - -pub struct PubStruct; - -impl PubTrait for PubStruct { - fn pub_trait_opt(&self, a: &Option>) {} - fn pub_trait_ret(&self) -> &Option> { - panic!() - } -} - -struct PrivateStruct; - -impl PrivateTrait for PrivateStruct { - fn trait_opt(&self, a: &Option) {} - fn trait_ret(&self) -> &Option { - panic!() - } -} - -fn main() {} diff --git a/tests/ui/rest_pat_in_fully_bound_structs.fixed b/tests/ui/rest_pat_in_fully_bound_structs.fixed new file mode 100644 index 000000000000..ac200b3b19b7 --- /dev/null +++ b/tests/ui/rest_pat_in_fully_bound_structs.fixed @@ -0,0 +1,61 @@ +#![warn(clippy::rest_pat_in_fully_bound_structs)] +#![allow(clippy::struct_field_names)] + +struct A { + a: i32, + b: i64, + c: &'static str, +} + +macro_rules! foo { + ($param:expr) => { + match $param { + A { a: 0, b: 0, c: "", .. } => {}, + _ => {}, + } + }; +} + +fn main() { + let a_struct = A { a: 5, b: 42, c: "A" }; + + match a_struct { + A { a: 5, b: 42, c: "", } => {}, // Lint + //~^ rest_pat_in_fully_bound_structs + A { a: 0, b: 0, c: "", } => {}, // Lint + //~^ rest_pat_in_fully_bound_structs + _ => {}, + } + + match a_struct { + A { a: 5, b: 42, .. } => {}, + A { a: 0, b: 0, c: "", } => {}, // Lint + //~^ rest_pat_in_fully_bound_structs + _ => {}, + } + + // No lint + match a_struct { + A { a: 5, .. } => {}, + A { a: 0, b: 0, .. } => {}, + _ => {}, + } + + // No lint + foo!(a_struct); + + #[non_exhaustive] + struct B { + a: u32, + b: u32, + c: u64, + } + + let b_struct = B { a: 5, b: 42, c: 342 }; + + match b_struct { + B { a: 5, b: 42, .. } => {}, + B { a: 0, b: 0, c: 128, .. } => {}, // No Lint + _ => {}, + } +} diff --git a/tests/ui/rest_pat_in_fully_bound_structs.stderr b/tests/ui/rest_pat_in_fully_bound_structs.stderr index d048933ddb7b..8a2da302b9ef 100644 --- a/tests/ui/rest_pat_in_fully_bound_structs.stderr +++ b/tests/ui/rest_pat_in_fully_bound_structs.stderr @@ -4,9 +4,13 @@ error: unnecessary use of `..` pattern in struct binding. All fields were alread LL | A { a: 5, b: 42, c: "", .. } => {}, // Lint | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: consider removing `..` from this binding = note: `-D clippy::rest-pat-in-fully-bound-structs` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::rest_pat_in_fully_bound_structs)]` +help: consider removing `..` from this binding + | +LL - A { a: 5, b: 42, c: "", .. } => {}, // Lint +LL + A { a: 5, b: 42, c: "", } => {}, // Lint + | error: unnecessary use of `..` pattern in struct binding. All fields were already bound --> tests/ui/rest_pat_in_fully_bound_structs.rs:25:9 @@ -14,7 +18,11 @@ error: unnecessary use of `..` pattern in struct binding. All fields were alread LL | A { a: 0, b: 0, c: "", .. } => {}, // Lint | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: consider removing `..` from this binding +help: consider removing `..` from this binding + | +LL - A { a: 0, b: 0, c: "", .. } => {}, // Lint +LL + A { a: 0, b: 0, c: "", } => {}, // Lint + | error: unnecessary use of `..` pattern in struct binding. All fields were already bound --> tests/ui/rest_pat_in_fully_bound_structs.rs:32:9 @@ -22,7 +30,11 @@ error: unnecessary use of `..` pattern in struct binding. All fields were alread LL | A { a: 0, b: 0, c: "", .. } => {}, // Lint | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: consider removing `..` from this binding +help: consider removing `..` from this binding + | +LL - A { a: 0, b: 0, c: "", .. } => {}, // Lint +LL + A { a: 0, b: 0, c: "", } => {}, // Lint + | error: aborting due to 3 previous errors diff --git a/tests/ui/semicolon_inside_block.fixed b/tests/ui/semicolon_inside_block.fixed index 7308e78aae26..468f0a5b1e47 100644 --- a/tests/ui/semicolon_inside_block.fixed +++ b/tests/ui/semicolon_inside_block.fixed @@ -3,7 +3,8 @@ clippy::unused_unit, clippy::unnecessary_operation, clippy::no_effect, - clippy::single_element_loop + clippy::single_element_loop, + clippy::double_parens )] #![warn(clippy::semicolon_inside_block)] @@ -87,6 +88,20 @@ fn main() { unit_fn_block() } +#[rustfmt::skip] +fn issue15380() { + ( {0;0}); + + ({ + 0; + 0 + }); + + (({ 0 })) ; + + ( ( { 0 } ) ) ; +} + pub fn issue15388() { #[rustfmt::skip] {0; 0}; diff --git a/tests/ui/semicolon_inside_block.rs b/tests/ui/semicolon_inside_block.rs index 467bf4d779f2..101374af2647 100644 --- a/tests/ui/semicolon_inside_block.rs +++ b/tests/ui/semicolon_inside_block.rs @@ -3,7 +3,8 @@ clippy::unused_unit, clippy::unnecessary_operation, clippy::no_effect, - clippy::single_element_loop + clippy::single_element_loop, + clippy::double_parens )] #![warn(clippy::semicolon_inside_block)] @@ -87,6 +88,20 @@ fn main() { unit_fn_block() } +#[rustfmt::skip] +fn issue15380() { + ( {0;0}); + + ({ + 0; + 0 + }); + + (({ 0 })) ; + + ( ( { 0 } ) ) ; +} + pub fn issue15388() { #[rustfmt::skip] {0; 0}; diff --git a/tests/ui/semicolon_inside_block.stderr b/tests/ui/semicolon_inside_block.stderr index 23433f4e7ef9..2046dd1c36be 100644 --- a/tests/ui/semicolon_inside_block.stderr +++ b/tests/ui/semicolon_inside_block.stderr @@ -1,5 +1,5 @@ error: consider moving the `;` inside the block for consistent formatting - --> tests/ui/semicolon_inside_block.rs:38:5 + --> tests/ui/semicolon_inside_block.rs:39:5 | LL | { unit_fn_block() }; | ^^^^^^^^^^^^^^^^^^^^ @@ -13,7 +13,7 @@ LL + { unit_fn_block(); } | error: consider moving the `;` inside the block for consistent formatting - --> tests/ui/semicolon_inside_block.rs:40:5 + --> tests/ui/semicolon_inside_block.rs:41:5 | LL | unsafe { unit_fn_block() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL + unsafe { unit_fn_block(); } | error: consider moving the `;` inside the block for consistent formatting - --> tests/ui/semicolon_inside_block.rs:49:5 + --> tests/ui/semicolon_inside_block.rs:50:5 | LL | / { LL | | @@ -41,7 +41,7 @@ LL ~ } | error: consider moving the `;` inside the block for consistent formatting - --> tests/ui/semicolon_inside_block.rs:63:5 + --> tests/ui/semicolon_inside_block.rs:64:5 | LL | { m!(()) }; | ^^^^^^^^^^^ diff --git a/tests/ui/track-diagnostics-clippy.stderr b/tests/ui/track-diagnostics-clippy.stderr index d5533877b451..3ceb501463b7 100644 --- a/tests/ui/track-diagnostics-clippy.stderr +++ b/tests/ui/track-diagnostics-clippy.stderr @@ -16,7 +16,7 @@ LL | let d = 42; LL | d | ^ | - = note: -Ztrack-diagnostics: created at clippy_lints/src/returns.rs:LL:CC + = note: -Ztrack-diagnostics: created at clippy_lints/src/returns/let_and_return.rs:LL:CC = note: `-D clippy::let-and-return` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::let_and_return)]` help: return the expression directly diff --git a/tests/ui/transmute_ptr_to_ref.fixed b/tests/ui/transmute_ptr_to_ref.fixed index 61e3ac2fe88e..c130575df960 100644 --- a/tests/ui/transmute_ptr_to_ref.fixed +++ b/tests/ui/transmute_ptr_to_ref.fixed @@ -5,7 +5,7 @@ clippy::missing_transmute_annotations )] -unsafe fn _ptr_to_ref(p: *const T, m: *mut T, o: *const U, om: *mut U) { +fn ptr_to_ref(p: *const T, m: *mut T, o: *const U, om: *mut U) { unsafe { let _: &T = &*p; //~^ transmute_ptr_to_ref @@ -37,7 +37,7 @@ unsafe fn _ptr_to_ref(p: *const T, m: *mut T, o: *const U, om: *mut U) { } } -fn _issue1231() { +fn issue1231() { struct Foo<'a, T> { bar: &'a T, } @@ -55,7 +55,7 @@ fn _issue1231() { //~^ transmute_ptr_to_ref } -unsafe fn _issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &'b u32 { +fn issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &'b u32 { unsafe { match 0 { 0 => &*x.cast::<&u32>(), @@ -71,7 +71,7 @@ unsafe fn _issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &' } #[clippy::msrv = "1.38"] -unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { +fn meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { unsafe { let a = 0u32; let a = &a as *const u32; @@ -89,7 +89,7 @@ unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { } #[clippy::msrv = "1.37"] -unsafe fn _under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { +fn under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { unsafe { let a = 0u32; let a = &a as *const u32; @@ -106,4 +106,33 @@ unsafe fn _under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { } } +// handle DSTs +fn issue13357(ptr: *const [i32], s_ptr: *const &str, a_s_ptr: *const [&str]) { + unsafe { + // different types, without erased regions + let _ = &*(ptr as *const [u32]); + //~^ transmute_ptr_to_ref + let _: &[u32] = &*(ptr as *const [u32]); + //~^ transmute_ptr_to_ref + + // different types, with erased regions + let _ = &*(a_s_ptr as *const [&[u8]]); + //~^ transmute_ptr_to_ref + let _: &[&[u8]] = &*(a_s_ptr as *const [&[u8]]); + //~^ transmute_ptr_to_ref + + // same type, without erased regions + let _ = &*(ptr as *const [i32]); + //~^ transmute_ptr_to_ref + let _: &[i32] = &*ptr; + //~^ transmute_ptr_to_ref + + // same type, with erased regions + let _ = &*(a_s_ptr as *const [&str]); + //~^ transmute_ptr_to_ref + let _: &[&str] = &*(a_s_ptr as *const [&str]); + //~^ transmute_ptr_to_ref + } +} + fn main() {} diff --git a/tests/ui/transmute_ptr_to_ref.rs b/tests/ui/transmute_ptr_to_ref.rs index 48e2f527b554..f79d54234a2c 100644 --- a/tests/ui/transmute_ptr_to_ref.rs +++ b/tests/ui/transmute_ptr_to_ref.rs @@ -5,7 +5,7 @@ clippy::missing_transmute_annotations )] -unsafe fn _ptr_to_ref(p: *const T, m: *mut T, o: *const U, om: *mut U) { +fn ptr_to_ref(p: *const T, m: *mut T, o: *const U, om: *mut U) { unsafe { let _: &T = std::mem::transmute(p); //~^ transmute_ptr_to_ref @@ -37,7 +37,7 @@ unsafe fn _ptr_to_ref(p: *const T, m: *mut T, o: *const U, om: *mut U) { } } -fn _issue1231() { +fn issue1231() { struct Foo<'a, T> { bar: &'a T, } @@ -55,7 +55,7 @@ fn _issue1231() { //~^ transmute_ptr_to_ref } -unsafe fn _issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &'b u32 { +fn issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &'b u32 { unsafe { match 0 { 0 => std::mem::transmute(x), @@ -71,7 +71,7 @@ unsafe fn _issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &' } #[clippy::msrv = "1.38"] -unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { +fn meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { unsafe { let a = 0u32; let a = &a as *const u32; @@ -89,7 +89,7 @@ unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { } #[clippy::msrv = "1.37"] -unsafe fn _under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { +fn under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { unsafe { let a = 0u32; let a = &a as *const u32; @@ -106,4 +106,33 @@ unsafe fn _under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { } } +// handle DSTs +fn issue13357(ptr: *const [i32], s_ptr: *const &str, a_s_ptr: *const [&str]) { + unsafe { + // different types, without erased regions + let _ = core::mem::transmute::<_, &[u32]>(ptr); + //~^ transmute_ptr_to_ref + let _: &[u32] = core::mem::transmute(ptr); + //~^ transmute_ptr_to_ref + + // different types, with erased regions + let _ = core::mem::transmute::<_, &[&[u8]]>(a_s_ptr); + //~^ transmute_ptr_to_ref + let _: &[&[u8]] = core::mem::transmute(a_s_ptr); + //~^ transmute_ptr_to_ref + + // same type, without erased regions + let _ = core::mem::transmute::<_, &[i32]>(ptr); + //~^ transmute_ptr_to_ref + let _: &[i32] = core::mem::transmute(ptr); + //~^ transmute_ptr_to_ref + + // same type, with erased regions + let _ = core::mem::transmute::<_, &[&str]>(a_s_ptr); + //~^ transmute_ptr_to_ref + let _: &[&str] = core::mem::transmute(a_s_ptr); + //~^ transmute_ptr_to_ref + } +} + fn main() {} diff --git a/tests/ui/transmute_ptr_to_ref.stderr b/tests/ui/transmute_ptr_to_ref.stderr index 7685c345c861..3f404d295fef 100644 --- a/tests/ui/transmute_ptr_to_ref.stderr +++ b/tests/ui/transmute_ptr_to_ref.stderr @@ -43,13 +43,13 @@ error: transmute from a pointer type (`*mut U`) to a reference type (`&T`) LL | let _: &T = std::mem::transmute(om); | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(om as *const T)` -error: transmute from a pointer type (`*const i32`) to a reference type (`&_issue1231::Foo<'_, u8>`) +error: transmute from a pointer type (`*const i32`) to a reference type (`&issue1231::Foo<'_, u8>`) --> tests/ui/transmute_ptr_to_ref.rs:46:32 | LL | let _: &Foo = unsafe { std::mem::transmute::<_, &Foo<_>>(raw) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*raw.cast::>()` -error: transmute from a pointer type (`*const i32`) to a reference type (`&_issue1231::Foo<'_, &u8>`) +error: transmute from a pointer type (`*const i32`) to a reference type (`&issue1231::Foo<'_, &u8>`) --> tests/ui/transmute_ptr_to_ref.rs:49:33 | LL | let _: &Foo<&u8> = unsafe { std::mem::transmute::<_, &Foo<&_>>(raw) }; @@ -133,5 +133,53 @@ error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32 LL | _ => std::mem::transmute::<_, &&'b u32>(x), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(x as *const () as *const &'b u32)` -error: aborting due to 22 previous errors +error: transmute from a pointer type (`*const [i32]`) to a reference type (`&[u32]`) + --> tests/ui/transmute_ptr_to_ref.rs:113:17 + | +LL | let _ = core::mem::transmute::<_, &[u32]>(ptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(ptr as *const [u32])` + +error: transmute from a pointer type (`*const [i32]`) to a reference type (`&[u32]`) + --> tests/ui/transmute_ptr_to_ref.rs:115:25 + | +LL | let _: &[u32] = core::mem::transmute(ptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(ptr as *const [u32])` + +error: transmute from a pointer type (`*const [&str]`) to a reference type (`&[&[u8]]`) + --> tests/ui/transmute_ptr_to_ref.rs:119:17 + | +LL | let _ = core::mem::transmute::<_, &[&[u8]]>(a_s_ptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a_s_ptr as *const [&[u8]])` + +error: transmute from a pointer type (`*const [&str]`) to a reference type (`&[&[u8]]`) + --> tests/ui/transmute_ptr_to_ref.rs:121:27 + | +LL | let _: &[&[u8]] = core::mem::transmute(a_s_ptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a_s_ptr as *const [&[u8]])` + +error: transmute from a pointer type (`*const [i32]`) to a reference type (`&[i32]`) + --> tests/ui/transmute_ptr_to_ref.rs:125:17 + | +LL | let _ = core::mem::transmute::<_, &[i32]>(ptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(ptr as *const [i32])` + +error: transmute from a pointer type (`*const [i32]`) to a reference type (`&[i32]`) + --> tests/ui/transmute_ptr_to_ref.rs:127:25 + | +LL | let _: &[i32] = core::mem::transmute(ptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*ptr` + +error: transmute from a pointer type (`*const [&str]`) to a reference type (`&[&str]`) + --> tests/ui/transmute_ptr_to_ref.rs:131:17 + | +LL | let _ = core::mem::transmute::<_, &[&str]>(a_s_ptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a_s_ptr as *const [&str])` + +error: transmute from a pointer type (`*const [&str]`) to a reference type (`&[&str]`) + --> tests/ui/transmute_ptr_to_ref.rs:133:26 + | +LL | let _: &[&str] = core::mem::transmute(a_s_ptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a_s_ptr as *const [&str])` + +error: aborting due to 30 previous errors diff --git a/tests/ui/use_self.fixed b/tests/ui/use_self.fixed index cccb6bffabb7..075e31d202b0 100644 --- a/tests/ui/use_self.fixed +++ b/tests/ui/use_self.fixed @@ -530,8 +530,8 @@ mod issue7206 { impl<'a> S2> { fn new_again() -> Self { - Self::new() - //~^ use_self + S2::new() + // FIXME: ^Broken by PR #15611 } } } @@ -755,3 +755,17 @@ mod crash_check_13128 { } } } + +mod issue_13277 { + trait Foo { + type Item<'foo>; + } + struct Bar<'b> { + content: &'b str, + } + impl<'b> Foo for Option> { + // when checking whether `Option>` has a lifetime, check not only the outer + // `Option`, but also the inner `Bar<'foo>` + type Item<'foo> = Option>; + } +} diff --git a/tests/ui/use_self.rs b/tests/ui/use_self.rs index 09288677aa71..6fbba0bbc550 100644 --- a/tests/ui/use_self.rs +++ b/tests/ui/use_self.rs @@ -531,7 +531,7 @@ mod issue7206 { impl<'a> S2> { fn new_again() -> Self { S2::new() - //~^ use_self + // FIXME: ^Broken by PR #15611 } } } @@ -755,3 +755,17 @@ mod crash_check_13128 { } } } + +mod issue_13277 { + trait Foo { + type Item<'foo>; + } + struct Bar<'b> { + content: &'b str, + } + impl<'b> Foo for Option> { + // when checking whether `Option>` has a lifetime, check not only the outer + // `Option`, but also the inner `Bar<'foo>` + type Item<'foo> = Option>; + } +} diff --git a/tests/ui/use_self.stderr b/tests/ui/use_self.stderr index 781327696ac1..5f65c53ea25c 100644 --- a/tests/ui/use_self.stderr +++ b/tests/ui/use_self.stderr @@ -169,12 +169,6 @@ error: unnecessary structure name repetition LL | A::new::(submod::B {}) | ^ help: use the applicable keyword: `Self` -error: unnecessary structure name repetition - --> tests/ui/use_self.rs:533:13 - | -LL | S2::new() - | ^^ help: use the applicable keyword: `Self` - error: unnecessary structure name repetition --> tests/ui/use_self.rs:571:17 | @@ -259,5 +253,5 @@ error: unnecessary structure name repetition LL | E::A => {}, | ^ help: use the applicable keyword: `Self` -error: aborting due to 43 previous errors +error: aborting due to 42 previous errors diff --git a/tests/ui/useless_attribute.fixed b/tests/ui/useless_attribute.fixed index be4fb55ddfb8..15070dd9c2ca 100644 --- a/tests/ui/useless_attribute.fixed +++ b/tests/ui/useless_attribute.fixed @@ -158,3 +158,13 @@ pub mod redundant_imports_issue { empty!(); } + +pub mod issue15636 { + pub mod f { + #[deprecated(since = "TBD")] + pub mod deprec {} + } + + #[allow(deprecated_in_future)] + pub use f::deprec; +} diff --git a/tests/ui/useless_attribute.rs b/tests/ui/useless_attribute.rs index 5a1bcf97a5b4..3f530de7fd8e 100644 --- a/tests/ui/useless_attribute.rs +++ b/tests/ui/useless_attribute.rs @@ -158,3 +158,13 @@ pub mod redundant_imports_issue { empty!(); } + +pub mod issue15636 { + pub mod f { + #[deprecated(since = "TBD")] + pub mod deprec {} + } + + #[allow(deprecated_in_future)] + pub use f::deprec; +} From 63a55083aa03e94dc380af31d51e3ffd944cb2b4 Mon Sep 17 00:00:00 2001 From: pommicket Date: Mon, 12 May 2025 10:32:21 -0400 Subject: [PATCH 020/226] Add clippy::self_only_used_in_recursion lint and use it instead of clippy::only_used_in_recursion when the parameter in question is self. --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/only_used_in_recursion.rs | 175 ++++++++++++++++----- tests/ui/only_used_in_recursion.rs | 3 +- tests/ui/only_used_in_recursion.stderr | 70 +++++---- 5 files changed, 173 insertions(+), 77 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a9be0214372..f4e4cbfe3750 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6597,6 +6597,7 @@ Released 2018-09-13 [`self_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_assignment [`self_named_constructors`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_constructors [`self_named_module_files`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_module_files +[`self_only_used_in_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_only_used_in_recursion [`semicolon_if_nothing_returned`]: https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_if_nothing_returned [`semicolon_inside_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_inside_block [`semicolon_outside_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_outside_block diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 1b19a8851edd..c67026e6f266 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -575,6 +575,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES_INFO, crate::octal_escapes::OCTAL_ESCAPES_INFO, crate::only_used_in_recursion::ONLY_USED_IN_RECURSION_INFO, + crate::only_used_in_recursion::SELF_ONLY_USED_IN_RECURSION_INFO, crate::operators::ABSURD_EXTREME_COMPARISONS_INFO, crate::operators::ARITHMETIC_SIDE_EFFECTS_INFO, crate::operators::ASSIGN_OP_PATTERN_INFO, diff --git a/clippy_lints/src/only_used_in_recursion.rs b/clippy_lints/src/only_used_in_recursion.rs index a42763172f56..bd2fb9d1aa72 100644 --- a/clippy_lints/src/only_used_in_recursion.rs +++ b/clippy_lints/src/only_used_in_recursion.rs @@ -24,6 +24,33 @@ declare_clippy_lint! { /// the calculations have no side-effects (function calls or mutating dereference) /// and the assigned variables are also only in recursion, it is useless. /// + /// ### Example + /// ```no_run + /// fn f(a: usize, b: usize) -> usize { + /// if a == 0 { + /// 1 + /// } else { + /// f(a - 1, b + 1) + /// } + /// } + /// # fn main() { + /// # print!("{}", f(1, 1)); + /// # } + /// ``` + /// Use instead: + /// ```no_run + /// fn f(a: usize) -> usize { + /// if a == 0 { + /// 1 + /// } else { + /// f(a - 1) + /// } + /// } + /// # fn main() { + /// # print!("{}", f(1)); + /// # } + /// ``` + /// /// ### Known problems /// Too many code paths in the linting code are currently untested and prone to produce false /// positives or are prone to have performance implications. @@ -51,39 +78,90 @@ declare_clippy_lint! { /// - struct pattern binding /// /// Also, when you recurse the function name with path segments, it is not possible to detect. - /// - /// ### Example - /// ```no_run - /// fn f(a: usize, b: usize) -> usize { - /// if a == 0 { - /// 1 - /// } else { - /// f(a - 1, b + 1) - /// } - /// } - /// # fn main() { - /// # print!("{}", f(1, 1)); - /// # } - /// ``` - /// Use instead: - /// ```no_run - /// fn f(a: usize) -> usize { - /// if a == 0 { - /// 1 - /// } else { - /// f(a - 1) - /// } - /// } - /// # fn main() { - /// # print!("{}", f(1)); - /// # } - /// ``` #[clippy::version = "1.61.0"] pub ONLY_USED_IN_RECURSION, complexity, "arguments that is only used in recursion can be removed" } -impl_lint_pass!(OnlyUsedInRecursion => [ONLY_USED_IN_RECURSION]); + +declare_clippy_lint! { + /// ### What it does + /// Checks for `self` receiver that is only used in recursion with no side-effects. + /// + /// ### Why is this bad? + /// + /// It may be possible to remove the `self` argument, allowing the function to be + /// used without an object of type `Self`. + /// + /// ### Example + /// ```no_run + /// struct Foo; + /// impl Foo { + /// fn f(&self, n: u32) -> u32 { + /// if n == 0 { + /// 1 + /// } else { + /// n * self.f(n - 1) + /// } + /// } + /// } + /// # fn main() { + /// # print!("{}", Foo.f(10)); + /// # } + /// ``` + /// Use instead: + /// ```no_run + /// struct Foo; + /// impl Foo { + /// fn f(n: u32) -> u32 { + /// if n == 0 { + /// 1 + /// } else { + /// n * Self::f(n - 1) + /// } + /// } + /// } + /// # fn main() { + /// # print!("{}", Foo::f(10)); + /// # } + /// ``` + /// + /// ### Known problems + /// Too many code paths in the linting code are currently untested and prone to produce false + /// positives or are prone to have performance implications. + /// + /// In some cases, this would not catch all useless arguments. + /// + /// ```no_run + /// struct Foo; + /// impl Foo { + /// fn foo(&self, a: usize) -> usize { + /// let f = |x| x; + /// + /// if a == 0 { + /// 1 + /// } else { + /// f(self).foo(a) + /// } + /// } + /// } + /// ``` + /// + /// For example, here `self` is only used in recursion, but the lint would not catch it. + /// + /// List of some examples that can not be caught: + /// - binary operation of non-primitive types + /// - closure usage + /// - some `break` relative operations + /// - struct pattern binding + /// + /// Also, when you recurse the function name with path segments, it is not possible to detect. + #[clippy::version = "1.92.0"] + pub SELF_ONLY_USED_IN_RECURSION, + pedantic, + "self receiver only used to recursively call method can be removed" +} +impl_lint_pass!(OnlyUsedInRecursion => [ONLY_USED_IN_RECURSION, SELF_ONLY_USED_IN_RECURSION]); #[derive(Clone, Copy)] enum FnKind { @@ -355,26 +433,39 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { self.params.flag_for_linting(); for param in &self.params.params { if param.apply_lint.get() { - span_lint_and_then( - cx, - ONLY_USED_IN_RECURSION, - param.ident.span, - "parameter is only used in recursion", - |diag| { - if param.ident.name != kw::SelfLower { + if param.ident.name == kw::SelfLower { + span_lint_and_then( + cx, + SELF_ONLY_USED_IN_RECURSION, + param.ident.span, + "`self` is only used in recursion", + |diag| { + diag.span_note( + param.uses.iter().map(|x| x.span).collect::>(), + "`self` used here", + ); + }, + ); + } else { + span_lint_and_then( + cx, + ONLY_USED_IN_RECURSION, + param.ident.span, + "parameter is only used in recursion", + |diag| { diag.span_suggestion( param.ident.span, "if this is intentional, prefix it with an underscore", format!("_{}", param.ident.name), Applicability::MaybeIncorrect, ); - } - diag.span_note( - param.uses.iter().map(|x| x.span).collect::>(), - "parameter used here", - ); - }, - ); + diag.span_note( + param.uses.iter().map(|x| x.span).collect::>(), + "parameter used here", + ); + }, + ); + } } } self.params.clear(); diff --git a/tests/ui/only_used_in_recursion.rs b/tests/ui/only_used_in_recursion.rs index 7d6075ba9ea4..d58635969238 100644 --- a/tests/ui/only_used_in_recursion.rs +++ b/tests/ui/only_used_in_recursion.rs @@ -1,4 +1,5 @@ #![warn(clippy::only_used_in_recursion)] +#![warn(clippy::self_only_used_in_recursion)] //@no-rustfix fn _simple(x: u32) -> u32 { x @@ -74,7 +75,7 @@ impl A { } fn _method_self(&self, flag: usize, a: usize) -> usize { - //~^ only_used_in_recursion + //~^ self_only_used_in_recursion //~| only_used_in_recursion if flag == 0 { 0 } else { self._method_self(flag - 1, a) } diff --git a/tests/ui/only_used_in_recursion.stderr b/tests/ui/only_used_in_recursion.stderr index ca08319e1120..5d1e3e9c50fd 100644 --- a/tests/ui/only_used_in_recursion.stderr +++ b/tests/ui/only_used_in_recursion.stderr @@ -1,11 +1,11 @@ error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:11:27 + --> tests/ui/only_used_in_recursion.rs:12:27 | LL | fn _one_unused(flag: u32, a: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:14:53 + --> tests/ui/only_used_in_recursion.rs:15:53 | LL | if flag == 0 { 0 } else { _one_unused(flag - 1, a) } | ^ @@ -13,181 +13,183 @@ LL | if flag == 0 { 0 } else { _one_unused(flag - 1, a) } = help: to override `-D warnings` add `#[allow(clippy::only_used_in_recursion)]` error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:17:27 + --> tests/ui/only_used_in_recursion.rs:18:27 | LL | fn _two_unused(flag: u32, a: u32, b: i32) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:21:53 + --> tests/ui/only_used_in_recursion.rs:22:53 | LL | if flag == 0 { 0 } else { _two_unused(flag - 1, a, b) } | ^ error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:17:35 + --> tests/ui/only_used_in_recursion.rs:18:35 | LL | fn _two_unused(flag: u32, a: u32, b: i32) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:21:56 + --> tests/ui/only_used_in_recursion.rs:22:56 | LL | if flag == 0 { 0 } else { _two_unused(flag - 1, a, b) } | ^ error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:24:26 + --> tests/ui/only_used_in_recursion.rs:25:26 | LL | fn _with_calc(flag: u32, a: i64) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:30:32 + --> tests/ui/only_used_in_recursion.rs:31:32 | LL | _with_calc(flag - 1, (-a + 10) * 5) | ^ error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:39:33 + --> tests/ui/only_used_in_recursion.rs:40:33 | LL | fn _used_with_unused(flag: u32, a: i32, b: i32) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:46:38 + --> tests/ui/only_used_in_recursion.rs:47:38 | LL | _used_with_unused(flag - 1, -a, a + b) | ^ ^ error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:39:41 + --> tests/ui/only_used_in_recursion.rs:40:41 | LL | fn _used_with_unused(flag: u32, a: i32, b: i32) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:46:45 + --> tests/ui/only_used_in_recursion.rs:47:45 | LL | _used_with_unused(flag - 1, -a, a + b) | ^ error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:50:35 + --> tests/ui/only_used_in_recursion.rs:51:35 | LL | fn _codependent_unused(flag: u32, a: i32, b: i32) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:57:39 + --> tests/ui/only_used_in_recursion.rs:58:39 | LL | _codependent_unused(flag - 1, a * b, a + b) | ^ ^ error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:50:43 + --> tests/ui/only_used_in_recursion.rs:51:43 | LL | fn _codependent_unused(flag: u32, a: i32, b: i32) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:57:43 + --> tests/ui/only_used_in_recursion.rs:58:43 | LL | _codependent_unused(flag - 1, a * b, a + b) | ^ ^ error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:61:30 + --> tests/ui/only_used_in_recursion.rs:62:30 | LL | fn _not_primitive(flag: u32, b: String) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:64:56 + --> tests/ui/only_used_in_recursion.rs:65:56 | LL | if flag == 0 { 0 } else { _not_primitive(flag - 1, b) } | ^ error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:70:29 + --> tests/ui/only_used_in_recursion.rs:71:29 | LL | fn _method(flag: usize, a: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:73:59 + --> tests/ui/only_used_in_recursion.rs:74:59 | LL | if flag == 0 { 0 } else { Self::_method(flag - 1, a) } | ^ -error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:76:22 +error: `self` is only used in recursion + --> tests/ui/only_used_in_recursion.rs:77:22 | LL | fn _method_self(&self, flag: usize, a: usize) -> usize { | ^^^^ | -note: parameter used here - --> tests/ui/only_used_in_recursion.rs:80:35 +note: `self` used here + --> tests/ui/only_used_in_recursion.rs:81:35 | LL | if flag == 0 { 0 } else { self._method_self(flag - 1, a) } | ^^^^ + = note: `-D clippy::self-only-used-in-recursion` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::self_only_used_in_recursion)]` error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:76:41 + --> tests/ui/only_used_in_recursion.rs:77:41 | LL | fn _method_self(&self, flag: usize, a: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:80:63 + --> tests/ui/only_used_in_recursion.rs:81:63 | LL | if flag == 0 { 0 } else { self._method_self(flag - 1, a) } | ^ error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:90:26 + --> tests/ui/only_used_in_recursion.rs:91:26 | LL | fn method(flag: u32, a: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:93:58 + --> tests/ui/only_used_in_recursion.rs:94:58 | LL | if flag == 0 { 0 } else { Self::method(flag - 1, a) } | ^ error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:96:38 + --> tests/ui/only_used_in_recursion.rs:97:38 | LL | fn method_self(&self, flag: u32, a: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:99:62 + --> tests/ui/only_used_in_recursion.rs:100:62 | LL | if flag == 0 { 0 } else { self.method_self(flag - 1, a) } | ^ error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:124:26 + --> tests/ui/only_used_in_recursion.rs:125:26 | LL | fn method(flag: u32, a: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:127:58 + --> tests/ui/only_used_in_recursion.rs:128:58 | LL | if flag == 0 { 0 } else { Self::method(flag - 1, a) } | ^ error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:130:38 + --> tests/ui/only_used_in_recursion.rs:131:38 | LL | fn method_self(&self, flag: u32, a: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:133:62 + --> tests/ui/only_used_in_recursion.rs:134:62 | LL | if flag == 0 { 0 } else { self.method_self(flag - 1, a) } | ^ From 4a909bc6b6d39f3596e12ebd77f1dc02f039af3d Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Fri, 19 Sep 2025 13:24:16 +0500 Subject: [PATCH 021/226] Fix versions lints from 1.90.0 --- clippy_lints/src/doc/mod.rs | 2 +- clippy_lints/src/operators/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index eca3bc390d77..f8ae770b3a4d 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -315,7 +315,7 @@ declare_clippy_lint! { /// /// [example of a good link](https://github.com/rust-lang/rust-clippy/) /// pub fn do_something() {} /// ``` - #[clippy::version = "1.84.0"] + #[clippy::version = "1.90.0"] pub DOC_BROKEN_LINK, pedantic, "broken document link" diff --git a/clippy_lints/src/operators/mod.rs b/clippy_lints/src/operators/mod.rs index bdbbb3475cd5..aaea4ff11fc3 100644 --- a/clippy_lints/src/operators/mod.rs +++ b/clippy_lints/src/operators/mod.rs @@ -854,7 +854,7 @@ declare_clippy_lint! { /// println!("{a} is divisible by {b}"); /// } /// ``` - #[clippy::version = "1.89.0"] + #[clippy::version = "1.90.0"] pub MANUAL_IS_MULTIPLE_OF, complexity, "manual implementation of `.is_multiple_of()`" From fb7e89cc7268f7b02d39e2c61ab4bd2df458a586 Mon Sep 17 00:00:00 2001 From: yanglsh Date: Tue, 9 Sep 2025 23:30:24 +0800 Subject: [PATCH 022/226] fix: `question_mark` FP on variables used after --- clippy_lints/src/question_mark.rs | 10 +++++++++- tests/ui/question_mark.fixed | 25 ++++++++++++++++++++++++ tests/ui/question_mark.rs | 32 +++++++++++++++++++++++++++++++ tests/ui/question_mark.stderr | 22 ++++++++++++++++++++- 4 files changed, 87 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index 4aa100a50e05..d3a5a5dddfbe 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -6,7 +6,8 @@ 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 clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; +use clippy_utils::ty::{implements_trait, is_copy, is_type_diagnostic_item}; +use clippy_utils::usage::local_used_after_expr; use clippy_utils::{ eq_expr_value, fn_def_id_with_node_args, higher, is_else_clause, is_in_const_context, is_lint_allowed, is_path_lang_item, is_res_lang_ctor, pat_and_expr_can_be_question_mark, path_res, path_to_local, path_to_local_id, @@ -483,6 +484,13 @@ fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: .filter(|e| *e) .is_none() { + if !is_copy(cx, caller_ty) + && let Some(hir_id) = path_to_local(let_expr) + && local_used_after_expr(cx, hir_id, expr) + { + return; + } + let mut applicability = Applicability::MachineApplicable; let receiver_str = snippet_with_applicability(cx, let_expr.span, "..", &mut applicability); let requires_semi = matches!(cx.tcx.parent_hir_node(expr.hir_id), Node::Stmt(_)); diff --git a/tests/ui/question_mark.fixed b/tests/ui/question_mark.fixed index ac81b324c204..2c5ee0245038 100644 --- a/tests/ui/question_mark.fixed +++ b/tests/ui/question_mark.fixed @@ -475,3 +475,28 @@ fn issue_15679() -> Result { Ok(0) } + +mod issue14894 { + fn use_after_question_mark(do_something_else: impl Fn() -> Result) -> Result<(), ()> { + let result = do_something_else(); + if let Err(reason) = result { + return Err(reason); + } + drop(result); + + let result = do_something_else(); + let x = result?; + drop(x); + + Ok(()) + } + + #[expect(dropping_copy_types)] + fn use_after_question_mark_but_is_copy(do_something_else: impl Fn() -> Result) -> Result<(), ()> { + let result = do_something_else(); + result?; + drop(result); + + Ok(()) + } +} diff --git a/tests/ui/question_mark.rs b/tests/ui/question_mark.rs index b5866dac6b8f..b9ff9d1565b2 100644 --- a/tests/ui/question_mark.rs +++ b/tests/ui/question_mark.rs @@ -583,3 +583,35 @@ fn issue_15679() -> Result { Ok(0) } + +mod issue14894 { + fn use_after_question_mark(do_something_else: impl Fn() -> Result) -> Result<(), ()> { + let result = do_something_else(); + if let Err(reason) = result { + return Err(reason); + } + drop(result); + + let result = do_something_else(); + let x = match result { + //~^ question_mark + Ok(v) => v, + Err(e) => return Err(e), + }; + drop(x); + + Ok(()) + } + + #[expect(dropping_copy_types)] + fn use_after_question_mark_but_is_copy(do_something_else: impl Fn() -> Result) -> Result<(), ()> { + let result = do_something_else(); + if let Err(reason) = result { + //~^ question_mark + return Err(reason); + } + drop(result); + + Ok(()) + } +} diff --git a/tests/ui/question_mark.stderr b/tests/ui/question_mark.stderr index 1ecd936292e5..9b2896328e66 100644 --- a/tests/ui/question_mark.stderr +++ b/tests/ui/question_mark.stderr @@ -313,5 +313,25 @@ LL | | Err(err) => return Err(<&str as Into>::into(err)), LL | | }; | |_____^ help: try instead: `some_result?` -error: aborting due to 33 previous errors +error: this `match` expression can be replaced with `?` + --> tests/ui/question_mark.rs:596:17 + | +LL | let x = match result { + | _________________^ +LL | | +LL | | Ok(v) => v, +LL | | Err(e) => return Err(e), +LL | | }; + | |_________^ help: try instead: `result?` + +error: this block may be rewritten with the `?` operator + --> tests/ui/question_mark.rs:609:9 + | +LL | / if let Err(reason) = result { +LL | | +LL | | return Err(reason); +LL | | } + | |_________^ help: replace it with: `result?;` + +error: aborting due to 35 previous errors From 3e65620bef7d86a8f914a2a408dd91ca66b3499d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Justus=20Fl=C3=BCgel?= Date: Sun, 7 Sep 2025 15:33:11 +0200 Subject: [PATCH 023/226] Add tests for `explicit_deref_methods` lints from within proc macros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Justus Flügel --- tests/ui/explicit_deref_methods.fixed | 15 +++++++++++++++ tests/ui/explicit_deref_methods.rs | 15 +++++++++++++++ tests/ui/explicit_deref_methods.stderr | 26 +++++++++++++------------- 3 files changed, 43 insertions(+), 13 deletions(-) diff --git a/tests/ui/explicit_deref_methods.fixed b/tests/ui/explicit_deref_methods.fixed index 52c4d1b1f301..97e8e0bafe4f 100644 --- a/tests/ui/explicit_deref_methods.fixed +++ b/tests/ui/explicit_deref_methods.fixed @@ -1,3 +1,4 @@ +//@aux-build:proc_macros.rs #![warn(clippy::explicit_deref_methods)] #![allow(unused_variables, unused_must_use)] #![allow( @@ -14,6 +15,8 @@ use std::ops::{Deref, DerefMut}; +extern crate proc_macros; + fn concat(deref_str: &str) -> String { format!("{}bar", deref_str) } @@ -121,6 +124,18 @@ fn main() { let b: &str = expr_deref!(&*a); //~^ explicit_deref_methods + proc_macros::external! { + let a: &mut String = &mut String::from("foo"); + let b: &str = a.deref(); + } + + // Issue #15168 + proc_macros::with_span! { + span + let a: &mut String = &mut String::from("foo"); + let b: &str = a.deref(); + } + // The struct does not implement Deref trait #[derive(Copy, Clone)] struct NoLint(u32); diff --git a/tests/ui/explicit_deref_methods.rs b/tests/ui/explicit_deref_methods.rs index 706d6cb2b79a..b689649d49dd 100644 --- a/tests/ui/explicit_deref_methods.rs +++ b/tests/ui/explicit_deref_methods.rs @@ -1,3 +1,4 @@ +//@aux-build:proc_macros.rs #![warn(clippy::explicit_deref_methods)] #![allow(unused_variables, unused_must_use)] #![allow( @@ -14,6 +15,8 @@ use std::ops::{Deref, DerefMut}; +extern crate proc_macros; + fn concat(deref_str: &str) -> String { format!("{}bar", deref_str) } @@ -121,6 +124,18 @@ fn main() { let b: &str = expr_deref!(a.deref()); //~^ explicit_deref_methods + proc_macros::external! { + let a: &mut String = &mut String::from("foo"); + let b: &str = a.deref(); + } + + // Issue #15168 + proc_macros::with_span! { + span + let a: &mut String = &mut String::from("foo"); + let b: &str = a.deref(); + } + // The struct does not implement Deref trait #[derive(Copy, Clone)] struct NoLint(u32); diff --git a/tests/ui/explicit_deref_methods.stderr b/tests/ui/explicit_deref_methods.stderr index 5036884366cf..e2f2e68720b1 100644 --- a/tests/ui/explicit_deref_methods.stderr +++ b/tests/ui/explicit_deref_methods.stderr @@ -1,5 +1,5 @@ error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:55:19 + --> tests/ui/explicit_deref_methods.rs:58:19 | LL | let b: &str = a.deref(); | ^^^^^^^^^ help: try: `&*a` @@ -8,73 +8,73 @@ LL | let b: &str = a.deref(); = help: to override `-D warnings` add `#[allow(clippy::explicit_deref_methods)]` error: explicit `deref_mut` method call - --> tests/ui/explicit_deref_methods.rs:58:23 + --> tests/ui/explicit_deref_methods.rs:61:23 | LL | let b: &mut str = a.deref_mut(); | ^^^^^^^^^^^^^ help: try: `&mut **a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:62:39 + --> tests/ui/explicit_deref_methods.rs:65:39 | LL | let b: String = format!("{}, {}", a.deref(), a.deref()); | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:62:50 + --> tests/ui/explicit_deref_methods.rs:65:50 | LL | let b: String = format!("{}, {}", a.deref(), a.deref()); | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:66:20 + --> tests/ui/explicit_deref_methods.rs:69:20 | LL | println!("{}", a.deref()); | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:70:11 + --> tests/ui/explicit_deref_methods.rs:73:11 | LL | match a.deref() { | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:75:28 + --> tests/ui/explicit_deref_methods.rs:78:28 | LL | let b: String = concat(a.deref()); | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:78:13 + --> tests/ui/explicit_deref_methods.rs:81:13 | LL | let b = just_return(a).deref(); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `just_return(a)` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:81:28 + --> tests/ui/explicit_deref_methods.rs:84:28 | LL | let b: String = concat(just_return(a).deref()); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `just_return(a)` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:121:31 + --> tests/ui/explicit_deref_methods.rs:124:31 | LL | let b: &str = expr_deref!(a.deref()); | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:139:14 + --> tests/ui/explicit_deref_methods.rs:154:14 | LL | let _ = &Deref::deref(&"foo"); | ^^^^^^^^^^^^^^^^^^^^ help: try: `*&"foo"` error: explicit `deref_mut` method call - --> tests/ui/explicit_deref_methods.rs:141:14 + --> tests/ui/explicit_deref_methods.rs:156:14 | LL | let _ = &DerefMut::deref_mut(&mut x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut **&mut x` error: explicit `deref_mut` method call - --> tests/ui/explicit_deref_methods.rs:142:14 + --> tests/ui/explicit_deref_methods.rs:157:14 | LL | let _ = &DerefMut::deref_mut((&mut &mut x).deref_mut()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut ***(&mut &mut x)` From 2f6503d57406b17d2c56ce77339df5547589c1f6 Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Sat, 20 Sep 2025 19:45:28 +0500 Subject: [PATCH 024/226] Update actions/download-artifact to v5 --- .github/workflows/clippy_mq.yml | 2 +- .github/workflows/lintcheck.yml | 2 +- .github/workflows/lintcheck_summary.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/clippy_mq.yml b/.github/workflows/clippy_mq.yml index 0bcb71359359..9d099137449e 100644 --- a/.github/workflows/clippy_mq.yml +++ b/.github/workflows/clippy_mq.yml @@ -181,7 +181,7 @@ jobs: # Download - name: Download target dir - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: name: binaries path: target/debug diff --git a/.github/workflows/lintcheck.yml b/.github/workflows/lintcheck.yml index 390d6a0f7475..45fd10ae7614 100644 --- a/.github/workflows/lintcheck.yml +++ b/.github/workflows/lintcheck.yml @@ -126,7 +126,7 @@ jobs: fail-on-cache-miss: true - name: Download JSON - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 - name: Store PR number run: echo ${{ github.event.pull_request.number }} > pr.txt diff --git a/.github/workflows/lintcheck_summary.yml b/.github/workflows/lintcheck_summary.yml index 52f52e155a07..183258a96437 100644 --- a/.github/workflows/lintcheck_summary.yml +++ b/.github/workflows/lintcheck_summary.yml @@ -27,7 +27,7 @@ jobs: if: ${{ github.event.workflow_run.conclusion == 'success' }} steps: - name: Download artifact - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: name: summary path: untrusted From 77f44f2f0760867aef315fb8429fa1c495622fb7 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 21 Sep 2025 11:05:22 +0200 Subject: [PATCH 025/226] `default_constructed_unit_structs`: use `check_source_text` --- clippy_lints/src/default_constructed_unit_structs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/default_constructed_unit_structs.rs b/clippy_lints/src/default_constructed_unit_structs.rs index f8a9037fc804..641f8ae03b72 100644 --- a/clippy_lints/src/default_constructed_unit_structs.rs +++ b/clippy_lints/src/default_constructed_unit_structs.rs @@ -75,7 +75,7 @@ impl LateLintPass<'_> for DefaultConstructedUnitStructs { && !base.is_suggestable_infer_ty() { let mut removals = vec![(expr.span.with_lo(qpath.qself_span().hi()), String::new())]; - if expr.span.with_source_text(cx, |s| s.starts_with('<')) == Some(true) { + if expr.span.check_source_text(cx, |s| s.starts_with('<')) { // Remove `<`, '>` has already been removed by the existing removal expression. removals.push((expr.span.with_hi(qpath.qself_span().lo()), String::new())); } From f76664ca83b816c08370e806b61f5230fd980d18 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 21 Sep 2025 08:57:36 +0200 Subject: [PATCH 026/226] clean-up `match_same_arms` a bit --- clippy_lints/src/matches/match_same_arms.rs | 54 ++++++++------------- 1 file changed, 20 insertions(+), 34 deletions(-) diff --git a/clippy_lints/src/matches/match_same_arms.rs b/clippy_lints/src/matches/match_same_arms.rs index ae277da089fd..818e50424554 100644 --- a/clippy_lints/src/matches/match_same_arms.rs +++ b/clippy_lints/src/matches/match_same_arms.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::SpanRangeExt; -use clippy_utils::{SpanlessEq, SpanlessHash, fulfill_or_allowed, is_lint_allowed, path_to_local, search_same}; +use clippy_utils::{SpanlessEq, fulfill_or_allowed, hash_expr, is_lint_allowed, path_to_local, search_same}; use core::cmp::Ordering; use core::{iter, slice}; use itertools::Itertools; @@ -18,11 +18,7 @@ use super::MATCH_SAME_ARMS; #[expect(clippy::too_many_lines)] pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { - let hash = |&(_, arm): &(usize, &Arm<'_>)| -> u64 { - let mut h = SpanlessHash::new(cx); - h.hash_expr(arm.body); - h.finish() - }; + let hash = |&(_, arm): &(_, &Arm<'_>)| hash_expr(cx, arm.body); let arena = DroplessArena::default(); let normalized_pats: Vec<_> = arms @@ -35,9 +31,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { .iter() .enumerate() .map(|(i, pat)| { - normalized_pats[i + 1..] - .iter() - .enumerate() + (normalized_pats[i + 1..].iter().enumerate()) .find_map(|(j, other)| pat.has_overlapping_values(other).then_some(i + 1 + j)) .unwrap_or(normalized_pats.len()) }) @@ -48,16 +42,15 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { .iter() .enumerate() .map(|(i, pat)| { - normalized_pats[..i] - .iter() - .enumerate() - .rev() - .zip(forwards_blocking_idxs[..i].iter().copied().rev()) - .skip_while(|&(_, forward_block)| forward_block > i) - .find_map(|((j, other), forward_block)| { - (forward_block == i || pat.has_overlapping_values(other)).then_some(j) - }) - .unwrap_or(0) + iter::zip( + normalized_pats[..i].iter().enumerate().rev(), + forwards_blocking_idxs[..i].iter().copied().rev(), + ) + .skip_while(|&(_, forward_block)| forward_block > i) + .find_map(|((j, other), forward_block)| { + (forward_block == i || pat.has_overlapping_values(other)).then_some(j) + }) + .unwrap_or(0) }) .collect(); @@ -158,12 +151,12 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { .map(|(_, arm)| arm.pat.span.get_source_text(cx)) .collect::>>() { - let mut suggs = src + let suggs = src .iter() .map(|(_, arm)| (adjusted_arm_span(cx, arm.span), String::new())) + .chain([(dest.pat.span, pat_snippets.iter().join(" | "))]) .collect_vec(); - suggs.push((dest.pat.span, pat_snippets.iter().join(" | "))); diag.multipart_suggestion_verbose( "otherwise merge the patterns into a single arm", suggs, @@ -396,10 +389,7 @@ impl<'a> NormalizedPat<'a> { if lpath != rpath { return false; } - lpats - .iter() - .zip(rpats.iter()) - .all(|(lpat, rpat)| lpat.has_overlapping_values(rpat)) + iter::zip(lpats, rpats).all(|(lpat, rpat)| lpat.has_overlapping_values(rpat)) }, (Self::Path(x), Self::Path(y)) => x == y, (Self::LitStr(x), Self::LitStr(y)) => x == y, @@ -409,7 +399,7 @@ impl<'a> NormalizedPat<'a> { (Self::Range(ref x), Self::Range(ref y)) => x.overlaps(y), (Self::Range(ref range), Self::LitInt(x)) | (Self::LitInt(x), Self::Range(ref range)) => range.contains(x), (Self::Slice(lpats, None), Self::Slice(rpats, None)) => { - lpats.len() == rpats.len() && lpats.iter().zip(rpats.iter()).all(|(x, y)| x.has_overlapping_values(y)) + lpats.len() == rpats.len() && iter::zip(lpats, rpats).all(|(x, y)| x.has_overlapping_values(y)) }, (Self::Slice(pats, None), Self::Slice(front, Some(back))) | (Self::Slice(front, Some(back)), Self::Slice(pats, None)) => { @@ -418,16 +408,12 @@ impl<'a> NormalizedPat<'a> { if pats.len() < front.len() + back.len() { return false; } - pats[..front.len()] - .iter() - .zip(front.iter()) - .chain(pats[pats.len() - back.len()..].iter().zip(back.iter())) + iter::zip(&pats[..front.len()], front) + .chain(iter::zip(&pats[pats.len() - back.len()..], back)) .all(|(x, y)| x.has_overlapping_values(y)) }, - (Self::Slice(lfront, Some(lback)), Self::Slice(rfront, Some(rback))) => lfront - .iter() - .zip(rfront.iter()) - .chain(lback.iter().rev().zip(rback.iter().rev())) + (Self::Slice(lfront, Some(lback)), Self::Slice(rfront, Some(rback))) => iter::zip(lfront, rfront) + .chain(iter::zip(lback.iter().rev(), rback.iter().rev())) .all(|(x, y)| x.has_overlapping_values(y)), // Enums can mix unit variants with tuple/struct variants. These can never overlap. From b6c4b3068ba7398c3e82b54675dcade446f276bf Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Fri, 19 Sep 2025 22:51:17 +0200 Subject: [PATCH 027/226] move `copies.rs` to `ifs/mod.rs` all the lints work with `if`-expressions --- clippy_lints/src/declared_lints.rs | 8 ++++---- clippy_lints/src/{copies.rs => ifs/mod.rs} | 0 clippy_lints/src/lib.rs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) rename clippy_lints/src/{copies.rs => ifs/mod.rs} (100%) diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 5563b8094f01..a468cc19004c 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -84,10 +84,6 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::collapsible_if::COLLAPSIBLE_IF_INFO, crate::collection_is_never_read::COLLECTION_IS_NEVER_READ_INFO, crate::comparison_chain::COMPARISON_CHAIN_INFO, - crate::copies::BRANCHES_SHARING_CODE_INFO, - crate::copies::IFS_SAME_COND_INFO, - crate::copies::IF_SAME_THEN_ELSE_INFO, - crate::copies::SAME_FUNCTIONS_IN_IF_CONDITION_INFO, crate::copy_iterator::COPY_ITERATOR_INFO, crate::crate_in_macro_def::CRATE_IN_MACRO_DEF_INFO, crate::create_dir::CREATE_DIR_INFO, @@ -204,6 +200,10 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::if_let_mutex::IF_LET_MUTEX_INFO, crate::if_not_else::IF_NOT_ELSE_INFO, crate::if_then_some_else_none::IF_THEN_SOME_ELSE_NONE_INFO, + crate::ifs::BRANCHES_SHARING_CODE_INFO, + crate::ifs::IFS_SAME_COND_INFO, + crate::ifs::IF_SAME_THEN_ELSE_INFO, + crate::ifs::SAME_FUNCTIONS_IN_IF_CONDITION_INFO, crate::ignored_unit_patterns::IGNORED_UNIT_PATTERNS_INFO, crate::impl_hash_with_borrow_str_and_bytes::IMPL_HASH_BORROW_WITH_STR_AND_BYTES_INFO, crate::implicit_hasher::IMPLICIT_HASHER_INFO, diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/ifs/mod.rs similarity index 100% rename from clippy_lints/src/copies.rs rename to clippy_lints/src/ifs/mod.rs diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c56fa257b068..1cae377f3fbd 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -100,7 +100,6 @@ mod cognitive_complexity; mod collapsible_if; mod collection_is_never_read; mod comparison_chain; -mod copies; mod copy_iterator; mod crate_in_macro_def; mod create_dir; @@ -158,6 +157,7 @@ mod future_not_send; mod if_let_mutex; mod if_not_else; mod if_then_some_else_none; +mod ifs; mod ignored_unit_patterns; mod impl_hash_with_borrow_str_and_bytes; mod implicit_hasher; @@ -549,7 +549,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::new(empty_enum::EmptyEnum)); store.register_late_pass(|_| Box::new(invalid_upcast_comparisons::InvalidUpcastComparisons)); store.register_late_pass(|_| Box::::default()); - store.register_late_pass(move |tcx| Box::new(copies::CopyAndPaste::new(tcx, conf))); + store.register_late_pass(move |tcx| Box::new(ifs::CopyAndPaste::new(tcx, conf))); store.register_late_pass(|_| Box::new(copy_iterator::CopyIterator)); let format_args = format_args_storage.clone(); store.register_late_pass(move |_| Box::new(format::UselessFormat::new(format_args.clone()))); From 95f5a00d8628fba223c802251af3c42547927c5a Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 21 Sep 2025 11:19:14 +0200 Subject: [PATCH 028/226] extract each lint into its own module --- clippy_lints/src/ifs/branches_sharing_code.rs | 407 ++++++++++++++ clippy_lints/src/ifs/if_same_then_else.rs | 29 + clippy_lints/src/ifs/ifs_same_cond.rs | 46 ++ clippy_lints/src/ifs/mod.rs | 508 +----------------- .../src/ifs/same_functions_in_if_cond.rs | 31 ++ 5 files changed, 525 insertions(+), 496 deletions(-) create mode 100644 clippy_lints/src/ifs/branches_sharing_code.rs create mode 100644 clippy_lints/src/ifs/if_same_then_else.rs create mode 100644 clippy_lints/src/ifs/ifs_same_cond.rs create mode 100644 clippy_lints/src/ifs/same_functions_in_if_cond.rs diff --git a/clippy_lints/src/ifs/branches_sharing_code.rs b/clippy_lints/src/ifs/branches_sharing_code.rs new file mode 100644 index 000000000000..eb1025f71498 --- /dev/null +++ b/clippy_lints/src/ifs/branches_sharing_code.rs @@ -0,0 +1,407 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::{IntoSpan, SpanRangeExt, first_line_of_span, indent_of, reindent_multiline, snippet}; +use clippy_utils::ty::needs_ordered_drop; +use clippy_utils::visitors::for_each_expr_without_closures; +use clippy_utils::{ + ContainsName, HirEqInterExpr, SpanlessEq, capture_local_usage, get_enclosing_block, hash_expr, hash_stmt, + path_to_local, +}; +use core::iter; +use core::ops::ControlFlow; +use rustc_errors::Applicability; +use rustc_hir::{Block, Expr, ExprKind, HirId, HirIdSet, LetStmt, Node, Stmt, StmtKind, intravisit}; +use rustc_lint::LateContext; +use rustc_span::hygiene::walk_chain; +use rustc_span::source_map::SourceMap; +use rustc_span::{Span, Symbol}; + +use super::BRANCHES_SHARING_CODE; + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + conds: &[&'tcx Expr<'_>], + blocks: &[&'tcx Block<'_>], + expr: &'tcx Expr<'_>, +) { + // We only lint ifs with multiple blocks + let &[first_block, ref blocks @ ..] = blocks else { + return; + }; + let &[.., last_block] = blocks else { + return; + }; + + let res = scan_block_for_eq(cx, conds, first_block, blocks); + let sm = cx.tcx.sess.source_map(); + let start_suggestion = res.start_span(first_block, sm).map(|span| { + let first_line_span = first_line_of_span(cx, expr.span); + let replace_span = first_line_span.with_hi(span.hi()); + let cond_span = first_line_span.until(first_block.span); + let cond_snippet = reindent_multiline(&snippet(cx, cond_span, "_"), false, None); + let cond_indent = indent_of(cx, cond_span); + let moved_snippet = reindent_multiline(&snippet(cx, span, "_"), true, None); + let suggestion = moved_snippet + "\n" + &cond_snippet + "{"; + let suggestion = reindent_multiline(&suggestion, true, cond_indent); + (replace_span, suggestion) + }); + let end_suggestion = res.end_span(last_block, sm).map(|span| { + let moved_snipped = reindent_multiline(&snippet(cx, span, "_"), true, None); + let indent = indent_of(cx, expr.span.shrink_to_hi()); + let suggestion = "}\n".to_string() + &moved_snipped; + let suggestion = reindent_multiline(&suggestion, true, indent); + + let span = span.with_hi(last_block.span.hi()); + // Improve formatting if the inner block has indentation (i.e. normal Rust formatting) + let span = span + .map_range(cx, |_, src, range| { + (range.start > 4 && src.get(range.start - 4..range.start)? == " ") + .then_some(range.start - 4..range.end) + }) + .map_or(span, |range| range.with_ctxt(span.ctxt())); + (span, suggestion.clone()) + }); + + let (span, msg, end_span) = match (&start_suggestion, &end_suggestion) { + (&Some((span, _)), &Some((end_span, _))) => ( + span, + "all if blocks contain the same code at both the start and the end", + Some(end_span), + ), + (&Some((span, _)), None) => (span, "all if blocks contain the same code at the start", None), + (None, &Some((span, _))) => (span, "all if blocks contain the same code at the end", None), + (None, None) => return, + }; + span_lint_and_then(cx, BRANCHES_SHARING_CODE, span, msg, |diag| { + if let Some(span) = end_span { + diag.span_note(span, "this code is shared at the end"); + } + if let Some((span, sugg)) = start_suggestion { + diag.span_suggestion( + span, + "consider moving these statements before the if", + sugg, + Applicability::Unspecified, + ); + } + if let Some((span, sugg)) = end_suggestion { + diag.span_suggestion( + span, + "consider moving these statements after the if", + sugg, + Applicability::Unspecified, + ); + if is_expr_parent_assignment(cx, expr) || !cx.typeck_results().expr_ty(expr).is_unit() { + diag.note("the end suggestion probably needs some adjustments to use the expression result correctly"); + } + } + if check_for_warn_of_moved_symbol(cx, &res.moved_locals, expr) { + diag.warn("some moved values might need to be renamed to avoid wrong references"); + } + }); +} + +struct BlockEq { + /// The end of the range of equal stmts at the start. + start_end_eq: usize, + /// The start of the range of equal stmts at the end. + end_begin_eq: Option, + /// The name and id of every local which can be moved at the beginning and the end. + moved_locals: Vec<(HirId, Symbol)>, +} +impl BlockEq { + fn start_span(&self, b: &Block<'_>, sm: &SourceMap) -> Option { + match &b.stmts[..self.start_end_eq] { + [first, .., last] => Some(sm.stmt_span(first.span, b.span).to(sm.stmt_span(last.span, b.span))), + [s] => Some(sm.stmt_span(s.span, b.span)), + [] => None, + } + } + + fn end_span(&self, b: &Block<'_>, sm: &SourceMap) -> Option { + match (&b.stmts[b.stmts.len() - self.end_begin_eq?..], b.expr) { + ([first, .., last], None) => Some(sm.stmt_span(first.span, b.span).to(sm.stmt_span(last.span, b.span))), + ([first, ..], Some(last)) => Some(sm.stmt_span(first.span, b.span).to(sm.stmt_span(last.span, b.span))), + ([s], None) => Some(sm.stmt_span(s.span, b.span)), + ([], Some(e)) => Some(walk_chain(e.span, b.span.ctxt())), + ([], None) => None, + } + } +} + +/// If the statement is a local, checks if the bound names match the expected list of names. +fn eq_binding_names(s: &Stmt<'_>, names: &[(HirId, Symbol)]) -> bool { + if let StmtKind::Let(l) = s.kind { + let mut i = 0usize; + let mut res = true; + l.pat.each_binding_or_first(&mut |_, _, _, name| { + if names.get(i).is_some_and(|&(_, n)| n == name.name) { + i += 1; + } else { + res = false; + } + }); + res && i == names.len() + } else { + false + } +} + +/// Checks if the statement modifies or moves any of the given locals. +fn modifies_any_local<'tcx>(cx: &LateContext<'tcx>, s: &'tcx Stmt<'_>, locals: &HirIdSet) -> bool { + for_each_expr_without_closures(s, |e| { + if let Some(id) = path_to_local(e) + && locals.contains(&id) + && !capture_local_usage(cx, e).is_imm_ref() + { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) + } + }) + .is_some() +} + +/// Checks if the given statement should be considered equal to the statement in the same +/// position for each block. +fn eq_stmts( + stmt: &Stmt<'_>, + blocks: &[&Block<'_>], + get_stmt: impl for<'a> Fn(&'a Block<'a>) -> Option<&'a Stmt<'a>>, + eq: &mut HirEqInterExpr<'_, '_, '_>, + moved_bindings: &mut Vec<(HirId, Symbol)>, +) -> bool { + (if let StmtKind::Let(l) = stmt.kind { + let old_count = moved_bindings.len(); + l.pat.each_binding_or_first(&mut |_, id, _, name| { + moved_bindings.push((id, name.name)); + }); + let new_bindings = &moved_bindings[old_count..]; + blocks + .iter() + .all(|b| get_stmt(b).is_some_and(|s| eq_binding_names(s, new_bindings))) + } else { + true + }) && blocks.iter().all(|b| get_stmt(b).is_some_and(|s| eq.eq_stmt(s, stmt))) +} + +#[expect(clippy::too_many_lines)] +fn scan_block_for_eq<'tcx>( + cx: &LateContext<'tcx>, + conds: &[&'tcx Expr<'_>], + block: &'tcx Block<'_>, + blocks: &[&'tcx Block<'_>], +) -> BlockEq { + let mut eq = SpanlessEq::new(cx); + let mut eq = eq.inter_expr(); + let mut moved_locals = Vec::new(); + + let mut cond_locals = HirIdSet::default(); + for &cond in conds { + let _: Option = for_each_expr_without_closures(cond, |e| { + if let Some(id) = path_to_local(e) { + cond_locals.insert(id); + } + ControlFlow::Continue(()) + }); + } + + let mut local_needs_ordered_drop = false; + let start_end_eq = block + .stmts + .iter() + .enumerate() + .find(|&(i, stmt)| { + if let StmtKind::Let(l) = stmt.kind + && needs_ordered_drop(cx, cx.typeck_results().node_type(l.hir_id)) + { + local_needs_ordered_drop = true; + return true; + } + modifies_any_local(cx, stmt, &cond_locals) + || !eq_stmts(stmt, blocks, |b| b.stmts.get(i), &mut eq, &mut moved_locals) + }) + .map_or(block.stmts.len(), |(i, stmt)| { + adjust_by_closest_callsite(i, stmt, block.stmts[..i].iter().enumerate().rev()) + }); + + if local_needs_ordered_drop { + return BlockEq { + start_end_eq, + end_begin_eq: None, + moved_locals, + }; + } + + // Walk backwards through the final expression/statements so long as their hashes are equal. Note + // `SpanlessHash` treats all local references as equal allowing locals declared earlier in the block + // to match those in other blocks. e.g. If each block ends with the following the hash value will be + // the same even though each `x` binding will have a different `HirId`: + // let x = foo(); + // x + 50 + let expr_hash_eq = if let Some(e) = block.expr { + let hash = hash_expr(cx, e); + blocks.iter().all(|b| b.expr.is_some_and(|e| hash_expr(cx, e) == hash)) + } else { + blocks.iter().all(|b| b.expr.is_none()) + }; + if !expr_hash_eq { + return BlockEq { + start_end_eq, + end_begin_eq: None, + moved_locals, + }; + } + let end_search_start = block.stmts[start_end_eq..] + .iter() + .rev() + .enumerate() + .find(|&(offset, stmt)| { + let hash = hash_stmt(cx, stmt); + blocks.iter().any(|b| { + b.stmts + // the bounds check will catch the underflow + .get(b.stmts.len().wrapping_sub(offset + 1)) + .is_none_or(|s| hash != hash_stmt(cx, s)) + }) + }) + .map_or(block.stmts.len() - start_end_eq, |(i, stmt)| { + adjust_by_closest_callsite(i, stmt, (0..i).rev().zip(block.stmts[(block.stmts.len() - i)..].iter())) + }); + + let moved_locals_at_start = moved_locals.len(); + let mut i = end_search_start; + let end_begin_eq = block.stmts[block.stmts.len() - end_search_start..] + .iter() + .zip(iter::repeat_with(move || { + let x = i; + i -= 1; + x + })) + .fold(end_search_start, |init, (stmt, offset)| { + if eq_stmts( + stmt, + blocks, + |b| b.stmts.get(b.stmts.len() - offset), + &mut eq, + &mut moved_locals, + ) { + init + } else { + // Clear out all locals seen at the end so far. None of them can be moved. + let stmts = &blocks[0].stmts; + for stmt in &stmts[stmts.len() - init..=stmts.len() - offset] { + if let StmtKind::Let(l) = stmt.kind { + l.pat.each_binding_or_first(&mut |_, id, _, _| { + // FIXME(rust/#120456) - is `swap_remove` correct? + eq.locals.swap_remove(&id); + }); + } + } + moved_locals.truncate(moved_locals_at_start); + offset - 1 + } + }); + if let Some(e) = block.expr { + for block in blocks { + if block.expr.is_some_and(|expr| !eq.eq_expr(expr, e)) { + moved_locals.truncate(moved_locals_at_start); + return BlockEq { + start_end_eq, + end_begin_eq: None, + moved_locals, + }; + } + } + } + + BlockEq { + start_end_eq, + end_begin_eq: Some(end_begin_eq), + moved_locals, + } +} + +/// Adjusts the index for which the statements begin to differ to the closest macro callsite. +/// This avoids giving suggestions that requires splitting a macro call in half, when only a +/// part of the macro expansion is equal. +/// +/// For example, for the following macro: +/// ```rust,ignore +/// macro_rules! foo { +/// ($x:expr) => { +/// let y = 42; +/// $x; +/// }; +/// } +/// ``` +/// If the macro is called like this: +/// ```rust,ignore +/// if false { +/// let z = 42; +/// foo!(println!("Hello")); +/// } else { +/// let z = 42; +/// foo!(println!("World")); +/// } +/// ``` +/// Although the expanded `let y = 42;` is equal, the macro call should not be included in the +/// suggestion. +fn adjust_by_closest_callsite<'tcx>( + i: usize, + stmt: &'tcx Stmt<'tcx>, + mut iter: impl Iterator)>, +) -> usize { + let Some((_, first)) = iter.next() else { + return 0; + }; + + // If it is already at the boundary of a macro call, then just return. + if first.span.source_callsite() != stmt.span.source_callsite() { + return i; + } + + iter.find(|(_, stmt)| stmt.span.source_callsite() != first.span.source_callsite()) + .map_or(0, |(i, _)| i + 1) +} + +fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &[(HirId, Symbol)], if_expr: &Expr<'_>) -> bool { + get_enclosing_block(cx, if_expr.hir_id).is_some_and(|block| { + let ignore_span = block.span.shrink_to_lo().to(if_expr.span); + + symbols + .iter() + .filter(|&&(_, name)| !name.as_str().starts_with('_')) + .any(|&(_, name)| { + let mut walker = ContainsName { name, cx }; + + // Scan block + let mut res = block + .stmts + .iter() + .filter(|stmt| !ignore_span.overlaps(stmt.span)) + .try_for_each(|stmt| intravisit::walk_stmt(&mut walker, stmt)); + + if let Some(expr) = block.expr + && res.is_continue() + { + res = intravisit::walk_expr(&mut walker, expr); + } + + res.is_break() + }) + }) +} + +fn is_expr_parent_assignment(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + let parent = cx.tcx.parent_hir_node(expr.hir_id); + if let Node::LetStmt(LetStmt { init: Some(e), .. }) + | Node::Expr(Expr { + kind: ExprKind::Assign(_, e, _), + .. + }) = parent + { + return e.hir_id == expr.hir_id; + } + + false +} diff --git a/clippy_lints/src/ifs/if_same_then_else.rs b/clippy_lints/src/ifs/if_same_then_else.rs new file mode 100644 index 000000000000..69402ec89076 --- /dev/null +++ b/clippy_lints/src/ifs/if_same_then_else.rs @@ -0,0 +1,29 @@ +use clippy_utils::SpanlessEq; +use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::higher::has_let_expr; +use rustc_hir::{Block, Expr}; +use rustc_lint::LateContext; + +use super::IF_SAME_THEN_ELSE; + +pub(super) fn check(cx: &LateContext<'_>, conds: &[&Expr<'_>], blocks: &[&Block<'_>]) -> bool { + let mut eq = SpanlessEq::new(cx); + blocks + .array_windows::<2>() + .enumerate() + .fold(true, |all_eq, (i, &[lhs, rhs])| { + if eq.eq_block(lhs, rhs) && !has_let_expr(conds[i]) && conds.get(i + 1).is_none_or(|e| !has_let_expr(e)) { + span_lint_and_note( + cx, + IF_SAME_THEN_ELSE, + lhs.span, + "this `if` has identical blocks", + Some(rhs.span), + "same as this", + ); + all_eq + } else { + false + } + }) +} diff --git a/clippy_lints/src/ifs/ifs_same_cond.rs b/clippy_lints/src/ifs/ifs_same_cond.rs new file mode 100644 index 000000000000..ca76fc2587db --- /dev/null +++ b/clippy_lints/src/ifs/ifs_same_cond.rs @@ -0,0 +1,46 @@ +use clippy_utils::diagnostics::span_lint; +use clippy_utils::ty::InteriorMut; +use clippy_utils::{SpanlessEq, eq_expr_value, find_binding_init, hash_expr, path_to_local, search_same}; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::LateContext; + +use super::IFS_SAME_COND; + +fn method_caller_is_mutable<'tcx>( + cx: &LateContext<'tcx>, + caller_expr: &Expr<'_>, + interior_mut: &mut InteriorMut<'tcx>, +) -> bool { + let caller_ty = cx.typeck_results().expr_ty(caller_expr); + + interior_mut.is_interior_mut_ty(cx, caller_ty) + || caller_ty.is_mutable_ptr() + // `find_binding_init` will return the binding iff its not mutable + || path_to_local(caller_expr) + .and_then(|hid| find_binding_init(cx, hid)) + .is_none() +} + +/// Implementation of `IFS_SAME_COND`. +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, conds: &[&Expr<'_>], interior_mut: &mut InteriorMut<'tcx>) { + for group in search_same( + conds, + |e| hash_expr(cx, e), + |lhs, rhs| { + // Ignore eq_expr side effects iff one of the expression kind is a method call + // and the caller is not a mutable, including inner mutable type. + if let ExprKind::MethodCall(_, caller, _, _) = lhs.kind { + if method_caller_is_mutable(cx, caller, interior_mut) { + false + } else { + SpanlessEq::new(cx).eq_expr(lhs, rhs) + } + } else { + eq_expr_value(cx, lhs, rhs) + } + }, + ) { + let spans: Vec<_> = group.into_iter().map(|expr| expr.span).collect(); + span_lint(cx, IFS_SAME_COND, spans, "these `if` branches have the same condition"); + } +} diff --git a/clippy_lints/src/ifs/mod.rs b/clippy_lints/src/ifs/mod.rs index 4fdb497950f8..739f2fc91729 100644 --- a/clippy_lints/src/ifs/mod.rs +++ b/clippy_lints/src/ifs/mod.rs @@ -1,24 +1,15 @@ use clippy_config::Conf; -use clippy_utils::diagnostics::{span_lint, span_lint_and_note, span_lint_and_then}; -use clippy_utils::higher::has_let_expr; -use clippy_utils::source::{IntoSpan, SpanRangeExt, first_line_of_span, indent_of, reindent_multiline, snippet}; -use clippy_utils::ty::{InteriorMut, needs_ordered_drop}; -use clippy_utils::visitors::for_each_expr_without_closures; -use clippy_utils::{ - ContainsName, HirEqInterExpr, SpanlessEq, capture_local_usage, eq_expr_value, find_binding_init, - get_enclosing_block, hash_expr, hash_stmt, if_sequence, is_else_clause, is_lint_allowed, path_to_local, - search_same, -}; -use core::iter; -use core::ops::ControlFlow; -use rustc_errors::Applicability; -use rustc_hir::{Block, Expr, ExprKind, HirId, HirIdSet, LetStmt, Node, Stmt, StmtKind, intravisit}; +use clippy_utils::ty::InteriorMut; +use clippy_utils::{if_sequence, is_else_clause, is_lint_allowed}; +use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; -use rustc_span::hygiene::walk_chain; -use rustc_span::source_map::SourceMap; -use rustc_span::{Span, Symbol}; + +mod branches_sharing_code; +mod if_same_then_else; +mod ifs_same_cond; +mod same_functions_in_if_cond; declare_clippy_lint! { /// ### What it does @@ -179,488 +170,13 @@ impl<'tcx> LateLintPass<'tcx> for CopyAndPaste<'tcx> { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if !expr.span.from_expansion() && matches!(expr.kind, ExprKind::If(..)) && !is_else_clause(cx.tcx, expr) { let (conds, blocks) = if_sequence(expr); - lint_same_cond(cx, &conds, &mut self.interior_mut); - lint_same_fns_in_if_cond(cx, &conds); + ifs_same_cond::check(cx, &conds, &mut self.interior_mut); + same_functions_in_if_cond::check(cx, &conds); let all_same = - !is_lint_allowed(cx, IF_SAME_THEN_ELSE, expr.hir_id) && lint_if_same_then_else(cx, &conds, &blocks); + !is_lint_allowed(cx, IF_SAME_THEN_ELSE, expr.hir_id) && if_same_then_else::check(cx, &conds, &blocks); if !all_same && conds.len() != blocks.len() { - lint_branches_sharing_code(cx, &conds, &blocks, expr); + branches_sharing_code::check(cx, &conds, &blocks, expr); } } } } - -fn lint_if_same_then_else(cx: &LateContext<'_>, conds: &[&Expr<'_>], blocks: &[&Block<'_>]) -> bool { - let mut eq = SpanlessEq::new(cx); - blocks - .array_windows::<2>() - .enumerate() - .fold(true, |all_eq, (i, &[lhs, rhs])| { - if eq.eq_block(lhs, rhs) && !has_let_expr(conds[i]) && conds.get(i + 1).is_none_or(|e| !has_let_expr(e)) { - span_lint_and_note( - cx, - IF_SAME_THEN_ELSE, - lhs.span, - "this `if` has identical blocks", - Some(rhs.span), - "same as this", - ); - all_eq - } else { - false - } - }) -} - -fn lint_branches_sharing_code<'tcx>( - cx: &LateContext<'tcx>, - conds: &[&'tcx Expr<'_>], - blocks: &[&'tcx Block<'_>], - expr: &'tcx Expr<'_>, -) { - // We only lint ifs with multiple blocks - let &[first_block, ref blocks @ ..] = blocks else { - return; - }; - let &[.., last_block] = blocks else { - return; - }; - - let res = scan_block_for_eq(cx, conds, first_block, blocks); - let sm = cx.tcx.sess.source_map(); - let start_suggestion = res.start_span(first_block, sm).map(|span| { - let first_line_span = first_line_of_span(cx, expr.span); - let replace_span = first_line_span.with_hi(span.hi()); - let cond_span = first_line_span.until(first_block.span); - let cond_snippet = reindent_multiline(&snippet(cx, cond_span, "_"), false, None); - let cond_indent = indent_of(cx, cond_span); - let moved_snippet = reindent_multiline(&snippet(cx, span, "_"), true, None); - let suggestion = moved_snippet + "\n" + &cond_snippet + "{"; - let suggestion = reindent_multiline(&suggestion, true, cond_indent); - (replace_span, suggestion) - }); - let end_suggestion = res.end_span(last_block, sm).map(|span| { - let moved_snipped = reindent_multiline(&snippet(cx, span, "_"), true, None); - let indent = indent_of(cx, expr.span.shrink_to_hi()); - let suggestion = "}\n".to_string() + &moved_snipped; - let suggestion = reindent_multiline(&suggestion, true, indent); - - let span = span.with_hi(last_block.span.hi()); - // Improve formatting if the inner block has indentation (i.e. normal Rust formatting) - let span = span - .map_range(cx, |_, src, range| { - (range.start > 4 && src.get(range.start - 4..range.start)? == " ") - .then_some(range.start - 4..range.end) - }) - .map_or(span, |range| range.with_ctxt(span.ctxt())); - (span, suggestion.clone()) - }); - - let (span, msg, end_span) = match (&start_suggestion, &end_suggestion) { - (&Some((span, _)), &Some((end_span, _))) => ( - span, - "all if blocks contain the same code at both the start and the end", - Some(end_span), - ), - (&Some((span, _)), None) => (span, "all if blocks contain the same code at the start", None), - (None, &Some((span, _))) => (span, "all if blocks contain the same code at the end", None), - (None, None) => return, - }; - span_lint_and_then(cx, BRANCHES_SHARING_CODE, span, msg, |diag| { - if let Some(span) = end_span { - diag.span_note(span, "this code is shared at the end"); - } - if let Some((span, sugg)) = start_suggestion { - diag.span_suggestion( - span, - "consider moving these statements before the if", - sugg, - Applicability::Unspecified, - ); - } - if let Some((span, sugg)) = end_suggestion { - diag.span_suggestion( - span, - "consider moving these statements after the if", - sugg, - Applicability::Unspecified, - ); - if is_expr_parent_assignment(cx, expr) || !cx.typeck_results().expr_ty(expr).is_unit() { - diag.note("the end suggestion probably needs some adjustments to use the expression result correctly"); - } - } - if check_for_warn_of_moved_symbol(cx, &res.moved_locals, expr) { - diag.warn("some moved values might need to be renamed to avoid wrong references"); - } - }); -} - -struct BlockEq { - /// The end of the range of equal stmts at the start. - start_end_eq: usize, - /// The start of the range of equal stmts at the end. - end_begin_eq: Option, - /// The name and id of every local which can be moved at the beginning and the end. - moved_locals: Vec<(HirId, Symbol)>, -} -impl BlockEq { - fn start_span(&self, b: &Block<'_>, sm: &SourceMap) -> Option { - match &b.stmts[..self.start_end_eq] { - [first, .., last] => Some(sm.stmt_span(first.span, b.span).to(sm.stmt_span(last.span, b.span))), - [s] => Some(sm.stmt_span(s.span, b.span)), - [] => None, - } - } - - fn end_span(&self, b: &Block<'_>, sm: &SourceMap) -> Option { - match (&b.stmts[b.stmts.len() - self.end_begin_eq?..], b.expr) { - ([first, .., last], None) => Some(sm.stmt_span(first.span, b.span).to(sm.stmt_span(last.span, b.span))), - ([first, ..], Some(last)) => Some(sm.stmt_span(first.span, b.span).to(sm.stmt_span(last.span, b.span))), - ([s], None) => Some(sm.stmt_span(s.span, b.span)), - ([], Some(e)) => Some(walk_chain(e.span, b.span.ctxt())), - ([], None) => None, - } - } -} - -/// If the statement is a local, checks if the bound names match the expected list of names. -fn eq_binding_names(s: &Stmt<'_>, names: &[(HirId, Symbol)]) -> bool { - if let StmtKind::Let(l) = s.kind { - let mut i = 0usize; - let mut res = true; - l.pat.each_binding_or_first(&mut |_, _, _, name| { - if names.get(i).is_some_and(|&(_, n)| n == name.name) { - i += 1; - } else { - res = false; - } - }); - res && i == names.len() - } else { - false - } -} - -/// Checks if the statement modifies or moves any of the given locals. -fn modifies_any_local<'tcx>(cx: &LateContext<'tcx>, s: &'tcx Stmt<'_>, locals: &HirIdSet) -> bool { - for_each_expr_without_closures(s, |e| { - if let Some(id) = path_to_local(e) - && locals.contains(&id) - && !capture_local_usage(cx, e).is_imm_ref() - { - ControlFlow::Break(()) - } else { - ControlFlow::Continue(()) - } - }) - .is_some() -} - -/// Checks if the given statement should be considered equal to the statement in the same position -/// for each block. -fn eq_stmts( - stmt: &Stmt<'_>, - blocks: &[&Block<'_>], - get_stmt: impl for<'a> Fn(&'a Block<'a>) -> Option<&'a Stmt<'a>>, - eq: &mut HirEqInterExpr<'_, '_, '_>, - moved_bindings: &mut Vec<(HirId, Symbol)>, -) -> bool { - (if let StmtKind::Let(l) = stmt.kind { - let old_count = moved_bindings.len(); - l.pat.each_binding_or_first(&mut |_, id, _, name| { - moved_bindings.push((id, name.name)); - }); - let new_bindings = &moved_bindings[old_count..]; - blocks - .iter() - .all(|b| get_stmt(b).is_some_and(|s| eq_binding_names(s, new_bindings))) - } else { - true - }) && blocks.iter().all(|b| get_stmt(b).is_some_and(|s| eq.eq_stmt(s, stmt))) -} - -#[expect(clippy::too_many_lines)] -fn scan_block_for_eq<'tcx>( - cx: &LateContext<'tcx>, - conds: &[&'tcx Expr<'_>], - block: &'tcx Block<'_>, - blocks: &[&'tcx Block<'_>], -) -> BlockEq { - let mut eq = SpanlessEq::new(cx); - let mut eq = eq.inter_expr(); - let mut moved_locals = Vec::new(); - - let mut cond_locals = HirIdSet::default(); - for &cond in conds { - let _: Option = for_each_expr_without_closures(cond, |e| { - if let Some(id) = path_to_local(e) { - cond_locals.insert(id); - } - ControlFlow::Continue(()) - }); - } - - let mut local_needs_ordered_drop = false; - let start_end_eq = block - .stmts - .iter() - .enumerate() - .find(|&(i, stmt)| { - if let StmtKind::Let(l) = stmt.kind - && needs_ordered_drop(cx, cx.typeck_results().node_type(l.hir_id)) - { - local_needs_ordered_drop = true; - return true; - } - modifies_any_local(cx, stmt, &cond_locals) - || !eq_stmts(stmt, blocks, |b| b.stmts.get(i), &mut eq, &mut moved_locals) - }) - .map_or(block.stmts.len(), |(i, stmt)| { - adjust_by_closest_callsite(i, stmt, block.stmts[..i].iter().enumerate().rev()) - }); - - if local_needs_ordered_drop { - return BlockEq { - start_end_eq, - end_begin_eq: None, - moved_locals, - }; - } - - // Walk backwards through the final expression/statements so long as their hashes are equal. Note - // `SpanlessHash` treats all local references as equal allowing locals declared earlier in the block - // to match those in other blocks. e.g. If each block ends with the following the hash value will be - // the same even though each `x` binding will have a different `HirId`: - // let x = foo(); - // x + 50 - let expr_hash_eq = if let Some(e) = block.expr { - let hash = hash_expr(cx, e); - blocks.iter().all(|b| b.expr.is_some_and(|e| hash_expr(cx, e) == hash)) - } else { - blocks.iter().all(|b| b.expr.is_none()) - }; - if !expr_hash_eq { - return BlockEq { - start_end_eq, - end_begin_eq: None, - moved_locals, - }; - } - let end_search_start = block.stmts[start_end_eq..] - .iter() - .rev() - .enumerate() - .find(|&(offset, stmt)| { - let hash = hash_stmt(cx, stmt); - blocks.iter().any(|b| { - b.stmts - // the bounds check will catch the underflow - .get(b.stmts.len().wrapping_sub(offset + 1)) - .is_none_or(|s| hash != hash_stmt(cx, s)) - }) - }) - .map_or(block.stmts.len() - start_end_eq, |(i, stmt)| { - adjust_by_closest_callsite(i, stmt, (0..i).rev().zip(block.stmts[(block.stmts.len() - i)..].iter())) - }); - - let moved_locals_at_start = moved_locals.len(); - let mut i = end_search_start; - let end_begin_eq = block.stmts[block.stmts.len() - end_search_start..] - .iter() - .zip(iter::repeat_with(move || { - let x = i; - i -= 1; - x - })) - .fold(end_search_start, |init, (stmt, offset)| { - if eq_stmts( - stmt, - blocks, - |b| b.stmts.get(b.stmts.len() - offset), - &mut eq, - &mut moved_locals, - ) { - init - } else { - // Clear out all locals seen at the end so far. None of them can be moved. - let stmts = &blocks[0].stmts; - for stmt in &stmts[stmts.len() - init..=stmts.len() - offset] { - if let StmtKind::Let(l) = stmt.kind { - l.pat.each_binding_or_first(&mut |_, id, _, _| { - // FIXME(rust/#120456) - is `swap_remove` correct? - eq.locals.swap_remove(&id); - }); - } - } - moved_locals.truncate(moved_locals_at_start); - offset - 1 - } - }); - if let Some(e) = block.expr { - for block in blocks { - if block.expr.is_some_and(|expr| !eq.eq_expr(expr, e)) { - moved_locals.truncate(moved_locals_at_start); - return BlockEq { - start_end_eq, - end_begin_eq: None, - moved_locals, - }; - } - } - } - - BlockEq { - start_end_eq, - end_begin_eq: Some(end_begin_eq), - moved_locals, - } -} - -/// Adjusts the index for which the statements begin to differ to the closest macro callsite. This -/// avoids giving suggestions that requires splitting a macro call in half, when only a part of the -/// macro expansion is equal. -/// -/// For example, for the following macro: -/// ```rust,ignore -/// macro_rules! foo { -/// ($x:expr) => { -/// let y = 42; -/// $x; -/// }; -/// } -/// ``` -/// If the macro is called like this: -/// ```rust,ignore -/// if false { -/// let z = 42; -/// foo!(println!("Hello")); -/// } else { -/// let z = 42; -/// foo!(println!("World")); -/// } -/// ``` -/// Although the expanded `let y = 42;` is equal, the macro call should not be included in the -/// suggestion. -fn adjust_by_closest_callsite<'tcx>( - i: usize, - stmt: &'tcx Stmt<'tcx>, - mut iter: impl Iterator)>, -) -> usize { - let Some((_, first)) = iter.next() else { - return 0; - }; - - // If it is already at the boundary of a macro call, then just return. - if first.span.source_callsite() != stmt.span.source_callsite() { - return i; - } - - iter.find(|(_, stmt)| stmt.span.source_callsite() != first.span.source_callsite()) - .map_or(0, |(i, _)| i + 1) -} - -fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &[(HirId, Symbol)], if_expr: &Expr<'_>) -> bool { - get_enclosing_block(cx, if_expr.hir_id).is_some_and(|block| { - let ignore_span = block.span.shrink_to_lo().to(if_expr.span); - - symbols - .iter() - .filter(|&&(_, name)| !name.as_str().starts_with('_')) - .any(|&(_, name)| { - let mut walker = ContainsName { name, cx }; - - // Scan block - let mut res = block - .stmts - .iter() - .filter(|stmt| !ignore_span.overlaps(stmt.span)) - .try_for_each(|stmt| intravisit::walk_stmt(&mut walker, stmt)); - - if let Some(expr) = block.expr - && res.is_continue() - { - res = intravisit::walk_expr(&mut walker, expr); - } - - res.is_break() - }) - }) -} - -fn method_caller_is_mutable<'tcx>( - cx: &LateContext<'tcx>, - caller_expr: &Expr<'_>, - interior_mut: &mut InteriorMut<'tcx>, -) -> bool { - let caller_ty = cx.typeck_results().expr_ty(caller_expr); - - interior_mut.is_interior_mut_ty(cx, caller_ty) - || caller_ty.is_mutable_ptr() - // `find_binding_init` will return the binding iff its not mutable - || path_to_local(caller_expr) - .and_then(|hid| find_binding_init(cx, hid)) - .is_none() -} - -/// Implementation of `IFS_SAME_COND`. -fn lint_same_cond<'tcx>(cx: &LateContext<'tcx>, conds: &[&Expr<'_>], interior_mut: &mut InteriorMut<'tcx>) { - for group in search_same( - conds, - |e| hash_expr(cx, e), - |lhs, rhs| { - // Ignore eq_expr side effects iff one of the expression kind is a method call - // and the caller is not a mutable, including inner mutable type. - if let ExprKind::MethodCall(_, caller, _, _) = lhs.kind { - if method_caller_is_mutable(cx, caller, interior_mut) { - false - } else { - SpanlessEq::new(cx).eq_expr(lhs, rhs) - } - } else { - eq_expr_value(cx, lhs, rhs) - } - }, - ) { - let spans: Vec<_> = group.into_iter().map(|expr| expr.span).collect(); - span_lint(cx, IFS_SAME_COND, spans, "these `if` branches have the same condition"); - } -} - -/// Implementation of `SAME_FUNCTIONS_IN_IF_CONDITION`. -fn lint_same_fns_in_if_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) { - let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool = &|&lhs, &rhs| -> bool { - // Do not lint if any expr originates from a macro - if lhs.span.from_expansion() || rhs.span.from_expansion() { - return false; - } - // Do not spawn warning if `IFS_SAME_COND` already produced it. - if eq_expr_value(cx, lhs, rhs) { - return false; - } - SpanlessEq::new(cx).eq_expr(lhs, rhs) - }; - - for group in search_same(conds, |e| hash_expr(cx, e), eq) { - let spans: Vec<_> = group.into_iter().map(|expr| expr.span).collect(); - span_lint( - cx, - SAME_FUNCTIONS_IN_IF_CONDITION, - spans, - "these `if` branches have the same function call", - ); - } -} - -fn is_expr_parent_assignment(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - let parent = cx.tcx.parent_hir_node(expr.hir_id); - if let Node::LetStmt(LetStmt { init: Some(e), .. }) - | Node::Expr(Expr { - kind: ExprKind::Assign(_, e, _), - .. - }) = parent - { - return e.hir_id == expr.hir_id; - } - - false -} diff --git a/clippy_lints/src/ifs/same_functions_in_if_cond.rs b/clippy_lints/src/ifs/same_functions_in_if_cond.rs new file mode 100644 index 000000000000..1f6bf04e22e7 --- /dev/null +++ b/clippy_lints/src/ifs/same_functions_in_if_cond.rs @@ -0,0 +1,31 @@ +use clippy_utils::diagnostics::span_lint; +use clippy_utils::{SpanlessEq, eq_expr_value, hash_expr, search_same}; +use rustc_hir::Expr; +use rustc_lint::LateContext; + +use super::SAME_FUNCTIONS_IN_IF_CONDITION; + +/// Implementation of `SAME_FUNCTIONS_IN_IF_CONDITION`. +pub(super) fn check(cx: &LateContext<'_>, conds: &[&Expr<'_>]) { + let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool = &|&lhs, &rhs| -> bool { + // Do not lint if any expr originates from a macro + if lhs.span.from_expansion() || rhs.span.from_expansion() { + return false; + } + // Do not spawn warning if `IFS_SAME_COND` already produced it. + if eq_expr_value(cx, lhs, rhs) { + return false; + } + SpanlessEq::new(cx).eq_expr(lhs, rhs) + }; + + for group in search_same(conds, |e| hash_expr(cx, e), eq) { + let spans: Vec<_> = group.into_iter().map(|expr| expr.span).collect(); + span_lint( + cx, + SAME_FUNCTIONS_IN_IF_CONDITION, + spans, + "these `if` branches have the same function call", + ); + } +} From 7d80c15fa049a808e3b7cd7ff88447f9a42c0ceb Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Sun, 21 Sep 2025 10:20:56 -0400 Subject: [PATCH 029/226] Port #[macro_export] to the new attribute parsing infrastructure Co-authored-by: Anne Stijns --- clippy_lints/src/macro_metavars_in_unsafe.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/macro_metavars_in_unsafe.rs b/clippy_lints/src/macro_metavars_in_unsafe.rs index 9071c9c95f9d..c5acaf099933 100644 --- a/clippy_lints/src/macro_metavars_in_unsafe.rs +++ b/clippy_lints/src/macro_metavars_in_unsafe.rs @@ -5,10 +5,12 @@ use itertools::Itertools; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_stmt}; use rustc_hir::{BlockCheckMode, Expr, ExprKind, HirId, Stmt, UnsafeSource}; +use rustc_hir::attrs::AttributeKind; +use rustc_hir::find_attr; use rustc_lint::{LateContext, LateLintPass, Level, LintContext}; use rustc_middle::lint::LevelAndSource; use rustc_session::impl_lint_pass; -use rustc_span::{Span, SyntaxContext, sym}; +use rustc_span::{Span, SyntaxContext}; use std::collections::BTreeMap; use std::collections::btree_map::Entry; @@ -146,7 +148,8 @@ struct BodyVisitor<'a, 'tcx> { } fn is_public_macro(cx: &LateContext<'_>, def_id: LocalDefId) -> bool { - (cx.effective_visibilities.is_exported(def_id) || cx.tcx.has_attr(def_id, sym::macro_export)) + ( cx.effective_visibilities.is_exported(def_id) || + find_attr!(cx.tcx.get_all_attrs(def_id), AttributeKind::MacroExport{..}) ) && !cx.tcx.is_doc_hidden(def_id) } From d84505b2ca9d5c79c3700eb2098f8d0dc9aabb2e Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Thu, 18 Sep 2025 22:30:21 +0200 Subject: [PATCH 030/226] Post lint on the type HIR node Out of the 5 lints in the `derive` directory, only two of them (`unsafe_derive_deserialize` and `derive_partial_eq_without_eq`) posted the lint on the ADT node. This fixes the situation for the three other lints: - `derive_hash_with_manual_eq` - `derive_ord_xor_partial_ord` - `expl_impl_clone_on_copy` This allows `#[expect]` to be used on the ADT to silence the lint. Also, this makes `expl_impl_clone_on_copy` properly use an "help" message instead of a "note" one when suggesting a fix. --- .../src/derive/derive_ord_xor_partial_ord.rs | 7 ++++--- .../derive/derive_partial_eq_without_eq.rs | 13 ++++++++---- .../src/derive/derived_hash_with_manual_eq.rs | 10 ++++++---- .../src/derive/expl_impl_clone_on_copy.rs | 20 +++++++++++++------ clippy_lints/src/derive/mod.rs | 16 ++++++++++----- .../src/derive/unsafe_derive_deserialize.rs | 12 +++++++---- tests/ui/derive.rs | 13 ++++++++++++ tests/ui/derive.stderr | 10 +++++----- tests/ui/derive_ord_xor_partial_ord.rs | 15 ++++++++++++++ tests/ui/derived_hash_with_manual_eq.rs | 16 +++++++++++++++ 10 files changed, 101 insertions(+), 31 deletions(-) diff --git a/clippy_lints/src/derive/derive_ord_xor_partial_ord.rs b/clippy_lints/src/derive/derive_ord_xor_partial_ord.rs index cbbcb2f7a3ba..274c699ff9d2 100644 --- a/clippy_lints/src/derive/derive_ord_xor_partial_ord.rs +++ b/clippy_lints/src/derive/derive_ord_xor_partial_ord.rs @@ -1,5 +1,5 @@ -use clippy_utils::diagnostics::span_lint_and_then; -use rustc_hir as hir; +use clippy_utils::diagnostics::span_lint_hir_and_then; +use rustc_hir::{self as hir, HirId}; use rustc_lint::LateContext; use rustc_middle::ty::Ty; use rustc_span::{Span, sym}; @@ -12,6 +12,7 @@ pub(super) fn check<'tcx>( span: Span, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>, + adt_hir_id: HirId, ord_is_automatically_derived: bool, ) { if let Some(ord_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Ord) @@ -38,7 +39,7 @@ pub(super) fn check<'tcx>( "you are deriving `Ord` but have implemented `PartialOrd` explicitly" }; - span_lint_and_then(cx, DERIVE_ORD_XOR_PARTIAL_ORD, span, mess, |diag| { + span_lint_hir_and_then(cx, DERIVE_ORD_XOR_PARTIAL_ORD, adt_hir_id, span, mess, |diag| { if let Some(local_def_id) = impl_id.as_local() { let hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id); diag.span_note(cx.tcx.hir_span(hir_id), "`PartialOrd` implemented here"); diff --git a/clippy_lints/src/derive/derive_partial_eq_without_eq.rs b/clippy_lints/src/derive/derive_partial_eq_without_eq.rs index ed7881c461ff..fbace0bd73ac 100644 --- a/clippy_lints/src/derive/derive_partial_eq_without_eq.rs +++ b/clippy_lints/src/derive/derive_partial_eq_without_eq.rs @@ -2,8 +2,8 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::has_non_exhaustive_attr; use clippy_utils::ty::implements_trait_with_env; use rustc_errors::Applicability; -use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_hir::{self as hir, HirId}; use rustc_lint::LateContext; use rustc_middle::ty::{self, ClauseKind, GenericParamDefKind, ParamEnv, TraitPredicate, Ty, TyCtxt, Upcast}; use rustc_span::{Span, sym}; @@ -11,7 +11,13 @@ use rustc_span::{Span, sym}; use super::DERIVE_PARTIAL_EQ_WITHOUT_EQ; /// Implementation of the `DERIVE_PARTIAL_EQ_WITHOUT_EQ` lint. -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) { +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + span: Span, + trait_ref: &hir::TraitRef<'_>, + ty: Ty<'tcx>, + adt_hir_id: HirId, +) { if let ty::Adt(adt, args) = ty.kind() && cx.tcx.visibility(adt.did()).is_public() && let Some(eq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Eq) @@ -20,7 +26,6 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &hir::T && !has_non_exhaustive_attr(cx.tcx, *adt) && !ty_implements_eq_trait(cx.tcx, ty, eq_trait_def_id) && let typing_env = typing_env_for_derived_eq(cx.tcx, adt.did(), eq_trait_def_id) - && let Some(local_def_id) = adt.did().as_local() // If all of our fields implement `Eq`, we can implement `Eq` too && adt .all_fields() @@ -30,7 +35,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &hir::T span_lint_hir_and_then( cx, DERIVE_PARTIAL_EQ_WITHOUT_EQ, - cx.tcx.local_def_id_to_hir_id(local_def_id), + adt_hir_id, span.ctxt().outer_expn_data().call_site, "you are deriving `PartialEq` and can implement `Eq`", |diag| { diff --git a/clippy_lints/src/derive/derived_hash_with_manual_eq.rs b/clippy_lints/src/derive/derived_hash_with_manual_eq.rs index 6f36a58025a2..afc02ce32d48 100644 --- a/clippy_lints/src/derive/derived_hash_with_manual_eq.rs +++ b/clippy_lints/src/derive/derived_hash_with_manual_eq.rs @@ -1,5 +1,5 @@ -use clippy_utils::diagnostics::span_lint_and_then; -use rustc_hir as hir; +use clippy_utils::diagnostics::span_lint_hir_and_then; +use rustc_hir::{HirId, TraitRef}; use rustc_lint::LateContext; use rustc_middle::ty::Ty; use rustc_span::{Span, sym}; @@ -10,8 +10,9 @@ use super::DERIVED_HASH_WITH_MANUAL_EQ; pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, span: Span, - trait_ref: &hir::TraitRef<'_>, + trait_ref: &TraitRef<'_>, ty: Ty<'tcx>, + adt_hir_id: HirId, hash_is_automatically_derived: bool, ) { if let Some(peq_trait_def_id) = cx.tcx.lang_items().eq_trait() @@ -31,9 +32,10 @@ pub(super) fn check<'tcx>( // Only care about `impl PartialEq for Foo` // For `impl PartialEq for A, input_types is [A, B] if trait_ref.instantiate_identity().args.type_at(1) == ty { - span_lint_and_then( + span_lint_hir_and_then( cx, DERIVED_HASH_WITH_MANUAL_EQ, + adt_hir_id, span, "you are deriving `Hash` but have implemented `PartialEq` explicitly", |diag| { diff --git a/clippy_lints/src/derive/expl_impl_clone_on_copy.rs b/clippy_lints/src/derive/expl_impl_clone_on_copy.rs index 6b97b4bd6b4d..dfb723b86eb9 100644 --- a/clippy_lints/src/derive/expl_impl_clone_on_copy.rs +++ b/clippy_lints/src/derive/expl_impl_clone_on_copy.rs @@ -1,13 +1,19 @@ -use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::ty::{implements_trait, is_copy}; -use rustc_hir::{self as hir, Item}; +use rustc_hir::{self as hir, HirId, Item}; use rustc_lint::LateContext; use rustc_middle::ty::{self, GenericArgKind, Ty}; use super::EXPL_IMPL_CLONE_ON_COPY; /// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint. -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) { +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + item: &Item<'_>, + trait_ref: &hir::TraitRef<'_>, + ty: Ty<'tcx>, + adt_hir_id: HirId, +) { let clone_id = match cx.tcx.lang_items().clone_trait() { Some(id) if trait_ref.trait_def_id() == Some(id) => id, _ => return, @@ -54,12 +60,14 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &h return; } - span_lint_and_note( + span_lint_hir_and_then( cx, EXPL_IMPL_CLONE_ON_COPY, + adt_hir_id, item.span, "you are implementing `Clone` explicitly on a `Copy` type", - Some(item.span), - "consider deriving `Clone` or removing `Copy`", + |diag| { + diag.span_help(item.span, "consider deriving `Clone` or removing `Copy`"); + }, ); } diff --git a/clippy_lints/src/derive/mod.rs b/clippy_lints/src/derive/mod.rs index 1d63394ce37d..06efc2709faa 100644 --- a/clippy_lints/src/derive/mod.rs +++ b/clippy_lints/src/derive/mod.rs @@ -1,3 +1,5 @@ +use clippy_utils::path_res; +use rustc_hir::def::Res; use rustc_hir::{Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -194,21 +196,25 @@ impl<'tcx> LateLintPass<'tcx> for Derive { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if let ItemKind::Impl(Impl { of_trait: Some(of_trait), + self_ty, .. }) = item.kind + && let Res::Def(_, def_id) = path_res(cx, self_ty) + && let Some(local_def_id) = def_id.as_local() { + let adt_hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id); let trait_ref = &of_trait.trait_ref; let ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); let is_automatically_derived = cx.tcx.is_automatically_derived(item.owner_id.to_def_id()); - derived_hash_with_manual_eq::check(cx, item.span, trait_ref, ty, is_automatically_derived); - derive_ord_xor_partial_ord::check(cx, item.span, trait_ref, ty, is_automatically_derived); + derived_hash_with_manual_eq::check(cx, item.span, trait_ref, ty, adt_hir_id, is_automatically_derived); + derive_ord_xor_partial_ord::check(cx, item.span, trait_ref, ty, adt_hir_id, is_automatically_derived); if is_automatically_derived { - unsafe_derive_deserialize::check(cx, item, trait_ref, ty); - derive_partial_eq_without_eq::check(cx, item.span, trait_ref, ty); + unsafe_derive_deserialize::check(cx, item, trait_ref, ty, adt_hir_id); + derive_partial_eq_without_eq::check(cx, item.span, trait_ref, ty, adt_hir_id); } else { - expl_impl_clone_on_copy::check(cx, item, trait_ref, ty); + expl_impl_clone_on_copy::check(cx, item, trait_ref, ty, adt_hir_id); } } } diff --git a/clippy_lints/src/derive/unsafe_derive_deserialize.rs b/clippy_lints/src/derive/unsafe_derive_deserialize.rs index c391e7b62289..38f3251fd389 100644 --- a/clippy_lints/src/derive/unsafe_derive_deserialize.rs +++ b/clippy_lints/src/derive/unsafe_derive_deserialize.rs @@ -4,7 +4,7 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::{is_lint_allowed, paths}; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn, walk_item}; -use rustc_hir::{self as hir, BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, Item, UnsafeSource}; +use rustc_hir::{self as hir, BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, HirId, Item, UnsafeSource}; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, Ty}; @@ -13,7 +13,13 @@ use rustc_span::{Span, sym}; use super::UNSAFE_DERIVE_DESERIALIZE; /// Implementation of the `UNSAFE_DERIVE_DESERIALIZE` lint. -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) { +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + item: &Item<'_>, + trait_ref: &hir::TraitRef<'_>, + ty: Ty<'tcx>, + adt_hir_id: HirId, +) { fn has_unsafe<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>) -> bool { let mut visitor = UnsafeVisitor { cx }; walk_item(&mut visitor, item).is_break() @@ -22,8 +28,6 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &h if let Some(trait_def_id) = trait_ref.trait_def_id() && paths::SERDE_DESERIALIZE.matches(cx, trait_def_id) && let ty::Adt(def, _) = ty.kind() - && let Some(local_def_id) = def.did().as_local() - && let adt_hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id) && !is_lint_allowed(cx, UNSAFE_DERIVE_DESERIALIZE, adt_hir_id) && cx .tcx diff --git a/tests/ui/derive.rs b/tests/ui/derive.rs index 2c5fa0be9755..305f73c92cd5 100644 --- a/tests/ui/derive.rs +++ b/tests/ui/derive.rs @@ -130,3 +130,16 @@ fn issue14558() { } fn main() {} + +mod issue15708 { + // Check that the lint posts on the type definition node + #[expect(clippy::expl_impl_clone_on_copy)] + #[derive(Copy)] + struct S; + + impl Clone for S { + fn clone(&self) -> Self { + S + } + } +} diff --git a/tests/ui/derive.stderr b/tests/ui/derive.stderr index ff2c24ff48ee..48e55fc7469e 100644 --- a/tests/ui/derive.stderr +++ b/tests/ui/derive.stderr @@ -9,7 +9,7 @@ LL | | fn clone(&self) -> Self { LL | | } | |_^ | -note: consider deriving `Clone` or removing `Copy` +help: consider deriving `Clone` or removing `Copy` --> tests/ui/derive.rs:15:1 | LL | / impl Clone for Qux { @@ -33,7 +33,7 @@ LL | | fn clone(&self) -> Self { LL | | } | |_^ | -note: consider deriving `Clone` or removing `Copy` +help: consider deriving `Clone` or removing `Copy` --> tests/ui/derive.rs:41:1 | LL | / impl<'a> Clone for Lt<'a> { @@ -55,7 +55,7 @@ LL | | fn clone(&self) -> Self { LL | | } | |_^ | -note: consider deriving `Clone` or removing `Copy` +help: consider deriving `Clone` or removing `Copy` --> tests/ui/derive.rs:54:1 | LL | / impl Clone for BigArray { @@ -77,7 +77,7 @@ LL | | fn clone(&self) -> Self { LL | | } | |_^ | -note: consider deriving `Clone` or removing `Copy` +help: consider deriving `Clone` or removing `Copy` --> tests/ui/derive.rs:67:1 | LL | / impl Clone for FnPtr { @@ -99,7 +99,7 @@ LL | | fn clone(&self) -> Self { LL | | } | |_^ | -note: consider deriving `Clone` or removing `Copy` +help: consider deriving `Clone` or removing `Copy` --> tests/ui/derive.rs:89:1 | LL | / impl Clone for Generic2 { diff --git a/tests/ui/derive_ord_xor_partial_ord.rs b/tests/ui/derive_ord_xor_partial_ord.rs index 3ef4ee9463dc..b4bb24b0d2fe 100644 --- a/tests/ui/derive_ord_xor_partial_ord.rs +++ b/tests/ui/derive_ord_xor_partial_ord.rs @@ -76,3 +76,18 @@ mod use_ord { } fn main() {} + +mod issue15708 { + use std::cmp::{Ord, Ordering}; + + // Check that the lint posts on the type definition node + #[expect(clippy::derive_ord_xor_partial_ord)] + #[derive(PartialOrd, PartialEq, Eq)] + struct DerivePartialOrdInUseOrd; + + impl Ord for DerivePartialOrdInUseOrd { + fn cmp(&self, other: &Self) -> Ordering { + Ordering::Less + } + } +} diff --git a/tests/ui/derived_hash_with_manual_eq.rs b/tests/ui/derived_hash_with_manual_eq.rs index 88b574add3f2..9f5c85d7fbc3 100644 --- a/tests/ui/derived_hash_with_manual_eq.rs +++ b/tests/ui/derived_hash_with_manual_eq.rs @@ -41,3 +41,19 @@ impl std::hash::Hash for Bah { } fn main() {} + +mod issue15708 { + // Check that the lint posts on the type definition node + #[expect(clippy::derived_hash_with_manual_eq)] + #[derive(Debug, Clone, Copy, Eq, PartialOrd, Ord, Hash)] + pub struct Span { + start: usize, + end: usize, + } + + impl PartialEq for Span { + fn eq(&self, other: &Self) -> bool { + self.start.cmp(&other.start).then(self.end.cmp(&other.end)).is_eq() + } + } +} From 27cad5a61602e0435967d2c5c34f0d94178ab1be Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 13 Aug 2025 20:30:34 +0200 Subject: [PATCH 031/226] fix `unnecessary_semicolon`: FN on `#[feature(stmt_expr_attributes)]` --- clippy_lints/src/unnecessary_semicolon.rs | 3 ++- ...ary_semicolon_feature_stmt_expr_attributes.fixed | 13 +++++++++++++ ...essary_semicolon_feature_stmt_expr_attributes.rs | 13 +++++++++++++ ...ry_semicolon_feature_stmt_expr_attributes.stderr | 11 +++++++++++ 4 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 tests/ui/unnecessary_semicolon_feature_stmt_expr_attributes.fixed create mode 100644 tests/ui/unnecessary_semicolon_feature_stmt_expr_attributes.rs create mode 100644 tests/ui/unnecessary_semicolon_feature_stmt_expr_attributes.stderr diff --git a/clippy_lints/src/unnecessary_semicolon.rs b/clippy_lints/src/unnecessary_semicolon.rs index 76e24b6bf805..e1e450a52fdf 100644 --- a/clippy_lints/src/unnecessary_semicolon.rs +++ b/clippy_lints/src/unnecessary_semicolon.rs @@ -88,7 +88,8 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessarySemicolon { ) && cx.typeck_results().expr_ty(expr).is_unit() // if a stmt has attrs, then turning it into an expr will break the code, since attrs aren't allowed on exprs - && cx.tcx.hir_attrs(stmt.hir_id).is_empty() + // -- unless the corresponding feature is enabled + && (cx.tcx.hir_attrs(stmt.hir_id).is_empty() || cx.tcx.features().stmt_expr_attributes()) { if let Some(block_is_unit) = self.is_last_in_block(stmt) { if cx.tcx.sess.edition() <= Edition2021 && leaks_droppable_temporary_with_limited_lifetime(cx, expr) { diff --git a/tests/ui/unnecessary_semicolon_feature_stmt_expr_attributes.fixed b/tests/ui/unnecessary_semicolon_feature_stmt_expr_attributes.fixed new file mode 100644 index 000000000000..b90ae1365bbf --- /dev/null +++ b/tests/ui/unnecessary_semicolon_feature_stmt_expr_attributes.fixed @@ -0,0 +1,13 @@ +#![warn(clippy::unnecessary_semicolon)] +#![feature(stmt_expr_attributes)] + +fn main() { + // removing the `;` would turn the stmt into an expr, but attrs aren't allowed on exprs + // -- unless the feature `stmt_expr_attributes` is enabled + #[rustfmt::skip] + match 0 { + 0b00 => {} 0b01 => {} + 0b11 => {} _ => {} + } + //~^ unnecessary_semicolon +} diff --git a/tests/ui/unnecessary_semicolon_feature_stmt_expr_attributes.rs b/tests/ui/unnecessary_semicolon_feature_stmt_expr_attributes.rs new file mode 100644 index 000000000000..606c901c20d3 --- /dev/null +++ b/tests/ui/unnecessary_semicolon_feature_stmt_expr_attributes.rs @@ -0,0 +1,13 @@ +#![warn(clippy::unnecessary_semicolon)] +#![feature(stmt_expr_attributes)] + +fn main() { + // removing the `;` would turn the stmt into an expr, but attrs aren't allowed on exprs + // -- unless the feature `stmt_expr_attributes` is enabled + #[rustfmt::skip] + match 0 { + 0b00 => {} 0b01 => {} + 0b11 => {} _ => {} + }; + //~^ unnecessary_semicolon +} diff --git a/tests/ui/unnecessary_semicolon_feature_stmt_expr_attributes.stderr b/tests/ui/unnecessary_semicolon_feature_stmt_expr_attributes.stderr new file mode 100644 index 000000000000..3e98a92ef299 --- /dev/null +++ b/tests/ui/unnecessary_semicolon_feature_stmt_expr_attributes.stderr @@ -0,0 +1,11 @@ +error: unnecessary semicolon + --> tests/ui/unnecessary_semicolon_feature_stmt_expr_attributes.rs:11:6 + | +LL | }; + | ^ help: remove + | + = note: `-D clippy::unnecessary-semicolon` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_semicolon)]` + +error: aborting due to 1 previous error + From 0ed31beba681fab11b8137b81c981c57cab06308 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 21 Sep 2025 23:27:21 +0200 Subject: [PATCH 032/226] rename `mut_reference` to `unnecessary_mut_passed` The lint was probably renamed at some point, but the files weren't. This made it annoying to search for the lint. --- clippy_lints/src/declared_lints.rs | 2 +- clippy_lints/src/lib.rs | 4 ++-- ...reference.rs => unnecessary_mut_passed.rs} | 0 ...nce.fixed => unnecessary_mut_passed.fixed} | 0 ...reference.rs => unnecessary_mut_passed.rs} | 0 ...e.stderr => unnecessary_mut_passed.stderr} | 24 +++++++++---------- 6 files changed, 15 insertions(+), 15 deletions(-) rename clippy_lints/src/{mut_reference.rs => unnecessary_mut_passed.rs} (100%) rename tests/ui/{mut_reference.fixed => unnecessary_mut_passed.fixed} (100%) rename tests/ui/{mut_reference.rs => unnecessary_mut_passed.rs} (100%) rename tests/ui/{mut_reference.stderr => unnecessary_mut_passed.stderr} (80%) diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 5563b8094f01..89178290e1a2 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -535,7 +535,6 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::multiple_unsafe_ops_per_block::MULTIPLE_UNSAFE_OPS_PER_BLOCK_INFO, crate::mut_key::MUTABLE_KEY_TYPE_INFO, crate::mut_mut::MUT_MUT_INFO, - crate::mut_reference::UNNECESSARY_MUT_PASSED_INFO, crate::mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL_INFO, crate::mutex_atomic::MUTEX_ATOMIC_INFO, crate::mutex_atomic::MUTEX_INTEGER_INFO, @@ -752,6 +751,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::unnecessary_box_returns::UNNECESSARY_BOX_RETURNS_INFO, crate::unnecessary_literal_bound::UNNECESSARY_LITERAL_BOUND_INFO, crate::unnecessary_map_on_constructor::UNNECESSARY_MAP_ON_CONSTRUCTOR_INFO, + crate::unnecessary_mut_passed::UNNECESSARY_MUT_PASSED_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, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c56fa257b068..1d6c83669154 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -253,7 +253,6 @@ mod multiple_bound_locations; mod multiple_unsafe_ops_per_block; mod mut_key; mod mut_mut; -mod mut_reference; mod mutable_debug_assertion; mod mutex_atomic; mod needless_arbitrary_self_type; @@ -375,6 +374,7 @@ mod unit_types; mod unnecessary_box_returns; mod unnecessary_literal_bound; mod unnecessary_map_on_constructor; +mod unnecessary_mut_passed; mod unnecessary_owned_empty_strings; mod unnecessary_self_imports; mod unnecessary_semicolon; @@ -483,7 +483,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::new(misc::LintPass)); store.register_late_pass(|_| Box::new(eta_reduction::EtaReduction)); store.register_late_pass(|_| Box::new(mut_mut::MutMut)); - store.register_late_pass(|_| Box::new(mut_reference::UnnecessaryMutPassed)); + store.register_late_pass(|_| Box::new(unnecessary_mut_passed::UnnecessaryMutPassed)); store.register_late_pass(|_| Box::>::default()); store.register_late_pass(|_| Box::new(len_zero::LenZero)); store.register_late_pass(move |_| Box::new(attrs::Attributes::new(conf))); diff --git a/clippy_lints/src/mut_reference.rs b/clippy_lints/src/unnecessary_mut_passed.rs similarity index 100% rename from clippy_lints/src/mut_reference.rs rename to clippy_lints/src/unnecessary_mut_passed.rs diff --git a/tests/ui/mut_reference.fixed b/tests/ui/unnecessary_mut_passed.fixed similarity index 100% rename from tests/ui/mut_reference.fixed rename to tests/ui/unnecessary_mut_passed.fixed diff --git a/tests/ui/mut_reference.rs b/tests/ui/unnecessary_mut_passed.rs similarity index 100% rename from tests/ui/mut_reference.rs rename to tests/ui/unnecessary_mut_passed.rs diff --git a/tests/ui/mut_reference.stderr b/tests/ui/unnecessary_mut_passed.stderr similarity index 80% rename from tests/ui/mut_reference.stderr rename to tests/ui/unnecessary_mut_passed.stderr index 5ecfaa37416b..c69a637bf408 100644 --- a/tests/ui/mut_reference.stderr +++ b/tests/ui/unnecessary_mut_passed.stderr @@ -1,5 +1,5 @@ error: the function `takes_ref` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:56:15 + --> tests/ui/unnecessary_mut_passed.rs:56:15 | LL | takes_ref(&mut 42); | ^^^^^^^ help: remove this `mut`: `&42` @@ -8,67 +8,67 @@ LL | takes_ref(&mut 42); = help: to override `-D warnings` add `#[allow(clippy::unnecessary_mut_passed)]` error: the function `takes_ref_ref` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:58:19 + --> tests/ui/unnecessary_mut_passed.rs:58:19 | LL | takes_ref_ref(&mut &42); | ^^^^^^^^ help: remove this `mut`: `&&42` error: the function `takes_ref_refmut` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:60:22 + --> tests/ui/unnecessary_mut_passed.rs:60:22 | LL | takes_ref_refmut(&mut &mut 42); | ^^^^^^^^^^^^ help: remove this `mut`: `&&mut 42` error: the function `takes_raw_const` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:62:21 + --> tests/ui/unnecessary_mut_passed.rs:62:21 | LL | takes_raw_const(&mut 42); | ^^^^^^^ help: remove this `mut`: `&42` error: the function `as_ptr` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:66:12 + --> tests/ui/unnecessary_mut_passed.rs:66:12 | LL | as_ptr(&mut 42); | ^^^^^^^ help: remove this `mut`: `&42` error: the function `as_ptr` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:69:12 + --> tests/ui/unnecessary_mut_passed.rs:69:12 | LL | as_ptr(&mut &42); | ^^^^^^^^ help: remove this `mut`: `&&42` error: the function `as_ptr` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:72:12 + --> tests/ui/unnecessary_mut_passed.rs:72:12 | LL | as_ptr(&mut &mut 42); | ^^^^^^^^^^^^ help: remove this `mut`: `&&mut 42` error: the function `as_ptr` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:75:12 + --> tests/ui/unnecessary_mut_passed.rs:75:12 | LL | as_ptr(&mut 42); | ^^^^^^^ help: remove this `mut`: `&42` error: the method `takes_ref` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:80:25 + --> tests/ui/unnecessary_mut_passed.rs:80:25 | LL | my_struct.takes_ref(&mut 42); | ^^^^^^^ help: remove this `mut`: `&42` error: the method `takes_ref_ref` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:82:29 + --> tests/ui/unnecessary_mut_passed.rs:82:29 | LL | my_struct.takes_ref_ref(&mut &42); | ^^^^^^^^ help: remove this `mut`: `&&42` error: the method `takes_ref_refmut` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:84:32 + --> tests/ui/unnecessary_mut_passed.rs:84:32 | LL | my_struct.takes_ref_refmut(&mut &mut 42); | ^^^^^^^^^^^^ help: remove this `mut`: `&&mut 42` error: the method `takes_raw_const` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:86:31 + --> tests/ui/unnecessary_mut_passed.rs:86:31 | LL | my_struct.takes_raw_const(&mut 42); | ^^^^^^^ help: remove this `mut`: `&42` From e1ae7b783781b93ec6a112ce586f75ac8db8c0d4 Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Mon, 22 Sep 2025 07:22:37 +0200 Subject: [PATCH 033/226] tests: Check error when struct from wrong crate version is used for impl --- tests/run-make/duplicate-dependency/foo-v1.rs | 1 + tests/run-make/duplicate-dependency/foo-v2.rs | 1 + tests/run-make/duplicate-dependency/main.rs | 15 +++++++ .../run-make/duplicate-dependency/main.stderr | 18 ++++++++ .../duplicate-dependency/re-export-foo.rs | 3 ++ tests/run-make/duplicate-dependency/rmake.rs | 43 +++++++++++++++++++ 6 files changed, 81 insertions(+) create mode 100644 tests/run-make/duplicate-dependency/foo-v1.rs create mode 100644 tests/run-make/duplicate-dependency/foo-v2.rs create mode 100644 tests/run-make/duplicate-dependency/main.rs create mode 100644 tests/run-make/duplicate-dependency/main.stderr create mode 100644 tests/run-make/duplicate-dependency/re-export-foo.rs create mode 100644 tests/run-make/duplicate-dependency/rmake.rs diff --git a/tests/run-make/duplicate-dependency/foo-v1.rs b/tests/run-make/duplicate-dependency/foo-v1.rs new file mode 100644 index 000000000000..4a835673a596 --- /dev/null +++ b/tests/run-make/duplicate-dependency/foo-v1.rs @@ -0,0 +1 @@ +pub struct Foo; diff --git a/tests/run-make/duplicate-dependency/foo-v2.rs b/tests/run-make/duplicate-dependency/foo-v2.rs new file mode 100644 index 000000000000..4a835673a596 --- /dev/null +++ b/tests/run-make/duplicate-dependency/foo-v2.rs @@ -0,0 +1 @@ +pub struct Foo; diff --git a/tests/run-make/duplicate-dependency/main.rs b/tests/run-make/duplicate-dependency/main.rs new file mode 100644 index 000000000000..b22d9581c9a4 --- /dev/null +++ b/tests/run-make/duplicate-dependency/main.rs @@ -0,0 +1,15 @@ +struct Bar; + +impl From for foo::Foo { + fn from(_: Bar) -> Self { + foo::Foo + } +} + +fn main() { + // The user might wrongly expect this to work since From for Foo + // implies Into for Bar. What the user missed is that different + // versions of Foo exist in the dependency graph, and the impl is for the + // wrong version. + re_export_foo::into_foo(Bar); +} diff --git a/tests/run-make/duplicate-dependency/main.stderr b/tests/run-make/duplicate-dependency/main.stderr new file mode 100644 index 000000000000..2c912f872fe3 --- /dev/null +++ b/tests/run-make/duplicate-dependency/main.stderr @@ -0,0 +1,18 @@ +error[E0277]: the trait bound `re_export_foo::foo::Foo: From` is not satisfied + --> main.rs:14:29 + | +LL | re_export_foo::into_foo(Bar); + | ----------------------- ^^^ the trait `From` is not implemented for `re_export_foo::foo::Foo` + | | + | required by a bound introduced by this call + | + = note: required for `Bar` to implement `Into` +note: required by a bound in `into_foo` + --> $DIR/re-export-foo.rs:3:25 + | +LL | pub fn into_foo(_: impl Into) {} + | ^^^^^^^^^^^^^^ required by this bound in `into_foo` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/run-make/duplicate-dependency/re-export-foo.rs b/tests/run-make/duplicate-dependency/re-export-foo.rs new file mode 100644 index 000000000000..b346cfcee9b1 --- /dev/null +++ b/tests/run-make/duplicate-dependency/re-export-foo.rs @@ -0,0 +1,3 @@ +pub use foo; + +pub fn into_foo(_: impl Into) {} diff --git a/tests/run-make/duplicate-dependency/rmake.rs b/tests/run-make/duplicate-dependency/rmake.rs new file mode 100644 index 000000000000..13ab4caaba56 --- /dev/null +++ b/tests/run-make/duplicate-dependency/rmake.rs @@ -0,0 +1,43 @@ +use run_make_support::{Rustc, cwd, diff, rust_lib_name, rustc}; + +fn rustc_with_common_args() -> Rustc { + let mut rustc = rustc(); + rustc.remap_path_prefix(cwd(), "$DIR"); + rustc.edition("2018"); // Don't require `extern crate` + rustc +} + +fn main() { + rustc_with_common_args() + .input("foo-v1.rs") + .crate_type("rlib") + .crate_name("foo") + .extra_filename("-v1") + .metadata("-v1") + .run(); + + rustc_with_common_args() + .input("foo-v2.rs") + .crate_type("rlib") + .crate_name("foo") + .extra_filename("-v2") + .metadata("-v2") + .run(); + + rustc_with_common_args() + .input("re-export-foo.rs") + .crate_type("rlib") + .extern_("foo", rust_lib_name("foo-v2")) + .run(); + + let stderr = rustc_with_common_args() + .input("main.rs") + .extern_("foo", rust_lib_name("foo-v1")) + .extern_("re_export_foo", rust_lib_name("re_export_foo")) + .library_search_path(cwd()) + .ui_testing() + .run_fail() + .stderr_utf8(); + + diff().expected_file("main.stderr").actual_text("(rustc)", &stderr).run(); +} From f51fb9178e8d3648a908136d132bbfd1811481fd Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 21 Sep 2025 23:39:59 +0200 Subject: [PATCH 034/226] kcfi: only reify trait methods when dyn-compatible --- compiler/rustc_middle/src/ty/instance.rs | 4 +- .../sanitizer/kcfi/fn-ptr-reify-shim.rs | 74 +++++++++++++++++++ .../sanitizer/kcfi/naked-function.rs | 7 +- tests/ui/sanitizer/kcfi-c-variadic.rs | 20 +++++ 4 files changed, 100 insertions(+), 5 deletions(-) create mode 100644 tests/codegen-llvm/sanitizer/kcfi/fn-ptr-reify-shim.rs create mode 100644 tests/ui/sanitizer/kcfi-c-variadic.rs diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 34ead91b4f6d..c27d47fcc0d8 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -618,11 +618,11 @@ impl<'tcx> Instance<'tcx> { // be directly reified because it's closure-like. The reify can handle the // unresolved instance. resolved = Instance { def: InstanceKind::ReifyShim(def_id, reason), args } - // Reify `Trait::method` implementations - // FIXME(maurer) only reify it if it is a vtable-safe function + // Reify `Trait::method` implementations if the trait is dyn-compatible. } else if let Some(assoc) = tcx.opt_associated_item(def_id) && let AssocContainer::Trait | AssocContainer::TraitImpl(Ok(_)) = assoc.container + && tcx.is_dyn_compatible(assoc.container_id(tcx)) { // If this function could also go in a vtable, we need to `ReifyShim` it with // KCFI because it can only attach one type per function. diff --git a/tests/codegen-llvm/sanitizer/kcfi/fn-ptr-reify-shim.rs b/tests/codegen-llvm/sanitizer/kcfi/fn-ptr-reify-shim.rs new file mode 100644 index 000000000000..604b4c8c2f8a --- /dev/null +++ b/tests/codegen-llvm/sanitizer/kcfi/fn-ptr-reify-shim.rs @@ -0,0 +1,74 @@ +//@ add-core-stubs +//@ revisions: aarch64 x86_64 +//@ [aarch64] compile-flags: --target aarch64-unknown-none +//@ [aarch64] needs-llvm-components: aarch64 +//@ [x86_64] compile-flags: --target x86_64-unknown-none +//@ [x86_64] needs-llvm-components: x86 +//@ compile-flags: -Ctarget-feature=-crt-static -Zsanitizer=kcfi -Cno-prepopulate-passes -Copt-level=0 + +#![feature(no_core, lang_items)] +#![crate_type = "lib"] +#![no_core] + +// A `ReifyShim` should only be created when the trait is dyn-compatible. + +extern crate minicore; +use minicore::*; + +trait DynCompatible { + fn dyn_name(&self) -> &'static str; + + fn dyn_name_default(&self) -> &'static str { + let _ = self; + "dyn_default" + } +} + +// Not dyn-compatible because the `Self: Sized` bound is missing. +trait NotDynCompatible { + fn not_dyn_name() -> &'static str; + + fn not_dyn_name_default() -> &'static str { + "not_dyn_default" + } +} + +struct S; + +impl DynCompatible for S { + fn dyn_name(&self) -> &'static str { + "dyn_compatible" + } +} + +impl NotDynCompatible for S { + fn not_dyn_name() -> &'static str { + "not_dyn_compatible" + } +} + +#[no_mangle] +pub fn main() { + let s = S; + + // `DynCompatible` is indeed dyn-compatible. + let _: &dyn DynCompatible = &s; + + // CHECK: call ::dyn_name{{.*}}reify.shim.fnptr + let dyn_name = S::dyn_name as fn(&S) -> &str; + let _unused = dyn_name(&s); + + // CHECK: call fn_ptr_reify_shim::DynCompatible::dyn_name_default{{.*}}reify.shim.fnptr + let dyn_name_default = S::dyn_name_default as fn(&S) -> &str; + let _unused = dyn_name_default(&s); + + // Check using $ (end-of-line) that these calls do not contain `reify.shim.fnptr`. + + // CHECK: call ::not_dyn_name{{$}} + let not_dyn_name = S::not_dyn_name as fn() -> &'static str; + let _unused = not_dyn_name(); + + // CHECK: call fn_ptr_reify_shim::NotDynCompatible::not_dyn_name_default{{$}} + let not_dyn_name_default = S::not_dyn_name_default as fn() -> &'static str; + let _unused = not_dyn_name_default(); +} diff --git a/tests/codegen-llvm/sanitizer/kcfi/naked-function.rs b/tests/codegen-llvm/sanitizer/kcfi/naked-function.rs index 2c8cdc919b85..31f59ee01dec 100644 --- a/tests/codegen-llvm/sanitizer/kcfi/naked-function.rs +++ b/tests/codegen-llvm/sanitizer/kcfi/naked-function.rs @@ -15,8 +15,9 @@ use minicore::*; struct Thing; trait MyTrait { + // NOTE: this test assumes that this trait is dyn-compatible. #[unsafe(naked)] - extern "C" fn my_naked_function() { + extern "C" fn my_naked_function(&self) { // the real function is defined // CHECK: .globl // CHECK-SAME: my_naked_function @@ -34,13 +35,13 @@ impl MyTrait for Thing {} #[unsafe(no_mangle)] pub fn main() { // Trick the compiler into generating an indirect call. - const F: extern "C" fn() = Thing::my_naked_function; + const F: extern "C" fn(&Thing) = Thing::my_naked_function; // main calls the shim function // CHECK: call void // CHECK-SAME: my_naked_function // CHECK-SAME: reify.shim.fnptr - (F)(); + (F)(&Thing); } // CHECK: declare !kcfi_type diff --git a/tests/ui/sanitizer/kcfi-c-variadic.rs b/tests/ui/sanitizer/kcfi-c-variadic.rs new file mode 100644 index 000000000000..45d00a4524eb --- /dev/null +++ b/tests/ui/sanitizer/kcfi-c-variadic.rs @@ -0,0 +1,20 @@ +//@ needs-sanitizer-kcfi +//@ no-prefer-dynamic +//@ compile-flags: -Zsanitizer=kcfi -Cpanic=abort -Cunsafe-allow-abi-mismatch=sanitizer +//@ ignore-backends: gcc +//@ run-pass + +#![feature(c_variadic)] + +trait Trait { + unsafe extern "C" fn foo(x: i32, y: i32, mut ap: ...) -> i32 { + x + y + ap.arg::() + ap.arg::() + } +} + +impl Trait for i32 {} + +fn main() { + let f = i32::foo as unsafe extern "C" fn(i32, i32, ...) -> i32; + assert_eq!(unsafe { f(1, 2, 3, 4) }, 1 + 2 + 3 + 4); +} From a23d4e16d82953c493c75acc568a2c75a368828c Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Mon, 22 Sep 2025 15:56:27 +0200 Subject: [PATCH 035/226] flip1995 is back from vacation --- triagebot.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/triagebot.toml b/triagebot.toml index 7b19f8658c08..b2fb50918f58 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -60,7 +60,6 @@ contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIB users_on_vacation = [ "matthiaskrgr", "Manishearth", - "flip1995", ] [assign.owners] From 88f954453f794e7eaa16a4cb78b63e0777e12c20 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Mon, 22 Sep 2025 20:45:37 +0200 Subject: [PATCH 036/226] compiler-team.md: remove obsolete membership level There used to be compiler team and compiler-contributor team, and those have since been collapsed into 1 team. --- src/doc/rustc-dev-guide/src/compiler-team.md | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/compiler-team.md b/src/doc/rustc-dev-guide/src/compiler-team.md index 896d9e6f6d94..8c0481f6ea8f 100644 --- a/src/doc/rustc-dev-guide/src/compiler-team.md +++ b/src/doc/rustc-dev-guide/src/compiler-team.md @@ -134,12 +134,3 @@ Getting on the reviewer rotation is much appreciated as it lowers the review burden for all of us! However, if you don't have time to give people timely feedback on their PRs, it may be better that you don't get on the list. - -### Full team membership - -Full team membership is typically extended once someone made many -contributions to the Rust compiler over time, ideally (but not -necessarily) to multiple areas. Sometimes this might be implementing a -new feature, but it is also important — perhaps more important! — to -have time and willingness to help out with general upkeep such as -bugfixes, tracking regressions, and other less glamorous work. From 526d794d807ca0e54d3411fed4c508b1c6f0f0e5 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 20 Aug 2025 01:34:05 +0200 Subject: [PATCH 037/226] create let-chain --- clippy_lints/src/non_canonical_impls.rs | 224 +++++++++++------------- 1 file changed, 107 insertions(+), 117 deletions(-) diff --git a/clippy_lints/src/non_canonical_impls.rs b/clippy_lints/src/non_canonical_impls.rs index ba67dc62abbd..c479f40707e9 100644 --- a/clippy_lints/src/non_canonical_impls.rs +++ b/clippy_lints/src/non_canonical_impls.rs @@ -114,136 +114,126 @@ declare_lint_pass!(NonCanonicalImpls => [NON_CANONICAL_CLONE_IMPL, NON_CANONICAL impl LateLintPass<'_> for NonCanonicalImpls { #[expect(clippy::too_many_lines)] fn check_impl_item<'tcx>(&mut self, cx: &LateContext<'tcx>, impl_item: &ImplItem<'tcx>) { - let Node::Item(item) = cx.tcx.parent_hir_node(impl_item.hir_id()) else { - return; - }; - let Some(trait_impl) = cx.tcx.impl_trait_ref(item.owner_id).map(EarlyBinder::skip_binder) else { - return; - }; - if cx.tcx.is_automatically_derived(item.owner_id.to_def_id()) { - return; - } - let ImplItemKind::Fn(_, impl_item_id) = cx.tcx.hir_impl_item(impl_item.impl_item_id()).kind else { - return; - }; - let body = cx.tcx.hir_body(impl_item_id); - let ExprKind::Block(block, ..) = body.value.kind else { - return; - }; - if block.span.in_external_macro(cx.sess().source_map()) || is_from_proc_macro(cx, impl_item) { - return; - } - - let trait_name = cx.tcx.get_diagnostic_name(trait_impl.def_id); - if trait_name == Some(sym::Clone) - && let Some(copy_def_id) = cx.tcx.get_diagnostic_item(sym::Copy) - && implements_trait(cx, trait_impl.self_ty(), copy_def_id, &[]) + if let Node::Item(item) = cx.tcx.parent_hir_node(impl_item.hir_id()) + && let Some(trait_impl) = cx.tcx.impl_trait_ref(item.owner_id).map(EarlyBinder::skip_binder) + && !cx.tcx.is_automatically_derived(item.owner_id.to_def_id()) + && let ImplItemKind::Fn(_, impl_item_id) = cx.tcx.hir_impl_item(impl_item.impl_item_id()).kind + && let body = cx.tcx.hir_body(impl_item_id) + && let ExprKind::Block(block, ..) = body.value.kind + && !block.span.in_external_macro(cx.sess().source_map()) + && !is_from_proc_macro(cx, impl_item) { - if impl_item.ident.name == sym::clone { - if block.stmts.is_empty() - && let Some(expr) = block.expr - && let ExprKind::Unary(UnOp::Deref, deref) = expr.kind - && let ExprKind::Path(qpath) = deref.kind - && last_path_segment(&qpath).ident.name == kw::SelfLower - { - } else { + let trait_name = cx.tcx.get_diagnostic_name(trait_impl.def_id); + if trait_name == Some(sym::Clone) + && let Some(copy_def_id) = cx.tcx.get_diagnostic_item(sym::Copy) + && implements_trait(cx, trait_impl.self_ty(), copy_def_id, &[]) + { + if impl_item.ident.name == sym::clone { + if block.stmts.is_empty() + && let Some(expr) = block.expr + && let ExprKind::Unary(UnOp::Deref, deref) = expr.kind + && let ExprKind::Path(qpath) = deref.kind + && last_path_segment(&qpath).ident.name == kw::SelfLower + { + } else { + span_lint_and_sugg( + cx, + NON_CANONICAL_CLONE_IMPL, + block.span, + "non-canonical implementation of `clone` on a `Copy` type", + "change this to", + "{ *self }".to_owned(), + Applicability::MaybeIncorrect, + ); + + return; + } + } + + if impl_item.ident.name == sym::clone_from { span_lint_and_sugg( cx, NON_CANONICAL_CLONE_IMPL, - block.span, - "non-canonical implementation of `clone` on a `Copy` type", - "change this to", - "{ *self }".to_owned(), + impl_item.span, + "unnecessary implementation of `clone_from` on a `Copy` type", + "remove it", + String::new(), Applicability::MaybeIncorrect, ); + } + } else if trait_name == Some(sym::PartialOrd) + && impl_item.ident.name == sym::partial_cmp + && let Some(ord_def_id) = cx.tcx.get_diagnostic_item(sym::Ord) + && implements_trait(cx, trait_impl.self_ty(), ord_def_id, &[]) + { + // If the `cmp` call likely needs to be fully qualified in the suggestion + // (like `std::cmp::Ord::cmp`). It's unfortunate we must put this here but we can't + // access `cmp_expr` in the suggestion without major changes, as we lint in `else`. + let mut needs_fully_qualified = false; + if block.stmts.is_empty() + && let Some(expr) = block.expr + && expr_is_cmp(cx, expr, impl_item, &mut needs_fully_qualified) + { + return; + } + // Fix #12683, allow [`needless_return`] here + else if block.expr.is_none() + && let Some(stmt) = block.stmts.first() + && let rustc_hir::StmtKind::Semi(Expr { + kind: ExprKind::Ret(Some(ret)), + .. + }) = stmt.kind + && expr_is_cmp(cx, ret, impl_item, &mut needs_fully_qualified) + { + return; + } + // If `Self` and `Rhs` are not the same type, bail. This makes creating a valid + // suggestion tons more complex. + else if let [lhs, rhs, ..] = trait_impl.args.as_slice() + && lhs != rhs + { return; } - } - if impl_item.ident.name == sym::clone_from { - span_lint_and_sugg( + span_lint_and_then( cx, - NON_CANONICAL_CLONE_IMPL, - impl_item.span, - "unnecessary implementation of `clone_from` on a `Copy` type", - "remove it", - String::new(), - Applicability::MaybeIncorrect, + NON_CANONICAL_PARTIAL_ORD_IMPL, + item.span, + "non-canonical implementation of `partial_cmp` on an `Ord` type", + |diag| { + let [_, other] = body.params else { + return; + }; + let Some(std_or_core) = std_or_core(cx) else { + return; + }; + + let suggs = match (other.pat.simple_ident(), needs_fully_qualified) { + (Some(other_ident), true) => vec![( + block.span, + format!("{{ Some({std_or_core}::cmp::Ord::cmp(self, {})) }}", other_ident.name), + )], + (Some(other_ident), false) => { + vec![(block.span, format!("{{ Some(self.cmp({})) }}", other_ident.name))] + }, + (None, true) => vec![ + ( + block.span, + format!("{{ Some({std_or_core}::cmp::Ord::cmp(self, other)) }}"), + ), + (other.pat.span, "other".to_owned()), + ], + (None, false) => vec![ + (block.span, "{ Some(self.cmp(other)) }".to_owned()), + (other.pat.span, "other".to_owned()), + ], + }; + + diag.multipart_suggestion("change this to", suggs, Applicability::Unspecified); + }, ); } - } else if trait_name == Some(sym::PartialOrd) - && impl_item.ident.name == sym::partial_cmp - && let Some(ord_def_id) = cx.tcx.get_diagnostic_item(sym::Ord) - && implements_trait(cx, trait_impl.self_ty(), ord_def_id, &[]) - { - // If the `cmp` call likely needs to be fully qualified in the suggestion - // (like `std::cmp::Ord::cmp`). It's unfortunate we must put this here but we can't - // access `cmp_expr` in the suggestion without major changes, as we lint in `else`. - let mut needs_fully_qualified = false; - - if block.stmts.is_empty() - && let Some(expr) = block.expr - && expr_is_cmp(cx, expr, impl_item, &mut needs_fully_qualified) - { - return; - } - // Fix #12683, allow [`needless_return`] here - else if block.expr.is_none() - && let Some(stmt) = block.stmts.first() - && let rustc_hir::StmtKind::Semi(Expr { - kind: ExprKind::Ret(Some(ret)), - .. - }) = stmt.kind - && expr_is_cmp(cx, ret, impl_item, &mut needs_fully_qualified) - { - return; - } - // If `Self` and `Rhs` are not the same type, bail. This makes creating a valid - // suggestion tons more complex. - else if let [lhs, rhs, ..] = trait_impl.args.as_slice() - && lhs != rhs - { - return; - } - - span_lint_and_then( - cx, - NON_CANONICAL_PARTIAL_ORD_IMPL, - item.span, - "non-canonical implementation of `partial_cmp` on an `Ord` type", - |diag| { - let [_, other] = body.params else { - return; - }; - let Some(std_or_core) = std_or_core(cx) else { - return; - }; - - let suggs = match (other.pat.simple_ident(), needs_fully_qualified) { - (Some(other_ident), true) => vec![( - block.span, - format!("{{ Some({std_or_core}::cmp::Ord::cmp(self, {})) }}", other_ident.name), - )], - (Some(other_ident), false) => { - vec![(block.span, format!("{{ Some(self.cmp({})) }}", other_ident.name))] - }, - (None, true) => vec![ - ( - block.span, - format!("{{ Some({std_or_core}::cmp::Ord::cmp(self, other)) }}"), - ), - (other.pat.span, "other".to_owned()), - ], - (None, false) => vec![ - (block.span, "{ Some(self.cmp(other)) }".to_owned()), - (other.pat.span, "other".to_owned()), - ], - }; - - diag.multipart_suggestion("change this to", suggs, Applicability::Unspecified); - }, - ); } } } From 54e28471d707c86476f7519571eea74c0016d3c8 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 20 Aug 2025 01:31:18 +0200 Subject: [PATCH 038/226] extract helper functions too many lines no more! --- clippy_lints/src/non_canonical_impls.rs | 218 +++++++++++++----------- 1 file changed, 115 insertions(+), 103 deletions(-) diff --git a/clippy_lints/src/non_canonical_impls.rs b/clippy_lints/src/non_canonical_impls.rs index c479f40707e9..5f2c388ff678 100644 --- a/clippy_lints/src/non_canonical_impls.rs +++ b/clippy_lints/src/non_canonical_impls.rs @@ -5,9 +5,9 @@ use clippy_utils::{ }; use rustc_errors::Applicability; use rustc_hir::def_id::LocalDefId; -use rustc_hir::{Expr, ExprKind, ImplItem, ImplItemKind, LangItem, Node, UnOp}; +use rustc_hir::{Block, Body, Expr, ExprKind, ImplItem, ImplItemKind, Item, LangItem, Node, UnOp}; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty::EarlyBinder; +use rustc_middle::ty::{EarlyBinder, TraitRef}; use rustc_session::declare_lint_pass; use rustc_span::sym; use rustc_span::symbol::kw; @@ -112,7 +112,6 @@ declare_clippy_lint! { declare_lint_pass!(NonCanonicalImpls => [NON_CANONICAL_CLONE_IMPL, NON_CANONICAL_PARTIAL_ORD_IMPL]); impl LateLintPass<'_> for NonCanonicalImpls { - #[expect(clippy::too_many_lines)] fn check_impl_item<'tcx>(&mut self, cx: &LateContext<'tcx>, impl_item: &ImplItem<'tcx>) { if let Node::Item(item) = cx.tcx.parent_hir_node(impl_item.hir_id()) && let Some(trait_impl) = cx.tcx.impl_trait_ref(item.owner_id).map(EarlyBinder::skip_binder) @@ -128,116 +127,129 @@ impl LateLintPass<'_> for NonCanonicalImpls { && let Some(copy_def_id) = cx.tcx.get_diagnostic_item(sym::Copy) && implements_trait(cx, trait_impl.self_ty(), copy_def_id, &[]) { - if impl_item.ident.name == sym::clone { - if block.stmts.is_empty() - && let Some(expr) = block.expr - && let ExprKind::Unary(UnOp::Deref, deref) = expr.kind - && let ExprKind::Path(qpath) = deref.kind - && last_path_segment(&qpath).ident.name == kw::SelfLower - { - } else { - span_lint_and_sugg( - cx, - NON_CANONICAL_CLONE_IMPL, - block.span, - "non-canonical implementation of `clone` on a `Copy` type", - "change this to", - "{ *self }".to_owned(), - Applicability::MaybeIncorrect, - ); - - return; - } - } - - if impl_item.ident.name == sym::clone_from { - span_lint_and_sugg( - cx, - NON_CANONICAL_CLONE_IMPL, - impl_item.span, - "unnecessary implementation of `clone_from` on a `Copy` type", - "remove it", - String::new(), - Applicability::MaybeIncorrect, - ); - } + check_clone_on_copy(cx, impl_item, block); } else if trait_name == Some(sym::PartialOrd) && impl_item.ident.name == sym::partial_cmp && let Some(ord_def_id) = cx.tcx.get_diagnostic_item(sym::Ord) && implements_trait(cx, trait_impl.self_ty(), ord_def_id, &[]) { - // If the `cmp` call likely needs to be fully qualified in the suggestion - // (like `std::cmp::Ord::cmp`). It's unfortunate we must put this here but we can't - // access `cmp_expr` in the suggestion without major changes, as we lint in `else`. - let mut needs_fully_qualified = false; - - if block.stmts.is_empty() - && let Some(expr) = block.expr - && expr_is_cmp(cx, expr, impl_item, &mut needs_fully_qualified) - { - return; - } - // Fix #12683, allow [`needless_return`] here - else if block.expr.is_none() - && let Some(stmt) = block.stmts.first() - && let rustc_hir::StmtKind::Semi(Expr { - kind: ExprKind::Ret(Some(ret)), - .. - }) = stmt.kind - && expr_is_cmp(cx, ret, impl_item, &mut needs_fully_qualified) - { - return; - } - // If `Self` and `Rhs` are not the same type, bail. This makes creating a valid - // suggestion tons more complex. - else if let [lhs, rhs, ..] = trait_impl.args.as_slice() - && lhs != rhs - { - return; - } - - span_lint_and_then( - cx, - NON_CANONICAL_PARTIAL_ORD_IMPL, - item.span, - "non-canonical implementation of `partial_cmp` on an `Ord` type", - |diag| { - let [_, other] = body.params else { - return; - }; - let Some(std_or_core) = std_or_core(cx) else { - return; - }; - - let suggs = match (other.pat.simple_ident(), needs_fully_qualified) { - (Some(other_ident), true) => vec![( - block.span, - format!("{{ Some({std_or_core}::cmp::Ord::cmp(self, {})) }}", other_ident.name), - )], - (Some(other_ident), false) => { - vec![(block.span, format!("{{ Some(self.cmp({})) }}", other_ident.name))] - }, - (None, true) => vec![ - ( - block.span, - format!("{{ Some({std_or_core}::cmp::Ord::cmp(self, other)) }}"), - ), - (other.pat.span, "other".to_owned()), - ], - (None, false) => vec![ - (block.span, "{ Some(self.cmp(other)) }".to_owned()), - (other.pat.span, "other".to_owned()), - ], - }; - - diag.multipart_suggestion("change this to", suggs, Applicability::Unspecified); - }, - ); + check_partial_ord_on_ord(cx, impl_item, item, &trait_impl, body, block); } } } } +fn check_clone_on_copy(cx: &LateContext<'_>, impl_item: &ImplItem<'_>, block: &Block<'_>) { + if impl_item.ident.name == sym::clone { + if block.stmts.is_empty() + && let Some(expr) = block.expr + && let ExprKind::Unary(UnOp::Deref, deref) = expr.kind + && let ExprKind::Path(qpath) = deref.kind + && last_path_segment(&qpath).ident.name == kw::SelfLower + { + } else { + span_lint_and_sugg( + cx, + NON_CANONICAL_CLONE_IMPL, + block.span, + "non-canonical implementation of `clone` on a `Copy` type", + "change this to", + "{ *self }".to_owned(), + Applicability::MaybeIncorrect, + ); + } + } + + if impl_item.ident.name == sym::clone_from { + span_lint_and_sugg( + cx, + NON_CANONICAL_CLONE_IMPL, + impl_item.span, + "unnecessary implementation of `clone_from` on a `Copy` type", + "remove it", + String::new(), + Applicability::MaybeIncorrect, + ); + } +} + +fn check_partial_ord_on_ord<'tcx>( + cx: &LateContext<'tcx>, + impl_item: &ImplItem<'_>, + item: &Item<'_>, + trait_impl: &TraitRef<'_>, + body: &Body<'_>, + block: &Block<'tcx>, +) { + // If the `cmp` call likely needs to be fully qualified in the suggestion + // (like `std::cmp::Ord::cmp`). It's unfortunate we must put this here but we can't + // access `cmp_expr` in the suggestion without major changes, as we lint in `else`. + + let mut needs_fully_qualified = false; + if block.stmts.is_empty() + && let Some(expr) = block.expr + && expr_is_cmp(cx, expr, impl_item, &mut needs_fully_qualified) + { + return; + } + // Fix #12683, allow [`needless_return`] here + else if block.expr.is_none() + && let Some(stmt) = block.stmts.first() + && let rustc_hir::StmtKind::Semi(Expr { + kind: ExprKind::Ret(Some(ret)), + .. + }) = stmt.kind + && expr_is_cmp(cx, ret, impl_item, &mut needs_fully_qualified) + { + return; + } + // If `Self` and `Rhs` are not the same type, bail. This makes creating a valid + // suggestion tons more complex. + else if let [lhs, rhs, ..] = trait_impl.args.as_slice() + && lhs != rhs + { + return; + } + + span_lint_and_then( + cx, + NON_CANONICAL_PARTIAL_ORD_IMPL, + item.span, + "non-canonical implementation of `partial_cmp` on an `Ord` type", + |diag| { + let [_, other] = body.params else { + return; + }; + let Some(std_or_core) = std_or_core(cx) else { + return; + }; + + let suggs = match (other.pat.simple_ident(), needs_fully_qualified) { + (Some(other_ident), true) => vec![( + block.span, + format!("{{ Some({std_or_core}::cmp::Ord::cmp(self, {})) }}", other_ident.name), + )], + (Some(other_ident), false) => { + vec![(block.span, format!("{{ Some(self.cmp({})) }}", other_ident.name))] + }, + (None, true) => vec![ + ( + block.span, + format!("{{ Some({std_or_core}::cmp::Ord::cmp(self, other)) }}"), + ), + (other.pat.span, "other".to_owned()), + ], + (None, false) => vec![ + (block.span, "{ Some(self.cmp(other)) }".to_owned()), + (other.pat.span, "other".to_owned()), + ], + }; + + diag.multipart_suggestion("change this to", suggs, Applicability::Unspecified); + }, + ); +} + /// Return true if `expr_kind` is a `cmp` call. fn expr_is_cmp<'tcx>( cx: &LateContext<'tcx>, From 34d9a2a084b42533d508cbfe3207b91b2f014eba Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 20 Aug 2025 23:17:32 +0200 Subject: [PATCH 039/226] use `match` imo it's a bit more clear than checking for the same thing (`expr.kind`) in multiple branches --- clippy_lints/src/non_canonical_impls.rs | 37 +++++++++++++------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/clippy_lints/src/non_canonical_impls.rs b/clippy_lints/src/non_canonical_impls.rs index 5f2c388ff678..7dd515734030 100644 --- a/clippy_lints/src/non_canonical_impls.rs +++ b/clippy_lints/src/non_canonical_impls.rs @@ -258,26 +258,27 @@ fn expr_is_cmp<'tcx>( needs_fully_qualified: &mut bool, ) -> bool { let impl_item_did = impl_item.owner_id.def_id; - if let ExprKind::Call( - Expr { - kind: ExprKind::Path(some_path), - hir_id: some_hir_id, - .. - }, - [cmp_expr], - ) = expr.kind - { - is_res_lang_ctor(cx, cx.qpath_res(some_path, *some_hir_id), LangItem::OptionSome) + match expr.kind { + ExprKind::Call( + Expr { + kind: ExprKind::Path(some_path), + hir_id: some_hir_id, + .. + }, + [cmp_expr], + ) => { + is_res_lang_ctor(cx, cx.qpath_res(some_path, *some_hir_id), LangItem::OptionSome) // Fix #11178, allow `Self::cmp(self, ..)` too && self_cmp_call(cx, cmp_expr, impl_item_did, needs_fully_qualified) - } else if let ExprKind::MethodCall(_, recv, [], _) = expr.kind { - cx.tcx - .typeck(impl_item_did) - .type_dependent_def_id(expr.hir_id) - .is_some_and(|def_id| is_diag_trait_item(cx, def_id, sym::Into)) - && self_cmp_call(cx, recv, impl_item_did, needs_fully_qualified) - } else { - false + }, + ExprKind::MethodCall(_, recv, [], _) => { + cx.tcx + .typeck(impl_item_did) + .type_dependent_def_id(expr.hir_id) + .is_some_and(|def_id| is_diag_trait_item(cx, def_id, sym::Into)) + && self_cmp_call(cx, recv, impl_item_did, needs_fully_qualified) + }, + _ => false, } } From 21a5948ca17787961c6dcae8964086ddadb4f1b8 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 21 Sep 2025 22:04:25 +0200 Subject: [PATCH 040/226] incorporate review feedback --- clippy_lints/src/non_canonical_impls.rs | 30 ++++++++++++++----------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/clippy_lints/src/non_canonical_impls.rs b/clippy_lints/src/non_canonical_impls.rs index 7dd515734030..e531f797272d 100644 --- a/clippy_lints/src/non_canonical_impls.rs +++ b/clippy_lints/src/non_canonical_impls.rs @@ -113,16 +113,18 @@ declare_lint_pass!(NonCanonicalImpls => [NON_CANONICAL_CLONE_IMPL, NON_CANONICAL impl LateLintPass<'_> for NonCanonicalImpls { fn check_impl_item<'tcx>(&mut self, cx: &LateContext<'tcx>, impl_item: &ImplItem<'tcx>) { - if let Node::Item(item) = cx.tcx.parent_hir_node(impl_item.hir_id()) + if let ImplItemKind::Fn(_, impl_item_id) = impl_item.kind + && let Node::Item(item) = cx.tcx.parent_hir_node(impl_item.hir_id()) && let Some(trait_impl) = cx.tcx.impl_trait_ref(item.owner_id).map(EarlyBinder::skip_binder) + && let trait_name = cx.tcx.get_diagnostic_name(trait_impl.def_id) + // NOTE: check this early to avoid expensive checks that come after this one + && matches!(trait_name, Some(sym::Clone | sym::PartialOrd)) && !cx.tcx.is_automatically_derived(item.owner_id.to_def_id()) - && let ImplItemKind::Fn(_, impl_item_id) = cx.tcx.hir_impl_item(impl_item.impl_item_id()).kind && let body = cx.tcx.hir_body(impl_item_id) && let ExprKind::Block(block, ..) = body.value.kind && !block.span.in_external_macro(cx.sess().source_map()) && !is_from_proc_macro(cx, impl_item) { - let trait_name = cx.tcx.get_diagnostic_name(trait_impl.def_id); if trait_name == Some(sym::Clone) && let Some(copy_def_id) = cx.tcx.get_diagnostic_item(sym::Copy) && implements_trait(cx, trait_impl.self_ty(), copy_def_id, &[]) @@ -147,17 +149,19 @@ fn check_clone_on_copy(cx: &LateContext<'_>, impl_item: &ImplItem<'_>, block: &B && let ExprKind::Path(qpath) = deref.kind && last_path_segment(&qpath).ident.name == kw::SelfLower { - } else { - span_lint_and_sugg( - cx, - NON_CANONICAL_CLONE_IMPL, - block.span, - "non-canonical implementation of `clone` on a `Copy` type", - "change this to", - "{ *self }".to_owned(), - Applicability::MaybeIncorrect, - ); + // this is the canonical implementation, `fn clone(&self) -> Self { *self }` + return; } + + span_lint_and_sugg( + cx, + NON_CANONICAL_CLONE_IMPL, + block.span, + "non-canonical implementation of `clone` on a `Copy` type", + "change this to", + "{ *self }".to_owned(), + Applicability::MaybeIncorrect, + ); } if impl_item.ident.name == sym::clone_from { From 220e137089943b3977cdac4755212646bded08dc Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 23 Sep 2025 14:34:38 +0200 Subject: [PATCH 041/226] split `unnecessary_clone` test into `clone_on_ref_ptr` and `clone_on_copy` --- tests/ui/clone_on_copy.fixed | 65 +++++++++++++---- tests/ui/clone_on_copy.rs | 65 +++++++++++++---- tests/ui/clone_on_copy.stderr | 26 ++++--- tests/ui/clone_on_ref_ptr.fixed | 51 ++++++++++++++ tests/ui/clone_on_ref_ptr.rs | 51 ++++++++++++++ tests/ui/clone_on_ref_ptr.stderr | 41 +++++++++++ tests/ui/unnecessary_clone.rs | 111 ------------------------------ tests/ui/unnecessary_clone.stderr | 62 ----------------- 8 files changed, 261 insertions(+), 211 deletions(-) create mode 100644 tests/ui/clone_on_ref_ptr.fixed create mode 100644 tests/ui/clone_on_ref_ptr.rs create mode 100644 tests/ui/clone_on_ref_ptr.stderr delete mode 100644 tests/ui/unnecessary_clone.rs delete mode 100644 tests/ui/unnecessary_clone.stderr diff --git a/tests/ui/clone_on_copy.fixed b/tests/ui/clone_on_copy.fixed index 2dd8af152515..469121bbd740 100644 --- a/tests/ui/clone_on_copy.fixed +++ b/tests/ui/clone_on_copy.fixed @@ -1,25 +1,16 @@ +#![warn(clippy::clone_on_copy)] #![allow( - unused, clippy::redundant_clone, clippy::deref_addrof, clippy::no_effect, clippy::unnecessary_operation, - clippy::vec_init_then_push, clippy::toplevel_ref_arg, clippy::needless_borrow )] use std::cell::RefCell; -use std::rc::{self, Rc}; -use std::sync::{self, Arc}; -fn main() {} - -fn is_ascii(ch: char) -> bool { - ch.is_ascii() -} - -fn clone_on_copy() -> Option<(i32)> { +fn main() { 42; //~^ clone_on_copy @@ -65,20 +56,66 @@ fn clone_on_copy() -> Option<(i32)> { let x = 42; let ref y = x.clone(); // ok, binds by reference let ref mut y = x.clone(); // ok, binds by reference +} + +mod issue3052 { + struct A; + struct B; + struct C; + struct D; + #[derive(Copy, Clone)] + struct E; + + macro_rules! impl_deref { + ($src:ident, $dst:ident) => { + impl std::ops::Deref for $src { + type Target = $dst; + fn deref(&self) -> &Self::Target { + &$dst + } + } + }; + } + + impl_deref!(A, B); + impl_deref!(B, C); + impl_deref!(C, D); + impl std::ops::Deref for D { + type Target = &'static E; + fn deref(&self) -> &Self::Target { + &&E + } + } + + fn go1() { + let a = A; + let _: E = *****a; + //~^ clone_on_copy + + let _: E = *****a; + } +} + +fn issue4348() { + fn is_ascii(ch: char) -> bool { + ch.is_ascii() + } - // Issue #4348 let mut x = 43; let _ = &x.clone(); // ok, getting a ref 'a'.clone().make_ascii_uppercase(); // ok, clone and then mutate is_ascii('z'); //~^ clone_on_copy +} - // Issue #5436 +#[expect(clippy::vec_init_then_push)] +fn issue5436() { let mut vec = Vec::new(); vec.push(42); //~^ clone_on_copy +} - // Issue #9277 +fn issue9277() -> Option { let opt: &Option = &None; let value = (*opt)?; // operator precedence needed (*opt)? // diff --git a/tests/ui/clone_on_copy.rs b/tests/ui/clone_on_copy.rs index a371afb04a78..b05f1d3aa35e 100644 --- a/tests/ui/clone_on_copy.rs +++ b/tests/ui/clone_on_copy.rs @@ -1,25 +1,16 @@ +#![warn(clippy::clone_on_copy)] #![allow( - unused, clippy::redundant_clone, clippy::deref_addrof, clippy::no_effect, clippy::unnecessary_operation, - clippy::vec_init_then_push, clippy::toplevel_ref_arg, clippy::needless_borrow )] use std::cell::RefCell; -use std::rc::{self, Rc}; -use std::sync::{self, Arc}; -fn main() {} - -fn is_ascii(ch: char) -> bool { - ch.is_ascii() -} - -fn clone_on_copy() -> Option<(i32)> { +fn main() { 42.clone(); //~^ clone_on_copy @@ -65,20 +56,66 @@ fn clone_on_copy() -> Option<(i32)> { let x = 42; let ref y = x.clone(); // ok, binds by reference let ref mut y = x.clone(); // ok, binds by reference +} + +mod issue3052 { + struct A; + struct B; + struct C; + struct D; + #[derive(Copy, Clone)] + struct E; + + macro_rules! impl_deref { + ($src:ident, $dst:ident) => { + impl std::ops::Deref for $src { + type Target = $dst; + fn deref(&self) -> &Self::Target { + &$dst + } + } + }; + } + + impl_deref!(A, B); + impl_deref!(B, C); + impl_deref!(C, D); + impl std::ops::Deref for D { + type Target = &'static E; + fn deref(&self) -> &Self::Target { + &&E + } + } + + fn go1() { + let a = A; + let _: E = a.clone(); + //~^ clone_on_copy + + let _: E = *****a; + } +} + +fn issue4348() { + fn is_ascii(ch: char) -> bool { + ch.is_ascii() + } - // Issue #4348 let mut x = 43; let _ = &x.clone(); // ok, getting a ref 'a'.clone().make_ascii_uppercase(); // ok, clone and then mutate is_ascii('z'.clone()); //~^ clone_on_copy +} - // Issue #5436 +#[expect(clippy::vec_init_then_push)] +fn issue5436() { let mut vec = Vec::new(); vec.push(42.clone()); //~^ clone_on_copy +} - // Issue #9277 +fn issue9277() -> Option { let opt: &Option = &None; let value = opt.clone()?; // operator precedence needed (*opt)? // diff --git a/tests/ui/clone_on_copy.stderr b/tests/ui/clone_on_copy.stderr index 92cdd635d20a..c87d1d488dde 100644 --- a/tests/ui/clone_on_copy.stderr +++ b/tests/ui/clone_on_copy.stderr @@ -1,5 +1,5 @@ error: using `clone` on type `i32` which implements the `Copy` trait - --> tests/ui/clone_on_copy.rs:23:5 + --> tests/ui/clone_on_copy.rs:14:5 | LL | 42.clone(); | ^^^^^^^^^^ help: try removing the `clone` call: `42` @@ -8,52 +8,58 @@ LL | 42.clone(); = help: to override `-D warnings` add `#[allow(clippy::clone_on_copy)]` error: using `clone` on type `i32` which implements the `Copy` trait - --> tests/ui/clone_on_copy.rs:28:5 + --> tests/ui/clone_on_copy.rs:19:5 | LL | (&42).clone(); | ^^^^^^^^^^^^^ help: try dereferencing it: `*(&42)` error: using `clone` on type `i32` which implements the `Copy` trait - --> tests/ui/clone_on_copy.rs:32:5 + --> tests/ui/clone_on_copy.rs:23:5 | LL | rc.borrow().clone(); | ^^^^^^^^^^^^^^^^^^^ help: try dereferencing it: `*rc.borrow()` error: using `clone` on type `u32` which implements the `Copy` trait - --> tests/ui/clone_on_copy.rs:36:5 + --> tests/ui/clone_on_copy.rs:27:5 | LL | x.clone().rotate_left(1); | ^^^^^^^^^ help: try removing the `clone` call: `x` error: using `clone` on type `i32` which implements the `Copy` trait - --> tests/ui/clone_on_copy.rs:51:5 + --> tests/ui/clone_on_copy.rs:42:5 | LL | m!(42).clone(); | ^^^^^^^^^^^^^^ help: try removing the `clone` call: `m!(42)` error: using `clone` on type `[u32; 2]` which implements the `Copy` trait - --> tests/ui/clone_on_copy.rs:62:5 + --> tests/ui/clone_on_copy.rs:53:5 | LL | x.clone()[0]; | ^^^^^^^^^ help: try dereferencing it: `(*x)` +error: using `clone` on type `E` which implements the `Copy` trait + --> tests/ui/clone_on_copy.rs:92:20 + | +LL | let _: E = a.clone(); + | ^^^^^^^^^ help: try dereferencing it: `*****a` + error: using `clone` on type `char` which implements the `Copy` trait - --> tests/ui/clone_on_copy.rs:73:14 + --> tests/ui/clone_on_copy.rs:107:14 | LL | is_ascii('z'.clone()); | ^^^^^^^^^^^ help: try removing the `clone` call: `'z'` error: using `clone` on type `i32` which implements the `Copy` trait - --> tests/ui/clone_on_copy.rs:78:14 + --> tests/ui/clone_on_copy.rs:114:14 | LL | vec.push(42.clone()); | ^^^^^^^^^^ help: try removing the `clone` call: `42` error: using `clone` on type `Option` which implements the `Copy` trait - --> tests/ui/clone_on_copy.rs:83:17 + --> tests/ui/clone_on_copy.rs:120:17 | LL | let value = opt.clone()?; // operator precedence needed (*opt)? | ^^^^^^^^^^^ help: try dereferencing it: `(*opt)` -error: aborting due to 9 previous errors +error: aborting due to 10 previous errors diff --git a/tests/ui/clone_on_ref_ptr.fixed b/tests/ui/clone_on_ref_ptr.fixed new file mode 100644 index 000000000000..8ef4b3656636 --- /dev/null +++ b/tests/ui/clone_on_ref_ptr.fixed @@ -0,0 +1,51 @@ +#![warn(clippy::clone_on_ref_ptr)] + +use std::rc::{Rc, Weak as RcWeak}; +use std::sync::{Arc, Weak as ArcWeak}; + +fn main() {} + +fn clone_on_ref_ptr(rc: Rc, rc_weak: RcWeak, arc: Arc, arc_weak: ArcWeak) { + std::rc::Rc::::clone(&rc); + //~^ clone_on_ref_ptr + std::rc::Weak::::clone(&rc_weak); + //~^ clone_on_ref_ptr + std::sync::Arc::::clone(&arc); + //~^ clone_on_ref_ptr + std::sync::Weak::::clone(&arc_weak); + //~^ clone_on_ref_ptr + + Rc::clone(&rc); + Arc::clone(&arc); + RcWeak::clone(&rc_weak); + ArcWeak::clone(&arc_weak); +} + +trait SomeTrait {} +struct SomeImpl; +impl SomeTrait for SomeImpl {} + +fn trait_object() { + let x = Arc::new(SomeImpl); + let _: Arc = std::sync::Arc::::clone(&x); + //~^ clone_on_ref_ptr +} + +mod issue2076 { + use std::rc::Rc; + + macro_rules! try_opt { + ($expr: expr) => { + match $expr { + Some(value) => value, + None => return None, + } + }; + } + + fn func() -> Option> { + let rc = Rc::new(42); + Some(std::rc::Rc::::clone(&try_opt!(Some(rc)))) + //~^ clone_on_ref_ptr + } +} diff --git a/tests/ui/clone_on_ref_ptr.rs b/tests/ui/clone_on_ref_ptr.rs new file mode 100644 index 000000000000..fbd787099aee --- /dev/null +++ b/tests/ui/clone_on_ref_ptr.rs @@ -0,0 +1,51 @@ +#![warn(clippy::clone_on_ref_ptr)] + +use std::rc::{Rc, Weak as RcWeak}; +use std::sync::{Arc, Weak as ArcWeak}; + +fn main() {} + +fn clone_on_ref_ptr(rc: Rc, rc_weak: RcWeak, arc: Arc, arc_weak: ArcWeak) { + rc.clone(); + //~^ clone_on_ref_ptr + rc_weak.clone(); + //~^ clone_on_ref_ptr + arc.clone(); + //~^ clone_on_ref_ptr + arc_weak.clone(); + //~^ clone_on_ref_ptr + + Rc::clone(&rc); + Arc::clone(&arc); + RcWeak::clone(&rc_weak); + ArcWeak::clone(&arc_weak); +} + +trait SomeTrait {} +struct SomeImpl; +impl SomeTrait for SomeImpl {} + +fn trait_object() { + let x = Arc::new(SomeImpl); + let _: Arc = x.clone(); + //~^ clone_on_ref_ptr +} + +mod issue2076 { + use std::rc::Rc; + + macro_rules! try_opt { + ($expr: expr) => { + match $expr { + Some(value) => value, + None => return None, + } + }; + } + + fn func() -> Option> { + let rc = Rc::new(42); + Some(try_opt!(Some(rc)).clone()) + //~^ clone_on_ref_ptr + } +} diff --git a/tests/ui/clone_on_ref_ptr.stderr b/tests/ui/clone_on_ref_ptr.stderr new file mode 100644 index 000000000000..b15f0e803a35 --- /dev/null +++ b/tests/ui/clone_on_ref_ptr.stderr @@ -0,0 +1,41 @@ +error: using `.clone()` on a ref-counted pointer + --> tests/ui/clone_on_ref_ptr.rs:9:5 + | +LL | rc.clone(); + | ^^^^^^^^^^ help: try: `std::rc::Rc::::clone(&rc)` + | + = note: `-D clippy::clone-on-ref-ptr` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::clone_on_ref_ptr)]` + +error: using `.clone()` on a ref-counted pointer + --> tests/ui/clone_on_ref_ptr.rs:11:5 + | +LL | rc_weak.clone(); + | ^^^^^^^^^^^^^^^ help: try: `std::rc::Weak::::clone(&rc_weak)` + +error: using `.clone()` on a ref-counted pointer + --> tests/ui/clone_on_ref_ptr.rs:13:5 + | +LL | arc.clone(); + | ^^^^^^^^^^^ help: try: `std::sync::Arc::::clone(&arc)` + +error: using `.clone()` on a ref-counted pointer + --> tests/ui/clone_on_ref_ptr.rs:15:5 + | +LL | arc_weak.clone(); + | ^^^^^^^^^^^^^^^^ help: try: `std::sync::Weak::::clone(&arc_weak)` + +error: using `.clone()` on a ref-counted pointer + --> tests/ui/clone_on_ref_ptr.rs:30:33 + | +LL | let _: Arc = x.clone(); + | ^^^^^^^^^ help: try: `std::sync::Arc::::clone(&x)` + +error: using `.clone()` on a ref-counted pointer + --> tests/ui/clone_on_ref_ptr.rs:48:14 + | +LL | Some(try_opt!(Some(rc)).clone()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::rc::Rc::::clone(&try_opt!(Some(rc)))` + +error: aborting due to 6 previous errors + diff --git a/tests/ui/unnecessary_clone.rs b/tests/ui/unnecessary_clone.rs deleted file mode 100644 index 7335b6f9f039..000000000000 --- a/tests/ui/unnecessary_clone.rs +++ /dev/null @@ -1,111 +0,0 @@ -// does not test any rustfixable lints -#![warn(clippy::clone_on_ref_ptr)] -#![allow(unused)] -#![allow(clippy::redundant_clone, clippy::uninlined_format_args, clippy::unnecessary_wraps)] -//@no-rustfix -use std::cell::RefCell; -use std::rc::{self, Rc}; -use std::sync::{self, Arc}; - -trait SomeTrait {} -struct SomeImpl; -impl SomeTrait for SomeImpl {} - -fn main() {} - -fn clone_on_ref_ptr() { - let rc = Rc::new(true); - let arc = Arc::new(true); - - let rcweak = Rc::downgrade(&rc); - let arc_weak = Arc::downgrade(&arc); - - rc.clone(); - //~^ clone_on_ref_ptr - - Rc::clone(&rc); - - arc.clone(); - //~^ clone_on_ref_ptr - - Arc::clone(&arc); - - rcweak.clone(); - //~^ clone_on_ref_ptr - - rc::Weak::clone(&rcweak); - - arc_weak.clone(); - //~^ clone_on_ref_ptr - - sync::Weak::clone(&arc_weak); - - let x = Arc::new(SomeImpl); - let _: Arc = x.clone(); - //~^ clone_on_ref_ptr -} - -fn clone_on_copy_generic(t: T) { - t.clone(); - //~^ clone_on_copy - - Some(t).clone(); - //~^ clone_on_copy -} - -mod many_derefs { - struct A; - struct B; - struct C; - struct D; - #[derive(Copy, Clone)] - struct E; - - macro_rules! impl_deref { - ($src:ident, $dst:ident) => { - impl std::ops::Deref for $src { - type Target = $dst; - fn deref(&self) -> &Self::Target { - &$dst - } - } - }; - } - - impl_deref!(A, B); - impl_deref!(B, C); - impl_deref!(C, D); - impl std::ops::Deref for D { - type Target = &'static E; - fn deref(&self) -> &Self::Target { - &&E - } - } - - fn go1() { - let a = A; - let _: E = a.clone(); - //~^ clone_on_copy - - let _: E = *****a; - } -} - -mod issue2076 { - use std::rc::Rc; - - macro_rules! try_opt { - ($expr: expr) => { - match $expr { - Some(value) => value, - None => return None, - } - }; - } - - fn func() -> Option> { - let rc = Rc::new(42); - Some(try_opt!(Some(rc)).clone()) - //~^ clone_on_ref_ptr - } -} diff --git a/tests/ui/unnecessary_clone.stderr b/tests/ui/unnecessary_clone.stderr deleted file mode 100644 index 17518e123d12..000000000000 --- a/tests/ui/unnecessary_clone.stderr +++ /dev/null @@ -1,62 +0,0 @@ -error: using `.clone()` on a ref-counted pointer - --> tests/ui/unnecessary_clone.rs:23:5 - | -LL | rc.clone(); - | ^^^^^^^^^^ help: try: `std::rc::Rc::::clone(&rc)` - | - = note: `-D clippy::clone-on-ref-ptr` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::clone_on_ref_ptr)]` - -error: using `.clone()` on a ref-counted pointer - --> tests/ui/unnecessary_clone.rs:28:5 - | -LL | arc.clone(); - | ^^^^^^^^^^^ help: try: `std::sync::Arc::::clone(&arc)` - -error: using `.clone()` on a ref-counted pointer - --> tests/ui/unnecessary_clone.rs:33:5 - | -LL | rcweak.clone(); - | ^^^^^^^^^^^^^^ help: try: `std::rc::Weak::::clone(&rcweak)` - -error: using `.clone()` on a ref-counted pointer - --> tests/ui/unnecessary_clone.rs:38:5 - | -LL | arc_weak.clone(); - | ^^^^^^^^^^^^^^^^ help: try: `std::sync::Weak::::clone(&arc_weak)` - -error: using `.clone()` on a ref-counted pointer - --> tests/ui/unnecessary_clone.rs:44:33 - | -LL | let _: Arc = x.clone(); - | ^^^^^^^^^ help: try: `std::sync::Arc::::clone(&x)` - -error: using `clone` on type `T` which implements the `Copy` trait - --> tests/ui/unnecessary_clone.rs:49:5 - | -LL | t.clone(); - | ^^^^^^^^^ help: try removing the `clone` call: `t` - | - = note: `-D clippy::clone-on-copy` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::clone_on_copy)]` - -error: using `clone` on type `Option` which implements the `Copy` trait - --> tests/ui/unnecessary_clone.rs:52:5 - | -LL | Some(t).clone(); - | ^^^^^^^^^^^^^^^ help: try removing the `clone` call: `Some(t)` - -error: using `clone` on type `E` which implements the `Copy` trait - --> tests/ui/unnecessary_clone.rs:87:20 - | -LL | let _: E = a.clone(); - | ^^^^^^^^^ help: try dereferencing it: `*****a` - -error: using `.clone()` on a ref-counted pointer - --> tests/ui/unnecessary_clone.rs:108:14 - | -LL | Some(try_opt!(Some(rc)).clone()) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::rc::Rc::::clone(&try_opt!(Some(rc)))` - -error: aborting due to 9 previous errors - From 38a2829c97e07e7a403d59424403561de4f91a3a Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 23 Sep 2025 16:58:45 +0200 Subject: [PATCH 042/226] doc(trait_checking): use `is_some_and` --- book/src/development/trait_checking.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/book/src/development/trait_checking.md b/book/src/development/trait_checking.md index 6d01496eebe0..c6f6f6bd2f99 100644 --- a/book/src/development/trait_checking.md +++ b/book/src/development/trait_checking.md @@ -24,12 +24,11 @@ use rustc_span::symbol::sym; impl LateLintPass<'_> for CheckIteratorTraitLint { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - let implements_iterator = cx.tcx.get_diagnostic_item(sym::Iterator).map_or(false, |id| { - implements_trait(cx, cx.typeck_results().expr_ty(expr), id, &[]) - }); - if implements_iterator { - // [...] - } + let implements_iterator = (cx.tcx.get_diagnostic_item(sym::Iterator)) + .is_some_and(|id| implements_trait(cx, cx.typeck_results().expr_ty(expr), id, &[])); + if implements_iterator { + // [...] + } } } From ace2e274db8165fc5bfeba1faf83e15095723229 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Tue, 23 Sep 2025 23:02:59 +0200 Subject: [PATCH 043/226] Cleanup: group code for `single_char_add_str` lint in one file A lot of code was duplicated instead of being shared. --- clippy_lints/src/methods/mod.rs | 2 - .../src/methods/single_char_add_str.rs | 80 +++++++++++++++++-- .../src/methods/single_char_insert_string.rs | 67 ---------------- .../src/methods/single_char_push_string.rs | 65 --------------- 4 files changed, 73 insertions(+), 141 deletions(-) delete mode 100644 clippy_lints/src/methods/single_char_insert_string.rs delete mode 100644 clippy_lints/src/methods/single_char_push_string.rs diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 8679689c8ad4..3755ec37fb96 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -103,8 +103,6 @@ mod search_is_some; mod seek_from_current; mod seek_to_start_instead_of_rewind; mod single_char_add_str; -mod single_char_insert_string; -mod single_char_push_string; mod skip_while_next; mod sliced_string_as_bytes; mod stable_sort_primitive; diff --git a/clippy_lints/src/methods/single_char_add_str.rs b/clippy_lints/src/methods/single_char_add_str.rs index ef3d7acdc01e..1248d1658d77 100644 --- a/clippy_lints/src/methods/single_char_add_str.rs +++ b/clippy_lints/src/methods/single_char_add_str.rs @@ -1,14 +1,80 @@ -use crate::methods::{single_char_insert_string, single_char_push_string}; -use rustc_hir as hir; +use super::SINGLE_CHAR_ADD_STR; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::{snippet_with_applicability, str_literal_to_char_literal}; +use rustc_ast::BorrowKind; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; +use rustc_middle::ty; use rustc_span::sym; -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) { if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) { - match cx.tcx.get_diagnostic_name(fn_def_id) { - Some(sym::string_push_str) => single_char_push_string::check(cx, expr, receiver, args), - Some(sym::string_insert_str) => single_char_insert_string::check(cx, expr, receiver, args), - _ => {}, + let mut applicability = Applicability::MachineApplicable; + let (short_name, arg, extra) = match cx.tcx.get_diagnostic_name(fn_def_id) { + Some(sym::string_insert_str) => ( + "insert", + &args[1], + Some(|applicability| { + format!( + "{}, ", + snippet_with_applicability(cx, args[0].span, "..", applicability) + ) + }), + ), + Some(sym::string_push_str) => ("push", &args[0], None), + _ => return, + }; + + if let Some(extension_string) = str_literal_to_char_literal(cx, arg, &mut applicability, false) { + let base_string_snippet = + snippet_with_applicability(cx, receiver.span.source_callsite(), "_", &mut applicability); + span_lint_and_sugg( + cx, + SINGLE_CHAR_ADD_STR, + expr.span, + format!("calling `{short_name}_str()` using a single-character string literal"), + format!("consider using `{short_name}` with a character literal"), + format!( + "{base_string_snippet}.{short_name}({}{extension_string})", + extra.map_or(String::new(), |f| f(&mut applicability)) + ), + applicability, + ); + } else if let ExprKind::AddrOf(BorrowKind::Ref, _, inner) = arg.kind + && let ExprKind::MethodCall(path_segment, method_arg, [], _) = inner.kind + && path_segment.ident.name == sym::to_string + && (is_ref_char(cx, method_arg) || is_char(cx, method_arg)) + { + let base_string_snippet = + snippet_with_applicability(cx, receiver.span.source_callsite(), "_", &mut applicability); + let extension_string = match ( + snippet_with_applicability(cx, method_arg.span.source_callsite(), "_", &mut applicability), + is_ref_char(cx, method_arg), + ) { + (snippet, false) => snippet, + (snippet, true) => format!("*{snippet}").into(), + }; + span_lint_and_sugg( + cx, + SINGLE_CHAR_ADD_STR, + expr.span, + format!("calling `{short_name}_str()` using a single-character converted to string"), + format!("consider using `{short_name}` without `to_string()`"), + format!( + "{base_string_snippet}.{short_name}({}{extension_string})", + extra.map_or(String::new(), |f| f(&mut applicability)) + ), + applicability, + ); } } } + +fn is_ref_char(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + matches!(cx.typeck_results().expr_ty(expr).kind(), ty::Ref(_, ty, _) if ty.is_char()) +} + +fn is_char(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + cx.typeck_results().expr_ty(expr).is_char() +} diff --git a/clippy_lints/src/methods/single_char_insert_string.rs b/clippy_lints/src/methods/single_char_insert_string.rs deleted file mode 100644 index 4a1d25deade9..000000000000 --- a/clippy_lints/src/methods/single_char_insert_string.rs +++ /dev/null @@ -1,67 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{snippet_with_applicability, str_literal_to_char_literal}; -use rustc_ast::BorrowKind; -use rustc_errors::Applicability; -use rustc_hir::{self as hir, ExprKind}; -use rustc_lint::LateContext; - -use super::SINGLE_CHAR_ADD_STR; - -/// lint for length-1 `str`s as argument for `insert_str` -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { - let mut applicability = Applicability::MachineApplicable; - if let Some(extension_string) = str_literal_to_char_literal(cx, &args[1], &mut applicability, false) { - let base_string_snippet = - snippet_with_applicability(cx, receiver.span.source_callsite(), "_", &mut applicability); - let pos_arg = snippet_with_applicability(cx, args[0].span, "..", &mut applicability); - let sugg = format!("{base_string_snippet}.insert({pos_arg}, {extension_string})"); - span_lint_and_sugg( - cx, - SINGLE_CHAR_ADD_STR, - expr.span, - "calling `insert_str()` using a single-character string literal", - "consider using `insert` with a character literal", - sugg, - applicability, - ); - } - - if let ExprKind::AddrOf(BorrowKind::Ref, _, arg) = &args[1].kind - && let ExprKind::MethodCall(path_segment, method_arg, [], _) = &arg.kind - && path_segment.ident.name == rustc_span::sym::to_string - && (is_ref_char(cx, method_arg) || is_char(cx, method_arg)) - { - let base_string_snippet = - snippet_with_applicability(cx, receiver.span.source_callsite(), "..", &mut applicability); - let extension_string = - snippet_with_applicability(cx, method_arg.span.source_callsite(), "..", &mut applicability); - let pos_arg = snippet_with_applicability(cx, args[0].span, "..", &mut applicability); - let deref_string = if is_ref_char(cx, method_arg) { "*" } else { "" }; - - let sugg = format!("{base_string_snippet}.insert({pos_arg}, {deref_string}{extension_string})"); - span_lint_and_sugg( - cx, - SINGLE_CHAR_ADD_STR, - expr.span, - "calling `insert_str()` using a single-character converted to string", - "consider using `insert` without `to_string()`", - sugg, - applicability, - ); - } -} - -fn is_ref_char(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { - if cx.typeck_results().expr_ty(expr).is_ref() - && let rustc_middle::ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(expr).kind() - && ty.is_char() - { - return true; - } - - false -} - -fn is_char(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { - cx.typeck_results().expr_ty(expr).is_char() -} diff --git a/clippy_lints/src/methods/single_char_push_string.rs b/clippy_lints/src/methods/single_char_push_string.rs deleted file mode 100644 index bc271d593925..000000000000 --- a/clippy_lints/src/methods/single_char_push_string.rs +++ /dev/null @@ -1,65 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{snippet_with_applicability, str_literal_to_char_literal}; -use rustc_ast::BorrowKind; -use rustc_errors::Applicability; -use rustc_hir::{self as hir, ExprKind}; -use rustc_lint::LateContext; - -use super::SINGLE_CHAR_ADD_STR; - -/// lint for length-1 `str`s as argument for `push_str` -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { - let mut applicability = Applicability::MachineApplicable; - if let Some(extension_string) = str_literal_to_char_literal(cx, &args[0], &mut applicability, false) { - let base_string_snippet = - snippet_with_applicability(cx, receiver.span.source_callsite(), "..", &mut applicability); - let sugg = format!("{base_string_snippet}.push({extension_string})"); - span_lint_and_sugg( - cx, - SINGLE_CHAR_ADD_STR, - expr.span, - "calling `push_str()` using a single-character string literal", - "consider using `push` with a character literal", - sugg, - applicability, - ); - } - - if let ExprKind::AddrOf(BorrowKind::Ref, _, arg) = &args[0].kind - && let ExprKind::MethodCall(path_segment, method_arg, [], _) = &arg.kind - && path_segment.ident.name == rustc_span::sym::to_string - && (is_ref_char(cx, method_arg) || is_char(cx, method_arg)) - { - let base_string_snippet = - snippet_with_applicability(cx, receiver.span.source_callsite(), "..", &mut applicability); - let extension_string = - snippet_with_applicability(cx, method_arg.span.source_callsite(), "..", &mut applicability); - let deref_string = if is_ref_char(cx, method_arg) { "*" } else { "" }; - - let sugg = format!("{base_string_snippet}.push({deref_string}{extension_string})"); - span_lint_and_sugg( - cx, - SINGLE_CHAR_ADD_STR, - expr.span, - "calling `push_str()` using a single-character converted to string", - "consider using `push` without `to_string()`", - sugg, - applicability, - ); - } -} - -fn is_ref_char(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { - if cx.typeck_results().expr_ty(expr).is_ref() - && let rustc_middle::ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(expr).kind() - && ty.is_char() - { - return true; - } - - false -} - -fn is_char(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { - cx.typeck_results().expr_ty(expr).is_char() -} From 5317b7be529eb1274dd255cbb2fab94e74475e12 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 24 Sep 2025 09:15:38 +0200 Subject: [PATCH 044/226] clean-up --- clippy_lints/src/methods/mod.rs | 2 +- clippy_lints/src/methods/or_fun_call.rs | 356 ++++++++++++------------ tests/ui/or_fun_call.fixed | 1 - tests/ui/or_fun_call.rs | 1 - tests/ui/or_fun_call.stderr | 90 +++--- 5 files changed, 223 insertions(+), 227 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 3755ec37fb96..be1fcf136e92 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -4460,7 +4460,7 @@ declare_clippy_lint! { /// Checks for calls to `Read::bytes` on types which don't implement `BufRead`. /// /// ### Why is this bad? - /// The default implementation calls `read` for each byte, which can be very inefficient for data that’s not in memory, such as `File`. + /// The default implementation calls `read` for each byte, which can be very inefficient for data that's not in memory, such as `File`. /// /// ### Example /// ```no_run diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index 71b2f251eded..1433c5c1307b 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -18,7 +18,6 @@ use {rustc_ast as ast, rustc_hir as hir}; use super::{OR_FUN_CALL, UNWRAP_OR_DEFAULT}; /// Checks for the `OR_FUN_CALL` lint. -#[expect(clippy::too_many_lines)] pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, @@ -27,184 +26,6 @@ pub(super) fn check<'tcx>( receiver: &'tcx hir::Expr<'_>, args: &'tcx [hir::Expr<'_>], ) { - /// Checks for `unwrap_or(T::new())`, `unwrap_or(T::default())`, - /// `or_insert(T::new())` or `or_insert(T::default())`. - /// Similarly checks for `unwrap_or_else(T::new)`, `unwrap_or_else(T::default)`, - /// `or_insert_with(T::new)` or `or_insert_with(T::default)`. - fn check_unwrap_or_default( - cx: &LateContext<'_>, - name: Symbol, - receiver: &hir::Expr<'_>, - fun: &hir::Expr<'_>, - call_expr: Option<&hir::Expr<'_>>, - span: Span, - method_span: Span, - ) -> bool { - if !expr_type_is_certain(cx, receiver) { - return false; - } - - let is_new = |fun: &hir::Expr<'_>| { - if let hir::ExprKind::Path(ref qpath) = fun.kind { - let path = last_path_segment(qpath).ident.name; - matches!(path, sym::new) - } else { - false - } - }; - - let output_type_implements_default = |fun| { - let fun_ty = cx.typeck_results().expr_ty(fun); - if let ty::FnDef(def_id, args) = fun_ty.kind() { - let output_ty = cx.tcx.fn_sig(def_id).instantiate(cx.tcx, args).skip_binder().output(); - cx.tcx - .get_diagnostic_item(sym::Default) - .is_some_and(|default_trait_id| implements_trait(cx, output_ty, default_trait_id, &[])) - } else { - false - } - }; - - let sugg = match (name, call_expr.is_some()) { - (sym::unwrap_or, true) | (sym::unwrap_or_else, false) => sym::unwrap_or_default, - (sym::or_insert, true) | (sym::or_insert_with, false) => sym::or_default, - _ => return false, - }; - - let receiver_ty = cx.typeck_results().expr_ty_adjusted(receiver).peel_refs(); - let Some(suggested_method_def_id) = receiver_ty.ty_adt_def().and_then(|adt_def| { - cx.tcx - .inherent_impls(adt_def.did()) - .iter() - .flat_map(|impl_id| cx.tcx.associated_items(impl_id).filter_by_name_unhygienic(sugg)) - .find_map(|assoc| { - if assoc.is_method() && cx.tcx.fn_sig(assoc.def_id).skip_binder().inputs().skip_binder().len() == 1 - { - Some(assoc.def_id) - } else { - None - } - }) - }) else { - return false; - }; - let in_sugg_method_implementation = { - matches!( - suggested_method_def_id.as_local(), - Some(local_def_id) if local_def_id == cx.tcx.hir_get_parent_item(receiver.hir_id).def_id - ) - }; - if in_sugg_method_implementation { - return false; - } - - // `.unwrap_or(vec![])` is as readable as `.unwrap_or_default()`. And if the expression is a - // non-empty `Vec`, then it will not be a default value anyway. Bail out in all cases. - if call_expr.and_then(|call_expr| VecArgs::hir(cx, call_expr)).is_some() { - return false; - } - - // needs to target Default::default in particular or be *::new and have a Default impl - // available - if (is_new(fun) && output_type_implements_default(fun)) - || match call_expr { - Some(call_expr) => is_default_equivalent(cx, call_expr), - None => is_default_equivalent_call(cx, fun, None) || closure_body_returns_empty_to_string(cx, fun), - } - { - span_lint_and_sugg( - cx, - UNWRAP_OR_DEFAULT, - method_span.with_hi(span.hi()), - format!("use of `{name}` to construct default value"), - "try", - format!("{sugg}()"), - Applicability::MachineApplicable, - ); - - true - } else { - false - } - } - - /// Checks for `*or(foo())`. - #[expect(clippy::too_many_arguments)] - fn check_or_fn_call<'tcx>( - cx: &LateContext<'tcx>, - name: Symbol, - method_span: Span, - self_expr: &hir::Expr<'_>, - arg: &'tcx hir::Expr<'_>, - // `Some` if fn has second argument - second_arg: Option<&hir::Expr<'_>>, - span: Span, - // None if lambda is required - fun_span: Option, - ) -> bool { - // (path, fn_has_argument, methods, suffix) - const KNOW_TYPES: [(Symbol, bool, &[Symbol], &str); 7] = [ - (sym::BTreeEntry, false, &[sym::or_insert], "with"), - (sym::HashMapEntry, false, &[sym::or_insert], "with"), - ( - sym::Option, - false, - &[sym::map_or, sym::ok_or, sym::or, sym::unwrap_or], - "else", - ), - (sym::Option, false, &[sym::get_or_insert], "with"), - (sym::Option, true, &[sym::and], "then"), - (sym::Result, true, &[sym::map_or, sym::or, sym::unwrap_or], "else"), - (sym::Result, true, &[sym::and], "then"), - ]; - - if KNOW_TYPES.iter().any(|k| k.2.contains(&name)) - && switch_to_lazy_eval(cx, arg) - && !contains_return(arg) - && let self_ty = cx.typeck_results().expr_ty(self_expr) - && let Some(&(_, fn_has_arguments, _, suffix)) = KNOW_TYPES - .iter() - .find(|&&i| is_type_diagnostic_item(cx, self_ty, i.0) && i.2.contains(&name)) - { - let ctxt = span.ctxt(); - let mut app = Applicability::HasPlaceholders; - let sugg = { - let (snippet_span, use_lambda) = match (fn_has_arguments, fun_span) { - (false, Some(fun_span)) => (fun_span, false), - _ => (arg.span, true), - }; - - let snip = snippet_with_context(cx, snippet_span, ctxt, "..", &mut app).0; - let snip = if use_lambda { - let l_arg = if fn_has_arguments { "_" } else { "" }; - format!("|{l_arg}| {snip}") - } else { - snip.into_owned() - }; - - if let Some(f) = second_arg { - let f = snippet_with_context(cx, f.span, ctxt, "..", &mut app).0; - format!("{snip}, {f}") - } else { - snip - } - }; - let span_replace_word = method_span.with_hi(span.hi()); - span_lint_and_sugg( - cx, - OR_FUN_CALL, - span_replace_word, - format!("function call inside of `{name}`"), - "try", - format!("{name}_{suffix}({sugg})"), - app, - ); - true - } else { - false - } - } - if let [arg] = args { let inner_arg = peel_blocks(arg); for_each_expr(cx, inner_arg, |ex| { @@ -272,6 +93,183 @@ pub(super) fn check<'tcx>( } } +/// Checks for `unwrap_or(T::new())`, `unwrap_or(T::default())`, +/// `or_insert(T::new())` or `or_insert(T::default())`. +/// Similarly checks for `unwrap_or_else(T::new)`, `unwrap_or_else(T::default)`, +/// `or_insert_with(T::new)` or `or_insert_with(T::default)`. +fn check_unwrap_or_default( + cx: &LateContext<'_>, + name: Symbol, + receiver: &hir::Expr<'_>, + fun: &hir::Expr<'_>, + call_expr: Option<&hir::Expr<'_>>, + span: Span, + method_span: Span, +) -> bool { + if !expr_type_is_certain(cx, receiver) { + return false; + } + + let is_new = |fun: &hir::Expr<'_>| { + if let hir::ExprKind::Path(ref qpath) = fun.kind { + let path = last_path_segment(qpath).ident.name; + matches!(path, sym::new) + } else { + false + } + }; + + let output_type_implements_default = |fun| { + let fun_ty = cx.typeck_results().expr_ty(fun); + if let ty::FnDef(def_id, args) = fun_ty.kind() { + let output_ty = cx.tcx.fn_sig(def_id).instantiate(cx.tcx, args).skip_binder().output(); + cx.tcx + .get_diagnostic_item(sym::Default) + .is_some_and(|default_trait_id| implements_trait(cx, output_ty, default_trait_id, &[])) + } else { + false + } + }; + + let sugg = match (name, call_expr.is_some()) { + (sym::unwrap_or, true) | (sym::unwrap_or_else, false) => sym::unwrap_or_default, + (sym::or_insert, true) | (sym::or_insert_with, false) => sym::or_default, + _ => return false, + }; + + let receiver_ty = cx.typeck_results().expr_ty_adjusted(receiver).peel_refs(); + let Some(suggested_method_def_id) = receiver_ty.ty_adt_def().and_then(|adt_def| { + cx.tcx + .inherent_impls(adt_def.did()) + .iter() + .flat_map(|impl_id| cx.tcx.associated_items(impl_id).filter_by_name_unhygienic(sugg)) + .find_map(|assoc| { + if assoc.is_method() && cx.tcx.fn_sig(assoc.def_id).skip_binder().inputs().skip_binder().len() == 1 { + Some(assoc.def_id) + } else { + None + } + }) + }) else { + return false; + }; + let in_sugg_method_implementation = { + matches!( + suggested_method_def_id.as_local(), + Some(local_def_id) if local_def_id == cx.tcx.hir_get_parent_item(receiver.hir_id).def_id + ) + }; + if in_sugg_method_implementation { + return false; + } + + // `.unwrap_or(vec![])` is as readable as `.unwrap_or_default()`. And if the expression is a + // non-empty `Vec`, then it will not be a default value anyway. Bail out in all cases. + if call_expr.and_then(|call_expr| VecArgs::hir(cx, call_expr)).is_some() { + return false; + } + + // needs to target Default::default in particular or be *::new and have a Default impl + // available + if (is_new(fun) && output_type_implements_default(fun)) + || match call_expr { + Some(call_expr) => is_default_equivalent(cx, call_expr), + None => is_default_equivalent_call(cx, fun, None) || closure_body_returns_empty_to_string(cx, fun), + } + { + span_lint_and_sugg( + cx, + UNWRAP_OR_DEFAULT, + method_span.with_hi(span.hi()), + format!("use of `{name}` to construct default value"), + "try", + format!("{sugg}()"), + Applicability::MachineApplicable, + ); + + true + } else { + false + } +} + +/// Checks for `*or(foo())`. +#[expect(clippy::too_many_arguments)] +fn check_or_fn_call<'tcx>( + cx: &LateContext<'tcx>, + name: Symbol, + method_span: Span, + self_expr: &hir::Expr<'_>, + arg: &'tcx hir::Expr<'_>, + // `Some` if fn has second argument + second_arg: Option<&hir::Expr<'_>>, + span: Span, + // None if lambda is required + fun_span: Option, +) -> bool { + // (path, fn_has_argument, methods, suffix) + const KNOW_TYPES: [(Symbol, bool, &[Symbol], &str); 7] = [ + (sym::BTreeEntry, false, &[sym::or_insert], "with"), + (sym::HashMapEntry, false, &[sym::or_insert], "with"), + ( + sym::Option, + false, + &[sym::map_or, sym::ok_or, sym::or, sym::unwrap_or], + "else", + ), + (sym::Option, false, &[sym::get_or_insert], "with"), + (sym::Option, true, &[sym::and], "then"), + (sym::Result, true, &[sym::map_or, sym::or, sym::unwrap_or], "else"), + (sym::Result, true, &[sym::and], "then"), + ]; + + if KNOW_TYPES.iter().any(|k| k.2.contains(&name)) + && switch_to_lazy_eval(cx, arg) + && !contains_return(arg) + && let self_ty = cx.typeck_results().expr_ty(self_expr) + && let Some(&(_, fn_has_arguments, _, suffix)) = KNOW_TYPES + .iter() + .find(|&&i| is_type_diagnostic_item(cx, self_ty, i.0) && i.2.contains(&name)) + { + let ctxt = span.ctxt(); + let mut app = Applicability::HasPlaceholders; + let sugg = { + let (snippet_span, use_lambda) = match (fn_has_arguments, fun_span) { + (false, Some(fun_span)) => (fun_span, false), + _ => (arg.span, true), + }; + + let snip = snippet_with_context(cx, snippet_span, ctxt, "..", &mut app).0; + let snip = if use_lambda { + let l_arg = if fn_has_arguments { "_" } else { "" }; + format!("|{l_arg}| {snip}") + } else { + snip.into_owned() + }; + + if let Some(f) = second_arg { + let f = snippet_with_context(cx, f.span, ctxt, "..", &mut app).0; + format!("{snip}, {f}") + } else { + snip + } + }; + let span_replace_word = method_span.with_hi(span.hi()); + span_lint_and_sugg( + cx, + OR_FUN_CALL, + span_replace_word, + format!("function call inside of `{name}`"), + "try", + format!("{name}_{suffix}({sugg})"), + app, + ); + true + } else { + false + } +} + fn closure_body_returns_empty_to_string(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> bool { if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = e.kind { let body = cx.tcx.hir_body(body); diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 7a0be97017eb..f42e637328a1 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -1,5 +1,4 @@ #![warn(clippy::or_fun_call)] -#![allow(dead_code)] #![allow( clippy::borrow_as_ptr, clippy::uninlined_format_args, diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index 724af606de9c..7d2bd32ca0b3 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -1,5 +1,4 @@ #![warn(clippy::or_fun_call)] -#![allow(dead_code)] #![allow( clippy::borrow_as_ptr, clippy::uninlined_format_args, diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index 40b25f91154d..551ca8f3b801 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -1,5 +1,5 @@ error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:53:22 + --> tests/ui/or_fun_call.rs:52:22 | LL | with_constructor.unwrap_or(make()); | ^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(make)` @@ -8,7 +8,7 @@ LL | with_constructor.unwrap_or(make()); = help: to override `-D warnings` add `#[allow(clippy::or_fun_call)]` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:57:14 + --> tests/ui/or_fun_call.rs:56:14 | LL | with_new.unwrap_or(Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` @@ -17,205 +17,205 @@ LL | with_new.unwrap_or(Vec::new()); = help: to override `-D warnings` add `#[allow(clippy::unwrap_or_default)]` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:61:21 + --> tests/ui/or_fun_call.rs:60:21 | LL | with_const_args.unwrap_or(Vec::with_capacity(12)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| Vec::with_capacity(12))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:65:14 + --> tests/ui/or_fun_call.rs:64:14 | LL | with_err.unwrap_or(make()); | ^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|_| make())` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:69:19 + --> tests/ui/or_fun_call.rs:68:19 | LL | with_err_args.unwrap_or(Vec::with_capacity(12)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|_| Vec::with_capacity(12))` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:73:24 + --> tests/ui/or_fun_call.rs:72:24 | LL | with_default_trait.unwrap_or(Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:77:23 + --> tests/ui/or_fun_call.rs:76:23 | LL | with_default_type.unwrap_or(u64::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:97:18 + --> tests/ui/or_fun_call.rs:96:18 | LL | self_default.unwrap_or(::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(::default)` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:101:18 + --> tests/ui/or_fun_call.rs:100:18 | LL | real_default.unwrap_or(::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:105:14 + --> tests/ui/or_fun_call.rs:104:14 | LL | with_vec.unwrap_or(Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:109:21 + --> tests/ui/or_fun_call.rs:108:21 | LL | without_default.unwrap_or(Foo::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(Foo::new)` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:113:19 + --> tests/ui/or_fun_call.rs:112:19 | LL | map.entry(42).or_insert(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:117:23 + --> tests/ui/or_fun_call.rs:116:23 | LL | map_vec.entry(42).or_insert(Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:121:21 + --> tests/ui/or_fun_call.rs:120:21 | LL | btree.entry(42).or_insert(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:125:25 + --> tests/ui/or_fun_call.rs:124:25 | LL | btree_vec.entry(42).or_insert(Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:129:21 + --> tests/ui/or_fun_call.rs:128:21 | LL | let _ = stringy.unwrap_or(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `ok_or` - --> tests/ui/or_fun_call.rs:134:17 + --> tests/ui/or_fun_call.rs:133:17 | LL | let _ = opt.ok_or(format!("{} world.", hello)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ok_or_else(|| format!("{} world.", hello))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:139:21 + --> tests/ui/or_fun_call.rs:138:21 | LL | let _ = Some(1).unwrap_or(map[&1]); | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| map[&1])` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:142:21 + --> tests/ui/or_fun_call.rs:141:21 | LL | let _ = Some(1).unwrap_or(map[&1]); | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| map[&1])` error: function call inside of `or` - --> tests/ui/or_fun_call.rs:167:35 + --> tests/ui/or_fun_call.rs:166:35 | LL | let _ = Some("a".to_string()).or(Some("b".to_string())); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_else(|| Some("b".to_string()))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:210:18 + --> tests/ui/or_fun_call.rs:209:18 | LL | None.unwrap_or(ptr_to_ref(s)); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| ptr_to_ref(s))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:218:14 + --> tests/ui/or_fun_call.rs:217:14 | LL | None.unwrap_or(unsafe { ptr_to_ref(s) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:221:14 + --> tests/ui/or_fun_call.rs:220:14 | LL | None.unwrap_or( unsafe { ptr_to_ref(s) } ); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:297:25 + --> tests/ui/or_fun_call.rs:296:25 | LL | let _ = Some(4).map_or(g(), |v| v); | ^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(g, |v| v)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:299:25 + --> tests/ui/or_fun_call.rs:298:25 | LL | let _ = Some(4).map_or(g(), f); | ^^^^^^^^^^^^^^ help: try: `map_or_else(g, f)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:302:25 + --> tests/ui/or_fun_call.rs:301:25 | LL | let _ = Some(4).map_or("asd".to_string().len() as i32, f); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|| "asd".to_string().len() as i32, f)` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:333:18 + --> tests/ui/or_fun_call.rs:332:18 | LL | with_new.unwrap_or_else(Vec::new); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:337:28 + --> tests/ui/or_fun_call.rs:336:28 | LL | with_default_trait.unwrap_or_else(Default::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:341:27 + --> tests/ui/or_fun_call.rs:340:27 | LL | with_default_type.unwrap_or_else(u64::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:345:22 + --> tests/ui/or_fun_call.rs:344:22 | LL | real_default.unwrap_or_else(::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `or_insert_with` to construct default value - --> tests/ui/or_fun_call.rs:349:23 + --> tests/ui/or_fun_call.rs:348:23 | LL | map.entry(42).or_insert_with(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert_with` to construct default value - --> tests/ui/or_fun_call.rs:353:25 + --> tests/ui/or_fun_call.rs:352:25 | LL | btree.entry(42).or_insert_with(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:357:25 + --> tests/ui/or_fun_call.rs:356:25 | LL | let _ = stringy.unwrap_or_else(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:399:17 + --> tests/ui/or_fun_call.rs:398:17 | LL | let _ = opt.unwrap_or({ f() }); // suggest `.unwrap_or_else(f)` | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(f)` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:404:17 + --> tests/ui/or_fun_call.rs:403:17 | LL | let _ = opt.unwrap_or(f() + 1); // suggest `.unwrap_or_else(|| f() + 1)` | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| f() + 1)` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:409:17 + --> tests/ui/or_fun_call.rs:408:17 | LL | let _ = opt.unwrap_or({ | _________________^ @@ -235,55 +235,55 @@ LL ~ }); | error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:415:17 + --> tests/ui/or_fun_call.rs:414:17 | LL | let _ = opt.map_or(f() + 1, |v| v); // suggest `.map_or_else(|| f() + 1, |v| v)` | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|| f() + 1, |v| v)` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:420:17 + --> tests/ui/or_fun_call.rs:419:17 | LL | let _ = opt.unwrap_or({ i32::default() }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:427:21 + --> tests/ui/or_fun_call.rs:426:21 | LL | let _ = opt_foo.unwrap_or(Foo { val: String::default() }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| Foo { val: String::default() })` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:442:19 + --> tests/ui/or_fun_call.rs:441:19 | LL | let _ = x.map_or(g(), |v| v); | ^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|_| g(), |v| v)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:444:19 + --> tests/ui/or_fun_call.rs:443:19 | LL | let _ = x.map_or(g(), f); | ^^^^^^^^^^^^^^ help: try: `map_or_else(|_| g(), f)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:447:19 + --> tests/ui/or_fun_call.rs:446:19 | LL | let _ = x.map_or("asd".to_string().len() as i32, f); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|_| "asd".to_string().len() as i32, f)` error: function call inside of `get_or_insert` - --> tests/ui/or_fun_call.rs:458:15 + --> tests/ui/or_fun_call.rs:457:15 | LL | let _ = x.get_or_insert(g()); | ^^^^^^^^^^^^^^^^^^ help: try: `get_or_insert_with(g)` error: function call inside of `and` - --> tests/ui/or_fun_call.rs:468:15 + --> tests/ui/or_fun_call.rs:467:15 | LL | let _ = x.and(g()); | ^^^^^^^^ help: try: `and_then(|_| g())` error: function call inside of `and` - --> tests/ui/or_fun_call.rs:478:15 + --> tests/ui/or_fun_call.rs:477:15 | LL | let _ = x.and(g()); | ^^^^^^^^ help: try: `and_then(|_| g())` From 7449d63ab7dc12de7e311f1bf56de51051bb68dc Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 24 Sep 2025 09:40:45 +0200 Subject: [PATCH 045/226] fix(or_fun_call): respect MSRV for `Result::unwrap_or_default` suggestion --- book/src/lint_configuration.md | 1 + clippy_config/src/conf.rs | 1 + clippy_lints/src/methods/mod.rs | 2 +- clippy_lints/src/methods/or_fun_call.rs | 16 ++++++++++++--- clippy_utils/src/msrvs.rs | 2 +- tests/ui/or_fun_call.fixed | 15 ++++++++++++++ tests/ui/or_fun_call.rs | 15 ++++++++++++++ tests/ui/or_fun_call.stderr | 26 ++++++++++++++++++++++++- 8 files changed, 72 insertions(+), 6 deletions(-) diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index c2d080cd96a1..f8263bb8852a 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -883,6 +883,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`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) +* [`or_fun_call`](https://rust-lang.github.io/rust-clippy/master/index.html#or_fun_call) * [`ptr_as_ptr`](https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr) * [`question_mark`](https://rust-lang.github.io/rust-clippy/master/index.html#question_mark) * [`redundant_field_names`](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names) diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 2f28f6175ad8..e14e15a022f7 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -779,6 +779,7 @@ define_Conf! { needless_borrow, non_std_lazy_statics, option_as_ref_deref, + or_fun_call, ptr_as_ptr, question_mark, redundant_field_names, diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index be1fcf136e92..c18f20c17e2e 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -4854,7 +4854,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { }, ExprKind::MethodCall(method_call, receiver, args, _) => { let method_span = method_call.ident.span; - or_fun_call::check(cx, expr, method_span, method_call.ident.name, receiver, args); + or_fun_call::check(cx, expr, method_span, method_call.ident.name, receiver, args, self.msrv); expect_fun_call::check( cx, &self.format_args, diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index 1433c5c1307b..04e4503e4097 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -3,6 +3,7 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::eager_or_lazy::switch_to_lazy_eval; use clippy_utils::higher::VecArgs; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{expr_type_is_certain, implements_trait, is_type_diagnostic_item}; use clippy_utils::visitors::for_each_expr; @@ -25,6 +26,7 @@ pub(super) fn check<'tcx>( name: Symbol, receiver: &'tcx hir::Expr<'_>, args: &'tcx [hir::Expr<'_>], + msrv: Msrv, ) { if let [arg] = args { let inner_arg = peel_blocks(arg); @@ -45,11 +47,11 @@ pub(super) fn check<'tcx>( }; (!inner_fun_has_args && !is_nested_expr - && check_unwrap_or_default(cx, name, receiver, fun, Some(ex), expr.span, method_span)) + && check_unwrap_or_default(cx, name, receiver, fun, Some(ex), expr.span, method_span, msrv)) || check_or_fn_call(cx, name, method_span, receiver, arg, None, expr.span, fun_span) }, hir::ExprKind::Path(..) | hir::ExprKind::Closure(..) if !is_nested_expr => { - check_unwrap_or_default(cx, name, receiver, ex, None, expr.span, method_span) + check_unwrap_or_default(cx, name, receiver, ex, None, expr.span, method_span, msrv) }, hir::ExprKind::Index(..) | hir::ExprKind::MethodCall(..) => { check_or_fn_call(cx, name, method_span, receiver, arg, None, expr.span, None) @@ -97,6 +99,7 @@ pub(super) fn check<'tcx>( /// `or_insert(T::new())` or `or_insert(T::default())`. /// Similarly checks for `unwrap_or_else(T::new)`, `unwrap_or_else(T::default)`, /// `or_insert_with(T::new)` or `or_insert_with(T::default)`. +#[expect(clippy::too_many_arguments)] fn check_unwrap_or_default( cx: &LateContext<'_>, name: Symbol, @@ -105,7 +108,15 @@ fn check_unwrap_or_default( call_expr: Option<&hir::Expr<'_>>, span: Span, method_span: Span, + msrv: Msrv, ) -> bool { + let receiver_ty = cx.typeck_results().expr_ty_adjusted(receiver).peel_refs(); + + // Check MSRV, but only for `Result::unwrap_or_default` + if is_type_diagnostic_item(cx, receiver_ty, sym::Result) && !msrv.meets(cx, msrvs::RESULT_UNWRAP_OR_DEFAULT) { + return false; + } + if !expr_type_is_certain(cx, receiver) { return false; } @@ -137,7 +148,6 @@ fn check_unwrap_or_default( _ => return false, }; - let receiver_ty = cx.typeck_results().expr_ty_adjusted(receiver).peel_refs(); let Some(suggested_method_def_id) = receiver_ty.ty_adt_def().and_then(|adt_def| { cx.tcx .inherent_impls(adt_def.did()) diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 6e07ed9ffcc4..3cccce397544 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -77,7 +77,7 @@ msrv_aliases! { 1,24,0 { IS_ASCII_DIGIT, PTR_NULL } 1,18,0 { HASH_MAP_RETAIN, HASH_SET_RETAIN } 1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR } - 1,16,0 { STR_REPEAT } + 1,16,0 { STR_REPEAT, RESULT_UNWRAP_OR_DEFAULT } 1,15,0 { MAYBE_BOUND_IN_WHERE } 1,13,0 { QUESTION_MARK_OPERATOR } } diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index f42e637328a1..386351aa39f5 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -478,4 +478,19 @@ fn test_result_and() { //~^ or_fun_call } +#[clippy::msrv = "1.15"] +fn below_msrv(opt: Option, res: Result) { + let _ = opt.unwrap_or_default(); + //~^ unwrap_or_default + let _ = res.unwrap_or_else(|_| Default::default()); + //~^ or_fun_call +} + +#[clippy::msrv = "1.16"] +fn above_msrv(opt: Option, res: Result) { + let _ = opt.unwrap_or_default(); + //~^ unwrap_or_default + let _ = res.unwrap_or_default(); + //~^ unwrap_or_default +} fn main() {} diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index 7d2bd32ca0b3..e27f9aa65c37 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -478,4 +478,19 @@ fn test_result_and() { //~^ or_fun_call } +#[clippy::msrv = "1.15"] +fn below_msrv(opt: Option, res: Result) { + let _ = opt.unwrap_or(Default::default()); + //~^ unwrap_or_default + let _ = res.unwrap_or(Default::default()); + //~^ or_fun_call +} + +#[clippy::msrv = "1.16"] +fn above_msrv(opt: Option, res: Result) { + let _ = opt.unwrap_or(Default::default()); + //~^ unwrap_or_default + let _ = res.unwrap_or(Default::default()); + //~^ unwrap_or_default +} fn main() {} diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index 551ca8f3b801..6bce06ab20eb 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -288,5 +288,29 @@ error: function call inside of `and` LL | let _ = x.and(g()); | ^^^^^^^^ help: try: `and_then(|_| g())` -error: aborting due to 45 previous errors +error: use of `unwrap_or` to construct default value + --> tests/ui/or_fun_call.rs:483:17 + | +LL | let _ = opt.unwrap_or(Default::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` + +error: function call inside of `unwrap_or` + --> tests/ui/or_fun_call.rs:485:17 + | +LL | let _ = res.unwrap_or(Default::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|_| Default::default())` + +error: use of `unwrap_or` to construct default value + --> tests/ui/or_fun_call.rs:491:17 + | +LL | let _ = opt.unwrap_or(Default::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` + +error: use of `unwrap_or` to construct default value + --> tests/ui/or_fun_call.rs:493:17 + | +LL | let _ = res.unwrap_or(Default::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` + +error: aborting due to 49 previous errors From 9cb82d7ac849adf1bffb2d7e30f114db8d9a9208 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 21 Sep 2025 23:22:31 +0200 Subject: [PATCH 046/226] fix(unnecessary_mut_passed): retain parens around the arguments --- clippy_lints/src/unnecessary_mut_passed.rs | 33 +++- tests/ui/unnecessary_mut_passed.fixed | 20 +++ tests/ui/unnecessary_mut_passed.rs | 20 +++ tests/ui/unnecessary_mut_passed.stderr | 193 ++++++++++++++++++--- 4 files changed, 233 insertions(+), 33 deletions(-) diff --git a/clippy_lints/src/unnecessary_mut_passed.rs b/clippy_lints/src/unnecessary_mut_passed.rs index ec93ef97cfaf..eb2d7639e91f 100644 --- a/clippy_lints/src/unnecessary_mut_passed.rs +++ b/clippy_lints/src/unnecessary_mut_passed.rs @@ -1,5 +1,5 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::sugg::Sugg; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::SpanRangeExt; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability}; use rustc_lint::{LateContext, LateLintPass}; @@ -87,16 +87,33 @@ fn check_arguments<'tcx>( if let ty::Ref(_, _, Mutability::Not) | ty::RawPtr(_, Mutability::Not) = parameter.kind() && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, arg) = argument.kind { - let mut applicability = Applicability::MachineApplicable; - let sugg = Sugg::hir_with_applicability(cx, arg, "_", &mut applicability).addr(); - span_lint_and_sugg( + let applicability = Applicability::MachineApplicable; + + let span_to_remove = { + let span_until_arg = argument.span.until(arg.span); + if let Some(Some(ref_pos)) = span_until_arg.with_source_text(cx, |src| { + src + // we don't use `strip_prefix` here, because `argument` might be enclosed in parens, in + // which case `&` is no longer the prefix + .find('&') + // just a sanity check, in case some proc-macro messes up the spans + .filter(|ref_pos| src[*ref_pos..].contains("mut")) + }) && let Ok(lo) = u32::try_from(ref_pos + '&'.len_utf8()) + { + span_until_arg.split_at(lo).1 + } else { + return; + } + }; + + span_lint_and_then( cx, UNNECESSARY_MUT_PASSED, argument.span, format!("the {fn_kind} `{name}` doesn't need a mutable reference"), - "remove this `mut`", - sugg.to_string(), - applicability, + |diag| { + diag.span_suggestion_verbose(span_to_remove, "remove this `mut`", String::new(), applicability); + }, ); } } diff --git a/tests/ui/unnecessary_mut_passed.fixed b/tests/ui/unnecessary_mut_passed.fixed index 03d854099e64..63bbadb01dcb 100644 --- a/tests/ui/unnecessary_mut_passed.fixed +++ b/tests/ui/unnecessary_mut_passed.fixed @@ -40,6 +40,7 @@ mod issue11268 { struct MyStruct; impl MyStruct { + fn takes_nothing(&self) {} fn takes_ref(&self, a: &i32) {} fn takes_refmut(&self, a: &mut i32) {} fn takes_ref_ref(&self, a: &&i32) {} @@ -168,3 +169,22 @@ fn raw_ptrs(my_struct: MyStruct) { my_struct.takes_raw_mut(&raw mut n); my_struct.takes_raw_const(a); } + +#[expect(clippy::needless_borrow)] +fn issue15722(mut my_struct: MyStruct) { + (&my_struct).takes_nothing(); + //~^ unnecessary_mut_passed + (&my_struct).takes_nothing(); + + // Only put parens around the argument that used to have it + (&my_struct).takes_ref(&42); + //~^ unnecessary_mut_passed + //~| unnecessary_mut_passed + #[expect(clippy::double_parens)] + (&my_struct).takes_ref((&42)); + //~^ unnecessary_mut_passed + //~| unnecessary_mut_passed + #[expect(clippy::double_parens)] + my_struct.takes_ref((&42)); + //~^ unnecessary_mut_passed +} diff --git a/tests/ui/unnecessary_mut_passed.rs b/tests/ui/unnecessary_mut_passed.rs index 80e3f5069277..b719ca1871b2 100644 --- a/tests/ui/unnecessary_mut_passed.rs +++ b/tests/ui/unnecessary_mut_passed.rs @@ -40,6 +40,7 @@ mod issue11268 { struct MyStruct; impl MyStruct { + fn takes_nothing(&self) {} fn takes_ref(&self, a: &i32) {} fn takes_refmut(&self, a: &mut i32) {} fn takes_ref_ref(&self, a: &&i32) {} @@ -168,3 +169,22 @@ fn raw_ptrs(my_struct: MyStruct) { my_struct.takes_raw_mut(&raw mut n); my_struct.takes_raw_const(a); } + +#[expect(clippy::needless_borrow)] +fn issue15722(mut my_struct: MyStruct) { + (&mut my_struct).takes_nothing(); + //~^ unnecessary_mut_passed + (&my_struct).takes_nothing(); + + // Only put parens around the argument that used to have it + (&mut my_struct).takes_ref(&mut 42); + //~^ unnecessary_mut_passed + //~| unnecessary_mut_passed + #[expect(clippy::double_parens)] + (&mut my_struct).takes_ref((&mut 42)); + //~^ unnecessary_mut_passed + //~| unnecessary_mut_passed + #[expect(clippy::double_parens)] + my_struct.takes_ref((&mut 42)); + //~^ unnecessary_mut_passed +} diff --git a/tests/ui/unnecessary_mut_passed.stderr b/tests/ui/unnecessary_mut_passed.stderr index c69a637bf408..ace11027e3e2 100644 --- a/tests/ui/unnecessary_mut_passed.stderr +++ b/tests/ui/unnecessary_mut_passed.stderr @@ -1,77 +1,220 @@ error: the function `takes_ref` doesn't need a mutable reference - --> tests/ui/unnecessary_mut_passed.rs:56:15 + --> tests/ui/unnecessary_mut_passed.rs:57:15 | LL | takes_ref(&mut 42); - | ^^^^^^^ help: remove this `mut`: `&42` + | ^^^^^^^ | = note: `-D clippy::unnecessary-mut-passed` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_mut_passed)]` +help: remove this `mut` + | +LL - takes_ref(&mut 42); +LL + takes_ref(&42); + | error: the function `takes_ref_ref` doesn't need a mutable reference - --> tests/ui/unnecessary_mut_passed.rs:58:19 + --> tests/ui/unnecessary_mut_passed.rs:59:19 | LL | takes_ref_ref(&mut &42); - | ^^^^^^^^ help: remove this `mut`: `&&42` + | ^^^^^^^^ + | +help: remove this `mut` + | +LL - takes_ref_ref(&mut &42); +LL + takes_ref_ref(&&42); + | error: the function `takes_ref_refmut` doesn't need a mutable reference - --> tests/ui/unnecessary_mut_passed.rs:60:22 + --> tests/ui/unnecessary_mut_passed.rs:61:22 | LL | takes_ref_refmut(&mut &mut 42); - | ^^^^^^^^^^^^ help: remove this `mut`: `&&mut 42` + | ^^^^^^^^^^^^ + | +help: remove this `mut` + | +LL - takes_ref_refmut(&mut &mut 42); +LL + takes_ref_refmut(&&mut 42); + | error: the function `takes_raw_const` doesn't need a mutable reference - --> tests/ui/unnecessary_mut_passed.rs:62:21 + --> tests/ui/unnecessary_mut_passed.rs:63:21 | LL | takes_raw_const(&mut 42); - | ^^^^^^^ help: remove this `mut`: `&42` + | ^^^^^^^ + | +help: remove this `mut` + | +LL - takes_raw_const(&mut 42); +LL + takes_raw_const(&42); + | error: the function `as_ptr` doesn't need a mutable reference - --> tests/ui/unnecessary_mut_passed.rs:66:12 + --> tests/ui/unnecessary_mut_passed.rs:67:12 | LL | as_ptr(&mut 42); - | ^^^^^^^ help: remove this `mut`: `&42` + | ^^^^^^^ + | +help: remove this `mut` + | +LL - as_ptr(&mut 42); +LL + as_ptr(&42); + | error: the function `as_ptr` doesn't need a mutable reference - --> tests/ui/unnecessary_mut_passed.rs:69:12 + --> tests/ui/unnecessary_mut_passed.rs:70:12 | LL | as_ptr(&mut &42); - | ^^^^^^^^ help: remove this `mut`: `&&42` + | ^^^^^^^^ + | +help: remove this `mut` + | +LL - as_ptr(&mut &42); +LL + as_ptr(&&42); + | error: the function `as_ptr` doesn't need a mutable reference - --> tests/ui/unnecessary_mut_passed.rs:72:12 + --> tests/ui/unnecessary_mut_passed.rs:73:12 | LL | as_ptr(&mut &mut 42); - | ^^^^^^^^^^^^ help: remove this `mut`: `&&mut 42` + | ^^^^^^^^^^^^ + | +help: remove this `mut` + | +LL - as_ptr(&mut &mut 42); +LL + as_ptr(&&mut 42); + | error: the function `as_ptr` doesn't need a mutable reference - --> tests/ui/unnecessary_mut_passed.rs:75:12 + --> tests/ui/unnecessary_mut_passed.rs:76:12 | LL | as_ptr(&mut 42); - | ^^^^^^^ help: remove this `mut`: `&42` + | ^^^^^^^ + | +help: remove this `mut` + | +LL - as_ptr(&mut 42); +LL + as_ptr(&42); + | error: the method `takes_ref` doesn't need a mutable reference - --> tests/ui/unnecessary_mut_passed.rs:80:25 + --> tests/ui/unnecessary_mut_passed.rs:81:25 | LL | my_struct.takes_ref(&mut 42); - | ^^^^^^^ help: remove this `mut`: `&42` + | ^^^^^^^ + | +help: remove this `mut` + | +LL - my_struct.takes_ref(&mut 42); +LL + my_struct.takes_ref(&42); + | error: the method `takes_ref_ref` doesn't need a mutable reference - --> tests/ui/unnecessary_mut_passed.rs:82:29 + --> tests/ui/unnecessary_mut_passed.rs:83:29 | LL | my_struct.takes_ref_ref(&mut &42); - | ^^^^^^^^ help: remove this `mut`: `&&42` + | ^^^^^^^^ + | +help: remove this `mut` + | +LL - my_struct.takes_ref_ref(&mut &42); +LL + my_struct.takes_ref_ref(&&42); + | error: the method `takes_ref_refmut` doesn't need a mutable reference - --> tests/ui/unnecessary_mut_passed.rs:84:32 + --> tests/ui/unnecessary_mut_passed.rs:85:32 | LL | my_struct.takes_ref_refmut(&mut &mut 42); - | ^^^^^^^^^^^^ help: remove this `mut`: `&&mut 42` + | ^^^^^^^^^^^^ + | +help: remove this `mut` + | +LL - my_struct.takes_ref_refmut(&mut &mut 42); +LL + my_struct.takes_ref_refmut(&&mut 42); + | error: the method `takes_raw_const` doesn't need a mutable reference - --> tests/ui/unnecessary_mut_passed.rs:86:31 + --> tests/ui/unnecessary_mut_passed.rs:87:31 | LL | my_struct.takes_raw_const(&mut 42); - | ^^^^^^^ help: remove this `mut`: `&42` + | ^^^^^^^ + | +help: remove this `mut` + | +LL - my_struct.takes_raw_const(&mut 42); +LL + my_struct.takes_raw_const(&42); + | -error: aborting due to 12 previous errors +error: the method `takes_nothing` doesn't need a mutable reference + --> tests/ui/unnecessary_mut_passed.rs:175:5 + | +LL | (&mut my_struct).takes_nothing(); + | ^^^^^^^^^^^^^^^^ + | +help: remove this `mut` + | +LL - (&mut my_struct).takes_nothing(); +LL + (&my_struct).takes_nothing(); + | + +error: the method `takes_ref` doesn't need a mutable reference + --> tests/ui/unnecessary_mut_passed.rs:180:5 + | +LL | (&mut my_struct).takes_ref(&mut 42); + | ^^^^^^^^^^^^^^^^ + | +help: remove this `mut` + | +LL - (&mut my_struct).takes_ref(&mut 42); +LL + (&my_struct).takes_ref(&mut 42); + | + +error: the method `takes_ref` doesn't need a mutable reference + --> tests/ui/unnecessary_mut_passed.rs:180:32 + | +LL | (&mut my_struct).takes_ref(&mut 42); + | ^^^^^^^ + | +help: remove this `mut` + | +LL - (&mut my_struct).takes_ref(&mut 42); +LL + (&mut my_struct).takes_ref(&42); + | + +error: the method `takes_ref` doesn't need a mutable reference + --> tests/ui/unnecessary_mut_passed.rs:184:5 + | +LL | (&mut my_struct).takes_ref((&mut 42)); + | ^^^^^^^^^^^^^^^^ + | +help: remove this `mut` + | +LL - (&mut my_struct).takes_ref((&mut 42)); +LL + (&my_struct).takes_ref((&mut 42)); + | + +error: the method `takes_ref` doesn't need a mutable reference + --> tests/ui/unnecessary_mut_passed.rs:184:32 + | +LL | (&mut my_struct).takes_ref((&mut 42)); + | ^^^^^^^^^ + | +help: remove this `mut` + | +LL - (&mut my_struct).takes_ref((&mut 42)); +LL + (&mut my_struct).takes_ref((&42)); + | + +error: the method `takes_ref` doesn't need a mutable reference + --> tests/ui/unnecessary_mut_passed.rs:188:25 + | +LL | my_struct.takes_ref((&mut 42)); + | ^^^^^^^^^ + | +help: remove this `mut` + | +LL - my_struct.takes_ref((&mut 42)); +LL + my_struct.takes_ref((&42)); + | + +error: aborting due to 18 previous errors From d63a7ccc7cd9eab72a0ddca84625589d6ba0aec8 Mon Sep 17 00:00:00 2001 From: alexey semenyuk Date: Wed, 24 Sep 2025 23:41:07 +0500 Subject: [PATCH 047/226] Update actions/github-script to v8 --- .github/workflows/lintcheck_summary.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lintcheck_summary.yml b/.github/workflows/lintcheck_summary.yml index 183258a96437..6768cd65701a 100644 --- a/.github/workflows/lintcheck_summary.yml +++ b/.github/workflows/lintcheck_summary.yml @@ -35,7 +35,7 @@ jobs: github-token: ${{ github.token }} - name: Format comment - uses: actions/github-script@v7 + uses: actions/github-script@v8 with: script: | const fs = require("fs"); From d834935b57bcd4604727e8a1fa93e8785979eb5c Mon Sep 17 00:00:00 2001 From: DimitriiTrater Date: Thu, 11 Sep 2025 19:17:05 +0200 Subject: [PATCH 048/226] Fix atan2 inaccuracy in documentation --- library/std/src/num/f128.rs | 10 ++++++---- library/std/src/num/f16.rs | 10 ++++++---- library/std/src/num/f32.rs | 10 ++++++---- library/std/src/num/f64.rs | 10 ++++++---- 4 files changed, 24 insertions(+), 16 deletions(-) diff --git a/library/std/src/num/f128.rs b/library/std/src/num/f128.rs index b83692390b6b..5a85c1daaf0a 100644 --- a/library/std/src/num/f128.rs +++ b/library/std/src/num/f128.rs @@ -557,10 +557,12 @@ impl f128 { /// Computes the four quadrant arctangent of `self` (`y`) and `other` (`x`) in radians. /// - /// * `x = 0`, `y = 0`: `0` - /// * `x >= 0`: `arctan(y/x)` -> `[-pi/2, pi/2]` - /// * `y >= 0`: `arctan(y/x) + pi` -> `(pi/2, pi]` - /// * `y < 0`: `arctan(y/x) - pi` -> `(-pi, -pi/2)` + /// | `x` | `y` | Piecewise Definition | Range | + /// |---------|---------|----------------------|---------------| + /// | `>= +0` | `>= +0` | `arctan(y/x)` | `[+0, +pi/2]` | + /// | `>= +0` | `<= -0` | `arctan(y/x)` | `[-pi/2, -0]` | + /// | `<= -0` | `>= +0` | `arctan(y/x) + pi` | `[+pi/2, +pi]`| + /// | `<= -0` | `<= -0` | `arctan(y/x) - pi` | `[-pi, -pi/2]`| /// /// # Unspecified precision /// diff --git a/library/std/src/num/f16.rs b/library/std/src/num/f16.rs index 5599528717cb..cc10c41b9e7d 100644 --- a/library/std/src/num/f16.rs +++ b/library/std/src/num/f16.rs @@ -522,10 +522,12 @@ impl f16 { /// Computes the four quadrant arctangent of `self` (`y`) and `other` (`x`) in radians. /// - /// * `x = 0`, `y = 0`: `0` - /// * `x >= 0`: `arctan(y/x)` -> `[-pi/2, pi/2]` - /// * `y >= 0`: `arctan(y/x) + pi` -> `(pi/2, pi]` - /// * `y < 0`: `arctan(y/x) - pi` -> `(-pi, -pi/2)` + /// | `x` | `y` | Piecewise Definition | Range | + /// |---------|---------|----------------------|---------------| + /// | `>= +0` | `>= +0` | `arctan(y/x)` | `[+0, +pi/2]` | + /// | `>= +0` | `<= -0` | `arctan(y/x)` | `[-pi/2, -0]` | + /// | `<= -0` | `>= +0` | `arctan(y/x) + pi` | `[+pi/2, +pi]`| + /// | `<= -0` | `<= -0` | `arctan(y/x) - pi` | `[-pi, -pi/2]`| /// /// # Unspecified precision /// diff --git a/library/std/src/num/f32.rs b/library/std/src/num/f32.rs index 0247080a8d6b..72e5f4d4c410 100644 --- a/library/std/src/num/f32.rs +++ b/library/std/src/num/f32.rs @@ -826,10 +826,12 @@ impl f32 { /// Computes the four quadrant arctangent of `self` (`y`) and `other` (`x`) in radians. /// - /// * `x = 0`, `y = 0`: `0` - /// * `x >= 0`: `arctan(y/x)` -> `[-pi/2, pi/2]` - /// * `y >= 0`: `arctan(y/x) + pi` -> `(pi/2, pi]` - /// * `y < 0`: `arctan(y/x) - pi` -> `(-pi, -pi/2)` + /// | `x` | `y` | Piecewise Definition | Range | + /// |---------|---------|----------------------|---------------| + /// | `>= +0` | `>= +0` | `arctan(y/x)` | `[+0, +pi/2]` | + /// | `>= +0` | `<= -0` | `arctan(y/x)` | `[-pi/2, -0]` | + /// | `<= -0` | `>= +0` | `arctan(y/x) + pi` | `[+pi/2, +pi]`| + /// | `<= -0` | `<= -0` | `arctan(y/x) - pi` | `[-pi, -pi/2]`| /// /// # Unspecified precision /// diff --git a/library/std/src/num/f64.rs b/library/std/src/num/f64.rs index 1cfd3909d967..5f3e793c3a71 100644 --- a/library/std/src/num/f64.rs +++ b/library/std/src/num/f64.rs @@ -826,10 +826,12 @@ impl f64 { /// Computes the four quadrant arctangent of `self` (`y`) and `other` (`x`) in radians. /// - /// * `x = 0`, `y = 0`: `0` - /// * `x >= 0`: `arctan(y/x)` -> `[-pi/2, pi/2]` - /// * `y >= 0`: `arctan(y/x) + pi` -> `(pi/2, pi]` - /// * `y < 0`: `arctan(y/x) - pi` -> `(-pi, -pi/2)` + /// | `x` | `y` | Piecewise Definition | Range | + /// |---------|---------|----------------------|---------------| + /// | `>= +0` | `>= +0` | `arctan(y/x)` | `[+0, +pi/2]` | + /// | `>= +0` | `<= -0` | `arctan(y/x)` | `[-pi/2, -0]` | + /// | `<= -0` | `>= +0` | `arctan(y/x) + pi` | `[+pi/2, +pi]`| + /// | `<= -0` | `<= -0` | `arctan(y/x) - pi` | `[-pi, -pi/2]`| /// /// # Unspecified precision /// From 56194654c6b9f2ba5eb077c0e535969f34c12e21 Mon Sep 17 00:00:00 2001 From: Zihan Date: Tue, 23 Sep 2025 17:02:09 -0400 Subject: [PATCH 049/226] `filter_next`: check for `filter().next_back()` changelog: [`filter_next`]: suggest replacing `filter().next_back()` with `rfind()` for `DoubleEndedIterator` Signed-off-by: Zihan --- clippy_lints/src/methods/filter_next.rs | 91 +++++++++++++++---------- clippy_lints/src/methods/mod.rs | 12 +++- clippy_utils/src/msrvs.rs | 2 +- tests/ui/auxiliary/option_helpers.rs | 4 ++ tests/ui/methods.rs | 20 ++++++ tests/ui/methods.stderr | 13 +++- tests/ui/methods_fixable.fixed | 14 ++++ tests/ui/methods_fixable.rs | 14 ++++ tests/ui/methods_fixable.stderr | 14 +++- 9 files changed, 143 insertions(+), 41 deletions(-) diff --git a/clippy_lints/src/methods/filter_next.rs b/clippy_lints/src/methods/filter_next.rs index 72f83b245a0c..e1a9a79e20ee 100644 --- a/clippy_lints/src/methods/filter_next.rs +++ b/clippy_lints/src/methods/filter_next.rs @@ -10,51 +10,68 @@ use rustc_span::sym; use super::FILTER_NEXT; -/// lint use of `filter().next()` for `Iterators` +#[derive(Copy, Clone)] +pub(super) enum Direction { + Forward, + Backward, +} + +/// lint use of `filter().next()` for `Iterator` and `filter().next_back()` for +/// `DoubleEndedIterator` pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, recv: &'tcx hir::Expr<'_>, filter_arg: &'tcx hir::Expr<'_>, + direction: Direction, ) { - // lint if caller of `.filter().next()` is an Iterator - let recv_impls_iterator = cx + // lint if caller of `.filter().next()` is an Iterator or `.filter().next_back()` is a + // DoubleEndedIterator + let (required_trait, next_method, find_method) = match direction { + Direction::Forward => (sym::Iterator, "next", "find"), + Direction::Backward => (sym::DoubleEndedIterator, "next_back", "rfind"), + }; + if !cx .tcx - .get_diagnostic_item(sym::Iterator) - .is_some_and(|id| implements_trait(cx, cx.typeck_results().expr_ty(recv), id, &[])); - if recv_impls_iterator { - let msg = "called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling \ - `.find(..)` instead"; - let filter_snippet = snippet(cx, filter_arg.span, ".."); - if filter_snippet.lines().count() <= 1 { - let iter_snippet = snippet(cx, recv.span, ".."); - // add note if not multi-line - span_lint_and_then(cx, FILTER_NEXT, expr.span, msg, |diag| { - let (applicability, pat) = if let Some(id) = path_to_local_with_projections(recv) - && let hir::Node::Pat(pat) = cx.tcx.hir_node(id) - && let hir::PatKind::Binding(BindingMode(_, Mutability::Not), _, ident, _) = pat.kind - { - (Applicability::Unspecified, Some((pat.span, ident))) - } else { - (Applicability::MachineApplicable, None) - }; + .get_diagnostic_item(required_trait) + .is_some_and(|id| implements_trait(cx, cx.typeck_results().expr_ty(recv), id, &[])) + { + return; + } + let msg = format!( + "called `filter(..).{next_method}()` on an `{}`. This is more succinctly expressed by calling \ + `.{find_method}(..)` instead", + required_trait.as_str() + ); + let filter_snippet = snippet(cx, filter_arg.span, ".."); + if filter_snippet.lines().count() <= 1 { + let iter_snippet = snippet(cx, recv.span, ".."); + // add note if not multi-line + span_lint_and_then(cx, FILTER_NEXT, expr.span, msg, |diag| { + let (applicability, pat) = if let Some(id) = path_to_local_with_projections(recv) + && let hir::Node::Pat(pat) = cx.tcx.hir_node(id) + && let hir::PatKind::Binding(BindingMode(_, Mutability::Not), _, ident, _) = pat.kind + { + (Applicability::Unspecified, Some((pat.span, ident))) + } else { + (Applicability::MachineApplicable, None) + }; - diag.span_suggestion( - expr.span, - "try", - format!("{iter_snippet}.find({filter_snippet})"), - applicability, + diag.span_suggestion( + expr.span, + "try", + format!("{iter_snippet}.{find_method}({filter_snippet})"), + applicability, + ); + + if let Some((pat_span, ident)) = pat { + diag.span_help( + pat_span, + format!("you will also need to make `{ident}` mutable, because `{find_method}` takes `&mut self`"), ); - - if let Some((pat_span, ident)) = pat { - diag.span_help( - pat_span, - format!("you will also need to make `{ident}` mutable, because `find` takes `&mut self`"), - ); - } - }); - } else { - span_lint(cx, FILTER_NEXT, expr.span, msg); - } + } + }); + } else { + span_lint(cx, FILTER_NEXT, expr.span, msg); } } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 8679689c8ad4..9b27a6c19e1f 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -5369,7 +5369,9 @@ impl Methods { iter_overeager_cloned::Op::LaterCloned, false, ), - (sym::filter, [arg]) => filter_next::check(cx, expr, recv2, arg), + (sym::filter, [arg]) => { + filter_next::check(cx, expr, recv2, arg, filter_next::Direction::Forward); + }, (sym::filter_map, [arg]) => filter_map_next::check(cx, expr, recv2, arg, self.msrv), (sym::iter, []) => iter_next_slice::check(cx, expr, recv2), (sym::skip, [arg]) => iter_skip_next::check(cx, expr, recv2, arg), @@ -5379,6 +5381,14 @@ impl Methods { } } }, + (sym::next_back, []) => { + if let Some((name2, recv2, args2, _, _)) = method_call(recv) + && let (sym::filter, [arg]) = (name2, args2) + && self.msrv.meets(cx, msrvs::DOUBLE_ENDED_ITERATOR_RFIND) + { + filter_next::check(cx, expr, recv2, arg, filter_next::Direction::Backward); + } + }, (sym::nth, [n_arg]) => match method_call(recv) { Some((sym::bytes, recv2, [], _, _)) => bytes_nth::check(cx, expr, recv2, n_arg), Some((sym::cloned, recv2, [], _, _)) => iter_overeager_cloned::check( diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 6e07ed9ffcc4..ad3d52193bf6 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -72,7 +72,7 @@ msrv_aliases! { 1,30,0 { ITERATOR_FIND_MAP, TOOL_ATTRIBUTES } 1,29,0 { ITER_FLATTEN } 1,28,0 { FROM_BOOL, REPEAT_WITH, SLICE_FROM_REF } - 1,27,0 { ITERATOR_TRY_FOLD } + 1,27,0 { ITERATOR_TRY_FOLD, DOUBLE_ENDED_ITERATOR_RFIND } 1,26,0 { RANGE_INCLUSIVE, STRING_RETAIN, POINTER_ADD_SUB_METHODS } 1,24,0 { IS_ASCII_DIGIT, PTR_NULL } 1,18,0 { HASH_MAP_RETAIN, HASH_SET_RETAIN } diff --git a/tests/ui/auxiliary/option_helpers.rs b/tests/ui/auxiliary/option_helpers.rs index f9bc9436b079..796d4dabf04d 100644 --- a/tests/ui/auxiliary/option_helpers.rs +++ b/tests/ui/auxiliary/option_helpers.rs @@ -25,6 +25,10 @@ impl IteratorFalsePositives { self } + pub fn next_back(self) -> IteratorFalsePositives { + self + } + pub fn find(self) -> Option { Some(self.foo) } diff --git a/tests/ui/methods.rs b/tests/ui/methods.rs index f73fe288b0f8..9595888b99f8 100644 --- a/tests/ui/methods.rs +++ b/tests/ui/methods.rs @@ -135,6 +135,26 @@ fn filter_next() { let _ = foo.filter(42).next(); } +#[rustfmt::skip] +fn filter_next_back() { + let v = vec![3, 2, 1, 0, -1, -2, -3]; + + // Multi-line case. + let _ = v.iter().filter(|&x| { + //~^ filter_next + *x < 0 + } + ).next_back(); + + // Check that we don't lint if the caller is not an `Iterator`. + let foo = IteratorFalsePositives { foo: 0 }; + let _ = foo.filter().next_back(); + + let foo = IteratorMethodFalsePositives {}; + let _ = foo.filter(42).next_back(); +} + fn main() { filter_next(); + filter_next_back(); } diff --git a/tests/ui/methods.stderr b/tests/ui/methods.stderr index b226ce7c65da..45efea4ee01c 100644 --- a/tests/ui/methods.stderr +++ b/tests/ui/methods.stderr @@ -24,5 +24,16 @@ LL | | ).next(); = note: `-D clippy::filter-next` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::filter_next)]` -error: aborting due to 2 previous errors +error: called `filter(..).next_back()` on an `DoubleEndedIterator`. This is more succinctly expressed by calling `.rfind(..)` instead + --> tests/ui/methods.rs:143:13 + | +LL | let _ = v.iter().filter(|&x| { + | _____________^ +LL | | +LL | | *x < 0 +LL | | } +LL | | ).next_back(); + | |________________________________^ + +error: aborting due to 3 previous errors diff --git a/tests/ui/methods_fixable.fixed b/tests/ui/methods_fixable.fixed index 49730d811558..287d8d881ec2 100644 --- a/tests/ui/methods_fixable.fixed +++ b/tests/ui/methods_fixable.fixed @@ -8,4 +8,18 @@ fn main() { // Single-line case. let _ = v.iter().find(|&x| *x < 0); //~^ filter_next + + let _ = v.iter().rfind(|&x| *x < 0); + //~^ filter_next +} + +#[clippy::msrv = "1.27"] +fn msrv_1_27() { + let _ = vec![1].into_iter().rfind(|&x| x < 0); + //~^ filter_next +} + +#[clippy::msrv = "1.26"] +fn msrv_1_26() { + let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); } diff --git a/tests/ui/methods_fixable.rs b/tests/ui/methods_fixable.rs index a499b63b6f5b..11ce1b63560d 100644 --- a/tests/ui/methods_fixable.rs +++ b/tests/ui/methods_fixable.rs @@ -8,4 +8,18 @@ fn main() { // Single-line case. let _ = v.iter().filter(|&x| *x < 0).next(); //~^ filter_next + + let _ = v.iter().filter(|&x| *x < 0).next_back(); + //~^ filter_next +} + +#[clippy::msrv = "1.27"] +fn msrv_1_27() { + let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); + //~^ filter_next +} + +#[clippy::msrv = "1.26"] +fn msrv_1_26() { + let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); } diff --git a/tests/ui/methods_fixable.stderr b/tests/ui/methods_fixable.stderr index 852e7a224a6e..d26b5e9ac271 100644 --- a/tests/ui/methods_fixable.stderr +++ b/tests/ui/methods_fixable.stderr @@ -7,5 +7,17 @@ LL | let _ = v.iter().filter(|&x| *x < 0).next(); = note: `-D clippy::filter-next` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::filter_next)]` -error: aborting due to 1 previous error +error: called `filter(..).next_back()` on an `DoubleEndedIterator`. This is more succinctly expressed by calling `.rfind(..)` instead + --> tests/ui/methods_fixable.rs:12:13 + | +LL | let _ = v.iter().filter(|&x| *x < 0).next_back(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `v.iter().rfind(|&x| *x < 0)` + +error: called `filter(..).next_back()` on an `DoubleEndedIterator`. This is more succinctly expressed by calling `.rfind(..)` instead + --> tests/ui/methods_fixable.rs:18:13 + | +LL | let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec![1].into_iter().rfind(|&x| x < 0)` + +error: aborting due to 3 previous errors From 283aad06e9a9145b963119b2b061db92ada9d680 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 1 Aug 2025 21:55:11 +0300 Subject: [PATCH 050/226] resolve: Do not finalize shadowed bindings I.e. do not mark them as used, or non-speculative loaded, or similar. Previously they were sometimes finalized during early resolution, causing issues like https://github.com/rust-lang/rust/pull/144793#issuecomment-3168108005. --- tests/ui/useless_attribute.fixed | 2 +- tests/ui/useless_attribute.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ui/useless_attribute.fixed b/tests/ui/useless_attribute.fixed index 15070dd9c2ca..e0bc23e0788c 100644 --- a/tests/ui/useless_attribute.fixed +++ b/tests/ui/useless_attribute.fixed @@ -153,7 +153,7 @@ pub mod redundant_imports_issue { () => {}; } - #[expect(redundant_imports)] + #[expect(unused_imports)] pub(crate) use empty; empty!(); diff --git a/tests/ui/useless_attribute.rs b/tests/ui/useless_attribute.rs index 3f530de7fd8e..30a4c354b238 100644 --- a/tests/ui/useless_attribute.rs +++ b/tests/ui/useless_attribute.rs @@ -153,7 +153,7 @@ pub mod redundant_imports_issue { () => {}; } - #[expect(redundant_imports)] + #[expect(unused_imports)] pub(crate) use empty; empty!(); From bd39ea463f30fd51be0b4587191c67cd53bf6658 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 25 Sep 2025 00:17:03 +0200 Subject: [PATCH 051/226] inline `clippy_utils::ptr` into `needless_pass_by_value` ..and make it less generic, thanks to all the callers being known --- clippy_lints/src/needless_pass_by_value.rs | 63 +++++++++++++++++----- clippy_utils/src/lib.rs | 1 - clippy_utils/src/ptr.rs | 52 ------------------ 3 files changed, 51 insertions(+), 65 deletions(-) delete mode 100644 clippy_utils/src/ptr.rs diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 32ded96c1236..455d1426aa8c 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -1,16 +1,16 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ptr::get_spans; use clippy_utils::source::{SpanRangeExt, snippet}; use clippy_utils::ty::{ implements_trait, implements_trait_with_env_from_iter, is_copy, is_type_diagnostic_item, is_type_lang_item, }; -use clippy_utils::{is_self, peel_hir_ty_options}; +use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; +use clippy_utils::{is_self, path_to_local_id, peel_hir_ty_options, strip_pat_refs, sym}; use rustc_abi::ExternAbi; use rustc_errors::{Applicability, Diag}; use rustc_hir::intravisit::FnKind; use rustc_hir::{ - Attribute, BindingMode, Body, FnDecl, GenericArg, HirId, HirIdSet, Impl, ItemKind, LangItem, Mutability, Node, - PatKind, QPath, TyKind, + Attribute, BindingMode, Body, ExprKind, FnDecl, GenericArg, HirId, HirIdSet, Impl, ItemKind, LangItem, Mutability, + Node, PatKind, QPath, TyKind, }; use rustc_hir_typeck::expr_use_visitor as euv; use rustc_lint::{LateContext, LateLintPass}; @@ -19,10 +19,13 @@ use rustc_middle::ty::{self, Ty, TypeVisitableExt}; use rustc_session::declare_lint_pass; use rustc_span::def_id::LocalDefId; use rustc_span::symbol::kw; -use rustc_span::{Span, sym}; +use rustc_span::{Span, Symbol}; use rustc_trait_selection::traits; use rustc_trait_selection::traits::misc::type_allowed_to_implement_copy; +use std::borrow::Cow; +use std::ops::ControlFlow; + declare_clippy_lint! { /// ### What it does /// Checks for functions taking arguments by value, but not @@ -217,7 +220,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { } if is_type_diagnostic_item(cx, ty, sym::Vec) - && let Some(clone_spans) = get_spans(cx, Some(body.id()), idx, &[(sym::clone, ".to_owned()")]) + && let Some(clone_spans) = get_spans(cx, body, idx, &[(sym::clone, ".to_owned()")]) && let TyKind::Path(QPath::Resolved(_, path)) = input.kind && let Some(elem_ty) = path .segments @@ -260,12 +263,8 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { } if is_type_lang_item(cx, ty, LangItem::String) - && let Some(clone_spans) = get_spans( - cx, - Some(body.id()), - idx, - &[(sym::clone, ".to_string()"), (sym::as_str, "")], - ) + && let Some(clone_spans) = + get_spans(cx, body, idx, &[(sym::clone, ".to_string()"), (sym::as_str, "")]) { diag.span_suggestion( input.span, @@ -340,3 +339,43 @@ impl<'tcx> euv::Delegate<'tcx> for MovedVariablesCtxt { fn fake_read(&mut self, _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} } + +fn get_spans<'tcx>( + cx: &LateContext<'tcx>, + body: &'tcx Body<'_>, + idx: usize, + replacements: &[(Symbol, &'static str)], +) -> Option)>> { + if let PatKind::Binding(_, binding_id, _, _) = strip_pat_refs(body.params[idx].pat).kind { + extract_clone_suggestions(cx, binding_id, replacements, body) + } else { + Some(vec![]) + } +} + +fn extract_clone_suggestions<'tcx>( + cx: &LateContext<'tcx>, + id: HirId, + replace: &[(Symbol, &'static str)], + body: &'tcx Body<'_>, +) -> Option)>> { + let mut spans = Vec::new(); + for_each_expr_without_closures(body, |e| { + if let ExprKind::MethodCall(seg, recv, [], _) = e.kind + && path_to_local_id(recv, id) + { + if seg.ident.name == sym::capacity { + return ControlFlow::Break(()); + } + for &(fn_name, suffix) in replace { + if seg.ident.name == fn_name { + spans.push((e.span, snippet(cx, recv.span, "_") + suffix)); + return ControlFlow::Continue(Descend::No); + } + } + } + ControlFlow::Continue(Descend::Yes) + }) + .is_none() + .then_some(spans) +} diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index feadc0ecf659..d6586625a1e5 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -63,7 +63,6 @@ pub mod mir; pub mod msrvs; pub mod numeric_literal; pub mod paths; -pub mod ptr; pub mod qualify_min_const_fn; pub mod source; pub mod str_utils; diff --git a/clippy_utils/src/ptr.rs b/clippy_utils/src/ptr.rs deleted file mode 100644 index 5847e916e340..000000000000 --- a/clippy_utils/src/ptr.rs +++ /dev/null @@ -1,52 +0,0 @@ -use crate::source::snippet; -use crate::visitors::{Descend, for_each_expr_without_closures}; -use crate::{path_to_local_id, strip_pat_refs, sym}; -use core::ops::ControlFlow; -use rustc_hir::{Body, BodyId, ExprKind, HirId, PatKind}; -use rustc_lint::LateContext; -use rustc_span::{Span, Symbol}; -use std::borrow::Cow; - -pub fn get_spans( - cx: &LateContext<'_>, - opt_body_id: Option, - idx: usize, - replacements: &[(Symbol, &'static str)], -) -> Option)>> { - if let Some(body) = opt_body_id.map(|id| cx.tcx.hir_body(id)) { - if let PatKind::Binding(_, binding_id, _, _) = strip_pat_refs(body.params[idx].pat).kind { - extract_clone_suggestions(cx, binding_id, replacements, body) - } else { - Some(vec![]) - } - } else { - Some(vec![]) - } -} - -fn extract_clone_suggestions<'tcx>( - cx: &LateContext<'tcx>, - id: HirId, - replace: &[(Symbol, &'static str)], - body: &'tcx Body<'_>, -) -> Option)>> { - let mut spans = Vec::new(); - for_each_expr_without_closures(body, |e| { - if let ExprKind::MethodCall(seg, recv, [], _) = e.kind - && path_to_local_id(recv, id) - { - if seg.ident.name == sym::capacity { - return ControlFlow::Break(()); - } - for &(fn_name, suffix) in replace { - if seg.ident.name == fn_name { - spans.push((e.span, snippet(cx, recv.span, "_") + suffix)); - return ControlFlow::Continue(Descend::No); - } - } - } - ControlFlow::Continue(Descend::Yes) - }) - .is_none() - .then_some(spans) -} From 36d6327fa46a949d217b691fc7fb978359ca636b Mon Sep 17 00:00:00 2001 From: beepster4096 <19316085+beepster4096@users.noreply.github.com> Date: Fri, 26 Sep 2025 12:17:06 -0700 Subject: [PATCH 052/226] castkind::subtype in clippy --- clippy_utils/src/qualify_min_const_fn.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index 2bda6d50373c..cc98fac45c7c 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -141,7 +141,8 @@ fn check_rvalue<'tcx>( | CastKind::FloatToFloat | CastKind::FnPtrToPtr | CastKind::PtrToPtr - | CastKind::PointerCoercion(PointerCoercion::MutToConstPointer | PointerCoercion::ArrayToPointer, _), + | CastKind::PointerCoercion(PointerCoercion::MutToConstPointer | PointerCoercion::ArrayToPointer, _) + | CastKind::Subtype, operand, _, ) => check_operand(cx, operand, span, body, msrv), @@ -312,7 +313,6 @@ fn check_place<'tcx>( | ProjectionElem::OpaqueCast(..) | ProjectionElem::Downcast(..) | ProjectionElem::Subslice { .. } - | ProjectionElem::Subtype(_) | ProjectionElem::Index(_) | ProjectionElem::UnwrapUnsafeBinder(_) => {}, } From 714fdacdf8f2c07950e3f3ddd1999cba10b828af Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Mon, 15 Sep 2025 22:01:36 -0400 Subject: [PATCH 053/226] Apply cfg(bootstrap) replacement --- clippy_lints/src/lib.rs | 1 - tests/missing-test-files.rs | 1 - 2 files changed, 2 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c56fa257b068..b0083b99f175 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -7,7 +7,6 @@ #![feature(iter_intersperse)] #![feature(iter_partition_in_place)] #![feature(never_type)] -#![cfg_attr(bootstrap, feature(round_char_boundary))] #![feature(rustc_private)] #![feature(stmt_expr_attributes)] #![feature(unwrap_infallible)] diff --git a/tests/missing-test-files.rs b/tests/missing-test-files.rs index 63f960c92fa3..9fff3132498d 100644 --- a/tests/missing-test-files.rs +++ b/tests/missing-test-files.rs @@ -1,6 +1,5 @@ #![warn(rust_2018_idioms, unused_lifetimes)] #![allow(clippy::assertions_on_constants)] -#![cfg_attr(bootstrap, feature(path_file_prefix))] use std::cmp::Ordering; use std::ffi::OsStr; From 77c96c031b40298a013befa2d3ebf11f459f9275 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 5 Aug 2025 23:07:11 +0200 Subject: [PATCH 054/226] clean-up --- tests/ui/double_parens.rs | 9 +-------- tests/ui/double_parens.stderr | 10 +++++----- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/tests/ui/double_parens.rs b/tests/ui/double_parens.rs index 7c976015b4e7..1e0303361595 100644 --- a/tests/ui/double_parens.rs +++ b/tests/ui/double_parens.rs @@ -1,5 +1,5 @@ #![warn(clippy::double_parens)] -#![allow(dead_code, clippy::eq_op)] +#![expect(clippy::eq_op)] #![feature(custom_inner_attributes)] #![rustfmt::skip] @@ -14,32 +14,26 @@ impl DummyStruct { fn simple_double_parens() -> i32 { ((0)) //~^ double_parens - - } fn fn_double_parens() { dummy_fn((0)); //~^ double_parens - } fn method_double_parens(x: DummyStruct) { x.dummy_method((0)); //~^ double_parens - } fn tuple_double_parens() -> (i32, i32) { ((1, 2)) //~^ double_parens - } fn unit_double_parens() { (()) //~^ double_parens - } fn fn_tuple_ok() { @@ -63,7 +57,6 @@ fn inside_macro() { assert_eq!((1, 2), (1, 2), "Error"); assert_eq!(((1, 2)), (1, 2), "Error"); //~^ double_parens - } fn main() {} diff --git a/tests/ui/double_parens.stderr b/tests/ui/double_parens.stderr index e119f54949b1..da48219c97e1 100644 --- a/tests/ui/double_parens.stderr +++ b/tests/ui/double_parens.stderr @@ -8,31 +8,31 @@ LL | ((0)) = help: to override `-D warnings` add `#[allow(clippy::double_parens)]` error: consider removing unnecessary double parentheses - --> tests/ui/double_parens.rs:22:14 + --> tests/ui/double_parens.rs:20:14 | LL | dummy_fn((0)); | ^^^ error: consider removing unnecessary double parentheses - --> tests/ui/double_parens.rs:28:20 + --> tests/ui/double_parens.rs:25:20 | LL | x.dummy_method((0)); | ^^^ error: consider removing unnecessary double parentheses - --> tests/ui/double_parens.rs:34:5 + --> tests/ui/double_parens.rs:30:5 | LL | ((1, 2)) | ^^^^^^^^ error: consider removing unnecessary double parentheses - --> tests/ui/double_parens.rs:40:5 + --> tests/ui/double_parens.rs:35:5 | LL | (()) | ^^^^ error: consider removing unnecessary double parentheses - --> tests/ui/double_parens.rs:64:16 + --> tests/ui/double_parens.rs:58:16 | LL | assert_eq!(((1, 2)), (1, 2), "Error"); | ^^^^^^^^ From dbef783309a6692bd0e6573055f3e6252d444708 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Fri, 22 Aug 2025 18:13:08 +0200 Subject: [PATCH 055/226] call args "args" in the `Call` case as well since that's what they are --- clippy_lints/src/double_parens.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/double_parens.rs b/clippy_lints/src/double_parens.rs index 4dd8f01ee709..73078547c695 100644 --- a/clippy_lints/src/double_parens.rs +++ b/clippy_lints/src/double_parens.rs @@ -42,11 +42,11 @@ impl EarlyLintPass for DoubleParens { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { let span = match &expr.kind { ExprKind::Paren(in_paren) if matches!(in_paren.kind, ExprKind::Paren(_) | ExprKind::Tup(_)) => expr.span, - ExprKind::Call(_, params) - if let [param] = &**params - && let ExprKind::Paren(_) = param.kind => + ExprKind::Call(_, args) + if let [args] = &**args + && let ExprKind::Paren(_) = args.kind => { - param.span + args.span }, ExprKind::MethodCall(call) if let [arg] = &*call.args From 6a6b4a3ba8ef0793f275e9eb8351f8acea72e8f1 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Fri, 22 Aug 2025 18:14:04 +0200 Subject: [PATCH 056/226] combine the `Call` and `MethodCall` cases --- clippy_lints/src/double_parens.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/double_parens.rs b/clippy_lints/src/double_parens.rs index 73078547c695..d014efcde48d 100644 --- a/clippy_lints/src/double_parens.rs +++ b/clippy_lints/src/double_parens.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint; -use rustc_ast::ast::{Expr, ExprKind}; +use rustc_ast::ast::{Expr, ExprKind, MethodCall}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::declare_lint_pass; @@ -42,18 +42,12 @@ impl EarlyLintPass for DoubleParens { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { let span = match &expr.kind { ExprKind::Paren(in_paren) if matches!(in_paren.kind, ExprKind::Paren(_) | ExprKind::Tup(_)) => expr.span, - ExprKind::Call(_, args) + ExprKind::Call(_, args) | ExprKind::MethodCall(box MethodCall { args, .. }) if let [args] = &**args && let ExprKind::Paren(_) = args.kind => { args.span }, - ExprKind::MethodCall(call) - if let [arg] = &*call.args - && let ExprKind::Paren(_) = arg.kind => - { - arg.span - }, _ => return, }; if !expr.span.from_expansion() { From 82b2eb13f8ed8777c97cdceb94137a4ed73d43f6 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Fri, 22 Aug 2025 18:18:03 +0200 Subject: [PATCH 057/226] add structured suggestions --- clippy_lints/src/double_parens.rs | 66 ++++++++++++++++++++++--------- tests/ui/double_parens.fixed | 63 +++++++++++++++++++++++++++++ tests/ui/double_parens.rs | 1 + tests/ui/double_parens.stderr | 28 ++++++------- 4 files changed, 126 insertions(+), 32 deletions(-) create mode 100644 tests/ui/double_parens.fixed diff --git a/clippy_lints/src/double_parens.rs b/clippy_lints/src/double_parens.rs index d014efcde48d..ab6e47712120 100644 --- a/clippy_lints/src/double_parens.rs +++ b/clippy_lints/src/double_parens.rs @@ -1,5 +1,7 @@ -use clippy_utils::diagnostics::span_lint; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::{HasSession, snippet_with_applicability}; use rustc_ast::ast::{Expr, ExprKind, MethodCall}; +use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::declare_lint_pass; @@ -24,7 +26,7 @@ declare_clippy_lint! { /// Use instead: /// ```no_run /// fn simple_no_parens() -> i32 { - /// 0 + /// (0) /// } /// /// # fn foo(bar: usize) {} @@ -40,23 +42,51 @@ declare_lint_pass!(DoubleParens => [DOUBLE_PARENS]); impl EarlyLintPass for DoubleParens { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { - let span = match &expr.kind { - ExprKind::Paren(in_paren) if matches!(in_paren.kind, ExprKind::Paren(_) | ExprKind::Tup(_)) => expr.span, - ExprKind::Call(_, args) | ExprKind::MethodCall(box MethodCall { args, .. }) - if let [args] = &**args - && let ExprKind::Paren(_) = args.kind => - { - args.span + if expr.span.from_expansion() { + return; + } + + match &expr.kind { + // ((..)) + // ^^^^^^ expr + // ^^^^ inner + ExprKind::Paren(inner) if matches!(inner.kind, ExprKind::Paren(_) | ExprKind::Tup(_)) => { + // suggest removing the outer parens + let mut applicability = Applicability::MachineApplicable; + let sugg = snippet_with_applicability(cx.sess(), inner.span, "_", &mut applicability); + span_lint_and_sugg( + cx, + DOUBLE_PARENS, + expr.span, + "unnecessary parentheses", + "remove them", + sugg.to_string(), + applicability, + ); }, - _ => return, - }; - if !expr.span.from_expansion() { - span_lint( - cx, - DOUBLE_PARENS, - span, - "consider removing unnecessary double parentheses", - ); + + // func((n)) + // ^^^^^^^^^ expr + // ^^^ arg + // ^ inner + ExprKind::Call(_, args) | ExprKind::MethodCall(box MethodCall { args, .. }) + if let [arg] = &**args + && let ExprKind::Paren(inner) = &arg.kind => + { + // suggest removing the inner parens + let mut applicability = Applicability::MachineApplicable; + let sugg = snippet_with_applicability(cx.sess(), inner.span, "_", &mut applicability); + span_lint_and_sugg( + cx, + DOUBLE_PARENS, + arg.span, + "unnecessary parentheses", + "remove them", + sugg.to_string(), + applicability, + ); + }, + _ => {}, } } } diff --git a/tests/ui/double_parens.fixed b/tests/ui/double_parens.fixed new file mode 100644 index 000000000000..7fa116942fe8 --- /dev/null +++ b/tests/ui/double_parens.fixed @@ -0,0 +1,63 @@ +#![warn(clippy::double_parens)] +#![expect(clippy::eq_op)] +#![feature(custom_inner_attributes)] +#![rustfmt::skip] + +fn dummy_fn(_: T) {} + +struct DummyStruct; + +impl DummyStruct { + fn dummy_method(self, _: T) {} +} + +fn simple_double_parens() -> i32 { + (0) + //~^ double_parens +} + +fn fn_double_parens() { + dummy_fn(0); + //~^ double_parens +} + +fn method_double_parens(x: DummyStruct) { + x.dummy_method(0); + //~^ double_parens +} + +fn tuple_double_parens() -> (i32, i32) { + (1, 2) + //~^ double_parens +} + +#[allow(clippy::unused_unit)] +fn unit_double_parens() { + () + //~^ double_parens +} + +fn fn_tuple_ok() { + dummy_fn((1, 2)); +} + +fn method_tuple_ok(x: DummyStruct) { + x.dummy_method((1, 2)); +} + +fn fn_unit_ok() { + dummy_fn(()); +} + +fn method_unit_ok(x: DummyStruct) { + x.dummy_method(()); +} + +// Issue #3206 +fn inside_macro() { + assert_eq!((1, 2), (1, 2), "Error"); + assert_eq!((1, 2), (1, 2), "Error"); + //~^ double_parens +} + +fn main() {} diff --git a/tests/ui/double_parens.rs b/tests/ui/double_parens.rs index 1e0303361595..8730b4b12468 100644 --- a/tests/ui/double_parens.rs +++ b/tests/ui/double_parens.rs @@ -31,6 +31,7 @@ fn tuple_double_parens() -> (i32, i32) { //~^ double_parens } +#[allow(clippy::unused_unit)] fn unit_double_parens() { (()) //~^ double_parens diff --git a/tests/ui/double_parens.stderr b/tests/ui/double_parens.stderr index da48219c97e1..2b37877ccdd8 100644 --- a/tests/ui/double_parens.stderr +++ b/tests/ui/double_parens.stderr @@ -1,41 +1,41 @@ -error: consider removing unnecessary double parentheses +error: unnecessary parentheses --> tests/ui/double_parens.rs:15:5 | LL | ((0)) - | ^^^^^ + | ^^^^^ help: remove them: `(0)` | = note: `-D clippy::double-parens` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::double_parens)]` -error: consider removing unnecessary double parentheses +error: unnecessary parentheses --> tests/ui/double_parens.rs:20:14 | LL | dummy_fn((0)); - | ^^^ + | ^^^ help: remove them: `0` -error: consider removing unnecessary double parentheses +error: unnecessary parentheses --> tests/ui/double_parens.rs:25:20 | LL | x.dummy_method((0)); - | ^^^ + | ^^^ help: remove them: `0` -error: consider removing unnecessary double parentheses +error: unnecessary parentheses --> tests/ui/double_parens.rs:30:5 | LL | ((1, 2)) - | ^^^^^^^^ + | ^^^^^^^^ help: remove them: `(1, 2)` -error: consider removing unnecessary double parentheses - --> tests/ui/double_parens.rs:35:5 +error: unnecessary parentheses + --> tests/ui/double_parens.rs:36:5 | LL | (()) - | ^^^^ + | ^^^^ help: remove them: `()` -error: consider removing unnecessary double parentheses - --> tests/ui/double_parens.rs:58:16 +error: unnecessary parentheses + --> tests/ui/double_parens.rs:59:16 | LL | assert_eq!(((1, 2)), (1, 2), "Error"); - | ^^^^^^^^ + | ^^^^^^^^ help: remove them: `(1, 2)` error: aborting due to 6 previous errors From b613be223dfc2151bc7aea15019b5ee04607621c Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Fri, 22 Aug 2025 18:19:10 +0200 Subject: [PATCH 058/226] fix a bug while we're at it --- clippy_lints/src/double_parens.rs | 57 ++++++++++++++++--------------- tests/ui/double_parens.fixed | 40 ++++++++++++++++++++-- tests/ui/double_parens.rs | 40 ++++++++++++++++++++-- tests/ui/double_parens.stderr | 31 ++++++++++++++++- 4 files changed, 136 insertions(+), 32 deletions(-) diff --git a/clippy_lints/src/double_parens.rs b/clippy_lints/src/double_parens.rs index ab6e47712120..bddf4702fb34 100644 --- a/clippy_lints/src/double_parens.rs +++ b/clippy_lints/src/double_parens.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{HasSession, snippet_with_applicability}; +use clippy_utils::source::{HasSession, snippet_with_applicability, snippet_with_context}; use rustc_ast::ast::{Expr, ExprKind, MethodCall}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; @@ -42,27 +42,28 @@ declare_lint_pass!(DoubleParens => [DOUBLE_PARENS]); impl EarlyLintPass for DoubleParens { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { - if expr.span.from_expansion() { - return; - } - match &expr.kind { // ((..)) // ^^^^^^ expr // ^^^^ inner ExprKind::Paren(inner) if matches!(inner.kind, ExprKind::Paren(_) | ExprKind::Tup(_)) => { // suggest removing the outer parens - let mut applicability = Applicability::MachineApplicable; - let sugg = snippet_with_applicability(cx.sess(), inner.span, "_", &mut applicability); - span_lint_and_sugg( - cx, - DOUBLE_PARENS, - expr.span, - "unnecessary parentheses", - "remove them", - sugg.to_string(), - applicability, - ); + if expr.span.eq_ctxt(inner.span) { + let mut applicability = Applicability::MachineApplicable; + // We don't need to use `snippet_with_context` here, because: + // - if `inner`'s `ctxt` is from macro, we don't lint in the first place (see the check above) + // - otherwise, calling `snippet_with_applicability` on a not-from-macro span is fine + let sugg = snippet_with_applicability(cx.sess(), inner.span, "_", &mut applicability); + span_lint_and_sugg( + cx, + DOUBLE_PARENS, + expr.span, + "unnecessary parentheses", + "remove them", + sugg.to_string(), + applicability, + ); + } }, // func((n)) @@ -74,17 +75,19 @@ impl EarlyLintPass for DoubleParens { && let ExprKind::Paren(inner) = &arg.kind => { // suggest removing the inner parens - let mut applicability = Applicability::MachineApplicable; - let sugg = snippet_with_applicability(cx.sess(), inner.span, "_", &mut applicability); - span_lint_and_sugg( - cx, - DOUBLE_PARENS, - arg.span, - "unnecessary parentheses", - "remove them", - sugg.to_string(), - applicability, - ); + if expr.span.eq_ctxt(arg.span) { + let mut applicability = Applicability::MachineApplicable; + let sugg = snippet_with_context(cx.sess(), inner.span, arg.span.ctxt(), "_", &mut applicability).0; + span_lint_and_sugg( + cx, + DOUBLE_PARENS, + arg.span, + "unnecessary parentheses", + "remove them", + sugg.to_string(), + applicability, + ); + } }, _ => {}, } diff --git a/tests/ui/double_parens.fixed b/tests/ui/double_parens.fixed index 7fa116942fe8..dedc513438d1 100644 --- a/tests/ui/double_parens.fixed +++ b/tests/ui/double_parens.fixed @@ -1,5 +1,5 @@ #![warn(clippy::double_parens)] -#![expect(clippy::eq_op)] +#![expect(clippy::eq_op, clippy::no_effect)] #![feature(custom_inner_attributes)] #![rustfmt::skip] @@ -8,7 +8,7 @@ fn dummy_fn(_: T) {} struct DummyStruct; impl DummyStruct { - fn dummy_method(self, _: T) {} + fn dummy_method(&self, _: T) {} } fn simple_double_parens() -> i32 { @@ -60,4 +60,40 @@ fn inside_macro() { //~^ double_parens } +fn issue9000(x: DummyStruct) { + macro_rules! foo { + () => {(100)} + } + // don't lint: the inner paren comes from the macro expansion + (foo!()); + dummy_fn(foo!()); + x.dummy_method(foo!()); + + macro_rules! baz { + ($n:literal) => {($n)} + } + // don't lint: don't get confused by the expression inside the inner paren + // having the same `ctxt` as the overall expression + // (this is a bug that happened during the development of the fix) + (baz!(100)); + dummy_fn(baz!(100)); + x.dummy_method(baz!(100)); + + // should lint: both parens are from inside the macro + macro_rules! bar { + () => {(100)} + //~^ double_parens + } + bar!(); + + // should lint: both parens are from outside the macro; + // make sure to suggest the macro unexpanded + (vec![1, 2]); + //~^ double_parens + dummy_fn(vec![1, 2]); + //~^ double_parens + x.dummy_method(vec![1, 2]); + //~^ double_parens +} + fn main() {} diff --git a/tests/ui/double_parens.rs b/tests/ui/double_parens.rs index 8730b4b12468..27f252485b71 100644 --- a/tests/ui/double_parens.rs +++ b/tests/ui/double_parens.rs @@ -1,5 +1,5 @@ #![warn(clippy::double_parens)] -#![expect(clippy::eq_op)] +#![expect(clippy::eq_op, clippy::no_effect)] #![feature(custom_inner_attributes)] #![rustfmt::skip] @@ -8,7 +8,7 @@ fn dummy_fn(_: T) {} struct DummyStruct; impl DummyStruct { - fn dummy_method(self, _: T) {} + fn dummy_method(&self, _: T) {} } fn simple_double_parens() -> i32 { @@ -60,4 +60,40 @@ fn inside_macro() { //~^ double_parens } +fn issue9000(x: DummyStruct) { + macro_rules! foo { + () => {(100)} + } + // don't lint: the inner paren comes from the macro expansion + (foo!()); + dummy_fn(foo!()); + x.dummy_method(foo!()); + + macro_rules! baz { + ($n:literal) => {($n)} + } + // don't lint: don't get confused by the expression inside the inner paren + // having the same `ctxt` as the overall expression + // (this is a bug that happened during the development of the fix) + (baz!(100)); + dummy_fn(baz!(100)); + x.dummy_method(baz!(100)); + + // should lint: both parens are from inside the macro + macro_rules! bar { + () => {((100))} + //~^ double_parens + } + bar!(); + + // should lint: both parens are from outside the macro; + // make sure to suggest the macro unexpanded + ((vec![1, 2])); + //~^ double_parens + dummy_fn((vec![1, 2])); + //~^ double_parens + x.dummy_method((vec![1, 2])); + //~^ double_parens +} + fn main() {} diff --git a/tests/ui/double_parens.stderr b/tests/ui/double_parens.stderr index 2b37877ccdd8..3a740e44cacf 100644 --- a/tests/ui/double_parens.stderr +++ b/tests/ui/double_parens.stderr @@ -37,5 +37,34 @@ error: unnecessary parentheses LL | assert_eq!(((1, 2)), (1, 2), "Error"); | ^^^^^^^^ help: remove them: `(1, 2)` -error: aborting due to 6 previous errors +error: unnecessary parentheses + --> tests/ui/double_parens.rs:84:16 + | +LL | () => {((100))} + | ^^^^^^^ help: remove them: `(100)` +... +LL | bar!(); + | ------ in this macro invocation + | + = note: this error originates in the macro `bar` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: unnecessary parentheses + --> tests/ui/double_parens.rs:91:5 + | +LL | ((vec![1, 2])); + | ^^^^^^^^^^^^^^ help: remove them: `(vec![1, 2])` + +error: unnecessary parentheses + --> tests/ui/double_parens.rs:93:14 + | +LL | dummy_fn((vec![1, 2])); + | ^^^^^^^^^^^^ help: remove them: `vec![1, 2]` + +error: unnecessary parentheses + --> tests/ui/double_parens.rs:95:20 + | +LL | x.dummy_method((vec![1, 2])); + | ^^^^^^^^^^^^ help: remove them: `vec![1, 2]` + +error: aborting due to 10 previous errors From bb1081e5a9a85081d944c885939809a08db84dd3 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 5 Aug 2025 11:58:48 +0200 Subject: [PATCH 059/226] move unfixable stuff to a separate file --- tests/ui/mut_mut.rs | 9 +++----- tests/ui/mut_mut.stderr | 18 ++++++++-------- tests/ui/mut_mut_unfixable.rs | 34 ++++++++++++++++++++++++++++++ tests/ui/mut_mut_unfixable.stderr | 35 +++++++++++++++++++++++++++++++ 4 files changed, 81 insertions(+), 15 deletions(-) create mode 100644 tests/ui/mut_mut_unfixable.rs create mode 100644 tests/ui/mut_mut_unfixable.stderr diff --git a/tests/ui/mut_mut.rs b/tests/ui/mut_mut.rs index bbcdbc89b6a4..d006578b3d28 100644 --- a/tests/ui/mut_mut.rs +++ b/tests/ui/mut_mut.rs @@ -12,9 +12,8 @@ extern crate proc_macros; use proc_macros::{external, inline_macros}; -fn fun(x: &mut &mut u32) -> bool { +fn fun(x: &mut &mut u32) { //~^ mut_mut - **x > 0 } fn less_fun(x: *mut *mut u32) { @@ -37,19 +36,17 @@ fn main() { //~^ mut_mut } - if fun(x) { + { let y: &mut &mut u32 = &mut &mut 2; //~^ mut_mut //~| mut_mut - **y + **x; } - if fun(x) { + { let y: &mut &mut &mut u32 = &mut &mut &mut 2; //~^ mut_mut //~| mut_mut //~| mut_mut - ***y + **x; } let mut z = inline!(&mut $(&mut 3u32)); diff --git a/tests/ui/mut_mut.stderr b/tests/ui/mut_mut.stderr index 74b0c9ba145a..8517c4e5ad89 100644 --- a/tests/ui/mut_mut.stderr +++ b/tests/ui/mut_mut.stderr @@ -1,20 +1,20 @@ error: generally you want to avoid `&mut &mut _` if possible --> tests/ui/mut_mut.rs:15:11 | -LL | fn fun(x: &mut &mut u32) -> bool { +LL | fn fun(x: &mut &mut u32) { | ^^^^^^^^^^^^^ | = note: `-D clippy::mut-mut` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::mut_mut)]` error: generally you want to avoid `&mut &mut _` if possible - --> tests/ui/mut_mut.rs:33:17 + --> tests/ui/mut_mut.rs:32:17 | LL | let mut x = &mut &mut 1u32; | ^^^^^^^^^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> tests/ui/mut_mut.rs:55:25 + --> tests/ui/mut_mut.rs:52:25 | LL | let mut z = inline!(&mut $(&mut 3u32)); | ^ @@ -22,37 +22,37 @@ LL | let mut z = inline!(&mut $(&mut 3u32)); = note: this error originates in the macro `__inline_mac_fn_main` (in Nightly builds, run with -Z macro-backtrace for more info) error: this expression mutably borrows a mutable reference. Consider reborrowing - --> tests/ui/mut_mut.rs:36:21 + --> tests/ui/mut_mut.rs:35:21 | LL | let mut y = &mut x; | ^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> tests/ui/mut_mut.rs:41:32 + --> tests/ui/mut_mut.rs:40:32 | LL | let y: &mut &mut u32 = &mut &mut 2; | ^^^^^^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> tests/ui/mut_mut.rs:41:16 + --> tests/ui/mut_mut.rs:40:16 | LL | let y: &mut &mut u32 = &mut &mut 2; | ^^^^^^^^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> tests/ui/mut_mut.rs:48:37 + --> tests/ui/mut_mut.rs:46:37 | LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; | ^^^^^^^^^^^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> tests/ui/mut_mut.rs:48:16 + --> tests/ui/mut_mut.rs:46:16 | LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; | ^^^^^^^^^^^^^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> tests/ui/mut_mut.rs:48:21 + --> tests/ui/mut_mut.rs:46:21 | LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; | ^^^^^^^^^^^^^ diff --git a/tests/ui/mut_mut_unfixable.rs b/tests/ui/mut_mut_unfixable.rs new file mode 100644 index 000000000000..db0552a86ec9 --- /dev/null +++ b/tests/ui/mut_mut_unfixable.rs @@ -0,0 +1,34 @@ +//@no-rustfix + +#![warn(clippy::mut_mut)] +#![allow(unused)] +#![expect(clippy::no_effect)] + +//! removing the extra `&mut`s will break the derefs + +fn fun(x: &mut &mut u32) -> bool { + //~^ mut_mut + **x > 0 +} + +fn main() { + let mut x = &mut &mut 1u32; + //~^ mut_mut + { + let mut y = &mut x; + //~^ mut_mut + ***y + **x; + } + + if fun(x) { + let y = &mut &mut 2; + //~^ mut_mut + **y + **x; + } + + if fun(x) { + let y = &mut &mut &mut 2; + //~^ mut_mut + ***y + **x; + } +} diff --git a/tests/ui/mut_mut_unfixable.stderr b/tests/ui/mut_mut_unfixable.stderr new file mode 100644 index 000000000000..bb0363ef7ed9 --- /dev/null +++ b/tests/ui/mut_mut_unfixable.stderr @@ -0,0 +1,35 @@ +error: generally you want to avoid `&mut &mut _` if possible + --> tests/ui/mut_mut_unfixable.rs:9:11 + | +LL | fn fun(x: &mut &mut u32) -> bool { + | ^^^^^^^^^^^^^ + | + = note: `-D clippy::mut-mut` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::mut_mut)]` + +error: generally you want to avoid `&mut &mut _` if possible + --> tests/ui/mut_mut_unfixable.rs:15:17 + | +LL | let mut x = &mut &mut 1u32; + | ^^^^^^^^^^^^^^ + +error: this expression mutably borrows a mutable reference. Consider reborrowing + --> tests/ui/mut_mut_unfixable.rs:18:21 + | +LL | let mut y = &mut x; + | ^^^^^^ + +error: generally you want to avoid `&mut &mut _` if possible + --> tests/ui/mut_mut_unfixable.rs:24:17 + | +LL | let y = &mut &mut 2; + | ^^^^^^^^^^^ + +error: generally you want to avoid `&mut &mut _` if possible + --> tests/ui/mut_mut_unfixable.rs:30:17 + | +LL | let y = &mut &mut &mut 2; + | ^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors + From a4fba850a90a3a7f9a33d872de09795d9801743f Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 3 Aug 2025 23:32:07 +0200 Subject: [PATCH 060/226] sugg on expr 1 --- clippy_lints/src/mut_mut.rs | 13 ++++- tests/ui/mut_mut.fixed | 94 +++++++++++++++++++++++++++++++ tests/ui/mut_mut.stderr | 4 +- tests/ui/mut_mut_unfixable.stderr | 4 +- 4 files changed, 108 insertions(+), 7 deletions(-) create mode 100644 tests/ui/mut_mut.fixed diff --git a/clippy_lints/src/mut_mut.rs b/clippy_lints/src/mut_mut.rs index d98c70e7f5a8..c8b4ad3f7722 100644 --- a/clippy_lints/src/mut_mut.rs +++ b/clippy_lints/src/mut_mut.rs @@ -1,5 +1,7 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_hir}; +use clippy_utils::diagnostics::{span_lint, span_lint_hir, span_lint_hir_and_then}; use clippy_utils::higher; +use clippy_utils::sugg::Sugg; +use rustc_errors::Applicability; use rustc_hir::{self as hir, AmbigArg, intravisit}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty; @@ -80,12 +82,17 @@ impl<'tcx> intravisit::Visitor<'tcx> for MutVisitor<'_, 'tcx> { } else if let ty::Ref(_, ty, hir::Mutability::Mut) = self.cx.typeck_results().expr_ty(e).kind() && ty.peel_refs().is_sized(self.cx.tcx, self.cx.typing_env()) { - span_lint_hir( + let mut applicability = Applicability::MaybeIncorrect; + let sugg = Sugg::hir_with_applicability(self.cx, e, "..", &mut applicability).mut_addr_deref(); + span_lint_hir_and_then( self.cx, MUT_MUT, expr.hir_id, expr.span, - "this expression mutably borrows a mutable reference. Consider reborrowing", + "this expression mutably borrows a mutable reference", + |diag| { + diag.span_suggestion(expr.span, "reborrow instead", sugg, applicability); + }, ); } } diff --git a/tests/ui/mut_mut.fixed b/tests/ui/mut_mut.fixed new file mode 100644 index 000000000000..cbce826b6e85 --- /dev/null +++ b/tests/ui/mut_mut.fixed @@ -0,0 +1,94 @@ +//@aux-build:proc_macros.rs + +#![warn(clippy::mut_mut)] +#![allow(unused)] +#![allow( + clippy::no_effect, + clippy::uninlined_format_args, + clippy::unnecessary_operation, + clippy::needless_pass_by_ref_mut +)] + +extern crate proc_macros; +use proc_macros::{external, inline_macros}; + +fn fun(x: &mut &mut u32) { + //~^ mut_mut +} + +fn less_fun(x: *mut *mut u32) { + let y = x; +} + +macro_rules! mut_ptr { + ($p:expr) => { + &mut $p + }; +} + +#[allow(unused_mut, unused_variables)] +#[inline_macros] +fn main() { + let mut x = &mut &mut 1u32; + //~^ mut_mut + { + let mut y = &mut *x; + //~^ mut_mut + } + + { + let y: &mut &mut u32 = &mut &mut 2; + //~^ mut_mut + //~| mut_mut + } + + { + let y: &mut &mut &mut u32 = &mut &mut &mut 2; + //~^ mut_mut + //~| mut_mut + //~| mut_mut + } + + let mut z = inline!(&mut $(&mut 3u32)); + //~^ mut_mut +} + +fn issue939() { + let array = [5, 6, 7, 8, 9]; + let mut args = array.iter().skip(2); + for &arg in &mut args { + println!("{}", arg); + } + + let args = &mut args; + for arg in args { + println!(":{}", arg); + } +} + +fn issue6922() { + // do not lint from an external macro + external!(let mut_mut_ty: &mut &mut u32 = &mut &mut 1u32;); +} + +mod issue9035 { + use std::fmt::Display; + + struct Foo<'a> { + inner: &'a mut dyn Display, + } + + impl Foo<'_> { + fn foo(&mut self) { + let hlp = &mut self.inner; + bar(hlp); + } + } + + fn bar(_: &mut impl Display) {} +} + +fn allow_works() { + #[allow(clippy::mut_mut)] + let _ = &mut &mut 1; +} diff --git a/tests/ui/mut_mut.stderr b/tests/ui/mut_mut.stderr index 8517c4e5ad89..aa4731e8fe04 100644 --- a/tests/ui/mut_mut.stderr +++ b/tests/ui/mut_mut.stderr @@ -21,11 +21,11 @@ LL | let mut z = inline!(&mut $(&mut 3u32)); | = note: this error originates in the macro `__inline_mac_fn_main` (in Nightly builds, run with -Z macro-backtrace for more info) -error: this expression mutably borrows a mutable reference. Consider reborrowing +error: this expression mutably borrows a mutable reference --> tests/ui/mut_mut.rs:35:21 | LL | let mut y = &mut x; - | ^^^^^^ + | ^^^^^^ help: reborrow instead: `&mut *x` error: generally you want to avoid `&mut &mut _` if possible --> tests/ui/mut_mut.rs:40:32 diff --git a/tests/ui/mut_mut_unfixable.stderr b/tests/ui/mut_mut_unfixable.stderr index bb0363ef7ed9..835b272258d3 100644 --- a/tests/ui/mut_mut_unfixable.stderr +++ b/tests/ui/mut_mut_unfixable.stderr @@ -13,11 +13,11 @@ error: generally you want to avoid `&mut &mut _` if possible LL | let mut x = &mut &mut 1u32; | ^^^^^^^^^^^^^^ -error: this expression mutably borrows a mutable reference. Consider reborrowing +error: this expression mutably borrows a mutable reference --> tests/ui/mut_mut_unfixable.rs:18:21 | LL | let mut y = &mut x; - | ^^^^^^ + | ^^^^^^ help: reborrow instead: `&mut *x` error: generally you want to avoid `&mut &mut _` if possible --> tests/ui/mut_mut_unfixable.rs:24:17 From 70be286cefb2dae0a708e0351c09a6a1170cbd54 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 5 Aug 2025 12:14:09 +0200 Subject: [PATCH 061/226] sugg on expr 2 --- clippy_lints/src/mut_mut.rs | 11 ++++++++--- tests/ui/mut_mut.fixed | 8 ++++---- tests/ui/mut_mut.stderr | 16 ++++++++-------- tests/ui/mut_mut_unfixable.stderr | 12 ++++++------ 4 files changed, 26 insertions(+), 21 deletions(-) diff --git a/clippy_lints/src/mut_mut.rs b/clippy_lints/src/mut_mut.rs index c8b4ad3f7722..0feed460d2ed 100644 --- a/clippy_lints/src/mut_mut.rs +++ b/clippy_lints/src/mut_mut.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_hir, span_lint_hir_and_then}; +use clippy_utils::diagnostics::{span_lint, span_lint_hir_and_then}; use clippy_utils::higher; use clippy_utils::sugg::Sugg; use rustc_errors::Applicability; @@ -72,12 +72,17 @@ impl<'tcx> intravisit::Visitor<'tcx> for MutVisitor<'_, 'tcx> { intravisit::walk_expr(self, body); } else if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, e) = expr.kind { if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, _) = e.kind { - span_lint_hir( + let mut applicability = Applicability::MaybeIncorrect; + let sugg = Sugg::hir_with_applicability(self.cx, e, "..", &mut applicability); + span_lint_hir_and_then( self.cx, MUT_MUT, expr.hir_id, expr.span, - "generally you want to avoid `&mut &mut _` if possible", + "an expression of form `&mut &mut _`", + |diag| { + diag.span_suggestion(expr.span, "remove the extra `&mut`", sugg, applicability); + }, ); } else if let ty::Ref(_, ty, hir::Mutability::Mut) = self.cx.typeck_results().expr_ty(e).kind() && ty.peel_refs().is_sized(self.cx.tcx, self.cx.typing_env()) diff --git a/tests/ui/mut_mut.fixed b/tests/ui/mut_mut.fixed index cbce826b6e85..c6b318d76510 100644 --- a/tests/ui/mut_mut.fixed +++ b/tests/ui/mut_mut.fixed @@ -29,7 +29,7 @@ macro_rules! mut_ptr { #[allow(unused_mut, unused_variables)] #[inline_macros] fn main() { - let mut x = &mut &mut 1u32; + let mut x = &mut 1u32; //~^ mut_mut { let mut y = &mut *x; @@ -37,19 +37,19 @@ fn main() { } { - let y: &mut &mut u32 = &mut &mut 2; + let y: &mut &mut u32 = &mut 2; //~^ mut_mut //~| mut_mut } { - let y: &mut &mut &mut u32 = &mut &mut &mut 2; + let y: &mut &mut &mut u32 = &mut &mut 2; //~^ mut_mut //~| mut_mut //~| mut_mut } - let mut z = inline!(&mut $(&mut 3u32)); + let mut z = inline!(&mut 3u32mut $(&mut 3u32)); //~^ mut_mut } diff --git a/tests/ui/mut_mut.stderr b/tests/ui/mut_mut.stderr index aa4731e8fe04..f21de359e64f 100644 --- a/tests/ui/mut_mut.stderr +++ b/tests/ui/mut_mut.stderr @@ -7,17 +7,17 @@ LL | fn fun(x: &mut &mut u32) { = note: `-D clippy::mut-mut` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::mut_mut)]` -error: generally you want to avoid `&mut &mut _` if possible +error: an expression of form `&mut &mut _` --> tests/ui/mut_mut.rs:32:17 | LL | let mut x = &mut &mut 1u32; - | ^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut 1u32` -error: generally you want to avoid `&mut &mut _` if possible +error: an expression of form `&mut &mut _` --> tests/ui/mut_mut.rs:52:25 | LL | let mut z = inline!(&mut $(&mut 3u32)); - | ^ + | ^ help: remove the extra `&mut`: `&mut 3u32` | = note: this error originates in the macro `__inline_mac_fn_main` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -27,11 +27,11 @@ error: this expression mutably borrows a mutable reference LL | let mut y = &mut x; | ^^^^^^ help: reborrow instead: `&mut *x` -error: generally you want to avoid `&mut &mut _` if possible +error: an expression of form `&mut &mut _` --> tests/ui/mut_mut.rs:40:32 | LL | let y: &mut &mut u32 = &mut &mut 2; - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ help: remove the extra `&mut`: `&mut 2` error: generally you want to avoid `&mut &mut _` if possible --> tests/ui/mut_mut.rs:40:16 @@ -39,11 +39,11 @@ error: generally you want to avoid `&mut &mut _` if possible LL | let y: &mut &mut u32 = &mut &mut 2; | ^^^^^^^^^^^^^ -error: generally you want to avoid `&mut &mut _` if possible +error: an expression of form `&mut &mut _` --> tests/ui/mut_mut.rs:46:37 | LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut &mut 2` error: generally you want to avoid `&mut &mut _` if possible --> tests/ui/mut_mut.rs:46:16 diff --git a/tests/ui/mut_mut_unfixable.stderr b/tests/ui/mut_mut_unfixable.stderr index 835b272258d3..129e5b24387e 100644 --- a/tests/ui/mut_mut_unfixable.stderr +++ b/tests/ui/mut_mut_unfixable.stderr @@ -7,11 +7,11 @@ LL | fn fun(x: &mut &mut u32) -> bool { = note: `-D clippy::mut-mut` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::mut_mut)]` -error: generally you want to avoid `&mut &mut _` if possible +error: an expression of form `&mut &mut _` --> tests/ui/mut_mut_unfixable.rs:15:17 | LL | let mut x = &mut &mut 1u32; - | ^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut 1u32` error: this expression mutably borrows a mutable reference --> tests/ui/mut_mut_unfixable.rs:18:21 @@ -19,17 +19,17 @@ error: this expression mutably borrows a mutable reference LL | let mut y = &mut x; | ^^^^^^ help: reborrow instead: `&mut *x` -error: generally you want to avoid `&mut &mut _` if possible +error: an expression of form `&mut &mut _` --> tests/ui/mut_mut_unfixable.rs:24:17 | LL | let y = &mut &mut 2; - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ help: remove the extra `&mut`: `&mut 2` -error: generally you want to avoid `&mut &mut _` if possible +error: an expression of form `&mut &mut _` --> tests/ui/mut_mut_unfixable.rs:30:17 | LL | let y = &mut &mut &mut 2; - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut &mut 2` error: aborting due to 5 previous errors From 269870a5f9b48117ba9bac65e8b9549df421e9c2 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Mon, 4 Aug 2025 20:21:19 +0200 Subject: [PATCH 062/226] fix: don't lint in macro calls --- clippy_lints/src/mut_mut.rs | 3 +++ tests/ui/mut_mut.fixed | 3 +-- tests/ui/mut_mut.rs | 1 - tests/ui/mut_mut.stderr | 10 +--------- 4 files changed, 5 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/mut_mut.rs b/clippy_lints/src/mut_mut.rs index 0feed460d2ed..376d7214c267 100644 --- a/clippy_lints/src/mut_mut.rs +++ b/clippy_lints/src/mut_mut.rs @@ -72,6 +72,9 @@ impl<'tcx> intravisit::Visitor<'tcx> for MutVisitor<'_, 'tcx> { intravisit::walk_expr(self, body); } else if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, e) = expr.kind { if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, _) = e.kind { + if !expr.span.eq_ctxt(e.span) { + return; + } let mut applicability = Applicability::MaybeIncorrect; let sugg = Sugg::hir_with_applicability(self.cx, e, "..", &mut applicability); span_lint_hir_and_then( diff --git a/tests/ui/mut_mut.fixed b/tests/ui/mut_mut.fixed index c6b318d76510..137306064b43 100644 --- a/tests/ui/mut_mut.fixed +++ b/tests/ui/mut_mut.fixed @@ -49,8 +49,7 @@ fn main() { //~| mut_mut } - let mut z = inline!(&mut 3u32mut $(&mut 3u32)); - //~^ mut_mut + let mut z = inline!(&mut $(&mut 3u32)); } fn issue939() { diff --git a/tests/ui/mut_mut.rs b/tests/ui/mut_mut.rs index d006578b3d28..92ecfbfc02d1 100644 --- a/tests/ui/mut_mut.rs +++ b/tests/ui/mut_mut.rs @@ -50,7 +50,6 @@ fn main() { } let mut z = inline!(&mut $(&mut 3u32)); - //~^ mut_mut } fn issue939() { diff --git a/tests/ui/mut_mut.stderr b/tests/ui/mut_mut.stderr index f21de359e64f..5f7c9b4fe23f 100644 --- a/tests/ui/mut_mut.stderr +++ b/tests/ui/mut_mut.stderr @@ -13,14 +13,6 @@ error: an expression of form `&mut &mut _` LL | let mut x = &mut &mut 1u32; | ^^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut 1u32` -error: an expression of form `&mut &mut _` - --> tests/ui/mut_mut.rs:52:25 - | -LL | let mut z = inline!(&mut $(&mut 3u32)); - | ^ help: remove the extra `&mut`: `&mut 3u32` - | - = note: this error originates in the macro `__inline_mac_fn_main` (in Nightly builds, run with -Z macro-backtrace for more info) - error: this expression mutably borrows a mutable reference --> tests/ui/mut_mut.rs:35:21 | @@ -57,5 +49,5 @@ error: generally you want to avoid `&mut &mut _` if possible LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; | ^^^^^^^^^^^^^ -error: aborting due to 9 previous errors +error: aborting due to 8 previous errors From 729d92cf0405ef8ee06e76b9e59b4892caa173f0 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Mon, 4 Aug 2025 00:12:47 +0200 Subject: [PATCH 063/226] sugg on ty --- clippy_lints/src/mut_mut.rs | 16 +++++++++++----- tests/ui/mut_mut.stderr | 16 ++++++++-------- tests/ui/mut_mut_unfixable.stderr | 4 ++-- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/clippy_lints/src/mut_mut.rs b/clippy_lints/src/mut_mut.rs index 376d7214c267..7d7d4fa542c1 100644 --- a/clippy_lints/src/mut_mut.rs +++ b/clippy_lints/src/mut_mut.rs @@ -1,5 +1,6 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_hir_and_then}; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::higher; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use rustc_errors::Applicability; use rustc_hir::{self as hir, AmbigArg, intravisit}; @@ -37,15 +38,20 @@ impl<'tcx> LateLintPass<'tcx> for MutMut { 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 - && mty.mutbl == hir::Mutability::Mut + && let hir::TyKind::Ref(_, mty2) = mty.ty.kind + && mty2.mutbl == hir::Mutability::Mut && !ty.span.in_external_macro(cx.sess().source_map()) { - span_lint( + let mut applicability = Applicability::MaybeIncorrect; + let sugg = snippet_with_applicability(cx.sess(), mty.ty.span, "..", &mut applicability); + span_lint_and_sugg( cx, MUT_MUT, ty.span, - "generally you want to avoid `&mut &mut _` if possible", + "a type of form `&mut &mut _`", + "remove the extra `&mut`", + sugg.to_string(), + applicability, ); } } diff --git a/tests/ui/mut_mut.stderr b/tests/ui/mut_mut.stderr index 5f7c9b4fe23f..341ac643417c 100644 --- a/tests/ui/mut_mut.stderr +++ b/tests/ui/mut_mut.stderr @@ -1,8 +1,8 @@ -error: generally you want to avoid `&mut &mut _` if possible +error: a type of form `&mut &mut _` --> tests/ui/mut_mut.rs:15:11 | LL | fn fun(x: &mut &mut u32) { - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut u32` | = note: `-D clippy::mut-mut` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::mut_mut)]` @@ -25,11 +25,11 @@ error: an expression of form `&mut &mut _` LL | let y: &mut &mut u32 = &mut &mut 2; | ^^^^^^^^^^^ help: remove the extra `&mut`: `&mut 2` -error: generally you want to avoid `&mut &mut _` if possible +error: a type of form `&mut &mut _` --> tests/ui/mut_mut.rs:40:16 | LL | let y: &mut &mut u32 = &mut &mut 2; - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut u32` error: an expression of form `&mut &mut _` --> tests/ui/mut_mut.rs:46:37 @@ -37,17 +37,17 @@ error: an expression of form `&mut &mut _` LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; | ^^^^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut &mut 2` -error: generally you want to avoid `&mut &mut _` if possible +error: a type of form `&mut &mut _` --> tests/ui/mut_mut.rs:46:16 | LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; - | ^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut &mut u32` -error: generally you want to avoid `&mut &mut _` if possible +error: a type of form `&mut &mut _` --> tests/ui/mut_mut.rs:46:21 | LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut u32` error: aborting due to 8 previous errors diff --git a/tests/ui/mut_mut_unfixable.stderr b/tests/ui/mut_mut_unfixable.stderr index 129e5b24387e..da820f766d02 100644 --- a/tests/ui/mut_mut_unfixable.stderr +++ b/tests/ui/mut_mut_unfixable.stderr @@ -1,8 +1,8 @@ -error: generally you want to avoid `&mut &mut _` if possible +error: a type of form `&mut &mut _` --> tests/ui/mut_mut_unfixable.rs:9:11 | LL | fn fun(x: &mut &mut u32) -> bool { - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut u32` | = note: `-D clippy::mut-mut` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::mut_mut)]` From c6971c22c1450abaa9f4913db60725df85632e21 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 5 Aug 2025 12:37:20 +0200 Subject: [PATCH 064/226] only lint once on 3+ `&mut`s for some reason this seems to already be the case with exprs, so only do it for tys --- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/mut_mut.rs | 20 +++++++++++++++++--- tests/ui/mut_mut.fixed | 7 +++---- tests/ui/mut_mut.rs | 1 - tests/ui/mut_mut.stderr | 8 +------- 5 files changed, 22 insertions(+), 16 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 51dabee78e9f..733113fe0d33 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -482,7 +482,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::new(needless_for_each::NeedlessForEach)); store.register_late_pass(|_| Box::new(misc::LintPass)); store.register_late_pass(|_| Box::new(eta_reduction::EtaReduction)); - store.register_late_pass(|_| Box::new(mut_mut::MutMut)); + store.register_late_pass(|_| Box::new(mut_mut::MutMut::default())); store.register_late_pass(|_| Box::new(unnecessary_mut_passed::UnnecessaryMutPassed)); store.register_late_pass(|_| Box::>::default()); store.register_late_pass(|_| Box::new(len_zero::LenZero)); diff --git a/clippy_lints/src/mut_mut.rs b/clippy_lints/src/mut_mut.rs index 7d7d4fa542c1..968d99143c4b 100644 --- a/clippy_lints/src/mut_mut.rs +++ b/clippy_lints/src/mut_mut.rs @@ -2,11 +2,12 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::higher; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; -use rustc_hir::{self as hir, AmbigArg, intravisit}; +use rustc_hir::{self as hir, AmbigArg, HirId, intravisit}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty; -use rustc_session::declare_lint_pass; +use rustc_session::impl_lint_pass; declare_clippy_lint! { /// ### What it does @@ -28,7 +29,12 @@ declare_clippy_lint! { "usage of double-mut refs, e.g., `&mut &mut ...`" } -declare_lint_pass!(MutMut => [MUT_MUT]); +impl_lint_pass!(MutMut => [MUT_MUT]); + +#[derive(Default)] +pub(crate) struct MutMut { + seen_tys: FxHashSet, +} impl<'tcx> LateLintPass<'tcx> for MutMut { fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) { @@ -42,6 +48,14 @@ impl<'tcx> LateLintPass<'tcx> for MutMut { && mty2.mutbl == hir::Mutability::Mut && !ty.span.in_external_macro(cx.sess().source_map()) { + if self.seen_tys.contains(&ty.hir_id) { + // we have 2+ `&mut`s, e.g., `&mut &mut &mut x` + // and we have already flagged on the outermost `&mut &mut (&mut x)`, + // so don't flag the inner `&mut &mut (x)` + return; + } + self.seen_tys.insert(mty.ty.hir_id); + let mut applicability = Applicability::MaybeIncorrect; let sugg = snippet_with_applicability(cx.sess(), mty.ty.span, "..", &mut applicability); span_lint_and_sugg( diff --git a/tests/ui/mut_mut.fixed b/tests/ui/mut_mut.fixed index 137306064b43..d89129e70b00 100644 --- a/tests/ui/mut_mut.fixed +++ b/tests/ui/mut_mut.fixed @@ -12,7 +12,7 @@ extern crate proc_macros; use proc_macros::{external, inline_macros}; -fn fun(x: &mut &mut u32) { +fn fun(x: &mut u32) { //~^ mut_mut } @@ -37,16 +37,15 @@ fn main() { } { - let y: &mut &mut u32 = &mut 2; + let y: &mut u32 = &mut 2; //~^ mut_mut //~| mut_mut } { - let y: &mut &mut &mut u32 = &mut &mut 2; + let y: &mut &mut u32 = &mut &mut 2; //~^ mut_mut //~| mut_mut - //~| mut_mut } let mut z = inline!(&mut $(&mut 3u32)); diff --git a/tests/ui/mut_mut.rs b/tests/ui/mut_mut.rs index 92ecfbfc02d1..bbec48011a17 100644 --- a/tests/ui/mut_mut.rs +++ b/tests/ui/mut_mut.rs @@ -46,7 +46,6 @@ fn main() { let y: &mut &mut &mut u32 = &mut &mut &mut 2; //~^ mut_mut //~| mut_mut - //~| mut_mut } let mut z = inline!(&mut $(&mut 3u32)); diff --git a/tests/ui/mut_mut.stderr b/tests/ui/mut_mut.stderr index 341ac643417c..b30a64d8fe74 100644 --- a/tests/ui/mut_mut.stderr +++ b/tests/ui/mut_mut.stderr @@ -43,11 +43,5 @@ error: a type of form `&mut &mut _` LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; | ^^^^^^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut &mut u32` -error: a type of form `&mut &mut _` - --> tests/ui/mut_mut.rs:46:21 - | -LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; - | ^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut u32` - -error: aborting due to 8 previous errors +error: aborting due to 7 previous errors From 4053c446541940e878b7a9b4a92cb2c4696b3340 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 5 Aug 2025 13:31:04 +0200 Subject: [PATCH 065/226] misc: import more from `hir` --- clippy_lints/src/mut_mut.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/mut_mut.rs b/clippy_lints/src/mut_mut.rs index 968d99143c4b..574c0c8f8de0 100644 --- a/clippy_lints/src/mut_mut.rs +++ b/clippy_lints/src/mut_mut.rs @@ -4,7 +4,7 @@ use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; -use rustc_hir::{self as hir, AmbigArg, HirId, intravisit}; +use rustc_hir::{self as hir, AmbigArg, BorrowKind, Expr, ExprKind, HirId, Mutability, TyKind, intravisit}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty; use rustc_session::impl_lint_pass; @@ -42,10 +42,10 @@ impl<'tcx> LateLintPass<'tcx> for MutMut { } 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(_, mty2) = mty.ty.kind - && mty2.mutbl == hir::Mutability::Mut + if let TyKind::Ref(_, mty) = ty.kind + && mty.mutbl == Mutability::Mut + && let TyKind::Ref(_, mty2) = mty.ty.kind + && mty2.mutbl == Mutability::Mut && !ty.span.in_external_macro(cx.sess().source_map()) { if self.seen_tys.contains(&ty.hir_id) { @@ -76,7 +76,7 @@ pub struct MutVisitor<'a, 'tcx> { } impl<'tcx> intravisit::Visitor<'tcx> for MutVisitor<'_, 'tcx> { - fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { if expr.span.in_external_macro(self.cx.sess().source_map()) { return; } @@ -90,8 +90,8 @@ impl<'tcx> intravisit::Visitor<'tcx> for MutVisitor<'_, 'tcx> { // Let's ignore the generated code. intravisit::walk_expr(self, arg); intravisit::walk_expr(self, body); - } else if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, e) = expr.kind { - if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, _) = e.kind { + } else if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, e) = expr.kind { + if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, _) = e.kind { if !expr.span.eq_ctxt(e.span) { return; } @@ -107,7 +107,7 @@ impl<'tcx> intravisit::Visitor<'tcx> for MutVisitor<'_, 'tcx> { diag.span_suggestion(expr.span, "remove the extra `&mut`", sugg, applicability); }, ); - } else if let ty::Ref(_, ty, hir::Mutability::Mut) = self.cx.typeck_results().expr_ty(e).kind() + } else if let ty::Ref(_, ty, Mutability::Mut) = self.cx.typeck_results().expr_ty(e).kind() && ty.peel_refs().is_sized(self.cx.tcx, self.cx.typing_env()) { let mut applicability = Applicability::MaybeIncorrect; From 9a1b2bcc9f82f14e8a5215a2a7b9229c7f1df390 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 5 Aug 2025 13:49:43 +0200 Subject: [PATCH 066/226] peel _all_ extra `&mut`s see https://rust-lang.zulipchat.com/#narrow/channel/257328-clippy/topic/problem.20when.20suggestion.20only.20removes.20one.20.22level.22.20of.20a.20lint/near/532889973 --- clippy_lints/src/mut_mut.rs | 47 ++++++++++++++++++++++++++++--- tests/ui/mut_mut.fixed | 2 +- tests/ui/mut_mut.stderr | 4 +-- tests/ui/mut_mut_unfixable.stderr | 2 +- 4 files changed, 47 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/mut_mut.rs b/clippy_lints/src/mut_mut.rs index 574c0c8f8de0..d66daf0c785f 100644 --- a/clippy_lints/src/mut_mut.rs +++ b/clippy_lints/src/mut_mut.rs @@ -56,14 +56,30 @@ impl<'tcx> LateLintPass<'tcx> for MutMut { } self.seen_tys.insert(mty.ty.hir_id); + // if there is an even longer chain, like `&mut &mut &mut x`, suggest peeling off + // all extra ones at once + let (mut t, mut t2) = (mty.ty, mty2.ty); + let mut many_muts = false; + loop { + if let TyKind::Ref(_, next) = t2.kind + && next.mutbl == Mutability::Mut + { + (t, t2) = (t2, next.ty); + many_muts = true; + } else { + break; + } + } + let mut applicability = Applicability::MaybeIncorrect; - let sugg = snippet_with_applicability(cx.sess(), mty.ty.span, "..", &mut applicability); + let sugg = snippet_with_applicability(cx.sess(), t.span, "..", &mut applicability); + let suffix = if many_muts { "s" } else { "" }; span_lint_and_sugg( cx, MUT_MUT, ty.span, "a type of form `&mut &mut _`", - "remove the extra `&mut`", + format!("remove the extra `&mut`{suffix}"), sugg.to_string(), applicability, ); @@ -91,12 +107,30 @@ impl<'tcx> intravisit::Visitor<'tcx> for MutVisitor<'_, 'tcx> { intravisit::walk_expr(self, arg); intravisit::walk_expr(self, body); } else if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, e) = expr.kind { - if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, _) = e.kind { + if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, e2) = e.kind { if !expr.span.eq_ctxt(e.span) { return; } + + // if there is an even longer chain, like `&mut &mut &mut x`, suggest peeling off + // all extra ones at once + let (mut e, mut e2) = (e, e2); + let mut many_muts = false; + loop { + if !e.span.eq_ctxt(e2.span) { + return; + } + if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, next) = e2.kind { + (e, e2) = (e2, next); + many_muts = true; + } else { + break; + } + } + let mut applicability = Applicability::MaybeIncorrect; let sugg = Sugg::hir_with_applicability(self.cx, e, "..", &mut applicability); + let suffix = if many_muts { "s" } else { "" }; span_lint_hir_and_then( self.cx, MUT_MUT, @@ -104,7 +138,12 @@ impl<'tcx> intravisit::Visitor<'tcx> for MutVisitor<'_, 'tcx> { expr.span, "an expression of form `&mut &mut _`", |diag| { - diag.span_suggestion(expr.span, "remove the extra `&mut`", sugg, applicability); + diag.span_suggestion( + expr.span, + format!("remove the extra `&mut`{suffix}"), + sugg, + applicability, + ); }, ); } else if let ty::Ref(_, ty, Mutability::Mut) = self.cx.typeck_results().expr_ty(e).kind() diff --git a/tests/ui/mut_mut.fixed b/tests/ui/mut_mut.fixed index d89129e70b00..f9a7f5dcb5a1 100644 --- a/tests/ui/mut_mut.fixed +++ b/tests/ui/mut_mut.fixed @@ -43,7 +43,7 @@ fn main() { } { - let y: &mut &mut u32 = &mut &mut 2; + let y: &mut u32 = &mut 2; //~^ mut_mut //~| mut_mut } diff --git a/tests/ui/mut_mut.stderr b/tests/ui/mut_mut.stderr index b30a64d8fe74..85e9c5649b89 100644 --- a/tests/ui/mut_mut.stderr +++ b/tests/ui/mut_mut.stderr @@ -35,13 +35,13 @@ error: an expression of form `&mut &mut _` --> tests/ui/mut_mut.rs:46:37 | LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; - | ^^^^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut &mut 2` + | ^^^^^^^^^^^^^^^^ help: remove the extra `&mut`s: `&mut 2` error: a type of form `&mut &mut _` --> tests/ui/mut_mut.rs:46:16 | LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; - | ^^^^^^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut &mut u32` + | ^^^^^^^^^^^^^^^^^^ help: remove the extra `&mut`s: `&mut u32` error: aborting due to 7 previous errors diff --git a/tests/ui/mut_mut_unfixable.stderr b/tests/ui/mut_mut_unfixable.stderr index da820f766d02..e967ed353725 100644 --- a/tests/ui/mut_mut_unfixable.stderr +++ b/tests/ui/mut_mut_unfixable.stderr @@ -29,7 +29,7 @@ error: an expression of form `&mut &mut _` --> tests/ui/mut_mut_unfixable.rs:30:17 | LL | let y = &mut &mut &mut 2; - | ^^^^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut &mut 2` + | ^^^^^^^^^^^^^^^^ help: remove the extra `&mut`s: `&mut 2` error: aborting due to 5 previous errors From 554680683f69f142e97d502b941c0ce8797ad0c6 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 5 Aug 2025 14:06:50 +0200 Subject: [PATCH 067/226] remember all the nested types while we loop see https://rust-lang.zulipchat.com/#narrow/channel/25732 --- clippy_lints/src/mut_mut.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/mut_mut.rs b/clippy_lints/src/mut_mut.rs index d66daf0c785f..75b8aa9c90cc 100644 --- a/clippy_lints/src/mut_mut.rs +++ b/clippy_lints/src/mut_mut.rs @@ -54,13 +54,15 @@ impl<'tcx> LateLintPass<'tcx> for MutMut { // so don't flag the inner `&mut &mut (x)` return; } - self.seen_tys.insert(mty.ty.hir_id); // if there is an even longer chain, like `&mut &mut &mut x`, suggest peeling off // all extra ones at once let (mut t, mut t2) = (mty.ty, mty2.ty); let mut many_muts = false; loop { + // this should allow us to remember all the nested types, so that the `contains` + // above fails faster + self.seen_tys.insert(t.hir_id); if let TyKind::Ref(_, next) = t2.kind && next.mutbl == Mutability::Mut { From 75b68606bc11529c2dd0e0f12dd317b74d40409c Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 5 Aug 2025 14:12:41 +0200 Subject: [PATCH 068/226] add an unfixable case as recommended in https://rust-lang.zulipchat.com/#narrow/channel/257328-clippy/topic/problem.20when.20suggestion.20only.20removes.20one.20.22level.22.20of.20a.20lint/near/532894687 --- tests/ui/mut_mut_unfixable.rs | 8 ++++++++ tests/ui/mut_mut_unfixable.stderr | 8 +++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/tests/ui/mut_mut_unfixable.rs b/tests/ui/mut_mut_unfixable.rs index db0552a86ec9..271cb7b96889 100644 --- a/tests/ui/mut_mut_unfixable.rs +++ b/tests/ui/mut_mut_unfixable.rs @@ -31,4 +31,12 @@ fn main() { //~^ mut_mut ***y + **x; } + + if fun(x) { + // The lint will remove the extra `&mut`, but the result will still be a `&mut` of an expr + // of type `&mut _` (x), so the lint will fire again. That's because we've decided that + // doing both fixes in one run is not worth it, given how improbable code like this is. + let y = &mut &mut x; + //~^ mut_mut + } } diff --git a/tests/ui/mut_mut_unfixable.stderr b/tests/ui/mut_mut_unfixable.stderr index e967ed353725..cf66eb2ed1ec 100644 --- a/tests/ui/mut_mut_unfixable.stderr +++ b/tests/ui/mut_mut_unfixable.stderr @@ -31,5 +31,11 @@ error: an expression of form `&mut &mut _` LL | let y = &mut &mut &mut 2; | ^^^^^^^^^^^^^^^^ help: remove the extra `&mut`s: `&mut 2` -error: aborting due to 5 previous errors +error: an expression of form `&mut &mut _` + --> tests/ui/mut_mut_unfixable.rs:39:17 + | +LL | let y = &mut &mut x; + | ^^^^^^^^^^^ help: remove the extra `&mut`: `&mut x` + +error: aborting due to 6 previous errors From dc534c49b3ce818ade236527149a94a51ea91a34 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 5 Aug 2025 14:14:41 +0200 Subject: [PATCH 069/226] doc: add more examples, and a "Use instead" section, don't be condescending --- clippy_lints/src/mut_mut.rs | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/mut_mut.rs b/clippy_lints/src/mut_mut.rs index 75b8aa9c90cc..588afd85afb0 100644 --- a/clippy_lints/src/mut_mut.rs +++ b/clippy_lints/src/mut_mut.rs @@ -14,19 +14,30 @@ declare_clippy_lint! { /// Checks for instances of `mut mut` references. /// /// ### Why is this bad? - /// Multiple `mut`s don't add anything meaningful to the - /// source. This is either a copy'n'paste error, or it shows a fundamental - /// misunderstanding of references. + /// This is usually just a typo or a misunderstanding of how references work. /// /// ### Example /// ```no_run - /// # let mut y = 1; - /// let x = &mut &mut y; + /// let x = &mut &mut 1; + /// + /// let mut x = &mut 1; + /// let y = &mut x; + /// + /// fn foo(x: &mut &mut u32) {} + /// ``` + /// Use instead + /// ```no_run + /// let x = &mut 1; + /// + /// let mut x = &mut 1; + /// let y = &mut *x; // reborrow + /// + /// fn foo(x: &mut u32) {} /// ``` #[clippy::version = "pre 1.29.0"] pub MUT_MUT, pedantic, - "usage of double-mut refs, e.g., `&mut &mut ...`" + "usage of double mut-refs, e.g., `&mut &mut ...`" } impl_lint_pass!(MutMut => [MUT_MUT]); From 6b4febeeb7be931c9317bf987604bbd94967cd94 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 28 Sep 2025 15:39:57 +0200 Subject: [PATCH 070/226] simplify and inline `is_async_fn` --- clippy_lints/src/cognitive_complexity.rs | 4 ++-- clippy_lints/src/implicit_return.rs | 4 ++-- clippy_utils/src/lib.rs | 11 +---------- 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs index 7646aa48b772..c0f30e456d8d 100644 --- a/clippy_lints/src/cognitive_complexity.rs +++ b/clippy_lints/src/cognitive_complexity.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::{IntoSpan, SpanRangeExt}; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::for_each_expr_without_closures; -use clippy_utils::{LimitStack, get_async_fn_body, is_async_fn, sym}; +use clippy_utils::{LimitStack, get_async_fn_body, sym}; use core::ops::ControlFlow; use rustc_hir::intravisit::FnKind; use rustc_hir::{Attribute, Body, Expr, ExprKind, FnDecl}; @@ -147,7 +147,7 @@ impl<'tcx> LateLintPass<'tcx> for CognitiveComplexity { def_id: LocalDefId, ) { if !cx.tcx.has_attr(def_id, sym::test) { - let expr = if is_async_fn(kind) { + let expr = if kind.asyncness().is_async() { match get_async_fn_body(cx.tcx, body) { Some(b) => b, None => { diff --git a/clippy_lints/src/implicit_return.rs b/clippy_lints/src/implicit_return.rs index 076017a247b4..6ed478b2708a 100644 --- a/clippy_lints/src/implicit_return.rs +++ b/clippy_lints/src/implicit_return.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::source::{snippet_with_applicability, snippet_with_context, walk_span_to_context}; use clippy_utils::visitors::for_each_expr_without_closures; -use clippy_utils::{desugar_await, get_async_closure_expr, get_async_fn_body, is_async_fn, is_from_proc_macro}; +use clippy_utils::{desugar_await, get_async_closure_expr, get_async_fn_body, is_from_proc_macro}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; @@ -240,7 +240,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitReturn { return; } - let expr = if is_async_fn(kind) { + let expr = if kind.asyncness().is_async() { match get_async_fn_body(cx.tcx, body) { Some(e) => e, None => return, diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index d6586625a1e5..708491df7707 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -98,7 +98,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; use rustc_hir::definitions::{DefPath, DefPathData}; use rustc_hir::hir_id::{HirIdMap, HirIdSet}; -use rustc_hir::intravisit::{FnKind, Visitor, walk_expr}; +use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{ self as hir, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg, GenericArgs, @@ -1854,15 +1854,6 @@ pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, (conds, blocks) } -/// Checks if the given function kind is an async function. -pub fn is_async_fn(kind: FnKind<'_>) -> bool { - match kind { - FnKind::ItemFn(_, _, header) => header.asyncness.is_async(), - FnKind::Method(_, sig) => sig.header.asyncness.is_async(), - FnKind::Closure => false, - } -} - /// Peels away all the compiler generated code surrounding the body of an async closure. pub fn get_async_closure_expr<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> { if let ExprKind::Closure(&Closure { From 30f284d15befc66143fee59918ffac209a0ae657 Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Sun, 28 Sep 2025 14:40:39 -0700 Subject: [PATCH 071/226] remove explicit deref of AbiAlign for most methods Much of the compiler calls functions on Align projected from AbiAlign. AbiAlign impls Deref to its inner Align, so we can simplify these away. Also, it will minimize disruption when AbiAlign is removed. For now, preserve usages that might resolve to PartialOrd or PartialEq, as those have odd inference. --- clippy_lints/src/casts/cast_ptr_alignment.rs | 4 ++-- clippy_lints/src/casts/manual_dangling_ptr.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/casts/cast_ptr_alignment.rs b/clippy_lints/src/casts/cast_ptr_alignment.rs index d78da9396faf..7d14ba7fcf13 100644 --- a/clippy_lints/src/casts/cast_ptr_alignment.rs +++ b/clippy_lints/src/casts/cast_ptr_alignment.rs @@ -43,8 +43,8 @@ fn lint_cast_ptr_alignment<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, cast_f expr.span, format!( "casting from `{cast_from}` to a more-strictly-aligned pointer (`{cast_to}`) ({} < {} bytes)", - from_layout.align.abi.bytes(), - to_layout.align.abi.bytes(), + from_layout.align.bytes(), + to_layout.align.bytes(), ), ); } diff --git a/clippy_lints/src/casts/manual_dangling_ptr.rs b/clippy_lints/src/casts/manual_dangling_ptr.rs index 92910cf8adf5..ff5320719aa2 100644 --- a/clippy_lints/src/casts/manual_dangling_ptr.rs +++ b/clippy_lints/src/casts/manual_dangling_ptr.rs @@ -72,7 +72,7 @@ fn is_literal_aligned(cx: &LateContext<'_>, lit: &Spanned, to: &Ty<'_>) cx.tcx .layout_of(cx.typing_env().as_query_input(to_mid_ty)) .is_ok_and(|layout| { - let align = u128::from(layout.align.abi.bytes()); + let align = u128::from(layout.align.bytes()); u128::from(val) <= align }) } From f6e223cdb5b848f2eca1ce06aac9f9f1b764abf2 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Mon, 29 Sep 2025 00:21:27 +0200 Subject: [PATCH 072/226] clean-up --- clippy_lints/src/methods/mod.rs | 181 ++++++++---------- .../src/methods/wrong_self_convention.rs | 36 ++-- 2 files changed, 97 insertions(+), 120 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index a1cdab9cc491..195d91c4e66b 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -149,13 +149,12 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::macros::FormatArgsStorage; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item}; +use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy}; use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, peel_blocks, return_ty, sym}; pub use path_ends_with_ext::DEFAULT_ALLOWED_DOTFILES; use rustc_abi::ExternAbi; use rustc_data_structures::fx::FxHashSet; -use rustc_hir as hir; -use rustc_hir::{Expr, ExprKind, Node, Stmt, StmtKind, TraitItem, TraitItemKind}; +use rustc_hir::{self as hir, Expr, ExprKind, Node, Stmt, StmtKind, TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::{self, TraitRef, Ty}; use rustc_session::impl_lint_pass; @@ -4889,13 +4888,13 @@ impl<'tcx> LateLintPass<'tcx> for Methods { if impl_item.span.in_external_macro(cx.sess().source_map()) { return; } - let name = impl_item.ident.name; - let parent = cx.tcx.hir_get_parent_item(impl_item.hir_id()).def_id; - let item = cx.tcx.hir_expect_item(parent); - let self_ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); - - let implements_trait = matches!(item.kind, hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. })); if let hir::ImplItemKind::Fn(ref sig, id) = impl_item.kind { + let name = impl_item.ident.name; + let parent = cx.tcx.hir_get_parent_item(impl_item.hir_id()).def_id; + let item = cx.tcx.hir_expect_item(parent); + let self_ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); + + let implements_trait = matches!(item.kind, hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. })); let method_sig = cx.tcx.fn_sig(impl_item.owner_id).instantiate_identity(); let method_sig = cx.tcx.instantiate_bound_regions_with_erased(method_sig); let first_arg_ty_opt = method_sig.inputs().iter().next().copied(); @@ -4908,9 +4907,8 @@ impl<'tcx> LateLintPass<'tcx> for Methods { && method_config.output_type.matches(&sig.decl.output) // in case there is no first arg, since we already have checked the number of arguments // it's should be always true - && first_arg_ty_opt.is_none_or(|first_arg_ty| method_config - .self_kind.matches(cx, self_ty, first_arg_ty) - ) + && first_arg_ty_opt + .is_none_or(|first_arg_ty| method_config.self_kind.matches(cx, self_ty, first_arg_ty)) && fn_header_equals(method_config.fn_header, sig.header) && method_config.lifetime_param_cond(impl_item) { @@ -4948,21 +4946,14 @@ impl<'tcx> LateLintPass<'tcx> for Methods { false, ); } - } - // if this impl block implements a trait, lint in trait definition instead - if implements_trait { - return; - } - - if let hir::ImplItemKind::Fn(_, _) = impl_item.kind { - let ret_ty = return_ty(cx, impl_item.owner_id); - - if contains_ty_adt_constructor_opaque(cx, ret_ty, self_ty) { - return; - } - - if name == sym::new && ret_ty != self_ty { + // if this impl block implements a trait, lint in trait definition instead + if !implements_trait + && impl_item.ident.name == sym::new + && let ret_ty = return_ty(cx, impl_item.owner_id) + && ret_ty != self_ty + && !contains_ty_adt_constructor_opaque(cx, ret_ty, self_ty) + { span_lint( cx, NEW_RET_NO_SELF, @@ -4978,41 +4969,41 @@ impl<'tcx> LateLintPass<'tcx> for Methods { return; } - if let TraitItemKind::Fn(ref sig, _) = item.kind - && sig.decl.implicit_self.has_implicit_self() - && let Some(first_arg_hir_ty) = sig.decl.inputs.first() - && let Some(&first_arg_ty) = cx - .tcx - .fn_sig(item.owner_id) - .instantiate_identity() - .inputs() - .skip_binder() - .first() - { - let self_ty = TraitRef::identity(cx.tcx, item.owner_id.to_def_id()).self_ty(); - wrong_self_convention::check( - cx, - item.ident.name, - self_ty, - first_arg_ty, - first_arg_hir_ty.span, - false, - true, - ); - } + if let TraitItemKind::Fn(ref sig, _) = item.kind { + if sig.decl.implicit_self.has_implicit_self() + && let Some(first_arg_hir_ty) = sig.decl.inputs.first() + && let Some(&first_arg_ty) = cx + .tcx + .fn_sig(item.owner_id) + .instantiate_identity() + .inputs() + .skip_binder() + .first() + { + let self_ty = TraitRef::identity(cx.tcx, item.owner_id.to_def_id()).self_ty(); + wrong_self_convention::check( + cx, + item.ident.name, + self_ty, + first_arg_ty, + first_arg_hir_ty.span, + false, + true, + ); + } - if item.ident.name == sym::new - && let TraitItemKind::Fn(_, _) = item.kind - && let ret_ty = return_ty(cx, item.owner_id) - && let self_ty = TraitRef::identity(cx.tcx, item.owner_id.to_def_id()).self_ty() - && !ret_ty.contains(self_ty) - { - span_lint( - cx, - NEW_RET_NO_SELF, - item.span, - "methods called `new` usually return `Self`", - ); + if item.ident.name == sym::new + && let ret_ty = return_ty(cx, item.owner_id) + && let self_ty = TraitRef::identity(cx.tcx, item.owner_id.to_def_id()).self_ty() + && !ret_ty.contains(self_ty) + { + span_lint( + cx, + NEW_RET_NO_SELF, + item.span, + "methods called `new` usually return `Self`", + ); + } } } } @@ -5776,36 +5767,36 @@ impl ShouldImplTraitCase { #[rustfmt::skip] const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [ - ShouldImplTraitCase::new("std::ops::Add", sym::add, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::convert::AsMut", sym::as_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), - ShouldImplTraitCase::new("std::convert::AsRef", sym::as_ref, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true), - ShouldImplTraitCase::new("std::ops::BitAnd", sym::bitand, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::BitOr", sym::bitor, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::BitXor", sym::bitxor, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::borrow::Borrow", sym::borrow, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true), - ShouldImplTraitCase::new("std::borrow::BorrowMut", sym::borrow_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), - ShouldImplTraitCase::new("std::clone::Clone", sym::clone, 1, FN_HEADER, SelfKind::Ref, OutType::Any, true), - ShouldImplTraitCase::new("std::cmp::Ord", sym::cmp, 2, FN_HEADER, SelfKind::Ref, OutType::Any, true), - ShouldImplTraitCase::new("std::default::Default", kw::Default, 0, FN_HEADER, SelfKind::No, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Deref", sym::deref, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true), - ShouldImplTraitCase::new("std::ops::DerefMut", sym::deref_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), - ShouldImplTraitCase::new("std::ops::Div", sym::div, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Drop", sym::drop, 1, FN_HEADER, SelfKind::RefMut, OutType::Unit, true), - ShouldImplTraitCase::new("std::cmp::PartialEq", sym::eq, 2, FN_HEADER, SelfKind::Ref, OutType::Bool, true), - ShouldImplTraitCase::new("std::iter::FromIterator", sym::from_iter, 1, FN_HEADER, SelfKind::No, OutType::Any, true), - ShouldImplTraitCase::new("std::str::FromStr", sym::from_str, 1, FN_HEADER, SelfKind::No, OutType::Any, true), - ShouldImplTraitCase::new("std::hash::Hash", sym::hash, 2, FN_HEADER, SelfKind::Ref, OutType::Unit, true), - ShouldImplTraitCase::new("std::ops::Index", sym::index, 2, FN_HEADER, SelfKind::Ref, OutType::Ref, true), - ShouldImplTraitCase::new("std::ops::IndexMut", sym::index_mut, 2, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), - ShouldImplTraitCase::new("std::iter::IntoIterator", sym::into_iter, 1, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Mul", sym::mul, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Neg", sym::neg, 1, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::iter::Iterator", sym::next, 1, FN_HEADER, SelfKind::RefMut, OutType::Any, false), - ShouldImplTraitCase::new("std::ops::Not", sym::not, 1, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Rem", sym::rem, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Shl", sym::shl, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Shr", sym::shr, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Sub", sym::sub, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Add", sym::add, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), + ShouldImplTraitCase::new("std::convert::AsMut", sym::as_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true ), + ShouldImplTraitCase::new("std::convert::AsRef", sym::as_ref, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true ), + ShouldImplTraitCase::new("std::ops::BitAnd", sym::bitand, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), + ShouldImplTraitCase::new("std::ops::BitOr", sym::bitor, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), + ShouldImplTraitCase::new("std::ops::BitXor", sym::bitxor, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), + ShouldImplTraitCase::new("std::borrow::Borrow", sym::borrow, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true ), + ShouldImplTraitCase::new("std::borrow::BorrowMut", sym::borrow_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true ), + ShouldImplTraitCase::new("std::clone::Clone", sym::clone, 1, FN_HEADER, SelfKind::Ref, OutType::Any, true ), + ShouldImplTraitCase::new("std::cmp::Ord", sym::cmp, 2, FN_HEADER, SelfKind::Ref, OutType::Any, true ), + ShouldImplTraitCase::new("std::default::Default", kw::Default, 0, FN_HEADER, SelfKind::No, OutType::Any, true ), + ShouldImplTraitCase::new("std::ops::Deref", sym::deref, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true ), + ShouldImplTraitCase::new("std::ops::DerefMut", sym::deref_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true ), + ShouldImplTraitCase::new("std::ops::Div", sym::div, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), + ShouldImplTraitCase::new("std::ops::Drop", sym::drop, 1, FN_HEADER, SelfKind::RefMut, OutType::Unit, true ), + ShouldImplTraitCase::new("std::cmp::PartialEq", sym::eq, 2, FN_HEADER, SelfKind::Ref, OutType::Bool, true ), + ShouldImplTraitCase::new("std::iter::FromIterator", sym::from_iter, 1, FN_HEADER, SelfKind::No, OutType::Any, true ), + ShouldImplTraitCase::new("std::str::FromStr", sym::from_str, 1, FN_HEADER, SelfKind::No, OutType::Any, true ), + ShouldImplTraitCase::new("std::hash::Hash", sym::hash, 2, FN_HEADER, SelfKind::Ref, OutType::Unit, true ), + ShouldImplTraitCase::new("std::ops::Index", sym::index, 2, FN_HEADER, SelfKind::Ref, OutType::Ref, true ), + ShouldImplTraitCase::new("std::ops::IndexMut", sym::index_mut, 2, FN_HEADER, SelfKind::RefMut, OutType::Ref, true ), + ShouldImplTraitCase::new("std::iter::IntoIterator", sym::into_iter, 1, FN_HEADER, SelfKind::Value, OutType::Any, true ), + ShouldImplTraitCase::new("std::ops::Mul", sym::mul, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), + ShouldImplTraitCase::new("std::ops::Neg", sym::neg, 1, FN_HEADER, SelfKind::Value, OutType::Any, true ), + ShouldImplTraitCase::new("std::iter::Iterator", sym::next, 1, FN_HEADER, SelfKind::RefMut, OutType::Any, false), + ShouldImplTraitCase::new("std::ops::Not", sym::not, 1, FN_HEADER, SelfKind::Value, OutType::Any, true ), + ShouldImplTraitCase::new("std::ops::Rem", sym::rem, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), + ShouldImplTraitCase::new("std::ops::Shl", sym::shl, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), + ShouldImplTraitCase::new("std::ops::Shr", sym::shr, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), + ShouldImplTraitCase::new("std::ops::Sub", sym::sub, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), ]; #[derive(Clone, Copy, PartialEq, Eq, Debug)] @@ -5823,12 +5814,10 @@ impl SelfKind { true } else if let Some(boxed_ty) = ty.boxed_ty() { boxed_ty == parent_ty - } else if is_type_diagnostic_item(cx, ty, sym::Rc) || is_type_diagnostic_item(cx, ty, sym::Arc) { - if let ty::Adt(_, args) = ty.kind() { - args.types().next() == Some(parent_ty) - } else { - false - } + } else if let ty::Adt(adt, args) = ty.kind() + && matches!(cx.tcx.get_diagnostic_name(adt.did()), Some(sym::Rc | sym::Arc)) + { + args.types().next() == Some(parent_ty) } else { false } diff --git a/clippy_lints/src/methods/wrong_self_convention.rs b/clippy_lints/src/methods/wrong_self_convention.rs index ad9b3c364542..249119d549ba 100644 --- a/clippy_lints/src/methods/wrong_self_convention.rs +++ b/clippy_lints/src/methods/wrong_self_convention.rs @@ -1,6 +1,7 @@ use crate::methods::SelfKind; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::is_copy; +use itertools::Itertools; use rustc_lint::LateContext; use rustc_middle::ty::Ty; use rustc_span::{Span, Symbol}; @@ -61,20 +62,20 @@ impl Convention { impl fmt::Display for Convention { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { match *self { - Self::Eq(this) => format!("`{this}`").fmt(f), - Self::StartsWith(this) => format!("`{this}*`").fmt(f), - Self::EndsWith(this) => format!("`*{this}`").fmt(f), - Self::NotEndsWith(this) => format!("`~{this}`").fmt(f), + Self::Eq(this) => write!(f, "`{this}`"), + Self::StartsWith(this) => write!(f, "`{this}*`"), + Self::EndsWith(this) => write!(f, "`*{this}`"), + Self::NotEndsWith(this) => write!(f, "`~{this}`"), Self::IsSelfTypeCopy(is_true) => { - format!("`self` type is{} `Copy`", if is_true { "" } else { " not" }).fmt(f) + write!(f, "`self` type is{} `Copy`", if is_true { "" } else { " not" }) }, Self::ImplementsTrait(is_true) => { let (negation, s_suffix) = if is_true { ("", "s") } else { (" does not", "") }; - format!("method{negation} implement{s_suffix} a trait").fmt(f) + write!(f, "method{negation} implement{s_suffix} a trait") }, Self::IsTraitItem(is_true) => { let suffix = if is_true { " is" } else { " is not" }; - format!("method{suffix} a trait item").fmt(f) + write!(f, "method{suffix} a trait item") }, } } @@ -115,18 +116,9 @@ pub(super) fn check<'tcx>( let s = conventions .iter() - .filter_map(|conv| { - if (cut_ends_with_conv && matches!(conv, Convention::NotEndsWith(_))) - || matches!(conv, Convention::ImplementsTrait(_)) - || matches!(conv, Convention::IsTraitItem(_)) - { - None - } else { - Some(conv.to_string()) - } - }) - .collect::>() - .join(" and "); + .filter(|conv| !(cut_ends_with_conv && matches!(conv, Convention::NotEndsWith(_)))) + .filter(|conv| !matches!(conv, Convention::ImplementsTrait(_) | Convention::IsTraitItem(_))) + .format(" and "); format!("methods with the following characteristics: ({s})") } else { @@ -140,11 +132,7 @@ pub(super) fn check<'tcx>( first_arg_span, format!( "{suggestion} usually take {}", - &self_kinds - .iter() - .map(|k| k.description()) - .collect::>() - .join(" or ") + self_kinds.iter().map(|k| k.description()).format(" or ") ), None, "consider choosing a less ambiguous name", From 26a9a371ee3fa8731815cbff5b397d66457afc4c Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 28 Sep 2025 23:46:30 +0200 Subject: [PATCH 073/226] clean-up: extract `should_implement_trait` Also put `SelfKind` into `lib.rs`, since it's shared with `wrong_self_convention` -- not `utils.rs`, because it's not really a utility function --- clippy_lints/src/methods/lib.rs | 70 ++++++ clippy_lints/src/methods/mod.rs | 229 +----------------- .../src/methods/should_implement_trait.rs | 165 +++++++++++++ .../src/methods/wrong_self_convention.rs | 2 +- 4 files changed, 247 insertions(+), 219 deletions(-) create mode 100644 clippy_lints/src/methods/lib.rs create mode 100644 clippy_lints/src/methods/should_implement_trait.rs diff --git a/clippy_lints/src/methods/lib.rs b/clippy_lints/src/methods/lib.rs new file mode 100644 index 000000000000..84038283bcf8 --- /dev/null +++ b/clippy_lints/src/methods/lib.rs @@ -0,0 +1,70 @@ +use clippy_utils::sym; +use clippy_utils::ty::{implements_trait, is_copy}; +use rustc_hir::Mutability; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty}; + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub(super) enum SelfKind { + Value, + Ref, + RefMut, + No, // When we want the first argument type to be different than `Self` +} + +impl SelfKind { + pub(super) fn matches<'a>(self, cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool { + fn matches_value<'a>(cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool { + if ty == parent_ty { + true + } else if let Some(boxed_ty) = ty.boxed_ty() { + boxed_ty == parent_ty + } else if let ty::Adt(adt_def, args) = ty.kind() + && matches!(cx.tcx.get_diagnostic_name(adt_def.did()), Some(sym::Rc | sym::Arc)) + { + args.types().next() == Some(parent_ty) + } else { + false + } + } + + fn matches_ref<'a>(cx: &LateContext<'a>, mutability: Mutability, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool { + if let ty::Ref(_, t, m) = *ty.kind() { + return m == mutability && t == parent_ty; + } + + let trait_sym = match mutability { + Mutability::Not => sym::AsRef, + Mutability::Mut => sym::AsMut, + }; + + let Some(trait_def_id) = cx.tcx.get_diagnostic_item(trait_sym) else { + return false; + }; + implements_trait(cx, ty, trait_def_id, &[parent_ty.into()]) + } + + fn matches_none<'a>(cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool { + !matches_value(cx, parent_ty, ty) + && !matches_ref(cx, Mutability::Not, parent_ty, ty) + && !matches_ref(cx, Mutability::Mut, parent_ty, ty) + } + + match self { + Self::Value => matches_value(cx, parent_ty, ty), + Self::Ref => matches_ref(cx, Mutability::Not, parent_ty, ty) || ty == parent_ty && is_copy(cx, ty), + Self::RefMut => matches_ref(cx, Mutability::Mut, parent_ty, ty), + Self::No => matches_none(cx, parent_ty, ty), + } + } + + #[must_use] + pub(super) fn description(self) -> &'static str { + match self { + Self::Value => "`self` by value", + Self::Ref => "`self` by reference", + Self::RefMut => "`self` by mutable reference", + Self::No => "no `self`", + } + } +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 195d91c4e66b..8c2948e4a74e 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -55,6 +55,7 @@ mod iter_skip_zero; mod iter_with_drain; mod iterator_step_by_zero; mod join_absolute_paths; +mod lib; mod manual_c_str_literals; mod manual_contains; mod manual_inspect; @@ -102,6 +103,7 @@ mod return_and_then; mod search_is_some; mod seek_from_current; mod seek_to_start_instead_of_rewind; +mod should_implement_trait; mod single_char_add_str; mod skip_while_next; mod sliced_string_as_bytes; @@ -146,19 +148,18 @@ mod zst_offset; use clippy_config::Conf; use clippy_utils::consts::{ConstEvalCtxt, Constant}; -use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use clippy_utils::diagnostics::span_lint; use clippy_utils::macros::FormatArgsStorage; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy}; -use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, peel_blocks, return_ty, sym}; +use clippy_utils::ty::contains_ty_adt_constructor_opaque; +use clippy_utils::{contains_return, is_trait_method, iter_input_pats, peel_blocks, return_ty, sym}; pub use path_ends_with_ext::DEFAULT_ALLOWED_DOTFILES; -use rustc_abi::ExternAbi; use rustc_data_structures::fx::FxHashSet; use rustc_hir::{self as hir, Expr, ExprKind, Node, Stmt, StmtKind, TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty::{self, TraitRef, Ty}; +use rustc_middle::ty::TraitRef; use rustc_session::impl_lint_pass; -use rustc_span::{Span, Symbol, kw}; +use rustc_span::{Span, Symbol}; declare_clippy_lint! { /// ### What it does @@ -4888,47 +4889,17 @@ impl<'tcx> LateLintPass<'tcx> for Methods { if impl_item.span.in_external_macro(cx.sess().source_map()) { return; } + if let hir::ImplItemKind::Fn(ref sig, id) = impl_item.kind { - let name = impl_item.ident.name; let parent = cx.tcx.hir_get_parent_item(impl_item.hir_id()).def_id; let item = cx.tcx.hir_expect_item(parent); let self_ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); - let implements_trait = matches!(item.kind, hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. })); + let method_sig = cx.tcx.fn_sig(impl_item.owner_id).instantiate_identity(); let method_sig = cx.tcx.instantiate_bound_regions_with_erased(method_sig); let first_arg_ty_opt = method_sig.inputs().iter().next().copied(); - // if this impl block implements a trait, lint in trait definition instead - if !implements_trait && cx.effective_visibilities.is_exported(impl_item.owner_id.def_id) { - // check missing trait implementations - for method_config in &TRAIT_METHODS { - if name == method_config.method_name - && sig.decl.inputs.len() == method_config.param_count - && method_config.output_type.matches(&sig.decl.output) - // in case there is no first arg, since we already have checked the number of arguments - // it's should be always true - && first_arg_ty_opt - .is_none_or(|first_arg_ty| method_config.self_kind.matches(cx, self_ty, first_arg_ty)) - && fn_header_equals(method_config.fn_header, sig.header) - && method_config.lifetime_param_cond(impl_item) - { - span_lint_and_help( - cx, - SHOULD_IMPLEMENT_TRAIT, - impl_item.span, - format!( - "method `{}` can be confused for the standard trait method `{}::{}`", - method_config.method_name, method_config.trait_name, method_config.method_name - ), - None, - format!( - "consider implementing the trait `{}` or choosing a less ambiguous method name", - method_config.trait_name - ), - ); - } - } - } + should_implement_trait::check_impl_item(cx, impl_item, self_ty, implements_trait, first_arg_ty_opt, sig); if sig.decl.implicit_self.has_implicit_self() && !(self.avoid_breaking_exported_api @@ -4938,7 +4909,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { { wrong_self_convention::check( cx, - name, + impl_item.ident.name, self_ty, first_arg_ty, first_arg.pat.span, @@ -5711,181 +5682,3 @@ fn lint_binary_expr_with_method_call(cx: &LateContext<'_>, info: &mut BinaryExpr lint_with_both_lhs_and_rhs!(chars_next_cmp_with_unwrap::check, cx, info); lint_with_both_lhs_and_rhs!(chars_last_cmp_with_unwrap::check, cx, info); } - -const FN_HEADER: hir::FnHeader = hir::FnHeader { - safety: hir::HeaderSafety::Normal(hir::Safety::Safe), - constness: hir::Constness::NotConst, - asyncness: hir::IsAsync::NotAsync, - abi: ExternAbi::Rust, -}; - -struct ShouldImplTraitCase { - trait_name: &'static str, - method_name: Symbol, - param_count: usize, - fn_header: hir::FnHeader, - // implicit self kind expected (none, self, &self, ...) - self_kind: SelfKind, - // checks against the output type - output_type: OutType, - // certain methods with explicit lifetimes can't implement the equivalent trait method - lint_explicit_lifetime: bool, -} -impl ShouldImplTraitCase { - const fn new( - trait_name: &'static str, - method_name: Symbol, - param_count: usize, - fn_header: hir::FnHeader, - self_kind: SelfKind, - output_type: OutType, - lint_explicit_lifetime: bool, - ) -> ShouldImplTraitCase { - ShouldImplTraitCase { - trait_name, - method_name, - param_count, - fn_header, - self_kind, - output_type, - lint_explicit_lifetime, - } - } - - fn lifetime_param_cond(&self, impl_item: &hir::ImplItem<'_>) -> bool { - self.lint_explicit_lifetime - || !impl_item.generics.params.iter().any(|p| { - matches!( - p.kind, - hir::GenericParamKind::Lifetime { - kind: hir::LifetimeParamKind::Explicit - } - ) - }) - } -} - -#[rustfmt::skip] -const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [ - ShouldImplTraitCase::new("std::ops::Add", sym::add, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), - ShouldImplTraitCase::new("std::convert::AsMut", sym::as_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true ), - ShouldImplTraitCase::new("std::convert::AsRef", sym::as_ref, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true ), - ShouldImplTraitCase::new("std::ops::BitAnd", sym::bitand, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), - ShouldImplTraitCase::new("std::ops::BitOr", sym::bitor, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), - ShouldImplTraitCase::new("std::ops::BitXor", sym::bitxor, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), - ShouldImplTraitCase::new("std::borrow::Borrow", sym::borrow, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true ), - ShouldImplTraitCase::new("std::borrow::BorrowMut", sym::borrow_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true ), - ShouldImplTraitCase::new("std::clone::Clone", sym::clone, 1, FN_HEADER, SelfKind::Ref, OutType::Any, true ), - ShouldImplTraitCase::new("std::cmp::Ord", sym::cmp, 2, FN_HEADER, SelfKind::Ref, OutType::Any, true ), - ShouldImplTraitCase::new("std::default::Default", kw::Default, 0, FN_HEADER, SelfKind::No, OutType::Any, true ), - ShouldImplTraitCase::new("std::ops::Deref", sym::deref, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true ), - ShouldImplTraitCase::new("std::ops::DerefMut", sym::deref_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true ), - ShouldImplTraitCase::new("std::ops::Div", sym::div, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), - ShouldImplTraitCase::new("std::ops::Drop", sym::drop, 1, FN_HEADER, SelfKind::RefMut, OutType::Unit, true ), - ShouldImplTraitCase::new("std::cmp::PartialEq", sym::eq, 2, FN_HEADER, SelfKind::Ref, OutType::Bool, true ), - ShouldImplTraitCase::new("std::iter::FromIterator", sym::from_iter, 1, FN_HEADER, SelfKind::No, OutType::Any, true ), - ShouldImplTraitCase::new("std::str::FromStr", sym::from_str, 1, FN_HEADER, SelfKind::No, OutType::Any, true ), - ShouldImplTraitCase::new("std::hash::Hash", sym::hash, 2, FN_HEADER, SelfKind::Ref, OutType::Unit, true ), - ShouldImplTraitCase::new("std::ops::Index", sym::index, 2, FN_HEADER, SelfKind::Ref, OutType::Ref, true ), - ShouldImplTraitCase::new("std::ops::IndexMut", sym::index_mut, 2, FN_HEADER, SelfKind::RefMut, OutType::Ref, true ), - ShouldImplTraitCase::new("std::iter::IntoIterator", sym::into_iter, 1, FN_HEADER, SelfKind::Value, OutType::Any, true ), - ShouldImplTraitCase::new("std::ops::Mul", sym::mul, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), - ShouldImplTraitCase::new("std::ops::Neg", sym::neg, 1, FN_HEADER, SelfKind::Value, OutType::Any, true ), - ShouldImplTraitCase::new("std::iter::Iterator", sym::next, 1, FN_HEADER, SelfKind::RefMut, OutType::Any, false), - ShouldImplTraitCase::new("std::ops::Not", sym::not, 1, FN_HEADER, SelfKind::Value, OutType::Any, true ), - ShouldImplTraitCase::new("std::ops::Rem", sym::rem, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), - ShouldImplTraitCase::new("std::ops::Shl", sym::shl, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), - ShouldImplTraitCase::new("std::ops::Shr", sym::shr, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), - ShouldImplTraitCase::new("std::ops::Sub", sym::sub, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), -]; - -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -enum SelfKind { - Value, - Ref, - RefMut, - No, // When we want the first argument type to be different than `Self` -} - -impl SelfKind { - fn matches<'a>(self, cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool { - fn matches_value<'a>(cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool { - if ty == parent_ty { - true - } else if let Some(boxed_ty) = ty.boxed_ty() { - boxed_ty == parent_ty - } else if let ty::Adt(adt, args) = ty.kind() - && matches!(cx.tcx.get_diagnostic_name(adt.did()), Some(sym::Rc | sym::Arc)) - { - args.types().next() == Some(parent_ty) - } else { - false - } - } - - fn matches_ref<'a>(cx: &LateContext<'a>, mutability: hir::Mutability, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool { - if let ty::Ref(_, t, m) = *ty.kind() { - return m == mutability && t == parent_ty; - } - - let trait_sym = match mutability { - hir::Mutability::Not => sym::AsRef, - hir::Mutability::Mut => sym::AsMut, - }; - - let Some(trait_def_id) = cx.tcx.get_diagnostic_item(trait_sym) else { - return false; - }; - implements_trait(cx, ty, trait_def_id, &[parent_ty.into()]) - } - - fn matches_none<'a>(cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool { - !matches_value(cx, parent_ty, ty) - && !matches_ref(cx, hir::Mutability::Not, parent_ty, ty) - && !matches_ref(cx, hir::Mutability::Mut, parent_ty, ty) - } - - match self { - Self::Value => matches_value(cx, parent_ty, ty), - Self::Ref => matches_ref(cx, hir::Mutability::Not, parent_ty, ty) || ty == parent_ty && is_copy(cx, ty), - Self::RefMut => matches_ref(cx, hir::Mutability::Mut, parent_ty, ty), - Self::No => matches_none(cx, parent_ty, ty), - } - } - - #[must_use] - fn description(self) -> &'static str { - match self { - Self::Value => "`self` by value", - Self::Ref => "`self` by reference", - Self::RefMut => "`self` by mutable reference", - Self::No => "no `self`", - } - } -} - -#[derive(Clone, Copy)] -enum OutType { - Unit, - Bool, - Any, - Ref, -} - -impl OutType { - fn matches(self, ty: &hir::FnRetTy<'_>) -> bool { - let is_unit = |ty: &hir::Ty<'_>| matches!(ty.kind, hir::TyKind::Tup(&[])); - match (self, ty) { - (Self::Unit, &hir::FnRetTy::DefaultReturn(_)) => true, - (Self::Unit, &hir::FnRetTy::Return(ty)) if is_unit(ty) => true, - (Self::Bool, &hir::FnRetTy::Return(ty)) if is_bool(ty) => true, - (Self::Any, &hir::FnRetTy::Return(ty)) if !is_unit(ty) => true, - (Self::Ref, &hir::FnRetTy::Return(ty)) => matches!(ty.kind, hir::TyKind::Ref(_, _)), - _ => false, - } - } -} - -fn fn_header_equals(expected: hir::FnHeader, actual: hir::FnHeader) -> bool { - expected.constness == actual.constness && expected.safety == actual.safety && expected.asyncness == actual.asyncness -} diff --git a/clippy_lints/src/methods/should_implement_trait.rs b/clippy_lints/src/methods/should_implement_trait.rs new file mode 100644 index 000000000000..3f6600159a2f --- /dev/null +++ b/clippy_lints/src/methods/should_implement_trait.rs @@ -0,0 +1,165 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::{is_bool, sym}; +use rustc_abi::ExternAbi; +use rustc_hir as hir; +use rustc_hir::{FnSig, ImplItem}; +use rustc_lint::LateContext; +use rustc_middle::ty::Ty; +use rustc_span::{Symbol, kw}; + +use super::SHOULD_IMPLEMENT_TRAIT; +use super::lib::SelfKind; + +pub(super) fn check_impl_item<'tcx>( + cx: &LateContext<'tcx>, + impl_item: &'tcx ImplItem<'_>, + self_ty: Ty<'tcx>, + impl_implements_trait: bool, + first_arg_ty_opt: Option>, + sig: &FnSig<'_>, +) { + // if this impl block implements a trait, lint in trait definition instead + if !impl_implements_trait && cx.effective_visibilities.is_exported(impl_item.owner_id.def_id) { + // check missing trait implementations + for method_config in &TRAIT_METHODS { + if impl_item.ident.name == method_config.method_name + && sig.decl.inputs.len() == method_config.param_count + && method_config.output_type.matches(&sig.decl.output) + // in case there is no first arg, since we already have checked the number of arguments + // it's should be always true + && first_arg_ty_opt + .is_none_or(|first_arg_ty| method_config.self_kind.matches(cx, self_ty, first_arg_ty)) + && fn_header_equals(method_config.fn_header, sig.header) + && method_config.lifetime_param_cond(impl_item) + { + span_lint_and_help( + cx, + SHOULD_IMPLEMENT_TRAIT, + impl_item.span, + format!( + "method `{}` can be confused for the standard trait method `{}::{}`", + method_config.method_name, method_config.trait_name, method_config.method_name + ), + None, + format!( + "consider implementing the trait `{}` or choosing a less ambiguous method name", + method_config.trait_name + ), + ); + } + } + } +} + +const FN_HEADER: hir::FnHeader = hir::FnHeader { + safety: hir::HeaderSafety::Normal(hir::Safety::Safe), + constness: hir::Constness::NotConst, + asyncness: hir::IsAsync::NotAsync, + abi: ExternAbi::Rust, +}; + +struct ShouldImplTraitCase { + trait_name: &'static str, + method_name: Symbol, + param_count: usize, + fn_header: hir::FnHeader, + // implicit self kind expected (none, self, &self, ...) + self_kind: SelfKind, + // checks against the output type + output_type: OutType, + // certain methods with explicit lifetimes can't implement the equivalent trait method + lint_explicit_lifetime: bool, +} +impl ShouldImplTraitCase { + const fn new( + trait_name: &'static str, + method_name: Symbol, + param_count: usize, + fn_header: hir::FnHeader, + self_kind: SelfKind, + output_type: OutType, + lint_explicit_lifetime: bool, + ) -> ShouldImplTraitCase { + ShouldImplTraitCase { + trait_name, + method_name, + param_count, + fn_header, + self_kind, + output_type, + lint_explicit_lifetime, + } + } + + fn lifetime_param_cond(&self, impl_item: &ImplItem<'_>) -> bool { + self.lint_explicit_lifetime + || !impl_item.generics.params.iter().any(|p| { + matches!( + p.kind, + hir::GenericParamKind::Lifetime { + kind: hir::LifetimeParamKind::Explicit + } + ) + }) + } +} + +#[rustfmt::skip] +const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [ + ShouldImplTraitCase::new("std::ops::Add", sym::add, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), + ShouldImplTraitCase::new("std::convert::AsMut", sym::as_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true ), + ShouldImplTraitCase::new("std::convert::AsRef", sym::as_ref, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true ), + ShouldImplTraitCase::new("std::ops::BitAnd", sym::bitand, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), + ShouldImplTraitCase::new("std::ops::BitOr", sym::bitor, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), + ShouldImplTraitCase::new("std::ops::BitXor", sym::bitxor, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), + ShouldImplTraitCase::new("std::borrow::Borrow", sym::borrow, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true ), + ShouldImplTraitCase::new("std::borrow::BorrowMut", sym::borrow_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true ), + ShouldImplTraitCase::new("std::clone::Clone", sym::clone, 1, FN_HEADER, SelfKind::Ref, OutType::Any, true ), + ShouldImplTraitCase::new("std::cmp::Ord", sym::cmp, 2, FN_HEADER, SelfKind::Ref, OutType::Any, true ), + ShouldImplTraitCase::new("std::default::Default", kw::Default, 0, FN_HEADER, SelfKind::No, OutType::Any, true ), + ShouldImplTraitCase::new("std::ops::Deref", sym::deref, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true ), + ShouldImplTraitCase::new("std::ops::DerefMut", sym::deref_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true ), + ShouldImplTraitCase::new("std::ops::Div", sym::div, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), + ShouldImplTraitCase::new("std::ops::Drop", sym::drop, 1, FN_HEADER, SelfKind::RefMut, OutType::Unit, true ), + ShouldImplTraitCase::new("std::cmp::PartialEq", sym::eq, 2, FN_HEADER, SelfKind::Ref, OutType::Bool, true ), + ShouldImplTraitCase::new("std::iter::FromIterator", sym::from_iter, 1, FN_HEADER, SelfKind::No, OutType::Any, true ), + ShouldImplTraitCase::new("std::str::FromStr", sym::from_str, 1, FN_HEADER, SelfKind::No, OutType::Any, true ), + ShouldImplTraitCase::new("std::hash::Hash", sym::hash, 2, FN_HEADER, SelfKind::Ref, OutType::Unit, true ), + ShouldImplTraitCase::new("std::ops::Index", sym::index, 2, FN_HEADER, SelfKind::Ref, OutType::Ref, true ), + ShouldImplTraitCase::new("std::ops::IndexMut", sym::index_mut, 2, FN_HEADER, SelfKind::RefMut, OutType::Ref, true ), + ShouldImplTraitCase::new("std::iter::IntoIterator", sym::into_iter, 1, FN_HEADER, SelfKind::Value, OutType::Any, true ), + ShouldImplTraitCase::new("std::ops::Mul", sym::mul, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), + ShouldImplTraitCase::new("std::ops::Neg", sym::neg, 1, FN_HEADER, SelfKind::Value, OutType::Any, true ), + ShouldImplTraitCase::new("std::iter::Iterator", sym::next, 1, FN_HEADER, SelfKind::RefMut, OutType::Any, false), + ShouldImplTraitCase::new("std::ops::Not", sym::not, 1, FN_HEADER, SelfKind::Value, OutType::Any, true ), + ShouldImplTraitCase::new("std::ops::Rem", sym::rem, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), + ShouldImplTraitCase::new("std::ops::Shl", sym::shl, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), + ShouldImplTraitCase::new("std::ops::Shr", sym::shr, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), + ShouldImplTraitCase::new("std::ops::Sub", sym::sub, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), +]; + +#[derive(Clone, Copy)] +enum OutType { + Unit, + Bool, + Any, + Ref, +} + +impl OutType { + fn matches(self, ty: &hir::FnRetTy<'_>) -> bool { + let is_unit = |ty: &hir::Ty<'_>| matches!(ty.kind, hir::TyKind::Tup(&[])); + match (self, ty) { + (Self::Unit, &hir::FnRetTy::DefaultReturn(_)) => true, + (Self::Unit, &hir::FnRetTy::Return(ty)) if is_unit(ty) => true, + (Self::Bool, &hir::FnRetTy::Return(ty)) if is_bool(ty) => true, + (Self::Any, &hir::FnRetTy::Return(ty)) if !is_unit(ty) => true, + (Self::Ref, &hir::FnRetTy::Return(ty)) => matches!(ty.kind, hir::TyKind::Ref(_, _)), + _ => false, + } + } +} + +fn fn_header_equals(expected: hir::FnHeader, actual: hir::FnHeader) -> bool { + expected.constness == actual.constness && expected.safety == actual.safety && expected.asyncness == actual.asyncness +} diff --git a/clippy_lints/src/methods/wrong_self_convention.rs b/clippy_lints/src/methods/wrong_self_convention.rs index 249119d549ba..74b297c13621 100644 --- a/clippy_lints/src/methods/wrong_self_convention.rs +++ b/clippy_lints/src/methods/wrong_self_convention.rs @@ -1,4 +1,3 @@ -use crate::methods::SelfKind; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::is_copy; use itertools::Itertools; @@ -8,6 +7,7 @@ use rustc_span::{Span, Symbol}; use std::fmt; use super::WRONG_SELF_CONVENTION; +use super::lib::SelfKind; #[rustfmt::skip] const CONVENTIONS: [(&[Convention], &[SelfKind]); 9] = [ From 2d2d143a18249dbdf2709991d95e29e378f0c66c Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Mon, 29 Sep 2025 01:45:13 +0200 Subject: [PATCH 074/226] fix: respect crate's edition --- .../src/methods/should_implement_trait.rs | 66 ++++--- ...tderr => method_list_1.edition2015.stderr} | 30 +-- .../method_list_1.edition2021.stderr | 184 ++++++++++++++++++ tests/ui/should_impl_trait/method_list_1.rs | 3 + .../method_list_2.edition2015.stderr | 172 ++++++++++++++++ .../method_list_2.edition2021.stderr | 184 ++++++++++++++++++ tests/ui/should_impl_trait/method_list_2.rs | 5 +- 7 files changed, 598 insertions(+), 46 deletions(-) rename tests/ui/should_impl_trait/{method_list_1.stderr => method_list_1.edition2015.stderr} (86%) create mode 100644 tests/ui/should_impl_trait/method_list_1.edition2021.stderr create mode 100644 tests/ui/should_impl_trait/method_list_2.edition2015.stderr create mode 100644 tests/ui/should_impl_trait/method_list_2.edition2021.stderr diff --git a/clippy_lints/src/methods/should_implement_trait.rs b/clippy_lints/src/methods/should_implement_trait.rs index 3f6600159a2f..5f13b8c7e91d 100644 --- a/clippy_lints/src/methods/should_implement_trait.rs +++ b/clippy_lints/src/methods/should_implement_trait.rs @@ -5,6 +5,7 @@ use rustc_hir as hir; use rustc_hir::{FnSig, ImplItem}; use rustc_lint::LateContext; use rustc_middle::ty::Ty; +use rustc_span::edition::Edition::{self, Edition2015, Edition2021}; use rustc_span::{Symbol, kw}; use super::SHOULD_IMPLEMENT_TRAIT; @@ -31,6 +32,7 @@ pub(super) fn check_impl_item<'tcx>( .is_none_or(|first_arg_ty| method_config.self_kind.matches(cx, self_ty, first_arg_ty)) && fn_header_equals(method_config.fn_header, sig.header) && method_config.lifetime_param_cond(impl_item) + && method_config.in_prelude_since <= cx.tcx.sess.edition() { span_lint_and_help( cx, @@ -69,8 +71,10 @@ struct ShouldImplTraitCase { output_type: OutType, // certain methods with explicit lifetimes can't implement the equivalent trait method lint_explicit_lifetime: bool, + in_prelude_since: Edition, } impl ShouldImplTraitCase { + #[expect(clippy::too_many_arguments)] const fn new( trait_name: &'static str, method_name: Symbol, @@ -79,6 +83,7 @@ impl ShouldImplTraitCase { self_kind: SelfKind, output_type: OutType, lint_explicit_lifetime: bool, + in_prelude_since: Edition, ) -> ShouldImplTraitCase { ShouldImplTraitCase { trait_name, @@ -88,6 +93,7 @@ impl ShouldImplTraitCase { self_kind, output_type, lint_explicit_lifetime, + in_prelude_since, } } @@ -106,36 +112,36 @@ impl ShouldImplTraitCase { #[rustfmt::skip] const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [ - ShouldImplTraitCase::new("std::ops::Add", sym::add, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), - ShouldImplTraitCase::new("std::convert::AsMut", sym::as_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true ), - ShouldImplTraitCase::new("std::convert::AsRef", sym::as_ref, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true ), - ShouldImplTraitCase::new("std::ops::BitAnd", sym::bitand, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), - ShouldImplTraitCase::new("std::ops::BitOr", sym::bitor, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), - ShouldImplTraitCase::new("std::ops::BitXor", sym::bitxor, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), - ShouldImplTraitCase::new("std::borrow::Borrow", sym::borrow, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true ), - ShouldImplTraitCase::new("std::borrow::BorrowMut", sym::borrow_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true ), - ShouldImplTraitCase::new("std::clone::Clone", sym::clone, 1, FN_HEADER, SelfKind::Ref, OutType::Any, true ), - ShouldImplTraitCase::new("std::cmp::Ord", sym::cmp, 2, FN_HEADER, SelfKind::Ref, OutType::Any, true ), - ShouldImplTraitCase::new("std::default::Default", kw::Default, 0, FN_HEADER, SelfKind::No, OutType::Any, true ), - ShouldImplTraitCase::new("std::ops::Deref", sym::deref, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true ), - ShouldImplTraitCase::new("std::ops::DerefMut", sym::deref_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true ), - ShouldImplTraitCase::new("std::ops::Div", sym::div, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), - ShouldImplTraitCase::new("std::ops::Drop", sym::drop, 1, FN_HEADER, SelfKind::RefMut, OutType::Unit, true ), - ShouldImplTraitCase::new("std::cmp::PartialEq", sym::eq, 2, FN_HEADER, SelfKind::Ref, OutType::Bool, true ), - ShouldImplTraitCase::new("std::iter::FromIterator", sym::from_iter, 1, FN_HEADER, SelfKind::No, OutType::Any, true ), - ShouldImplTraitCase::new("std::str::FromStr", sym::from_str, 1, FN_HEADER, SelfKind::No, OutType::Any, true ), - ShouldImplTraitCase::new("std::hash::Hash", sym::hash, 2, FN_HEADER, SelfKind::Ref, OutType::Unit, true ), - ShouldImplTraitCase::new("std::ops::Index", sym::index, 2, FN_HEADER, SelfKind::Ref, OutType::Ref, true ), - ShouldImplTraitCase::new("std::ops::IndexMut", sym::index_mut, 2, FN_HEADER, SelfKind::RefMut, OutType::Ref, true ), - ShouldImplTraitCase::new("std::iter::IntoIterator", sym::into_iter, 1, FN_HEADER, SelfKind::Value, OutType::Any, true ), - ShouldImplTraitCase::new("std::ops::Mul", sym::mul, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), - ShouldImplTraitCase::new("std::ops::Neg", sym::neg, 1, FN_HEADER, SelfKind::Value, OutType::Any, true ), - ShouldImplTraitCase::new("std::iter::Iterator", sym::next, 1, FN_HEADER, SelfKind::RefMut, OutType::Any, false), - ShouldImplTraitCase::new("std::ops::Not", sym::not, 1, FN_HEADER, SelfKind::Value, OutType::Any, true ), - ShouldImplTraitCase::new("std::ops::Rem", sym::rem, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), - ShouldImplTraitCase::new("std::ops::Shl", sym::shl, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), - ShouldImplTraitCase::new("std::ops::Shr", sym::shr, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), - ShouldImplTraitCase::new("std::ops::Sub", sym::sub, 2, FN_HEADER, SelfKind::Value, OutType::Any, true ), + ShouldImplTraitCase::new("std::ops::Add", sym::add, 2, FN_HEADER, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::convert::AsMut", sym::as_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true, Edition2015), + ShouldImplTraitCase::new("std::convert::AsRef", sym::as_ref, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true, Edition2015), + ShouldImplTraitCase::new("std::ops::BitAnd", sym::bitand, 2, FN_HEADER, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::BitOr", sym::bitor, 2, FN_HEADER, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::BitXor", sym::bitxor, 2, FN_HEADER, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::borrow::Borrow", sym::borrow, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true, Edition2015), + ShouldImplTraitCase::new("std::borrow::BorrowMut", sym::borrow_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true, Edition2015), + ShouldImplTraitCase::new("std::clone::Clone", sym::clone, 1, FN_HEADER, SelfKind::Ref, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::cmp::Ord", sym::cmp, 2, FN_HEADER, SelfKind::Ref, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::default::Default", kw::Default, 0, FN_HEADER, SelfKind::No, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Deref", sym::deref, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true, Edition2015), + ShouldImplTraitCase::new("std::ops::DerefMut", sym::deref_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Div", sym::div, 2, FN_HEADER, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Drop", sym::drop, 1, FN_HEADER, SelfKind::RefMut, OutType::Unit, true, Edition2015), + ShouldImplTraitCase::new("std::cmp::PartialEq", sym::eq, 2, FN_HEADER, SelfKind::Ref, OutType::Bool, true, Edition2015), + ShouldImplTraitCase::new("std::iter::FromIterator", sym::from_iter, 1, FN_HEADER, SelfKind::No, OutType::Any, true, Edition2021), + ShouldImplTraitCase::new("std::str::FromStr", sym::from_str, 1, FN_HEADER, SelfKind::No, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::hash::Hash", sym::hash, 2, FN_HEADER, SelfKind::Ref, OutType::Unit, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Index", sym::index, 2, FN_HEADER, SelfKind::Ref, OutType::Ref, true, Edition2015), + ShouldImplTraitCase::new("std::ops::IndexMut", sym::index_mut, 2, FN_HEADER, SelfKind::RefMut, OutType::Ref, true, Edition2015), + ShouldImplTraitCase::new("std::iter::IntoIterator", sym::into_iter, 1, FN_HEADER, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Mul", sym::mul, 2, FN_HEADER, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Neg", sym::neg, 1, FN_HEADER, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::iter::Iterator", sym::next, 1, FN_HEADER, SelfKind::RefMut, OutType::Any, false, Edition2015), + ShouldImplTraitCase::new("std::ops::Not", sym::not, 1, FN_HEADER, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Rem", sym::rem, 2, FN_HEADER, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Shl", sym::shl, 2, FN_HEADER, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Shr", sym::shr, 2, FN_HEADER, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Sub", sym::sub, 2, FN_HEADER, SelfKind::Value, OutType::Any, true, Edition2015), ]; #[derive(Clone, Copy)] diff --git a/tests/ui/should_impl_trait/method_list_1.stderr b/tests/ui/should_impl_trait/method_list_1.edition2015.stderr similarity index 86% rename from tests/ui/should_impl_trait/method_list_1.stderr rename to tests/ui/should_impl_trait/method_list_1.edition2015.stderr index 5609d6a21a36..0312fa8f04fa 100644 --- a/tests/ui/should_impl_trait/method_list_1.stderr +++ b/tests/ui/should_impl_trait/method_list_1.edition2015.stderr @@ -1,5 +1,5 @@ error: method `add` can be confused for the standard trait method `std::ops::Add::add` - --> tests/ui/should_impl_trait/method_list_1.rs:24:5 + --> tests/ui/should_impl_trait/method_list_1.rs:27:5 | LL | / pub fn add(self, other: T) -> T { LL | | @@ -13,7 +13,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::should_implement_trait)]` error: method `as_mut` can be confused for the standard trait method `std::convert::AsMut::as_mut` - --> tests/ui/should_impl_trait/method_list_1.rs:30:5 + --> tests/ui/should_impl_trait/method_list_1.rs:33:5 | LL | / pub fn as_mut(&mut self) -> &mut T { LL | | @@ -25,7 +25,7 @@ LL | | } = help: consider implementing the trait `std::convert::AsMut` or choosing a less ambiguous method name error: method `as_ref` can be confused for the standard trait method `std::convert::AsRef::as_ref` - --> tests/ui/should_impl_trait/method_list_1.rs:36:5 + --> tests/ui/should_impl_trait/method_list_1.rs:39:5 | LL | / pub fn as_ref(&self) -> &T { LL | | @@ -37,7 +37,7 @@ LL | | } = help: consider implementing the trait `std::convert::AsRef` or choosing a less ambiguous method name error: method `bitand` can be confused for the standard trait method `std::ops::BitAnd::bitand` - --> tests/ui/should_impl_trait/method_list_1.rs:42:5 + --> tests/ui/should_impl_trait/method_list_1.rs:45:5 | LL | / pub fn bitand(self, rhs: T) -> T { LL | | @@ -49,7 +49,7 @@ LL | | } = help: consider implementing the trait `std::ops::BitAnd` or choosing a less ambiguous method name error: method `bitor` can be confused for the standard trait method `std::ops::BitOr::bitor` - --> tests/ui/should_impl_trait/method_list_1.rs:48:5 + --> tests/ui/should_impl_trait/method_list_1.rs:51:5 | LL | / pub fn bitor(self, rhs: Self) -> Self { LL | | @@ -61,7 +61,7 @@ LL | | } = help: consider implementing the trait `std::ops::BitOr` or choosing a less ambiguous method name error: method `bitxor` can be confused for the standard trait method `std::ops::BitXor::bitxor` - --> tests/ui/should_impl_trait/method_list_1.rs:54:5 + --> tests/ui/should_impl_trait/method_list_1.rs:57:5 | LL | / pub fn bitxor(self, rhs: Self) -> Self { LL | | @@ -73,7 +73,7 @@ LL | | } = help: consider implementing the trait `std::ops::BitXor` or choosing a less ambiguous method name error: method `borrow` can be confused for the standard trait method `std::borrow::Borrow::borrow` - --> tests/ui/should_impl_trait/method_list_1.rs:60:5 + --> tests/ui/should_impl_trait/method_list_1.rs:63:5 | LL | / pub fn borrow(&self) -> &str { LL | | @@ -85,7 +85,7 @@ LL | | } = help: consider implementing the trait `std::borrow::Borrow` or choosing a less ambiguous method name error: method `borrow_mut` can be confused for the standard trait method `std::borrow::BorrowMut::borrow_mut` - --> tests/ui/should_impl_trait/method_list_1.rs:66:5 + --> tests/ui/should_impl_trait/method_list_1.rs:69:5 | LL | / pub fn borrow_mut(&mut self) -> &mut str { LL | | @@ -97,7 +97,7 @@ LL | | } = help: consider implementing the trait `std::borrow::BorrowMut` or choosing a less ambiguous method name error: method `clone` can be confused for the standard trait method `std::clone::Clone::clone` - --> tests/ui/should_impl_trait/method_list_1.rs:72:5 + --> tests/ui/should_impl_trait/method_list_1.rs:75:5 | LL | / pub fn clone(&self) -> Self { LL | | @@ -109,7 +109,7 @@ LL | | } = help: consider implementing the trait `std::clone::Clone` or choosing a less ambiguous method name error: method `cmp` can be confused for the standard trait method `std::cmp::Ord::cmp` - --> tests/ui/should_impl_trait/method_list_1.rs:78:5 + --> tests/ui/should_impl_trait/method_list_1.rs:81:5 | LL | / pub fn cmp(&self, other: &Self) -> Self { LL | | @@ -121,7 +121,7 @@ LL | | } = help: consider implementing the trait `std::cmp::Ord` or choosing a less ambiguous method name error: method `default` can be confused for the standard trait method `std::default::Default::default` - --> tests/ui/should_impl_trait/method_list_1.rs:84:5 + --> tests/ui/should_impl_trait/method_list_1.rs:87:5 | LL | / pub fn default() -> Self { LL | | @@ -133,7 +133,7 @@ LL | | } = help: consider implementing the trait `std::default::Default` or choosing a less ambiguous method name error: method `deref` can be confused for the standard trait method `std::ops::Deref::deref` - --> tests/ui/should_impl_trait/method_list_1.rs:90:5 + --> tests/ui/should_impl_trait/method_list_1.rs:93:5 | LL | / pub fn deref(&self) -> &Self { LL | | @@ -145,7 +145,7 @@ LL | | } = help: consider implementing the trait `std::ops::Deref` or choosing a less ambiguous method name error: method `deref_mut` can be confused for the standard trait method `std::ops::DerefMut::deref_mut` - --> tests/ui/should_impl_trait/method_list_1.rs:96:5 + --> tests/ui/should_impl_trait/method_list_1.rs:99:5 | LL | / pub fn deref_mut(&mut self) -> &mut Self { LL | | @@ -157,7 +157,7 @@ LL | | } = help: consider implementing the trait `std::ops::DerefMut` or choosing a less ambiguous method name error: method `div` can be confused for the standard trait method `std::ops::Div::div` - --> tests/ui/should_impl_trait/method_list_1.rs:102:5 + --> tests/ui/should_impl_trait/method_list_1.rs:105:5 | LL | / pub fn div(self, rhs: Self) -> Self { LL | | @@ -169,7 +169,7 @@ LL | | } = help: consider implementing the trait `std::ops::Div` or choosing a less ambiguous method name error: method `drop` can be confused for the standard trait method `std::ops::Drop::drop` - --> tests/ui/should_impl_trait/method_list_1.rs:108:5 + --> tests/ui/should_impl_trait/method_list_1.rs:111:5 | LL | / pub fn drop(&mut self) { LL | | diff --git a/tests/ui/should_impl_trait/method_list_1.edition2021.stderr b/tests/ui/should_impl_trait/method_list_1.edition2021.stderr new file mode 100644 index 000000000000..0312fa8f04fa --- /dev/null +++ b/tests/ui/should_impl_trait/method_list_1.edition2021.stderr @@ -0,0 +1,184 @@ +error: method `add` can be confused for the standard trait method `std::ops::Add::add` + --> tests/ui/should_impl_trait/method_list_1.rs:27:5 + | +LL | / pub fn add(self, other: T) -> T { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Add` or choosing a less ambiguous method name + = note: `-D clippy::should-implement-trait` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::should_implement_trait)]` + +error: method `as_mut` can be confused for the standard trait method `std::convert::AsMut::as_mut` + --> tests/ui/should_impl_trait/method_list_1.rs:33:5 + | +LL | / pub fn as_mut(&mut self) -> &mut T { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::convert::AsMut` or choosing a less ambiguous method name + +error: method `as_ref` can be confused for the standard trait method `std::convert::AsRef::as_ref` + --> tests/ui/should_impl_trait/method_list_1.rs:39:5 + | +LL | / pub fn as_ref(&self) -> &T { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::convert::AsRef` or choosing a less ambiguous method name + +error: method `bitand` can be confused for the standard trait method `std::ops::BitAnd::bitand` + --> tests/ui/should_impl_trait/method_list_1.rs:45:5 + | +LL | / pub fn bitand(self, rhs: T) -> T { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::BitAnd` or choosing a less ambiguous method name + +error: method `bitor` can be confused for the standard trait method `std::ops::BitOr::bitor` + --> tests/ui/should_impl_trait/method_list_1.rs:51:5 + | +LL | / pub fn bitor(self, rhs: Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::BitOr` or choosing a less ambiguous method name + +error: method `bitxor` can be confused for the standard trait method `std::ops::BitXor::bitxor` + --> tests/ui/should_impl_trait/method_list_1.rs:57:5 + | +LL | / pub fn bitxor(self, rhs: Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::BitXor` or choosing a less ambiguous method name + +error: method `borrow` can be confused for the standard trait method `std::borrow::Borrow::borrow` + --> tests/ui/should_impl_trait/method_list_1.rs:63:5 + | +LL | / pub fn borrow(&self) -> &str { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::borrow::Borrow` or choosing a less ambiguous method name + +error: method `borrow_mut` can be confused for the standard trait method `std::borrow::BorrowMut::borrow_mut` + --> tests/ui/should_impl_trait/method_list_1.rs:69:5 + | +LL | / pub fn borrow_mut(&mut self) -> &mut str { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::borrow::BorrowMut` or choosing a less ambiguous method name + +error: method `clone` can be confused for the standard trait method `std::clone::Clone::clone` + --> tests/ui/should_impl_trait/method_list_1.rs:75:5 + | +LL | / pub fn clone(&self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::clone::Clone` or choosing a less ambiguous method name + +error: method `cmp` can be confused for the standard trait method `std::cmp::Ord::cmp` + --> tests/ui/should_impl_trait/method_list_1.rs:81:5 + | +LL | / pub fn cmp(&self, other: &Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::cmp::Ord` or choosing a less ambiguous method name + +error: method `default` can be confused for the standard trait method `std::default::Default::default` + --> tests/ui/should_impl_trait/method_list_1.rs:87:5 + | +LL | / pub fn default() -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::default::Default` or choosing a less ambiguous method name + +error: method `deref` can be confused for the standard trait method `std::ops::Deref::deref` + --> tests/ui/should_impl_trait/method_list_1.rs:93:5 + | +LL | / pub fn deref(&self) -> &Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Deref` or choosing a less ambiguous method name + +error: method `deref_mut` can be confused for the standard trait method `std::ops::DerefMut::deref_mut` + --> tests/ui/should_impl_trait/method_list_1.rs:99:5 + | +LL | / pub fn deref_mut(&mut self) -> &mut Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::DerefMut` or choosing a less ambiguous method name + +error: method `div` can be confused for the standard trait method `std::ops::Div::div` + --> tests/ui/should_impl_trait/method_list_1.rs:105:5 + | +LL | / pub fn div(self, rhs: Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Div` or choosing a less ambiguous method name + +error: method `drop` can be confused for the standard trait method `std::ops::Drop::drop` + --> tests/ui/should_impl_trait/method_list_1.rs:111:5 + | +LL | / pub fn drop(&mut self) { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Drop` or choosing a less ambiguous method name + +error: aborting due to 15 previous errors + diff --git a/tests/ui/should_impl_trait/method_list_1.rs b/tests/ui/should_impl_trait/method_list_1.rs index e8de0e04c0c4..bbb04c0c5aa1 100644 --- a/tests/ui/should_impl_trait/method_list_1.rs +++ b/tests/ui/should_impl_trait/method_list_1.rs @@ -1,3 +1,6 @@ +//@revisions: edition2015 edition2021 +//@[edition2015] edition:2015 +//@[edition2021] edition:2021 #![allow( clippy::missing_errors_doc, clippy::needless_pass_by_value, diff --git a/tests/ui/should_impl_trait/method_list_2.edition2015.stderr b/tests/ui/should_impl_trait/method_list_2.edition2015.stderr new file mode 100644 index 000000000000..259815908fee --- /dev/null +++ b/tests/ui/should_impl_trait/method_list_2.edition2015.stderr @@ -0,0 +1,172 @@ +error: method `eq` can be confused for the standard trait method `std::cmp::PartialEq::eq` + --> tests/ui/should_impl_trait/method_list_2.rs:28:5 + | +LL | / pub fn eq(&self, other: &Self) -> bool { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::cmp::PartialEq` or choosing a less ambiguous method name + = note: `-D clippy::should-implement-trait` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::should_implement_trait)]` + +error: method `from_str` can be confused for the standard trait method `std::str::FromStr::from_str` + --> tests/ui/should_impl_trait/method_list_2.rs:40:5 + | +LL | / pub fn from_str(s: &str) -> Result { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::str::FromStr` or choosing a less ambiguous method name + +error: method `hash` can be confused for the standard trait method `std::hash::Hash::hash` + --> tests/ui/should_impl_trait/method_list_2.rs:46:5 + | +LL | / pub fn hash(&self, state: &mut T) { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::hash::Hash` or choosing a less ambiguous method name + +error: method `index` can be confused for the standard trait method `std::ops::Index::index` + --> tests/ui/should_impl_trait/method_list_2.rs:52:5 + | +LL | / pub fn index(&self, index: usize) -> &Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Index` or choosing a less ambiguous method name + +error: method `index_mut` can be confused for the standard trait method `std::ops::IndexMut::index_mut` + --> tests/ui/should_impl_trait/method_list_2.rs:58:5 + | +LL | / pub fn index_mut(&mut self, index: usize) -> &mut Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::IndexMut` or choosing a less ambiguous method name + +error: method `into_iter` can be confused for the standard trait method `std::iter::IntoIterator::into_iter` + --> tests/ui/should_impl_trait/method_list_2.rs:64:5 + | +LL | / pub fn into_iter(self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::iter::IntoIterator` or choosing a less ambiguous method name + +error: method `mul` can be confused for the standard trait method `std::ops::Mul::mul` + --> tests/ui/should_impl_trait/method_list_2.rs:70:5 + | +LL | / pub fn mul(self, rhs: Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Mul` or choosing a less ambiguous method name + +error: method `neg` can be confused for the standard trait method `std::ops::Neg::neg` + --> tests/ui/should_impl_trait/method_list_2.rs:76:5 + | +LL | / pub fn neg(self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Neg` or choosing a less ambiguous method name + +error: method `next` can be confused for the standard trait method `std::iter::Iterator::next` + --> tests/ui/should_impl_trait/method_list_2.rs:82:5 + | +LL | / pub fn next(&mut self) -> Option { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::iter::Iterator` or choosing a less ambiguous method name + +error: method `not` can be confused for the standard trait method `std::ops::Not::not` + --> tests/ui/should_impl_trait/method_list_2.rs:88:5 + | +LL | / pub fn not(self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Not` or choosing a less ambiguous method name + +error: method `rem` can be confused for the standard trait method `std::ops::Rem::rem` + --> tests/ui/should_impl_trait/method_list_2.rs:94:5 + | +LL | / pub fn rem(self, rhs: Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Rem` or choosing a less ambiguous method name + +error: method `shl` can be confused for the standard trait method `std::ops::Shl::shl` + --> tests/ui/should_impl_trait/method_list_2.rs:100:5 + | +LL | / pub fn shl(self, rhs: Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Shl` or choosing a less ambiguous method name + +error: method `shr` can be confused for the standard trait method `std::ops::Shr::shr` + --> tests/ui/should_impl_trait/method_list_2.rs:106:5 + | +LL | / pub fn shr(self, rhs: Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Shr` or choosing a less ambiguous method name + +error: method `sub` can be confused for the standard trait method `std::ops::Sub::sub` + --> tests/ui/should_impl_trait/method_list_2.rs:112:5 + | +LL | / pub fn sub(self, rhs: Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Sub` or choosing a less ambiguous method name + +error: aborting due to 14 previous errors + diff --git a/tests/ui/should_impl_trait/method_list_2.edition2021.stderr b/tests/ui/should_impl_trait/method_list_2.edition2021.stderr new file mode 100644 index 000000000000..2f90b61e7a17 --- /dev/null +++ b/tests/ui/should_impl_trait/method_list_2.edition2021.stderr @@ -0,0 +1,184 @@ +error: method `eq` can be confused for the standard trait method `std::cmp::PartialEq::eq` + --> tests/ui/should_impl_trait/method_list_2.rs:28:5 + | +LL | / pub fn eq(&self, other: &Self) -> bool { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::cmp::PartialEq` or choosing a less ambiguous method name + = note: `-D clippy::should-implement-trait` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::should_implement_trait)]` + +error: method `from_iter` can be confused for the standard trait method `std::iter::FromIterator::from_iter` + --> tests/ui/should_impl_trait/method_list_2.rs:34:5 + | +LL | / pub fn from_iter(iter: T) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::iter::FromIterator` or choosing a less ambiguous method name + +error: method `from_str` can be confused for the standard trait method `std::str::FromStr::from_str` + --> tests/ui/should_impl_trait/method_list_2.rs:40:5 + | +LL | / pub fn from_str(s: &str) -> Result { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::str::FromStr` or choosing a less ambiguous method name + +error: method `hash` can be confused for the standard trait method `std::hash::Hash::hash` + --> tests/ui/should_impl_trait/method_list_2.rs:46:5 + | +LL | / pub fn hash(&self, state: &mut T) { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::hash::Hash` or choosing a less ambiguous method name + +error: method `index` can be confused for the standard trait method `std::ops::Index::index` + --> tests/ui/should_impl_trait/method_list_2.rs:52:5 + | +LL | / pub fn index(&self, index: usize) -> &Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Index` or choosing a less ambiguous method name + +error: method `index_mut` can be confused for the standard trait method `std::ops::IndexMut::index_mut` + --> tests/ui/should_impl_trait/method_list_2.rs:58:5 + | +LL | / pub fn index_mut(&mut self, index: usize) -> &mut Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::IndexMut` or choosing a less ambiguous method name + +error: method `into_iter` can be confused for the standard trait method `std::iter::IntoIterator::into_iter` + --> tests/ui/should_impl_trait/method_list_2.rs:64:5 + | +LL | / pub fn into_iter(self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::iter::IntoIterator` or choosing a less ambiguous method name + +error: method `mul` can be confused for the standard trait method `std::ops::Mul::mul` + --> tests/ui/should_impl_trait/method_list_2.rs:70:5 + | +LL | / pub fn mul(self, rhs: Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Mul` or choosing a less ambiguous method name + +error: method `neg` can be confused for the standard trait method `std::ops::Neg::neg` + --> tests/ui/should_impl_trait/method_list_2.rs:76:5 + | +LL | / pub fn neg(self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Neg` or choosing a less ambiguous method name + +error: method `next` can be confused for the standard trait method `std::iter::Iterator::next` + --> tests/ui/should_impl_trait/method_list_2.rs:82:5 + | +LL | / pub fn next(&mut self) -> Option { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::iter::Iterator` or choosing a less ambiguous method name + +error: method `not` can be confused for the standard trait method `std::ops::Not::not` + --> tests/ui/should_impl_trait/method_list_2.rs:88:5 + | +LL | / pub fn not(self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Not` or choosing a less ambiguous method name + +error: method `rem` can be confused for the standard trait method `std::ops::Rem::rem` + --> tests/ui/should_impl_trait/method_list_2.rs:94:5 + | +LL | / pub fn rem(self, rhs: Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Rem` or choosing a less ambiguous method name + +error: method `shl` can be confused for the standard trait method `std::ops::Shl::shl` + --> tests/ui/should_impl_trait/method_list_2.rs:100:5 + | +LL | / pub fn shl(self, rhs: Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Shl` or choosing a less ambiguous method name + +error: method `shr` can be confused for the standard trait method `std::ops::Shr::shr` + --> tests/ui/should_impl_trait/method_list_2.rs:106:5 + | +LL | / pub fn shr(self, rhs: Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Shr` or choosing a less ambiguous method name + +error: method `sub` can be confused for the standard trait method `std::ops::Sub::sub` + --> tests/ui/should_impl_trait/method_list_2.rs:112:5 + | +LL | / pub fn sub(self, rhs: Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Sub` or choosing a less ambiguous method name + +error: aborting due to 15 previous errors + diff --git a/tests/ui/should_impl_trait/method_list_2.rs b/tests/ui/should_impl_trait/method_list_2.rs index 1f25ab3938a3..4dfbe7e0f9f4 100644 --- a/tests/ui/should_impl_trait/method_list_2.rs +++ b/tests/ui/should_impl_trait/method_list_2.rs @@ -1,3 +1,6 @@ +//@revisions: edition2015 edition2021 +//@[edition2015] edition:2015 +//@[edition2021] edition:2021 #![allow( clippy::missing_errors_doc, clippy::needless_pass_by_value, @@ -29,7 +32,7 @@ impl T { } pub fn from_iter(iter: T) -> Self { - //~^ should_implement_trait + //~[edition2021]^ should_implement_trait unimplemented!() } From 92e65dc4d485e464204346557c228374a8828f7c Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Mon, 29 Sep 2025 18:39:39 +0200 Subject: [PATCH 075/226] how-to-build-and-run.md: replace "rm build" with "x clean --all" --- src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md b/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md index 36610f28854b..f8ee9629f047 100644 --- a/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md +++ b/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md @@ -187,7 +187,7 @@ Alternatively, you can write `bootstrap.toml` by hand. See `bootstrap.example.to settings and explanations of them. See `src/bootstrap/defaults` for common settings to change. If you have already built `rustc` and you change settings related to LLVM, then you may have to -execute `rm -rf build` for subsequent configuration changes to take effect. Note that `./x +execute `./x clean --all` for subsequent configuration changes to take effect. Note that `./x clean` will not cause a rebuild of LLVM. ## Common `x` commands From eaeca967785ef51145016839bb7f8ceec876ae1c Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Mon, 29 Sep 2025 18:58:03 +0200 Subject: [PATCH 076/226] how-to-build-and-run.md: update "building compiler" section --- .../src/building/how-to-build-and-run.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md b/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md index 36610f28854b..fb4da9c187ea 100644 --- a/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md +++ b/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md @@ -226,16 +226,17 @@ Once you've created a `bootstrap.toml`, you are now ready to run `x`. There are a lot of options here, but let's start with what is probably the best "go to" command for building a local compiler: -```bash -./x build library +```console +./x build rustc ``` -This may *look* like it only builds the standard library, but that is not the case. -What this command does is the following: +What this command does is build `rustc` using the stage0 compiler and stage0 `std`. -- Build `rustc` using the stage0 compiler - - This produces the stage1 compiler -- Build `std` using the stage1 compiler +To build `rustc` with the in-tree `std`, use this command instead: + +```console +./x build rustc --stage 2 +``` This final product (stage1 compiler + libs built using that compiler) is what you need to build other Rust programs (unless you use `#![no_std]` or @@ -253,7 +254,7 @@ signature of some function, you can use `./x check` instead for a much faster bu Note that this whole command just gives you a subset of the full `rustc` build. The **full** `rustc` build (what you get with `./x build ---stage 2 compiler/rustc`) has quite a few more steps: +--stage 2 rustc`) has quite a few more steps: - Build `rustc` with the stage1 compiler. - The resulting compiler here is called the "stage2" compiler, which uses stage1 std from the previous command. From 17cecfd311848f10c60014fe4714b1475ee545d5 Mon Sep 17 00:00:00 2001 From: yanglsh Date: Tue, 30 Sep 2025 02:00:06 +0800 Subject: [PATCH 077/226] fix: `new_without_default` FP on private type with trait impl --- clippy_lints/src/new_without_default.rs | 2 +- tests/ui/new_without_default.fixed | 20 ++++++++++++++++++++ tests/ui/new_without_default.rs | 20 ++++++++++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs index b598a390005b..ab7b09c48a7f 100644 --- a/clippy_lints/src/new_without_default.rs +++ b/clippy_lints/src/new_without_default.rs @@ -94,7 +94,7 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { return; } if sig.decl.inputs.is_empty() - && cx.effective_visibilities.is_reachable(impl_item.owner_id.def_id) + && cx.effective_visibilities.is_exported(impl_item.owner_id.def_id) && let self_ty = cx.tcx.type_of(item.owner_id).instantiate_identity() && self_ty == return_ty(cx, impl_item.owner_id) && let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default) diff --git a/tests/ui/new_without_default.fixed b/tests/ui/new_without_default.fixed index 277c335cd885..b5558cd3086c 100644 --- a/tests/ui/new_without_default.fixed +++ b/tests/ui/new_without_default.fixed @@ -322,3 +322,23 @@ where Self { _kv: None } } } + +mod issue15778 { + pub struct Foo(Vec); + + impl Foo { + pub fn new() -> Self { + Self(Vec::new()) + } + } + + impl<'a> IntoIterator for &'a Foo { + type Item = &'a i32; + + type IntoIter = std::slice::Iter<'a, i32>; + + fn into_iter(self) -> Self::IntoIter { + self.0.as_slice().iter() + } + } +} diff --git a/tests/ui/new_without_default.rs b/tests/ui/new_without_default.rs index f2844897c93d..be598c81718f 100644 --- a/tests/ui/new_without_default.rs +++ b/tests/ui/new_without_default.rs @@ -265,3 +265,23 @@ where Self { _kv: None } } } + +mod issue15778 { + pub struct Foo(Vec); + + impl Foo { + pub fn new() -> Self { + Self(Vec::new()) + } + } + + impl<'a> IntoIterator for &'a Foo { + type Item = &'a i32; + + type IntoIter = std::slice::Iter<'a, i32>; + + fn into_iter(self) -> Self::IntoIter { + self.0.as_slice().iter() + } + } +} From 642feb7e562edcc5d95bbc2e6ca27438fcd3b35f Mon Sep 17 00:00:00 2001 From: yanglsh Date: Tue, 30 Sep 2025 02:28:54 +0800 Subject: [PATCH 078/226] fix: `if_then_some_else_none` FP when return exists in block expr --- clippy_lints/src/if_then_some_else_none.rs | 1 + tests/ui/if_then_some_else_none.fixed | 12 ++++++++++++ tests/ui/if_then_some_else_none.rs | 12 ++++++++++++ 3 files changed, 25 insertions(+) diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs index b50d91f10146..f9fee292837e 100644 --- a/clippy_lints/src/if_then_some_else_none.rs +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -79,6 +79,7 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { && !is_in_const_context(cx) && self.msrv.meets(cx, msrvs::BOOL_THEN) && !contains_return(then_block.stmts) + && then_block.expr.is_none_or(|expr| !contains_return(expr)) { let method_name = if switch_to_eager_eval(cx, expr) && self.msrv.meets(cx, msrvs::BOOL_THEN_SOME) { sym::then_some diff --git a/tests/ui/if_then_some_else_none.fixed b/tests/ui/if_then_some_else_none.fixed index 0fd130609aee..7da9401a308f 100644 --- a/tests/ui/if_then_some_else_none.fixed +++ b/tests/ui/if_then_some_else_none.fixed @@ -206,3 +206,15 @@ fn dont_lint_inside_macros() { } let _: Option = mac!(true, 42); } + +mod issue15770 { + fn maybe_error() -> Result { + Err("error!") + } + + pub fn trying(b: bool) -> Result<(), &'static str> { + let _x: Option = if b { Some(maybe_error()?) } else { None }; + // Process _x locally + Ok(()) + } +} diff --git a/tests/ui/if_then_some_else_none.rs b/tests/ui/if_then_some_else_none.rs index 640828aa9bf6..02962f83ce8a 100644 --- a/tests/ui/if_then_some_else_none.rs +++ b/tests/ui/if_then_some_else_none.rs @@ -262,3 +262,15 @@ fn dont_lint_inside_macros() { } let _: Option = mac!(true, 42); } + +mod issue15770 { + fn maybe_error() -> Result { + Err("error!") + } + + pub fn trying(b: bool) -> Result<(), &'static str> { + let _x: Option = if b { Some(maybe_error()?) } else { None }; + // Process _x locally + Ok(()) + } +} From c3e4d2bb90c71040adc120e3322fa2e3696152ab Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Mon, 29 Sep 2025 19:21:21 +0200 Subject: [PATCH 079/226] clean-up --- clippy_lints/src/unit_types/let_unit_value.rs | 25 +++++++++++++++++-- tests/ui/let_unit.fixed | 15 +++++------ tests/ui/let_unit.rs | 15 +++++------ tests/ui/let_unit.stderr | 10 ++++---- 4 files changed, 40 insertions(+), 25 deletions(-) diff --git a/clippy_lints/src/unit_types/let_unit_value.rs b/clippy_lints/src/unit_types/let_unit_value.rs index d5b6c1758549..1f05617a746f 100644 --- a/clippy_lints/src/unit_types/let_unit_value.rs +++ b/clippy_lints/src/unit_types/let_unit_value.rs @@ -89,15 +89,30 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, format_args: &FormatArgsStorag walk_body(&mut visitor, body); let mut has_in_format_capture = false; - suggestions.extend(visitor.spans.iter().filter_map(|span| match span { + suggestions.extend(visitor.spans.into_iter().filter_map(|span| match span { MaybeInFormatCapture::Yes => { has_in_format_capture = true; None }, - MaybeInFormatCapture::No(span) => Some((*span, "()".to_string())), + MaybeInFormatCapture::No(span) => Some((span, "()".to_string())), })); if has_in_format_capture { + // In a case like this: + // ``` + // let unit = returns_unit(); + // eprintln!("{unit}"); + // ``` + // we can't remove the `unit` binding and replace its uses with a `()`, + // because the `eprintln!` would break. + // + // So do the following instead: + // ``` + // let unit = (); + // returns_unit(); + // eprintln!("{unit}"); + // ``` + // TODO: find a less awkward way to do this suggestions.push(( init.span, format!("();\n{}", reindent_multiline(&snip, false, indent_of(cx, local.span))), @@ -132,6 +147,12 @@ struct UnitVariableCollector<'a, 'tcx> { macro_call: Option<&'a FormatArgs>, } +/// Whether the unit variable is captured in a `format!`: +/// +/// ```ignore +/// let unit = (); +/// eprintln!("{unit}"); +/// ``` enum MaybeInFormatCapture { Yes, No(Span), diff --git a/tests/ui/let_unit.fixed b/tests/ui/let_unit.fixed index 381d4cac4622..d640c6a916d0 100644 --- a/tests/ui/let_unit.fixed +++ b/tests/ui/let_unit.fixed @@ -1,5 +1,5 @@ #![warn(clippy::let_unit_value)] -#![allow(unused, clippy::no_effect, clippy::needless_late_init, path_statements)] +#![allow(clippy::no_effect, clippy::needless_late_init, path_statements)] macro_rules! let_and_return { ($n:expr) => {{ @@ -15,12 +15,12 @@ fn main() { if true { // do not lint this, since () is explicit let _a = (); - let () = dummy(); + let () = returns_unit(); let () = (); - () = dummy(); + () = returns_unit(); () = (); let _a: () = (); - let _a: () = dummy(); + let _a: () = returns_unit(); } consume_units_with_for_loop(); // should be fine as well @@ -30,7 +30,7 @@ fn main() { let_and_return!(()) // should be fine } -fn dummy() {} +fn returns_unit() {} // Related to issue #1964 fn consume_units_with_for_loop() { @@ -181,8 +181,6 @@ async fn issue10433() { pub async fn issue11502(a: ()) {} pub fn issue12594() { - fn returns_unit() {} - fn returns_result(res: T) -> Result { Ok(res) } @@ -200,11 +198,10 @@ pub fn issue12594() { } fn issue15061() { - fn return_unit() {} fn do_something(x: ()) {} let res = (); - return_unit(); + returns_unit(); //~^ let_unit_value do_something(()); println!("{res:?}"); diff --git a/tests/ui/let_unit.rs b/tests/ui/let_unit.rs index cdfc74991c40..d966d2ec0a5c 100644 --- a/tests/ui/let_unit.rs +++ b/tests/ui/let_unit.rs @@ -1,5 +1,5 @@ #![warn(clippy::let_unit_value)] -#![allow(unused, clippy::no_effect, clippy::needless_late_init, path_statements)] +#![allow(clippy::no_effect, clippy::needless_late_init, path_statements)] macro_rules! let_and_return { ($n:expr) => {{ @@ -15,12 +15,12 @@ fn main() { if true { // do not lint this, since () is explicit let _a = (); - let () = dummy(); + let () = returns_unit(); let () = (); - () = dummy(); + () = returns_unit(); () = (); let _a: () = (); - let _a: () = dummy(); + let _a: () = returns_unit(); } consume_units_with_for_loop(); // should be fine as well @@ -30,7 +30,7 @@ fn main() { let_and_return!(()) // should be fine } -fn dummy() {} +fn returns_unit() {} // Related to issue #1964 fn consume_units_with_for_loop() { @@ -181,8 +181,6 @@ async fn issue10433() { pub async fn issue11502(a: ()) {} pub fn issue12594() { - fn returns_unit() {} - fn returns_result(res: T) -> Result { Ok(res) } @@ -200,10 +198,9 @@ pub fn issue12594() { } fn issue15061() { - fn return_unit() {} fn do_something(x: ()) {} - let res = return_unit(); + let res = returns_unit(); //~^ let_unit_value do_something(res); println!("{res:?}"); diff --git a/tests/ui/let_unit.stderr b/tests/ui/let_unit.stderr index 637c9ff686bd..4d9bbbe31074 100644 --- a/tests/ui/let_unit.stderr +++ b/tests/ui/let_unit.stderr @@ -55,7 +55,7 @@ LL + }; | error: this let-binding has unit value - --> tests/ui/let_unit.rs:192:9 + --> tests/ui/let_unit.rs:190:9 | LL | let res = returns_unit(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -69,15 +69,15 @@ LL ~ returns_result(()).unwrap(); | error: this let-binding has unit value - --> tests/ui/let_unit.rs:206:5 + --> tests/ui/let_unit.rs:203:5 | -LL | let res = return_unit(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let res = returns_unit(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ | help: replace variable usages with `()` | LL ~ let res = (); -LL ~ return_unit(); +LL ~ returns_unit(); LL | LL ~ do_something(()); | From 48ee4705ddf68c452525b931355cf546a1bba023 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Mon, 29 Sep 2025 21:16:15 +0200 Subject: [PATCH 080/226] fix: actually use the `Applicability` --- clippy_lints/src/unit_types/let_unit_value.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/unit_types/let_unit_value.rs b/clippy_lints/src/unit_types/let_unit_value.rs index 1f05617a746f..80a99cfd0544 100644 --- a/clippy_lints/src/unit_types/let_unit_value.rs +++ b/clippy_lints/src/unit_types/let_unit_value.rs @@ -117,11 +117,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, format_args: &FormatArgsStorag init.span, format!("();\n{}", reindent_multiline(&snip, false, indent_of(cx, local.span))), )); - diag.multipart_suggestion( - "replace variable usages with `()`", - suggestions, - Applicability::MachineApplicable, - ); + diag.multipart_suggestion("replace variable usages with `()`", suggestions, app); return; } } @@ -132,7 +128,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, format_args: &FormatArgsStorag } else { "omit the `let` binding and replace variable usages with `()`" }; - diag.multipart_suggestion(message, suggestions, Applicability::MachineApplicable); + diag.multipart_suggestion(message, suggestions, app); }, ); } From f6dd112a0ae3e017828dd49a5dc4e5edd648bca6 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Mon, 29 Sep 2025 19:20:47 +0200 Subject: [PATCH 081/226] fix: create the suggestion "differentially" --- clippy_lints/src/unit_types/let_unit_value.rs | 18 +++-- tests/ui/let_unit.fixed | 31 +++++++- tests/ui/let_unit.rs | 30 ++++++- tests/ui/let_unit.stderr | 78 +++++++++++++------ 4 files changed, 118 insertions(+), 39 deletions(-) diff --git a/clippy_lints/src/unit_types/let_unit_value.rs b/clippy_lints/src/unit_types/let_unit_value.rs index 80a99cfd0544..424aa14cd686 100644 --- a/clippy_lints/src/unit_types/let_unit_value.rs +++ b/clippy_lints/src/unit_types/let_unit_value.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::{FormatArgsStorage, find_format_arg_expr, is_format_macro, root_macro_call_first_node}; -use clippy_utils::source::{indent_of, reindent_multiline, snippet_with_context}; +use clippy_utils::source::{snippet_indent, walk_span_to_context}; use clippy_utils::visitors::{for_each_local_assignment, for_each_value_source}; use core::ops::ControlFlow; use rustc_ast::{FormatArgs, FormatArgumentKind}; @@ -74,10 +74,10 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, format_args: &FormatArgsStorag "this let-binding has unit value", |diag| { let mut suggestions = Vec::new(); + let init_new_span = walk_span_to_context(init.span, local.span.ctxt()).unwrap(); // Suggest omitting the `let` binding - let mut app = Applicability::MachineApplicable; - let snip = snippet_with_context(cx, init.span, local.span.ctxt(), "()", &mut app).0; + let app = Applicability::MachineApplicable; // If this is a binding pattern, we need to add suggestions to remove any usages // of the variable @@ -114,21 +114,23 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, format_args: &FormatArgsStorag // ``` // TODO: find a less awkward way to do this suggestions.push(( - init.span, - format!("();\n{}", reindent_multiline(&snip, false, indent_of(cx, local.span))), + init_new_span.shrink_to_lo(), + format!("();\n{}", snippet_indent(cx, local.span).as_deref().unwrap_or("")), )); - diag.multipart_suggestion("replace variable usages with `()`", suggestions, app); + diag.multipart_suggestion_verbose("replace variable usages with `()`", suggestions, app); return; } } - suggestions.push((local.span, format!("{snip};"))); + // let local = returns_unit(); + // ^^^^^^^^^^^^ remove this + suggestions.push((local.span.until(init_new_span), String::new())); let message = if suggestions.len() == 1 { "omit the `let` binding" } else { "omit the `let` binding and replace variable usages with `()`" }; - diag.multipart_suggestion(message, suggestions, app); + diag.multipart_suggestion_verbose(message, suggestions, app); }, ); } diff --git a/tests/ui/let_unit.fixed b/tests/ui/let_unit.fixed index d640c6a916d0..e8517f18e788 100644 --- a/tests/ui/let_unit.fixed +++ b/tests/ui/let_unit.fixed @@ -1,5 +1,10 @@ #![warn(clippy::let_unit_value)] -#![allow(clippy::no_effect, clippy::needless_late_init, path_statements)] +#![allow( + clippy::no_effect, + clippy::needless_late_init, + path_statements, + clippy::match_single_binding +)] macro_rules! let_and_return { ($n:expr) => {{ @@ -197,12 +202,30 @@ pub fn issue12594() { } } -fn issue15061() { - fn do_something(x: ()) {} +fn takes_unit(x: ()) {} +fn issue15061() { let res = (); returns_unit(); //~^ let_unit_value - do_something(()); + takes_unit(()); + println!("{res:?}"); +} + +fn issue15771() { + match "Example String" { + _ => returns_unit(), + //~^ let_unit_value + } + + if true {} + //~^ let_unit_value +} + +fn issue_15784() { + let res = (); + eprintln!("I return unit"); + //~^ let_unit_value + takes_unit(()); println!("{res:?}"); } diff --git a/tests/ui/let_unit.rs b/tests/ui/let_unit.rs index d966d2ec0a5c..3f6f0139b2fe 100644 --- a/tests/ui/let_unit.rs +++ b/tests/ui/let_unit.rs @@ -1,5 +1,10 @@ #![warn(clippy::let_unit_value)] -#![allow(clippy::no_effect, clippy::needless_late_init, path_statements)] +#![allow( + clippy::no_effect, + clippy::needless_late_init, + path_statements, + clippy::match_single_binding +)] macro_rules! let_and_return { ($n:expr) => {{ @@ -197,11 +202,28 @@ pub fn issue12594() { } } -fn issue15061() { - fn do_something(x: ()) {} +fn takes_unit(x: ()) {} +fn issue15061() { let res = returns_unit(); //~^ let_unit_value - do_something(res); + takes_unit(res); + println!("{res:?}"); +} + +fn issue15771() { + match "Example String" { + _ => _ = returns_unit(), + //~^ let_unit_value + } + + _ = if true {} + //~^ let_unit_value +} + +fn issue_15784() { + let res = eprintln!("I return unit"); + //~^ let_unit_value + takes_unit(res); println!("{res:?}"); } diff --git a/tests/ui/let_unit.stderr b/tests/ui/let_unit.stderr index 4d9bbbe31074..8ced32ab828f 100644 --- a/tests/ui/let_unit.stderr +++ b/tests/ui/let_unit.stderr @@ -1,14 +1,19 @@ error: this let-binding has unit value - --> tests/ui/let_unit.rs:11:5 + --> tests/ui/let_unit.rs:16:5 | LL | let _x = println!("x"); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `println!("x");` + | ^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::let-unit-value` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::let_unit_value)]` +help: omit the `let` binding + | +LL - let _x = println!("x"); +LL + println!("x"); + | error: this let-binding has unit value - --> tests/ui/let_unit.rs:60:5 + --> tests/ui/let_unit.rs:65:5 | LL | / let _ = v LL | | @@ -21,18 +26,12 @@ LL | | .unwrap(); | help: omit the `let` binding | -LL ~ v -LL + -LL + .into_iter() -LL + .map(|i| i * 2) -LL + .filter(|i| i.is_multiple_of(2)) -LL + .map(|_| ()) -LL + .next() -LL + .unwrap(); +LL - let _ = v +LL + v | error: this let-binding has unit value - --> tests/ui/let_unit.rs:110:5 + --> tests/ui/let_unit.rs:115:5 | LL | / let x = match Some(0) { LL | | @@ -45,17 +44,12 @@ LL | | }; | help: omit the `let` binding | -LL ~ match Some(0) { -LL + -LL + None => f2(1), -LL + Some(0) => f(), -LL + Some(1) => f2(3), -LL + Some(_) => (), -LL + }; +LL - let x = match Some(0) { +LL + match Some(0) { | error: this let-binding has unit value - --> tests/ui/let_unit.rs:190:9 + --> tests/ui/let_unit.rs:195:9 | LL | let res = returns_unit(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -69,7 +63,7 @@ LL ~ returns_result(()).unwrap(); | error: this let-binding has unit value - --> tests/ui/let_unit.rs:203:5 + --> tests/ui/let_unit.rs:208:5 | LL | let res = returns_unit(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -79,8 +73,46 @@ help: replace variable usages with `()` LL ~ let res = (); LL ~ returns_unit(); LL | -LL ~ do_something(()); +LL ~ takes_unit(()); | -error: aborting due to 5 previous errors +error: this let-binding has unit value + --> tests/ui/let_unit.rs:216:14 + | +LL | _ => _ = returns_unit(), + | ^^^^^^^^^^^^^^^^^^ + | +help: omit the `let` binding + | +LL - _ => _ = returns_unit(), +LL + _ => returns_unit(), + | + +error: this let-binding has unit value + --> tests/ui/let_unit.rs:220:5 + | +LL | _ = if true {} + | ^^^^^^^^^^^^^^ + | +help: omit the `let` binding + | +LL - _ = if true {} +LL + if true {} + | + +error: this let-binding has unit value + --> tests/ui/let_unit.rs:225:5 + | +LL | let res = eprintln!("I return unit"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace variable usages with `()` + | +LL ~ let res = (); +LL ~ eprintln!("I return unit"); +LL | +LL ~ takes_unit(()); + | + +error: aborting due to 8 previous errors From 93355f1c7b279866718f76609450cfa1b8cc9973 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Fri, 19 Sep 2025 20:17:07 +0200 Subject: [PATCH 082/226] clean-up - reduce indentation - use `snippet_with_applicability` --- clippy_lints/src/new_without_default.rs | 170 ++++++++++++------------ tests/ui/new_without_default.fixed | 1 - tests/ui/new_without_default.rs | 1 - tests/ui/new_without_default.stderr | 18 +-- 4 files changed, 92 insertions(+), 98 deletions(-) diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs index ab7b09c48a7f..5a409a811c30 100644 --- a/clippy_lints/src/new_without_default.rs +++ b/clippy_lints/src/new_without_default.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::return_ty; -use clippy_utils::source::snippet; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::DiagExt; use rustc_errors::Applicability; use rustc_hir as hir; @@ -58,103 +58,99 @@ impl_lint_pass!(NewWithoutDefault => [NEW_WITHOUT_DEFAULT]); impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { - if let hir::ItemKind::Impl(hir::Impl { + let hir::ItemKind::Impl(hir::Impl { of_trait: None, generics, self_ty: impl_self_ty, .. }) = item.kind + else { + return; + }; + + for assoc_item in cx + .tcx + .associated_items(item.owner_id.def_id) + .filter_by_name_unhygienic(sym::new) { - for assoc_item in cx - .tcx - .associated_items(item.owner_id.def_id) - .filter_by_name_unhygienic(sym::new) + if let AssocKind::Fn { has_self: false, .. } = assoc_item.kind + && let impl_item = cx + .tcx + .hir_node_by_def_id(assoc_item.def_id.expect_local()) + .expect_impl_item() + && !impl_item.span.in_external_macro(cx.sess().source_map()) + && let hir::ImplItemKind::Fn(ref sig, _) = impl_item.kind + && let id = impl_item.owner_id + // can't be implemented for unsafe new + && !sig.header.is_unsafe() + // shouldn't be implemented when it is hidden in docs + && !cx.tcx.is_doc_hidden(impl_item.owner_id.def_id) + // when the result of `new()` depends on a parameter we should not require + // an impl of `Default` + && impl_item.generics.params.is_empty() + && sig.decl.inputs.is_empty() + && cx.effective_visibilities.is_exported(impl_item.owner_id.def_id) + && let self_ty = cx.tcx.type_of(item.owner_id).instantiate_identity() + && self_ty == return_ty(cx, impl_item.owner_id) + && let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default) { - if let AssocKind::Fn { has_self: false, .. } = assoc_item.kind { - let impl_item = cx - .tcx - .hir_node_by_def_id(assoc_item.def_id.expect_local()) - .expect_impl_item(); - if impl_item.span.in_external_macro(cx.sess().source_map()) { - return; - } - if let hir::ImplItemKind::Fn(ref sig, _) = impl_item.kind { - let id = impl_item.owner_id; - if sig.header.is_unsafe() { - // can't be implemented for unsafe new - return; - } - if cx.tcx.is_doc_hidden(impl_item.owner_id.def_id) { - // shouldn't be implemented when it is hidden in docs - return; - } - if !impl_item.generics.params.is_empty() { - // when the result of `new()` depends on a parameter we should not require - // an impl of `Default` - return; - } - if sig.decl.inputs.is_empty() - && cx.effective_visibilities.is_exported(impl_item.owner_id.def_id) - && let self_ty = cx.tcx.type_of(item.owner_id).instantiate_identity() - && self_ty == return_ty(cx, impl_item.owner_id) - && let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default) + if self.impling_types.is_none() { + let mut impls = HirIdSet::default(); + for &d in cx.tcx.local_trait_impls(default_trait_id) { + let ty = cx.tcx.type_of(d).instantiate_identity(); + if let Some(ty_def) = ty.ty_adt_def() + && let Some(local_def_id) = ty_def.did().as_local() { - if self.impling_types.is_none() { - let mut impls = HirIdSet::default(); - for &d in cx.tcx.local_trait_impls(default_trait_id) { - let ty = cx.tcx.type_of(d).instantiate_identity(); - if let Some(ty_def) = ty.ty_adt_def() - && let Some(local_def_id) = ty_def.did().as_local() - { - impls.insert(cx.tcx.local_def_id_to_hir_id(local_def_id)); - } - } - self.impling_types = Some(impls); - } - - // Check if a Default implementation exists for the Self type, regardless of - // generics - if let Some(ref impling_types) = self.impling_types - && let self_def = cx.tcx.type_of(item.owner_id).instantiate_identity() - && let Some(self_def) = self_def.ty_adt_def() - && let Some(self_local_did) = self_def.did().as_local() - && let self_id = cx.tcx.local_def_id_to_hir_id(self_local_did) - && impling_types.contains(&self_id) - { - return; - } - - let generics_sugg = snippet(cx, generics.span, ""); - let where_clause_sugg = if generics.has_where_clause_predicates { - format!("\n{}\n", snippet(cx, generics.where_clause_span, "")) - } else { - String::new() - }; - let self_ty_fmt = self_ty.to_string(); - let self_type_snip = snippet(cx, impl_self_ty.span, &self_ty_fmt); - span_lint_hir_and_then( - cx, - NEW_WITHOUT_DEFAULT, - id.into(), - impl_item.span, - format!("you should consider adding a `Default` implementation for `{self_type_snip}`"), - |diag| { - diag.suggest_prepend_item( - cx, - item.span, - "try adding this", - &create_new_without_default_suggest_msg( - &self_type_snip, - &generics_sugg, - &where_clause_sugg, - ), - Applicability::MachineApplicable, - ); - }, - ); + impls.insert(cx.tcx.local_def_id_to_hir_id(local_def_id)); } } + self.impling_types = Some(impls); } + + // Check if a Default implementation exists for the Self type, regardless of + // generics + if let Some(ref impling_types) = self.impling_types + && let self_def = cx.tcx.type_of(item.owner_id).instantiate_identity() + && let Some(self_def) = self_def.ty_adt_def() + && let Some(self_local_did) = self_def.did().as_local() + && let self_id = cx.tcx.local_def_id_to_hir_id(self_local_did) + && impling_types.contains(&self_id) + { + return; + } + + let mut app = Applicability::MachineApplicable; + let generics_sugg = snippet_with_applicability(cx, generics.span, "", &mut app); + let where_clause_sugg = if generics.has_where_clause_predicates { + format!( + "\n{}\n", + snippet_with_applicability(cx, generics.where_clause_span, "", &mut app) + ) + } else { + String::new() + }; + let self_ty_fmt = self_ty.to_string(); + let self_type_snip = snippet_with_applicability(cx, impl_self_ty.span, &self_ty_fmt, &mut app); + span_lint_hir_and_then( + cx, + NEW_WITHOUT_DEFAULT, + id.into(), + impl_item.span, + format!("you should consider adding a `Default` implementation for `{self_type_snip}`"), + |diag| { + diag.suggest_prepend_item( + cx, + item.span, + "try adding this", + &create_new_without_default_suggest_msg( + &self_type_snip, + &generics_sugg, + &where_clause_sugg, + ), + app, + ); + }, + ); } } } diff --git a/tests/ui/new_without_default.fixed b/tests/ui/new_without_default.fixed index b5558cd3086c..4eb2a9903b9a 100644 --- a/tests/ui/new_without_default.fixed +++ b/tests/ui/new_without_default.fixed @@ -1,5 +1,4 @@ #![allow( - dead_code, clippy::missing_safety_doc, clippy::extra_unused_lifetimes, clippy::extra_unused_type_parameters, diff --git a/tests/ui/new_without_default.rs b/tests/ui/new_without_default.rs index be598c81718f..cfcff1e3ac3d 100644 --- a/tests/ui/new_without_default.rs +++ b/tests/ui/new_without_default.rs @@ -1,5 +1,4 @@ #![allow( - dead_code, clippy::missing_safety_doc, clippy::extra_unused_lifetimes, clippy::extra_unused_type_parameters, diff --git a/tests/ui/new_without_default.stderr b/tests/ui/new_without_default.stderr index 70a65aba464b..a0b4d3704f56 100644 --- a/tests/ui/new_without_default.stderr +++ b/tests/ui/new_without_default.stderr @@ -1,5 +1,5 @@ error: you should consider adding a `Default` implementation for `Foo` - --> tests/ui/new_without_default.rs:13:5 + --> tests/ui/new_without_default.rs:12:5 | LL | / pub fn new() -> Foo { LL | | @@ -20,7 +20,7 @@ LL + } | error: you should consider adding a `Default` implementation for `Bar` - --> tests/ui/new_without_default.rs:23:5 + --> tests/ui/new_without_default.rs:22:5 | LL | / pub fn new() -> Self { LL | | @@ -39,7 +39,7 @@ LL + } | error: you should consider adding a `Default` implementation for `LtKo<'c>` - --> tests/ui/new_without_default.rs:89:5 + --> tests/ui/new_without_default.rs:88:5 | LL | / pub fn new() -> LtKo<'c> { LL | | @@ -58,7 +58,7 @@ LL + } | error: you should consider adding a `Default` implementation for `Const` - --> tests/ui/new_without_default.rs:123:5 + --> tests/ui/new_without_default.rs:122:5 | LL | / pub const fn new() -> Const { LL | | @@ -76,7 +76,7 @@ LL + } | error: you should consider adding a `Default` implementation for `NewNotEqualToDerive` - --> tests/ui/new_without_default.rs:184:5 + --> tests/ui/new_without_default.rs:183:5 | LL | / pub fn new() -> Self { LL | | @@ -95,7 +95,7 @@ LL + } | error: you should consider adding a `Default` implementation for `FooGenerics` - --> tests/ui/new_without_default.rs:194:5 + --> tests/ui/new_without_default.rs:193:5 | LL | / pub fn new() -> Self { LL | | @@ -114,7 +114,7 @@ LL + } | error: you should consider adding a `Default` implementation for `BarGenerics` - --> tests/ui/new_without_default.rs:203:5 + --> tests/ui/new_without_default.rs:202:5 | LL | / pub fn new() -> Self { LL | | @@ -133,7 +133,7 @@ LL + } | error: you should consider adding a `Default` implementation for `Foo` - --> tests/ui/new_without_default.rs:216:9 + --> tests/ui/new_without_default.rs:215:9 | LL | / pub fn new() -> Self { LL | | @@ -154,7 +154,7 @@ LL ~ impl Foo { | error: you should consider adding a `Default` implementation for `MyStruct` - --> tests/ui/new_without_default.rs:263:5 + --> tests/ui/new_without_default.rs:262:5 | LL | / pub fn new() -> Self { LL | | From 7a04adae7d68f9a404e5843039479b954cfa0bb5 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Fri, 19 Sep 2025 22:21:16 +0200 Subject: [PATCH 083/226] fix(new_without_default): if `new` has `#[cfg]`, copy that onto `impl Default` --- clippy_lints/src/new_without_default.rs | 30 +++++++-- tests/ui/new_without_default.fixed | 68 +++++++++++++++++++++ tests/ui/new_without_default.rs | 40 ++++++++++++ tests/ui/new_without_default.stderr | 81 ++++++++++++++++++++++++- 4 files changed, 213 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs index 5a409a811c30..6fc034b6fc5d 100644 --- a/clippy_lints/src/new_without_default.rs +++ b/clippy_lints/src/new_without_default.rs @@ -74,10 +74,8 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { .filter_by_name_unhygienic(sym::new) { if let AssocKind::Fn { has_self: false, .. } = assoc_item.kind - && let impl_item = cx - .tcx - .hir_node_by_def_id(assoc_item.def_id.expect_local()) - .expect_impl_item() + && let assoc_item_hir_id = cx.tcx.local_def_id_to_hir_id(assoc_item.def_id.expect_local()) + && let impl_item = cx.tcx.hir_node(assoc_item_hir_id).expect_impl_item() && !impl_item.span.in_external_macro(cx.sess().source_map()) && let hir::ImplItemKind::Fn(ref sig, _) = impl_item.kind && let id = impl_item.owner_id @@ -120,6 +118,26 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { } let mut app = Applicability::MachineApplicable; + let attrs_sugg = { + let mut sugg = String::new(); + for attr in cx.tcx.hir_attrs(assoc_item_hir_id) { + if !attr.has_name(sym::cfg_trace) { + // This might be some other attribute that the `impl Default` ought to inherit. + // But it could also be one of the many attributes that: + // - can't be put on an impl block -- like `#[inline]` + // - we can't even build a suggestion for, since `Attribute::span` may panic. + // + // Because of all that, remain on the safer side -- don't inherit this attr, and just + // reduce the applicability + app = Applicability::MaybeIncorrect; + continue; + } + + sugg.push_str(&snippet_with_applicability(cx.sess(), attr.span(), "_", &mut app)); + sugg.push('\n'); + } + sugg + }; let generics_sugg = snippet_with_applicability(cx, generics.span, "", &mut app); let where_clause_sugg = if generics.has_where_clause_predicates { format!( @@ -143,6 +161,7 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { item.span, "try adding this", &create_new_without_default_suggest_msg( + &attrs_sugg, &self_type_snip, &generics_sugg, &where_clause_sugg, @@ -157,13 +176,14 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { } fn create_new_without_default_suggest_msg( + attrs_sugg: &str, self_type_snip: &str, generics_sugg: &str, where_clause_sugg: &str, ) -> String { #[rustfmt::skip] format!( -"impl{generics_sugg} Default for {self_type_snip}{where_clause_sugg} {{ +"{attrs_sugg}impl{generics_sugg} Default for {self_type_snip}{where_clause_sugg} {{ fn default() -> Self {{ Self::new() }} diff --git a/tests/ui/new_without_default.fixed b/tests/ui/new_without_default.fixed index 4eb2a9903b9a..9a5e90b48065 100644 --- a/tests/ui/new_without_default.fixed +++ b/tests/ui/new_without_default.fixed @@ -322,6 +322,74 @@ where } } +// From issue #14552, but with `#[cfg]`s that are actually `true` in the uitest context + +pub struct NewWithCfg; +#[cfg(not(test))] +impl Default for NewWithCfg { + fn default() -> Self { + Self::new() + } +} + +impl NewWithCfg { + #[cfg(not(test))] + pub fn new() -> Self { + //~^ new_without_default + unimplemented!() + } +} + +pub struct NewWith2Cfgs; +#[cfg(not(test))] +#[cfg(panic = "unwind")] +impl Default for NewWith2Cfgs { + fn default() -> Self { + Self::new() + } +} + +impl NewWith2Cfgs { + #[cfg(not(test))] + #[cfg(panic = "unwind")] + pub fn new() -> Self { + //~^ new_without_default + unimplemented!() + } +} + +pub struct NewWithExtraneous; +impl Default for NewWithExtraneous { + fn default() -> Self { + Self::new() + } +} + +impl NewWithExtraneous { + #[inline] + pub fn new() -> Self { + //~^ new_without_default + unimplemented!() + } +} + +pub struct NewWithCfgAndExtraneous; +#[cfg(not(test))] +impl Default for NewWithCfgAndExtraneous { + fn default() -> Self { + Self::new() + } +} + +impl NewWithCfgAndExtraneous { + #[cfg(not(test))] + #[inline] + pub fn new() -> Self { + //~^ new_without_default + unimplemented!() + } +} + mod issue15778 { pub struct Foo(Vec); diff --git a/tests/ui/new_without_default.rs b/tests/ui/new_without_default.rs index cfcff1e3ac3d..f7466aa32189 100644 --- a/tests/ui/new_without_default.rs +++ b/tests/ui/new_without_default.rs @@ -265,6 +265,46 @@ where } } +// From issue #14552, but with `#[cfg]`s that are actually `true` in the uitest context + +pub struct NewWithCfg; +impl NewWithCfg { + #[cfg(not(test))] + pub fn new() -> Self { + //~^ new_without_default + unimplemented!() + } +} + +pub struct NewWith2Cfgs; +impl NewWith2Cfgs { + #[cfg(not(test))] + #[cfg(panic = "unwind")] + pub fn new() -> Self { + //~^ new_without_default + unimplemented!() + } +} + +pub struct NewWithExtraneous; +impl NewWithExtraneous { + #[inline] + pub fn new() -> Self { + //~^ new_without_default + unimplemented!() + } +} + +pub struct NewWithCfgAndExtraneous; +impl NewWithCfgAndExtraneous { + #[cfg(not(test))] + #[inline] + pub fn new() -> Self { + //~^ new_without_default + unimplemented!() + } +} + mod issue15778 { pub struct Foo(Vec); diff --git a/tests/ui/new_without_default.stderr b/tests/ui/new_without_default.stderr index a0b4d3704f56..1e0d5e213199 100644 --- a/tests/ui/new_without_default.stderr +++ b/tests/ui/new_without_default.stderr @@ -174,5 +174,84 @@ LL + } LL + } | -error: aborting due to 9 previous errors +error: you should consider adding a `Default` implementation for `NewWithCfg` + --> tests/ui/new_without_default.rs:273:5 + | +LL | / pub fn new() -> Self { +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | +help: try adding this + | +LL + #[cfg(not(test))] +LL + impl Default for NewWithCfg { +LL + fn default() -> Self { +LL + Self::new() +LL + } +LL + } +LL | impl NewWithCfg { + | + +error: you should consider adding a `Default` implementation for `NewWith2Cfgs` + --> tests/ui/new_without_default.rs:283:5 + | +LL | / pub fn new() -> Self { +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | +help: try adding this + | +LL + #[cfg(not(test))] +LL + #[cfg(panic = "unwind")] +LL + impl Default for NewWith2Cfgs { +LL + fn default() -> Self { +LL + Self::new() +LL + } +LL + } +LL | impl NewWith2Cfgs { + | + +error: you should consider adding a `Default` implementation for `NewWithExtraneous` + --> tests/ui/new_without_default.rs:292:5 + | +LL | / pub fn new() -> Self { +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | +help: try adding this + | +LL + impl Default for NewWithExtraneous { +LL + fn default() -> Self { +LL + Self::new() +LL + } +LL + } + | + +error: you should consider adding a `Default` implementation for `NewWithCfgAndExtraneous` + --> tests/ui/new_without_default.rs:302:5 + | +LL | / pub fn new() -> Self { +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | +help: try adding this + | +LL + #[cfg(not(test))] +LL + impl Default for NewWithCfgAndExtraneous { +LL + fn default() -> Self { +LL + Self::new() +LL + } +LL + } +LL | impl NewWithCfgAndExtraneous { + | + +error: aborting due to 13 previous errors From d615d2ff34566c399862cfe0feb96d05767ce5ce Mon Sep 17 00:00:00 2001 From: joboet Date: Tue, 30 Sep 2025 11:59:08 +0200 Subject: [PATCH 084/226] std: call WinSock cleanup function directly instead of through its reexport --- library/std/src/sys/net/connection/socket/windows.rs | 2 +- library/std/src/sys/pal/windows/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std/src/sys/net/connection/socket/windows.rs b/library/std/src/sys/net/connection/socket/windows.rs index 5b6f4cedf1b7..2221023a2125 100644 --- a/library/std/src/sys/net/connection/socket/windows.rs +++ b/library/std/src/sys/net/connection/socket/windows.rs @@ -111,7 +111,7 @@ pub(super) mod netc { } } -pub use crate::sys::pal::winsock::{cleanup, cvt, cvt_gai, cvt_r, startup as init}; +pub use crate::sys::pal::winsock::{cvt, cvt_gai, cvt_r, startup as init}; #[expect(missing_debug_implementations)] pub struct Socket(OwnedSocket); diff --git a/library/std/src/sys/pal/windows/mod.rs b/library/std/src/sys/pal/windows/mod.rs index 18ab34982676..a5f060080130 100644 --- a/library/std/src/sys/pal/windows/mod.rs +++ b/library/std/src/sys/pal/windows/mod.rs @@ -58,7 +58,7 @@ pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) { // SAFETY: must be called only once during runtime cleanup. // NOTE: this is not guaranteed to run, for example when the program aborts. pub unsafe fn cleanup() { - crate::sys::net::cleanup(); + winsock::cleanup(); } #[inline] From eba1416c015803aab6d3ac0b7efebb28165b50ab Mon Sep 17 00:00:00 2001 From: joboet Date: Tue, 30 Sep 2025 12:43:36 +0200 Subject: [PATCH 085/226] std: improve internal socket functions --- .../std/src/sys/net/connection/socket/mod.rs | 92 ++++++++++++++----- 1 file changed, 67 insertions(+), 25 deletions(-) diff --git a/library/std/src/sys/net/connection/socket/mod.rs b/library/std/src/sys/net/connection/socket/mod.rs index 1dd06e97bbab..8a758947cc2d 100644 --- a/library/std/src/sys/net/connection/socket/mod.rs +++ b/library/std/src/sys/net/connection/socket/mod.rs @@ -3,6 +3,7 @@ mod tests; use crate::ffi::{c_int, c_void}; use crate::io::{self, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut}; +use crate::mem::MaybeUninit; use crate::net::{ Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs, }; @@ -177,6 +178,11 @@ fn socket_addr_to_c(addr: &SocketAddr) -> (SocketAddrCRepr, c::socklen_t) { } } +/// Converts the C socket address stored in `storage` to a Rust `SocketAddr`. +/// +/// # Safety +/// * `storage` must contain a valid C socket address whose length is no larger +/// than `len`. unsafe fn socket_addr_from_c( storage: *const c::sockaddr_storage, len: usize, @@ -202,49 +208,85 @@ unsafe fn socket_addr_from_c( // sockaddr and misc bindings //////////////////////////////////////////////////////////////////////////////// -pub fn setsockopt( +/// Sets the value of a socket option. +/// +/// # Safety +/// `T` must be the type associated with the given socket option. +pub unsafe fn setsockopt( sock: &Socket, level: c_int, option_name: c_int, option_value: T, ) -> io::Result<()> { - unsafe { - cvt(c::setsockopt( + let option_len = size_of::() as c::socklen_t; + // SAFETY: + // * `sock` is opened for the duration of this call, as `sock` owns the socket. + // * the pointer to `option_value` is readable at a size of `size_of::` + // bytes + // * the value of `option_value` has a valid type for the given socket option + // (guaranteed by caller). + cvt(unsafe { + c::setsockopt( sock.as_raw(), level, option_name, (&raw const option_value) as *const _, - size_of::() as c::socklen_t, - ))?; - Ok(()) - } + option_len, + ) + })?; + Ok(()) } -pub fn getsockopt(sock: &Socket, level: c_int, option_name: c_int) -> io::Result { - unsafe { - let mut option_value: T = mem::zeroed(); - let mut option_len = size_of::() as c::socklen_t; - cvt(c::getsockopt( +/// Gets the value of a socket option. +/// +/// # Safety +/// `T` must be the type associated with the given socket option. +pub unsafe fn getsockopt( + sock: &Socket, + level: c_int, + option_name: c_int, +) -> io::Result { + let mut option_value = MaybeUninit::::zeroed(); + let mut option_len = size_of::() as c::socklen_t; + + // SAFETY: + // * `sock` is opened for the duration of this call, as `sock` owns the socket. + // * the pointer to `option_value` is writable and the stack allocation has + // space for `size_of::` bytes. + cvt(unsafe { + c::getsockopt( sock.as_raw(), level, option_name, - (&raw mut option_value) as *mut _, + option_value.as_mut_ptr().cast(), &mut option_len, - ))?; - Ok(option_value) - } + ) + })?; + + // SAFETY: the `getsockopt` call succeeded and the caller guarantees that + // `T` is the type of this option, thus `option_value` must have + // been initialized by the system. + Ok(unsafe { option_value.assume_init() }) } -fn sockname(f: F) -> io::Result +/// Wraps a call to a platform function that returns a socket address. +/// +/// # Safety +/// * if `f` returns a success (i.e. `cvt` returns `Ok` when called on the +/// return value), the buffer provided to `f` must have been initialized +/// with a valid C socket address, the length of which must be written +/// to the second argument. +unsafe fn sockname(f: F) -> io::Result where F: FnOnce(*mut c::sockaddr, *mut c::socklen_t) -> c_int, { - unsafe { - let mut storage: c::sockaddr_storage = mem::zeroed(); - let mut len = size_of_val(&storage) as c::socklen_t; - cvt(f((&raw mut storage) as *mut _, &mut len))?; - socket_addr_from_c(&storage, len as usize) - } + let mut storage = MaybeUninit::::zeroed(); + let mut len = size_of::() as c::socklen_t; + cvt(f(storage.as_mut_ptr().cast(), &mut len))?; + // SAFETY: + // The caller guarantees that the storage has been successfully initialized + // and its size written to `len` if `f` returns a success. + unsafe { socket_addr_from_c(storage.as_ptr(), len as usize) } } #[cfg(target_os = "android")] @@ -546,8 +588,8 @@ impl TcpListener { // The `accept` function will fill in the storage with the address, // so we don't need to zero it here. // reference: https://linux.die.net/man/2/accept4 - let mut storage: mem::MaybeUninit = mem::MaybeUninit::uninit(); - let mut len = size_of_val(&storage) as c::socklen_t; + let mut storage = MaybeUninit::::uninit(); + let mut len = size_of::() as c::socklen_t; let sock = self.inner.accept(storage.as_mut_ptr() as *mut _, &mut len)?; let addr = unsafe { socket_addr_from_c(storage.as_ptr(), len as usize)? }; Ok((TcpStream { inner: sock }, addr)) From aa1263e7684341a73b600eaf0bbc70067e196243 Mon Sep 17 00:00:00 2001 From: joboet Date: Tue, 30 Sep 2025 16:55:21 +0200 Subject: [PATCH 086/226] std: add missing unsafe blocks --- .../src/sys/net/connection/socket/hermit.rs | 14 +- .../std/src/sys/net/connection/socket/mod.rs | 88 ++++++----- .../src/sys/net/connection/socket/solid.rs | 14 +- .../std/src/sys/net/connection/socket/unix.rs | 148 +++++++++--------- .../src/sys/net/connection/socket/wasip2.rs | 10 +- .../src/sys/net/connection/socket/windows.rs | 14 +- 6 files changed, 154 insertions(+), 134 deletions(-) diff --git a/library/std/src/sys/net/connection/socket/hermit.rs b/library/std/src/sys/net/connection/socket/hermit.rs index 5200eaa5786a..0c105ed20fa7 100644 --- a/library/std/src/sys/net/connection/socket/hermit.rs +++ b/library/std/src/sys/net/connection/socket/hermit.rs @@ -242,11 +242,11 @@ impl Socket { None => netc::timeval { tv_sec: 0, tv_usec: 0 }, }; - setsockopt(self, netc::SOL_SOCKET, kind, timeout) + unsafe { setsockopt(self, netc::SOL_SOCKET, kind, timeout) } } pub fn timeout(&self, kind: i32) -> io::Result> { - let raw: netc::timeval = getsockopt(self, netc::SOL_SOCKET, kind)?; + let raw: netc::timeval = unsafe { getsockopt(self, netc::SOL_SOCKET, kind)? }; if raw.tv_sec == 0 && raw.tv_usec == 0 { Ok(None) } else { @@ -272,22 +272,22 @@ impl Socket { l_linger: linger.unwrap_or_default().as_secs() as libc::c_int, }; - setsockopt(self, netc::SOL_SOCKET, netc::SO_LINGER, linger) + unsafe { setsockopt(self, netc::SOL_SOCKET, netc::SO_LINGER, linger) } } pub fn linger(&self) -> io::Result> { - let val: netc::linger = getsockopt(self, netc::SOL_SOCKET, netc::SO_LINGER)?; + let val: netc::linger = unsafe { getsockopt(self, netc::SOL_SOCKET, netc::SO_LINGER)? }; Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64))) } pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { let value: i32 = if nodelay { 1 } else { 0 }; - setsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY, value) + unsafe { setsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY, value) } } pub fn nodelay(&self) -> io::Result { - let raw: i32 = getsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY)?; + let raw: i32 = unsafe { getsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY)? }; Ok(raw != 0) } @@ -304,7 +304,7 @@ impl Socket { } pub fn take_error(&self) -> io::Result> { - let raw: c_int = getsockopt(self, libc::SOL_SOCKET, libc::SO_ERROR)?; + let raw: c_int = unsafe { getsockopt(self, libc::SOL_SOCKET, libc::SO_ERROR)? }; if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } } diff --git a/library/std/src/sys/net/connection/socket/mod.rs b/library/std/src/sys/net/connection/socket/mod.rs index 8a758947cc2d..d01a52b86109 100644 --- a/library/std/src/sys/net/connection/socket/mod.rs +++ b/library/std/src/sys/net/connection/socket/mod.rs @@ -442,11 +442,11 @@ impl TcpStream { } pub fn peer_addr(&self) -> io::Result { - sockname(|buf, len| unsafe { c::getpeername(self.inner.as_raw(), buf, len) }) + unsafe { sockname(|buf, len| c::getpeername(self.inner.as_raw(), buf, len)) } } pub fn socket_addr(&self) -> io::Result { - sockname(|buf, len| unsafe { c::getsockname(self.inner.as_raw(), buf, len) }) + unsafe { sockname(|buf, len| c::getsockname(self.inner.as_raw(), buf, len)) } } pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { @@ -474,11 +474,11 @@ impl TcpStream { } pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { - setsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL, ttl as c_int) + unsafe { setsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL, ttl as c_int) } } pub fn ttl(&self) -> io::Result { - let raw: c_int = getsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL)?; + let raw: c_int = unsafe { getsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL)? }; Ok(raw as u32) } @@ -545,7 +545,9 @@ impl TcpListener { // which allows “socket hijacking”, so we explicitly don't set it here. // https://docs.microsoft.com/en-us/windows/win32/winsock/using-so-reuseaddr-and-so-exclusiveaddruse #[cfg(not(windows))] - setsockopt(&sock, c::SOL_SOCKET, c::SO_REUSEADDR, 1 as c_int)?; + unsafe { + setsockopt(&sock, c::SOL_SOCKET, c::SO_REUSEADDR, 1 as c_int)? + }; // Bind our new socket let (addr, len) = socket_addr_to_c(addr); @@ -581,7 +583,7 @@ impl TcpListener { } pub fn socket_addr(&self) -> io::Result { - sockname(|buf, len| unsafe { c::getsockname(self.inner.as_raw(), buf, len) }) + unsafe { sockname(|buf, len| c::getsockname(self.inner.as_raw(), buf, len)) } } pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { @@ -600,20 +602,20 @@ impl TcpListener { } pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { - setsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL, ttl as c_int) + unsafe { setsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL, ttl as c_int) } } pub fn ttl(&self) -> io::Result { - let raw: c_int = getsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL)?; + let raw: c_int = unsafe { getsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL)? }; Ok(raw as u32) } pub fn set_only_v6(&self, only_v6: bool) -> io::Result<()> { - setsockopt(&self.inner, c::IPPROTO_IPV6, c::IPV6_V6ONLY, only_v6 as c_int) + unsafe { setsockopt(&self.inner, c::IPPROTO_IPV6, c::IPV6_V6ONLY, only_v6 as c_int) } } pub fn only_v6(&self) -> io::Result { - let raw: c_int = getsockopt(&self.inner, c::IPPROTO_IPV6, c::IPV6_V6ONLY)?; + let raw: c_int = unsafe { getsockopt(&self.inner, c::IPPROTO_IPV6, c::IPV6_V6ONLY)? }; Ok(raw != 0) } @@ -676,11 +678,11 @@ impl UdpSocket { } pub fn peer_addr(&self) -> io::Result { - sockname(|buf, len| unsafe { c::getpeername(self.inner.as_raw(), buf, len) }) + unsafe { sockname(|buf, len| c::getpeername(self.inner.as_raw(), buf, len)) } } pub fn socket_addr(&self) -> io::Result { - sockname(|buf, len| unsafe { c::getsockname(self.inner.as_raw(), buf, len) }) + unsafe { sockname(|buf, len| c::getsockname(self.inner.as_raw(), buf, len)) } } pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { @@ -728,48 +730,62 @@ impl UdpSocket { } pub fn set_broadcast(&self, broadcast: bool) -> io::Result<()> { - setsockopt(&self.inner, c::SOL_SOCKET, c::SO_BROADCAST, broadcast as c_int) + unsafe { setsockopt(&self.inner, c::SOL_SOCKET, c::SO_BROADCAST, broadcast as c_int) } } pub fn broadcast(&self) -> io::Result { - let raw: c_int = getsockopt(&self.inner, c::SOL_SOCKET, c::SO_BROADCAST)?; + let raw: c_int = unsafe { getsockopt(&self.inner, c::SOL_SOCKET, c::SO_BROADCAST)? }; Ok(raw != 0) } pub fn set_multicast_loop_v4(&self, multicast_loop_v4: bool) -> io::Result<()> { - setsockopt( - &self.inner, - c::IPPROTO_IP, - c::IP_MULTICAST_LOOP, - multicast_loop_v4 as IpV4MultiCastType, - ) + unsafe { + setsockopt( + &self.inner, + c::IPPROTO_IP, + c::IP_MULTICAST_LOOP, + multicast_loop_v4 as IpV4MultiCastType, + ) + } } pub fn multicast_loop_v4(&self) -> io::Result { - let raw: IpV4MultiCastType = getsockopt(&self.inner, c::IPPROTO_IP, c::IP_MULTICAST_LOOP)?; + let raw: IpV4MultiCastType = + unsafe { getsockopt(&self.inner, c::IPPROTO_IP, c::IP_MULTICAST_LOOP)? }; Ok(raw != 0) } pub fn set_multicast_ttl_v4(&self, multicast_ttl_v4: u32) -> io::Result<()> { - setsockopt( - &self.inner, - c::IPPROTO_IP, - c::IP_MULTICAST_TTL, - multicast_ttl_v4 as IpV4MultiCastType, - ) + unsafe { + setsockopt( + &self.inner, + c::IPPROTO_IP, + c::IP_MULTICAST_TTL, + multicast_ttl_v4 as IpV4MultiCastType, + ) + } } pub fn multicast_ttl_v4(&self) -> io::Result { - let raw: IpV4MultiCastType = getsockopt(&self.inner, c::IPPROTO_IP, c::IP_MULTICAST_TTL)?; + let raw: IpV4MultiCastType = + unsafe { getsockopt(&self.inner, c::IPPROTO_IP, c::IP_MULTICAST_TTL)? }; Ok(raw as u32) } pub fn set_multicast_loop_v6(&self, multicast_loop_v6: bool) -> io::Result<()> { - setsockopt(&self.inner, c::IPPROTO_IPV6, c::IPV6_MULTICAST_LOOP, multicast_loop_v6 as c_int) + unsafe { + setsockopt( + &self.inner, + c::IPPROTO_IPV6, + c::IPV6_MULTICAST_LOOP, + multicast_loop_v6 as c_int, + ) + } } pub fn multicast_loop_v6(&self) -> io::Result { - let raw: c_int = getsockopt(&self.inner, c::IPPROTO_IPV6, c::IPV6_MULTICAST_LOOP)?; + let raw: c_int = + unsafe { getsockopt(&self.inner, c::IPPROTO_IPV6, c::IPV6_MULTICAST_LOOP)? }; Ok(raw != 0) } @@ -778,7 +794,7 @@ impl UdpSocket { imr_multiaddr: ip_v4_addr_to_c(multiaddr), imr_interface: ip_v4_addr_to_c(interface), }; - setsockopt(&self.inner, c::IPPROTO_IP, c::IP_ADD_MEMBERSHIP, mreq) + unsafe { setsockopt(&self.inner, c::IPPROTO_IP, c::IP_ADD_MEMBERSHIP, mreq) } } pub fn join_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> { @@ -786,7 +802,7 @@ impl UdpSocket { ipv6mr_multiaddr: ip_v6_addr_to_c(multiaddr), ipv6mr_interface: to_ipv6mr_interface(interface), }; - setsockopt(&self.inner, c::IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, mreq) + unsafe { setsockopt(&self.inner, c::IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, mreq) } } pub fn leave_multicast_v4(&self, multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> io::Result<()> { @@ -794,7 +810,7 @@ impl UdpSocket { imr_multiaddr: ip_v4_addr_to_c(multiaddr), imr_interface: ip_v4_addr_to_c(interface), }; - setsockopt(&self.inner, c::IPPROTO_IP, c::IP_DROP_MEMBERSHIP, mreq) + unsafe { setsockopt(&self.inner, c::IPPROTO_IP, c::IP_DROP_MEMBERSHIP, mreq) } } pub fn leave_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> { @@ -802,15 +818,15 @@ impl UdpSocket { ipv6mr_multiaddr: ip_v6_addr_to_c(multiaddr), ipv6mr_interface: to_ipv6mr_interface(interface), }; - setsockopt(&self.inner, c::IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, mreq) + unsafe { setsockopt(&self.inner, c::IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, mreq) } } pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { - setsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL, ttl as c_int) + unsafe { setsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL, ttl as c_int) } } pub fn ttl(&self) -> io::Result { - let raw: c_int = getsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL)?; + let raw: c_int = unsafe { getsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL)? }; Ok(raw as u32) } diff --git a/library/std/src/sys/net/connection/socket/solid.rs b/library/std/src/sys/net/connection/socket/solid.rs index 94bb605c1007..dfde5513f909 100644 --- a/library/std/src/sys/net/connection/socket/solid.rs +++ b/library/std/src/sys/net/connection/socket/solid.rs @@ -303,11 +303,11 @@ impl Socket { } None => netc::timeval { tv_sec: 0, tv_usec: 0 }, }; - setsockopt(self, netc::SOL_SOCKET, kind, timeout) + unsafe { setsockopt(self, netc::SOL_SOCKET, kind, timeout) } } pub fn timeout(&self, kind: c_int) -> io::Result> { - let raw: netc::timeval = getsockopt(self, netc::SOL_SOCKET, kind)?; + let raw: netc::timeval = unsafe { getsockopt(self, netc::SOL_SOCKET, kind)? }; if raw.tv_sec == 0 && raw.tv_usec == 0 { Ok(None) } else { @@ -333,21 +333,21 @@ impl Socket { l_linger: linger.unwrap_or_default().as_secs() as netc::c_int, }; - setsockopt(self, netc::SOL_SOCKET, netc::SO_LINGER, linger) + unsafe { setsockopt(self, netc::SOL_SOCKET, netc::SO_LINGER, linger) } } pub fn linger(&self) -> io::Result> { - let val: netc::linger = getsockopt(self, netc::SOL_SOCKET, netc::SO_LINGER)?; + let val: netc::linger = unsafe { getsockopt(self, netc::SOL_SOCKET, netc::SO_LINGER)? }; Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64))) } pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { - setsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY, nodelay as c_int) + unsafe { setsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY, nodelay as c_int) } } pub fn nodelay(&self) -> io::Result { - let raw: c_int = getsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY)?; + let raw: c_int = unsafe { getsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY)? }; Ok(raw != 0) } @@ -360,7 +360,7 @@ impl Socket { } pub fn take_error(&self) -> io::Result> { - let raw: c_int = getsockopt(self, netc::SOL_SOCKET, netc::SO_ERROR)?; + let raw: c_int = unsafe { getsockopt(self, netc::SOL_SOCKET, netc::SO_ERROR)? }; if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } } diff --git a/library/std/src/sys/net/connection/socket/unix.rs b/library/std/src/sys/net/connection/socket/unix.rs index a191576d93b9..3f05449903fd 100644 --- a/library/std/src/sys/net/connection/socket/unix.rs +++ b/library/std/src/sys/net/connection/socket/unix.rs @@ -72,47 +72,45 @@ impl Socket { } pub fn new_raw(fam: c_int, ty: c_int) -> io::Result { - unsafe { - cfg_select! { - any( - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "illumos", - target_os = "hurd", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_os = "cygwin", - target_os = "nto", - target_os = "solaris", - ) => { - // On platforms that support it we pass the SOCK_CLOEXEC - // flag to atomically create the socket and set it as - // CLOEXEC. On Linux this was added in 2.6.27. - let fd = cvt(libc::socket(fam, ty | libc::SOCK_CLOEXEC, 0))?; - let socket = Socket(FileDesc::from_raw_fd(fd)); + cfg_select! { + any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "illumos", + target_os = "hurd", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", + target_os = "cygwin", + target_os = "nto", + target_os = "solaris", + ) => { + // On platforms that support it we pass the SOCK_CLOEXEC + // flag to atomically create the socket and set it as + // CLOEXEC. On Linux this was added in 2.6.27. + let fd = cvt(unsafe { libc::socket(fam, ty | libc::SOCK_CLOEXEC, 0) })?; + let socket = Socket(unsafe { FileDesc::from_raw_fd(fd) }); - // DragonFlyBSD, FreeBSD and NetBSD use `SO_NOSIGPIPE` as a `setsockopt` - // flag to disable `SIGPIPE` emission on socket. - #[cfg(any(target_os = "freebsd", target_os = "netbsd", target_os = "dragonfly"))] - setsockopt(&socket, libc::SOL_SOCKET, libc::SO_NOSIGPIPE, 1)?; + // DragonFlyBSD, FreeBSD and NetBSD use `SO_NOSIGPIPE` as a `setsockopt` + // flag to disable `SIGPIPE` emission on socket. + #[cfg(any(target_os = "freebsd", target_os = "netbsd", target_os = "dragonfly"))] + unsafe { setsockopt(&socket, libc::SOL_SOCKET, libc::SO_NOSIGPIPE, 1)? }; - Ok(socket) - } - _ => { - let fd = cvt(libc::socket(fam, ty, 0))?; - let fd = FileDesc::from_raw_fd(fd); - fd.set_cloexec()?; - let socket = Socket(fd); + Ok(socket) + } + _ => { + let fd = cvt(unsafe { libc::socket(fam, ty, 0) })?; + let fd = unsafe { FileDesc::from_raw_fd(fd) }; + fd.set_cloexec()?; + let socket = Socket(fd); - // macOS and iOS use `SO_NOSIGPIPE` as a `setsockopt` - // flag to disable `SIGPIPE` emission on socket. - #[cfg(target_vendor = "apple")] - setsockopt(&socket, libc::SOL_SOCKET, libc::SO_NOSIGPIPE, 1)?; + // macOS and iOS use `SO_NOSIGPIPE` as a `setsockopt` + // flag to disable `SIGPIPE` emission on socket. + #[cfg(target_vendor = "apple")] + unsafe { setsockopt(&socket, libc::SOL_SOCKET, libc::SO_NOSIGPIPE, 1)? }; - Ok(socket) - } + Ok(socket) } } } @@ -413,11 +411,11 @@ impl Socket { } None => libc::timeval { tv_sec: 0, tv_usec: 0 }, }; - setsockopt(self, libc::SOL_SOCKET, kind, timeout) + unsafe { setsockopt(self, libc::SOL_SOCKET, kind, timeout) } } pub fn timeout(&self, kind: libc::c_int) -> io::Result> { - let raw: libc::timeval = getsockopt(self, libc::SOL_SOCKET, kind)?; + let raw: libc::timeval = unsafe { getsockopt(self, libc::SOL_SOCKET, kind)? }; if raw.tv_sec == 0 && raw.tv_usec == 0 { Ok(None) } else { @@ -444,7 +442,7 @@ impl Socket { l_linger: linger.unwrap_or_default().as_secs() as libc::c_int, }; - setsockopt(self, libc::SOL_SOCKET, SO_LINGER, linger) + unsafe { setsockopt(self, libc::SOL_SOCKET, SO_LINGER, linger) } } #[cfg(target_os = "cygwin")] @@ -454,32 +452,32 @@ impl Socket { l_linger: linger.unwrap_or_default().as_secs() as libc::c_ushort, }; - setsockopt(self, libc::SOL_SOCKET, SO_LINGER, linger) + unsafe { setsockopt(self, libc::SOL_SOCKET, SO_LINGER, linger) } } pub fn linger(&self) -> io::Result> { - let val: libc::linger = getsockopt(self, libc::SOL_SOCKET, SO_LINGER)?; + let val: libc::linger = unsafe { getsockopt(self, libc::SOL_SOCKET, SO_LINGER)? }; Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64))) } pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { - setsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY, nodelay as c_int) + unsafe { setsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY, nodelay as c_int) } } pub fn nodelay(&self) -> io::Result { - let raw: c_int = getsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY)?; + let raw: c_int = unsafe { getsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY)? }; Ok(raw != 0) } #[cfg(any(target_os = "android", target_os = "linux", target_os = "cygwin"))] pub fn set_quickack(&self, quickack: bool) -> io::Result<()> { - setsockopt(self, libc::IPPROTO_TCP, libc::TCP_QUICKACK, quickack as c_int) + unsafe { setsockopt(self, libc::IPPROTO_TCP, libc::TCP_QUICKACK, quickack as c_int) } } #[cfg(any(target_os = "android", target_os = "linux", target_os = "cygwin"))] pub fn quickack(&self) -> io::Result { - let raw: c_int = getsockopt(self, libc::IPPROTO_TCP, libc::TCP_QUICKACK)?; + let raw: c_int = unsafe { getsockopt(self, libc::IPPROTO_TCP, libc::TCP_QUICKACK)? }; Ok(raw != 0) } @@ -487,12 +485,12 @@ impl Socket { #[cfg(target_os = "linux")] pub fn set_deferaccept(&self, accept: Duration) -> io::Result<()> { let val = cmp::min(accept.as_secs(), c_int::MAX as u64) as c_int; - setsockopt(self, libc::IPPROTO_TCP, libc::TCP_DEFER_ACCEPT, val) + unsafe { setsockopt(self, libc::IPPROTO_TCP, libc::TCP_DEFER_ACCEPT, val) } } #[cfg(target_os = "linux")] pub fn deferaccept(&self) -> io::Result { - let raw: c_int = getsockopt(self, libc::IPPROTO_TCP, libc::TCP_DEFER_ACCEPT)?; + let raw: c_int = unsafe { getsockopt(self, libc::IPPROTO_TCP, libc::TCP_DEFER_ACCEPT)? }; Ok(Duration::from_secs(raw as _)) } @@ -506,21 +504,23 @@ impl Socket { } let mut arg: libc::accept_filter_arg = unsafe { mem::zeroed() }; arg.af_name = buf; - setsockopt(self, libc::SOL_SOCKET, libc::SO_ACCEPTFILTER, &mut arg) + unsafe { setsockopt(self, libc::SOL_SOCKET, libc::SO_ACCEPTFILTER, &mut arg) } } else { - setsockopt( - self, - libc::SOL_SOCKET, - libc::SO_ACCEPTFILTER, - core::ptr::null_mut() as *mut c_void, - ) + unsafe { + setsockopt( + self, + libc::SOL_SOCKET, + libc::SO_ACCEPTFILTER, + core::ptr::null_mut() as *mut c_void, + ) + } } } #[cfg(any(target_os = "freebsd", target_os = "netbsd"))] pub fn acceptfilter(&self) -> io::Result<&CStr> { let arg: libc::accept_filter_arg = - getsockopt(self, libc::SOL_SOCKET, libc::SO_ACCEPTFILTER)?; + unsafe { getsockopt(self, libc::SOL_SOCKET, libc::SO_ACCEPTFILTER)? }; let s: &[u8] = unsafe { core::slice::from_raw_parts(arg.af_name.as_ptr() as *const u8, 16) }; let name = CStr::from_bytes_with_nul(s).unwrap(); @@ -531,53 +531,57 @@ impl Socket { pub fn set_exclbind(&self, excl: bool) -> io::Result<()> { // not yet on libc crate const SO_EXCLBIND: i32 = 0x1015; - setsockopt(self, libc::SOL_SOCKET, SO_EXCLBIND, excl) + unsafe { setsockopt(self, libc::SOL_SOCKET, SO_EXCLBIND, excl) } } #[cfg(any(target_os = "solaris", target_os = "illumos"))] pub fn exclbind(&self) -> io::Result { // not yet on libc crate const SO_EXCLBIND: i32 = 0x1015; - let raw: c_int = getsockopt(self, libc::SOL_SOCKET, SO_EXCLBIND)?; + let raw: c_int = unsafe { getsockopt(self, libc::SOL_SOCKET, SO_EXCLBIND)? }; Ok(raw != 0) } #[cfg(any(target_os = "android", target_os = "linux", target_os = "cygwin"))] pub fn set_passcred(&self, passcred: bool) -> io::Result<()> { - setsockopt(self, libc::SOL_SOCKET, libc::SO_PASSCRED, passcred as libc::c_int) + unsafe { setsockopt(self, libc::SOL_SOCKET, libc::SO_PASSCRED, passcred as libc::c_int) } } #[cfg(any(target_os = "android", target_os = "linux", target_os = "cygwin"))] pub fn passcred(&self) -> io::Result { - let passcred: libc::c_int = getsockopt(self, libc::SOL_SOCKET, libc::SO_PASSCRED)?; + let passcred: libc::c_int = + unsafe { getsockopt(self, libc::SOL_SOCKET, libc::SO_PASSCRED)? }; Ok(passcred != 0) } #[cfg(target_os = "netbsd")] pub fn set_local_creds(&self, local_creds: bool) -> io::Result<()> { - setsockopt(self, 0 as libc::c_int, libc::LOCAL_CREDS, local_creds as libc::c_int) + unsafe { setsockopt(self, 0 as libc::c_int, libc::LOCAL_CREDS, local_creds as libc::c_int) } } #[cfg(target_os = "netbsd")] pub fn local_creds(&self) -> io::Result { - let local_creds: libc::c_int = getsockopt(self, 0 as libc::c_int, libc::LOCAL_CREDS)?; + let local_creds: libc::c_int = + unsafe { getsockopt(self, 0 as libc::c_int, libc::LOCAL_CREDS)? }; Ok(local_creds != 0) } #[cfg(target_os = "freebsd")] pub fn set_local_creds_persistent(&self, local_creds_persistent: bool) -> io::Result<()> { - setsockopt( - self, - libc::AF_LOCAL, - libc::LOCAL_CREDS_PERSISTENT, - local_creds_persistent as libc::c_int, - ) + unsafe { + setsockopt( + self, + libc::AF_LOCAL, + libc::LOCAL_CREDS_PERSISTENT, + local_creds_persistent as libc::c_int, + ) + } } #[cfg(target_os = "freebsd")] pub fn local_creds_persistent(&self) -> io::Result { let local_creds_persistent: libc::c_int = - getsockopt(self, libc::AF_LOCAL, libc::LOCAL_CREDS_PERSISTENT)?; + unsafe { getsockopt(self, libc::AF_LOCAL, libc::LOCAL_CREDS_PERSISTENT)? }; Ok(local_creds_persistent != 0) } @@ -590,7 +594,7 @@ impl Socket { #[cfg(target_os = "vita")] pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { let option = nonblocking as libc::c_int; - setsockopt(self, libc::SOL_SOCKET, libc::SO_NONBLOCK, option) + unsafe { setsockopt(self, libc::SOL_SOCKET, libc::SO_NONBLOCK, option) } } #[cfg(any(target_os = "solaris", target_os = "illumos"))] @@ -608,11 +612,11 @@ impl Socket { let option = libc::SO_USER_COOKIE; #[cfg(target_os = "openbsd")] let option = libc::SO_RTABLE; - setsockopt(self, libc::SOL_SOCKET, option, mark as libc::c_int) + unsafe { setsockopt(self, libc::SOL_SOCKET, option, mark as libc::c_int) } } pub fn take_error(&self) -> io::Result> { - let raw: c_int = getsockopt(self, libc::SOL_SOCKET, libc::SO_ERROR)?; + let raw: c_int = unsafe { getsockopt(self, libc::SOL_SOCKET, libc::SO_ERROR)? }; if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } } diff --git a/library/std/src/sys/net/connection/socket/wasip2.rs b/library/std/src/sys/net/connection/socket/wasip2.rs index c77c50fece1a..fbd89f3c8b05 100644 --- a/library/std/src/sys/net/connection/socket/wasip2.rs +++ b/library/std/src/sys/net/connection/socket/wasip2.rs @@ -270,11 +270,11 @@ impl Socket { } None => netc::timeval { tv_sec: 0, tv_usec: 0 }, }; - setsockopt(self, netc::SOL_SOCKET, kind, timeout) + unsafe { setsockopt(self, netc::SOL_SOCKET, kind, timeout) } } pub fn timeout(&self, kind: c_int) -> io::Result> { - let raw: netc::timeval = getsockopt(self, netc::SOL_SOCKET, kind)?; + let raw: netc::timeval = unsafe { getsockopt(self, netc::SOL_SOCKET, kind)? }; if raw.tv_sec == 0 && raw.tv_usec == 0 { Ok(None) } else { @@ -303,11 +303,11 @@ impl Socket { } pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { - setsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY, nodelay as c_int) + unsafe { setsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY, nodelay as c_int) } } pub fn nodelay(&self) -> io::Result { - let raw: c_int = getsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY)?; + let raw: c_int = unsafe { getsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY)? }; Ok(raw != 0) } @@ -317,7 +317,7 @@ impl Socket { } pub fn take_error(&self) -> io::Result> { - let raw: c_int = getsockopt(self, netc::SOL_SOCKET, netc::SO_ERROR)?; + let raw: c_int = unsafe { getsockopt(self, netc::SOL_SOCKET, netc::SO_ERROR)? }; if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } } diff --git a/library/std/src/sys/net/connection/socket/windows.rs b/library/std/src/sys/net/connection/socket/windows.rs index 2221023a2125..2fdebe6c90d8 100644 --- a/library/std/src/sys/net/connection/socket/windows.rs +++ b/library/std/src/sys/net/connection/socket/windows.rs @@ -384,11 +384,11 @@ impl Socket { } None => 0, }; - setsockopt(self, c::SOL_SOCKET, kind, timeout) + unsafe { setsockopt(self, c::SOL_SOCKET, kind, timeout) } } pub fn timeout(&self, kind: c_int) -> io::Result> { - let raw: u32 = getsockopt(self, c::SOL_SOCKET, kind)?; + let raw: u32 = unsafe { getsockopt(self, c::SOL_SOCKET, kind)? }; if raw == 0 { Ok(None) } else { @@ -421,26 +421,26 @@ impl Socket { l_linger: linger.unwrap_or_default().as_secs() as c_ushort, }; - setsockopt(self, c::SOL_SOCKET, c::SO_LINGER, linger) + unsafe { setsockopt(self, c::SOL_SOCKET, c::SO_LINGER, linger) } } pub fn linger(&self) -> io::Result> { - let val: c::LINGER = getsockopt(self, c::SOL_SOCKET, c::SO_LINGER)?; + let val: c::LINGER = unsafe { getsockopt(self, c::SOL_SOCKET, c::SO_LINGER)? }; Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64))) } pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { - setsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY, nodelay as c::BOOL) + unsafe { setsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY, nodelay as c::BOOL) } } pub fn nodelay(&self) -> io::Result { - let raw: c::BOOL = getsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY)?; + let raw: c::BOOL = unsafe { getsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY)? }; Ok(raw != 0) } pub fn take_error(&self) -> io::Result> { - let raw: c_int = getsockopt(self, c::SOL_SOCKET, c::SO_ERROR)?; + let raw: c_int = unsafe { getsockopt(self, c::SOL_SOCKET, c::SO_ERROR)? }; if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } } From 1da0092218e4b7bbc25034c3e5b98cb85a01a619 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 30 Sep 2025 13:05:45 +0200 Subject: [PATCH 087/226] `redundant_pattern_match`: clean-up - cast `&[Arm]` to `&[Arm; 2]` early on to hopefully avoid bounds checks - use `if let [pattern] = X { .. }` instead of `if let patterns = X { let pattern = patterns[0]; .. }` - use `Symbol`s instead of `&str`s - reduce indentation --- .../src/matches/redundant_pattern_match.rs | 145 ++++++++---------- 1 file changed, 66 insertions(+), 79 deletions(-) diff --git a/clippy_lints/src/matches/redundant_pattern_match.rs b/clippy_lints/src/matches/redundant_pattern_match.rs index c936c96f9719..9d0115791838 100644 --- a/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/clippy_lints/src/matches/redundant_pattern_match.rs @@ -269,66 +269,61 @@ fn find_method_sugg_for_if_let<'tcx>( } pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op: &Expr<'_>, arms: &[Arm<'_>]) { - if arms.len() == 2 { - let node_pair = (&arms[0].pat.kind, &arms[1].pat.kind); + if let Ok(arms) = arms.try_into() // TODO: use `slice::as_array` once stabilized + && let Some((good_method, maybe_guard)) = found_good_method(cx, arms) + { + let span = is_expn_of(expr.span, sym::matches).unwrap_or(expr.span.to(op.span)); + let result_expr = match &op.kind { + ExprKind::AddrOf(_, _, borrowed) => borrowed, + _ => op, + }; + let mut app = Applicability::MachineApplicable; + let receiver_sugg = Sugg::hir_with_applicability(cx, result_expr, "_", &mut app).maybe_paren(); + let mut sugg = format!("{receiver_sugg}.{good_method}"); - if let Some((good_method, maybe_guard)) = found_good_method(cx, arms, node_pair) { - let span = is_expn_of(expr.span, sym::matches).unwrap_or(expr.span.to(op.span)); - let result_expr = match &op.kind { - ExprKind::AddrOf(_, _, borrowed) => borrowed, - _ => op, - }; - let mut app = Applicability::MachineApplicable; - let receiver_sugg = Sugg::hir_with_applicability(cx, result_expr, "_", &mut app).maybe_paren(); - 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! - // `guard` here is `Guard::If` with the let expression somewhere deep in the tree of exprs, - // counter to the intuition that it should be `Guard::IfLet`, so we need another check - // to see that there aren't any let chains anywhere in the guard, as that would break - // if we suggest `t.is_none() && (let X = y && z)` for: - // `match t { None if let X = y && z => true, _ => false }` - let has_nested_let_chain = for_each_expr_without_closures(guard, |expr| { - if matches!(expr.kind, ExprKind::Let(..)) { - ControlFlow::Break(()) - } else { - ControlFlow::Continue(()) - } - }) - .is_some(); - - if has_nested_let_chain { - return; + if let Some(guard) = maybe_guard { + // wow, the HIR for match guards in `PAT if let PAT = expr && expr => ...` is annoying! + // `guard` here is `Guard::If` with the let expression somewhere deep in the tree of exprs, + // counter to the intuition that it should be `Guard::IfLet`, so we need another check + // to see that there aren't any let chains anywhere in the guard, as that would break + // if we suggest `t.is_none() && (let X = y && z)` for: + // `match t { None if let X = y && z => true, _ => false }` + let has_nested_let_chain = for_each_expr_without_closures(guard, |expr| { + if matches!(expr.kind, ExprKind::Let(..)) { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) } + }) + .is_some(); - let guard = Sugg::hir(cx, guard, ".."); - let _ = write!(sugg, " && {}", guard.maybe_paren()); + if has_nested_let_chain { + return; } - span_lint_and_sugg( - cx, - REDUNDANT_PATTERN_MATCHING, - span, - format!("redundant pattern matching, consider using `{good_method}`"), - "try", - sugg, - app, - ); + let guard = Sugg::hir(cx, guard, ".."); + let _ = write!(sugg, " && {}", guard.maybe_paren()); } + + span_lint_and_sugg( + cx, + REDUNDANT_PATTERN_MATCHING, + span, + format!("redundant pattern matching, consider using `{good_method}`"), + "try", + sugg, + app, + ); } } fn found_good_method<'tcx>( cx: &LateContext<'_>, - arms: &'tcx [Arm<'tcx>], - node: (&PatKind<'_>, &PatKind<'_>), + arms: &'tcx [Arm<'tcx>; 2], ) -> Option<(&'static str, Option<&'tcx Expr<'tcx>>)> { - match node { - (PatKind::TupleStruct(path_left, patterns_left, _), PatKind::TupleStruct(path_right, patterns_right, _)) - if patterns_left.len() == 1 && patterns_right.len() == 1 => - { - if let (PatKind::Wild, PatKind::Wild) = (&patterns_left[0].kind, &patterns_right[0].kind) { + match (&arms[0].pat.kind, &arms[1].pat.kind) { + (PatKind::TupleStruct(path_left, [pattern_left], _), PatKind::TupleStruct(path_right, [pattern_right], _)) => { + if let (PatKind::Wild, PatKind::Wild) = (&pattern_left.kind, &pattern_right.kind) { find_good_method_for_match( cx, arms, @@ -356,7 +351,7 @@ fn found_good_method<'tcx>( } }, ( - PatKind::TupleStruct(path_left, patterns, _), + PatKind::TupleStruct(path_left, [pattern], _), PatKind::Expr(PatExpr { kind: PatExprKind::Path(path_right), .. @@ -367,9 +362,9 @@ fn found_good_method<'tcx>( kind: PatExprKind::Path(path_left), .. }), - PatKind::TupleStruct(path_right, patterns, _), - ) if patterns.len() == 1 => { - if let PatKind::Wild = patterns[0].kind { + PatKind::TupleStruct(path_right, [pattern], _), + ) => { + if let PatKind::Wild = pattern.kind { find_good_method_for_match( cx, arms, @@ -396,8 +391,8 @@ fn found_good_method<'tcx>( None } }, - (PatKind::TupleStruct(path_left, patterns, _), PatKind::Wild) if patterns.len() == 1 => { - if let PatKind::Wild = patterns[0].kind { + (PatKind::TupleStruct(path_left, [pattern], _), PatKind::Wild) => { + if let PatKind::Wild = pattern.kind { get_good_method(cx, arms, path_left) } else { None @@ -426,31 +421,23 @@ fn get_ident(path: &QPath<'_>) -> Option { fn get_good_method<'tcx>( cx: &LateContext<'_>, - arms: &'tcx [Arm<'tcx>], + arms: &'tcx [Arm<'tcx>; 2], path_left: &QPath<'_>, ) -> Option<(&'static str, Option<&'tcx Expr<'tcx>>)> { - if let Some(name) = get_ident(path_left) { - let (expected_item_left, should_be_left, should_be_right) = match name.as_str() { - "Ok" => (Item::Lang(ResultOk), "is_ok()", "is_err()"), - "Err" => (Item::Lang(ResultErr), "is_err()", "is_ok()"), - "Some" => (Item::Lang(OptionSome), "is_some()", "is_none()"), - "None" => (Item::Lang(OptionNone), "is_none()", "is_some()"), - "Ready" => (Item::Lang(PollReady), "is_ready()", "is_pending()"), - "Pending" => (Item::Lang(PollPending), "is_pending()", "is_ready()"), - "V4" => (Item::Diag(sym::IpAddr, sym::V4), "is_ipv4()", "is_ipv6()"), - "V6" => (Item::Diag(sym::IpAddr, sym::V6), "is_ipv6()", "is_ipv4()"), - _ => return None, - }; - return find_good_method_for_matches_macro( - cx, - arms, - path_left, - expected_item_left, - should_be_left, - should_be_right, - ); - } - None + let ident = get_ident(path_left)?; + + let (expected_item_left, should_be_left, should_be_right) = match ident.name { + sym::Ok => (Item::Lang(ResultOk), "is_ok()", "is_err()"), + sym::Err => (Item::Lang(ResultErr), "is_err()", "is_ok()"), + sym::Some => (Item::Lang(OptionSome), "is_some()", "is_none()"), + sym::None => (Item::Lang(OptionNone), "is_none()", "is_some()"), + sym::Ready => (Item::Lang(PollReady), "is_ready()", "is_pending()"), + sym::Pending => (Item::Lang(PollPending), "is_pending()", "is_ready()"), + sym::V4 => (Item::Diag(sym::IpAddr, sym::V4), "is_ipv4()", "is_ipv6()"), + sym::V6 => (Item::Diag(sym::IpAddr, sym::V6), "is_ipv6()", "is_ipv4()"), + _ => return None, + }; + find_good_method_for_matches_macro(cx, arms, path_left, expected_item_left, should_be_left, should_be_right) } #[derive(Clone, Copy)] @@ -490,7 +477,7 @@ fn is_pat_variant(cx: &LateContext<'_>, pat: &Pat<'_>, path: &QPath<'_>, expecte #[expect(clippy::too_many_arguments)] fn find_good_method_for_match<'a, 'tcx>( cx: &LateContext<'_>, - arms: &'tcx [Arm<'tcx>], + arms: &'tcx [Arm<'tcx>; 2], path_left: &QPath<'_>, path_right: &QPath<'_>, expected_item_left: Item, @@ -525,7 +512,7 @@ fn find_good_method_for_match<'a, 'tcx>( fn find_good_method_for_matches_macro<'a, 'tcx>( cx: &LateContext<'_>, - arms: &'tcx [Arm<'tcx>], + arms: &'tcx [Arm<'tcx>; 2], path_left: &QPath<'_>, expected_item_left: Item, should_be_left: &'a str, From 3534594029ed1495290e013647a1f53da561f7f1 Mon Sep 17 00:00:00 2001 From: joboet Date: Tue, 30 Sep 2025 17:10:22 +0200 Subject: [PATCH 088/226] std: merge address family computation --- library/std/src/os/unix/net/datagram.rs | 2 +- library/std/src/os/unix/net/listener.rs | 4 ++-- library/std/src/os/unix/net/stream.rs | 4 ++-- .../std/src/sys/net/connection/socket/hermit.rs | 10 +--------- library/std/src/sys/net/connection/socket/mod.rs | 15 +++++++++++---- .../std/src/sys/net/connection/socket/solid.rs | 16 +++------------- .../std/src/sys/net/connection/socket/unix.rs | 14 +++----------- .../std/src/sys/net/connection/socket/wasip2.rs | 12 ++---------- .../std/src/sys/net/connection/socket/windows.rs | 6 +----- 9 files changed, 26 insertions(+), 57 deletions(-) diff --git a/library/std/src/os/unix/net/datagram.rs b/library/std/src/os/unix/net/datagram.rs index 469bfbb0d837..163267be1e5c 100644 --- a/library/std/src/os/unix/net/datagram.rs +++ b/library/std/src/os/unix/net/datagram.rs @@ -159,7 +159,7 @@ impl UnixDatagram { /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn unbound() -> io::Result { - let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_DGRAM)?; + let inner = Socket::new(libc::AF_UNIX, libc::SOCK_DGRAM)?; Ok(UnixDatagram(inner)) } diff --git a/library/std/src/os/unix/net/listener.rs b/library/std/src/os/unix/net/listener.rs index 27428c9eb285..5b4659e26188 100644 --- a/library/std/src/os/unix/net/listener.rs +++ b/library/std/src/os/unix/net/listener.rs @@ -71,7 +71,7 @@ impl UnixListener { #[stable(feature = "unix_socket", since = "1.10.0")] pub fn bind>(path: P) -> io::Result { unsafe { - let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_STREAM)?; + let inner = Socket::new(libc::AF_UNIX, libc::SOCK_STREAM)?; let (addr, len) = sockaddr_un(path.as_ref())?; #[cfg(any( target_os = "windows", @@ -136,7 +136,7 @@ impl UnixListener { #[stable(feature = "unix_socket_abstract", since = "1.70.0")] pub fn bind_addr(socket_addr: &SocketAddr) -> io::Result { unsafe { - let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_STREAM)?; + let inner = Socket::new(libc::AF_UNIX, libc::SOCK_STREAM)?; #[cfg(target_os = "linux")] const backlog: core::ffi::c_int = -1; #[cfg(not(target_os = "linux"))] diff --git a/library/std/src/os/unix/net/stream.rs b/library/std/src/os/unix/net/stream.rs index ea4171a7d287..851ff7f08795 100644 --- a/library/std/src/os/unix/net/stream.rs +++ b/library/std/src/os/unix/net/stream.rs @@ -105,7 +105,7 @@ impl UnixStream { #[stable(feature = "unix_socket", since = "1.10.0")] pub fn connect>(path: P) -> io::Result { unsafe { - let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_STREAM)?; + let inner = Socket::new(libc::AF_UNIX, libc::SOCK_STREAM)?; let (addr, len) = sockaddr_un(path.as_ref())?; cvt(libc::connect(inner.as_raw_fd(), (&raw const addr) as *const _, len))?; @@ -139,7 +139,7 @@ impl UnixStream { #[stable(feature = "unix_socket_abstract", since = "1.70.0")] pub fn connect_addr(socket_addr: &SocketAddr) -> io::Result { unsafe { - let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_STREAM)?; + let inner = Socket::new(libc::AF_UNIX, libc::SOCK_STREAM)?; cvt(libc::connect( inner.as_raw_fd(), (&raw const socket_addr.addr) as *const _, diff --git a/library/std/src/sys/net/connection/socket/hermit.rs b/library/std/src/sys/net/connection/socket/hermit.rs index 0c105ed20fa7..2f5c6fa31d40 100644 --- a/library/std/src/sys/net/connection/socket/hermit.rs +++ b/library/std/src/sys/net/connection/socket/hermit.rs @@ -37,15 +37,7 @@ pub fn init() {} pub struct Socket(FileDesc); impl Socket { - pub fn new(addr: &SocketAddr, ty: i32) -> io::Result { - let fam = match *addr { - SocketAddr::V4(..) => netc::AF_INET, - SocketAddr::V6(..) => netc::AF_INET6, - }; - Socket::new_raw(fam, ty) - } - - pub fn new_raw(fam: i32, ty: i32) -> io::Result { + pub fn new(fam: i32, ty: i32) -> io::Result { let fd = cvt(unsafe { netc::socket(fam, ty, 0) })?; Ok(Socket(unsafe { FileDesc::from_raw_fd(fd) })) } diff --git a/library/std/src/sys/net/connection/socket/mod.rs b/library/std/src/sys/net/connection/socket/mod.rs index d01a52b86109..d0a4a2fab497 100644 --- a/library/std/src/sys/net/connection/socket/mod.rs +++ b/library/std/src/sys/net/connection/socket/mod.rs @@ -178,6 +178,13 @@ fn socket_addr_to_c(addr: &SocketAddr) -> (SocketAddrCRepr, c::socklen_t) { } } +fn addr_family(addr: &SocketAddr) -> c_int { + match addr { + SocketAddr::V4(..) => c::AF_INET, + SocketAddr::V6(..) => c::AF_INET6, + } +} + /// Converts the C socket address stored in `storage` to a Rust `SocketAddr`. /// /// # Safety @@ -364,7 +371,7 @@ impl TcpStream { return each_addr(addr, inner); fn inner(addr: &SocketAddr) -> io::Result { - let sock = Socket::new(addr, c::SOCK_STREAM)?; + let sock = Socket::new(addr_family(addr), c::SOCK_STREAM)?; sock.connect(addr)?; Ok(TcpStream { inner: sock }) } @@ -373,7 +380,7 @@ impl TcpStream { pub fn connect_timeout(addr: &SocketAddr, timeout: Duration) -> io::Result { init(); - let sock = Socket::new(addr, c::SOCK_STREAM)?; + let sock = Socket::new(addr_family(addr), c::SOCK_STREAM)?; sock.connect_timeout(addr, timeout)?; Ok(TcpStream { inner: sock }) } @@ -535,7 +542,7 @@ impl TcpListener { return each_addr(addr, inner); fn inner(addr: &SocketAddr) -> io::Result { - let sock = Socket::new(addr, c::SOCK_STREAM)?; + let sock = Socket::new(addr_family(addr), c::SOCK_STREAM)?; // On platforms with Berkeley-derived sockets, this allows to quickly // rebind a socket, without needing to wait for the OS to clean up the @@ -661,7 +668,7 @@ impl UdpSocket { return each_addr(addr, inner); fn inner(addr: &SocketAddr) -> io::Result { - let sock = Socket::new(addr, c::SOCK_DGRAM)?; + let sock = Socket::new(addr_family(addr), c::SOCK_DGRAM)?; let (addr, len) = socket_addr_to_c(addr); cvt(unsafe { c::bind(sock.as_raw(), addr.as_ptr(), len as _) })?; Ok(UdpSocket { inner: sock }) diff --git a/library/std/src/sys/net/connection/socket/solid.rs b/library/std/src/sys/net/connection/socket/solid.rs index dfde5513f909..14cf75adcc06 100644 --- a/library/std/src/sys/net/connection/socket/solid.rs +++ b/library/std/src/sys/net/connection/socket/solid.rs @@ -115,19 +115,9 @@ pub fn init() {} pub struct Socket(OwnedFd); impl Socket { - pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result { - let fam = match *addr { - SocketAddr::V4(..) => netc::AF_INET, - SocketAddr::V6(..) => netc::AF_INET6, - }; - Socket::new_raw(fam, ty) - } - - pub fn new_raw(fam: c_int, ty: c_int) -> io::Result { - unsafe { - let fd = cvt(netc::socket(fam, ty, 0))?; - Ok(Self::from_raw_fd(fd)) - } + pub fn new(fam: c_int, ty: c_int) -> io::Result { + let fd = cvt(unsafe { netc::socket(fam, ty, 0) })?; + Ok(unsafe { Self::from_raw_fd(fd) }) } pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> { diff --git a/library/std/src/sys/net/connection/socket/unix.rs b/library/std/src/sys/net/connection/socket/unix.rs index 3f05449903fd..559e27604a9d 100644 --- a/library/std/src/sys/net/connection/socket/unix.rs +++ b/library/std/src/sys/net/connection/socket/unix.rs @@ -63,15 +63,7 @@ pub fn cvt_gai(err: c_int) -> io::Result<()> { } impl Socket { - pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result { - let fam = match *addr { - SocketAddr::V4(..) => libc::AF_INET, - SocketAddr::V6(..) => libc::AF_INET6, - }; - Socket::new_raw(fam, ty) - } - - pub fn new_raw(fam: c_int, ty: c_int) -> io::Result { + pub fn new(family: c_int, ty: c_int) -> io::Result { cfg_select! { any( target_os = "android", @@ -89,7 +81,7 @@ impl Socket { // On platforms that support it we pass the SOCK_CLOEXEC // flag to atomically create the socket and set it as // CLOEXEC. On Linux this was added in 2.6.27. - let fd = cvt(unsafe { libc::socket(fam, ty | libc::SOCK_CLOEXEC, 0) })?; + let fd = cvt(unsafe { libc::socket(family, ty | libc::SOCK_CLOEXEC, 0) })?; let socket = Socket(unsafe { FileDesc::from_raw_fd(fd) }); // DragonFlyBSD, FreeBSD and NetBSD use `SO_NOSIGPIPE` as a `setsockopt` @@ -100,7 +92,7 @@ impl Socket { Ok(socket) } _ => { - let fd = cvt(unsafe { libc::socket(fam, ty, 0) })?; + let fd = cvt(unsafe { libc::socket(family, ty, 0) })?; let fd = unsafe { FileDesc::from_raw_fd(fd) }; fd.set_cloexec()?; let socket = Socket(fd); diff --git a/library/std/src/sys/net/connection/socket/wasip2.rs b/library/std/src/sys/net/connection/socket/wasip2.rs index fbd89f3c8b05..a1b08609eb02 100644 --- a/library/std/src/sys/net/connection/socket/wasip2.rs +++ b/library/std/src/sys/net/connection/socket/wasip2.rs @@ -74,16 +74,8 @@ pub struct WasiSocket(OwnedFd); pub struct Socket(WasiSocket); impl Socket { - pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result { - let fam = match *addr { - SocketAddr::V4(..) => netc::AF_INET, - SocketAddr::V6(..) => netc::AF_INET6, - }; - Socket::new_raw(fam, ty) - } - - pub fn new_raw(fam: c_int, ty: c_int) -> io::Result { - let fd = cvt(unsafe { netc::socket(fam, ty, 0) })?; + pub fn new(family: c_int, ty: c_int) -> io::Result { + let fd = cvt(unsafe { netc::socket(family, ty, 0) })?; Ok(unsafe { Self::from_raw_fd(fd) }) } diff --git a/library/std/src/sys/net/connection/socket/windows.rs b/library/std/src/sys/net/connection/socket/windows.rs index 2fdebe6c90d8..6dbebc5e276e 100644 --- a/library/std/src/sys/net/connection/socket/windows.rs +++ b/library/std/src/sys/net/connection/socket/windows.rs @@ -117,11 +117,7 @@ pub use crate::sys::pal::winsock::{cvt, cvt_gai, cvt_r, startup as init}; pub struct Socket(OwnedSocket); impl Socket { - pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result { - let family = match *addr { - SocketAddr::V4(..) => netc::AF_INET, - SocketAddr::V6(..) => netc::AF_INET6, - }; + pub fn new(family: c_int, ty: c_int) -> io::Result { let socket = unsafe { c::WSASocketW( family, From 089fbd35aa7c606bc61c4fef6bf3da747d507b75 Mon Sep 17 00:00:00 2001 From: yanglsh Date: Tue, 30 Sep 2025 10:53:35 +0800 Subject: [PATCH 089/226] fix: `let_unit_value` suggests wrongly for field init shorthand --- clippy_lints/src/unit_types/let_unit_value.rs | 48 +++++++++++++------ tests/ui/let_unit.fixed | 10 ++++ tests/ui/let_unit.rs | 10 ++++ tests/ui/let_unit.stderr | 16 ++++++- 4 files changed, 69 insertions(+), 15 deletions(-) diff --git a/clippy_lints/src/unit_types/let_unit_value.rs b/clippy_lints/src/unit_types/let_unit_value.rs index 424aa14cd686..2645e94358e1 100644 --- a/clippy_lints/src/unit_types/let_unit_value.rs +++ b/clippy_lints/src/unit_types/let_unit_value.rs @@ -90,11 +90,12 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, format_args: &FormatArgsStorag let mut has_in_format_capture = false; suggestions.extend(visitor.spans.into_iter().filter_map(|span| match span { - MaybeInFormatCapture::Yes => { + VariableUsage::FormatCapture => { has_in_format_capture = true; None }, - MaybeInFormatCapture::No(span) => Some((span, "()".to_string())), + VariableUsage::Normal(span) => Some((span, "()".to_string())), + VariableUsage::FieldShorthand(span) => Some((span.shrink_to_hi(), ": ()".to_string())), })); if has_in_format_capture { @@ -141,19 +142,30 @@ struct UnitVariableCollector<'a, 'tcx> { cx: &'a LateContext<'tcx>, format_args: &'a FormatArgsStorage, id: HirId, - spans: Vec, + spans: Vec, macro_call: Option<&'a FormatArgs>, } -/// Whether the unit variable is captured in a `format!`: -/// -/// ```ignore -/// let unit = (); -/// eprintln!("{unit}"); -/// ``` -enum MaybeInFormatCapture { - Yes, - No(Span), +/// How the unit variable is used +enum VariableUsage { + Normal(Span), + /// Captured in a `format!`: + /// + /// ```ignore + /// let unit = (); + /// eprintln!("{unit}"); + /// ``` + FormatCapture, + /// In a field shorthand init: + /// + /// ```ignore + /// struct Foo { + /// unit: (), + /// } + /// let unit = (); + /// Foo { unit }; + /// ``` + FieldShorthand(Span), } impl<'a, 'tcx> UnitVariableCollector<'a, 'tcx> { @@ -193,9 +205,17 @@ impl<'tcx> Visitor<'tcx> for UnitVariableCollector<'_, 'tcx> { matches!(arg.kind, FormatArgumentKind::Captured(_)) && find_format_arg_expr(ex, arg).is_some() }) { - self.spans.push(MaybeInFormatCapture::Yes); + self.spans.push(VariableUsage::FormatCapture); } else { - self.spans.push(MaybeInFormatCapture::No(path.span)); + let parent = self.cx.tcx.parent_hir_node(ex.hir_id); + match parent { + Node::ExprField(expr_field) if expr_field.is_shorthand => { + self.spans.push(VariableUsage::FieldShorthand(ex.span)); + }, + _ => { + self.spans.push(VariableUsage::Normal(path.span)); + }, + } } } diff --git a/tests/ui/let_unit.fixed b/tests/ui/let_unit.fixed index e8517f18e788..6d984a495d2b 100644 --- a/tests/ui/let_unit.fixed +++ b/tests/ui/let_unit.fixed @@ -229,3 +229,13 @@ fn issue_15784() { takes_unit(()); println!("{res:?}"); } + +fn issue15789() { + struct Foo { + value: (), + } + println!(); + //~^ let_unit_value + + Foo { value: () }; +} diff --git a/tests/ui/let_unit.rs b/tests/ui/let_unit.rs index 3f6f0139b2fe..a0e32f0b67a0 100644 --- a/tests/ui/let_unit.rs +++ b/tests/ui/let_unit.rs @@ -227,3 +227,13 @@ fn issue_15784() { takes_unit(res); println!("{res:?}"); } + +fn issue15789() { + struct Foo { + value: (), + } + let value = println!(); + //~^ let_unit_value + + Foo { value }; +} diff --git a/tests/ui/let_unit.stderr b/tests/ui/let_unit.stderr index 8ced32ab828f..6e7b958df4d9 100644 --- a/tests/ui/let_unit.stderr +++ b/tests/ui/let_unit.stderr @@ -114,5 +114,19 @@ LL | LL ~ takes_unit(()); | -error: aborting due to 8 previous errors +error: this let-binding has unit value + --> tests/ui/let_unit.rs:235:5 + | +LL | let value = println!(); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: omit the `let` binding and replace variable usages with `()` + | +LL ~ println!(); +LL | +LL | +LL ~ Foo { value: () }; + | + +error: aborting due to 9 previous errors From 195ff236c36df93fed8bee9106b3ecbfb4874df0 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Mon, 29 Sep 2025 01:03:16 +0200 Subject: [PATCH 090/226] `new_ret_no_self`: extract to a separate module --- clippy_lints/src/methods/mod.rs | 33 ++------------- clippy_lints/src/methods/new_ret_no_self.rs | 46 +++++++++++++++++++++ 2 files changed, 50 insertions(+), 29 deletions(-) create mode 100644 clippy_lints/src/methods/new_ret_no_self.rs diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 8c2948e4a74e..157fafa1c615 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -80,6 +80,7 @@ mod needless_character_iteration; mod needless_collect; mod needless_option_as_deref; mod needless_option_take; +mod new_ret_no_self; mod no_effect_replace; mod obfuscated_if_else; mod ok_expect; @@ -148,11 +149,9 @@ mod zst_offset; use clippy_config::Conf; use clippy_utils::consts::{ConstEvalCtxt, Constant}; -use clippy_utils::diagnostics::span_lint; use clippy_utils::macros::FormatArgsStorage; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::ty::contains_ty_adt_constructor_opaque; -use clippy_utils::{contains_return, is_trait_method, iter_input_pats, peel_blocks, return_ty, sym}; +use clippy_utils::{contains_return, is_trait_method, iter_input_pats, peel_blocks, sym}; pub use path_ends_with_ext::DEFAULT_ALLOWED_DOTFILES; use rustc_data_structures::fx::FxHashSet; use rustc_hir::{self as hir, Expr, ExprKind, Node, Stmt, StmtKind, TraitItem, TraitItemKind}; @@ -4918,20 +4917,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ); } - // if this impl block implements a trait, lint in trait definition instead - if !implements_trait - && impl_item.ident.name == sym::new - && let ret_ty = return_ty(cx, impl_item.owner_id) - && ret_ty != self_ty - && !contains_ty_adt_constructor_opaque(cx, ret_ty, self_ty) - { - span_lint( - cx, - NEW_RET_NO_SELF, - impl_item.span, - "methods called `new` usually return `Self`", - ); - } + new_ret_no_self::check_impl_item(cx, impl_item, self_ty, implements_trait); } } @@ -4963,18 +4949,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ); } - if item.ident.name == sym::new - && let ret_ty = return_ty(cx, item.owner_id) - && let self_ty = TraitRef::identity(cx.tcx, item.owner_id.to_def_id()).self_ty() - && !ret_ty.contains(self_ty) - { - span_lint( - cx, - NEW_RET_NO_SELF, - item.span, - "methods called `new` usually return `Self`", - ); - } + new_ret_no_self::check_trait_item(cx, item); } } } diff --git a/clippy_lints/src/methods/new_ret_no_self.rs b/clippy_lints/src/methods/new_ret_no_self.rs new file mode 100644 index 000000000000..2aa28fbb5f40 --- /dev/null +++ b/clippy_lints/src/methods/new_ret_no_self.rs @@ -0,0 +1,46 @@ +use clippy_utils::diagnostics::span_lint; +use clippy_utils::return_ty; +use clippy_utils::ty::contains_ty_adt_constructor_opaque; +use rustc_hir::{ImplItem, TraitItem}; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty}; +use rustc_span::sym; + +use super::NEW_RET_NO_SELF; + +pub(super) fn check_impl_item<'tcx>( + cx: &LateContext<'tcx>, + impl_item: &'tcx ImplItem<'_>, + self_ty: Ty<'tcx>, + implements_trait: bool, +) { + // if this impl block implements a trait, lint in trait definition instead + if !implements_trait + && impl_item.ident.name == sym::new + && let ret_ty = return_ty(cx, impl_item.owner_id) + && ret_ty != self_ty + && !contains_ty_adt_constructor_opaque(cx, ret_ty, self_ty) + { + span_lint( + cx, + NEW_RET_NO_SELF, + impl_item.span, + "methods called `new` usually return `Self`", + ); + } +} + +pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, trait_item: &'tcx TraitItem<'tcx>) { + if trait_item.ident.name == sym::new + && let ret_ty = return_ty(cx, trait_item.owner_id) + && let self_ty = ty::TraitRef::identity(cx.tcx, trait_item.owner_id.to_def_id()).self_ty() + && !ret_ty.contains(self_ty) + { + span_lint( + cx, + NEW_RET_NO_SELF, + trait_item.span, + "methods called `new` usually return `Self`", + ); + } +} From b3219502d71776bd2ab5be92734f1a05c2b01eb2 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 30 Sep 2025 17:43:02 +0200 Subject: [PATCH 091/226] `map_identity`: simplify lint emission Instead of calling `span_lint_and_*` in multiple places, call `span_lint_and_then` once and then use the `Diag` methods. --- clippy_lints/src/methods/map_identity.rs | 80 +++++++++++------------- 1 file changed, 35 insertions(+), 45 deletions(-) diff --git a/clippy_lints/src/methods/map_identity.rs b/clippy_lints/src/methods/map_identity.rs index a98cfff8bfbd..6190c43578e9 100644 --- a/clippy_lints/src/methods/map_identity.rs +++ b/clippy_lints/src/methods/map_identity.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{is_copy, is_type_diagnostic_item}; use clippy_utils::{is_expr_untyped_identity_function, is_mutable, is_trait_method, path_to_local_with_projections}; @@ -27,48 +27,46 @@ pub(super) fn check( && is_expr_untyped_identity_function(cx, map_arg) && let Some(call_span) = expr.span.trim_start(caller.span) { - let main_sugg = (call_span, String::new()); - let mut app = if is_copy(cx, caller_ty) { - // there is technically a behavioral change here for `Copy` iterators, where - // `iter.map(|x| x).next()` would mutate a temporary copy of the iterator and - // changing it to `iter.next()` mutates iter directly - Applicability::Unspecified - } else { - Applicability::MachineApplicable - }; + span_lint_and_then(cx, MAP_IDENTITY, call_span, MSG, |diag| { + let main_sugg = (call_span, String::new()); + let mut app = if is_copy(cx, caller_ty) { + // there is technically a behavioral change here for `Copy` iterators, where + // `iter.map(|x| x).next()` would mutate a temporary copy of the iterator and + // changing it to `iter.next()` mutates iter directly + Applicability::Unspecified + } else { + Applicability::MachineApplicable + }; - let needs_to_be_mutable = cx.typeck_results().expr_ty_adjusted(expr).is_mutable_ptr(); - if needs_to_be_mutable && !is_mutable(cx, caller) { - if let Some(hir_id) = path_to_local_with_projections(caller) - && let Node::Pat(pat) = cx.tcx.hir_node(hir_id) - && let PatKind::Binding(_, _, ident, _) = pat.kind - { - // We can reach the binding -- suggest making it mutable - let suggs = vec![main_sugg, (ident.span.shrink_to_lo(), String::from("mut "))]; + let needs_to_be_mutable = cx.typeck_results().expr_ty_adjusted(expr).is_mutable_ptr(); + if needs_to_be_mutable && !is_mutable(cx, caller) { + if let Some(hir_id) = path_to_local_with_projections(caller) + && let Node::Pat(pat) = cx.tcx.hir_node(hir_id) + && let PatKind::Binding(_, _, ident, _) = pat.kind + { + // We can reach the binding -- suggest making it mutable + let suggs = vec![main_sugg, (ident.span.shrink_to_lo(), String::from("mut "))]; - let ident = snippet_with_applicability(cx.sess(), ident.span, "_", &mut app); + let ident = snippet_with_applicability(cx.sess(), ident.span, "_", &mut app); - span_lint_and_then(cx, MAP_IDENTITY, call_span, MSG, |diag| { diag.multipart_suggestion( format!("remove the call to `{name}`, and make `{ident}` mutable"), suggs, app, ); - }); - } else { - // If we can't make the binding mutable, prevent the suggestion from being automatically applied, - // and add a complementary help message. - app = Applicability::Unspecified; - - let method_requiring_mut = if let Node::Expr(expr) = cx.tcx.parent_hir_node(expr.hir_id) - && let ExprKind::MethodCall(method, ..) = expr.kind - { - Some(method.ident) } else { - None - }; + // If we can't make the binding mutable, prevent the suggestion from being automatically applied, + // and add a complementary help message. + app = Applicability::Unspecified; + + let method_requiring_mut = if let Node::Expr(expr) = cx.tcx.parent_hir_node(expr.hir_id) + && let ExprKind::MethodCall(method, ..) = expr.kind + { + Some(method.ident) + } else { + None + }; - span_lint_and_then(cx, MAP_IDENTITY, call_span, MSG, |diag| { diag.span_suggestion(main_sugg.0, format!("remove the call to `{name}`"), main_sugg.1, app); let note = if let Some(method_requiring_mut) = method_requiring_mut { @@ -77,18 +75,10 @@ pub(super) fn check( "this must be made mutable".to_string() }; diag.span_note(caller.span, note); - }); + } + } else { + diag.span_suggestion(main_sugg.0, format!("remove the call to `{name}`"), main_sugg.1, app); } - } else { - span_lint_and_sugg( - cx, - MAP_IDENTITY, - main_sugg.0, - MSG, - format!("remove the call to `{name}`"), - main_sugg.1, - app, - ); - } + }); } } From e9ede27a5d880e5b8075ffb98dd381f4d5ce09ca Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 24 Aug 2025 09:31:06 +0200 Subject: [PATCH 092/226] test: remove extraneous `#[allow(clippy::uninlined_format_args)]` These were probably added automatically to avoid code churn. I'd like to get rid of them bit by bit, so here's the first bit --- .../conf_deprecated_key.rs | 4 +- .../conf_deprecated_key.stderr | 2 +- .../index_refutable_slice.fixed | 1 - .../index_refutable_slice.rs | 1 - .../index_refutable_slice.stderr | 4 +- tests/ui/bind_instead_of_map.fixed | 1 - tests/ui/bind_instead_of_map.rs | 1 - tests/ui/bind_instead_of_map.stderr | 6 +- .../shared_at_top_and_bottom.rs | 5 +- .../shared_at_top_and_bottom.stderr | 24 ++-- tests/ui/cast_abs_to_unsigned.fixed | 4 +- tests/ui/cast_abs_to_unsigned.rs | 4 +- tests/ui/crashes/ice-4775.rs | 2 +- tests/ui/default_trait_access.fixed | 2 +- tests/ui/default_trait_access.rs | 2 +- tests/ui/default_trait_access.stderr | 16 +-- tests/ui/explicit_counter_loop.rs | 24 ++-- tests/ui/explicit_write.fixed | 5 +- tests/ui/explicit_write.rs | 5 +- tests/ui/explicit_write.stderr | 50 +++---- tests/ui/fallible_impl_from.rs | 3 +- tests/ui/fallible_impl_from.stderr | 20 +-- .../if_let_slice_binding.fixed | 14 +- .../if_let_slice_binding.rs | 14 +- tests/ui/infinite_iter.rs | 4 +- tests/ui/infinite_iter.stderr | 4 +- tests/ui/issue_2356.fixed | 3 +- tests/ui/issue_2356.rs | 3 +- tests/ui/issue_2356.stderr | 2 +- tests/ui/issue_4266.rs | 3 +- tests/ui/issue_4266.stderr | 6 +- tests/ui/match_single_binding.fixed | 37 +++-- tests/ui/match_single_binding.rs | 37 +++-- tests/ui/match_single_binding.stderr | 130 +++++++++--------- tests/ui/match_single_binding2.fixed | 3 +- tests/ui/match_single_binding2.rs | 3 +- tests/ui/match_single_binding2.stderr | 12 +- 37 files changed, 222 insertions(+), 239 deletions(-) diff --git a/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs b/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs index ecb43dc34a8a..b28e46af0a46 100644 --- a/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs +++ b/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs @@ -1,5 +1,3 @@ -#![allow(clippy::uninlined_format_args)] - fn main() {} #[warn(clippy::cognitive_complexity)] @@ -8,7 +6,7 @@ fn cognitive_complexity() { let x = vec![1, 2, 3]; for i in x { if i == 1 { - println!("{}", i); + println!("{i}"); } } } diff --git a/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr b/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr index 627498dc175c..95b0508189e9 100644 --- a/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr +++ b/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr @@ -11,7 +11,7 @@ LL | blacklisted-names = [ "..", "wibble" ] | ^^^^^^^^^^^^^^^^^ error: the function has a cognitive complexity of (3/2) - --> tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs:6:4 + --> tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs:4:4 | LL | fn cognitive_complexity() { | ^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.fixed b/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.fixed index 2877871d0bf4..36540bf1dcf7 100644 --- a/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.fixed +++ b/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.fixed @@ -1,4 +1,3 @@ -#![allow(clippy::uninlined_format_args)] #![deny(clippy::index_refutable_slice)] fn below_limit() { diff --git a/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs b/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs index f958b92a102a..da76bb20fd96 100644 --- a/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs +++ b/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs @@ -1,4 +1,3 @@ -#![allow(clippy::uninlined_format_args)] #![deny(clippy::index_refutable_slice)] fn below_limit() { diff --git a/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.stderr b/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.stderr index e1a8941e102f..022deb330e6e 100644 --- a/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.stderr +++ b/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.stderr @@ -1,11 +1,11 @@ error: this binding can be a slice pattern to avoid indexing - --> tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs:6:17 + --> tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs:5:17 | LL | if let Some(slice) = slice { | ^^^^^ | note: the lint level is defined here - --> tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs:2:9 + --> tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs:1:9 | LL | #![deny(clippy::index_refutable_slice)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/bind_instead_of_map.fixed b/tests/ui/bind_instead_of_map.fixed index 80e010e2dfd7..fa35a01242d1 100644 --- a/tests/ui/bind_instead_of_map.fixed +++ b/tests/ui/bind_instead_of_map.fixed @@ -1,5 +1,4 @@ #![deny(clippy::bind_instead_of_map)] -#![allow(clippy::uninlined_format_args)] // need a main anyway, use it get rid of unused warnings too pub fn main() { diff --git a/tests/ui/bind_instead_of_map.rs b/tests/ui/bind_instead_of_map.rs index 09aa8480cbd9..403077e72ff9 100644 --- a/tests/ui/bind_instead_of_map.rs +++ b/tests/ui/bind_instead_of_map.rs @@ -1,5 +1,4 @@ #![deny(clippy::bind_instead_of_map)] -#![allow(clippy::uninlined_format_args)] // need a main anyway, use it get rid of unused warnings too pub fn main() { diff --git a/tests/ui/bind_instead_of_map.stderr b/tests/ui/bind_instead_of_map.stderr index 08f85fb58549..3f8d631591e9 100644 --- a/tests/ui/bind_instead_of_map.stderr +++ b/tests/ui/bind_instead_of_map.stderr @@ -1,5 +1,5 @@ error: using `Option.and_then(Some)`, which is a no-op - --> tests/ui/bind_instead_of_map.rs:8:13 + --> tests/ui/bind_instead_of_map.rs:7:13 | LL | let _ = x.and_then(Some); | ^^^^^^^^^^^^^^^^ help: use the expression directly: `x` @@ -11,13 +11,13 @@ LL | #![deny(clippy::bind_instead_of_map)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)` - --> tests/ui/bind_instead_of_map.rs:10:13 + --> tests/ui/bind_instead_of_map.rs:9:13 | LL | let _ = x.and_then(|o| Some(o + 1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `x.map(|o| o + 1)` error: using `Result.and_then(Ok)`, which is a no-op - --> tests/ui/bind_instead_of_map.rs:17:13 + --> tests/ui/bind_instead_of_map.rs:16:13 | LL | let _ = x.and_then(Ok); | ^^^^^^^^^^^^^^ help: use the expression directly: `x` diff --git a/tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs b/tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs index e848f0601e32..1646b5705b6d 100644 --- a/tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs +++ b/tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs @@ -1,6 +1,5 @@ #![deny(clippy::branches_sharing_code, clippy::if_same_then_else)] #![allow(dead_code)] -#![allow(clippy::uninlined_format_args)] //@no-rustfix // branches_sharing_code at the top and bottom of the if blocks @@ -70,7 +69,7 @@ fn complexer_example() { let b = 0xffff00ff; let e_id = gen_id(a, b); - println!("From the a `{}` to the b `{}`", a, b); + println!("From the a `{a}` to the b `{b}`"); let pack = DataPack { id: e_id, @@ -83,7 +82,7 @@ fn complexer_example() { let b = 0xffff00ff; let e_id = gen_id(a, b); - println!("The new ID is '{}'", e_id); + println!("The new ID is '{e_id}'"); let pack = DataPack { id: e_id, diff --git a/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr b/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr index 40f3453edb9a..f5c51f7888d0 100644 --- a/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr +++ b/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr @@ -1,5 +1,5 @@ error: all if blocks contain the same code at both the start and the end - --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:17:5 + --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:16:5 | LL | / if x == 7 { LL | | @@ -10,7 +10,7 @@ LL | | let _overlap_end = 2 * t; | |_________________________________^ | note: this code is shared at the end - --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:31:5 + --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:30:5 | LL | / let _u = 9; LL | | } @@ -34,7 +34,7 @@ LL + let _u = 9; | error: all if blocks contain the same code at both the start and the end - --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:35:5 + --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:34:5 | LL | / if x == 99 { LL | | @@ -45,7 +45,7 @@ LL | | let _overlap_middle = r * r; | |____________________________________^ | note: this code is shared at the end - --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:48:5 + --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:47:5 | LL | / let _overlap_end = r * r * r; LL | | let z = "end"; @@ -67,7 +67,7 @@ LL + let z = "end"; | error: all if blocks contain the same code at both the start and the end - --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:66:5 + --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:65:5 | LL | / if (x > 7 && y < 13) || (x + y) % 2 == 1 { LL | | @@ -78,7 +78,7 @@ LL | | let e_id = gen_id(a, b); | |________________________________^ | note: this code is shared at the end - --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:88:5 + --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:87:5 | LL | / let pack = DataPack { LL | | id: e_id, @@ -108,7 +108,7 @@ LL + process_data(pack); | error: all if blocks contain the same code at both the start and the end - --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:101:5 + --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:100:5 | LL | / let _ = if x == 7 { ... | @@ -116,7 +116,7 @@ LL | | let _ = 19; | |___________________^ | note: this code is shared at the end - --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:112:5 + --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:111:5 | LL | / x << 2 LL | | }; @@ -134,7 +134,7 @@ LL ~ x << 2; | error: all if blocks contain the same code at both the start and the end - --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:115:5 + --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:114:5 | LL | / if x == 9 { ... | @@ -142,7 +142,7 @@ LL | | let _ = 17; | |___________________^ | note: this code is shared at the end - --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:126:5 + --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:125:5 | LL | / x * 4 LL | | } @@ -160,7 +160,7 @@ LL + x * 4 | error: all if blocks contain the same code at both the start and the end - --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:158:9 + --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:157:9 | LL | / if false { LL | | @@ -168,7 +168,7 @@ LL | | let x = 1; | |______________________^ | note: this code is shared at the end - --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:166:9 + --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:165:9 | LL | / let y = 1; LL | | } diff --git a/tests/ui/cast_abs_to_unsigned.fixed b/tests/ui/cast_abs_to_unsigned.fixed index b55c22f5ca83..44cb66a8d184 100644 --- a/tests/ui/cast_abs_to_unsigned.fixed +++ b/tests/ui/cast_abs_to_unsigned.fixed @@ -1,11 +1,11 @@ #![warn(clippy::cast_abs_to_unsigned)] -#![allow(clippy::uninlined_format_args, unused)] +#![allow(unused)] fn main() { let x: i32 = -42; let y: u32 = x.unsigned_abs(); //~^ cast_abs_to_unsigned - println!("The absolute value of {} is {}", x, y); + println!("The absolute value of {x} is {y}"); let a: i32 = -3; let _: usize = a.unsigned_abs() as usize; diff --git a/tests/ui/cast_abs_to_unsigned.rs b/tests/ui/cast_abs_to_unsigned.rs index 466aa6aeb1fb..555b9090fe43 100644 --- a/tests/ui/cast_abs_to_unsigned.rs +++ b/tests/ui/cast_abs_to_unsigned.rs @@ -1,11 +1,11 @@ #![warn(clippy::cast_abs_to_unsigned)] -#![allow(clippy::uninlined_format_args, unused)] +#![allow(unused)] fn main() { let x: i32 = -42; let y: u32 = x.abs() as u32; //~^ cast_abs_to_unsigned - println!("The absolute value of {} is {}", x, y); + println!("The absolute value of {x} is {y}"); let a: i32 = -3; let _: usize = a.abs() as usize; diff --git a/tests/ui/crashes/ice-4775.rs b/tests/ui/crashes/ice-4775.rs index dd6c6b8de25a..e8c47b4f3a77 100644 --- a/tests/ui/crashes/ice-4775.rs +++ b/tests/ui/crashes/ice-4775.rs @@ -7,7 +7,7 @@ pub struct ArrayWrapper([usize; N]); impl ArrayWrapper<{ N }> { pub fn ice(&self) { for i in self.0.iter() { - println!("{}", i); + println!("{i}"); } } } diff --git a/tests/ui/default_trait_access.fixed b/tests/ui/default_trait_access.fixed index d3fe09a052ef..e3bf603da80f 100644 --- a/tests/ui/default_trait_access.fixed +++ b/tests/ui/default_trait_access.fixed @@ -1,7 +1,6 @@ //@aux-build: proc_macros.rs #![deny(clippy::default_trait_access)] #![allow(dead_code, unused_imports)] -#![allow(clippy::uninlined_format_args)] extern crate proc_macros; @@ -63,6 +62,7 @@ fn main() { let _s21: String = with_span!(s Default::default()); + #[expect(clippy::uninlined_format_args)] println!( "[{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}]", s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16, s17, s18, s19, s20, diff --git a/tests/ui/default_trait_access.rs b/tests/ui/default_trait_access.rs index cdffb2a2ee8c..8cc065e5bced 100644 --- a/tests/ui/default_trait_access.rs +++ b/tests/ui/default_trait_access.rs @@ -1,7 +1,6 @@ //@aux-build: proc_macros.rs #![deny(clippy::default_trait_access)] #![allow(dead_code, unused_imports)] -#![allow(clippy::uninlined_format_args)] extern crate proc_macros; @@ -63,6 +62,7 @@ fn main() { let _s21: String = with_span!(s Default::default()); + #[expect(clippy::uninlined_format_args)] println!( "[{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}]", s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16, s17, s18, s19, s20, diff --git a/tests/ui/default_trait_access.stderr b/tests/ui/default_trait_access.stderr index aa7eb4f89558..aa89516f175c 100644 --- a/tests/ui/default_trait_access.stderr +++ b/tests/ui/default_trait_access.stderr @@ -1,5 +1,5 @@ error: calling `String::default()` is more clear than this expression - --> tests/ui/default_trait_access.rs:13:22 + --> tests/ui/default_trait_access.rs:12:22 | LL | let s1: String = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `String::default()` @@ -11,43 +11,43 @@ LL | #![deny(clippy::default_trait_access)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: calling `String::default()` is more clear than this expression - --> tests/ui/default_trait_access.rs:18:22 + --> tests/ui/default_trait_access.rs:17:22 | LL | let s3: String = D2::default(); | ^^^^^^^^^^^^^ help: try: `String::default()` error: calling `String::default()` is more clear than this expression - --> tests/ui/default_trait_access.rs:21:22 + --> tests/ui/default_trait_access.rs:20:22 | LL | let s4: String = std::default::Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `String::default()` error: calling `String::default()` is more clear than this expression - --> tests/ui/default_trait_access.rs:26:22 + --> tests/ui/default_trait_access.rs:25:22 | LL | let s6: String = default::Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `String::default()` error: calling `GenericDerivedDefault::default()` is more clear than this expression - --> tests/ui/default_trait_access.rs:37:46 + --> tests/ui/default_trait_access.rs:36:46 | LL | let s11: GenericDerivedDefault = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `GenericDerivedDefault::default()` error: calling `TupleDerivedDefault::default()` is more clear than this expression - --> tests/ui/default_trait_access.rs:44:36 + --> tests/ui/default_trait_access.rs:43:36 | LL | let s14: TupleDerivedDefault = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `TupleDerivedDefault::default()` error: calling `ArrayDerivedDefault::default()` is more clear than this expression - --> tests/ui/default_trait_access.rs:47:36 + --> tests/ui/default_trait_access.rs:46:36 | LL | let s15: ArrayDerivedDefault = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `ArrayDerivedDefault::default()` error: calling `TupleStructDerivedDefault::default()` is more clear than this expression - --> tests/ui/default_trait_access.rs:52:42 + --> tests/ui/default_trait_access.rs:51:42 | LL | let s17: TupleStructDerivedDefault = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `TupleStructDerivedDefault::default()` diff --git a/tests/ui/explicit_counter_loop.rs b/tests/ui/explicit_counter_loop.rs index 13934785d7b1..ec4cecf37766 100644 --- a/tests/ui/explicit_counter_loop.rs +++ b/tests/ui/explicit_counter_loop.rs @@ -1,5 +1,5 @@ #![warn(clippy::explicit_counter_loop)] -#![allow(clippy::uninlined_format_args, clippy::useless_vec)] +#![allow(clippy::useless_vec)] //@no-rustfix: suggestion does not remove the `+= 1` fn main() { let mut vec = vec![1, 2, 3, 4]; @@ -89,13 +89,13 @@ mod issue_1219 { for _v in &vec { index += 1 } - println!("index: {}", index); + println!("index: {index}"); // should not trigger the lint because the count is conditional #1219 let text = "banana"; let mut count = 0; for ch in text.chars() { - println!("{}", count); + println!("{count}"); if ch == 'a' { continue; } @@ -106,7 +106,7 @@ mod issue_1219 { let text = "banana"; let mut count = 0; for ch in text.chars() { - println!("{}", count); + println!("{count}"); if ch == 'a' { count += 1; } @@ -118,7 +118,7 @@ mod issue_1219 { for ch in text.chars() { //~^ explicit_counter_loop - println!("{}", count); + println!("{count}"); count += 1; if ch == 'a' { continue; @@ -131,7 +131,7 @@ mod issue_1219 { for ch in text.chars() { //~^ explicit_counter_loop - println!("{}", count); + println!("{count}"); count += 1; for i in 0..2 { let _ = 123; @@ -142,7 +142,7 @@ mod issue_1219 { let text = "banana"; let mut count = 0; for ch in text.chars() { - println!("{}", count); + println!("{count}"); count += 1; for i in 0..2 { count += 1; @@ -157,7 +157,7 @@ mod issue_3308 { let mut skips = 0; let erasures = vec![]; for i in 0..10 { - println!("{}", skips); + println!("{skips}"); while erasures.contains(&(i + skips)) { skips += 1; } @@ -166,7 +166,7 @@ mod issue_3308 { // should not trigger the lint because the count is incremented multiple times let mut skips = 0; for i in 0..10 { - println!("{}", skips); + println!("{skips}"); let mut j = 0; while j < 5 { skips += 1; @@ -177,7 +177,7 @@ mod issue_3308 { // should not trigger the lint because the count is incremented multiple times let mut skips = 0; for i in 0..10 { - println!("{}", skips); + println!("{skips}"); for j in 0..5 { skips += 1; } @@ -205,7 +205,7 @@ mod issue_4732 { for _v in slice { index += 1 } - let _closure = || println!("index: {}", index); + let _closure = || println!("index: {index}"); } } @@ -217,7 +217,7 @@ mod issue_4677 { let mut count = 0; for _i in slice { count += 1; - println!("{}", count); + println!("{count}"); } } } diff --git a/tests/ui/explicit_write.fixed b/tests/ui/explicit_write.fixed index 024999fc609e..ab28c1ccd8e0 100644 --- a/tests/ui/explicit_write.fixed +++ b/tests/ui/explicit_write.fixed @@ -1,6 +1,5 @@ #![warn(clippy::explicit_write)] #![allow(unused_imports)] -#![allow(clippy::uninlined_format_args)] fn stdout() -> String { String::new() @@ -40,7 +39,7 @@ fn main() { //~^ explicit_write let value = 1; - eprintln!("with {}", value); + eprintln!("with {value}"); //~^ explicit_write eprintln!("with {} {}", 2, value); //~^ explicit_write @@ -49,7 +48,7 @@ fn main() { eprintln!("macro arg {}", one!()); //~^ explicit_write let width = 2; - eprintln!("{:w$}", value, w = width); + eprintln!("{value:w$}", w = width); //~^ explicit_write } // these should not warn, different destination diff --git a/tests/ui/explicit_write.rs b/tests/ui/explicit_write.rs index c83c760d48c8..975ee103b627 100644 --- a/tests/ui/explicit_write.rs +++ b/tests/ui/explicit_write.rs @@ -1,6 +1,5 @@ #![warn(clippy::explicit_write)] #![allow(unused_imports)] -#![allow(clippy::uninlined_format_args)] fn stdout() -> String { String::new() @@ -40,7 +39,7 @@ fn main() { //~^ explicit_write let value = 1; - writeln!(std::io::stderr(), "with {}", value).unwrap(); + writeln!(std::io::stderr(), "with {value}").unwrap(); //~^ explicit_write writeln!(std::io::stderr(), "with {} {}", 2, value).unwrap(); //~^ explicit_write @@ -49,7 +48,7 @@ fn main() { writeln!(std::io::stderr(), "macro arg {}", one!()).unwrap(); //~^ explicit_write let width = 2; - writeln!(std::io::stderr(), "{:w$}", value, w = width).unwrap(); + writeln!(std::io::stderr(), "{value:w$}", w = width).unwrap(); //~^ explicit_write } // these should not warn, different destination diff --git a/tests/ui/explicit_write.stderr b/tests/ui/explicit_write.stderr index 670a0411b310..ef4b2a049a6d 100644 --- a/tests/ui/explicit_write.stderr +++ b/tests/ui/explicit_write.stderr @@ -1,5 +1,5 @@ error: use of `write!(stdout(), ...).unwrap()` - --> tests/ui/explicit_write.rs:23:9 + --> tests/ui/explicit_write.rs:22:9 | LL | write!(std::io::stdout(), "test").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `print!("test")` @@ -8,76 +8,76 @@ LL | write!(std::io::stdout(), "test").unwrap(); = help: to override `-D warnings` add `#[allow(clippy::explicit_write)]` error: use of `write!(stderr(), ...).unwrap()` - --> tests/ui/explicit_write.rs:25:9 + --> tests/ui/explicit_write.rs:24:9 | LL | write!(std::io::stderr(), "test").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprint!("test")` error: use of `writeln!(stdout(), ...).unwrap()` - --> tests/ui/explicit_write.rs:27:9 + --> tests/ui/explicit_write.rs:26:9 | LL | writeln!(std::io::stdout(), "test").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `println!("test")` error: use of `writeln!(stderr(), ...).unwrap()` - --> tests/ui/explicit_write.rs:29:9 + --> tests/ui/explicit_write.rs:28:9 | LL | writeln!(std::io::stderr(), "test").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprintln!("test")` error: use of `stdout().write_fmt(...).unwrap()` - --> tests/ui/explicit_write.rs:31:9 + --> tests/ui/explicit_write.rs:30:9 | LL | std::io::stdout().write_fmt(format_args!("test")).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `print!("test")` error: use of `stderr().write_fmt(...).unwrap()` - --> tests/ui/explicit_write.rs:33:9 + --> tests/ui/explicit_write.rs:32:9 | LL | std::io::stderr().write_fmt(format_args!("test")).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprint!("test")` error: use of `writeln!(stdout(), ...).unwrap()` - --> tests/ui/explicit_write.rs:37:9 + --> tests/ui/explicit_write.rs:36:9 | LL | writeln!(std::io::stdout(), "test\ntest").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `println!("test\ntest")` error: use of `writeln!(stderr(), ...).unwrap()` - --> tests/ui/explicit_write.rs:39:9 + --> tests/ui/explicit_write.rs:38:9 | LL | writeln!(std::io::stderr(), "test\ntest").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprintln!("test\ntest")` error: use of `writeln!(stderr(), ...).unwrap()` - --> tests/ui/explicit_write.rs:43:9 - | -LL | writeln!(std::io::stderr(), "with {}", value).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprintln!("with {}", value)` - -error: use of `writeln!(stderr(), ...).unwrap()` - --> tests/ui/explicit_write.rs:45:9 - | -LL | writeln!(std::io::stderr(), "with {} {}", 2, value).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprintln!("with {} {}", 2, value)` - -error: use of `writeln!(stderr(), ...).unwrap()` - --> tests/ui/explicit_write.rs:47:9 + --> tests/ui/explicit_write.rs:42:9 | LL | writeln!(std::io::stderr(), "with {value}").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprintln!("with {value}")` error: use of `writeln!(stderr(), ...).unwrap()` - --> tests/ui/explicit_write.rs:49:9 + --> tests/ui/explicit_write.rs:44:9 + | +LL | writeln!(std::io::stderr(), "with {} {}", 2, value).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprintln!("with {} {}", 2, value)` + +error: use of `writeln!(stderr(), ...).unwrap()` + --> tests/ui/explicit_write.rs:46:9 + | +LL | writeln!(std::io::stderr(), "with {value}").unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprintln!("with {value}")` + +error: use of `writeln!(stderr(), ...).unwrap()` + --> tests/ui/explicit_write.rs:48:9 | LL | writeln!(std::io::stderr(), "macro arg {}", one!()).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprintln!("macro arg {}", one!())` error: use of `writeln!(stderr(), ...).unwrap()` - --> tests/ui/explicit_write.rs:52:9 + --> tests/ui/explicit_write.rs:51:9 | -LL | writeln!(std::io::stderr(), "{:w$}", value, w = width).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprintln!("{:w$}", value, w = width)` +LL | writeln!(std::io::stderr(), "{value:w$}", w = width).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprintln!("{value:w$}", w = width)` error: aborting due to 13 previous errors diff --git a/tests/ui/fallible_impl_from.rs b/tests/ui/fallible_impl_from.rs index 1c62c1e937b6..28bb1157f6e5 100644 --- a/tests/ui/fallible_impl_from.rs +++ b/tests/ui/fallible_impl_from.rs @@ -1,5 +1,4 @@ #![deny(clippy::fallible_impl_from)] -#![allow(clippy::uninlined_format_args)] // docs example struct Foo(i32); @@ -62,7 +61,7 @@ impl<'a> From<&'a mut as ProjStrTrait>::ProjString> for Invalid { fn from(s: &'a mut as ProjStrTrait>::ProjString) -> Invalid { if s.parse::().ok().unwrap() != 42 { - panic!("{:?}", s); + panic!("{s:?}"); } Invalid } diff --git a/tests/ui/fallible_impl_from.stderr b/tests/ui/fallible_impl_from.stderr index 402494b39f30..25ecc8b0a39a 100644 --- a/tests/ui/fallible_impl_from.stderr +++ b/tests/ui/fallible_impl_from.stderr @@ -1,5 +1,5 @@ error: consider implementing `TryFrom` instead - --> tests/ui/fallible_impl_from.rs:6:1 + --> tests/ui/fallible_impl_from.rs:5:1 | LL | / impl From for Foo { LL | | @@ -11,7 +11,7 @@ LL | | } | = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail note: potential failure(s) - --> tests/ui/fallible_impl_from.rs:10:13 + --> tests/ui/fallible_impl_from.rs:9:13 | LL | Foo(s.parse().unwrap()) | ^^^^^^^^^^^^^^^^^^ @@ -22,7 +22,7 @@ LL | #![deny(clippy::fallible_impl_from)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: consider implementing `TryFrom` instead - --> tests/ui/fallible_impl_from.rs:29:1 + --> tests/ui/fallible_impl_from.rs:28:1 | LL | / impl From for Invalid { LL | | @@ -34,13 +34,13 @@ LL | | } | = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail note: potential failure(s) - --> tests/ui/fallible_impl_from.rs:34:13 + --> tests/ui/fallible_impl_from.rs:33:13 | LL | panic!(); | ^^^^^^^^ error: consider implementing `TryFrom` instead - --> tests/ui/fallible_impl_from.rs:40:1 + --> tests/ui/fallible_impl_from.rs:39:1 | LL | / impl From> for Invalid { LL | | @@ -52,7 +52,7 @@ LL | | } | = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail note: potential failure(s) - --> tests/ui/fallible_impl_from.rs:44:17 + --> tests/ui/fallible_impl_from.rs:43:17 | LL | let s = s.unwrap(); | ^^^^^^^^^^ @@ -65,7 +65,7 @@ LL | panic!("{:?}", s); | ^^^^^^^^^^^^^^^^^ error: consider implementing `TryFrom` instead - --> tests/ui/fallible_impl_from.rs:60:1 + --> tests/ui/fallible_impl_from.rs:59:1 | LL | / impl<'a> From<&'a mut as ProjStrTrait>::ProjString> for Invalid { LL | | @@ -77,12 +77,12 @@ LL | | } | = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail note: potential failure(s) - --> tests/ui/fallible_impl_from.rs:64:12 + --> tests/ui/fallible_impl_from.rs:63:12 | LL | if s.parse::().ok().unwrap() != 42 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -LL | panic!("{:?}", s); - | ^^^^^^^^^^^^^^^^^ +LL | panic!("{s:?}"); + | ^^^^^^^^^^^^^^^ error: aborting due to 4 previous errors diff --git a/tests/ui/index_refutable_slice/if_let_slice_binding.fixed b/tests/ui/index_refutable_slice/if_let_slice_binding.fixed index 050cdfcba966..dc7e09bbdc7d 100644 --- a/tests/ui/index_refutable_slice/if_let_slice_binding.fixed +++ b/tests/ui/index_refutable_slice/if_let_slice_binding.fixed @@ -1,5 +1,5 @@ #![deny(clippy::index_refutable_slice)] -#![allow(clippy::uninlined_format_args, clippy::needless_lifetimes, clippy::collapsible_if)] +#![allow(clippy::needless_lifetimes, clippy::collapsible_if)] enum SomeEnum { One(T), @@ -60,7 +60,7 @@ fn lintable_examples() { println!("{:?}", slice_1); } - println!("{:?}", slice); + println!("{slice:?}"); // This should not suggest using the `ref` keyword as the scrutinee is already // a reference @@ -70,7 +70,7 @@ fn lintable_examples() { println!("{:?}", slice_0); } - println!("{:?}", slice); + println!("{slice:?}"); } fn slice_index_above_limit() { @@ -113,7 +113,7 @@ fn check_slice_as_arg() { println!("This is interesting {}", slice[0]); } } - println!("{:?}", slice_wrapped); + println!("{slice_wrapped:?}"); } fn check_slice_in_struct() { @@ -152,7 +152,7 @@ fn check_slice_in_struct() { println!("This is super awesome! {}", slice_0); } } - println!("Complete wrap: {:?}", wrap); + println!("Complete wrap: {wrap:?}"); } /// This would be a nice additional feature to have in the future, but adding it @@ -164,14 +164,14 @@ fn mutable_slice_index() { if let Some(ref mut slice) = slice { slice[0] = String::from("Mr. Penguin"); } - println!("Use after modification: {:?}", slice); + println!("Use after modification: {slice:?}"); // Mut access on reference let mut slice: Option<[String; 1]> = Some([String::from("Cat")]); if let Some(slice) = &mut slice { slice[0] = String::from("Lord Meow Meow"); } - println!("Use after modification: {:?}", slice); + println!("Use after modification: {slice:?}"); } /// The lint will ignore bindings with sub patterns as it would be hard diff --git a/tests/ui/index_refutable_slice/if_let_slice_binding.rs b/tests/ui/index_refutable_slice/if_let_slice_binding.rs index 91429bfea276..f39ace101b45 100644 --- a/tests/ui/index_refutable_slice/if_let_slice_binding.rs +++ b/tests/ui/index_refutable_slice/if_let_slice_binding.rs @@ -1,5 +1,5 @@ #![deny(clippy::index_refutable_slice)] -#![allow(clippy::uninlined_format_args, clippy::needless_lifetimes, clippy::collapsible_if)] +#![allow(clippy::needless_lifetimes, clippy::collapsible_if)] enum SomeEnum { One(T), @@ -60,7 +60,7 @@ fn lintable_examples() { println!("{:?}", slice[1]); } - println!("{:?}", slice); + println!("{slice:?}"); // This should not suggest using the `ref` keyword as the scrutinee is already // a reference @@ -70,7 +70,7 @@ fn lintable_examples() { println!("{:?}", slice[0]); } - println!("{:?}", slice); + println!("{slice:?}"); } fn slice_index_above_limit() { @@ -113,7 +113,7 @@ fn check_slice_as_arg() { println!("This is interesting {}", slice[0]); } } - println!("{:?}", slice_wrapped); + println!("{slice_wrapped:?}"); } fn check_slice_in_struct() { @@ -152,7 +152,7 @@ fn check_slice_in_struct() { println!("This is super awesome! {}", slice[0]); } } - println!("Complete wrap: {:?}", wrap); + println!("Complete wrap: {wrap:?}"); } /// This would be a nice additional feature to have in the future, but adding it @@ -164,14 +164,14 @@ fn mutable_slice_index() { if let Some(ref mut slice) = slice { slice[0] = String::from("Mr. Penguin"); } - println!("Use after modification: {:?}", slice); + println!("Use after modification: {slice:?}"); // Mut access on reference let mut slice: Option<[String; 1]> = Some([String::from("Cat")]); if let Some(slice) = &mut slice { slice[0] = String::from("Lord Meow Meow"); } - println!("Use after modification: {:?}", slice); + println!("Use after modification: {slice:?}"); } /// The lint will ignore bindings with sub patterns as it would be hard diff --git a/tests/ui/infinite_iter.rs b/tests/ui/infinite_iter.rs index 701a86534ba0..4e1668ed04fb 100644 --- a/tests/ui/infinite_iter.rs +++ b/tests/ui/infinite_iter.rs @@ -1,4 +1,4 @@ -#![allow(clippy::uninlined_format_args, clippy::double_ended_iterator_last)] +#![allow(clippy::double_ended_iterator_last)] use std::iter::repeat; fn square_is_lower_64(x: &u32) -> bool { @@ -30,7 +30,7 @@ fn infinite_iters() { .rev() .cycle() .map(|x| x + 1_u32) - .for_each(|x| println!("{}", x)); + .for_each(|x| println!("{x}")); // infinite iter (0..3_u32).flat_map(|x| x..).sum::(); // infinite iter diff --git a/tests/ui/infinite_iter.stderr b/tests/ui/infinite_iter.stderr index b9e7c008f93e..3db97313b621 100644 --- a/tests/ui/infinite_iter.stderr +++ b/tests/ui/infinite_iter.stderr @@ -30,8 +30,8 @@ LL | | LL | | .rev() LL | | .cycle() LL | | .map(|x| x + 1_u32) -LL | | .for_each(|x| println!("{}", x)); - | |________________________________________^ +LL | | .for_each(|x| println!("{x}")); + | |______________________________________^ error: infinite iteration detected --> tests/ui/infinite_iter.rs:37:5 diff --git a/tests/ui/issue_2356.fixed b/tests/ui/issue_2356.fixed index 46ba653eba2c..3e066df77bfb 100644 --- a/tests/ui/issue_2356.fixed +++ b/tests/ui/issue_2356.fixed @@ -1,6 +1,5 @@ #![deny(clippy::while_let_on_iterator)] #![allow(unused_mut)] -#![allow(clippy::uninlined_format_args)] use std::iter::Iterator; @@ -16,7 +15,7 @@ impl Foo { fn foo2>(mut it: I) { for e in it { //~^ while_let_on_iterator - println!("{:?}", e); + println!("{e:?}"); } } } diff --git a/tests/ui/issue_2356.rs b/tests/ui/issue_2356.rs index defe2584a93e..98600d17c6df 100644 --- a/tests/ui/issue_2356.rs +++ b/tests/ui/issue_2356.rs @@ -1,6 +1,5 @@ #![deny(clippy::while_let_on_iterator)] #![allow(unused_mut)] -#![allow(clippy::uninlined_format_args)] use std::iter::Iterator; @@ -16,7 +15,7 @@ impl Foo { fn foo2>(mut it: I) { while let Some(e) = it.next() { //~^ while_let_on_iterator - println!("{:?}", e); + println!("{e:?}"); } } } diff --git a/tests/ui/issue_2356.stderr b/tests/ui/issue_2356.stderr index eae2ce97fc6b..ddee91fcfcd5 100644 --- a/tests/ui/issue_2356.stderr +++ b/tests/ui/issue_2356.stderr @@ -1,5 +1,5 @@ error: this loop could be written as a `for` loop - --> tests/ui/issue_2356.rs:17:9 + --> tests/ui/issue_2356.rs:16:9 | LL | while let Some(e) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for e in it` diff --git a/tests/ui/issue_4266.rs b/tests/ui/issue_4266.rs index 664f0b84a207..b2a01124995c 100644 --- a/tests/ui/issue_4266.rs +++ b/tests/ui/issue_4266.rs @@ -1,5 +1,4 @@ #![allow(dead_code)] -#![allow(clippy::uninlined_format_args)] async fn sink1<'a>(_: &'a str) {} // lint //~^ needless_lifetimes @@ -39,7 +38,7 @@ impl Foo { // rust-lang/rust#61115 // ok async fn print(s: &str) { - println!("{}", s); + println!("{s}"); } fn main() {} diff --git a/tests/ui/issue_4266.stderr b/tests/ui/issue_4266.stderr index 0e181025430f..b80a738a50be 100644 --- a/tests/ui/issue_4266.stderr +++ b/tests/ui/issue_4266.stderr @@ -1,5 +1,5 @@ error: the following explicit lifetimes could be elided: 'a - --> tests/ui/issue_4266.rs:4:16 + --> tests/ui/issue_4266.rs:3:16 | LL | async fn sink1<'a>(_: &'a str) {} // lint | ^^ ^^ @@ -8,13 +8,13 @@ LL | async fn sink1<'a>(_: &'a str) {} // lint = help: to override `-D warnings` add `#[allow(clippy::needless_lifetimes)]` error: the following explicit lifetimes could be elided: 'a - --> tests/ui/issue_4266.rs:10:21 + --> tests/ui/issue_4266.rs:9:21 | LL | async fn one_to_one<'a>(s: &'a str) -> &'a str { | ^^ ^^ ^^ error: methods called `new` usually take no `self` - --> tests/ui/issue_4266.rs:32:22 + --> tests/ui/issue_4266.rs:31:22 | LL | pub async fn new(&mut self) -> Self { | ^^^^^^^^^ diff --git a/tests/ui/match_single_binding.fixed b/tests/ui/match_single_binding.fixed index e29fb87dbc30..7e899a476666 100644 --- a/tests/ui/match_single_binding.fixed +++ b/tests/ui/match_single_binding.fixed @@ -4,7 +4,6 @@ clippy::let_unit_value, clippy::no_effect, clippy::toplevel_ref_arg, - clippy::uninlined_format_args, clippy::useless_vec )] @@ -32,11 +31,11 @@ fn main() { // Lint let (x, y, z) = (a, b, c); { - println!("{} {} {}", x, y, z); + println!("{x} {y} {z}"); } // Lint let (x, y, z) = (a, b, c); - println!("{} {} {}", x, y, z); + println!("{x} {y} {z}"); // Ok foo!(a); // Ok @@ -47,7 +46,7 @@ fn main() { // Ok let d = Some(5); match d { - Some(d) => println!("{}", d), + Some(d) => println!("{d}"), _ => println!("None"), } // Lint @@ -55,7 +54,7 @@ fn main() { // Lint { let x = 29; - println!("x has a value of {}", x); + println!("x has a value of {x}"); } // Lint { @@ -67,18 +66,18 @@ fn main() { // Lint let p = Point { x: 0, y: 7 }; let Point { x, y } = p; - println!("Coords: ({}, {})", x, y); + println!("Coords: ({x}, {y})"); // Lint let Point { x: x1, y: y1 } = p; - println!("Coords: ({}, {})", x1, y1); + println!("Coords: ({x1}, {y1})"); // Lint let x = 5; let ref r = x; - println!("Got a reference to {}", r); + println!("Got a reference to {r}"); // Lint let mut x = 5; let ref mut mr = x; - println!("Got a mutable reference to {}", mr); + println!("Got a mutable reference to {mr}"); // Lint let Point { x, y } = coords(); let product = x * y; @@ -122,7 +121,7 @@ fn issue_8723() { let (pre, suf) = val.split_at(idx); val = { - println!("{}", pre); + println!("{pre}"); suf }; @@ -210,20 +209,20 @@ mod issue15018 { let x = 1; { let (x, y, z) = (a, b, c); - println!("{} {} {}", x, y, z); + println!("{x} {y} {z}"); } println!("x = {x}"); } fn not_used_later(a: i32, b: i32, c: i32) { let (x, y, z) = (a, b, c); - println!("{} {} {}", x, y, z) + println!("{x} {y} {z}") } #[allow(irrefutable_let_patterns)] fn not_used_later_but_shadowed(a: i32, b: i32, c: i32) { let (x, y, z) = (a, b, c); - println!("{} {} {}", x, y, z); + println!("{x} {y} {z}"); let x = 1; println!("x = {x}"); } @@ -231,27 +230,27 @@ mod issue15018 { #[allow(irrefutable_let_patterns)] fn not_used_later_but_shadowed_nested(a: i32, b: i32, c: i32) { let (x, y, z) = (a, b, c); - println!("{} {} {}", x, y, z); + println!("{x} {x} {y}"); if let (x, y, z) = (a, b, c) { - println!("{} {} {}", x, y, z) + println!("{x} {y} {z}") } { let x: i32 = 1; { let (x, y, z) = (a, b, c); - println!("{} {} {}", x, y, z); + println!("{x} {y} {z}"); } if let (x, y, z) = (a, x, c) { - println!("{} {} {}", x, y, z) + println!("{x} {y} {z}") } } { let (x, y, z) = (a, b, c); - println!("{} {} {}", x, y, z); + println!("{x} {y} {z}"); let fn_ = |y| { - println!("{} {} {}", a, b, y); + println!("{a} {b} {y}"); }; fn_(c); } diff --git a/tests/ui/match_single_binding.rs b/tests/ui/match_single_binding.rs index ede1ab32beb5..37a96f2287c8 100644 --- a/tests/ui/match_single_binding.rs +++ b/tests/ui/match_single_binding.rs @@ -4,7 +4,6 @@ clippy::let_unit_value, clippy::no_effect, clippy::toplevel_ref_arg, - clippy::uninlined_format_args, clippy::useless_vec )] @@ -33,13 +32,13 @@ fn main() { match (a, b, c) { //~^ match_single_binding (x, y, z) => { - println!("{} {} {}", x, y, z); + println!("{x} {y} {z}"); }, } // Lint match (a, b, c) { //~^ match_single_binding - (x, y, z) => println!("{} {} {}", x, y, z), + (x, y, z) => println!("{x} {y} {z}"), } // Ok foo!(a); @@ -51,7 +50,7 @@ fn main() { // Ok let d = Some(5); match d { - Some(d) => println!("{}", d), + Some(d) => println!("{d}"), _ => println!("None"), } // Lint @@ -64,7 +63,7 @@ fn main() { //~^ match_single_binding _ => { let x = 29; - println!("x has a value of {}", x); + println!("x has a value of {x}"); }, } // Lint @@ -81,24 +80,24 @@ fn main() { let p = Point { x: 0, y: 7 }; match p { //~^ match_single_binding - Point { x, y } => println!("Coords: ({}, {})", x, y), + Point { x, y } => println!("Coords: ({x}, {y})"), } // Lint match p { //~^ match_single_binding - Point { x: x1, y: y1 } => println!("Coords: ({}, {})", x1, y1), + Point { x: x1, y: y1 } => println!("Coords: ({x1}, {y1})"), } // Lint let x = 5; match x { //~^ match_single_binding - ref r => println!("Got a reference to {}", r), + ref r => println!("Got a reference to {r}"), } // Lint let mut x = 5; match x { //~^ match_single_binding - ref mut mr => println!("Got a mutable reference to {}", mr), + ref mut mr => println!("Got a mutable reference to {mr}"), } // Lint let product = match coords() { @@ -150,7 +149,7 @@ fn issue_8723() { val = match val.split_at(idx) { //~^ match_single_binding (pre, suf) => { - println!("{}", pre); + println!("{pre}"); suf }, }; @@ -273,7 +272,7 @@ mod issue15018 { let x = 1; match (a, b, c) { //~^ match_single_binding - (x, y, z) => println!("{} {} {}", x, y, z), + (x, y, z) => println!("{x} {y} {z}"), } println!("x = {x}"); } @@ -281,7 +280,7 @@ mod issue15018 { fn not_used_later(a: i32, b: i32, c: i32) { match (a, b, c) { //~^ match_single_binding - (x, y, z) => println!("{} {} {}", x, y, z), + (x, y, z) => println!("{x} {y} {z}"), } } @@ -289,7 +288,7 @@ mod issue15018 { fn not_used_later_but_shadowed(a: i32, b: i32, c: i32) { match (a, b, c) { //~^ match_single_binding - (x, y, z) => println!("{} {} {}", x, y, z), + (x, y, z) => println!("{x} {y} {z}"), } let x = 1; println!("x = {x}"); @@ -299,30 +298,30 @@ mod issue15018 { fn not_used_later_but_shadowed_nested(a: i32, b: i32, c: i32) { match (a, b, c) { //~^ match_single_binding - (x, y, z) => println!("{} {} {}", x, y, z), + (x, y, z) => println!("{x} {x} {y}"), } if let (x, y, z) = (a, b, c) { - println!("{} {} {}", x, y, z) + println!("{x} {y} {z}") } { let x: i32 = 1; match (a, b, c) { //~^ match_single_binding - (x, y, z) => println!("{} {} {}", x, y, z), + (x, y, z) => println!("{x} {y} {z}"), } if let (x, y, z) = (a, x, c) { - println!("{} {} {}", x, y, z) + println!("{x} {y} {z}") } } { match (a, b, c) { //~^ match_single_binding - (x, y, z) => println!("{} {} {}", x, y, z), + (x, y, z) => println!("{x} {y} {z}"), } let fn_ = |y| { - println!("{} {} {}", a, b, y); + println!("{a} {b} {y}"); }; fn_(c); } diff --git a/tests/ui/match_single_binding.stderr b/tests/ui/match_single_binding.stderr index eea71777890e..82fc43aaa5ea 100644 --- a/tests/ui/match_single_binding.stderr +++ b/tests/ui/match_single_binding.stderr @@ -1,10 +1,10 @@ error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:33:5 + --> tests/ui/match_single_binding.rs:32:5 | LL | / match (a, b, c) { LL | | LL | | (x, y, z) => { -LL | | println!("{} {} {}", x, y, z); +LL | | println!("{x} {y} {z}"); LL | | }, LL | | } | |_____^ @@ -15,27 +15,27 @@ help: consider using a `let` statement | LL ~ let (x, y, z) = (a, b, c); LL + { -LL + println!("{} {} {}", x, y, z); +LL + println!("{x} {y} {z}"); LL + } | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:40:5 + --> tests/ui/match_single_binding.rs:39:5 | LL | / match (a, b, c) { LL | | -LL | | (x, y, z) => println!("{} {} {}", x, y, z), +LL | | (x, y, z) => println!("{x} {y} {z}"), LL | | } | |_____^ | help: consider using a `let` statement | LL ~ let (x, y, z) = (a, b, c); -LL + println!("{} {} {}", x, y, z); +LL + println!("{x} {y} {z}"); | error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:58:5 + --> tests/ui/match_single_binding.rs:57:5 | LL | / match a { LL | | @@ -44,13 +44,13 @@ LL | | } | |_____^ help: consider using the match body instead: `println!("whatever");` error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:63:5 + --> tests/ui/match_single_binding.rs:62:5 | LL | / match a { LL | | LL | | _ => { LL | | let x = 29; -LL | | println!("x has a value of {}", x); +LL | | println!("x has a value of {x}"); LL | | }, LL | | } | |_____^ @@ -59,12 +59,12 @@ help: consider using the match body instead | LL ~ { LL + let x = 29; -LL + println!("x has a value of {}", x); +LL + println!("x has a value of {x}"); LL + } | error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:71:5 + --> tests/ui/match_single_binding.rs:70:5 | LL | / match a { LL | | @@ -86,67 +86,67 @@ LL + } | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:82:5 + --> tests/ui/match_single_binding.rs:81:5 | LL | / match p { LL | | -LL | | Point { x, y } => println!("Coords: ({}, {})", x, y), +LL | | Point { x, y } => println!("Coords: ({x}, {y})"), LL | | } | |_____^ | help: consider using a `let` statement | LL ~ let Point { x, y } = p; -LL + println!("Coords: ({}, {})", x, y); +LL + println!("Coords: ({x}, {y})"); | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:87:5 + --> tests/ui/match_single_binding.rs:86:5 | LL | / match p { LL | | -LL | | Point { x: x1, y: y1 } => println!("Coords: ({}, {})", x1, y1), +LL | | Point { x: x1, y: y1 } => println!("Coords: ({x1}, {y1})"), LL | | } | |_____^ | help: consider using a `let` statement | LL ~ let Point { x: x1, y: y1 } = p; -LL + println!("Coords: ({}, {})", x1, y1); +LL + println!("Coords: ({x1}, {y1})"); | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:93:5 + --> tests/ui/match_single_binding.rs:92:5 | LL | / match x { LL | | -LL | | ref r => println!("Got a reference to {}", r), +LL | | ref r => println!("Got a reference to {r}"), LL | | } | |_____^ | help: consider using a `let` statement | LL ~ let ref r = x; -LL + println!("Got a reference to {}", r); +LL + println!("Got a reference to {r}"); | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:99:5 + --> tests/ui/match_single_binding.rs:98:5 | LL | / match x { LL | | -LL | | ref mut mr => println!("Got a mutable reference to {}", mr), +LL | | ref mut mr => println!("Got a mutable reference to {mr}"), LL | | } | |_____^ | help: consider using a `let` statement | LL ~ let ref mut mr = x; -LL + println!("Got a mutable reference to {}", mr); +LL + println!("Got a mutable reference to {mr}"); | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:104:5 + --> tests/ui/match_single_binding.rs:103:5 | LL | / let product = match coords() { LL | | @@ -161,7 +161,7 @@ LL + let product = x * y; | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:113:18 + --> tests/ui/match_single_binding.rs:112:18 | LL | .map(|i| match i.unwrap() { | __________________^ @@ -179,7 +179,7 @@ LL ~ }) | error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:140:5 + --> tests/ui/match_single_binding.rs:139:5 | LL | / match x { LL | | @@ -189,12 +189,12 @@ LL | | } | |_____^ help: consider using the match body instead: `println!("Not an array index start")` error: this assignment could be simplified - --> tests/ui/match_single_binding.rs:150:5 + --> tests/ui/match_single_binding.rs:149:5 | LL | / val = match val.split_at(idx) { LL | | LL | | (pre, suf) => { -LL | | println!("{}", pre); +LL | | println!("{pre}"); LL | | suf LL | | }, LL | | }; @@ -204,13 +204,13 @@ help: consider removing the `match` expression | LL ~ let (pre, suf) = val.split_at(idx); LL + val = { -LL + println!("{}", pre); +LL + println!("{pre}"); LL + suf LL ~ }; | error: this match could be replaced by its scrutinee and body - --> tests/ui/match_single_binding.rs:164:16 + --> tests/ui/match_single_binding.rs:163:16 | LL | let _ = || match side_effects() { | ________________^ @@ -228,7 +228,7 @@ LL ~ }; | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:171:5 + --> tests/ui/match_single_binding.rs:170:5 | LL | / match r { LL | | @@ -253,7 +253,7 @@ LL ~ }; | error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:185:5 + --> tests/ui/match_single_binding.rs:184:5 | LL | / match 1 { LL | | @@ -262,7 +262,7 @@ LL | | } | |_____^ help: consider using the match body instead: `();` error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:190:13 + --> tests/ui/match_single_binding.rs:189:13 | LL | let a = match 1 { | _____________^ @@ -272,7 +272,7 @@ LL | | }; | |_____^ help: consider using the match body instead: `()` error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:195:5 + --> tests/ui/match_single_binding.rs:194:5 | LL | / match 1 { LL | | @@ -281,7 +281,7 @@ LL | | } | |_____^ help: consider using the match body instead: `side_effects();` error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:200:13 + --> tests/ui/match_single_binding.rs:199:13 | LL | let b = match 1 { | _____________^ @@ -291,7 +291,7 @@ LL | | }; | |_____^ help: consider using the match body instead: `side_effects()` error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:205:5 + --> tests/ui/match_single_binding.rs:204:5 | LL | / match 1 { LL | | @@ -300,7 +300,7 @@ LL | | } | |_____^ help: consider using the match body instead: `println!("1");` error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:210:13 + --> tests/ui/match_single_binding.rs:209:13 | LL | let c = match 1 { | _____________^ @@ -310,7 +310,7 @@ LL | | }; | |_____^ help: consider using the match body instead: `println!("1")` error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:216:9 + --> tests/ui/match_single_binding.rs:215:9 | LL | / match 1 { LL | | @@ -319,7 +319,7 @@ LL | | }, | |_________^ help: consider using the match body instead: `()` error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:220:9 + --> tests/ui/match_single_binding.rs:219:9 | LL | / match 1 { LL | | @@ -328,7 +328,7 @@ LL | | }, | |_________^ help: consider using the match body instead: `side_effects()` error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:224:9 + --> tests/ui/match_single_binding.rs:223:9 | LL | / match 1 { LL | | @@ -337,7 +337,7 @@ LL | | }, | |_________^ help: consider using the match body instead: `println!("1")` error: this match could be replaced by its scrutinee and body - --> tests/ui/match_single_binding.rs:239:5 + --> tests/ui/match_single_binding.rs:238:5 | LL | / match dbg!(3) { LL | | _ => println!("here"), @@ -351,7 +351,7 @@ LL + println!("here"); | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:243:5 + --> tests/ui/match_single_binding.rs:242:5 | LL | / match dbg!(3) { LL | | id!(a) => println!("found {a}"), @@ -365,7 +365,7 @@ LL + println!("found {a}"); | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:247:5 + --> tests/ui/match_single_binding.rs:246:5 | LL | / let id!(_a) = match dbg!(3) { LL | | id!(b) => dbg!(b + 1), @@ -379,7 +379,7 @@ LL + let id!(_a) = dbg!(b + 1); | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:255:21 + --> tests/ui/match_single_binding.rs:254:21 | LL | inner: [(); match 1 { | _____________________^ @@ -397,7 +397,7 @@ LL ~ }], | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:263:13 + --> tests/ui/match_single_binding.rs:262:13 | LL | / match 1 { LL | | @@ -412,11 +412,11 @@ LL + 42 | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:274:9 + --> tests/ui/match_single_binding.rs:273:9 | LL | / match (a, b, c) { LL | | -LL | | (x, y, z) => println!("{} {} {}", x, y, z), +LL | | (x, y, z) => println!("{x} {y} {z}"), LL | | } | |_________^ | @@ -424,61 +424,61 @@ help: consider using a `let` statement | LL ~ { LL + let (x, y, z) = (a, b, c); -LL + println!("{} {} {}", x, y, z); +LL + println!("{x} {y} {z}"); LL + } | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:282:9 + --> tests/ui/match_single_binding.rs:281:9 | LL | / match (a, b, c) { LL | | -LL | | (x, y, z) => println!("{} {} {}", x, y, z), +LL | | (x, y, z) => println!("{x} {y} {z}"), LL | | } | |_________^ | help: consider using a `let` statement | LL ~ let (x, y, z) = (a, b, c); -LL + println!("{} {} {}", x, y, z) +LL + println!("{x} {y} {z}") | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:290:9 + --> tests/ui/match_single_binding.rs:289:9 | LL | / match (a, b, c) { LL | | -LL | | (x, y, z) => println!("{} {} {}", x, y, z), +LL | | (x, y, z) => println!("{x} {y} {z}"), LL | | } | |_________^ | help: consider using a `let` statement | LL ~ let (x, y, z) = (a, b, c); -LL + println!("{} {} {}", x, y, z); +LL + println!("{x} {y} {z}"); | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:300:9 + --> tests/ui/match_single_binding.rs:299:9 | LL | / match (a, b, c) { LL | | -LL | | (x, y, z) => println!("{} {} {}", x, y, z), +LL | | (x, y, z) => println!("{x} {x} {y}"), LL | | } | |_________^ | help: consider using a `let` statement | LL ~ let (x, y, z) = (a, b, c); -LL + println!("{} {} {}", x, y, z); +LL + println!("{x} {x} {y}"); | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:310:13 + --> tests/ui/match_single_binding.rs:309:13 | LL | / match (a, b, c) { LL | | -LL | | (x, y, z) => println!("{} {} {}", x, y, z), +LL | | (x, y, z) => println!("{x} {y} {z}"), LL | | } | |_____________^ | @@ -486,27 +486,27 @@ help: consider using a `let` statement | LL ~ { LL + let (x, y, z) = (a, b, c); -LL + println!("{} {} {}", x, y, z); +LL + println!("{x} {y} {z}"); LL + } | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:320:13 + --> tests/ui/match_single_binding.rs:319:13 | LL | / match (a, b, c) { LL | | -LL | | (x, y, z) => println!("{} {} {}", x, y, z), +LL | | (x, y, z) => println!("{x} {y} {z}"), LL | | } | |_____________^ | help: consider using a `let` statement | LL ~ let (x, y, z) = (a, b, c); -LL + println!("{} {} {}", x, y, z); +LL + println!("{x} {y} {z}"); | error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:335:12 + --> tests/ui/match_single_binding.rs:334:12 | LL | && match b { | ____________^ @@ -516,7 +516,7 @@ LL | | }; | |_________^ help: consider using the match body instead: `b < c` error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:341:12 + --> tests/ui/match_single_binding.rs:340:12 | LL | && match (a, b) { | ____________^ diff --git a/tests/ui/match_single_binding2.fixed b/tests/ui/match_single_binding2.fixed index 988121f50d0f..f00987470ae1 100644 --- a/tests/ui/match_single_binding2.fixed +++ b/tests/ui/match_single_binding2.fixed @@ -1,6 +1,5 @@ #![warn(clippy::match_single_binding)] #![allow(unused_variables)] -#![allow(clippy::uninlined_format_args)] fn main() { // Lint (additional curly braces needed, see #6572) @@ -29,7 +28,7 @@ fn main() { #[rustfmt::skip] Some((first, _second)) => { let (a, b) = get_tup(); - println!("a {:?} and b {:?}", a, b) + println!("a {a:?} and b {b:?}") }, None => println!("nothing"), } diff --git a/tests/ui/match_single_binding2.rs b/tests/ui/match_single_binding2.rs index a4fb2bd6f381..5416f647b4e6 100644 --- a/tests/ui/match_single_binding2.rs +++ b/tests/ui/match_single_binding2.rs @@ -1,6 +1,5 @@ #![warn(clippy::match_single_binding)] #![allow(unused_variables)] -#![allow(clippy::uninlined_format_args)] fn main() { // Lint (additional curly braces needed, see #6572) @@ -30,7 +29,7 @@ fn main() { Some((first, _second)) => { match get_tup() { //~^ match_single_binding - (a, b) => println!("a {:?} and b {:?}", a, b), + (a, b) => println!("a {a:?} and b {b:?}"), } }, None => println!("nothing"), diff --git a/tests/ui/match_single_binding2.stderr b/tests/ui/match_single_binding2.stderr index a24cbe3eed76..65b8aa6acd5e 100644 --- a/tests/ui/match_single_binding2.stderr +++ b/tests/ui/match_single_binding2.stderr @@ -1,5 +1,5 @@ error: this match could be written as a `let` statement - --> tests/ui/match_single_binding2.rs:17:36 + --> tests/ui/match_single_binding2.rs:16:36 | LL | Some((iter, _item)) => match iter.size_hint() { | ____________________________________^ @@ -19,22 +19,22 @@ LL ~ }, | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding2.rs:31:13 + --> tests/ui/match_single_binding2.rs:30:13 | LL | / match get_tup() { LL | | -LL | | (a, b) => println!("a {:?} and b {:?}", a, b), +LL | | (a, b) => println!("a {a:?} and b {b:?}"), LL | | } | |_____________^ | help: consider using a `let` statement | LL ~ let (a, b) = get_tup(); -LL + println!("a {:?} and b {:?}", a, b) +LL + println!("a {a:?} and b {b:?}") | error: this match could be replaced by its scrutinee and body - --> tests/ui/match_single_binding2.rs:43:5 + --> tests/ui/match_single_binding2.rs:42:5 | LL | / match side_effects() { LL | | @@ -49,7 +49,7 @@ LL + println!("Side effects"); | error: this match could be replaced by its scrutinee and body - --> tests/ui/match_single_binding2.rs:51:5 + --> tests/ui/match_single_binding2.rs:50:5 | LL | / match match x { LL | | From f9852613a965f2f3daccec46d63b85fb0407960d Mon Sep 17 00:00:00 2001 From: yanglsh Date: Mon, 25 Aug 2025 08:32:42 +0800 Subject: [PATCH 093/226] fix: `mem_replace_with_default` wrongly unmangled macros --- clippy_lints/src/mem_replace.rs | 13 +++++-------- tests/ui/mem_replace.fixed | 6 ++++++ tests/ui/mem_replace.rs | 6 ++++++ tests/ui/mem_replace.stderr | 8 +++++++- 4 files changed, 24 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index e39916f733d5..95ecef598700 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::{snippet, snippet_with_applicability}; +use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_non_aggregate_primitive_type; use clippy_utils::{ @@ -269,14 +269,11 @@ fn check_replace_with_default( ), |diag| { if !expr.span.from_expansion() { - let suggestion = format!("{top_crate}::mem::take({})", snippet(cx, dest.span, "")); + let mut applicability = Applicability::MachineApplicable; + let (dest_snip, _) = snippet_with_context(cx, dest.span, expr.span.ctxt(), "", &mut applicability); + let suggestion = format!("{top_crate}::mem::take({dest_snip})"); - diag.span_suggestion( - expr.span, - "consider using", - suggestion, - Applicability::MachineApplicable, - ); + diag.span_suggestion(expr.span, "consider using", suggestion, applicability); } }, ); diff --git a/tests/ui/mem_replace.fixed b/tests/ui/mem_replace.fixed index 870ef23113a2..94ad1aad3eb7 100644 --- a/tests/ui/mem_replace.fixed +++ b/tests/ui/mem_replace.fixed @@ -179,3 +179,9 @@ fn mem_replace_option_with_some_bad_msrv() { let mut an_option = Some(0); let replaced = mem::replace(&mut an_option, Some(1)); } + +fn issue15785() { + let mut text = String::from("foo"); + let replaced = std::mem::take(dbg!(&mut text)); + //~^ mem_replace_with_default +} diff --git a/tests/ui/mem_replace.rs b/tests/ui/mem_replace.rs index b4ed5eafea95..ac79660f0f1e 100644 --- a/tests/ui/mem_replace.rs +++ b/tests/ui/mem_replace.rs @@ -179,3 +179,9 @@ fn mem_replace_option_with_some_bad_msrv() { let mut an_option = Some(0); let replaced = mem::replace(&mut an_option, Some(1)); } + +fn issue15785() { + let mut text = String::from("foo"); + let replaced = std::mem::replace(dbg!(&mut text), String::default()); + //~^ mem_replace_with_default +} diff --git a/tests/ui/mem_replace.stderr b/tests/ui/mem_replace.stderr index fb4a367266d3..104c98540028 100644 --- a/tests/ui/mem_replace.stderr +++ b/tests/ui/mem_replace.stderr @@ -181,5 +181,11 @@ error: replacing an `Option` with `Some(..)` LL | let replaced = mem::replace(if b { &mut opt1 } else { &mut opt2 }, Some(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::replace()` instead: `(if b { &mut opt1 } else { &mut opt2 }).replace(1)` -error: aborting due to 29 previous errors +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> tests/ui/mem_replace.rs:185:20 + | +LL | let replaced = std::mem::replace(dbg!(&mut text), String::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(dbg!(&mut text))` + +error: aborting due to 30 previous errors From 1db4d8ebfdf14896c43fc714507e37b97db5d289 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Sun, 28 Sep 2025 23:04:55 +0000 Subject: [PATCH 094/226] Split Bound into Canonical and Bound --- clippy_lints/src/pass_by_ref_or_value.rs | 4 ++-- clippy_utils/src/ty/mod.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs index 1b1e77bbea8f..6e9142b22e0e 100644 --- a/clippy_lints/src/pass_by_ref_or_value.rs +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -14,7 +14,7 @@ use rustc_hir::{BindingMode, Body, FnDecl, Impl, ItemKind, MutTy, Mutability, No use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::adjustment::{Adjust, PointerCoercion}; use rustc_middle::ty::layout::LayoutOf; -use rustc_middle::ty::{self, RegionKind, TyCtxt}; +use rustc_middle::ty::{self, BoundVarIndexKind, RegionKind, TyCtxt}; use rustc_session::impl_lint_pass; use rustc_span::def_id::LocalDefId; use rustc_span::{Span, sym}; @@ -151,7 +151,7 @@ impl PassByRefOrValue { match *ty.skip_binder().kind() { ty::Ref(lt, ty, Mutability::Not) => { match lt.kind() { - RegionKind::ReBound(index, region) + RegionKind::ReBound(BoundVarIndexKind::Bound(index), region) if index.as_u32() == 0 && output_regions.contains(®ion) => { continue; diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index e4bc3b768294..c03469c2b885 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -21,7 +21,7 @@ use rustc_middle::traits::EvaluationResult; use rustc_middle::ty::adjustment::{Adjust, Adjustment}; use rustc_middle::ty::layout::ValidityRequirement; use rustc_middle::ty::{ - self, AdtDef, AliasTy, AssocItem, AssocTag, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind, GenericArgsRef, + self, AdtDef, AliasTy, AssocItem, AssocTag, Binder, BoundRegion, BoundVarIndexKind, FnSig, GenericArg, GenericArgKind, GenericArgsRef, GenericParamDefKind, IntTy, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr, }; @@ -826,7 +826,7 @@ pub fn for_each_top_level_late_bound_region( impl<'tcx, B, F: FnMut(BoundRegion) -> ControlFlow> TypeVisitor> for V { type Result = ControlFlow; fn visit_region(&mut self, r: Region<'tcx>) -> Self::Result { - if let RegionKind::ReBound(idx, bound) = r.kind() + if let RegionKind::ReBound(BoundVarIndexKind::Bound(idx), bound) = r.kind() && idx.as_u32() == self.index { (self.f)(bound) From 61b1f0e37fad6842c24c41b0de2cb7dbf90f658f Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sat, 23 Aug 2025 06:51:50 -0400 Subject: [PATCH 095/226] `assertions_on_constants`: Don't suggest removing assertions with non-local constants. --- clippy_lints/src/assertions_on_constants.rs | 104 +++++++++++--------- clippy_lints/src/lib.rs | 2 +- clippy_utils/src/msrvs.rs | 3 +- tests/ui/assertions_on_constants.rs | 29 +++++- tests/ui/assertions_on_constants.stderr | 88 +++++++++++------ 5 files changed, 146 insertions(+), 80 deletions(-) diff --git a/clippy_lints/src/assertions_on_constants.rs b/clippy_lints/src/assertions_on_constants.rs index b6684825835a..a6518216aa82 100644 --- a/clippy_lints/src/assertions_on_constants.rs +++ b/clippy_lints/src/assertions_on_constants.rs @@ -1,10 +1,13 @@ +use clippy_config::Conf; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::is_inside_always_const_context; -use clippy_utils::macros::{PanicExpn, find_assert_args, root_macro_call_first_node}; +use clippy_utils::macros::{find_assert_args, root_macro_call_first_node}; +use clippy_utils::msrvs::Msrv; +use clippy_utils::{is_inside_always_const_context, msrvs}; +use rustc_ast::LitKind; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::declare_lint_pass; +use rustc_session::impl_lint_pass; use rustc_span::sym; declare_clippy_lint! { @@ -28,56 +31,59 @@ declare_clippy_lint! { "`assert!(true)` / `assert!(false)` will be optimized out by the compiler, and should probably be replaced by a `panic!()` or `unreachable!()`" } -declare_lint_pass!(AssertionsOnConstants => [ASSERTIONS_ON_CONSTANTS]); +impl_lint_pass!(AssertionsOnConstants => [ASSERTIONS_ON_CONSTANTS]); +pub struct AssertionsOnConstants { + msrv: Msrv, +} +impl AssertionsOnConstants { + pub fn new(conf: &Conf) -> Self { + Self { msrv: conf.msrv } + } +} impl<'tcx> LateLintPass<'tcx> for AssertionsOnConstants { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - let Some(macro_call) = root_macro_call_first_node(cx, e) else { - return; - }; - let is_debug = match cx.tcx.get_diagnostic_name(macro_call.def_id) { - Some(sym::debug_assert_macro) => true, - Some(sym::assert_macro) => false, - _ => return, - }; - let Some((condition, panic_expn)) = find_assert_args(cx, e, macro_call.expn) else { - return; - }; - let Some(Constant::Bool(val)) = ConstEvalCtxt::new(cx).eval(condition) else { - return; - }; - - match condition.kind { - ExprKind::Path(..) | ExprKind::Lit(_) => {}, - _ if is_inside_always_const_context(cx.tcx, e.hir_id) => return, - _ => {}, - } - - if val { - span_lint_and_help( - cx, - ASSERTIONS_ON_CONSTANTS, - macro_call.span, - format!( - "`{}!(true)` will be optimized out by the compiler", - cx.tcx.item_name(macro_call.def_id) - ), - None, - "remove it", - ); - } else if !is_debug { - let (assert_arg, panic_arg) = match panic_expn { - PanicExpn::Empty => ("", ""), - _ => (", ..", ".."), + if let Some(macro_call) = root_macro_call_first_node(cx, e) + && let is_debug = match cx.tcx.get_diagnostic_name(macro_call.def_id) { + Some(sym::debug_assert_macro) => true, + Some(sym::assert_macro) => false, + _ => return, + } + && let Some((condition, _)) = find_assert_args(cx, e, macro_call.expn) + && let Some((Constant::Bool(assert_val), const_src)) = ConstEvalCtxt::new(cx).eval_with_source(condition) + && let in_const_context = is_inside_always_const_context(cx.tcx, e.hir_id) + && (const_src.is_local() || !in_const_context) + && !(is_debug && as_bool_lit(condition) == Some(false)) + { + let (msg, help) = if !const_src.is_local() { + let help = if self.msrv.meets(cx, msrvs::CONST_BLOCKS) { + "consider moving this into a const block: `const { assert!(..) }`" + } else if self.msrv.meets(cx, msrvs::CONST_PANIC) { + "consider moving this to an anonymous constant: `const _: () = { assert!(..); }`" + } else { + return; + }; + ("this assertion has a constant value", help) + } else if assert_val { + ("this assertion is always `true`", "remove the assertion") + } else { + ( + "this assertion is always `false`", + "replace this with `panic!()` or `unreachable!()`", + ) }; - span_lint_and_help( - cx, - ASSERTIONS_ON_CONSTANTS, - macro_call.span, - format!("`assert!(false{assert_arg})` should probably be replaced"), - None, - format!("use `panic!({panic_arg})` or `unreachable!({panic_arg})`"), - ); + + span_lint_and_help(cx, ASSERTIONS_ON_CONSTANTS, macro_call.span, msg, None, help); } } } + +fn as_bool_lit(e: &Expr<'_>) -> Option { + if let ExprKind::Lit(l) = e.kind + && let LitKind::Bool(b) = l.node + { + Some(b) + } else { + None + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 51dabee78e9f..0a955d238319 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -595,7 +595,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::new(redundant_clone::RedundantClone)); store.register_late_pass(|_| Box::new(slow_vector_initialization::SlowVectorInit)); store.register_late_pass(move |_| Box::new(unnecessary_wraps::UnnecessaryWraps::new(conf))); - store.register_late_pass(|_| Box::new(assertions_on_constants::AssertionsOnConstants)); + store.register_late_pass(|_| Box::new(assertions_on_constants::AssertionsOnConstants::new(conf))); store.register_late_pass(|_| Box::new(assertions_on_result_states::AssertionsOnResultStates)); store.register_late_pass(|_| Box::new(inherent_to_string::InherentToString)); store.register_late_pass(move |_| Box::new(trait_bounds::TraitBounds::new(conf))); diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 5ce28a582f4a..995deba07778 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -31,6 +31,7 @@ msrv_aliases! { 1,82,0 { IS_NONE_OR, REPEAT_N, RAW_REF_OP } 1,81,0 { LINT_REASONS_STABILIZATION, ERROR_IN_CORE, EXPLICIT_SELF_TYPE_ELISION, DURATION_ABS_DIFF } 1,80,0 { BOX_INTO_ITER, LAZY_CELL } + 1,79,0 { CONST_BLOCKS } 1,77,0 { C_STR_LITERALS } 1,76,0 { PTR_FROM_REF, OPTION_RESULT_INSPECT } 1,75,0 { OPTION_AS_SLICE } @@ -46,7 +47,7 @@ msrv_aliases! { 1,60,0 { ABS_DIFF } 1,59,0 { THREAD_LOCAL_CONST_INIT } 1,58,0 { FORMAT_ARGS_CAPTURE, PATTERN_TRAIT_CHAR_ARRAY, CONST_RAW_PTR_DEREF } - 1,57,0 { MAP_WHILE } + 1,57,0 { MAP_WHILE, CONST_PANIC } 1,56,0 { CONST_FN_UNION } 1,55,0 { SEEK_REWIND } 1,54,0 { INTO_KEYS } diff --git a/tests/ui/assertions_on_constants.rs b/tests/ui/assertions_on_constants.rs index c2516c541475..b613892f206a 100644 --- a/tests/ui/assertions_on_constants.rs +++ b/tests/ui/assertions_on_constants.rs @@ -42,7 +42,6 @@ fn main() { assert_const!(3); assert_const!(-1); - // Don't lint if based on `cfg!(..)`: assert!(cfg!(feature = "hey") || cfg!(not(feature = "asdf"))); let flag: bool = cfg!(not(feature = "asdf")); @@ -62,9 +61,37 @@ fn main() { const _: () = assert!(N.is_power_of_two()); } +const C: bool = true; + const _: () = { assert!(true); //~^ assertions_on_constants assert!(8 == (7 + 1)); + //~^ assertions_on_constants + + assert!(C); }; + +#[clippy::msrv = "1.57"] +fn _f1() { + assert!(C); + //~^ assertions_on_constants +} + +#[clippy::msrv = "1.56"] +fn _f2() { + assert!(C); +} + +#[clippy::msrv = "1.79"] +fn _f3() { + assert!(C); + //~^ assertions_on_constants +} + +#[clippy::msrv = "1.78"] +fn _f4() { + assert!(C); + //~^ assertions_on_constants +} diff --git a/tests/ui/assertions_on_constants.stderr b/tests/ui/assertions_on_constants.stderr index 8b7440ec4832..9aed1405fa04 100644 --- a/tests/ui/assertions_on_constants.stderr +++ b/tests/ui/assertions_on_constants.stderr @@ -1,100 +1,132 @@ -error: `assert!(true)` will be optimized out by the compiler +error: this assertion is always `true` --> tests/ui/assertions_on_constants.rs:10:5 | LL | assert!(true); | ^^^^^^^^^^^^^ | - = help: remove it + = help: remove the assertion = note: `-D clippy::assertions-on-constants` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::assertions_on_constants)]` -error: `assert!(false)` should probably be replaced +error: this assertion is always `false` --> tests/ui/assertions_on_constants.rs:13:5 | LL | assert!(false); | ^^^^^^^^^^^^^^ | - = help: use `panic!()` or `unreachable!()` + = help: replace this with `panic!()` or `unreachable!()` -error: `assert!(true)` will be optimized out by the compiler +error: this assertion is always `true` --> tests/ui/assertions_on_constants.rs:16:5 | LL | assert!(true, "true message"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: remove it + = help: remove the assertion -error: `assert!(false, ..)` should probably be replaced +error: this assertion is always `false` --> tests/ui/assertions_on_constants.rs:19:5 | LL | assert!(false, "false message"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: use `panic!(..)` or `unreachable!(..)` + = help: replace this with `panic!()` or `unreachable!()` -error: `assert!(false, ..)` should probably be replaced +error: this assertion is always `false` --> tests/ui/assertions_on_constants.rs:23:5 | LL | assert!(false, "{}", msg.to_uppercase()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: use `panic!(..)` or `unreachable!(..)` + = help: replace this with `panic!()` or `unreachable!()` -error: `assert!(true)` will be optimized out by the compiler +error: this assertion has a constant value --> tests/ui/assertions_on_constants.rs:27:5 | LL | assert!(B); | ^^^^^^^^^^ | - = help: remove it + = help: consider moving this into a const block: `const { assert!(..) }` -error: `assert!(false)` should probably be replaced +error: this assertion has a constant value --> tests/ui/assertions_on_constants.rs:31:5 | LL | assert!(C); | ^^^^^^^^^^ | - = help: use `panic!()` or `unreachable!()` + = help: consider moving this into a const block: `const { assert!(..) }` -error: `assert!(false, ..)` should probably be replaced +error: this assertion has a constant value --> tests/ui/assertions_on_constants.rs:34:5 | LL | assert!(C, "C message"); | ^^^^^^^^^^^^^^^^^^^^^^^ | - = help: use `panic!(..)` or `unreachable!(..)` + = help: consider moving this into a const block: `const { assert!(..) }` -error: `debug_assert!(true)` will be optimized out by the compiler +error: this assertion is always `true` --> tests/ui/assertions_on_constants.rs:37:5 | LL | debug_assert!(true); | ^^^^^^^^^^^^^^^^^^^ | - = help: remove it + = help: remove the assertion -error: `assert!(true)` will be optimized out by the compiler - --> tests/ui/assertions_on_constants.rs:54:19 +error: this assertion is always `true` + --> tests/ui/assertions_on_constants.rs:53:19 | LL | const _: () = assert!(true); | ^^^^^^^^^^^^^ | - = help: remove it + = help: remove the assertion -error: `assert!(true)` will be optimized out by the compiler - --> tests/ui/assertions_on_constants.rs:57:5 +error: this assertion is always `true` + --> tests/ui/assertions_on_constants.rs:56:5 | LL | assert!(8 == (7 + 1)); | ^^^^^^^^^^^^^^^^^^^^^ | - = help: remove it + = help: remove the assertion -error: `assert!(true)` will be optimized out by the compiler - --> tests/ui/assertions_on_constants.rs:66:5 +error: this assertion is always `true` + --> tests/ui/assertions_on_constants.rs:67:5 | LL | assert!(true); | ^^^^^^^^^^^^^ | - = help: remove it + = help: remove the assertion -error: aborting due to 12 previous errors +error: this assertion is always `true` + --> tests/ui/assertions_on_constants.rs:70:5 + | +LL | assert!(8 == (7 + 1)); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = help: remove the assertion + +error: this assertion has a constant value + --> tests/ui/assertions_on_constants.rs:78:5 + | +LL | assert!(C); + | ^^^^^^^^^^ + | + = help: consider moving this to an anonymous constant: `const _: () = { assert!(..); }` + +error: this assertion has a constant value + --> tests/ui/assertions_on_constants.rs:89:5 + | +LL | assert!(C); + | ^^^^^^^^^^ + | + = help: consider moving this into a const block: `const { assert!(..) }` + +error: this assertion has a constant value + --> tests/ui/assertions_on_constants.rs:95:5 + | +LL | assert!(C); + | ^^^^^^^^^^ + | + = help: consider moving this to an anonymous constant: `const _: () = { assert!(..); }` + +error: aborting due to 16 previous errors From f1079915b46f159e822a3f943ecafed8b7db8c6f Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Tue, 23 Sep 2025 18:22:08 +0200 Subject: [PATCH 096/226] Do not suggest using a `if let` chain if it is not supported This might be due to a low edition (< 2024) or too low a MSRV. --- book/src/lint_configuration.md | 1 + clippy_config/src/conf.rs | 1 + clippy_lints/src/collapsible_if.rs | 7 ++- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/unwrap.rs | 50 +++++++++++++------- clippy_utils/src/lib.rs | 6 +++ tests/ui/checked_unwrap/if_let_chains.rs | 24 ++++++++++ tests/ui/checked_unwrap/if_let_chains.stderr | 29 ++++++++++++ 8 files changed, 97 insertions(+), 23 deletions(-) create mode 100644 tests/ui/checked_unwrap/if_let_chains.rs create mode 100644 tests/ui/checked_unwrap/if_let_chains.stderr diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index f8263bb8852a..e7e0670357b3 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -898,6 +898,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`unchecked_duration_subtraction`](https://rust-lang.github.io/rust-clippy/master/index.html#unchecked_duration_subtraction) * [`uninlined_format_args`](https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args) * [`unnecessary_lazy_evaluations`](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations) +* [`unnecessary_unwrap`](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap) * [`unnested_or_patterns`](https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns) * [`unused_trait_names`](https://rust-lang.github.io/rust-clippy/master/index.html#unused_trait_names) * [`use_self`](https://rust-lang.github.io/rust-clippy/master/index.html#use_self) diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index e14e15a022f7..df8495dfe0ec 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -794,6 +794,7 @@ define_Conf! { unchecked_duration_subtraction, uninlined_format_args, unnecessary_lazy_evaluations, + unnecessary_unwrap, unnested_or_patterns, unused_trait_names, use_self, diff --git a/clippy_lints/src/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs index b239c20ab4d8..b13e307a3f9c 100644 --- a/clippy_lints/src/collapsible_if.rs +++ b/clippy_lints/src/collapsible_if.rs @@ -1,8 +1,8 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::msrvs::Msrv; use clippy_utils::source::{IntoSpan as _, SpanRangeExt, snippet, snippet_block_with_applicability}; -use clippy_utils::{span_contains_non_whitespace, sym, tokenize_with_text}; +use clippy_utils::{can_use_if_let_chains, span_contains_non_whitespace, sym, tokenize_with_text}; use rustc_ast::{BinOpKind, MetaItemInner}; use rustc_errors::Applicability; use rustc_hir::{Block, Expr, ExprKind, StmtKind}; @@ -216,8 +216,7 @@ impl CollapsibleIf { } fn eligible_condition(&self, cx: &LateContext<'_>, cond: &Expr<'_>) -> bool { - !matches!(cond.kind, ExprKind::Let(..)) - || (cx.tcx.sess.edition().at_least_rust_2024() && self.msrv.meets(cx, msrvs::LET_CHAINS)) + !matches!(cond.kind, ExprKind::Let(..)) || can_use_if_let_chains(cx, self.msrv) } // Check that nothing significant can be found between the initial `{` of `inner_if` and diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c56fa257b068..1a706df567e5 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -589,7 +589,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::new(map_unit_fn::MapUnit)); store.register_late_pass(|_| Box::new(inherent_impl::MultipleInherentImpl)); store.register_late_pass(|_| Box::new(neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd)); - store.register_late_pass(|_| Box::new(unwrap::Unwrap)); + store.register_late_pass(move |_| Box::new(unwrap::Unwrap::new(conf))); store.register_late_pass(move |_| Box::new(indexing_slicing::IndexingSlicing::new(conf))); store.register_late_pass(move |tcx| Box::new(non_copy_const::NonCopyConst::new(tcx, conf))); store.register_late_pass(|_| Box::new(redundant_clone::RedundantClone)); diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index 34dfe5b6546f..aee8028a75de 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -1,7 +1,9 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::msrvs::Msrv; +use clippy_utils::ty::get_type_diagnostic_name; use clippy_utils::usage::is_potentially_local_place; -use clippy_utils::{higher, path_to_local, sym}; +use clippy_utils::{can_use_if_let_chains, higher, path_to_local, sym}; use rustc_errors::Applicability; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn}; use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, Node, UnOp}; @@ -10,7 +12,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_session::declare_lint_pass; +use rustc_session::impl_lint_pass; use rustc_span::def_id::LocalDefId; use rustc_span::{Span, Symbol}; @@ -72,10 +74,21 @@ declare_clippy_lint! { "checks for calls of `unwrap[_err]()` that will always fail" } +pub(crate) struct Unwrap { + msrv: Msrv, +} + +impl Unwrap { + pub fn new(conf: &'static Conf) -> Self { + Self { msrv: conf.msrv } + } +} + /// Visitor that keeps track of which variables are unwrappable. struct UnwrappableVariablesVisitor<'a, 'tcx> { unwrappables: Vec>, cx: &'a LateContext<'tcx>, + msrv: Msrv, } /// What kind of unwrappable this is. @@ -133,12 +146,14 @@ fn collect_unwrap_info<'tcx>( invert: bool, is_entire_condition: bool, ) -> Vec> { - fn is_relevant_option_call(cx: &LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> bool { - is_type_diagnostic_item(cx, ty, sym::Option) && matches!(method_name, sym::is_none | sym::is_some) - } - - fn is_relevant_result_call(cx: &LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> bool { - is_type_diagnostic_item(cx, ty, sym::Result) && matches!(method_name, sym::is_err | sym::is_ok) + fn option_or_result_call(cx: &LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> Option<(UnwrappableKind, bool)> { + match (get_type_diagnostic_name(cx, ty)?, method_name) { + (sym::Option, sym::is_some) => Some((UnwrappableKind::Option, true)), + (sym::Option, sym::is_none) => Some((UnwrappableKind::Option, false)), + (sym::Result, sym::is_ok) => Some((UnwrappableKind::Result, true)), + (sym::Result, sym::is_err) => Some((UnwrappableKind::Result, false)), + _ => None, + } } match expr.kind { @@ -157,15 +172,9 @@ fn collect_unwrap_info<'tcx>( if let Some(local_id) = path_to_local(receiver) && let ty = cx.typeck_results().expr_ty(receiver) && let name = method_name.ident.name - && (is_relevant_option_call(cx, ty, name) || is_relevant_result_call(cx, ty, name)) => + && let Some((kind, unwrappable)) = option_or_result_call(cx, ty, name) => { - let unwrappable = matches!(name, sym::is_some | sym::is_ok); let safe_to_unwrap = unwrappable != invert; - let kind = if is_type_diagnostic_item(cx, ty, sym::Option) { - UnwrappableKind::Option - } else { - UnwrappableKind::Result - }; vec![UnwrapInfo { local_id, @@ -357,7 +366,11 @@ impl<'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'_, 'tcx> { ); } else { diag.span_label(unwrappable.check.span, "the check is happening here"); - diag.help("try using `if let` or `match`"); + if can_use_if_let_chains(self.cx, self.msrv) { + diag.help("try using `if let` or `match`"); + } else { + diag.help("try using `match`"); + } } }, ); @@ -383,7 +396,7 @@ impl<'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'_, 'tcx> { } } -declare_lint_pass!(Unwrap => [PANICKING_UNWRAP, UNNECESSARY_UNWRAP]); +impl_lint_pass!(Unwrap => [PANICKING_UNWRAP, UNNECESSARY_UNWRAP]); impl<'tcx> LateLintPass<'tcx> for Unwrap { fn check_fn( @@ -402,6 +415,7 @@ impl<'tcx> LateLintPass<'tcx> for Unwrap { let mut v = UnwrappableVariablesVisitor { unwrappables: Vec::new(), cx, + msrv: self.msrv, }; walk_fn(&mut v, kind, decl, body.id(), fn_id); diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index feadc0ecf659..1e0bd3a2c4dc 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -129,6 +129,7 @@ use visitors::{Visitable, for_each_unconsumed_temporary}; use crate::ast_utils::unordered_over; use crate::consts::{ConstEvalCtxt, Constant, mir_to_const}; use crate::higher::Range; +use crate::msrvs::Msrv; use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type}; use crate::visitors::for_each_expr_without_closures; @@ -3659,3 +3660,8 @@ pub fn is_expr_async_block(expr: &Expr<'_>) -> bool { }) ) } + +/// Checks if the chosen edition and `msrv` allows using `if let` chains. +pub fn can_use_if_let_chains(cx: &LateContext<'_>, msrv: Msrv) -> bool { + cx.tcx.sess.edition().at_least_rust_2024() && msrv.meets(cx, msrvs::LET_CHAINS) +} diff --git a/tests/ui/checked_unwrap/if_let_chains.rs b/tests/ui/checked_unwrap/if_let_chains.rs new file mode 100644 index 000000000000..cfa7715965cd --- /dev/null +++ b/tests/ui/checked_unwrap/if_let_chains.rs @@ -0,0 +1,24 @@ +//@require-annotations-for-level: ERROR +#![deny(clippy::unnecessary_unwrap)] + +#[clippy::msrv = "1.85"] +fn if_let_chains_unsupported(a: Option, b: Option) { + if a.is_none() || b.is_none() { + println!("a or b is not set"); + } else { + println!("the value of a is {}", a.unwrap()); + //~^ unnecessary_unwrap + //~| HELP: try using `match` + } +} + +#[clippy::msrv = "1.88"] +fn if_let_chains_supported(a: Option, b: Option) { + if a.is_none() || b.is_none() { + println!("a or b is not set"); + } else { + println!("the value of a is {}", a.unwrap()); + //~^ unnecessary_unwrap + //~| HELP: try using `if let` or `match` + } +} diff --git a/tests/ui/checked_unwrap/if_let_chains.stderr b/tests/ui/checked_unwrap/if_let_chains.stderr new file mode 100644 index 000000000000..8a4137de37a3 --- /dev/null +++ b/tests/ui/checked_unwrap/if_let_chains.stderr @@ -0,0 +1,29 @@ +error: called `unwrap` on `a` after checking its variant with `is_none` + --> tests/ui/checked_unwrap/if_let_chains.rs:9:42 + | +LL | if a.is_none() || b.is_none() { + | ----------- the check is happening here +... +LL | println!("the value of a is {}", a.unwrap()); + | ^^^^^^^^^^ + | + = help: try using `match` +note: the lint level is defined here + --> tests/ui/checked_unwrap/if_let_chains.rs:2:9 + | +LL | #![deny(clippy::unnecessary_unwrap)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: called `unwrap` on `a` after checking its variant with `is_none` + --> tests/ui/checked_unwrap/if_let_chains.rs:20:42 + | +LL | if a.is_none() || b.is_none() { + | ----------- the check is happening here +... +LL | println!("the value of a is {}", a.unwrap()); + | ^^^^^^^^^^ + | + = help: try using `if let` or `match` + +error: aborting due to 2 previous errors + From 3e24d50407e29f750ae8dc10e84bfef142b715e2 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sat, 23 Aug 2025 04:33:46 -0400 Subject: [PATCH 097/226] Rename `eval_simple` to `eval_local` and take the `SyntaxContext` as an argument. --- clippy_lints/src/assertions_on_constants.rs | 3 +- clippy_lints/src/floating_point_arithmetic.rs | 13 ++--- clippy_lints/src/if_not_else.rs | 12 +++-- .../src/invalid_upcast_comparisons.rs | 2 +- clippy_lints/src/manual_rem_euclid.rs | 20 +++++--- clippy_lints/src/matches/manual_unwrap_or.rs | 2 +- .../src/methods/is_digit_ascii_radix.rs | 2 +- .../src/methods/unnecessary_min_or_max.rs | 5 +- clippy_lints/src/minmax.rs | 12 +++-- .../src/operators/arithmetic_side_effects.rs | 4 +- clippy_lints/src/operators/erasing_op.rs | 4 +- clippy_lints/src/operators/float_cmp.rs | 5 +- clippy_lints/src/operators/identity_op.rs | 32 ++++++------ .../src/operators/manual_is_multiple_of.rs | 9 ++-- .../src/operators/numeric_arithmetic.rs | 2 +- clippy_lints/src/zero_div_zero.rs | 5 +- clippy_utils/src/consts.rs | 49 +++++++++++++------ clippy_utils/src/higher.rs | 2 +- clippy_utils/src/hir_utils.rs | 8 +-- tests/ui/assertions_on_constants.rs | 1 + tests/ui/assertions_on_constants.stderr | 24 ++++++--- 21 files changed, 134 insertions(+), 82 deletions(-) diff --git a/clippy_lints/src/assertions_on_constants.rs b/clippy_lints/src/assertions_on_constants.rs index a6518216aa82..2586c89bc868 100644 --- a/clippy_lints/src/assertions_on_constants.rs +++ b/clippy_lints/src/assertions_on_constants.rs @@ -50,7 +50,8 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnConstants { _ => return, } && let Some((condition, _)) = find_assert_args(cx, e, macro_call.expn) - && let Some((Constant::Bool(assert_val), const_src)) = ConstEvalCtxt::new(cx).eval_with_source(condition) + && let Some((Constant::Bool(assert_val), const_src)) = + ConstEvalCtxt::new(cx).eval_with_source(condition, macro_call.span.ctxt()) && let in_const_context = is_inside_always_const_context(cx.tcx, e.hir_id) && (const_src.is_local() || !in_const_context) && !(is_debug && as_bool_lit(condition) == Some(false)) diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 84d39dd81c91..5052bbb3ca03 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -11,6 +11,7 @@ use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::declare_lint_pass; +use rustc_span::SyntaxContext; use rustc_span::source_map::Spanned; use std::f32::consts as f32_consts; use std::f64::consts as f64_consts; @@ -517,8 +518,8 @@ fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) { fn is_testing_positive(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) -> bool { if let ExprKind::Binary(Spanned { node: op, .. }, left, right) = expr.kind { match op { - BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, right) && eq_expr_value(cx, left, test), - BinOpKind::Lt | BinOpKind::Le => is_zero(cx, left) && eq_expr_value(cx, right, test), + BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, right, expr.span.ctxt()) && eq_expr_value(cx, left, test), + BinOpKind::Lt | BinOpKind::Le => is_zero(cx, left, expr.span.ctxt()) && eq_expr_value(cx, right, test), _ => false, } } else { @@ -530,8 +531,8 @@ fn is_testing_positive(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) - fn is_testing_negative(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) -> bool { if let ExprKind::Binary(Spanned { node: op, .. }, left, right) = expr.kind { match op { - BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, left) && eq_expr_value(cx, right, test), - BinOpKind::Lt | BinOpKind::Le => is_zero(cx, right) && eq_expr_value(cx, left, test), + BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, left, expr.span.ctxt()) && eq_expr_value(cx, right, test), + BinOpKind::Lt | BinOpKind::Le => is_zero(cx, right, expr.span.ctxt()) && eq_expr_value(cx, left, test), _ => false, } } else { @@ -540,8 +541,8 @@ fn is_testing_negative(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) - } /// Returns true iff expr is some zero literal -fn is_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - match ConstEvalCtxt::new(cx).eval_simple(expr) { +fn is_zero(cx: &LateContext<'_>, expr: &Expr<'_>, ctxt: SyntaxContext) -> bool { + match ConstEvalCtxt::new(cx).eval_local(expr, ctxt) { Some(Int(i)) => i == 0, Some(F32(f)) => f == 0.0, Some(F64(f)) => f == 0.0, diff --git a/clippy_lints/src/if_not_else.rs b/clippy_lints/src/if_not_else.rs index e8afa69b537e..54e9538fcb99 100644 --- a/clippy_lints/src/if_not_else.rs +++ b/clippy_lints/src/if_not_else.rs @@ -60,10 +60,14 @@ impl LateLintPass<'_> for IfNotElse { ), // Don't lint on `… != 0`, as these are likely to be bit tests. // For example, `if foo & 0x0F00 != 0 { … } else { … }` is already in the "proper" order. - ExprKind::Binary(op, _, rhs) if op.node == BinOpKind::Ne && !is_zero_integer_const(cx, rhs) => ( - "unnecessary `!=` operation", - "change to `==` and swap the blocks of the `if`/`else`", - ), + ExprKind::Binary(op, _, rhs) + if op.node == BinOpKind::Ne && !is_zero_integer_const(cx, rhs, e.span.ctxt()) => + { + ( + "unnecessary `!=` operation", + "change to `==` and swap the blocks of the `if`/`else`", + ) + }, _ => return, }; diff --git a/clippy_lints/src/invalid_upcast_comparisons.rs b/clippy_lints/src/invalid_upcast_comparisons.rs index 1666e8e5ae32..885649074ab6 100644 --- a/clippy_lints/src/invalid_upcast_comparisons.rs +++ b/clippy_lints/src/invalid_upcast_comparisons.rs @@ -101,7 +101,7 @@ fn upcast_comparison_bounds_err<'tcx>( invert: bool, ) { if let Some((lb, ub)) = lhs_bounds - && let Some(norm_rhs_val) = ConstEvalCtxt::new(cx).eval_full_int(rhs) + && let Some(norm_rhs_val) = ConstEvalCtxt::new(cx).eval_full_int(rhs, span.ctxt()) { if rel == Rel::Eq || rel == Rel::Ne { if norm_rhs_val < lb || norm_rhs_val > ub { diff --git a/clippy_lints/src/manual_rem_euclid.rs b/clippy_lints/src/manual_rem_euclid.rs index 41e07e26bff0..1e91a429fe45 100644 --- a/clippy_lints/src/manual_rem_euclid.rs +++ b/clippy_lints/src/manual_rem_euclid.rs @@ -8,6 +8,7 @@ use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, Node, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; +use rustc_span::SyntaxContext; declare_clippy_lint! { /// ### What it does @@ -58,13 +59,13 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid { && add_lhs.span.ctxt() == ctxt && add_rhs.span.ctxt() == ctxt && !expr.span.in_external_macro(cx.sess().source_map()) - && let Some(const1) = check_for_unsigned_int_constant(cx, rem_rhs) - && let Some((const2, add_other)) = check_for_either_unsigned_int_constant(cx, add_lhs, add_rhs) + && let Some(const1) = check_for_unsigned_int_constant(cx, ctxt, rem_rhs) + && let Some((const2, add_other)) = check_for_either_unsigned_int_constant(cx, ctxt, add_lhs, add_rhs) && let ExprKind::Binary(rem2_op, rem2_lhs, rem2_rhs) = add_other.kind && rem2_op.node == BinOpKind::Rem && const1 == const2 && let Some(hir_id) = path_to_local(rem2_lhs) - && let Some(const3) = check_for_unsigned_int_constant(cx, rem2_rhs) + && let Some(const3) = check_for_unsigned_int_constant(cx, ctxt, rem2_rhs) // Also ensures the const is nonzero since zero can't be a divisor && const2 == const3 && rem2_lhs.span.ctxt() == ctxt @@ -103,16 +104,21 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid { // constant along with the other expression unchanged if so fn check_for_either_unsigned_int_constant<'a>( cx: &'a LateContext<'_>, + ctxt: SyntaxContext, left: &'a Expr<'_>, right: &'a Expr<'_>, ) -> Option<(u128, &'a Expr<'a>)> { - check_for_unsigned_int_constant(cx, left) + check_for_unsigned_int_constant(cx, ctxt, left) .map(|int_const| (int_const, right)) - .or_else(|| check_for_unsigned_int_constant(cx, right).map(|int_const| (int_const, left))) + .or_else(|| check_for_unsigned_int_constant(cx, ctxt, right).map(|int_const| (int_const, left))) } -fn check_for_unsigned_int_constant<'a>(cx: &'a LateContext<'_>, expr: &'a Expr<'_>) -> Option { - let int_const = ConstEvalCtxt::new(cx).eval_full_int(expr)?; +fn check_for_unsigned_int_constant<'a>( + cx: &'a LateContext<'_>, + ctxt: SyntaxContext, + expr: &'a Expr<'_>, +) -> Option { + let int_const = ConstEvalCtxt::new(cx).eval_full_int(expr, ctxt)?; match int_const { FullInt::S(s) => s.try_into().ok(), FullInt::U(u) => Some(u), diff --git a/clippy_lints/src/matches/manual_unwrap_or.rs b/clippy_lints/src/matches/manual_unwrap_or.rs index 8c3f52542d91..ac9e51890362 100644 --- a/clippy_lints/src/matches/manual_unwrap_or.rs +++ b/clippy_lints/src/matches/manual_unwrap_or.rs @@ -155,7 +155,7 @@ fn handle( && cx.typeck_results().expr_adjustments(body_some).is_empty() && let Some(or_body_snippet) = peel_blocks(body_none).span.get_source_text(cx) && let Some(indent) = indent_of(cx, expr.span) - && ConstEvalCtxt::new(cx).eval_simple(body_none).is_some() + && ConstEvalCtxt::new(cx).eval_local(body_none, expr.span.ctxt()).is_some() { let reindented_or_body = reindent_multiline(&or_body_snippet, true, Some(indent)); let mut app = Applicability::MachineApplicable; diff --git a/clippy_lints/src/methods/is_digit_ascii_radix.rs b/clippy_lints/src/methods/is_digit_ascii_radix.rs index 9c32e9ac539d..2aeb6f42d05c 100644 --- a/clippy_lints/src/methods/is_digit_ascii_radix.rs +++ b/clippy_lints/src/methods/is_digit_ascii_radix.rs @@ -18,7 +18,7 @@ pub(super) fn check<'tcx>( return; } - if let Some(radix_val) = ConstEvalCtxt::new(cx).eval_full_int(radix) { + if let Some(radix_val) = ConstEvalCtxt::new(cx).eval_full_int(radix, expr.span.ctxt()) { let (num, replacement) = match radix_val { FullInt::S(10) | FullInt::U(10) => (10, "is_ascii_digit"), FullInt::S(16) | FullInt::U(16) => (16, "is_ascii_hexdigit"), diff --git a/clippy_lints/src/methods/unnecessary_min_or_max.rs b/clippy_lints/src/methods/unnecessary_min_or_max.rs index b87d81b71026..130e4970a716 100644 --- a/clippy_lints/src/methods/unnecessary_min_or_max.rs +++ b/clippy_lints/src/methods/unnecessary_min_or_max.rs @@ -25,8 +25,9 @@ pub(super) fn check<'tcx>( && let Some(fn_name) = cx.tcx.get_diagnostic_name(id) && matches!(fn_name, sym::cmp_ord_min | sym::cmp_ord_max) { - if let Some((left, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(recv) - && let Some((right, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(arg) + let ctxt = expr.span.ctxt(); + if let Some((left, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(recv, ctxt) + && let Some((right, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(arg, ctxt) { let Some(ord) = Constant::partial_cmp(cx.tcx, typeck_results.expr_ty(recv), &left, &right) else { return; diff --git a/clippy_lints/src/minmax.rs b/clippy_lints/src/minmax.rs index 64eafc0ebccd..8f76e6c9048e 100644 --- a/clippy_lints/src/minmax.rs +++ b/clippy_lints/src/minmax.rs @@ -4,6 +4,7 @@ use clippy_utils::{is_trait_method, sym}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; +use rustc_span::SyntaxContext; use std::cmp::Ordering::{Equal, Greater, Less}; declare_clippy_lint! { @@ -68,8 +69,8 @@ fn min_max<'a, 'tcx>(cx: &LateContext<'tcx>, expr: &'a Expr<'a>) -> Option<(MinM .qpath_res(qpath, path.hir_id) .opt_def_id() .and_then(|def_id| match cx.tcx.get_diagnostic_name(def_id) { - Some(sym::cmp_min) => fetch_const(cx, None, args, MinMax::Min), - Some(sym::cmp_max) => fetch_const(cx, None, args, MinMax::Max), + Some(sym::cmp_min) => fetch_const(cx, expr.span.ctxt(), None, args, MinMax::Min), + Some(sym::cmp_max) => fetch_const(cx, expr.span.ctxt(), None, args, MinMax::Max), _ => None, }) } else { @@ -79,8 +80,8 @@ fn min_max<'a, 'tcx>(cx: &LateContext<'tcx>, expr: &'a Expr<'a>) -> Option<(MinM ExprKind::MethodCall(path, receiver, args @ [_], _) => { if cx.typeck_results().expr_ty(receiver).is_floating_point() || is_trait_method(cx, expr, sym::Ord) { match path.ident.name { - sym::max => fetch_const(cx, Some(receiver), args, MinMax::Max), - sym::min => fetch_const(cx, Some(receiver), args, MinMax::Min), + sym::max => fetch_const(cx, expr.span.ctxt(), Some(receiver), args, MinMax::Max), + sym::min => fetch_const(cx, expr.span.ctxt(), Some(receiver), args, MinMax::Min), _ => None, } } else { @@ -93,6 +94,7 @@ fn min_max<'a, 'tcx>(cx: &LateContext<'tcx>, expr: &'a Expr<'a>) -> Option<(MinM fn fetch_const<'a, 'tcx>( cx: &LateContext<'tcx>, + ctxt: SyntaxContext, receiver: Option<&'a Expr<'a>>, args: &'a [Expr<'a>], m: MinMax, @@ -104,7 +106,7 @@ fn fetch_const<'a, 'tcx>( return None; } let ecx = ConstEvalCtxt::new(cx); - match (ecx.eval_simple(first_arg), ecx.eval_simple(second_arg)) { + match (ecx.eval_local(first_arg, ctxt), ecx.eval_local(second_arg, ctxt)) { (Some(c), None) => Some((m, c, second_arg)), (None, Some(c)) => Some((m, c, first_arg)), // otherwise ignore diff --git a/clippy_lints/src/operators/arithmetic_side_effects.rs b/clippy_lints/src/operators/arithmetic_side_effects.rs index ea5b81aec31e..e062e55dad89 100644 --- a/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -190,7 +190,7 @@ impl ArithmeticSideEffects { lhs: &'tcx hir::Expr<'_>, rhs: &'tcx hir::Expr<'_>, ) { - if ConstEvalCtxt::new(cx).eval_simple(expr).is_some() { + if ConstEvalCtxt::new(cx).eval_local(expr, expr.span.ctxt()).is_some() { return; } if !matches!( @@ -283,7 +283,7 @@ impl ArithmeticSideEffects { let Some(arg) = args.first() else { return; }; - if ConstEvalCtxt::new(cx).eval_simple(receiver).is_some() { + if ConstEvalCtxt::new(cx).eval_local(receiver, expr.span.ctxt()).is_some() { return; } let instance_ty = cx.typeck_results().expr_ty_adjusted(receiver); diff --git a/clippy_lints/src/operators/erasing_op.rs b/clippy_lints/src/operators/erasing_op.rs index 8f5ee390f722..ae1a94a3e23f 100644 --- a/clippy_lints/src/operators/erasing_op.rs +++ b/clippy_lints/src/operators/erasing_op.rs @@ -39,7 +39,9 @@ fn check_op<'tcx>( other: &Expr<'tcx>, parent: &Expr<'tcx>, ) { - if ConstEvalCtxt::with_env(cx.tcx, cx.typing_env(), tck).eval_simple(op) == Some(Constant::Int(0)) { + if ConstEvalCtxt::with_env(cx.tcx, cx.typing_env(), tck).eval_local(op, parent.span.ctxt()) + == Some(Constant::Int(0)) + { if different_types(tck, other, parent) { return; } diff --git a/clippy_lints/src/operators/float_cmp.rs b/clippy_lints/src/operators/float_cmp.rs index ded161c8576a..03b2cf055d91 100644 --- a/clippy_lints/src/operators/float_cmp.rs +++ b/clippy_lints/src/operators/float_cmp.rs @@ -18,12 +18,13 @@ pub(crate) fn check<'tcx>( ) { if (op == BinOpKind::Eq || op == BinOpKind::Ne) && is_float(cx, left) { let ecx = ConstEvalCtxt::new(cx); - let left_is_local = match ecx.eval_with_source(left) { + let ctxt = expr.span.ctxt(); + let left_is_local = match ecx.eval_with_source(left, ctxt) { Some((c, s)) if !is_allowed(&c) => s.is_local(), Some(_) => return, None => true, }; - let right_is_local = match ecx.eval_with_source(right) { + let right_is_local = match ecx.eval_with_source(right, ctxt) { Some((c, s)) if !is_allowed(&c) => s.is_local(), Some(_) => return, None => true, diff --git a/clippy_lints/src/operators/identity_op.rs b/clippy_lints/src/operators/identity_op.rs index 3efbb8963587..43c62e1e131a 100644 --- a/clippy_lints/src/operators/identity_op.rs +++ b/clippy_lints/src/operators/identity_op.rs @@ -7,7 +7,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::{BinOpKind, Expr, ExprKind, Node, Path, QPath}; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::{Span, kw}; +use rustc_span::{Span, SyntaxContext, kw}; use super::IDENTITY_OP; @@ -41,42 +41,43 @@ pub(crate) fn check<'tcx>( (span, is_coerced) }; + let ctxt = expr.span.ctxt(); match op { BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => { - if is_redundant_op(cx, left, 0) { + if is_redundant_op(cx, left, 0, ctxt) { let paren = needs_parenthesis(cx, expr, right); span_ineffective_operation(cx, expr.span, peeled_right_span, paren, right_is_coerced_to_value); - } else if is_redundant_op(cx, right, 0) { + } else if is_redundant_op(cx, right, 0, ctxt) { let paren = needs_parenthesis(cx, expr, left); span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value); } }, BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => { - if is_redundant_op(cx, right, 0) { + if is_redundant_op(cx, right, 0, ctxt) { let paren = needs_parenthesis(cx, expr, left); span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value); } }, BinOpKind::Mul => { - if is_redundant_op(cx, left, 1) { + if is_redundant_op(cx, left, 1, ctxt) { let paren = needs_parenthesis(cx, expr, right); span_ineffective_operation(cx, expr.span, peeled_right_span, paren, right_is_coerced_to_value); - } else if is_redundant_op(cx, right, 1) { + } else if is_redundant_op(cx, right, 1, ctxt) { let paren = needs_parenthesis(cx, expr, left); span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value); } }, BinOpKind::Div => { - if is_redundant_op(cx, right, 1) { + if is_redundant_op(cx, right, 1, ctxt) { let paren = needs_parenthesis(cx, expr, left); span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value); } }, BinOpKind::BitAnd => { - if is_redundant_op(cx, left, -1) { + if is_redundant_op(cx, left, -1, ctxt) { let paren = needs_parenthesis(cx, expr, right); span_ineffective_operation(cx, expr.span, peeled_right_span, paren, right_is_coerced_to_value); - } else if is_redundant_op(cx, right, -1) { + } else if is_redundant_op(cx, right, -1, ctxt) { let paren = needs_parenthesis(cx, expr, left); span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value); } @@ -184,14 +185,17 @@ fn is_allowed<'tcx>( // This lint applies to integers and their references cx.typeck_results().expr_ty(left).peel_refs().is_integral() - && cx.typeck_results().expr_ty(right).peel_refs().is_integral() + && cx.typeck_results().expr_ty(right).peel_refs().is_integral() // `1 << 0` is a common pattern in bit manipulation code - && !(cmp == BinOpKind::Shl && is_zero_integer_const(cx, right) && integer_const(cx, left) == Some(1)) + && !(cmp == BinOpKind::Shl + && is_zero_integer_const(cx, right, expr.span.ctxt()) + && integer_const(cx, left, expr.span.ctxt()) == Some(1)) } fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span: Span, arg: Span) { let ecx = ConstEvalCtxt::new(cx); - if match (ecx.eval_full_int(left), ecx.eval_full_int(right)) { + let ctxt = span.ctxt(); + if match (ecx.eval_full_int(left, ctxt), ecx.eval_full_int(right, ctxt)) { (Some(FullInt::S(lv)), Some(FullInt::S(rv))) => lv.abs() < rv.abs(), (Some(FullInt::U(lv)), Some(FullInt::U(rv))) => lv < rv, _ => return, @@ -200,8 +204,8 @@ fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span } } -fn is_redundant_op(cx: &LateContext<'_>, e: &Expr<'_>, m: i8) -> bool { - if let Some(Constant::Int(v)) = ConstEvalCtxt::new(cx).eval_simple(e).map(Constant::peel_refs) { +fn is_redundant_op(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, ctxt: SyntaxContext) -> bool { + if let Some(Constant::Int(v)) = ConstEvalCtxt::new(cx).eval_local(e, ctxt).map(Constant::peel_refs) { let check = match *cx.typeck_results().expr_ty(e).peel_refs().kind() { ty::Int(ity) => unsext(cx.tcx, -1_i128, ity), ty::Uint(uty) => clip(cx.tcx, !0, uty), diff --git a/clippy_lints/src/operators/manual_is_multiple_of.rs b/clippy_lints/src/operators/manual_is_multiple_of.rs index 55bb78cfce5f..0b9bd4fb6d32 100644 --- a/clippy_lints/src/operators/manual_is_multiple_of.rs +++ b/clippy_lints/src/operators/manual_is_multiple_of.rs @@ -13,14 +13,14 @@ use super::MANUAL_IS_MULTIPLE_OF; pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, - expr: &Expr<'_>, + expr: &'tcx Expr<'tcx>, op: BinOpKind, lhs: &'tcx Expr<'tcx>, rhs: &'tcx Expr<'tcx>, msrv: Msrv, ) { if msrv.meets(cx, msrvs::UNSIGNED_IS_MULTIPLE_OF) - && let Some(operand) = uint_compare_to_zero(cx, op, lhs, rhs) + && let Some(operand) = uint_compare_to_zero(cx, expr, op, lhs, rhs) && let ExprKind::Binary(operand_op, operand_left, operand_right) = operand.kind && operand_op.node == BinOpKind::Rem && matches!( @@ -57,18 +57,19 @@ pub(super) fn check<'tcx>( // If we have a `x == 0`, `x != 0` or `x > 0` (or the reverted ones), return the non-zero operand fn uint_compare_to_zero<'tcx>( cx: &LateContext<'tcx>, + e: &'tcx Expr<'tcx>, op: BinOpKind, lhs: &'tcx Expr<'tcx>, rhs: &'tcx Expr<'tcx>, ) -> Option<&'tcx Expr<'tcx>> { let operand = if matches!(lhs.kind, ExprKind::Binary(..)) && matches!(op, BinOpKind::Eq | BinOpKind::Ne | BinOpKind::Gt) - && is_zero_integer_const(cx, rhs) + && is_zero_integer_const(cx, rhs, e.span.ctxt()) { lhs } else if matches!(rhs.kind, ExprKind::Binary(..)) && matches!(op, BinOpKind::Eq | BinOpKind::Ne | BinOpKind::Lt) - && is_zero_integer_const(cx, lhs) + && is_zero_integer_const(cx, lhs, e.span.ctxt()) { rhs } else { diff --git a/clippy_lints/src/operators/numeric_arithmetic.rs b/clippy_lints/src/operators/numeric_arithmetic.rs index 9b1b063c4737..622f328f369a 100644 --- a/clippy_lints/src/operators/numeric_arithmetic.rs +++ b/clippy_lints/src/operators/numeric_arithmetic.rs @@ -55,7 +55,7 @@ impl Context { return; } let ty = cx.typeck_results().expr_ty(arg); - if ConstEvalCtxt::new(cx).eval_simple(expr).is_none() && ty.is_floating_point() { + if ConstEvalCtxt::new(cx).eval_local(expr, expr.span.ctxt()).is_none() && ty.is_floating_point() { span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected"); self.expr_id = Some(expr.hir_id); } diff --git a/clippy_lints/src/zero_div_zero.rs b/clippy_lints/src/zero_div_zero.rs index 5eb207a0aedb..bb0cab3a3075 100644 --- a/clippy_lints/src/zero_div_zero.rs +++ b/clippy_lints/src/zero_div_zero.rs @@ -37,8 +37,9 @@ impl<'tcx> LateLintPass<'tcx> for ZeroDiv { // That's probably fine for this lint - it's pretty unlikely that someone would // do something like 0.0/(2.0 - 2.0), but it would be nice to warn on that case too. && let ecx = ConstEvalCtxt::new(cx) - && let Some(lhs_value) = ecx.eval_simple(left) - && let Some(rhs_value) = ecx.eval_simple(right) + && let ctxt = expr.span.ctxt() + && let Some(lhs_value) = ecx.eval_local(left, ctxt) + && let Some(rhs_value) = ecx.eval_local(right, ctxt) // FIXME(f16_f128): add these types when eq is available on all platforms && (Constant::F32(0.0) == lhs_value || Constant::F64(0.0) == lhs_value) && (Constant::F32(0.0) == rhs_value || Constant::F64(0.0) == rhs_value) diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index ecd88daa6b39..29f4988b4211 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -387,6 +387,7 @@ pub struct ConstEvalCtxt<'tcx> { typing_env: ty::TypingEnv<'tcx>, typeck: &'tcx TypeckResults<'tcx>, source: Cell, + ctxt: Cell, } impl<'tcx> ConstEvalCtxt<'tcx> { @@ -398,6 +399,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { typing_env: cx.typing_env(), typeck: cx.typeck_results(), source: Cell::new(ConstantSource::Local), + ctxt: Cell::new(SyntaxContext::root()), } } @@ -408,13 +410,15 @@ impl<'tcx> ConstEvalCtxt<'tcx> { typing_env, typeck, source: Cell::new(ConstantSource::Local), + ctxt: Cell::new(SyntaxContext::root()), } } /// Attempts to evaluate the expression and returns both the value and whether it's dependant on /// other items. - pub fn eval_with_source(&self, e: &Expr<'_>) -> Option<(Constant<'tcx>, ConstantSource)> { + pub fn eval_with_source(&self, e: &Expr<'_>, ctxt: SyntaxContext) -> Option<(Constant<'tcx>, ConstantSource)> { self.source.set(ConstantSource::Local); + self.ctxt.set(ctxt); self.expr(e).map(|c| (c, self.source.get())) } @@ -424,16 +428,16 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } /// Attempts to evaluate the expression without accessing other items. - pub fn eval_simple(&self, e: &Expr<'_>) -> Option> { - match self.eval_with_source(e) { + pub fn eval_local(&self, e: &Expr<'_>, ctxt: SyntaxContext) -> Option> { + match self.eval_with_source(e, ctxt) { Some((x, ConstantSource::Local)) => Some(x), _ => None, } } /// Attempts to evaluate the expression as an integer without accessing other items. - pub fn eval_full_int(&self, e: &Expr<'_>) -> Option { - match self.eval_with_source(e) { + pub fn eval_full_int(&self, e: &Expr<'_>, ctxt: SyntaxContext) -> Option { + match self.eval_with_source(e, ctxt) { Some((x, ConstantSource::Local)) => x.int_value(self.tcx, self.typeck.expr_ty(e)), _ => None, } @@ -455,6 +459,14 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } } + fn check_ctxt(&self, ctxt: SyntaxContext) { + self.source.set(if self.ctxt.get() != ctxt { + ConstantSource::Constant + } else { + self.source.get() + }); + } + 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 @@ -477,17 +489,18 @@ impl<'tcx> ConstEvalCtxt<'tcx> { /// Simple constant folding: Insert an expression, get a constant or none. fn expr(&self, e: &Expr<'_>) -> Option> { + self.check_ctxt(e.span.ctxt()); 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) => self.qpath(qpath, e.hir_id), - ExprKind::Block(block, _) => self.block(block), + ExprKind::Block(block, _) => { + self.check_ctxt(block.span.ctxt()); + self.block(block) + }, ExprKind::Lit(lit) => { - if is_direct_expn_of(e.span, sym::cfg).is_some() { - None - } else { - Some(lit_to_mir_constant(&lit.node, self.typeck.expr_ty_opt(e))) - } + self.check_ctxt(lit.span.ctxt()); + Some(lit_to_mir_constant(&lit.node, self.typeck.expr_ty_opt(e))) }, ExprKind::Array(vec) => self.multi(vec).map(Constant::Vec), ExprKind::Tup(tup) => self.multi(tup).map(Constant::Tuple), @@ -504,7 +517,10 @@ impl<'tcx> ConstEvalCtxt<'tcx> { UnOp::Deref => Some(if let Constant::Ref(r) = o { *r } else { o }), }), ExprKind::If(cond, then, ref otherwise) => self.ifthenelse(cond, then, *otherwise), - ExprKind::Binary(op, left, right) => self.binop(op.node, left, right), + ExprKind::Binary(op, left, right) => { + self.check_ctxt(e.span.ctxt()); + self.binop(op.node, left, right) + }, ExprKind::Call(callee, []) => { // We only handle a few const functions for now. if let ExprKind::Path(qpath) = &callee.kind @@ -525,6 +541,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { ExprKind::Index(arr, index, _) => self.index(arr, index), ExprKind::AddrOf(_, _, inner) => self.expr(inner).map(|r| Constant::Ref(Box::new(r))), ExprKind::Field(local_expr, ref field) => { + self.check_ctxt(field.span.ctxt()); let result = self.expr(local_expr); if let Some(Constant::Adt(constant)) = &self.expr(local_expr) && let ty::Adt(adt_def, _) = constant.ty().kind() @@ -958,8 +975,8 @@ fn field_of_struct<'tcx>( } /// If `expr` evaluates to an integer constant, return its value. -pub fn integer_const(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { - if let Some(Constant::Int(value)) = ConstEvalCtxt::new(cx).eval_simple(expr) { +pub fn integer_const(cx: &LateContext<'_>, expr: &Expr<'_>, ctxt: SyntaxContext) -> Option { + if let Some(Constant::Int(value)) = ConstEvalCtxt::new(cx).eval_local(expr, ctxt) { Some(value) } else { None @@ -968,6 +985,6 @@ pub fn integer_const(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { /// Check if `expr` evaluates to an integer constant of 0. #[inline] -pub fn is_zero_integer_const(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - integer_const(cx, expr) == Some(0) +pub fn is_zero_integer_const(cx: &LateContext<'_>, expr: &Expr<'_>, ctxt: SyntaxContext) -> bool { + integer_const(cx, expr, ctxt) == Some(0) } diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index bda28a663fb0..6f1bc28fbab8 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -454,7 +454,7 @@ pub fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) - return Some(VecInitKind::Default); } else if name.ident.name == sym::with_capacity { let arg = args.first()?; - return match ConstEvalCtxt::new(cx).eval_simple(arg) { + return match ConstEvalCtxt::new(cx).eval_local(arg, expr.span.ctxt()) { Some(Constant::Int(num)) => Some(VecInitKind::WithConstCapacity(num)), _ => Some(VecInitKind::WithExprCapacity(arg.hir_id)), }; diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index b79e15cd7170..a6d821c99c1b 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -290,8 +290,10 @@ impl HirEqInterExpr<'_, '_, '_> { if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results && typeck_lhs.expr_ty(left) == typeck_rhs.expr_ty(right) && let (Some(l), Some(r)) = ( - ConstEvalCtxt::with_env(self.inner.cx.tcx, self.inner.cx.typing_env(), typeck_lhs).eval_simple(left), - ConstEvalCtxt::with_env(self.inner.cx.tcx, self.inner.cx.typing_env(), typeck_rhs).eval_simple(right), + ConstEvalCtxt::with_env(self.inner.cx.tcx, self.inner.cx.typing_env(), typeck_lhs) + .eval_local(left, self.left_ctxt), + ConstEvalCtxt::with_env(self.inner.cx.tcx, self.inner.cx.typing_env(), typeck_rhs) + .eval_local(right, self.right_ctxt), ) && l == r { @@ -842,7 +844,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { #[expect(clippy::too_many_lines)] pub fn hash_expr(&mut self, e: &Expr<'_>) { let simple_const = self.maybe_typeck_results.and_then(|typeck_results| { - ConstEvalCtxt::with_env(self.cx.tcx, self.cx.typing_env(), typeck_results).eval_simple(e) + ConstEvalCtxt::with_env(self.cx.tcx, self.cx.typing_env(), typeck_results).eval_local(e, e.span.ctxt()) }); // const hashing may result in the same hash as some unrelated node, so add a sort of diff --git a/tests/ui/assertions_on_constants.rs b/tests/ui/assertions_on_constants.rs index b613892f206a..f467d4966aef 100644 --- a/tests/ui/assertions_on_constants.rs +++ b/tests/ui/assertions_on_constants.rs @@ -43,6 +43,7 @@ fn main() { assert_const!(-1); assert!(cfg!(feature = "hey") || cfg!(not(feature = "asdf"))); + //~^ assertions_on_constants let flag: bool = cfg!(not(feature = "asdf")); assert!(flag); diff --git a/tests/ui/assertions_on_constants.stderr b/tests/ui/assertions_on_constants.stderr index 9aed1405fa04..a996c41b6942 100644 --- a/tests/ui/assertions_on_constants.stderr +++ b/tests/ui/assertions_on_constants.stderr @@ -72,8 +72,16 @@ LL | debug_assert!(true); | = help: remove the assertion +error: this assertion has a constant value + --> tests/ui/assertions_on_constants.rs:45:5 + | +LL | assert!(cfg!(feature = "hey") || cfg!(not(feature = "asdf"))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider moving this into a const block: `const { assert!(..) }` + error: this assertion is always `true` - --> tests/ui/assertions_on_constants.rs:53:19 + --> tests/ui/assertions_on_constants.rs:54:19 | LL | const _: () = assert!(true); | ^^^^^^^^^^^^^ @@ -81,7 +89,7 @@ LL | const _: () = assert!(true); = help: remove the assertion error: this assertion is always `true` - --> tests/ui/assertions_on_constants.rs:56:5 + --> tests/ui/assertions_on_constants.rs:57:5 | LL | assert!(8 == (7 + 1)); | ^^^^^^^^^^^^^^^^^^^^^ @@ -89,7 +97,7 @@ LL | assert!(8 == (7 + 1)); = help: remove the assertion error: this assertion is always `true` - --> tests/ui/assertions_on_constants.rs:67:5 + --> tests/ui/assertions_on_constants.rs:68:5 | LL | assert!(true); | ^^^^^^^^^^^^^ @@ -97,7 +105,7 @@ LL | assert!(true); = help: remove the assertion error: this assertion is always `true` - --> tests/ui/assertions_on_constants.rs:70:5 + --> tests/ui/assertions_on_constants.rs:71:5 | LL | assert!(8 == (7 + 1)); | ^^^^^^^^^^^^^^^^^^^^^ @@ -105,7 +113,7 @@ LL | assert!(8 == (7 + 1)); = help: remove the assertion error: this assertion has a constant value - --> tests/ui/assertions_on_constants.rs:78:5 + --> tests/ui/assertions_on_constants.rs:79:5 | LL | assert!(C); | ^^^^^^^^^^ @@ -113,7 +121,7 @@ LL | assert!(C); = help: consider moving this to an anonymous constant: `const _: () = { assert!(..); }` error: this assertion has a constant value - --> tests/ui/assertions_on_constants.rs:89:5 + --> tests/ui/assertions_on_constants.rs:90:5 | LL | assert!(C); | ^^^^^^^^^^ @@ -121,12 +129,12 @@ LL | assert!(C); = help: consider moving this into a const block: `const { assert!(..) }` error: this assertion has a constant value - --> tests/ui/assertions_on_constants.rs:95:5 + --> tests/ui/assertions_on_constants.rs:96:5 | LL | assert!(C); | ^^^^^^^^^^ | = help: consider moving this to an anonymous constant: `const _: () = { assert!(..); }` -error: aborting due to 16 previous errors +error: aborting due to 17 previous errors From a7bd285a6a3de46ec32c355865b449a56861bfd9 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Fri, 26 Sep 2025 14:21:08 +0200 Subject: [PATCH 098/226] cmse: use BackendRepr to determine valid 64-bit return types --- .../src/hir_ty_lowering/cmse.rs | 48 ++++++++----------- 1 file changed, 19 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs index 0a41659ec668..378771c05ee3 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs @@ -1,8 +1,8 @@ -use rustc_abi::ExternAbi; +use rustc_abi::{BackendRepr, ExternAbi, Float, Integer, Primitive, Scalar}; use rustc_errors::{DiagCtxtHandle, E0781, struct_span_code_err}; use rustc_hir::{self as hir, HirId}; use rustc_middle::bug; -use rustc_middle::ty::layout::LayoutError; +use rustc_middle::ty::layout::{LayoutError, TyAndLayout}; use rustc_middle::ty::{self, TyCtxt}; use crate::errors; @@ -164,44 +164,34 @@ fn is_valid_cmse_output<'tcx>( ) -> Result> { // this type is only used for layout computation, which does not rely on regions let fn_sig = tcx.instantiate_bound_regions_with_erased(fn_sig); + let fn_sig = tcx.erase_and_anonymize_regions(fn_sig); let typing_env = ty::TypingEnv::fully_monomorphized(); + let layout = tcx.layout_of(typing_env.as_query_input(fn_sig.output()))?; - let mut ret_ty = fn_sig.output(); - let layout = tcx.layout_of(typing_env.as_query_input(ret_ty))?; + Ok(is_valid_cmse_output_layout(layout)) +} + +/// Returns whether the output will fit into the available registers +fn is_valid_cmse_output_layout<'tcx>(layout: TyAndLayout<'tcx>) -> bool { let size = layout.layout.size().bytes(); if size <= 4 { - return Ok(true); + return true; } else if size > 8 { - return Ok(false); + return false; } - // next we need to peel any repr(transparent) layers off - 'outer: loop { - let ty::Adt(adt_def, args) = ret_ty.kind() else { - break; - }; + // Accept scalar 64-bit types. + let BackendRepr::Scalar(scalar) = layout.layout.backend_repr else { + return false; + }; - if !adt_def.repr().transparent() { - break; - } + let Scalar::Initialized { value, .. } = scalar else { + return false; + }; - // the first field with non-trivial size and alignment must be the data - for variant_def in adt_def.variants() { - for field_def in variant_def.fields.iter() { - let ty = field_def.ty(tcx, args); - let layout = tcx.layout_of(typing_env.as_query_input(ty))?; - - if !layout.layout.is_1zst() { - ret_ty = ty; - continue 'outer; - } - } - } - } - - Ok(ret_ty == tcx.types.i64 || ret_ty == tcx.types.u64 || ret_ty == tcx.types.f64) + matches!(value, Primitive::Int(Integer::I64, _) | Primitive::Float(Float::F64)) } fn should_emit_generic_error<'tcx>(abi: ExternAbi, layout_err: &'tcx LayoutError<'tcx>) -> bool { From 4735c2184c72b8b3a8bad642407a74d4b19838fb Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Wed, 2 Jul 2025 17:18:57 +0800 Subject: [PATCH 099/226] Fix diagnostics str::replace comma to bar --- .../rustc_parse/src/parser/diagnostics.rs | 34 +++++++++---------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index a28af7833c38..5d6da6093120 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -2947,26 +2947,24 @@ impl<'a> Parser<'a> { } let seq_span = lo.to(self.prev_token.span); let mut err = self.dcx().struct_span_err(comma_span, "unexpected `,` in pattern"); - if let Ok(seq_snippet) = self.span_to_snippet(seq_span) { - err.multipart_suggestion( - format!( - "try adding parentheses to match on a tuple{}", - if let CommaRecoveryMode::LikelyTuple = rt { "" } else { "..." }, - ), - vec![ - (seq_span.shrink_to_lo(), "(".to_string()), - (seq_span.shrink_to_hi(), ")".to_string()), - ], + err.multipart_suggestion( + format!( + "try adding parentheses to match on a tuple{}", + if let CommaRecoveryMode::LikelyTuple = rt { "" } else { "..." }, + ), + vec![ + (seq_span.shrink_to_lo(), "(".to_string()), + (seq_span.shrink_to_hi(), ")".to_string()), + ], + Applicability::MachineApplicable, + ); + if let CommaRecoveryMode::EitherTupleOrPipe = rt { + err.span_suggestion( + comma_span, + "...or a vertical bar to match on alternative", + " |", Applicability::MachineApplicable, ); - if let CommaRecoveryMode::EitherTupleOrPipe = rt { - err.span_suggestion( - seq_span, - "...or a vertical bar to match on multiple alternatives", - seq_snippet.replace(',', " |"), - Applicability::MachineApplicable, - ); - } } Err(err) } From a08228d284a91e5b2514035b723e82deb9179985 Mon Sep 17 00:00:00 2001 From: Karol Zwolak Date: Sat, 27 Sep 2025 22:57:13 +0200 Subject: [PATCH 100/226] bless tests --- ...ssue-48492-tuple-destructure-missing-parens.stderr | 4 ++-- .../feature-gates/feature-gate-never_patterns.stderr | 2 +- tests/ui/parser/match-arm-without-body.rs | 4 ++-- tests/ui/parser/match-arm-without-body.stderr | 11 ++++------- 4 files changed, 9 insertions(+), 12 deletions(-) diff --git a/tests/ui/did_you_mean/issue-48492-tuple-destructure-missing-parens.stderr b/tests/ui/did_you_mean/issue-48492-tuple-destructure-missing-parens.stderr index c74cb89f85cb..dc875ad27ea6 100644 --- a/tests/ui/did_you_mean/issue-48492-tuple-destructure-missing-parens.stderr +++ b/tests/ui/did_you_mean/issue-48492-tuple-destructure-missing-parens.stderr @@ -32,10 +32,10 @@ help: try adding parentheses to match on a tuple... | LL | (Nucleotide::Adenine, Nucleotide::Cytosine, _) => true | + + -help: ...or a vertical bar to match on multiple alternatives +help: ...or a vertical bar to match on alternative | LL - Nucleotide::Adenine, Nucleotide::Cytosine, _ => true -LL + Nucleotide::Adenine | Nucleotide::Cytosine | _ => true +LL + Nucleotide::Adenine | Nucleotide::Cytosine, _ => true | error: unexpected `,` in pattern diff --git a/tests/ui/feature-gates/feature-gate-never_patterns.stderr b/tests/ui/feature-gates/feature-gate-never_patterns.stderr index 473e263c7965..5e810249b906 100644 --- a/tests/ui/feature-gates/feature-gate-never_patterns.stderr +++ b/tests/ui/feature-gates/feature-gate-never_patterns.stderr @@ -8,7 +8,7 @@ help: try adding parentheses to match on a tuple... | LL | (Some(_),) | + + -help: ...or a vertical bar to match on multiple alternatives +help: ...or a vertical bar to match on alternative | LL - Some(_), LL + Some(_) | diff --git a/tests/ui/parser/match-arm-without-body.rs b/tests/ui/parser/match-arm-without-body.rs index 4723abff8b6a..7fe5b6d25398 100644 --- a/tests/ui/parser/match-arm-without-body.rs +++ b/tests/ui/parser/match-arm-without-body.rs @@ -17,13 +17,13 @@ fn main() { Some(_), //~^ ERROR unexpected `,` in pattern //~| HELP try adding parentheses to match on a tuple - //~| HELP or a vertical bar to match on multiple alternatives + //~| HELP or a vertical bar to match on alternative } match Some(false) { Some(_), //~^ ERROR unexpected `,` in pattern //~| HELP try adding parentheses to match on a tuple - //~| HELP or a vertical bar to match on multiple alternatives + //~| HELP or a vertical bar to match on alternative _ => {} } match Some(false) { diff --git a/tests/ui/parser/match-arm-without-body.stderr b/tests/ui/parser/match-arm-without-body.stderr index a65875b787a3..85ff7db8d4aa 100644 --- a/tests/ui/parser/match-arm-without-body.stderr +++ b/tests/ui/parser/match-arm-without-body.stderr @@ -16,7 +16,7 @@ help: try adding parentheses to match on a tuple... | LL | (Some(_),) | + + -help: ...or a vertical bar to match on multiple alternatives +help: ...or a vertical bar to match on alternative | LL - Some(_), LL + Some(_) | @@ -36,13 +36,10 @@ LL | LL | LL ~ _) => {} | -help: ...or a vertical bar to match on multiple alternatives +help: ...or a vertical bar to match on alternative | -LL ~ Some(_) | -LL + -LL + -LL + -LL ~ _ => {} +LL - Some(_), +LL + Some(_) | | error: expected one of `.`, `=>`, `?`, or an operator, found reserved identifier `_` From 934ad740270944800d03f6290bcc131a8697e59f Mon Sep 17 00:00:00 2001 From: Karol Zwolak Date: Wed, 1 Oct 2025 22:40:23 +0200 Subject: [PATCH 101/226] regression test --- ...place-intended-bar-not-all-in-pattern.fixed | 18 ++++++++++++++++++ ...-replace-intended-bar-not-all-in-pattern.rs | 18 ++++++++++++++++++ ...lace-intended-bar-not-all-in-pattern.stderr | 18 ++++++++++++++++++ 3 files changed, 54 insertions(+) create mode 100644 tests/ui/suggestions/only-replace-intended-bar-not-all-in-pattern.fixed create mode 100644 tests/ui/suggestions/only-replace-intended-bar-not-all-in-pattern.rs create mode 100644 tests/ui/suggestions/only-replace-intended-bar-not-all-in-pattern.stderr diff --git a/tests/ui/suggestions/only-replace-intended-bar-not-all-in-pattern.fixed b/tests/ui/suggestions/only-replace-intended-bar-not-all-in-pattern.fixed new file mode 100644 index 000000000000..5abc1edd381b --- /dev/null +++ b/tests/ui/suggestions/only-replace-intended-bar-not-all-in-pattern.fixed @@ -0,0 +1,18 @@ +//@ run-rustfix + +// Regression test for issue #143330. +// Ensure we suggest to replace only the intended bar with a comma, not all bars in the pattern. + +fn main() { + struct Foo { x: i32, ch: char } + let pos = Foo { x: 2, ch: 'x' }; + match pos { + // All commas here were replaced with bars. + // Foo { x: 2 | ch: ' |' } | Foo { x: 3 | ch: '@' } => (), + (Foo { x: 2, ch: ',' } | Foo { x: 3, ch: '@' }) => (), + //~^ ERROR unexpected `,` in pattern + //~| HELP try adding parentheses to match on a tuple... + //~| HELP ...or a vertical bar to match on alternative + _ => todo!(), + } +} diff --git a/tests/ui/suggestions/only-replace-intended-bar-not-all-in-pattern.rs b/tests/ui/suggestions/only-replace-intended-bar-not-all-in-pattern.rs new file mode 100644 index 000000000000..5bd267c3bc40 --- /dev/null +++ b/tests/ui/suggestions/only-replace-intended-bar-not-all-in-pattern.rs @@ -0,0 +1,18 @@ +//@ run-rustfix + +// Regression test for issue #143330. +// Ensure we suggest to replace only the intended bar with a comma, not all bars in the pattern. + +fn main() { + struct Foo { x: i32, ch: char } + let pos = Foo { x: 2, ch: 'x' }; + match pos { + // All commas here were replaced with bars. + // Foo { x: 2 | ch: ' |' } | Foo { x: 3 | ch: '@' } => (), + Foo { x: 2, ch: ',' }, Foo { x: 3, ch: '@' } => (), + //~^ ERROR unexpected `,` in pattern + //~| HELP try adding parentheses to match on a tuple... + //~| HELP ...or a vertical bar to match on alternative + _ => todo!(), + } +} diff --git a/tests/ui/suggestions/only-replace-intended-bar-not-all-in-pattern.stderr b/tests/ui/suggestions/only-replace-intended-bar-not-all-in-pattern.stderr new file mode 100644 index 000000000000..db0bb8c50e86 --- /dev/null +++ b/tests/ui/suggestions/only-replace-intended-bar-not-all-in-pattern.stderr @@ -0,0 +1,18 @@ +error: unexpected `,` in pattern + --> $DIR/only-replace-intended-bar-not-all-in-pattern.rs:12:30 + | +LL | Foo { x: 2, ch: ',' }, Foo { x: 3, ch: '@' } => (), + | ^ + | +help: try adding parentheses to match on a tuple... + | +LL | (Foo { x: 2, ch: ',' }, Foo { x: 3, ch: '@' }) => (), + | + + +help: ...or a vertical bar to match on alternative + | +LL - Foo { x: 2, ch: ',' }, Foo { x: 3, ch: '@' } => (), +LL + Foo { x: 2, ch: ',' } | Foo { x: 3, ch: '@' } => (), + | + +error: aborting due to 1 previous error + From 951d35eeb6aa3bd0ba6fd399290cbdeceec5a97a Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sun, 21 Sep 2025 17:30:57 +0200 Subject: [PATCH 102/226] Do not trigger `inefficient_to_string` after Rust 1.82 Starting with Rust version 1.82.0, the compiler generates similar code with and without the `with_ref` cfg: ```rust fn f(x: impl IntoIterator) { for y in x { println!("{y}"); } } fn main() { #[cfg(with_ref)] let a = ["foo", "bar"].iter().map(|&s| s.to_string()); #[cfg(not(with_ref))] let a = ["foo", "bar"].iter().map(|s| s.to_string()); f(a); } ``` The generated code is strictly identical with `-O`, and identical modulo some minor reordering without. --- book/src/lint_configuration.md | 1 + clippy_config/src/conf.rs | 1 + clippy_lints/src/methods/inefficient_to_string.rs | 4 ++++ clippy_lints/src/methods/mod.rs | 11 +++++++---- clippy_utils/src/msrvs.rs | 2 +- tests/ui/inefficient_to_string.fixed | 8 ++++++++ tests/ui/inefficient_to_string.rs | 8 ++++++++ tests/ui/inefficient_to_string.stderr | 12 ++++++------ 8 files changed, 36 insertions(+), 11 deletions(-) diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index c2d080cd96a1..5953e5048e4a 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -845,6 +845,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`from_over_into`](https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into) * [`if_then_some_else_none`](https://rust-lang.github.io/rust-clippy/master/index.html#if_then_some_else_none) * [`index_refutable_slice`](https://rust-lang.github.io/rust-clippy/master/index.html#index_refutable_slice) +* [`inefficient_to_string`](https://rust-lang.github.io/rust-clippy/master/index.html#inefficient_to_string) * [`io_other_error`](https://rust-lang.github.io/rust-clippy/master/index.html#io_other_error) * [`iter_kv_map`](https://rust-lang.github.io/rust-clippy/master/index.html#iter_kv_map) * [`legacy_numeric_constants`](https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants) diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 2f28f6175ad8..6e8752c5f932 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -741,6 +741,7 @@ define_Conf! { from_over_into, if_then_some_else_none, index_refutable_slice, + inefficient_to_string, io_other_error, iter_kv_map, legacy_numeric_constants, diff --git a/clippy_lints/src/methods/inefficient_to_string.rs b/clippy_lints/src/methods/inefficient_to_string.rs index 47195fdd65f5..ab21515f47f7 100644 --- a/clippy_lints/src/methods/inefficient_to_string.rs +++ b/clippy_lints/src/methods/inefficient_to_string.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{is_type_lang_item, peel_and_count_ty_refs}; use rustc_errors::Applicability; @@ -16,6 +17,7 @@ pub fn check( method_name: Symbol, receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>], + msrv: Msrv, ) { if args.is_empty() && method_name == sym::to_string @@ -26,6 +28,8 @@ pub fn check( && let self_ty = args.type_at(0) && let (deref_self_ty, deref_count, _) = peel_and_count_ty_refs(self_ty) && deref_count >= 1 + // Since Rust 1.82, the specialized `ToString` is properly called + && !msrv.meets(cx, msrvs::SPECIALIZED_TO_STRING_FOR_REFS) && specializes_tostring(cx, deref_self_ty) { span_lint_and_then( diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 8679689c8ad4..5448a266ab7d 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -477,6 +477,9 @@ declare_clippy_lint! { /// ### What it does /// Checks for usage of `ok().expect(..)`. /// + /// Note: This lint only triggers for code marked compatible + /// with versions of the compiler older than Rust 1.82.0. + /// /// ### Why is this bad? /// Because you usually call `expect()` on the `Result` /// directly to get a better error message. @@ -1080,9 +1083,9 @@ declare_clippy_lint! { /// `T` implements `ToString` directly (like `&&str` or `&&String`). /// /// ### Why is this bad? - /// This bypasses the specialized implementation of - /// `ToString` and instead goes through the more expensive string formatting - /// facilities. + /// In versions of the compiler before Rust 1.82.0, this bypasses the specialized + /// implementation of`ToString` and instead goes through the more expensive string + /// formatting facilities. /// /// ### Example /// ```no_run @@ -4868,7 +4871,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ); clone_on_copy::check(cx, expr, method_call.ident.name, receiver, args); clone_on_ref_ptr::check(cx, expr, method_call.ident.name, receiver, args); - inefficient_to_string::check(cx, expr, method_call.ident.name, receiver, args); + inefficient_to_string::check(cx, expr, method_call.ident.name, receiver, args, self.msrv); single_char_add_str::check(cx, expr, receiver, args); into_iter_on_ref::check(cx, expr, method_span, method_call.ident.name, receiver); unnecessary_to_owned::check(cx, expr, method_call.ident.name, receiver, args, self.msrv); diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 6e07ed9ffcc4..43e9a8f50519 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -28,7 +28,7 @@ msrv_aliases! { 1,85,0 { UINT_FLOAT_MIDPOINT, CONST_SIZE_OF_VAL } 1,84,0 { CONST_OPTION_AS_SLICE, MANUAL_DANGLING_PTR } 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,82,0 { IS_NONE_OR, REPEAT_N, RAW_REF_OP, SPECIALIZED_TO_STRING_FOR_REFS } 1,81,0 { LINT_REASONS_STABILIZATION, ERROR_IN_CORE, EXPLICIT_SELF_TYPE_ELISION, DURATION_ABS_DIFF } 1,80,0 { BOX_INTO_ITER, LAZY_CELL } 1,77,0 { C_STR_LITERALS } diff --git a/tests/ui/inefficient_to_string.fixed b/tests/ui/inefficient_to_string.fixed index a0d34e58a925..29cf6de6ae5e 100644 --- a/tests/ui/inefficient_to_string.fixed +++ b/tests/ui/inefficient_to_string.fixed @@ -2,6 +2,7 @@ use std::borrow::Cow; +#[clippy::msrv = "1.81"] fn main() { let rstr: &str = "hello"; let rrstr: &&str = &rstr; @@ -34,3 +35,10 @@ fn main() { let _: String = (**rrrcow).to_string(); //~^ inefficient_to_string } + +#[clippy::msrv = "1.82"] +fn sufficient_msrv() { + let rstr: &str = "hello"; + let rrstr: &&str = &rstr; + let _: String = rrstr.to_string(); +} diff --git a/tests/ui/inefficient_to_string.rs b/tests/ui/inefficient_to_string.rs index cbe90d4a125b..724955c60f79 100644 --- a/tests/ui/inefficient_to_string.rs +++ b/tests/ui/inefficient_to_string.rs @@ -2,6 +2,7 @@ use std::borrow::Cow; +#[clippy::msrv = "1.81"] fn main() { let rstr: &str = "hello"; let rrstr: &&str = &rstr; @@ -34,3 +35,10 @@ fn main() { let _: String = rrrcow.to_string(); //~^ inefficient_to_string } + +#[clippy::msrv = "1.82"] +fn sufficient_msrv() { + let rstr: &str = "hello"; + let rrstr: &&str = &rstr; + let _: String = rrstr.to_string(); +} diff --git a/tests/ui/inefficient_to_string.stderr b/tests/ui/inefficient_to_string.stderr index 8593c0addc5f..ea3dd7e0ae2f 100644 --- a/tests/ui/inefficient_to_string.stderr +++ b/tests/ui/inefficient_to_string.stderr @@ -1,5 +1,5 @@ error: calling `to_string` on `&&str` - --> tests/ui/inefficient_to_string.rs:10:21 + --> tests/ui/inefficient_to_string.rs:11:21 | LL | let _: String = rrstr.to_string(); | ^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(*rrstr).to_string()` @@ -12,7 +12,7 @@ LL | #![deny(clippy::inefficient_to_string)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: calling `to_string` on `&&&str` - --> tests/ui/inefficient_to_string.rs:12:21 + --> tests/ui/inefficient_to_string.rs:13:21 | LL | let _: String = rrrstr.to_string(); | ^^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(**rrrstr).to_string()` @@ -20,7 +20,7 @@ LL | let _: String = rrrstr.to_string(); = help: `&&str` implements `ToString` through a slower blanket impl, but `str` has a fast specialization of `ToString` error: calling `to_string` on `&&std::string::String` - --> tests/ui/inefficient_to_string.rs:21:21 + --> tests/ui/inefficient_to_string.rs:22:21 | LL | let _: String = rrstring.to_string(); | ^^^^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(*rrstring).to_string()` @@ -28,7 +28,7 @@ LL | let _: String = rrstring.to_string(); = help: `&std::string::String` implements `ToString` through a slower blanket impl, but `std::string::String` has a fast specialization of `ToString` error: calling `to_string` on `&&&std::string::String` - --> tests/ui/inefficient_to_string.rs:23:21 + --> tests/ui/inefficient_to_string.rs:24:21 | LL | let _: String = rrrstring.to_string(); | ^^^^^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(**rrrstring).to_string()` @@ -36,7 +36,7 @@ LL | let _: String = rrrstring.to_string(); = help: `&&std::string::String` implements `ToString` through a slower blanket impl, but `std::string::String` has a fast specialization of `ToString` error: calling `to_string` on `&&std::borrow::Cow<'_, str>` - --> tests/ui/inefficient_to_string.rs:32:21 + --> tests/ui/inefficient_to_string.rs:33:21 | LL | let _: String = rrcow.to_string(); | ^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(*rrcow).to_string()` @@ -44,7 +44,7 @@ LL | let _: String = rrcow.to_string(); = help: `&std::borrow::Cow<'_, str>` implements `ToString` through a slower blanket impl, but `std::borrow::Cow<'_, str>` has a fast specialization of `ToString` error: calling `to_string` on `&&&std::borrow::Cow<'_, str>` - --> tests/ui/inefficient_to_string.rs:34:21 + --> tests/ui/inefficient_to_string.rs:35:21 | LL | let _: String = rrrcow.to_string(); | ^^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(**rrrcow).to_string()` From 08b4641f50f6f8fa3aa222fa606a0c1a2482b72d Mon Sep 17 00:00:00 2001 From: Jeremy Smart Date: Wed, 20 Aug 2025 18:20:53 -0400 Subject: [PATCH 103/226] add CloneFromCell and Cell::get_cloned --- library/alloc/src/lib.rs | 1 + library/alloc/src/rc.rs | 10 ++++- library/alloc/src/sync.rs | 9 ++++ library/core/src/cell.rs | 92 ++++++++++++++++++++++++++++++++++++++- library/core/src/tuple.rs | 10 +++++ 5 files changed, 119 insertions(+), 3 deletions(-) diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index cba1ce40f75d..e7cfd01e654e 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -103,6 +103,7 @@ #![feature(bstr)] #![feature(bstr_internals)] #![feature(cast_maybe_uninit)] +#![feature(cell_get_cloned)] #![feature(char_internals)] #![feature(char_max_len)] #![feature(clone_to_uninit)] diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index aed3357afbf4..783b6147e956 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -242,7 +242,7 @@ #![stable(feature = "rust1", since = "1.0.0")] use core::any::Any; -use core::cell::Cell; +use core::cell::{Cell, CloneFromCell}; #[cfg(not(no_global_oom_handling))] use core::clone::CloneToUninit; use core::clone::UseCloned; @@ -338,6 +338,10 @@ impl, U: ?Sized, A: Allocator> CoerceUnsized> for #[unstable(feature = "dispatch_from_dyn", issue = "none")] impl, U: ?Sized> DispatchFromDyn> for Rc {} +// SAFETY: `Rc::clone` doesn't access any `Cell`s which could contain the `Rc` being cloned. +#[unstable(feature = "cell_get_cloned", issue = "145329")] +unsafe impl CloneFromCell for Rc {} + impl Rc { #[inline] unsafe fn from_inner(ptr: NonNull>) -> Self { @@ -3011,6 +3015,10 @@ impl, U: ?Sized, A: Allocator> CoerceUnsized> f #[unstable(feature = "dispatch_from_dyn", issue = "none")] impl, U: ?Sized> DispatchFromDyn> for Weak {} +// SAFETY: `Weak::clone` doesn't access any `Cell`s which could contain the `Weak` being cloned. +#[unstable(feature = "cell_get_cloned", issue = "145329")] +unsafe impl CloneFromCell for Weak {} + impl Weak { /// Constructs a new `Weak`, without allocating any memory. /// Calling [`upgrade`] on the return value always gives [`None`]. diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index a466b74944c5..eddc9047f21b 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -9,6 +9,7 @@ //! `#[cfg(target_has_atomic = "ptr")]`. use core::any::Any; +use core::cell::CloneFromCell; #[cfg(not(no_global_oom_handling))] use core::clone::CloneToUninit; use core::clone::UseCloned; @@ -281,6 +282,10 @@ impl, U: ?Sized, A: Allocator> CoerceUnsized> fo #[unstable(feature = "dispatch_from_dyn", issue = "none")] impl, U: ?Sized> DispatchFromDyn> for Arc {} +// SAFETY: `Arc::clone` doesn't access any `Cell`s which could contain the `Arc` being cloned. +#[unstable(feature = "cell_get_cloned", issue = "145329")] +unsafe impl CloneFromCell for Arc {} + impl Arc { unsafe fn from_inner(ptr: NonNull>) -> Self { unsafe { Self::from_inner_in(ptr, Global) } @@ -356,6 +361,10 @@ impl, U: ?Sized, A: Allocator> CoerceUnsized> f #[unstable(feature = "dispatch_from_dyn", issue = "none")] impl, U: ?Sized> DispatchFromDyn> for Weak {} +// SAFETY: `Weak::clone` doesn't access any `Cell`s which could contain the `Weak` being cloned. +#[unstable(feature = "cell_get_cloned", issue = "145329")] +unsafe impl CloneFromCell for Weak {} + #[stable(feature = "arc_weak", since = "1.4.0")] impl fmt::Debug for Weak { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index 7d4a66640b10..a991bbf46b1f 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -253,11 +253,12 @@ use crate::cmp::Ordering; use crate::fmt::{self, Debug, Display}; use crate::marker::{PhantomData, Unsize}; -use crate::mem; -use crate::ops::{CoerceUnsized, Deref, DerefMut, DerefPure, DispatchFromDyn}; +use crate::mem::{self, ManuallyDrop}; +use crate::ops::{self, CoerceUnsized, Deref, DerefMut, DerefPure, DispatchFromDyn}; use crate::panic::const_panic; use crate::pin::PinCoerceUnsized; use crate::ptr::{self, NonNull}; +use crate::range; mod lazy; mod once; @@ -713,6 +714,93 @@ impl Cell<[T; N]> { } } +/// Types for which cloning `Cell` is sound. +/// +/// # Safety +/// +/// Implementing this trait for a type is sound if and only if the following code is sound for T = +/// that type. +/// +/// ``` +/// #![feature(cell_get_cloned)] +/// # use std::cell::{CloneFromCell, Cell}; +/// fn clone_from_cell(cell: &Cell) -> T { +/// unsafe { T::clone(&*cell.as_ptr()) } +/// } +/// ``` +/// +/// Importantly, you can't just implement `CloneFromCell` for any arbitrary `Copy` type, e.g. the +/// following is unsound: +/// +/// ```rust +/// #![feature(cell_get_cloned)] +/// # use std::cell::Cell; +/// +/// #[derive(Copy, Debug)] +/// pub struct Bad<'a>(Option<&'a Cell>>, u8); +/// +/// impl Clone for Bad<'_> { +/// fn clone(&self) -> Self { +/// let a: &u8 = &self.1; +/// // when self.0 points to self, we write to self.1 while we have a live `&u8` pointing to +/// // it -- this is UB +/// self.0.unwrap().set(Self(None, 1)); +/// dbg!((a, self)); +/// Self(None, 0) +/// } +/// } +/// +/// // this is not sound +/// // unsafe impl CloneFromCell for Bad<'_> {} +/// ``` +#[unstable(feature = "cell_get_cloned", issue = "145329")] +// Allow potential overlapping implementations in user code +#[marker] +pub unsafe trait CloneFromCell: Clone {} + +// `CloneFromCell` can be implemented for types that don't have indirection and which don't access +// `Cell`s in their `Clone` implementation. A commonly-used subset is covered here. +#[unstable(feature = "cell_get_cloned", issue = "145329")] +unsafe impl CloneFromCell for [T; N] {} +#[unstable(feature = "cell_get_cloned", issue = "145329")] +unsafe impl CloneFromCell for Option {} +#[unstable(feature = "cell_get_cloned", issue = "145329")] +unsafe impl CloneFromCell for Result {} +#[unstable(feature = "cell_get_cloned", issue = "145329")] +unsafe impl CloneFromCell for PhantomData {} +#[unstable(feature = "cell_get_cloned", issue = "145329")] +unsafe impl CloneFromCell for ManuallyDrop {} +#[unstable(feature = "cell_get_cloned", issue = "145329")] +unsafe impl CloneFromCell for ops::Range {} +#[unstable(feature = "cell_get_cloned", issue = "145329")] +unsafe impl CloneFromCell for range::Range {} + +#[unstable(feature = "cell_get_cloned", issue = "145329")] +impl Cell { + /// Get a clone of the `Cell` that contains a copy of the original value. + /// + /// This allows a cheaply `Clone`-able type like an `Rc` to be stored in a `Cell`, exposing the + /// cheaper `clone()` method. + /// + /// # Examples + /// + /// ``` + /// #![feature(cell_get_cloned)] + /// + /// use core::cell::Cell; + /// use std::rc::Rc; + /// + /// let rc = Rc::new(1usize); + /// let c1 = Cell::new(rc); + /// let c2 = c1.get_cloned(); + /// assert_eq!(*c2.into_inner(), 1); + /// ``` + pub fn get_cloned(&self) -> Self { + // SAFETY: T is CloneFromCell, which guarantees that this is sound. + Cell::new(T::clone(unsafe { &*self.as_ptr() })) + } +} + /// A mutable memory location with dynamically checked borrow rules /// /// See the [module-level documentation](self) for more. diff --git a/library/core/src/tuple.rs b/library/core/src/tuple.rs index c57a8d81ade1..58f81372aff7 100644 --- a/library/core/src/tuple.rs +++ b/library/core/src/tuple.rs @@ -1,5 +1,6 @@ // See core/src/primitive_docs.rs for documentation. +use crate::cell::CloneFromCell; use crate::cmp::Ordering::{self, *}; use crate::marker::{ConstParamTy_, StructuralPartialEq}; use crate::ops::ControlFlow::{self, Break, Continue}; @@ -155,6 +156,15 @@ macro_rules! tuple_impls { } } } + + maybe_tuple_doc! { + $($T)+ @ + // SAFETY: tuples introduce no additional indirection, so they can be copied whenever T + // can. + #[unstable(feature = "cell_get_cloned", issue = "145329")] + unsafe impl<$($T: CloneFromCell),+> CloneFromCell for ($($T,)+) + {} + } } } From 571412f8190089c36758031fe09fc0ece59be6b7 Mon Sep 17 00:00:00 2001 From: dianqk Date: Sun, 8 Jun 2025 15:30:09 +0800 Subject: [PATCH 104/226] mir-opt: Eliminate dead ref statements --- compiler/rustc_codegen_ssa/src/mir/analyze.rs | 4 + compiler/rustc_middle/src/mir/mod.rs | 45 ++- compiler/rustc_middle/src/mir/pretty.rs | 19 + compiler/rustc_middle/src/mir/statement.rs | 103 +++++- compiler/rustc_middle/src/mir/visit.rs | 40 ++- compiler/rustc_mir_dataflow/src/debuginfo.rs | 10 +- .../rustc_mir_dataflow/src/impls/liveness.rs | 78 ++-- .../src/cleanup_post_borrowck.rs | 4 +- compiler/rustc_mir_transform/src/copy_prop.rs | 4 +- compiler/rustc_mir_transform/src/coroutine.rs | 2 +- .../src/dead_store_elimination.rs | 55 ++- compiler/rustc_mir_transform/src/dest_prop.rs | 4 +- compiler/rustc_mir_transform/src/gvn.rs | 2 +- .../rustc_mir_transform/src/large_enums.rs | 2 +- compiler/rustc_mir_transform/src/patch.rs | 2 +- .../rustc_mir_transform/src/promote_consts.rs | 2 +- compiler/rustc_mir_transform/src/ref_prop.rs | 2 +- .../src/remove_place_mention.rs | 2 +- .../src/remove_storage_markers.rs | 2 +- .../rustc_mir_transform/src/remove_zsts.rs | 2 +- compiler/rustc_mir_transform/src/simplify.rs | 29 +- .../src/simplify_comparison_integral.rs | 4 +- compiler/rustc_mir_transform/src/sroa.rs | 10 +- tests/codegen-llvm/debug-fndef-size.rs | 4 +- ...ad_first.DeadStoreElimination-initial.diff | 31 ++ tests/mir-opt/dead-store-elimination/ref.rs | 33 ++ ...ef.tuple.DeadStoreElimination-initial.diff | 26 ++ ...ycfg.drop_debuginfo.SimplifyCfg-final.diff | 26 ++ ...reserve_debuginfo_1.SimplifyCfg-final.diff | 30 ++ ...reserve_debuginfo_2.SimplifyCfg-final.diff | 29 ++ .../inline_shims.drop.Inline.panic-abort.diff | 56 +-- ...y.run2-{closure#0}.Inline.panic-abort.diff | 6 +- ....run2-{closure#0}.Inline.panic-unwind.diff | 6 +- ...implifyComparisonIntegral.panic-abort.diff | 6 +- ...mplifyComparisonIntegral.panic-unwind.diff | 6 +- ...git.PreCodegen.after.32bit.panic-abort.mir | 4 + ...it.PreCodegen.after.32bit.panic-unwind.mir | 4 + ...git.PreCodegen.after.64bit.panic-abort.mir | 4 + ...it.PreCodegen.after.64bit.panic-unwind.mir | 4 + ...p_forward.PreCodegen.after.panic-abort.mir | 40 ++- ..._forward.PreCodegen.after.panic-unwind.mir | 40 ++- ...as_copy.clone_as_copy.PreCodegen.after.mir | 2 +- ...py.enum_clone_as_copy.PreCodegen.after.mir | 6 +- ...ace.PreCodegen.after.32bit.panic-abort.mir | 38 +- ...ce.PreCodegen.after.32bit.panic-unwind.mir | 38 +- ...ace.PreCodegen.after.64bit.panic-abort.mir | 38 +- ...ce.PreCodegen.after.64bit.panic-unwind.mir | 38 +- tests/mir-opt/pre-codegen/drop_boxed_slice.rs | 2 +- .../loops.filter_mapped.PreCodegen.after.mir | 44 +-- .../loops.int_range.PreCodegen.after.mir | 100 +++--- .../loops.mapped.PreCodegen.after.mir | 84 ++--- tests/mir-opt/pre-codegen/loops.rs | 1 + .../loops.vec_move.PreCodegen.after.mir | 340 ++++++++++++++++-- ...variant_a-{closure#0}.PreCodegen.after.mir | 199 +++++----- ...ated_loop.PreCodegen.after.panic-abort.mir | 14 + ...ward_loop.PreCodegen.after.panic-abort.mir | 14 + ...ard_loop.PreCodegen.after.panic-unwind.mir | 14 + ...erse_loop.PreCodegen.after.panic-abort.mir | 227 ++++++++++-- ...rse_loop.PreCodegen.after.panic-unwind.mir | 233 ++++++++++-- ..._is_empty.PreCodegen.after.panic-abort.mir | 8 + ...is_empty.PreCodegen.after.panic-unwind.mir | 8 + ...next_back.PreCodegen.after.panic-abort.mir | 189 +++++++++- ...ext_back.PreCodegen.after.panic-unwind.mir | 189 +++++++++- ...iter_next.PreCodegen.after.panic-abort.mir | 14 + ...ter_next.PreCodegen.after.panic-unwind.mir | 14 + ...ans.outer.PreCodegen.after.panic-abort.mir | 2 +- ...ns.outer.PreCodegen.after.panic-unwind.mir | 2 +- ..._to_slice.PreCodegen.after.panic-abort.mir | 8 + ...to_slice.PreCodegen.after.panic-unwind.mir | 8 + 69 files changed, 2142 insertions(+), 514 deletions(-) create mode 100644 tests/mir-opt/dead-store-elimination/ref.dead_first.DeadStoreElimination-initial.diff create mode 100644 tests/mir-opt/dead-store-elimination/ref.rs create mode 100644 tests/mir-opt/dead-store-elimination/ref.tuple.DeadStoreElimination-initial.diff create mode 100644 tests/mir-opt/debuginfo/simplifycfg.drop_debuginfo.SimplifyCfg-final.diff create mode 100644 tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_1.SimplifyCfg-final.diff create mode 100644 tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_2.SimplifyCfg-final.diff diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs index 45bc54519468..0a37a904193f 100644 --- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs +++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs @@ -260,6 +260,10 @@ impl<'a, 'b, 'tcx, Bx: BuilderMethods<'b, 'tcx>> Visitor<'tcx> for LocalAnalyzer PlaceContext::MutatingUse(MutatingUseContext::Yield) => bug!(), } } + + fn visit_statement_debuginfo(&mut self, _: &mir::StmtDebugInfo<'tcx>, _: Location) { + // Debuginfo does not generate actual code. + } } #[derive(Copy, Clone, Debug, PartialEq, Eq)] diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 28142382b130..8eb7aa71fcde 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -1298,6 +1298,10 @@ pub struct BasicBlockData<'tcx> { /// List of statements in this block. pub statements: Vec>, + /// All debuginfos happen before the statement. + /// Put debuginfos here when the last statement is eliminated. + pub after_last_stmt_debuginfos: StmtDebugInfos<'tcx>, + /// Terminator for this block. /// /// N.B., this should generally ONLY be `None` during construction. @@ -1325,7 +1329,12 @@ impl<'tcx> BasicBlockData<'tcx> { terminator: Option>, is_cleanup: bool, ) -> BasicBlockData<'tcx> { - BasicBlockData { statements, terminator, is_cleanup } + BasicBlockData { + statements, + after_last_stmt_debuginfos: StmtDebugInfos::default(), + terminator, + is_cleanup, + } } /// Accessor for terminator. @@ -1360,6 +1369,36 @@ impl<'tcx> BasicBlockData<'tcx> { self.terminator().successors() } } + + pub fn retain_statements(&mut self, mut f: F) + where + F: FnMut(&Statement<'tcx>) -> bool, + { + // Place debuginfos into the next retained statement, + // this `debuginfos` variable is used to cache debuginfos between two retained statements. + let mut debuginfos = StmtDebugInfos::default(); + self.statements.retain_mut(|stmt| { + let retain = f(stmt); + if retain { + stmt.debuginfos.prepend(&mut debuginfos); + } else { + debuginfos.append(&mut stmt.debuginfos); + } + retain + }); + self.after_last_stmt_debuginfos.prepend(&mut debuginfos); + } + + pub fn strip_nops(&mut self) { + self.retain_statements(|stmt| !matches!(stmt.kind, StatementKind::Nop)) + } + + pub fn drop_debuginfo(&mut self) { + self.after_last_stmt_debuginfos.drop_debuginfo(); + for stmt in self.statements.iter_mut() { + stmt.debuginfos.drop_debuginfo(); + } + } } /////////////////////////////////////////////////////////////////////////// @@ -1664,10 +1703,10 @@ mod size_asserts { use super::*; // tidy-alphabetical-start - static_assert_size!(BasicBlockData<'_>, 128); + static_assert_size!(BasicBlockData<'_>, 152); static_assert_size!(LocalDecl<'_>, 40); static_assert_size!(SourceScopeData<'_>, 64); - static_assert_size!(Statement<'_>, 32); + static_assert_size!(Statement<'_>, 56); static_assert_size!(Terminator<'_>, 96); static_assert_size!(VarDebugInfo<'_>, 88); // tidy-alphabetical-end diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 350d75c2ee77..8cf89f778a2a 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -719,6 +719,11 @@ impl<'de, 'tcx> MirWriter<'de, 'tcx> { let mut current_location = Location { block, statement_index: 0 }; for statement in &data.statements { (self.extra_data)(PassWhere::BeforeLocation(current_location), w)?; + + for debuginfo in statement.debuginfos.iter() { + writeln!(w, "{INDENT}{INDENT}// DBG: {debuginfo:?};")?; + } + let indented_body = format!("{INDENT}{INDENT}{statement:?};"); if self.options.include_extra_comments { writeln!( @@ -749,6 +754,10 @@ impl<'de, 'tcx> MirWriter<'de, 'tcx> { current_location.statement_index += 1; } + for debuginfo in data.after_last_stmt_debuginfos.iter() { + writeln!(w, "{INDENT}{INDENT}// DBG: {debuginfo:?};")?; + } + // Terminator at the bottom. (self.extra_data)(PassWhere::BeforeLocation(current_location), w)?; if data.terminator.is_some() { @@ -829,6 +838,16 @@ impl Debug for Statement<'_> { } } +impl Debug for StmtDebugInfo<'_> { + fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { + match self { + StmtDebugInfo::AssignRef(local, place) => { + write!(fmt, "{local:?} = &{place:?}") + } + } + } +} + impl Display for NonDivergingIntrinsic<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index e009fe05b53e..9deb43eb7084 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -1,5 +1,7 @@ //! Functionality for statements, operands, places, and things that appear in them. +use std::ops; + use tracing::{debug, instrument}; use super::interpret::GlobalAlloc; @@ -15,17 +17,34 @@ use crate::ty::CoroutineArgsExt; pub struct Statement<'tcx> { pub source_info: SourceInfo, pub kind: StatementKind<'tcx>, + /// Some debuginfos appearing before the primary statement. + pub debuginfos: StmtDebugInfos<'tcx>, } impl<'tcx> Statement<'tcx> { /// Changes a statement to a nop. This is both faster than deleting instructions and avoids /// invalidating statement indices in `Location`s. - pub fn make_nop(&mut self) { - self.kind = StatementKind::Nop + pub fn make_nop(&mut self, drop_debuginfo: bool) { + if matches!(self.kind, StatementKind::Nop) { + return; + } + let replaced_stmt = std::mem::replace(&mut self.kind, StatementKind::Nop); + if !drop_debuginfo { + match replaced_stmt { + StatementKind::Assign(box (place, Rvalue::Ref(_, _, ref_place))) + if let Some(local) = place.as_local() => + { + self.debuginfos.push(StmtDebugInfo::AssignRef(local, ref_place)); + } + _ => { + bug!("debuginfo is not yet supported.") + } + } + } } pub fn new(source_info: SourceInfo, kind: StatementKind<'tcx>) -> Self { - Statement { source_info, kind } + Statement { source_info, kind, debuginfos: StmtDebugInfos::default() } } } @@ -63,6 +82,17 @@ impl<'tcx> StatementKind<'tcx> { _ => None, } } + + pub fn as_debuginfo(&self) -> Option> { + match self { + StatementKind::Assign(box (place, Rvalue::Ref(_, _, ref_place))) + if let Some(local) = place.as_local() => + { + Some(StmtDebugInfo::AssignRef(local, *ref_place)) + } + _ => None, + } + } } /////////////////////////////////////////////////////////////////////////// @@ -967,3 +997,70 @@ impl RawPtrKind { } } } + +#[derive(Default, Debug, Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)] +pub struct StmtDebugInfos<'tcx>(Vec>); + +impl<'tcx> StmtDebugInfos<'tcx> { + pub fn push(&mut self, debuginfo: StmtDebugInfo<'tcx>) { + self.0.push(debuginfo); + } + + pub fn drop_debuginfo(&mut self) { + self.0.clear(); + } + + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + pub fn prepend(&mut self, debuginfos: &mut Self) { + if debuginfos.is_empty() { + return; + }; + debuginfos.0.append(self); + std::mem::swap(debuginfos, self); + } + + pub fn append(&mut self, debuginfos: &mut Self) { + if debuginfos.is_empty() { + return; + }; + self.0.append(debuginfos); + } + + pub fn extend(&mut self, debuginfos: &Self) { + if debuginfos.is_empty() { + return; + }; + self.0.extend_from_slice(debuginfos); + } + + pub fn retain(&mut self, f: F) + where + F: FnMut(&StmtDebugInfo<'tcx>) -> bool, + { + self.0.retain(f); + } +} + +impl<'tcx> ops::Deref for StmtDebugInfos<'tcx> { + type Target = Vec>; + + #[inline] + fn deref(&self) -> &Vec> { + &self.0 + } +} + +impl<'tcx> ops::DerefMut for StmtDebugInfos<'tcx> { + #[inline] + fn deref_mut(&mut self) -> &mut Vec> { + &mut self.0 + } +} + +#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)] +pub enum StmtDebugInfo<'tcx> { + AssignRef(Local, Place<'tcx>), +} diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index f39234778005..47ae23afd556 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -95,6 +95,14 @@ macro_rules! make_mir_visitor { self.super_source_scope_data(scope_data); } + fn visit_statement_debuginfo( + &mut self, + stmt_debuginfo: & $($mutability)? StmtDebugInfo<'tcx>, + location: Location + ) { + self.super_statement_debuginfo(stmt_debuginfo, location); + } + fn visit_statement( &mut self, statement: & $($mutability)? Statement<'tcx>, @@ -301,6 +309,7 @@ macro_rules! make_mir_visitor { { let BasicBlockData { statements, + after_last_stmt_debuginfos, terminator, is_cleanup: _ } = data; @@ -312,8 +321,11 @@ macro_rules! make_mir_visitor { index += 1; } + let location = Location { block, statement_index: index }; + for debuginfo in after_last_stmt_debuginfos as & $($mutability)? [_] { + self.visit_statement_debuginfo(debuginfo, location); + } if let Some(terminator) = terminator { - let location = Location { block, statement_index: index }; self.visit_terminator(terminator, location); } } @@ -376,14 +388,38 @@ macro_rules! make_mir_visitor { } } + fn super_statement_debuginfo( + &mut self, + stmt_debuginfo: & $($mutability)? StmtDebugInfo<'tcx>, + location: Location + ) { + match stmt_debuginfo { + StmtDebugInfo::AssignRef(local, place) => { + self.visit_local( + $(& $mutability)? *local, + PlaceContext::NonUse(NonUseContext::VarDebugInfo), + location + ); + self.visit_place( + place, + PlaceContext::NonUse(NonUseContext::VarDebugInfo), + location + ); + }, + } + } + fn super_statement( &mut self, statement: & $($mutability)? Statement<'tcx>, location: Location ) { - let Statement { source_info, kind } = statement; + let Statement { source_info, kind, debuginfos } = statement; self.visit_source_info(source_info); + for debuginfo in debuginfos as & $($mutability)? [_] { + self.visit_statement_debuginfo(debuginfo, location); + } match kind { StatementKind::Assign(box (place, rvalue)) => { self.visit_assign(place, rvalue, location); diff --git a/compiler/rustc_mir_dataflow/src/debuginfo.rs b/compiler/rustc_mir_dataflow/src/debuginfo.rs index 0d25ce91c9a9..274c5943946c 100644 --- a/compiler/rustc_mir_dataflow/src/debuginfo.rs +++ b/compiler/rustc_mir_dataflow/src/debuginfo.rs @@ -5,16 +5,16 @@ use rustc_middle::mir::*; /// Return the set of locals that appear in debuginfo. pub fn debuginfo_locals(body: &Body<'_>) -> DenseBitSet { let mut visitor = DebuginfoLocals(DenseBitSet::new_empty(body.local_decls.len())); - for debuginfo in body.var_debug_info.iter() { - visitor.visit_var_debug_info(debuginfo); - } + visitor.visit_body(body); visitor.0 } struct DebuginfoLocals(DenseBitSet); impl Visitor<'_> for DebuginfoLocals { - fn visit_local(&mut self, local: Local, _: PlaceContext, _: Location) { - self.0.insert(local); + fn visit_local(&mut self, local: Local, place_context: PlaceContext, _: Location) { + if place_context == PlaceContext::NonUse(NonUseContext::VarDebugInfo) { + self.0.insert(local); + } } } diff --git a/compiler/rustc_mir_dataflow/src/impls/liveness.rs b/compiler/rustc_mir_dataflow/src/impls/liveness.rs index 5eba474a60c7..24da4b0bea28 100644 --- a/compiler/rustc_mir_dataflow/src/impls/liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/liveness.rs @@ -210,6 +210,7 @@ impl DefUse { /// All of the caveats of `MaybeLiveLocals` apply. pub struct MaybeTransitiveLiveLocals<'a> { always_live: &'a DenseBitSet, + debuginfo_locals: &'a DenseBitSet, } impl<'a> MaybeTransitiveLiveLocals<'a> { @@ -217,8 +218,46 @@ impl<'a> MaybeTransitiveLiveLocals<'a> { /// considered live. /// /// This should include at least all locals that are ever borrowed. - pub fn new(always_live: &'a DenseBitSet) -> Self { - MaybeTransitiveLiveLocals { always_live } + pub fn new( + always_live: &'a DenseBitSet, + debuginfo_locals: &'a DenseBitSet, + ) -> Self { + MaybeTransitiveLiveLocals { always_live, debuginfo_locals } + } + + pub fn can_be_removed_if_dead<'tcx>( + stmt_kind: &StatementKind<'tcx>, + always_live: &DenseBitSet, + debuginfo_locals: &'a DenseBitSet, + ) -> Option> { + // Compute the place that we are storing to, if any + let destination = match stmt_kind { + StatementKind::Assign(box (place, rvalue)) => (rvalue.is_safe_to_remove() + && (!debuginfo_locals.contains(place.local) + || (place.as_local().is_some() && matches!(rvalue, mir::Rvalue::Ref(..))))) + .then_some(*place), + StatementKind::SetDiscriminant { place, .. } | StatementKind::Deinit(place) => { + (!debuginfo_locals.contains(place.local)).then_some(**place) + } + StatementKind::FakeRead(_) + | StatementKind::StorageLive(_) + | StatementKind::StorageDead(_) + | StatementKind::Retag(..) + | StatementKind::AscribeUserType(..) + | StatementKind::PlaceMention(..) + | StatementKind::Coverage(..) + | StatementKind::Intrinsic(..) + | StatementKind::ConstEvalCounter + | StatementKind::BackwardIncompatibleDropHint { .. } + | StatementKind::Nop => None, + }; + if let Some(destination) = destination + && !destination.is_indirect() + && !always_live.contains(destination.local) + { + return Some(destination); + } + None } } @@ -243,32 +282,15 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> { statement: &mir::Statement<'tcx>, location: Location, ) { - // Compute the place that we are storing to, if any - let destination = match &statement.kind { - StatementKind::Assign(assign) => assign.1.is_safe_to_remove().then_some(assign.0), - StatementKind::SetDiscriminant { place, .. } | StatementKind::Deinit(place) => { - Some(**place) - } - StatementKind::FakeRead(_) - | StatementKind::StorageLive(_) - | StatementKind::StorageDead(_) - | StatementKind::Retag(..) - | StatementKind::AscribeUserType(..) - | StatementKind::PlaceMention(..) - | StatementKind::Coverage(..) - | StatementKind::Intrinsic(..) - | StatementKind::ConstEvalCounter - | StatementKind::BackwardIncompatibleDropHint { .. } - | StatementKind::Nop => None, - }; - if let Some(destination) = destination { - if !destination.is_indirect() - && !state.contains(destination.local) - && !self.always_live.contains(destination.local) - { - // This store is dead - return; - } + if let Some(destination) = + Self::can_be_removed_if_dead(&statement.kind, &self.always_live, &self.debuginfo_locals) + && !state.contains(destination.local) + // FIXME: We can eliminate the statement, but we'll need the statements it depends on + // for debuginfos. We need a way to handle this. + && !self.debuginfo_locals.contains(destination.local) + { + // This store is dead + return; } TransferFunction(state).visit_statement(statement, location); } diff --git a/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs b/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs index 4be67b873f73..b0bf7f484bed 100644 --- a/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs +++ b/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs @@ -36,7 +36,9 @@ impl<'tcx> crate::MirPass<'tcx> for CleanupPostBorrowck { CoverageKind::BlockMarker { .. } | CoverageKind::SpanMarker { .. }, ) | StatementKind::FakeRead(..) - | StatementKind::BackwardIncompatibleDropHint { .. } => statement.make_nop(), + | StatementKind::BackwardIncompatibleDropHint { .. } => { + statement.make_nop(true) + } StatementKind::Assign(box ( _, Rvalue::Cast( diff --git a/compiler/rustc_mir_transform/src/copy_prop.rs b/compiler/rustc_mir_transform/src/copy_prop.rs index cddeefca6817..f0bc286a9402 100644 --- a/compiler/rustc_mir_transform/src/copy_prop.rs +++ b/compiler/rustc_mir_transform/src/copy_prop.rs @@ -138,7 +138,7 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> { if let StatementKind::StorageLive(l) | StatementKind::StorageDead(l) = stmt.kind && self.storage_to_remove.contains(l) { - stmt.make_nop(); + stmt.make_nop(true); return; } @@ -150,7 +150,7 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> { *rhs && lhs == rhs { - stmt.make_nop(); + stmt.make_nop(true); } } } diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index c5cd06f170c4..814eded910df 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -411,7 +411,7 @@ impl<'tcx> MutVisitor<'tcx> for TransformVisitor<'tcx> { if let StatementKind::StorageLive(l) | StatementKind::StorageDead(l) = s.kind && self.remap.contains(l) { - s.make_nop(); + s.make_nop(true); } } diff --git a/compiler/rustc_mir_transform/src/dead_store_elimination.rs b/compiler/rustc_mir_transform/src/dead_store_elimination.rs index eea2b0990d73..a5f8a22e83c8 100644 --- a/compiler/rustc_mir_transform/src/dead_store_elimination.rs +++ b/compiler/rustc_mir_transform/src/dead_store_elimination.rs @@ -33,10 +33,9 @@ fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { // If the user requests complete debuginfo, mark the locals that appear in it as live, so // we don't remove assignments to them. - let mut always_live = debuginfo_locals(body); - always_live.union(&borrowed_locals); + let debuginfo_locals = debuginfo_locals(body); - let mut live = MaybeTransitiveLiveLocals::new(&always_live) + let mut live = MaybeTransitiveLiveLocals::new(&borrowed_locals, &debuginfo_locals) .iterate_to_fixpoint(tcx, body, None) .into_results_cursor(body); @@ -75,35 +74,23 @@ fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { } for (statement_index, statement) in bb_data.statements.iter().enumerate().rev() { - let loc = Location { block: bb, statement_index }; - if let StatementKind::Assign(assign) = &statement.kind { - if !assign.1.is_safe_to_remove() { - continue; - } - } - match &statement.kind { - StatementKind::Assign(box (place, _)) - | StatementKind::SetDiscriminant { place: box place, .. } - | StatementKind::Deinit(box place) => { - if !place.is_indirect() && !always_live.contains(place.local) { - live.seek_before_primary_effect(loc); - if !live.get().contains(place.local) { - patch.push(loc); - } - } - } - StatementKind::Retag(_, _) - | StatementKind::StorageLive(_) - | StatementKind::StorageDead(_) - | StatementKind::Coverage(_) - | StatementKind::Intrinsic(_) - | StatementKind::ConstEvalCounter - | StatementKind::PlaceMention(_) - | StatementKind::BackwardIncompatibleDropHint { .. } - | StatementKind::Nop => {} - - StatementKind::FakeRead(_) | StatementKind::AscribeUserType(_, _) => { - bug!("{:?} not found in this MIR phase!", statement.kind) + if let Some(destination) = MaybeTransitiveLiveLocals::can_be_removed_if_dead( + &statement.kind, + &borrowed_locals, + &debuginfo_locals, + ) { + let loc = Location { block: bb, statement_index }; + live.seek_before_primary_effect(loc); + if !live.get().contains(destination.local) { + let drop_debuginfo = !debuginfo_locals.contains(destination.local); + // When eliminating a dead statement, we need to address + // the debug information for that statement. + assert!( + drop_debuginfo || statement.kind.as_debuginfo().is_some(), + "don't know how to retain the debug information for {:?}", + statement.kind + ); + patch.push((loc, drop_debuginfo)); } } } @@ -114,8 +101,8 @@ fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { } let bbs = body.basic_blocks.as_mut_preserves_cfg(); - for Location { block, statement_index } in patch { - bbs[block].statements[statement_index].make_nop(); + for (Location { block, statement_index }, drop_debuginfo) in patch { + bbs[block].statements[statement_index].make_nop(drop_debuginfo); } for (block, argument_index) in call_operands_to_move { let TerminatorKind::Call { ref mut args, .. } = bbs[block].terminator_mut().kind else { diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs index 74c22ff10c19..1f38433fa5a4 100644 --- a/compiler/rustc_mir_transform/src/dest_prop.rs +++ b/compiler/rustc_mir_transform/src/dest_prop.rs @@ -276,7 +276,7 @@ impl<'tcx> MutVisitor<'tcx> for Merger<'tcx> { StatementKind::StorageDead(local) | StatementKind::StorageLive(local) if self.merged_locals.contains(*local) => { - statement.make_nop(); + statement.make_nop(true); return; } _ => (), @@ -291,7 +291,7 @@ impl<'tcx> MutVisitor<'tcx> for Merger<'tcx> { // (this includes the original statement we wanted to eliminate). if dest == place { debug!("{:?} turned into self-assignment, deleting", location); - statement.make_nop(); + statement.make_nop(true); } } _ => {} diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 99691d9e045b..3ff8dc6dbb37 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -1877,7 +1877,7 @@ impl<'tcx> MutVisitor<'tcx> for StorageRemover<'tcx> { StatementKind::StorageLive(l) | StatementKind::StorageDead(l) if self.reused_locals.contains(l) => { - stmt.make_nop() + stmt.make_nop(true) } _ => self.super_statement(stmt, loc), } diff --git a/compiler/rustc_mir_transform/src/large_enums.rs b/compiler/rustc_mir_transform/src/large_enums.rs index 1a91d6bd7da9..1b90e9158f6b 100644 --- a/compiler/rustc_mir_transform/src/large_enums.rs +++ b/compiler/rustc_mir_transform/src/large_enums.rs @@ -156,7 +156,7 @@ impl<'tcx> crate::MirPass<'tcx> for EnumSizeOpt { patch.add_statement(location, stmt); } - st.make_nop(); + st.make_nop(true); } } diff --git a/compiler/rustc_mir_transform/src/patch.rs b/compiler/rustc_mir_transform/src/patch.rs index cc8ea76011bf..2c535d011a0e 100644 --- a/compiler/rustc_mir_transform/src/patch.rs +++ b/compiler/rustc_mir_transform/src/patch.rs @@ -270,7 +270,7 @@ impl<'tcx> MirPatch<'tcx> { body.local_decls.extend(self.new_locals); for loc in self.nop_statements { - bbs[loc.block].statements[loc.statement_index].make_nop(); + bbs[loc.block].statements[loc.statement_index].make_nop(true); } let mut new_statements = self.new_statements; diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index 48ddf5a1bcab..c7dc18a4a134 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -1049,7 +1049,7 @@ fn promote_candidates<'tcx>( // Eliminate assignments to, and drops of promoted temps. let promoted = |index: Local| temps[index] == TempState::PromotedOut; for block in body.basic_blocks_mut() { - block.statements.retain(|statement| match &statement.kind { + block.retain_statements(|statement| match &statement.kind { StatementKind::Assign(box (place, _)) => { if let Some(index) = place.as_local() { !promoted(index) diff --git a/compiler/rustc_mir_transform/src/ref_prop.rs b/compiler/rustc_mir_transform/src/ref_prop.rs index b9d6e74ecae8..deb0a146476c 100644 --- a/compiler/rustc_mir_transform/src/ref_prop.rs +++ b/compiler/rustc_mir_transform/src/ref_prop.rs @@ -435,7 +435,7 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'tcx> { StatementKind::StorageLive(l) | StatementKind::StorageDead(l) if self.storage_to_remove.contains(l) => { - stmt.make_nop(); + stmt.make_nop(true); } // Do not remove assignments as they may still be useful for debuginfo. _ => self.super_statement(stmt, loc), diff --git a/compiler/rustc_mir_transform/src/remove_place_mention.rs b/compiler/rustc_mir_transform/src/remove_place_mention.rs index cb598ceb4dfe..d56b51bb496e 100644 --- a/compiler/rustc_mir_transform/src/remove_place_mention.rs +++ b/compiler/rustc_mir_transform/src/remove_place_mention.rs @@ -14,7 +14,7 @@ impl<'tcx> crate::MirPass<'tcx> for RemovePlaceMention { fn run_pass(&self, _: TyCtxt<'tcx>, body: &mut Body<'tcx>) { trace!("Running RemovePlaceMention on {:?}", body.source); for data in body.basic_blocks.as_mut_preserves_cfg() { - data.statements.retain(|statement| match statement.kind { + data.retain_statements(|statement| match statement.kind { StatementKind::PlaceMention(..) | StatementKind::Nop => false, _ => true, }) diff --git a/compiler/rustc_mir_transform/src/remove_storage_markers.rs b/compiler/rustc_mir_transform/src/remove_storage_markers.rs index 1ae33c009687..cb97d2c865ac 100644 --- a/compiler/rustc_mir_transform/src/remove_storage_markers.rs +++ b/compiler/rustc_mir_transform/src/remove_storage_markers.rs @@ -14,7 +14,7 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveStorageMarkers { fn run_pass(&self, _tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { trace!("Running RemoveStorageMarkers on {:?}", body.source); for data in body.basic_blocks.as_mut_preserves_cfg() { - data.statements.retain(|statement| match statement.kind { + data.retain_statements(|statement| match statement.kind { StatementKind::StorageLive(..) | StatementKind::StorageDead(..) | StatementKind::Nop => false, diff --git a/compiler/rustc_mir_transform/src/remove_zsts.rs b/compiler/rustc_mir_transform/src/remove_zsts.rs index c4dc8638b26a..90c1b3520b96 100644 --- a/compiler/rustc_mir_transform/src/remove_zsts.rs +++ b/compiler/rustc_mir_transform/src/remove_zsts.rs @@ -141,7 +141,7 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> { && let ty = place_for_ty.ty(self.local_decls, self.tcx).ty && self.known_to_be_zst(ty) { - statement.make_nop(); + statement.make_nop(true); } else { self.super_statement(statement, loc); } diff --git a/compiler/rustc_mir_transform/src/simplify.rs b/compiler/rustc_mir_transform/src/simplify.rs index 75917d23883b..9f7bb3b03795 100644 --- a/compiler/rustc_mir_transform/src/simplify.rs +++ b/compiler/rustc_mir_transform/src/simplify.rs @@ -36,7 +36,9 @@ use itertools::Itertools as _; use rustc_index::{Idx, IndexSlice, IndexVec}; -use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor}; +use rustc_middle::mir::visit::{ + MutVisitor, MutatingUseContext, NonUseContext, PlaceContext, Visitor, +}; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; use rustc_span::DUMMY_SP; @@ -303,7 +305,7 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> { fn strip_nops(&mut self) { for blk in self.basic_blocks.iter_mut() { - blk.statements.retain(|stmt| !matches!(stmt.kind, StatementKind::Nop)) + blk.retain_statements(|stmt| !matches!(stmt.kind, StatementKind::Nop)) } } } @@ -539,12 +541,20 @@ impl<'tcx> Visitor<'tcx> for UsedLocals { self.super_statement(statement, location); } - StatementKind::ConstEvalCounter | StatementKind::Nop => {} - - StatementKind::StorageLive(_local) | StatementKind::StorageDead(_local) => {} + StatementKind::ConstEvalCounter + | StatementKind::Nop + | StatementKind::StorageLive(..) + | StatementKind::StorageDead(..) => { + for debuginfo in statement.debuginfos.iter() { + self.visit_statement_debuginfo(debuginfo, location); + } + } StatementKind::Assign(box (ref place, ref rvalue)) => { if rvalue.is_safe_to_remove() { + for debuginfo in statement.debuginfos.iter() { + self.visit_statement_debuginfo(debuginfo, location); + } self.visit_lhs(place, location); self.visit_rvalue(rvalue, location); } else { @@ -555,15 +565,18 @@ impl<'tcx> Visitor<'tcx> for UsedLocals { StatementKind::SetDiscriminant { ref place, variant_index: _ } | StatementKind::Deinit(ref place) | StatementKind::BackwardIncompatibleDropHint { ref place, reason: _ } => { + for debuginfo in statement.debuginfos.iter() { + self.visit_statement_debuginfo(debuginfo, location); + } self.visit_lhs(place, location); } } } - fn visit_local(&mut self, local: Local, _ctx: PlaceContext, _location: Location) { + fn visit_local(&mut self, local: Local, ctx: PlaceContext, _location: Location) { if self.increment { self.use_count[local] += 1; - } else { + } else if ctx != PlaceContext::NonUse(NonUseContext::VarDebugInfo) { assert_ne!(self.use_count[local], 0); self.use_count[local] -= 1; } @@ -583,7 +596,7 @@ fn remove_unused_definitions_helper(used_locals: &mut UsedLocals, body: &mut Bod for data in body.basic_blocks.as_mut_preserves_cfg() { // Remove unnecessary StorageLive and StorageDead annotations. - data.statements.retain(|statement| { + data.retain_statements(|statement| { let keep = match &statement.kind { StatementKind::StorageLive(local) | StatementKind::StorageDead(local) => { used_locals.is_used(*local) diff --git a/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs b/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs index c60eb566521c..4597439e269f 100644 --- a/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs +++ b/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs @@ -76,7 +76,7 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyComparisonIntegral { // delete comparison statement if it the value being switched on was moved, which means // it can not be user later on if opt.can_remove_bin_op_stmt { - bb.statements[opt.bin_op_stmt_idx].make_nop(); + bb.statements[opt.bin_op_stmt_idx].make_nop(true); } else { // if the integer being compared to a const integral is being moved into the // comparison, e.g `_2 = Eq(move _3, const 'x');` @@ -136,7 +136,7 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyComparisonIntegral { } for (idx, bb_idx) in storage_deads_to_remove { - body.basic_blocks_mut()[bb_idx].statements[idx].make_nop(); + body.basic_blocks_mut()[bb_idx].statements[idx].make_nop(true); } for (idx, stmt) in storage_deads_to_insert { diff --git a/compiler/rustc_mir_transform/src/sroa.rs b/compiler/rustc_mir_transform/src/sroa.rs index 38769885f368..99f10b8d91d2 100644 --- a/compiler/rustc_mir_transform/src/sroa.rs +++ b/compiler/rustc_mir_transform/src/sroa.rs @@ -318,7 +318,7 @@ impl<'tcx, 'll> MutVisitor<'tcx> for ReplacementVisitor<'tcx, 'll> { for (_, _, fl) in final_locals { self.patch.add_statement(location, StatementKind::StorageLive(fl)); } - statement.make_nop(); + statement.make_nop(true); } return; } @@ -327,7 +327,7 @@ impl<'tcx, 'll> MutVisitor<'tcx> for ReplacementVisitor<'tcx, 'll> { for (_, _, fl) in final_locals { self.patch.add_statement(location, StatementKind::StorageDead(fl)); } - statement.make_nop(); + statement.make_nop(true); } return; } @@ -337,7 +337,7 @@ impl<'tcx, 'll> MutVisitor<'tcx> for ReplacementVisitor<'tcx, 'll> { self.patch .add_statement(location, StatementKind::Deinit(Box::new(fl.into()))); } - statement.make_nop(); + statement.make_nop(true); return; } } @@ -367,7 +367,7 @@ impl<'tcx, 'll> MutVisitor<'tcx> for ReplacementVisitor<'tcx, 'll> { ); } } - statement.make_nop(); + statement.make_nop(true); return; } } @@ -429,7 +429,7 @@ impl<'tcx, 'll> MutVisitor<'tcx> for ReplacementVisitor<'tcx, 'll> { StatementKind::Assign(Box::new((new_local.into(), rvalue))), ); } - statement.make_nop(); + statement.make_nop(true); return; } } diff --git a/tests/codegen-llvm/debug-fndef-size.rs b/tests/codegen-llvm/debug-fndef-size.rs index 8f716c34e7b7..02629bd748c4 100644 --- a/tests/codegen-llvm/debug-fndef-size.rs +++ b/tests/codegen-llvm/debug-fndef-size.rs @@ -16,5 +16,5 @@ pub fn main() { // CHECK: %compare.dbg.spill = alloca [0 x i8], align 1 // CHECK: dbg{{.}}declare({{(metadata )?}}ptr %compare.dbg.spill, {{(metadata )?}}![[VAR:.*]], {{(metadata )?}}!DIExpression() -// CHECK: ![[TYPE:.*]] = !DIDerivedType(tag: DW_TAG_pointer_type, name: "fn(&i32, &i32) -> core::cmp::Ordering", baseType: !{{.*}}, align: 8, dwarfAddressSpace: {{.*}}) -// CHECK: ![[VAR]] = !DILocalVariable(name: "compare", scope: !{{.*}}, file: !{{.*}}, line: {{.*}}, type: ![[TYPE]], align: 8) +// CHECK-DAG: ![[TYPE:.*]] = !DIDerivedType(tag: DW_TAG_pointer_type, name: "fn(&i32, &i32) -> core::cmp::Ordering", baseType: !{{.*}}, align: 8, dwarfAddressSpace: {{.*}}) +// CHECK-DAG: ![[VAR]] = !DILocalVariable(name: "compare", scope: !{{.*}}, file: !{{.*}}, line: {{.*}}, type: ![[TYPE]], align: 8) diff --git a/tests/mir-opt/dead-store-elimination/ref.dead_first.DeadStoreElimination-initial.diff b/tests/mir-opt/dead-store-elimination/ref.dead_first.DeadStoreElimination-initial.diff new file mode 100644 index 000000000000..d823241bc620 --- /dev/null +++ b/tests/mir-opt/dead-store-elimination/ref.dead_first.DeadStoreElimination-initial.diff @@ -0,0 +1,31 @@ +- // MIR for `dead_first` before DeadStoreElimination-initial ++ // MIR for `dead_first` after DeadStoreElimination-initial + + fn dead_first(_1: &Foo) -> &i32 { + debug v => _1; + let mut _0: &i32; + let mut _2: &i32; + let mut _3: &i32; + let _4: &i32; + scope 1 { + debug a => _2; + } + + bb0: { + StorageLive(_2); +- _2 = &((*_1).2: i32); ++ // DBG: _2 = &((*_1).2: i32); ++ nop; + StorageLive(_3); + StorageLive(_4); + _4 = &((*_1).0: i32); + _3 = &(*_4); + _2 = move _3; + StorageDead(_3); + StorageDead(_4); + _0 = &(*_2); + StorageDead(_2); + return; + } + } + diff --git a/tests/mir-opt/dead-store-elimination/ref.rs b/tests/mir-opt/dead-store-elimination/ref.rs new file mode 100644 index 000000000000..18d9ea8b84d7 --- /dev/null +++ b/tests/mir-opt/dead-store-elimination/ref.rs @@ -0,0 +1,33 @@ +//@ test-mir-pass: DeadStoreElimination-initial + +pub struct Foo { + a: i32, + b: i64, + c: i32, +} + +// EMIT_MIR ref.tuple.DeadStoreElimination-initial.diff +pub fn tuple(v: (i32, &Foo)) -> i32 { + // CHECK-LABEL: fn tuple + // CHECK: debug _dead => [[dead:_[0-9]+]]; + // CHECK: bb0: + // FIXME: Preserve `tmp` for debuginfo, but we can merge it into the debuginfo. + // CHECK: [[tmp:_[0-9]+]] = deref_copy (_1.1: &Foo); + // CHECK-NEXT: DBG: [[dead]] = &((*[[tmp]]).2: i32) + let _dead = &v.1.c; + v.1.a +} + +// EMIT_MIR ref.dead_first.DeadStoreElimination-initial.diff +pub fn dead_first(v: &Foo) -> &i32 { + // CHECK-LABEL: fn dead_first + // CHECK: debug a => [[var_a:_[0-9]+]]; + // CHECK: bb0: + // CHECK: DBG: [[var_a]] = &((*_1).2: i32) + // CHECK: [[tmp_4:_[0-9]+]] = &((*_1).0: i32) + // CHECK: [[tmp_3:_[0-9]+]] = &(*[[tmp_4]]) + // CHECK: [[var_a]] = move [[tmp_3]] + let mut a = &v.c; + a = &v.a; + a +} diff --git a/tests/mir-opt/dead-store-elimination/ref.tuple.DeadStoreElimination-initial.diff b/tests/mir-opt/dead-store-elimination/ref.tuple.DeadStoreElimination-initial.diff new file mode 100644 index 000000000000..0547a42cab24 --- /dev/null +++ b/tests/mir-opt/dead-store-elimination/ref.tuple.DeadStoreElimination-initial.diff @@ -0,0 +1,26 @@ +- // MIR for `tuple` before DeadStoreElimination-initial ++ // MIR for `tuple` after DeadStoreElimination-initial + + fn tuple(_1: (i32, &Foo)) -> i32 { + debug v => _1; + let mut _0: i32; + let _2: &i32; + let mut _3: &Foo; + let mut _4: &Foo; + scope 1 { + debug _dead => _2; + } + + bb0: { + StorageLive(_2); + _3 = deref_copy (_1.1: &Foo); +- _2 = &((*_3).2: i32); ++ // DBG: _2 = &((*_3).2: i32); ++ nop; + _4 = deref_copy (_1.1: &Foo); + _0 = copy ((*_4).0: i32); + StorageDead(_2); + return; + } + } + diff --git a/tests/mir-opt/debuginfo/simplifycfg.drop_debuginfo.SimplifyCfg-final.diff b/tests/mir-opt/debuginfo/simplifycfg.drop_debuginfo.SimplifyCfg-final.diff new file mode 100644 index 000000000000..d4a73351ee4a --- /dev/null +++ b/tests/mir-opt/debuginfo/simplifycfg.drop_debuginfo.SimplifyCfg-final.diff @@ -0,0 +1,26 @@ +- // MIR for `drop_debuginfo` before SimplifyCfg-final ++ // MIR for `drop_debuginfo` after SimplifyCfg-final + + fn drop_debuginfo(_1: &Foo, _2: bool) -> i32 { + debug foo_a => _3; + debug foo_b => _4; + let mut _0: i32; + let mut _3: &i32; + let mut _4: &i64; + + bb0: { +- switchInt(copy _2) -> [1: bb1, otherwise: bb2]; +- } +- +- bb1: { +- // DBG: _3 = &((*_1).0: i32); +- goto -> bb2; +- } +- +- bb2: { + // DBG: _4 = &((*_1).1: i64); + _0 = copy ((*_1).2: i32); + return; + } + } + diff --git a/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_1.SimplifyCfg-final.diff b/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_1.SimplifyCfg-final.diff new file mode 100644 index 000000000000..1c12358ad893 --- /dev/null +++ b/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_1.SimplifyCfg-final.diff @@ -0,0 +1,30 @@ +- // MIR for `preserve_debuginfo_1` before SimplifyCfg-final ++ // MIR for `preserve_debuginfo_1` after SimplifyCfg-final + + fn preserve_debuginfo_1(_1: &Foo, _2: &mut bool) -> i32 { + debug foo_a => _3; + debug foo_b => _4; + debug foo_c => _5; + let mut _0: i32; + let mut _3: &i32; + let mut _4: &i64; + let mut _5: &i32; + + bb0: { +- goto -> bb1; +- } +- +- bb1: { + (*_2) = const true; + // DBG: _3 = &((*_1).0: i32); +- goto -> bb2; +- } +- +- bb2: { + // DBG: _4 = &((*_1).1: i64); + _0 = copy ((*_1).2: i32); + // DBG: _5 = &((*_1).2: i32); + return; + } + } + diff --git a/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_2.SimplifyCfg-final.diff b/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_2.SimplifyCfg-final.diff new file mode 100644 index 000000000000..de8e5612c878 --- /dev/null +++ b/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_2.SimplifyCfg-final.diff @@ -0,0 +1,29 @@ +- // MIR for `preserve_debuginfo_2` before SimplifyCfg-final ++ // MIR for `preserve_debuginfo_2` after SimplifyCfg-final + + fn preserve_debuginfo_2(_1: &Foo) -> i32 { + debug foo_a => _2; + debug foo_b => _3; + debug foo_c => _4; + let mut _0: i32; + let mut _2: &i32; + let mut _3: &i64; + let mut _4: &i32; + + bb0: { +- goto -> bb1; +- } +- +- bb1: { + // DBG: _2 = &((*_1).0: i32); +- goto -> bb2; +- } +- +- bb2: { + // DBG: _3 = &((*_1).1: i64); + _0 = copy ((*_1).2: i32); + // DBG: _4 = &((*_1).2: i32); + return; + } + } + diff --git a/tests/mir-opt/inline/inline_shims.drop.Inline.panic-abort.diff b/tests/mir-opt/inline/inline_shims.drop.Inline.panic-abort.diff index f6c111a2228a..f7729c7c5fa6 100644 --- a/tests/mir-opt/inline/inline_shims.drop.Inline.panic-abort.diff +++ b/tests/mir-opt/inline/inline_shims.drop.Inline.panic-abort.diff @@ -16,10 +16,12 @@ + let mut _9: *mut A; + let mut _10: usize; + scope 3 (inlined Vec::::as_mut_ptr) { ++ let mut _11: &alloc::raw_vec::RawVec; + scope 4 (inlined alloc::raw_vec::RawVec::::ptr) { ++ let mut _12: &alloc::raw_vec::RawVecInner; + scope 5 (inlined alloc::raw_vec::RawVecInner::ptr::) { + scope 6 (inlined alloc::raw_vec::RawVecInner::non_null::) { -+ let mut _11: std::ptr::NonNull; ++ let mut _13: std::ptr::NonNull; + scope 7 (inlined Unique::::cast::) { + scope 8 (inlined NonNull::::cast::) { + scope 9 (inlined NonNull::::as_ptr) { @@ -39,15 +41,15 @@ + } + } + scope 14 (inlined drop_in_place::<[A]> - shim(Some([A]))) { -+ let mut _12: usize; -+ let mut _13: *mut A; -+ let mut _14: bool; ++ let mut _14: usize; ++ let mut _15: *mut A; ++ let mut _16: bool; + } + } + } + scope 15 (inlined drop_in_place::> - shim(Some(Option))) { -+ let mut _15: isize; -+ let mut _16: isize; ++ let mut _17: isize; ++ let mut _18: isize; + } bb0: { @@ -62,16 +64,22 @@ + StorageLive(_8); + StorageLive(_9); + StorageLive(_11); -+ _11 = copy (((((*_6).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).0: std::ptr::Unique).0: std::ptr::NonNull); -+ _9 = copy _11 as *mut A (Transmute); ++ // DBG: _11 = &((*_6).0: alloc::raw_vec::RawVec); ++ StorageLive(_12); ++ // DBG: _12 = &(((*_6).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner); ++ StorageLive(_13); ++ _13 = copy (((((*_6).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).0: std::ptr::Unique).0: std::ptr::NonNull); ++ _9 = copy _13 as *mut A (Transmute); ++ StorageDead(_13); ++ StorageDead(_12); + StorageDead(_11); + _10 = copy ((*_6).1: usize); + _8 = *mut [A] from (copy _9, copy _10); + StorageDead(_9); -+ StorageLive(_12); -+ StorageLive(_13); + StorageLive(_14); -+ _12 = const 0_usize; ++ StorageLive(_15); ++ StorageLive(_16); ++ _14 = const 0_usize; + goto -> bb4; } @@ -83,35 +91,35 @@ StorageLive(_5); _5 = copy _2; - _0 = drop_in_place::>(move _5) -> [return: bb2, unwind unreachable]; -+ StorageLive(_15); -+ StorageLive(_16); -+ _15 = discriminant((*_5)); -+ switchInt(move _15) -> [0: bb5, otherwise: bb6]; ++ StorageLive(_17); ++ StorageLive(_18); ++ _17 = discriminant((*_5)); ++ switchInt(move _17) -> [0: bb5, otherwise: bb6]; } bb2: { ++ StorageDead(_16); ++ StorageDead(_15); + StorageDead(_14); -+ StorageDead(_13); -+ StorageDead(_12); + StorageDead(_8); + StorageDead(_10); + drop(((*_4).0: alloc::raw_vec::RawVec)) -> [return: bb1, unwind unreachable]; + } + + bb3: { -+ _13 = &raw mut (*_8)[_12]; -+ _12 = Add(move _12, const 1_usize); -+ drop((*_13)) -> [return: bb4, unwind unreachable]; ++ _15 = &raw mut (*_8)[_14]; ++ _14 = Add(move _14, const 1_usize); ++ drop((*_15)) -> [return: bb4, unwind unreachable]; + } + + bb4: { -+ _14 = Eq(copy _12, copy _10); -+ switchInt(move _14) -> [0: bb3, otherwise: bb2]; ++ _16 = Eq(copy _14, copy _10); ++ switchInt(move _16) -> [0: bb3, otherwise: bb2]; + } + + bb5: { -+ StorageDead(_16); -+ StorageDead(_15); ++ StorageDead(_18); ++ StorageDead(_17); StorageDead(_5); return; + } diff --git a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff index 2ae86e2eb8bb..649b5a652027 100644 --- a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff +++ b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff @@ -228,12 +228,12 @@ + StorageLive(_41); + StorageLive(_42); + StorageLive(_43); -+ _45 = &mut _19; ++ // DBG: _45 = &_19; + StorageLive(_46); -+ _46 = &mut (_19.0: &mut std::future::Ready<()>); ++ // DBG: _46 = &(_19.0: &mut std::future::Ready<()>); + _44 = copy (_19.0: &mut std::future::Ready<()>); + StorageDead(_46); -+ _43 = &mut ((*_44).0: std::option::Option<()>); ++ // DBG: _43 = &((*_44).0: std::option::Option<()>); + StorageLive(_47); + _47 = Option::<()>::None; + _42 = copy ((*_44).0: std::option::Option<()>); diff --git a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff index d7ae931aaae5..32021b20ef44 100644 --- a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff @@ -245,12 +245,12 @@ + StorageLive(_43); + StorageLive(_44); + StorageLive(_45); -+ _47 = &mut _19; ++ // DBG: _47 = &_19; + StorageLive(_48); -+ _48 = &mut (_19.0: &mut std::future::Ready<()>); ++ // DBG: _48 = &(_19.0: &mut std::future::Ready<()>); + _46 = copy (_19.0: &mut std::future::Ready<()>); + StorageDead(_48); -+ _45 = &mut ((*_46).0: std::option::Option<()>); ++ // DBG: _45 = &((*_46).0: std::option::Option<()>); + StorageLive(_49); + _49 = Option::<()>::None; + _44 = copy ((*_46).0: std::option::Option<()>); diff --git a/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-abort.diff b/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-abort.diff index 5cf36b9aebf2..a294c1b4588d 100644 --- a/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-abort.diff +++ b/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-abort.diff @@ -34,11 +34,11 @@ bb2: { StorageLive(_5); - _5 = &(*_2)[0 of 3]; + // DBG: _5 = &(*_2)[0 of 3]; StorageLive(_6); - _6 = &(*_2)[1 of 3]; + // DBG: _6 = &(*_2)[1 of 3]; StorageLive(_7); - _7 = &(*_2)[2 of 3]; + // DBG: _7 = &(*_2)[2 of 3]; StorageDead(_7); StorageDead(_6); StorageDead(_5); diff --git a/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-unwind.diff b/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-unwind.diff index 0598a3aa3f19..691271cf94ed 100644 --- a/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-unwind.diff +++ b/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-unwind.diff @@ -34,11 +34,11 @@ bb2: { StorageLive(_5); - _5 = &(*_2)[0 of 3]; + // DBG: _5 = &(*_2)[0 of 3]; StorageLive(_6); - _6 = &(*_2)[1 of 3]; + // DBG: _6 = &(*_2)[1 of 3]; StorageLive(_7); - _7 = &(*_2)[2 of 3]; + // DBG: _7 = &(*_2)[2 of 3]; StorageDead(_7); StorageDead(_6); StorageDead(_5); diff --git a/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.32bit.panic-abort.mir b/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.32bit.panic-abort.mir index b5c23822162c..d4248f5d87fb 100644 --- a/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.32bit.panic-abort.mir +++ b/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.32bit.panic-abort.mir @@ -6,6 +6,7 @@ fn num_to_digit(_1: char) -> u32 { let mut _4: std::option::Option; scope 1 (inlined char::methods::::is_digit) { let _2: std::option::Option; + let mut _7: &std::option::Option; scope 2 (inlined Option::::is_some) { let mut _3: isize; scope 3 { @@ -20,14 +21,17 @@ fn num_to_digit(_1: char) -> u32 { } bb0: { + StorageLive(_7); StorageLive(_2); _2 = char::methods::::to_digit(copy _1, const 8_u32) -> [return: bb1, unwind unreachable]; } bb1: { + // DBG: _7 = &_2; StorageLive(_3); _3 = discriminant(_2); StorageDead(_2); + StorageDead(_7); switchInt(move _3) -> [1: bb2, otherwise: bb7]; } diff --git a/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.32bit.panic-unwind.mir b/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.32bit.panic-unwind.mir index f22b8835735d..a834932df5a8 100644 --- a/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.32bit.panic-unwind.mir +++ b/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.32bit.panic-unwind.mir @@ -6,6 +6,7 @@ fn num_to_digit(_1: char) -> u32 { let mut _4: std::option::Option; scope 1 (inlined char::methods::::is_digit) { let _2: std::option::Option; + let mut _7: &std::option::Option; scope 2 (inlined Option::::is_some) { let mut _3: isize; scope 3 { @@ -20,14 +21,17 @@ fn num_to_digit(_1: char) -> u32 { } bb0: { + StorageLive(_7); StorageLive(_2); _2 = char::methods::::to_digit(copy _1, const 8_u32) -> [return: bb1, unwind continue]; } bb1: { + // DBG: _7 = &_2; StorageLive(_3); _3 = discriminant(_2); StorageDead(_2); + StorageDead(_7); switchInt(move _3) -> [1: bb2, otherwise: bb7]; } diff --git a/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.64bit.panic-abort.mir b/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.64bit.panic-abort.mir index b5c23822162c..d4248f5d87fb 100644 --- a/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.64bit.panic-abort.mir +++ b/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.64bit.panic-abort.mir @@ -6,6 +6,7 @@ fn num_to_digit(_1: char) -> u32 { let mut _4: std::option::Option; scope 1 (inlined char::methods::::is_digit) { let _2: std::option::Option; + let mut _7: &std::option::Option; scope 2 (inlined Option::::is_some) { let mut _3: isize; scope 3 { @@ -20,14 +21,17 @@ fn num_to_digit(_1: char) -> u32 { } bb0: { + StorageLive(_7); StorageLive(_2); _2 = char::methods::::to_digit(copy _1, const 8_u32) -> [return: bb1, unwind unreachable]; } bb1: { + // DBG: _7 = &_2; StorageLive(_3); _3 = discriminant(_2); StorageDead(_2); + StorageDead(_7); switchInt(move _3) -> [1: bb2, otherwise: bb7]; } diff --git a/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.64bit.panic-unwind.mir b/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.64bit.panic-unwind.mir index f22b8835735d..a834932df5a8 100644 --- a/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.64bit.panic-unwind.mir +++ b/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.64bit.panic-unwind.mir @@ -6,6 +6,7 @@ fn num_to_digit(_1: char) -> u32 { let mut _4: std::option::Option; scope 1 (inlined char::methods::::is_digit) { let _2: std::option::Option; + let mut _7: &std::option::Option; scope 2 (inlined Option::::is_some) { let mut _3: isize; scope 3 { @@ -20,14 +21,17 @@ fn num_to_digit(_1: char) -> u32 { } bb0: { + StorageLive(_7); StorageLive(_2); _2 = char::methods::::to_digit(copy _1, const 8_u32) -> [return: bb1, unwind continue]; } bb1: { + // DBG: _7 = &_2; StorageLive(_3); _3 = discriminant(_2); StorageDead(_2); + StorageDead(_7); switchInt(move _3) -> [1: bb2, otherwise: bb7]; } diff --git a/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-abort.mir index 83478e60b5d4..23dbc066dd52 100644 --- a/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-abort.mir @@ -5,7 +5,9 @@ fn step_forward(_1: u16, _2: usize) -> u16 { debug n => _2; let mut _0: u16; scope 1 (inlined ::forward) { - let mut _8: u16; + let _8: std::option::Option; + let mut _10: u16; + let mut _11: &std::option::Option; scope 2 { } scope 3 (inlined ::forward_checked) { @@ -13,8 +15,9 @@ fn step_forward(_1: u16, _2: usize) -> u16 { scope 6 (inlined core::num::::checked_add) { let mut _5: (u16, bool); let mut _6: bool; + let mut _7: u16; scope 7 (inlined std::intrinsics::unlikely) { - let _7: (); + let _9: (); } } } @@ -35,6 +38,8 @@ fn step_forward(_1: u16, _2: usize) -> u16 { bb0: { StorageLive(_4); + StorageLive(_11); + StorageLive(_8); StorageLive(_3); _3 = Gt(copy _2, const 65535_usize); switchInt(move _3) -> [0: bb1, otherwise: bb5]; @@ -53,34 +58,55 @@ fn step_forward(_1: u16, _2: usize) -> u16 { bb2: { StorageDead(_5); StorageDead(_6); + StorageLive(_7); + _7 = AddUnchecked(copy _1, copy _4); + _8 = Option::::Some(move _7); + StorageDead(_7); + // DBG: _11 = &_8; + StorageDead(_8); + StorageDead(_11); goto -> bb7; } bb3: { - _7 = std::intrinsics::cold_path() -> [return: bb4, unwind unreachable]; + _9 = std::intrinsics::cold_path() -> [return: bb4, unwind unreachable]; } bb4: { StorageDead(_5); StorageDead(_6); + _8 = const Option::::None; + // DBG: _11 = &_8; goto -> bb6; } bb5: { StorageDead(_3); + _8 = const Option::::None; + // DBG: _11 = &_8; goto -> bb6; } bb6: { + StorageDead(_8); + StorageDead(_11); assert(!const true, "attempt to compute `{} + {}`, which would overflow", const core::num::::MAX, const 1_u16) -> [success: bb7, unwind unreachable]; } bb7: { - StorageLive(_8); - _8 = copy _2 as u16 (IntToInt); - _0 = Add(copy _1, copy _8); - StorageDead(_8); + StorageLive(_10); + _10 = copy _2 as u16 (IntToInt); + _0 = Add(copy _1, copy _10); + StorageDead(_10); StorageDead(_4); return; } } + +ALLOC0 (size: 4, align: 2) { + 00 00 __ __ │ ..░░ +} + +ALLOC1 (size: 4, align: 2) { + 00 00 __ __ │ ..░░ +} diff --git a/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-unwind.mir index ac7a6e044519..ac15f070597e 100644 --- a/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-unwind.mir @@ -5,7 +5,9 @@ fn step_forward(_1: u16, _2: usize) -> u16 { debug n => _2; let mut _0: u16; scope 1 (inlined ::forward) { - let mut _8: u16; + let _8: std::option::Option; + let mut _10: u16; + let mut _11: &std::option::Option; scope 2 { } scope 3 (inlined ::forward_checked) { @@ -13,8 +15,9 @@ fn step_forward(_1: u16, _2: usize) -> u16 { scope 6 (inlined core::num::::checked_add) { let mut _5: (u16, bool); let mut _6: bool; + let mut _7: u16; scope 7 (inlined std::intrinsics::unlikely) { - let _7: (); + let _9: (); } } } @@ -35,6 +38,8 @@ fn step_forward(_1: u16, _2: usize) -> u16 { bb0: { StorageLive(_4); + StorageLive(_11); + StorageLive(_8); StorageLive(_3); _3 = Gt(copy _2, const 65535_usize); switchInt(move _3) -> [0: bb1, otherwise: bb5]; @@ -53,34 +58,55 @@ fn step_forward(_1: u16, _2: usize) -> u16 { bb2: { StorageDead(_5); StorageDead(_6); + StorageLive(_7); + _7 = AddUnchecked(copy _1, copy _4); + _8 = Option::::Some(move _7); + StorageDead(_7); + // DBG: _11 = &_8; + StorageDead(_8); + StorageDead(_11); goto -> bb7; } bb3: { - _7 = std::intrinsics::cold_path() -> [return: bb4, unwind unreachable]; + _9 = std::intrinsics::cold_path() -> [return: bb4, unwind unreachable]; } bb4: { StorageDead(_5); StorageDead(_6); + _8 = const Option::::None; + // DBG: _11 = &_8; goto -> bb6; } bb5: { StorageDead(_3); + _8 = const Option::::None; + // DBG: _11 = &_8; goto -> bb6; } bb6: { + StorageDead(_8); + StorageDead(_11); assert(!const true, "attempt to compute `{} + {}`, which would overflow", const core::num::::MAX, const 1_u16) -> [success: bb7, unwind continue]; } bb7: { - StorageLive(_8); - _8 = copy _2 as u16 (IntToInt); - _0 = Add(copy _1, copy _8); - StorageDead(_8); + StorageLive(_10); + _10 = copy _2 as u16 (IntToInt); + _0 = Add(copy _1, copy _10); + StorageDead(_10); StorageDead(_4); return; } } + +ALLOC0 (size: 4, align: 2) { + 00 00 __ __ │ ..░░ +} + +ALLOC1 (size: 4, align: 2) { + 00 00 __ __ │ ..░░ +} diff --git a/tests/mir-opt/pre-codegen/clone_as_copy.clone_as_copy.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/clone_as_copy.clone_as_copy.PreCodegen.after.mir index 34747e5a9285..66239a0ef663 100644 --- a/tests/mir-opt/pre-codegen/clone_as_copy.clone_as_copy.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/clone_as_copy.clone_as_copy.PreCodegen.after.mir @@ -13,7 +13,7 @@ fn clone_as_copy(_1: &NestCopy) -> NestCopy { bb0: { StorageLive(_2); - _2 = &((*_1).1: AllCopy); + // DBG: _2 = &((*_1).1: AllCopy); _0 = copy (*_1); StorageDead(_2); return; diff --git a/tests/mir-opt/pre-codegen/clone_as_copy.enum_clone_as_copy.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/clone_as_copy.enum_clone_as_copy.PreCodegen.after.mir index e67f362ee04a..aa03cec070ad 100644 --- a/tests/mir-opt/pre-codegen/clone_as_copy.enum_clone_as_copy.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/clone_as_copy.enum_clone_as_copy.PreCodegen.after.mir @@ -35,15 +35,15 @@ fn enum_clone_as_copy(_1: &Enum1) -> Enum1 { } bb1: { - _3 = &(((*_1) as A).0: AllCopy); + // DBG: _3 = &(((*_1) as A).0: AllCopy); _0 = copy (*_1); goto -> bb3; } bb2: { - _4 = &(((*_1) as B).0: NestCopy); + // DBG: _4 = &(((*_1) as B).0: NestCopy); StorageLive(_5); - _5 = &((((*_1) as B).0: NestCopy).1: AllCopy); + // DBG: _5 = &((((*_1) as B).0: NestCopy).1: AllCopy); StorageDead(_5); _0 = copy (*_1); goto -> bb3; diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-abort.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-abort.mir index ba6ce0ee5286..0dd2125dbe0d 100644 --- a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-abort.mir @@ -8,9 +8,10 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { let _2: std::ptr::NonNull<[T]>; let mut _3: *mut [T]; let mut _4: *const [T]; - let _11: (); + let _12: (); + let mut _13: &std::alloc::Layout; scope 3 { - let _8: std::ptr::alignment::AlignmentEnum; + let _8: std::alloc::Layout; scope 4 { scope 12 (inlined Layout::size) { } @@ -26,15 +27,19 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } scope 18 (inlined ::deallocate) { let mut _9: *mut u8; + let mut _14: &std::alloc::Layout; scope 19 (inlined Layout::size) { } scope 20 (inlined NonNull::::as_ptr) { } scope 21 (inlined std::alloc::dealloc) { - let mut _10: usize; + let mut _11: usize; + let mut _15: &std::alloc::Layout; + let mut _16: &std::alloc::Layout; scope 22 (inlined Layout::size) { } scope 23 (inlined Layout::align) { + let mut _10: std::ptr::alignment::AlignmentEnum; scope 24 (inlined std::ptr::Alignment::as_usize) { } } @@ -63,6 +68,7 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } bb0: { + StorageLive(_8); StorageLive(_2); _2 = copy (((*_1).0: std::ptr::Unique<[T]>).0: std::ptr::NonNull<[T]>); StorageLive(_4); @@ -74,31 +80,45 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { bb1: { _6 = AlignOf(T); - StorageLive(_7); _7 = copy _6 as std::ptr::Alignment (Transmute); - _8 = move (_7.0: std::ptr::alignment::AlignmentEnum); - StorageDead(_7); + _8 = Layout { size: copy _5, align: copy _7 }; StorageDead(_6); StorageDead(_4); + StorageLive(_13); + // DBG: _13 = &_8; + StorageDead(_13); switchInt(copy _5) -> [0: bb4, otherwise: bb2]; } bb2: { + StorageLive(_14); + // DBG: _14 = &_8; + StorageDead(_14); StorageLive(_9); _9 = copy _3 as *mut u8 (PtrToPtr); + StorageLive(_15); + // DBG: _15 = &_8; + StorageDead(_15); + StorageLive(_11); + StorageLive(_16); + // DBG: _16 = &_8; StorageLive(_10); - _10 = discriminant(_8); - _11 = alloc::alloc::__rust_dealloc(move _9, move _5, move _10) -> [return: bb3, unwind unreachable]; + _10 = copy (_7.0: std::ptr::alignment::AlignmentEnum); + _11 = discriminant(_10); + StorageDead(_10); + StorageDead(_16); + _12 = alloc::alloc::__rust_dealloc(move _9, move _5, move _11) -> [return: bb3, unwind unreachable]; } bb3: { - StorageDead(_10); + StorageDead(_11); StorageDead(_9); goto -> bb4; } bb4: { StorageDead(_2); + StorageDead(_8); return; } } diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-unwind.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-unwind.mir index ba6ce0ee5286..0dd2125dbe0d 100644 --- a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-unwind.mir @@ -8,9 +8,10 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { let _2: std::ptr::NonNull<[T]>; let mut _3: *mut [T]; let mut _4: *const [T]; - let _11: (); + let _12: (); + let mut _13: &std::alloc::Layout; scope 3 { - let _8: std::ptr::alignment::AlignmentEnum; + let _8: std::alloc::Layout; scope 4 { scope 12 (inlined Layout::size) { } @@ -26,15 +27,19 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } scope 18 (inlined ::deallocate) { let mut _9: *mut u8; + let mut _14: &std::alloc::Layout; scope 19 (inlined Layout::size) { } scope 20 (inlined NonNull::::as_ptr) { } scope 21 (inlined std::alloc::dealloc) { - let mut _10: usize; + let mut _11: usize; + let mut _15: &std::alloc::Layout; + let mut _16: &std::alloc::Layout; scope 22 (inlined Layout::size) { } scope 23 (inlined Layout::align) { + let mut _10: std::ptr::alignment::AlignmentEnum; scope 24 (inlined std::ptr::Alignment::as_usize) { } } @@ -63,6 +68,7 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } bb0: { + StorageLive(_8); StorageLive(_2); _2 = copy (((*_1).0: std::ptr::Unique<[T]>).0: std::ptr::NonNull<[T]>); StorageLive(_4); @@ -74,31 +80,45 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { bb1: { _6 = AlignOf(T); - StorageLive(_7); _7 = copy _6 as std::ptr::Alignment (Transmute); - _8 = move (_7.0: std::ptr::alignment::AlignmentEnum); - StorageDead(_7); + _8 = Layout { size: copy _5, align: copy _7 }; StorageDead(_6); StorageDead(_4); + StorageLive(_13); + // DBG: _13 = &_8; + StorageDead(_13); switchInt(copy _5) -> [0: bb4, otherwise: bb2]; } bb2: { + StorageLive(_14); + // DBG: _14 = &_8; + StorageDead(_14); StorageLive(_9); _9 = copy _3 as *mut u8 (PtrToPtr); + StorageLive(_15); + // DBG: _15 = &_8; + StorageDead(_15); + StorageLive(_11); + StorageLive(_16); + // DBG: _16 = &_8; StorageLive(_10); - _10 = discriminant(_8); - _11 = alloc::alloc::__rust_dealloc(move _9, move _5, move _10) -> [return: bb3, unwind unreachable]; + _10 = copy (_7.0: std::ptr::alignment::AlignmentEnum); + _11 = discriminant(_10); + StorageDead(_10); + StorageDead(_16); + _12 = alloc::alloc::__rust_dealloc(move _9, move _5, move _11) -> [return: bb3, unwind unreachable]; } bb3: { - StorageDead(_10); + StorageDead(_11); StorageDead(_9); goto -> bb4; } bb4: { StorageDead(_2); + StorageDead(_8); return; } } diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-abort.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-abort.mir index ba6ce0ee5286..0dd2125dbe0d 100644 --- a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-abort.mir @@ -8,9 +8,10 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { let _2: std::ptr::NonNull<[T]>; let mut _3: *mut [T]; let mut _4: *const [T]; - let _11: (); + let _12: (); + let mut _13: &std::alloc::Layout; scope 3 { - let _8: std::ptr::alignment::AlignmentEnum; + let _8: std::alloc::Layout; scope 4 { scope 12 (inlined Layout::size) { } @@ -26,15 +27,19 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } scope 18 (inlined ::deallocate) { let mut _9: *mut u8; + let mut _14: &std::alloc::Layout; scope 19 (inlined Layout::size) { } scope 20 (inlined NonNull::::as_ptr) { } scope 21 (inlined std::alloc::dealloc) { - let mut _10: usize; + let mut _11: usize; + let mut _15: &std::alloc::Layout; + let mut _16: &std::alloc::Layout; scope 22 (inlined Layout::size) { } scope 23 (inlined Layout::align) { + let mut _10: std::ptr::alignment::AlignmentEnum; scope 24 (inlined std::ptr::Alignment::as_usize) { } } @@ -63,6 +68,7 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } bb0: { + StorageLive(_8); StorageLive(_2); _2 = copy (((*_1).0: std::ptr::Unique<[T]>).0: std::ptr::NonNull<[T]>); StorageLive(_4); @@ -74,31 +80,45 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { bb1: { _6 = AlignOf(T); - StorageLive(_7); _7 = copy _6 as std::ptr::Alignment (Transmute); - _8 = move (_7.0: std::ptr::alignment::AlignmentEnum); - StorageDead(_7); + _8 = Layout { size: copy _5, align: copy _7 }; StorageDead(_6); StorageDead(_4); + StorageLive(_13); + // DBG: _13 = &_8; + StorageDead(_13); switchInt(copy _5) -> [0: bb4, otherwise: bb2]; } bb2: { + StorageLive(_14); + // DBG: _14 = &_8; + StorageDead(_14); StorageLive(_9); _9 = copy _3 as *mut u8 (PtrToPtr); + StorageLive(_15); + // DBG: _15 = &_8; + StorageDead(_15); + StorageLive(_11); + StorageLive(_16); + // DBG: _16 = &_8; StorageLive(_10); - _10 = discriminant(_8); - _11 = alloc::alloc::__rust_dealloc(move _9, move _5, move _10) -> [return: bb3, unwind unreachable]; + _10 = copy (_7.0: std::ptr::alignment::AlignmentEnum); + _11 = discriminant(_10); + StorageDead(_10); + StorageDead(_16); + _12 = alloc::alloc::__rust_dealloc(move _9, move _5, move _11) -> [return: bb3, unwind unreachable]; } bb3: { - StorageDead(_10); + StorageDead(_11); StorageDead(_9); goto -> bb4; } bb4: { StorageDead(_2); + StorageDead(_8); return; } } diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-unwind.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-unwind.mir index ba6ce0ee5286..0dd2125dbe0d 100644 --- a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-unwind.mir @@ -8,9 +8,10 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { let _2: std::ptr::NonNull<[T]>; let mut _3: *mut [T]; let mut _4: *const [T]; - let _11: (); + let _12: (); + let mut _13: &std::alloc::Layout; scope 3 { - let _8: std::ptr::alignment::AlignmentEnum; + let _8: std::alloc::Layout; scope 4 { scope 12 (inlined Layout::size) { } @@ -26,15 +27,19 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } scope 18 (inlined ::deallocate) { let mut _9: *mut u8; + let mut _14: &std::alloc::Layout; scope 19 (inlined Layout::size) { } scope 20 (inlined NonNull::::as_ptr) { } scope 21 (inlined std::alloc::dealloc) { - let mut _10: usize; + let mut _11: usize; + let mut _15: &std::alloc::Layout; + let mut _16: &std::alloc::Layout; scope 22 (inlined Layout::size) { } scope 23 (inlined Layout::align) { + let mut _10: std::ptr::alignment::AlignmentEnum; scope 24 (inlined std::ptr::Alignment::as_usize) { } } @@ -63,6 +68,7 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } bb0: { + StorageLive(_8); StorageLive(_2); _2 = copy (((*_1).0: std::ptr::Unique<[T]>).0: std::ptr::NonNull<[T]>); StorageLive(_4); @@ -74,31 +80,45 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { bb1: { _6 = AlignOf(T); - StorageLive(_7); _7 = copy _6 as std::ptr::Alignment (Transmute); - _8 = move (_7.0: std::ptr::alignment::AlignmentEnum); - StorageDead(_7); + _8 = Layout { size: copy _5, align: copy _7 }; StorageDead(_6); StorageDead(_4); + StorageLive(_13); + // DBG: _13 = &_8; + StorageDead(_13); switchInt(copy _5) -> [0: bb4, otherwise: bb2]; } bb2: { + StorageLive(_14); + // DBG: _14 = &_8; + StorageDead(_14); StorageLive(_9); _9 = copy _3 as *mut u8 (PtrToPtr); + StorageLive(_15); + // DBG: _15 = &_8; + StorageDead(_15); + StorageLive(_11); + StorageLive(_16); + // DBG: _16 = &_8; StorageLive(_10); - _10 = discriminant(_8); - _11 = alloc::alloc::__rust_dealloc(move _9, move _5, move _10) -> [return: bb3, unwind unreachable]; + _10 = copy (_7.0: std::ptr::alignment::AlignmentEnum); + _11 = discriminant(_10); + StorageDead(_10); + StorageDead(_16); + _12 = alloc::alloc::__rust_dealloc(move _9, move _5, move _11) -> [return: bb3, unwind unreachable]; } bb3: { - StorageDead(_10); + StorageDead(_11); StorageDead(_9); goto -> bb4; } bb4: { StorageDead(_2); + StorageDead(_8); return; } } diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.rs b/tests/mir-opt/pre-codegen/drop_boxed_slice.rs index 9ceba9444b8d..c291366a694e 100644 --- a/tests/mir-opt/pre-codegen/drop_boxed_slice.rs +++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.rs @@ -11,7 +11,7 @@ pub unsafe fn generic_in_place(ptr: *mut Box<[T]>) { // CHECK: [[SIZE:_.+]] = std::intrinsics::size_of_val::<[T]> // CHECK: [[ALIGN:_.+]] = AlignOf(T); // CHECK: [[B:_.+]] = copy [[ALIGN]] as std::ptr::Alignment (Transmute); - // CHECK: [[C:_.+]] = move ([[B]].0: std::ptr::alignment::AlignmentEnum); + // CHECK: [[C:_.+]] = copy ([[B]].0: std::ptr::alignment::AlignmentEnum); // CHECK: [[D:_.+]] = discriminant([[C]]); // CHECK: = alloc::alloc::__rust_dealloc({{.+}}, move [[SIZE]], move [[D]]) -> std::ptr::drop_in_place(ptr) diff --git a/tests/mir-opt/pre-codegen/loops.filter_mapped.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/loops.filter_mapped.PreCodegen.after.mir index 75e8cb1d8618..8f30ad30fccd 100644 --- a/tests/mir-opt/pre-codegen/loops.filter_mapped.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/loops.filter_mapped.PreCodegen.after.mir @@ -6,20 +6,20 @@ fn filter_mapped(_1: impl Iterator, _2: impl Fn(T) -> Option) -> () let mut _0: (); let mut _3: std::iter::FilterMap, impl Fn(T) -> Option>; let mut _4: std::iter::FilterMap, impl Fn(T) -> Option>; - let mut _5: &mut std::iter::FilterMap, impl Fn(T) -> Option>; - let mut _8: std::option::Option; - let mut _9: isize; - let _11: (); + let mut _7: std::option::Option; + let mut _8: isize; + let _10: (); + let mut _11: &mut std::iter::FilterMap, impl Fn(T) -> Option>; scope 1 { debug iter => _4; - let _10: U; + let _9: U; scope 2 { - debug x => _10; + debug x => _9; } scope 4 (inlined , impl Fn(T) -> Option> as Iterator>::next) { - debug self => _5; - let mut _6: &mut impl Iterator; - let mut _7: &mut impl Fn(T) -> Option; + debug self => _11; + let mut _5: &mut impl Iterator; + let mut _6: &mut impl Fn(T) -> Option; } } scope 3 (inlined , impl Fn(T) -> Option> as IntoIterator>::into_iter) { @@ -37,24 +37,24 @@ fn filter_mapped(_1: impl Iterator, _2: impl Fn(T) -> Option) -> () } bb2: { - StorageLive(_8); - _5 = &mut _4; - StorageLive(_6); - _6 = &mut (_4.0: impl Iterator); StorageLive(_7); - _7 = &mut (_4.1: impl Fn(T) -> Option); - _8 = as Iterator>::find_map:: Option>(move _6, move _7) -> [return: bb3, unwind: bb9]; + // DBG: _11 = &_4; + StorageLive(_5); + _5 = &mut (_4.0: impl Iterator); + StorageLive(_6); + _6 = &mut (_4.1: impl Fn(T) -> Option); + _7 = as Iterator>::find_map:: Option>(move _5, move _6) -> [return: bb3, unwind: bb9]; } bb3: { - StorageDead(_7); StorageDead(_6); - _9 = discriminant(_8); - switchInt(move _9) -> [0: bb4, 1: bb6, otherwise: bb8]; + StorageDead(_5); + _8 = discriminant(_7); + switchInt(move _8) -> [0: bb4, 1: bb6, otherwise: bb8]; } bb4: { - StorageDead(_8); + StorageDead(_7); drop(_4) -> [return: bb5, unwind continue]; } @@ -64,12 +64,12 @@ fn filter_mapped(_1: impl Iterator, _2: impl Fn(T) -> Option) -> () } bb6: { - _10 = move ((_8 as Some).0: U); - _11 = opaque::(move _10) -> [return: bb7, unwind: bb9]; + _9 = move ((_7 as Some).0: U); + _10 = opaque::(move _9) -> [return: bb7, unwind: bb9]; } bb7: { - StorageDead(_8); + StorageDead(_7); goto -> bb2; } diff --git a/tests/mir-opt/pre-codegen/loops.int_range.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/loops.int_range.PreCodegen.after.mir index 154cbd3791cb..b1af3141ee61 100644 --- a/tests/mir-opt/pre-codegen/loops.int_range.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/loops.int_range.PreCodegen.after.mir @@ -5,32 +5,31 @@ fn int_range(_1: usize, _2: usize) -> () { debug end => _2; let mut _0: (); let mut _3: std::ops::Range; - let mut _4: std::ops::Range; - let mut _5: &mut std::ops::Range; - let mut _13: std::option::Option; - let _15: (); + let mut _9: std::option::Option; + let _11: (); + let mut _12: &mut std::ops::Range; scope 1 { - debug iter => _4; - let _14: usize; + debug iter => _3; + let _10: usize; scope 2 { - debug i => _14; + debug i => _10; } scope 4 (inlined iter::range::>::next) { - debug self => _5; + debug self => _12; scope 5 (inlined as iter::range::RangeIteratorImpl>::spec_next) { - debug self => _5; - let mut _6: &usize; - let mut _7: &usize; - let mut _10: bool; - let _11: usize; - let mut _12: usize; + debug self => _12; + let mut _6: bool; + let _7: usize; + let mut _8: usize; + let mut _13: &usize; + let mut _14: &usize; scope 6 { - debug old => _11; + debug old => _7; scope 8 (inlined ::forward_unchecked) { - debug start => _11; + debug start => _7; debug n => const 1_usize; scope 9 (inlined #[track_caller] core::num::::unchecked_add) { - debug self => _11; + debug self => _7; debug rhs => const 1_usize; scope 10 (inlined core::ub_checks::check_language_ub) { scope 11 (inlined core::ub_checks::check_language_ub::runtime) { @@ -40,10 +39,10 @@ fn int_range(_1: usize, _2: usize) -> () { } } scope 7 (inlined std::cmp::impls::::lt) { - debug self => _6; - debug other => _7; - let mut _8: usize; - let mut _9: usize; + debug self => _13; + debug other => _14; + let mut _4: usize; + let mut _5: usize; } } } @@ -54,54 +53,51 @@ fn int_range(_1: usize, _2: usize) -> () { bb0: { _3 = std::ops::Range:: { start: copy _1, end: copy _2 }; - StorageLive(_4); - _4 = copy _3; goto -> bb1; } bb1: { - StorageLive(_13); - _5 = &mut _4; - StorageLive(_10); - StorageLive(_6); - _6 = &(_4.0: usize); - StorageLive(_7); - _7 = &(_4.1: usize); - StorageLive(_8); - _8 = copy (_4.0: usize); StorageLive(_9); - _9 = copy (_4.1: usize); - _10 = Lt(move _8, move _9); - StorageDead(_9); - StorageDead(_8); - switchInt(move _10) -> [0: bb2, otherwise: bb3]; + // DBG: _12 = &_3; + StorageLive(_6); + StorageLive(_13); + // DBG: _13 = &(_3.0: usize); + StorageLive(_14); + // DBG: _14 = &(_3.1: usize); + StorageLive(_4); + _4 = copy (_3.0: usize); + StorageLive(_5); + _5 = copy (_3.1: usize); + _6 = Lt(move _4, move _5); + StorageDead(_5); + StorageDead(_4); + switchInt(move _6) -> [0: bb2, otherwise: bb3]; } bb2: { - StorageDead(_7); - StorageDead(_6); - StorageDead(_10); + StorageDead(_14); StorageDead(_13); - StorageDead(_4); + StorageDead(_6); + StorageDead(_9); return; } bb3: { - StorageDead(_7); + StorageDead(_14); + StorageDead(_13); + _7 = copy (_3.0: usize); + StorageLive(_8); + _8 = AddUnchecked(copy _7, const 1_usize); + (_3.0: usize) = move _8; + StorageDead(_8); + _9 = Option::::Some(copy _7); StorageDead(_6); - _11 = copy (_4.0: usize); - StorageLive(_12); - _12 = AddUnchecked(copy _11, const 1_usize); - (_4.0: usize) = move _12; - StorageDead(_12); - _13 = Option::::Some(copy _11); - StorageDead(_10); - _14 = copy ((_13 as Some).0: usize); - _15 = opaque::(move _14) -> [return: bb4, unwind continue]; + _10 = copy ((_9 as Some).0: usize); + _11 = opaque::(move _10) -> [return: bb4, unwind continue]; } bb4: { - StorageDead(_13); + StorageDead(_9); goto -> bb1; } } diff --git a/tests/mir-opt/pre-codegen/loops.mapped.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/loops.mapped.PreCodegen.after.mir index d22ea54004c9..406c96fc32f4 100644 --- a/tests/mir-opt/pre-codegen/loops.mapped.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/loops.mapped.PreCodegen.after.mir @@ -6,32 +6,32 @@ fn mapped(_1: impl Iterator, _2: impl Fn(T) -> U) -> () { let mut _0: (); let mut _3: std::iter::Map, impl Fn(T) -> U>; let mut _4: std::iter::Map, impl Fn(T) -> U>; - let mut _5: &mut std::iter::Map, impl Fn(T) -> U>; - let mut _13: std::option::Option; - let _15: (); + let mut _12: std::option::Option; + let _14: (); + let mut _15: &mut std::iter::Map, impl Fn(T) -> U>; scope 1 { debug iter => _4; - let _14: U; + let _13: U; scope 2 { - debug x => _14; + debug x => _13; } scope 4 (inlined , impl Fn(T) -> U> as Iterator>::next) { - debug self => _5; - let mut _6: &mut impl Iterator; - let mut _7: std::option::Option; - let mut _8: &mut impl Fn(T) -> U; + debug self => _15; + let mut _5: &mut impl Iterator; + let mut _6: std::option::Option; + let mut _7: &mut impl Fn(T) -> U; scope 5 (inlined Option::::map:: U>) { - debug self => _7; - debug f => _8; - let mut _9: isize; - let _10: T; - let mut _11: (T,); - let mut _12: U; + debug self => _6; + debug f => _7; + let mut _8: isize; + let _9: T; + let mut _10: (T,); + let mut _11: U; scope 6 { - debug x => _10; + debug x => _9; scope 7 (inlined ops::function::impls:: for &mut impl Fn(T) -> U>::call_once) { - debug self => _8; - debug args => _11; + debug self => _7; + debug args => _10; } } } @@ -52,30 +52,30 @@ fn mapped(_1: impl Iterator, _2: impl Fn(T) -> U) -> () { } bb2: { - StorageLive(_13); - _5 = &mut _4; - StorageLive(_8); + StorageLive(_12); + // DBG: _15 = &_4; StorageLive(_7); StorageLive(_6); - _6 = &mut (_4.0: impl Iterator); - _7 = as Iterator>::next(move _6) -> [return: bb3, unwind: bb10]; + StorageLive(_5); + _5 = &mut (_4.0: impl Iterator); + _6 = as Iterator>::next(move _5) -> [return: bb3, unwind: bb10]; } bb3: { - StorageDead(_6); - _8 = &mut (_4.1: impl Fn(T) -> U); + StorageDead(_5); + _7 = &mut (_4.1: impl Fn(T) -> U); + StorageLive(_8); StorageLive(_9); - StorageLive(_10); - _9 = discriminant(_7); - switchInt(move _9) -> [0: bb4, 1: bb6, otherwise: bb9]; + _8 = discriminant(_6); + switchInt(move _8) -> [0: bb4, 1: bb6, otherwise: bb9]; } bb4: { - StorageDead(_10); StorageDead(_9); - StorageDead(_7); StorageDead(_8); - StorageDead(_13); + StorageDead(_6); + StorageDead(_7); + StorageDead(_12); drop(_4) -> [return: bb5, unwind continue]; } @@ -85,27 +85,27 @@ fn mapped(_1: impl Iterator, _2: impl Fn(T) -> U) -> () { } bb6: { - _10 = move ((_7 as Some).0: T); - StorageLive(_12); + _9 = move ((_6 as Some).0: T); StorageLive(_11); - _11 = (copy _10,); - _12 = U as FnMut<(T,)>>::call_mut(move _8, move _11) -> [return: bb7, unwind: bb10]; + StorageLive(_10); + _10 = (copy _9,); + _11 = U as FnMut<(T,)>>::call_mut(move _7, move _10) -> [return: bb7, unwind: bb10]; } bb7: { - StorageDead(_11); - _13 = Option::::Some(move _12); - StorageDead(_12); StorageDead(_10); + _12 = Option::::Some(move _11); + StorageDead(_11); StorageDead(_9); - StorageDead(_7); StorageDead(_8); - _14 = move ((_13 as Some).0: U); - _15 = opaque::(move _14) -> [return: bb8, unwind: bb10]; + StorageDead(_6); + StorageDead(_7); + _13 = move ((_12 as Some).0: U); + _14 = opaque::(move _13) -> [return: bb8, unwind: bb10]; } bb8: { - StorageDead(_13); + StorageDead(_12); goto -> bb2; } diff --git a/tests/mir-opt/pre-codegen/loops.rs b/tests/mir-opt/pre-codegen/loops.rs index d0b8cc8db7a9..952dd8cac60f 100644 --- a/tests/mir-opt/pre-codegen/loops.rs +++ b/tests/mir-opt/pre-codegen/loops.rs @@ -1,5 +1,6 @@ // skip-filecheck //@ compile-flags: -O -Zmir-opt-level=2 -g +//@ ignore-std-debug-assertions (debug assertions result in different inlines) //@ needs-unwind #![crate_type = "lib"] diff --git a/tests/mir-opt/pre-codegen/loops.vec_move.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/loops.vec_move.PreCodegen.after.mir index e537dd6a28ef..03cdc221c308 100644 --- a/tests/mir-opt/pre-codegen/loops.vec_move.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/loops.vec_move.PreCodegen.after.mir @@ -3,72 +3,354 @@ fn vec_move(_1: Vec) -> () { debug v => _1; let mut _0: (); - let mut _2: std::vec::IntoIter; - let mut _3: std::vec::IntoIter; - let mut _4: &mut std::vec::IntoIter; - let mut _5: std::option::Option; - let mut _6: isize; - let _8: (); + let mut _22: std::vec::IntoIter; + let mut _23: std::vec::IntoIter; + let mut _24: &mut std::vec::IntoIter; + let mut _25: std::option::Option; + let mut _26: isize; + let _28: (); scope 1 { - debug iter => _3; - let _7: impl Sized; + debug iter => _23; + let _27: impl Sized; scope 2 { - debug x => _7; + debug x => _27; + } + } + scope 3 (inlined as IntoIterator>::into_iter) { + debug self => _1; + let _2: std::mem::ManuallyDrop>; + let mut _3: *const std::alloc::Global; + let mut _8: usize; + let mut _10: *mut impl Sized; + let mut _11: *const impl Sized; + let mut _12: usize; + let _29: &std::vec::Vec; + let mut _30: &std::mem::ManuallyDrop>; + let mut _31: &alloc::raw_vec::RawVec; + let mut _32: &std::mem::ManuallyDrop>; + let _33: &std::vec::Vec; + let mut _34: &std::mem::ManuallyDrop>; + let _35: &std::vec::Vec; + let mut _36: &std::mem::ManuallyDrop>; + let mut _37: &alloc::raw_vec::RawVec; + let mut _38: &std::mem::ManuallyDrop>; + scope 4 { + debug me => _2; + scope 5 { + debug alloc => const ManuallyDrop:: {{ value: std::alloc::Global }}; + let _6: std::ptr::NonNull; + scope 6 { + debug buf => _6; + let _7: *mut impl Sized; + scope 7 { + debug begin => _7; + scope 8 { + debug end => _11; + let _20: usize; + scope 9 { + debug cap => _20; + } + scope 39 (inlined > as Deref>::deref) { + debug self => _38; + } + scope 40 (inlined alloc::raw_vec::RawVec::::capacity) { + debug self => _37; + let mut _19: usize; + let mut _42: &alloc::raw_vec::RawVecInner; + scope 41 (inlined std::mem::size_of::) { + } + scope 42 (inlined alloc::raw_vec::RawVecInner::capacity) { + debug self => _42; + debug elem_size => _19; + let mut _21: core::num::niche_types::UsizeNoHighBit; + scope 43 (inlined core::num::niche_types::UsizeNoHighBit::as_inner) { + debug self => _21; + } + } + } + } + scope 25 (inlined > as Deref>::deref) { + debug self => _34; + } + scope 26 (inlined Vec::::len) { + debug self => _33; + let mut _13: bool; + scope 27 { + } + } + scope 28 (inlined std::ptr::mut_ptr::::wrapping_byte_add) { + debug self => _7; + debug count => _12; + let mut _14: *mut u8; + let mut _18: *mut u8; + scope 29 (inlined std::ptr::mut_ptr::::cast::) { + debug self => _7; + } + scope 30 (inlined std::ptr::mut_ptr::::wrapping_add) { + debug self => _14; + debug count => _12; + let mut _15: isize; + scope 31 (inlined std::ptr::mut_ptr::::wrapping_offset) { + debug self => _14; + debug count => _15; + let mut _16: *const u8; + let mut _17: *const u8; + } + } + scope 32 (inlined std::ptr::mut_ptr::::with_metadata_of::) { + debug self => _18; + debug meta => _5; + scope 33 (inlined std::ptr::metadata::) { + debug ptr => _5; + } + scope 34 (inlined std::ptr::from_raw_parts_mut::) { + } + } + } + scope 35 (inlined > as Deref>::deref) { + debug self => _36; + } + scope 36 (inlined Vec::::len) { + debug self => _35; + let mut _9: bool; + scope 37 { + } + } + scope 38 (inlined #[track_caller] std::ptr::mut_ptr::::add) { + debug self => _7; + debug count => _8; + } + } + scope 24 (inlined NonNull::::as_ptr) { + debug self => _6; + } + } + scope 17 (inlined > as Deref>::deref) { + debug self => _32; + } + scope 18 (inlined alloc::raw_vec::RawVec::::non_null) { + debug self => _31; + let mut _41: &alloc::raw_vec::RawVecInner; + scope 19 (inlined alloc::raw_vec::RawVecInner::non_null::) { + let mut _4: std::ptr::NonNull; + scope 20 (inlined Unique::::cast::) { + scope 21 (inlined NonNull::::cast::) { + let mut _5: *const impl Sized; + scope 22 (inlined NonNull::::as_ptr) { + } + } + } + scope 23 (inlined Unique::::as_non_null_ptr) { + } + } + } + } + scope 11 (inlined > as Deref>::deref) { + debug self => _30; + } + scope 12 (inlined Vec::::allocator) { + debug self => _29; + let mut _39: &alloc::raw_vec::RawVec; + scope 13 (inlined alloc::raw_vec::RawVec::::allocator) { + let mut _40: &alloc::raw_vec::RawVecInner; + scope 14 (inlined alloc::raw_vec::RawVecInner::allocator) { + } + } + } + scope 15 (inlined #[track_caller] std::ptr::read::) { + debug src => _3; + } + scope 16 (inlined ManuallyDrop::::new) { + debug value => const std::alloc::Global; + } + } + scope 10 (inlined ManuallyDrop::>::new) { + debug value => _1; } } bb0: { + StorageLive(_22); + StorageLive(_29); + StorageLive(_6); + StorageLive(_7); + StorageLive(_33); + StorageLive(_11); + StorageLive(_35); + StorageLive(_20); + StorageLive(_5); + StorageLive(_4); + StorageLive(_17); StorageLive(_2); - _2 = as IntoIterator>::into_iter(move _1) -> [return: bb1, unwind continue]; + _2 = ManuallyDrop::> { value: copy _1 }; + StorageLive(_3); + StorageLive(_30); + // DBG: _30 = &_2; + // DBG: _29 = &(_2.0: std::vec::Vec); + StorageDead(_30); + StorageLive(_39); + // DBG: _39 = &((_2.0: std::vec::Vec).0: alloc::raw_vec::RawVec); + StorageLive(_40); + // DBG: _40 = &(((_2.0: std::vec::Vec).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner); + StorageDead(_40); + StorageDead(_39); + _3 = &raw const ((((_2.0: std::vec::Vec).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).2: std::alloc::Global); + StorageDead(_3); + StorageLive(_31); + StorageLive(_32); + // DBG: _32 = &_2; + StorageDead(_32); + // DBG: _31 = &((_2.0: std::vec::Vec).0: alloc::raw_vec::RawVec); + StorageLive(_41); + // DBG: _41 = &(((_2.0: std::vec::Vec).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner); + _4 = copy (((((_2.0: std::vec::Vec).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).0: std::ptr::Unique).0: std::ptr::NonNull); + _5 = copy _4 as *const impl Sized (Transmute); + _6 = NonNull:: { pointer: copy _5 }; + StorageDead(_41); + StorageDead(_31); + _7 = copy _4 as *mut impl Sized (Transmute); + switchInt(const ::IS_ZST) -> [0: bb1, otherwise: bb2]; } bb1: { - StorageLive(_3); - _3 = move _2; - goto -> bb2; + StorageLive(_10); + StorageLive(_8); + StorageLive(_36); + // DBG: _36 = &_2; + // DBG: _35 = &(_2.0: std::vec::Vec); + StorageDead(_36); + _8 = copy ((_2.0: std::vec::Vec).1: usize); + StorageLive(_9); + _9 = Le(copy _8, const ::MAX_SLICE_LEN); + assume(move _9); + StorageDead(_9); + _10 = Offset(copy _7, copy _8); + _11 = copy _10 as *const impl Sized (PtrToPtr); + StorageDead(_8); + StorageDead(_10); + goto -> bb4; } bb2: { - StorageLive(_5); - _4 = &mut _3; - _5 = as Iterator>::next(move _4) -> [return: bb3, unwind: bb9]; + StorageLive(_12); + StorageLive(_34); + // DBG: _34 = &_2; + // DBG: _33 = &(_2.0: std::vec::Vec); + StorageDead(_34); + _12 = copy ((_2.0: std::vec::Vec).1: usize); + StorageLive(_13); + _13 = Le(copy _12, const ::MAX_SLICE_LEN); + assume(move _13); + StorageDead(_13); + StorageLive(_18); + StorageLive(_14); + _14 = copy _4 as *mut u8 (Transmute); + StorageLive(_15); + _15 = copy _12 as isize (IntToInt); + StorageLive(_16); + _16 = copy _4 as *const u8 (Transmute); + _17 = arith_offset::(move _16, move _15) -> [return: bb3, unwind unreachable]; } bb3: { - _6 = discriminant(_5); - switchInt(move _6) -> [0: bb4, 1: bb6, otherwise: bb8]; + StorageDead(_16); + _18 = copy _17 as *mut u8 (PtrToPtr); + StorageDead(_15); + StorageDead(_14); + StorageDead(_18); + StorageDead(_12); + _11 = copy _17 as *const impl Sized (PtrToPtr); + goto -> bb4; } bb4: { - StorageDead(_5); - drop(_3) -> [return: bb5, unwind continue]; + StorageLive(_37); + StorageLive(_38); + // DBG: _38 = &_2; + StorageDead(_38); + // DBG: _37 = &((_2.0: std::vec::Vec).0: alloc::raw_vec::RawVec); + StorageLive(_42); + // DBG: _42 = &(((_2.0: std::vec::Vec).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner); + StorageLive(_19); + _19 = SizeOf(impl Sized); + switchInt(move _19) -> [0: bb5, otherwise: bb6]; } bb5: { - StorageDead(_3); - StorageDead(_2); - return; + _20 = const usize::MAX; + goto -> bb7; } bb6: { - _7 = move ((_5 as Some).0: impl Sized); - _8 = opaque::(move _7) -> [return: bb7, unwind: bb9]; + StorageLive(_21); + _21 = copy ((((_2.0: std::vec::Vec).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).1: core::num::niche_types::UsizeNoHighBit); + _20 = copy _21 as usize (Transmute); + StorageDead(_21); + goto -> bb7; } bb7: { + StorageDead(_19); + StorageDead(_42); + StorageDead(_37); + _22 = std::vec::IntoIter:: { buf: copy _6, phantom: const ZeroSized: PhantomData, cap: move _20, alloc: const ManuallyDrop:: {{ value: std::alloc::Global }}, ptr: copy _6, end: copy _11 }; + StorageDead(_2); + StorageDead(_17); + StorageDead(_4); StorageDead(_5); - goto -> bb2; + StorageDead(_20); + StorageDead(_35); + StorageDead(_11); + StorageDead(_33); + StorageDead(_7); + StorageDead(_6); + StorageDead(_29); + StorageLive(_23); + _23 = move _22; + goto -> bb8; } bb8: { + StorageLive(_25); + _24 = &mut _23; + _25 = as Iterator>::next(move _24) -> [return: bb9, unwind: bb15]; + } + + bb9: { + _26 = discriminant(_25); + switchInt(move _26) -> [0: bb10, 1: bb12, otherwise: bb14]; + } + + bb10: { + StorageDead(_25); + drop(_23) -> [return: bb11, unwind continue]; + } + + bb11: { + StorageDead(_23); + StorageDead(_22); + return; + } + + bb12: { + _27 = move ((_25 as Some).0: impl Sized); + _28 = opaque::(move _27) -> [return: bb13, unwind: bb15]; + } + + bb13: { + StorageDead(_25); + goto -> bb8; + } + + bb14: { unreachable; } - bb9 (cleanup): { - drop(_3) -> [return: bb10, unwind terminate(cleanup)]; + bb15 (cleanup): { + drop(_23) -> [return: bb16, unwind terminate(cleanup)]; } - bb10 (cleanup): { + bb16 (cleanup): { resume; } } diff --git a/tests/mir-opt/pre-codegen/slice_filter.variant_a-{closure#0}.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/slice_filter.variant_a-{closure#0}.PreCodegen.after.mir index cbdd194afd3a..9d4bacef954e 100644 --- a/tests/mir-opt/pre-codegen/slice_filter.variant_a-{closure#0}.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/slice_filter.variant_a-{closure#0}.PreCodegen.after.mir @@ -3,183 +3,164 @@ fn variant_a::{closure#0}(_1: &mut {closure@$DIR/slice_filter.rs:7:25: 7:39}, _2: &&(usize, usize, usize, usize)) -> bool { let mut _0: bool; let mut _3: &(usize, usize, usize, usize); - let _4: &usize; - let _5: &usize; - let _6: &usize; - let _7: &usize; - let mut _8: &&usize; - let _9: &usize; - let mut _10: &&usize; - let mut _13: bool; - let mut _14: &&usize; + let mut _6: bool; + let mut _9: bool; + let mut _10: bool; + let _13: &usize; + let _14: &usize; let _15: &usize; - let mut _16: &&usize; - let mut _19: bool; + let _16: &usize; + let mut _17: &&usize; + let mut _18: &&usize; + let mut _19: &&usize; let mut _20: &&usize; - let _21: &usize; + let mut _21: &&usize; let mut _22: &&usize; - let mut _23: bool; + let mut _23: &&usize; let mut _24: &&usize; - let _25: &usize; - let mut _26: &&usize; scope 1 { - debug a => _4; - debug b => _5; - debug c => _6; - debug d => _7; + debug a => _13; + debug b => _14; + debug c => _15; + debug d => _16; scope 2 (inlined std::cmp::impls::::le) { - debug self => _8; - debug other => _10; + debug self => _17; + debug other => _18; scope 3 (inlined std::cmp::impls::::le) { - debug self => _4; - debug other => _6; - let mut _11: usize; - let mut _12: usize; + debug self => _13; + debug other => _15; + let mut _4: usize; + let mut _5: usize; } } scope 4 (inlined std::cmp::impls::::le) { - debug self => _14; - debug other => _16; + debug self => _19; + debug other => _20; scope 5 (inlined std::cmp::impls::::le) { - debug self => _7; - debug other => _5; - let mut _17: usize; - let mut _18: usize; + debug self => _16; + debug other => _14; + let mut _7: usize; + let mut _8: usize; } } scope 6 (inlined std::cmp::impls::::le) { - debug self => _20; + debug self => _21; debug other => _22; scope 7 (inlined std::cmp::impls::::le) { - debug self => _6; - debug other => _4; + debug self => _15; + debug other => _13; } } scope 8 (inlined std::cmp::impls::::le) { - debug self => _24; - debug other => _26; + debug self => _23; + debug other => _24; scope 9 (inlined std::cmp::impls::::le) { - debug self => _5; - debug other => _7; - let mut _27: usize; - let mut _28: usize; + debug self => _14; + debug other => _16; + let mut _11: usize; + let mut _12: usize; } } } bb0: { _3 = copy (*_2); - _4 = &((*_3).0: usize); - _5 = &((*_3).1: usize); - _6 = &((*_3).2: usize); - _7 = &((*_3).3: usize); - StorageLive(_13); - StorageLive(_8); - _8 = &_4; - StorageLive(_10); - StorageLive(_9); - _9 = copy _6; - _10 = &_9; - _11 = copy ((*_3).0: usize); - _12 = copy ((*_3).2: usize); - _13 = Le(copy _11, copy _12); - switchInt(move _13) -> [0: bb1, otherwise: bb2]; + // DBG: _13 = &((*_3).0: usize); + // DBG: _14 = &((*_3).1: usize); + // DBG: _15 = &((*_3).2: usize); + // DBG: _16 = &((*_3).3: usize); + StorageLive(_6); + StorageLive(_17); + // DBG: _17 = &_13; + StorageLive(_18); + // DBG: _18 = &_15; + _4 = copy ((*_3).0: usize); + _5 = copy ((*_3).2: usize); + _6 = Le(copy _4, copy _5); + switchInt(move _6) -> [0: bb1, otherwise: bb2]; } bb1: { - StorageDead(_9); - StorageDead(_10); - StorageDead(_8); + StorageDead(_18); + StorageDead(_17); goto -> bb4; } bb2: { - StorageDead(_9); - StorageDead(_10); - StorageDead(_8); - StorageLive(_19); - StorageLive(_14); - _14 = &_7; - StorageLive(_16); - StorageLive(_15); - _15 = copy _5; - _16 = &_15; - StorageLive(_17); - _17 = copy ((*_3).3: usize); - StorageLive(_18); - _18 = copy ((*_3).1: usize); - _19 = Le(move _17, move _18); StorageDead(_18); StorageDead(_17); - switchInt(move _19) -> [0: bb3, otherwise: bb8]; + StorageLive(_9); + StorageLive(_19); + // DBG: _19 = &_16; + StorageLive(_20); + // DBG: _20 = &_14; + StorageLive(_7); + _7 = copy ((*_3).3: usize); + StorageLive(_8); + _8 = copy ((*_3).1: usize); + _9 = Le(move _7, move _8); + StorageDead(_8); + StorageDead(_7); + switchInt(move _9) -> [0: bb3, otherwise: bb8]; } bb3: { - StorageDead(_15); - StorageDead(_16); - StorageDead(_14); + StorageDead(_20); + StorageDead(_19); goto -> bb4; } bb4: { - StorageLive(_23); - StorageLive(_20); - _20 = &_6; - StorageLive(_22); + StorageLive(_10); StorageLive(_21); - _21 = copy _4; - _22 = &_21; - _23 = Le(copy _12, copy _11); - switchInt(move _23) -> [0: bb5, otherwise: bb6]; + // DBG: _21 = &_15; + StorageLive(_22); + // DBG: _22 = &_13; + _10 = Le(copy _5, copy _4); + switchInt(move _10) -> [0: bb5, otherwise: bb6]; } bb5: { - StorageDead(_21); StorageDead(_22); - StorageDead(_20); + StorageDead(_21); _0 = const false; goto -> bb7; } bb6: { - StorageDead(_21); StorageDead(_22); - StorageDead(_20); + StorageDead(_21); + StorageLive(_23); + // DBG: _23 = &_14; StorageLive(_24); - _24 = &_5; - StorageLive(_26); - StorageLive(_25); - _25 = copy _7; - _26 = &_25; - StorageLive(_27); - _27 = copy ((*_3).1: usize); - StorageLive(_28); - _28 = copy ((*_3).3: usize); - _0 = Le(move _27, move _28); - StorageDead(_28); - StorageDead(_27); - StorageDead(_25); - StorageDead(_26); + // DBG: _24 = &_16; + StorageLive(_11); + _11 = copy ((*_3).1: usize); + StorageLive(_12); + _12 = copy ((*_3).3: usize); + _0 = Le(move _11, move _12); + StorageDead(_12); + StorageDead(_11); StorageDead(_24); + StorageDead(_23); goto -> bb7; } bb7: { - StorageDead(_23); + StorageDead(_10); goto -> bb9; } bb8: { - StorageDead(_15); - StorageDead(_16); - StorageDead(_14); + StorageDead(_20); + StorageDead(_19); _0 = const true; goto -> bb9; } bb9: { - StorageDead(_19); - StorageDead(_13); + StorageDead(_9); + StorageDead(_6); return; } } diff --git a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir index 104987b0fdda..1b2e7b7163cd 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir @@ -51,6 +51,9 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { let mut _16: bool; let mut _20: usize; let _22: &T; + let mut _34: &std::ptr::NonNull; + let mut _35: &std::ptr::NonNull; + let mut _36: &std::ptr::NonNull; scope 29 { let _12: *const T; scope 30 { @@ -186,7 +189,11 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { bb5: { StorageLive(_16); + StorageLive(_34); + // DBG: _34 = &_11; + StorageLive(_35); _13 = copy _12 as std::ptr::NonNull (Transmute); + // DBG: _35 = &_13; StorageLive(_14); _14 = copy _11 as *mut T (Transmute); StorageLive(_15); @@ -198,6 +205,8 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { } bb6: { + StorageDead(_35); + StorageDead(_34); StorageDead(_16); StorageLive(_18); StorageLive(_17); @@ -210,6 +219,8 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { } bb7: { + StorageDead(_35); + StorageDead(_34); StorageDead(_16); StorageDead(_22); StorageDead(_13); @@ -255,10 +266,13 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { } bb13: { + StorageLive(_36); + // DBG: _36 = &_11; StorageLive(_21); _21 = copy _11 as *const T (Transmute); _22 = &(*_21); StorageDead(_21); + StorageDead(_36); _23 = Option::<&T>::Some(copy _22); StorageDead(_22); StorageDead(_13); diff --git a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir index 4d0e3548e7d6..c18dd01a189a 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir @@ -23,6 +23,9 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { let mut _15: bool; let mut _19: usize; let _21: &T; + let mut _27: &std::ptr::NonNull; + let mut _28: &std::ptr::NonNull; + let mut _29: &std::ptr::NonNull; scope 17 { let _11: *const T; scope 18 { @@ -148,7 +151,11 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { bb5: { StorageLive(_15); + StorageLive(_27); + // DBG: _27 = &_10; + StorageLive(_28); _12 = copy _11 as std::ptr::NonNull (Transmute); + // DBG: _28 = &_12; StorageLive(_13); _13 = copy _10 as *mut T (Transmute); StorageLive(_14); @@ -160,6 +167,8 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb6: { + StorageDead(_28); + StorageDead(_27); StorageDead(_15); StorageLive(_17); StorageLive(_16); @@ -172,6 +181,8 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb7: { + StorageDead(_28); + StorageDead(_27); StorageDead(_15); StorageDead(_21); StorageDead(_12); @@ -213,10 +224,13 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb13: { + StorageLive(_29); + // DBG: _29 = &_10; StorageLive(_20); _20 = copy _10 as *const T (Transmute); _21 = &(*_20); StorageDead(_20); + StorageDead(_29); _22 = Option::<&T>::Some(copy _21); StorageDead(_21); StorageDead(_12); diff --git a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir index 2b5d8c27d710..6334c611430a 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir @@ -23,6 +23,9 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { let mut _15: bool; let mut _19: usize; let _21: &T; + let mut _27: &std::ptr::NonNull; + let mut _28: &std::ptr::NonNull; + let mut _29: &std::ptr::NonNull; scope 17 { let _11: *const T; scope 18 { @@ -148,7 +151,11 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { bb5: { StorageLive(_15); + StorageLive(_27); + // DBG: _27 = &_10; + StorageLive(_28); _12 = copy _11 as std::ptr::NonNull (Transmute); + // DBG: _28 = &_12; StorageLive(_13); _13 = copy _10 as *mut T (Transmute); StorageLive(_14); @@ -160,6 +167,8 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb6: { + StorageDead(_28); + StorageDead(_27); StorageDead(_15); StorageLive(_17); StorageLive(_16); @@ -172,6 +181,8 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb7: { + StorageDead(_28); + StorageDead(_27); StorageDead(_15); StorageDead(_21); StorageDead(_12); @@ -213,10 +224,13 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb13: { + StorageLive(_29); + // DBG: _29 = &_10; StorageLive(_20); _20 = copy _10 as *const T (Transmute); _21 = &(*_20); StorageDead(_20); + StorageDead(_29); _22 = Option::<&T>::Some(copy _21); StorageDead(_21); StorageDead(_12); diff --git a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir index 0adf268d766d..24c6618d31ca 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir @@ -7,19 +7,90 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { let mut _10: std::slice::Iter<'_, T>; let mut _11: std::iter::Rev>; let mut _12: std::iter::Rev>; - let mut _14: std::option::Option<&T>; - let mut _15: isize; - let mut _17: &impl Fn(&T); - let mut _18: (&T,); - let _19: (); + let mut _33: std::option::Option<&T>; + let mut _35: &impl Fn(&T); + let mut _36: (&T,); + let _37: (); scope 1 { debug iter => _12; - let _16: &T; + let _34: &T; scope 2 { - debug x => _16; + debug x => _34; } scope 18 (inlined > as Iterator>::next) { - let mut _13: &mut std::slice::Iter<'_, T>; + scope 19 (inlined as DoubleEndedIterator>::next_back) { + let mut _13: *const T; + let mut _18: bool; + let mut _19: *const T; + let _32: &T; + let mut _38: &std::ptr::NonNull; + let mut _39: &std::ptr::NonNull; + scope 20 { + let _14: std::ptr::NonNull; + let _20: usize; + scope 21 { + } + scope 22 { + scope 25 (inlined as PartialEq>::eq) { + let mut _15: std::ptr::NonNull; + let mut _16: *mut T; + let mut _17: *mut T; + scope 26 (inlined NonNull::::as_ptr) { + } + scope 27 (inlined NonNull::::as_ptr) { + } + } + } + scope 23 (inlined std::ptr::const_ptr::::addr) { + scope 24 (inlined std::ptr::const_ptr::::cast::<()>) { + } + } + } + scope 28 (inlined std::slice::Iter::<'_, T>::next_back_unchecked) { + let _26: std::ptr::NonNull; + let mut _40: &std::ptr::NonNull; + scope 29 (inlined std::slice::Iter::<'_, T>::pre_dec_end) { + let mut _21: *mut *const T; + let mut _22: *mut std::ptr::NonNull; + let mut _23: std::ptr::NonNull; + let mut _27: *mut *const T; + let mut _28: *mut usize; + let mut _29: usize; + let mut _30: usize; + scope 30 { + scope 31 { + } + scope 32 { + scope 35 (inlined NonNull::::sub) { + scope 36 (inlined #[track_caller] core::num::::unchecked_neg) { + scope 37 (inlined core::ub_checks::check_language_ub) { + scope 38 (inlined core::ub_checks::check_language_ub::runtime) { + } + } + } + scope 39 (inlined NonNull::::offset) { + let mut _24: *const T; + let mut _25: *const T; + scope 40 (inlined NonNull::::as_ptr) { + } + } + } + } + scope 33 (inlined std::ptr::mut_ptr::::cast::) { + } + scope 34 (inlined std::ptr::mut_ptr::::cast::>) { + } + } + } + scope 41 (inlined NonNull::::as_ref::<'_>) { + let _31: *const T; + scope 42 (inlined NonNull::::as_ptr) { + } + scope 43 (inlined std::ptr::mut_ptr::::cast_const) { + } + } + } + } } } scope 3 (inlined core::slice::::iter) { @@ -105,45 +176,145 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb4: { + StorageLive(_33); + StorageLive(_20); + StorageLive(_19); StorageLive(_14); - StorageLive(_13); - _13 = &mut (_12.0: std::slice::Iter<'_, T>); - _14 = as DoubleEndedIterator>::next_back(move _13) -> [return: bb5, unwind unreachable]; + StorageLive(_32); + StorageLive(_18); + switchInt(const ::IS_ZST) -> [0: bb5, otherwise: bb6]; } bb5: { + StorageLive(_13); + _13 = copy ((_12.0: std::slice::Iter<'_, T>).1: *const T); + _14 = copy _13 as std::ptr::NonNull (Transmute); StorageDead(_13); - _15 = discriminant(_14); - switchInt(move _15) -> [0: bb6, 1: bb8, otherwise: bb10]; + StorageLive(_38); + // DBG: _38 = &((_12.0: std::slice::Iter<'_, T>).0: std::ptr::NonNull); + StorageLive(_39); + // DBG: _39 = &_14; + StorageLive(_16); + StorageLive(_15); + _15 = copy ((_12.0: std::slice::Iter<'_, T>).0: std::ptr::NonNull); + _16 = copy _15 as *mut T (Transmute); + StorageDead(_15); + StorageLive(_17); + _17 = copy _14 as *mut T (Transmute); + _18 = Eq(copy _16, copy _17); + StorageDead(_17); + StorageDead(_16); + StorageDead(_39); + StorageDead(_38); + goto -> bb7; } bb6: { - StorageDead(_14); - StorageDead(_12); - drop(_2) -> [return: bb7, unwind unreachable]; + _19 = copy ((_12.0: std::slice::Iter<'_, T>).1: *const T); + _20 = copy _19 as usize (Transmute); + _18 = Eq(copy _20, const 0_usize); + goto -> bb7; } bb7: { - return; + switchInt(move _18) -> [0: bb8, otherwise: bb15]; } bb8: { - _16 = copy ((_14 as Some).0: &T); - StorageLive(_17); - _17 = &_2; - StorageLive(_18); - _18 = (copy _16,); - _19 = >::call(move _17, move _18) -> [return: bb9, unwind unreachable]; + StorageLive(_26); + StorageLive(_40); + StorageLive(_28); + StorageLive(_22); + StorageLive(_23); + switchInt(const ::IS_ZST) -> [0: bb9, otherwise: bb12]; } bb9: { - StorageDead(_18); - StorageDead(_17); - StorageDead(_14); - goto -> bb4; + StorageLive(_21); + _21 = &raw mut ((_12.0: std::slice::Iter<'_, T>).1: *const T); + _22 = copy _21 as *mut std::ptr::NonNull (PtrToPtr); + StorageDead(_21); + _23 = copy (*_22); + switchInt(const ::IS_ZST) -> [0: bb10, otherwise: bb11]; } bb10: { - unreachable; + StorageLive(_25); + StorageLive(_24); + _24 = copy _23 as *const T (Transmute); + _25 = Offset(copy _24, const -1_isize); + StorageDead(_24); + _23 = NonNull:: { pointer: copy _25 }; + StorageDead(_25); + goto -> bb11; + } + + bb11: { + (*_22) = move _23; + _26 = copy (*_22); + goto -> bb13; + } + + bb12: { + StorageLive(_27); + _27 = &raw mut ((_12.0: std::slice::Iter<'_, T>).1: *const T); + _28 = copy _27 as *mut usize (PtrToPtr); + StorageDead(_27); + StorageLive(_30); + StorageLive(_29); + _29 = copy (*_28); + _30 = SubUnchecked(move _29, const 1_usize); + StorageDead(_29); + (*_28) = move _30; + StorageDead(_30); + _26 = copy ((_12.0: std::slice::Iter<'_, T>).0: std::ptr::NonNull); + goto -> bb13; + } + + bb13: { + StorageDead(_23); + StorageDead(_22); + StorageDead(_28); + // DBG: _40 = &_26; + StorageLive(_31); + _31 = copy _26 as *const T (Transmute); + _32 = &(*_31); + StorageDead(_31); + StorageDead(_40); + StorageDead(_26); + _33 = Option::<&T>::Some(copy _32); + StorageDead(_18); + StorageDead(_32); + StorageDead(_14); + StorageDead(_19); + StorageDead(_20); + _34 = copy ((_33 as Some).0: &T); + StorageLive(_35); + _35 = &_2; + StorageLive(_36); + _36 = (copy _34,); + _37 = >::call(move _35, move _36) -> [return: bb14, unwind unreachable]; + } + + bb14: { + StorageDead(_36); + StorageDead(_35); + StorageDead(_33); + goto -> bb4; + } + + bb15: { + StorageDead(_18); + StorageDead(_32); + StorageDead(_14); + StorageDead(_19); + StorageDead(_20); + StorageDead(_33); + StorageDead(_12); + drop(_2) -> [return: bb16, unwind unreachable]; + } + + bb16: { + return; } } diff --git a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir index cb0d640d445b..e5c7bcca44b6 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir @@ -7,19 +7,90 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { let mut _10: std::slice::Iter<'_, T>; let mut _11: std::iter::Rev>; let mut _12: std::iter::Rev>; - let mut _14: std::option::Option<&T>; - let mut _15: isize; - let mut _17: &impl Fn(&T); - let mut _18: (&T,); - let _19: (); + let mut _33: std::option::Option<&T>; + let mut _35: &impl Fn(&T); + let mut _36: (&T,); + let _37: (); scope 1 { debug iter => _12; - let _16: &T; + let _34: &T; scope 2 { - debug x => _16; + debug x => _34; } scope 18 (inlined > as Iterator>::next) { - let mut _13: &mut std::slice::Iter<'_, T>; + scope 19 (inlined as DoubleEndedIterator>::next_back) { + let mut _13: *const T; + let mut _18: bool; + let mut _19: *const T; + let _32: &T; + let mut _38: &std::ptr::NonNull; + let mut _39: &std::ptr::NonNull; + scope 20 { + let _14: std::ptr::NonNull; + let _20: usize; + scope 21 { + } + scope 22 { + scope 25 (inlined as PartialEq>::eq) { + let mut _15: std::ptr::NonNull; + let mut _16: *mut T; + let mut _17: *mut T; + scope 26 (inlined NonNull::::as_ptr) { + } + scope 27 (inlined NonNull::::as_ptr) { + } + } + } + scope 23 (inlined std::ptr::const_ptr::::addr) { + scope 24 (inlined std::ptr::const_ptr::::cast::<()>) { + } + } + } + scope 28 (inlined std::slice::Iter::<'_, T>::next_back_unchecked) { + let _26: std::ptr::NonNull; + let mut _40: &std::ptr::NonNull; + scope 29 (inlined std::slice::Iter::<'_, T>::pre_dec_end) { + let mut _21: *mut *const T; + let mut _22: *mut std::ptr::NonNull; + let mut _23: std::ptr::NonNull; + let mut _27: *mut *const T; + let mut _28: *mut usize; + let mut _29: usize; + let mut _30: usize; + scope 30 { + scope 31 { + } + scope 32 { + scope 35 (inlined NonNull::::sub) { + scope 36 (inlined #[track_caller] core::num::::unchecked_neg) { + scope 37 (inlined core::ub_checks::check_language_ub) { + scope 38 (inlined core::ub_checks::check_language_ub::runtime) { + } + } + } + scope 39 (inlined NonNull::::offset) { + let mut _24: *const T; + let mut _25: *const T; + scope 40 (inlined NonNull::::as_ptr) { + } + } + } + } + scope 33 (inlined std::ptr::mut_ptr::::cast::) { + } + scope 34 (inlined std::ptr::mut_ptr::::cast::>) { + } + } + } + scope 41 (inlined NonNull::::as_ref::<'_>) { + let _31: *const T; + scope 42 (inlined NonNull::::as_ptr) { + } + scope 43 (inlined std::ptr::mut_ptr::::cast_const) { + } + } + } + } } } scope 3 (inlined core::slice::::iter) { @@ -105,53 +176,153 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb4: { + StorageLive(_33); + StorageLive(_20); + StorageLive(_19); StorageLive(_14); - StorageLive(_13); - _13 = &mut (_12.0: std::slice::Iter<'_, T>); - _14 = as DoubleEndedIterator>::next_back(move _13) -> [return: bb5, unwind: bb11]; + StorageLive(_32); + StorageLive(_18); + switchInt(const ::IS_ZST) -> [0: bb5, otherwise: bb6]; } bb5: { + StorageLive(_13); + _13 = copy ((_12.0: std::slice::Iter<'_, T>).1: *const T); + _14 = copy _13 as std::ptr::NonNull (Transmute); StorageDead(_13); - _15 = discriminant(_14); - switchInt(move _15) -> [0: bb6, 1: bb8, otherwise: bb10]; + StorageLive(_38); + // DBG: _38 = &((_12.0: std::slice::Iter<'_, T>).0: std::ptr::NonNull); + StorageLive(_39); + // DBG: _39 = &_14; + StorageLive(_16); + StorageLive(_15); + _15 = copy ((_12.0: std::slice::Iter<'_, T>).0: std::ptr::NonNull); + _16 = copy _15 as *mut T (Transmute); + StorageDead(_15); + StorageLive(_17); + _17 = copy _14 as *mut T (Transmute); + _18 = Eq(copy _16, copy _17); + StorageDead(_17); + StorageDead(_16); + StorageDead(_39); + StorageDead(_38); + goto -> bb7; } bb6: { - StorageDead(_14); - StorageDead(_12); - drop(_2) -> [return: bb7, unwind continue]; + _19 = copy ((_12.0: std::slice::Iter<'_, T>).1: *const T); + _20 = copy _19 as usize (Transmute); + _18 = Eq(copy _20, const 0_usize); + goto -> bb7; } bb7: { - return; + switchInt(move _18) -> [0: bb8, otherwise: bb17]; } bb8: { - _16 = copy ((_14 as Some).0: &T); - StorageLive(_17); - _17 = &_2; - StorageLive(_18); - _18 = (copy _16,); - _19 = >::call(move _17, move _18) -> [return: bb9, unwind: bb11]; + StorageLive(_26); + StorageLive(_40); + StorageLive(_28); + StorageLive(_22); + StorageLive(_23); + switchInt(const ::IS_ZST) -> [0: bb9, otherwise: bb12]; } bb9: { - StorageDead(_18); - StorageDead(_17); - StorageDead(_14); - goto -> bb4; + StorageLive(_21); + _21 = &raw mut ((_12.0: std::slice::Iter<'_, T>).1: *const T); + _22 = copy _21 as *mut std::ptr::NonNull (PtrToPtr); + StorageDead(_21); + _23 = copy (*_22); + switchInt(const ::IS_ZST) -> [0: bb10, otherwise: bb11]; } bb10: { - unreachable; + StorageLive(_25); + StorageLive(_24); + _24 = copy _23 as *const T (Transmute); + _25 = Offset(copy _24, const -1_isize); + StorageDead(_24); + _23 = NonNull:: { pointer: copy _25 }; + StorageDead(_25); + goto -> bb11; } - bb11 (cleanup): { - drop(_2) -> [return: bb12, unwind terminate(cleanup)]; + bb11: { + (*_22) = move _23; + _26 = copy (*_22); + goto -> bb13; } - bb12 (cleanup): { + bb12: { + StorageLive(_27); + _27 = &raw mut ((_12.0: std::slice::Iter<'_, T>).1: *const T); + _28 = copy _27 as *mut usize (PtrToPtr); + StorageDead(_27); + StorageLive(_30); + StorageLive(_29); + _29 = copy (*_28); + _30 = SubUnchecked(move _29, const 1_usize); + StorageDead(_29); + (*_28) = move _30; + StorageDead(_30); + _26 = copy ((_12.0: std::slice::Iter<'_, T>).0: std::ptr::NonNull); + goto -> bb13; + } + + bb13: { + StorageDead(_23); + StorageDead(_22); + StorageDead(_28); + // DBG: _40 = &_26; + StorageLive(_31); + _31 = copy _26 as *const T (Transmute); + _32 = &(*_31); + StorageDead(_31); + StorageDead(_40); + StorageDead(_26); + _33 = Option::<&T>::Some(copy _32); + StorageDead(_18); + StorageDead(_32); + StorageDead(_14); + StorageDead(_19); + StorageDead(_20); + _34 = copy ((_33 as Some).0: &T); + StorageLive(_35); + _35 = &_2; + StorageLive(_36); + _36 = (copy _34,); + _37 = >::call(move _35, move _36) -> [return: bb14, unwind: bb15]; + } + + bb14: { + StorageDead(_36); + StorageDead(_35); + StorageDead(_33); + goto -> bb4; + } + + bb15 (cleanup): { + drop(_2) -> [return: bb16, unwind terminate(cleanup)]; + } + + bb16 (cleanup): { resume; } + + bb17: { + StorageDead(_18); + StorageDead(_32); + StorageDead(_14); + StorageDead(_19); + StorageDead(_20); + StorageDead(_33); + StorageDead(_12); + drop(_2) -> [return: bb18, unwind continue]; + } + + bb18: { + return; + } } diff --git a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_generic_is_empty.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_generic_is_empty.PreCodegen.after.panic-abort.mir index 9b510380b10b..18a3d8e7c4ad 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_generic_is_empty.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_generic_is_empty.PreCodegen.after.panic-abort.mir @@ -6,6 +6,8 @@ fn slice_iter_generic_is_empty(_1: &std::slice::Iter<'_, T>) -> bool { scope 1 (inlined as ExactSizeIterator>::is_empty) { let mut _2: *const T; let mut _7: *const T; + let mut _9: &std::ptr::NonNull; + let mut _10: &std::ptr::NonNull; scope 2 { let _3: std::ptr::NonNull; let _8: usize; @@ -41,6 +43,10 @@ fn slice_iter_generic_is_empty(_1: &std::slice::Iter<'_, T>) -> bool { _2 = copy ((*_1).1: *const T); _3 = copy _2 as std::ptr::NonNull (Transmute); StorageDead(_2); + StorageLive(_9); + // DBG: _9 = &((*_1).0: std::ptr::NonNull); + StorageLive(_10); + // DBG: _10 = &_3; StorageLive(_5); StorageLive(_4); _4 = copy ((*_1).0: std::ptr::NonNull); @@ -51,6 +57,8 @@ fn slice_iter_generic_is_empty(_1: &std::slice::Iter<'_, T>) -> bool { _0 = Eq(copy _5, copy _6); StorageDead(_6); StorageDead(_5); + StorageDead(_10); + StorageDead(_9); goto -> bb3; } diff --git a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_generic_is_empty.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_generic_is_empty.PreCodegen.after.panic-unwind.mir index 9b510380b10b..18a3d8e7c4ad 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_generic_is_empty.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_generic_is_empty.PreCodegen.after.panic-unwind.mir @@ -6,6 +6,8 @@ fn slice_iter_generic_is_empty(_1: &std::slice::Iter<'_, T>) -> bool { scope 1 (inlined as ExactSizeIterator>::is_empty) { let mut _2: *const T; let mut _7: *const T; + let mut _9: &std::ptr::NonNull; + let mut _10: &std::ptr::NonNull; scope 2 { let _3: std::ptr::NonNull; let _8: usize; @@ -41,6 +43,10 @@ fn slice_iter_generic_is_empty(_1: &std::slice::Iter<'_, T>) -> bool { _2 = copy ((*_1).1: *const T); _3 = copy _2 as std::ptr::NonNull (Transmute); StorageDead(_2); + StorageLive(_9); + // DBG: _9 = &((*_1).0: std::ptr::NonNull); + StorageLive(_10); + // DBG: _10 = &_3; StorageLive(_5); StorageLive(_4); _4 = copy ((*_1).0: std::ptr::NonNull); @@ -51,6 +57,8 @@ fn slice_iter_generic_is_empty(_1: &std::slice::Iter<'_, T>) -> bool { _0 = Eq(copy _5, copy _6); StorageDead(_6); StorageDead(_5); + StorageDead(_10); + StorageDead(_9); goto -> bb3; } diff --git a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-abort.mir index 78f96bf41955..590d4ac2c838 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-abort.mir @@ -3,12 +3,199 @@ fn slice_iter_mut_next_back(_1: &mut std::slice::IterMut<'_, T>) -> Option<&mut T> { debug it => _1; let mut _0: std::option::Option<&mut T>; + scope 1 (inlined as DoubleEndedIterator>::next_back) { + let mut _2: *mut T; + let mut _7: bool; + let mut _8: *mut T; + let mut _21: &mut T; + let mut _22: &std::ptr::NonNull; + let mut _23: &std::ptr::NonNull; + scope 2 { + let _3: std::ptr::NonNull; + let _9: usize; + scope 3 { + } + scope 4 { + scope 7 (inlined as PartialEq>::eq) { + let mut _4: std::ptr::NonNull; + let mut _5: *mut T; + let mut _6: *mut T; + scope 8 (inlined NonNull::::as_ptr) { + } + scope 9 (inlined NonNull::::as_ptr) { + } + } + } + scope 5 (inlined std::ptr::mut_ptr::::addr) { + scope 6 (inlined std::ptr::mut_ptr::::cast::<()>) { + } + } + } + scope 10 (inlined std::slice::IterMut::<'_, T>::next_back_unchecked) { + let mut _15: std::ptr::NonNull; + let mut _24: &mut std::ptr::NonNull; + scope 11 (inlined std::slice::IterMut::<'_, T>::pre_dec_end) { + let mut _10: *mut *mut T; + let mut _11: *mut std::ptr::NonNull; + let mut _12: std::ptr::NonNull; + let mut _16: *mut *mut T; + let mut _17: *mut usize; + let mut _18: usize; + let mut _19: usize; + scope 12 { + scope 13 { + } + scope 14 { + scope 17 (inlined NonNull::::sub) { + scope 18 (inlined #[track_caller] core::num::::unchecked_neg) { + scope 19 (inlined core::ub_checks::check_language_ub) { + scope 20 (inlined core::ub_checks::check_language_ub::runtime) { + } + } + } + scope 21 (inlined NonNull::::offset) { + let mut _13: *const T; + let mut _14: *const T; + scope 22 (inlined NonNull::::as_ptr) { + } + } + } + } + scope 15 (inlined std::ptr::mut_ptr::::cast::) { + } + scope 16 (inlined std::ptr::mut_ptr::::cast::>) { + } + } + } + scope 23 (inlined NonNull::::as_mut::<'_>) { + let mut _20: *mut T; + scope 24 (inlined NonNull::::as_ptr) { + } + } + } + } bb0: { - _0 = as DoubleEndedIterator>::next_back(move _1) -> [return: bb1, unwind unreachable]; + StorageLive(_9); + StorageLive(_8); + StorageLive(_3); + StorageLive(_2); + StorageLive(_21); + StorageLive(_7); + switchInt(const ::IS_ZST) -> [0: bb1, otherwise: bb2]; } bb1: { + _2 = copy ((*_1).1: *mut T); + _3 = copy _2 as std::ptr::NonNull (Transmute); + StorageLive(_22); + // DBG: _22 = &((*_1).0: std::ptr::NonNull); + StorageLive(_23); + // DBG: _23 = &_3; + StorageLive(_5); + StorageLive(_4); + _4 = copy ((*_1).0: std::ptr::NonNull); + _5 = copy _4 as *mut T (Transmute); + StorageDead(_4); + StorageLive(_6); + _6 = copy _3 as *mut T (Transmute); + _7 = Eq(copy _5, copy _6); + StorageDead(_6); + StorageDead(_5); + StorageDead(_23); + StorageDead(_22); + goto -> bb3; + } + + bb2: { + _8 = copy ((*_1).1: *mut T); + _9 = copy _8 as usize (Transmute); + _7 = Eq(copy _9, const 0_usize); + goto -> bb3; + } + + bb3: { + switchInt(move _7) -> [0: bb4, otherwise: bb10]; + } + + bb4: { + StorageLive(_15); + StorageLive(_24); + StorageLive(_17); + StorageLive(_11); + StorageLive(_12); + switchInt(const ::IS_ZST) -> [0: bb5, otherwise: bb8]; + } + + bb5: { + StorageLive(_10); + _10 = &raw mut ((*_1).1: *mut T); + _11 = copy _10 as *mut std::ptr::NonNull (PtrToPtr); + StorageDead(_10); + _12 = copy (*_11); + switchInt(const ::IS_ZST) -> [0: bb6, otherwise: bb7]; + } + + bb6: { + StorageLive(_14); + StorageLive(_13); + _13 = copy _12 as *const T (Transmute); + _14 = Offset(copy _13, const -1_isize); + StorageDead(_13); + _12 = NonNull:: { pointer: copy _14 }; + StorageDead(_14); + goto -> bb7; + } + + bb7: { + (*_11) = move _12; + _15 = copy (*_11); + goto -> bb9; + } + + bb8: { + StorageLive(_16); + _16 = &raw mut ((*_1).1: *mut T); + _17 = copy _16 as *mut usize (PtrToPtr); + StorageDead(_16); + StorageLive(_19); + StorageLive(_18); + _18 = copy (*_17); + _19 = SubUnchecked(move _18, const 1_usize); + StorageDead(_18); + (*_17) = move _19; + StorageDead(_19); + _15 = copy ((*_1).0: std::ptr::NonNull); + goto -> bb9; + } + + bb9: { + StorageDead(_12); + StorageDead(_11); + StorageDead(_17); + // DBG: _24 = &_15; + StorageLive(_20); + _20 = copy _15 as *mut T (Transmute); + _21 = &mut (*_20); + StorageDead(_20); + StorageDead(_24); + StorageDead(_15); + _0 = Option::<&mut T>::Some(copy _21); + goto -> bb11; + } + + bb10: { + _0 = const {transmute(0x0000000000000000): Option<&mut T>}; + goto -> bb11; + } + + bb11: { + StorageDead(_7); + StorageDead(_21); + StorageDead(_2); + StorageDead(_3); + StorageDead(_8); + StorageDead(_9); return; } } diff --git a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-unwind.mir index dfe5e206fada..590d4ac2c838 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-unwind.mir @@ -3,12 +3,199 @@ fn slice_iter_mut_next_back(_1: &mut std::slice::IterMut<'_, T>) -> Option<&mut T> { debug it => _1; let mut _0: std::option::Option<&mut T>; + scope 1 (inlined as DoubleEndedIterator>::next_back) { + let mut _2: *mut T; + let mut _7: bool; + let mut _8: *mut T; + let mut _21: &mut T; + let mut _22: &std::ptr::NonNull; + let mut _23: &std::ptr::NonNull; + scope 2 { + let _3: std::ptr::NonNull; + let _9: usize; + scope 3 { + } + scope 4 { + scope 7 (inlined as PartialEq>::eq) { + let mut _4: std::ptr::NonNull; + let mut _5: *mut T; + let mut _6: *mut T; + scope 8 (inlined NonNull::::as_ptr) { + } + scope 9 (inlined NonNull::::as_ptr) { + } + } + } + scope 5 (inlined std::ptr::mut_ptr::::addr) { + scope 6 (inlined std::ptr::mut_ptr::::cast::<()>) { + } + } + } + scope 10 (inlined std::slice::IterMut::<'_, T>::next_back_unchecked) { + let mut _15: std::ptr::NonNull; + let mut _24: &mut std::ptr::NonNull; + scope 11 (inlined std::slice::IterMut::<'_, T>::pre_dec_end) { + let mut _10: *mut *mut T; + let mut _11: *mut std::ptr::NonNull; + let mut _12: std::ptr::NonNull; + let mut _16: *mut *mut T; + let mut _17: *mut usize; + let mut _18: usize; + let mut _19: usize; + scope 12 { + scope 13 { + } + scope 14 { + scope 17 (inlined NonNull::::sub) { + scope 18 (inlined #[track_caller] core::num::::unchecked_neg) { + scope 19 (inlined core::ub_checks::check_language_ub) { + scope 20 (inlined core::ub_checks::check_language_ub::runtime) { + } + } + } + scope 21 (inlined NonNull::::offset) { + let mut _13: *const T; + let mut _14: *const T; + scope 22 (inlined NonNull::::as_ptr) { + } + } + } + } + scope 15 (inlined std::ptr::mut_ptr::::cast::) { + } + scope 16 (inlined std::ptr::mut_ptr::::cast::>) { + } + } + } + scope 23 (inlined NonNull::::as_mut::<'_>) { + let mut _20: *mut T; + scope 24 (inlined NonNull::::as_ptr) { + } + } + } + } bb0: { - _0 = as DoubleEndedIterator>::next_back(move _1) -> [return: bb1, unwind continue]; + StorageLive(_9); + StorageLive(_8); + StorageLive(_3); + StorageLive(_2); + StorageLive(_21); + StorageLive(_7); + switchInt(const ::IS_ZST) -> [0: bb1, otherwise: bb2]; } bb1: { + _2 = copy ((*_1).1: *mut T); + _3 = copy _2 as std::ptr::NonNull (Transmute); + StorageLive(_22); + // DBG: _22 = &((*_1).0: std::ptr::NonNull); + StorageLive(_23); + // DBG: _23 = &_3; + StorageLive(_5); + StorageLive(_4); + _4 = copy ((*_1).0: std::ptr::NonNull); + _5 = copy _4 as *mut T (Transmute); + StorageDead(_4); + StorageLive(_6); + _6 = copy _3 as *mut T (Transmute); + _7 = Eq(copy _5, copy _6); + StorageDead(_6); + StorageDead(_5); + StorageDead(_23); + StorageDead(_22); + goto -> bb3; + } + + bb2: { + _8 = copy ((*_1).1: *mut T); + _9 = copy _8 as usize (Transmute); + _7 = Eq(copy _9, const 0_usize); + goto -> bb3; + } + + bb3: { + switchInt(move _7) -> [0: bb4, otherwise: bb10]; + } + + bb4: { + StorageLive(_15); + StorageLive(_24); + StorageLive(_17); + StorageLive(_11); + StorageLive(_12); + switchInt(const ::IS_ZST) -> [0: bb5, otherwise: bb8]; + } + + bb5: { + StorageLive(_10); + _10 = &raw mut ((*_1).1: *mut T); + _11 = copy _10 as *mut std::ptr::NonNull (PtrToPtr); + StorageDead(_10); + _12 = copy (*_11); + switchInt(const ::IS_ZST) -> [0: bb6, otherwise: bb7]; + } + + bb6: { + StorageLive(_14); + StorageLive(_13); + _13 = copy _12 as *const T (Transmute); + _14 = Offset(copy _13, const -1_isize); + StorageDead(_13); + _12 = NonNull:: { pointer: copy _14 }; + StorageDead(_14); + goto -> bb7; + } + + bb7: { + (*_11) = move _12; + _15 = copy (*_11); + goto -> bb9; + } + + bb8: { + StorageLive(_16); + _16 = &raw mut ((*_1).1: *mut T); + _17 = copy _16 as *mut usize (PtrToPtr); + StorageDead(_16); + StorageLive(_19); + StorageLive(_18); + _18 = copy (*_17); + _19 = SubUnchecked(move _18, const 1_usize); + StorageDead(_18); + (*_17) = move _19; + StorageDead(_19); + _15 = copy ((*_1).0: std::ptr::NonNull); + goto -> bb9; + } + + bb9: { + StorageDead(_12); + StorageDead(_11); + StorageDead(_17); + // DBG: _24 = &_15; + StorageLive(_20); + _20 = copy _15 as *mut T (Transmute); + _21 = &mut (*_20); + StorageDead(_20); + StorageDead(_24); + StorageDead(_15); + _0 = Option::<&mut T>::Some(copy _21); + goto -> bb11; + } + + bb10: { + _0 = const {transmute(0x0000000000000000): Option<&mut T>}; + goto -> bb11; + } + + bb11: { + StorageDead(_7); + StorageDead(_21); + StorageDead(_2); + StorageDead(_3); + StorageDead(_8); + StorageDead(_9); return; } } diff --git a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-abort.mir index cc0fce26149e..e9ed932cbafb 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-abort.mir @@ -10,6 +10,9 @@ fn slice_iter_next(_1: &mut std::slice::Iter<'_, T>) -> Option<&T> { let mut _10: std::ptr::NonNull; let mut _12: usize; let _14: &T; + let mut _15: &std::ptr::NonNull; + let mut _16: &std::ptr::NonNull; + let mut _17: &std::ptr::NonNull; scope 2 { let _3: *const T; scope 3 { @@ -67,7 +70,11 @@ fn slice_iter_next(_1: &mut std::slice::Iter<'_, T>) -> Option<&T> { bb1: { StorageLive(_7); + StorageLive(_15); + // DBG: _15 = &_2; + StorageLive(_16); _4 = copy _3 as std::ptr::NonNull (Transmute); + // DBG: _16 = &_4; StorageLive(_5); _5 = copy _2 as *mut T (Transmute); StorageLive(_6); @@ -79,6 +86,8 @@ fn slice_iter_next(_1: &mut std::slice::Iter<'_, T>) -> Option<&T> { } bb2: { + StorageDead(_16); + StorageDead(_15); StorageDead(_7); StorageLive(_10); StorageLive(_9); @@ -94,6 +103,8 @@ fn slice_iter_next(_1: &mut std::slice::Iter<'_, T>) -> Option<&T> { } bb3: { + StorageDead(_16); + StorageDead(_15); _0 = const {transmute(0x0000000000000000): Option<&T>}; StorageDead(_7); goto -> bb8; @@ -116,10 +127,13 @@ fn slice_iter_next(_1: &mut std::slice::Iter<'_, T>) -> Option<&T> { } bb7: { + StorageLive(_17); + // DBG: _17 = &_2; StorageLive(_13); _13 = copy _2 as *const T (Transmute); _14 = &(*_13); StorageDead(_13); + StorageDead(_17); _0 = Option::<&T>::Some(copy _14); goto -> bb8; } diff --git a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-unwind.mir index cc0fce26149e..e9ed932cbafb 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-unwind.mir @@ -10,6 +10,9 @@ fn slice_iter_next(_1: &mut std::slice::Iter<'_, T>) -> Option<&T> { let mut _10: std::ptr::NonNull; let mut _12: usize; let _14: &T; + let mut _15: &std::ptr::NonNull; + let mut _16: &std::ptr::NonNull; + let mut _17: &std::ptr::NonNull; scope 2 { let _3: *const T; scope 3 { @@ -67,7 +70,11 @@ fn slice_iter_next(_1: &mut std::slice::Iter<'_, T>) -> Option<&T> { bb1: { StorageLive(_7); + StorageLive(_15); + // DBG: _15 = &_2; + StorageLive(_16); _4 = copy _3 as std::ptr::NonNull (Transmute); + // DBG: _16 = &_4; StorageLive(_5); _5 = copy _2 as *mut T (Transmute); StorageLive(_6); @@ -79,6 +86,8 @@ fn slice_iter_next(_1: &mut std::slice::Iter<'_, T>) -> Option<&T> { } bb2: { + StorageDead(_16); + StorageDead(_15); StorageDead(_7); StorageLive(_10); StorageLive(_9); @@ -94,6 +103,8 @@ fn slice_iter_next(_1: &mut std::slice::Iter<'_, T>) -> Option<&T> { } bb3: { + StorageDead(_16); + StorageDead(_15); _0 = const {transmute(0x0000000000000000): Option<&T>}; StorageDead(_7); goto -> bb8; @@ -116,10 +127,13 @@ fn slice_iter_next(_1: &mut std::slice::Iter<'_, T>) -> Option<&T> { } bb7: { + StorageLive(_17); + // DBG: _17 = &_2; StorageLive(_13); _13 = copy _2 as *const T (Transmute); _14 = &(*_13); StorageDead(_13); + StorageDead(_17); _0 = Option::<&T>::Some(copy _14); goto -> bb8; } diff --git a/tests/mir-opt/pre-codegen/spans.outer.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/spans.outer.PreCodegen.after.panic-abort.mir index fe4e2deab870..79aa9c5ae1e3 100644 --- a/tests/mir-opt/pre-codegen/spans.outer.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/spans.outer.PreCodegen.after.panic-abort.mir @@ -9,7 +9,7 @@ fn outer(_1: u8) -> u8 { } bb0: { - _2 = &_1; // scope 0 at $DIR/spans.rs:11:11: 11:13 + // DBG: _2 = &_1; _0 = copy _1; // scope 1 at $DIR/spans.rs:15:5: 15:7 return; // scope 0 at $DIR/spans.rs:12:2: 12:2 } diff --git a/tests/mir-opt/pre-codegen/spans.outer.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/spans.outer.PreCodegen.after.panic-unwind.mir index fe4e2deab870..79aa9c5ae1e3 100644 --- a/tests/mir-opt/pre-codegen/spans.outer.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/spans.outer.PreCodegen.after.panic-unwind.mir @@ -9,7 +9,7 @@ fn outer(_1: u8) -> u8 { } bb0: { - _2 = &_1; // scope 0 at $DIR/spans.rs:11:11: 11:13 + // DBG: _2 = &_1; _0 = copy _1; // scope 1 at $DIR/spans.rs:15:5: 15:7 return; // scope 0 at $DIR/spans.rs:12:2: 12:2 } diff --git a/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir index 2eee8a97db0d..eebd4a7eb50e 100644 --- a/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir @@ -11,7 +11,9 @@ fn vec_deref_to_slice(_1: &Vec) -> &[u8] { let mut _4: usize; scope 3 (inlined Vec::::as_ptr) { debug self => _1; + let mut _6: &alloc::raw_vec::RawVec; scope 4 (inlined alloc::raw_vec::RawVec::::ptr) { + let mut _7: &alloc::raw_vec::RawVecInner; scope 5 (inlined alloc::raw_vec::RawVecInner::ptr::) { scope 6 (inlined alloc::raw_vec::RawVecInner::non_null::) { let mut _2: std::ptr::NonNull; @@ -55,8 +57,14 @@ fn vec_deref_to_slice(_1: &Vec) -> &[u8] { bb0: { StorageLive(_2); StorageLive(_3); + StorageLive(_6); + // DBG: _6 = &((*_1).0: alloc::raw_vec::RawVec); + StorageLive(_7); + // DBG: _7 = &(((*_1).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner); _2 = copy (((((*_1).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).0: std::ptr::Unique).0: std::ptr::NonNull); + StorageDead(_7); _3 = copy _2 as *const u8 (Transmute); + StorageDead(_6); StorageLive(_4); _4 = copy ((*_1).1: usize); StorageLive(_5); diff --git a/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir index 2eee8a97db0d..eebd4a7eb50e 100644 --- a/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir @@ -11,7 +11,9 @@ fn vec_deref_to_slice(_1: &Vec) -> &[u8] { let mut _4: usize; scope 3 (inlined Vec::::as_ptr) { debug self => _1; + let mut _6: &alloc::raw_vec::RawVec; scope 4 (inlined alloc::raw_vec::RawVec::::ptr) { + let mut _7: &alloc::raw_vec::RawVecInner; scope 5 (inlined alloc::raw_vec::RawVecInner::ptr::) { scope 6 (inlined alloc::raw_vec::RawVecInner::non_null::) { let mut _2: std::ptr::NonNull; @@ -55,8 +57,14 @@ fn vec_deref_to_slice(_1: &Vec) -> &[u8] { bb0: { StorageLive(_2); StorageLive(_3); + StorageLive(_6); + // DBG: _6 = &((*_1).0: alloc::raw_vec::RawVec); + StorageLive(_7); + // DBG: _7 = &(((*_1).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner); _2 = copy (((((*_1).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).0: std::ptr::Unique).0: std::ptr::NonNull); + StorageDead(_7); _3 = copy _2 as *const u8 (Transmute); + StorageDead(_6); StorageLive(_4); _4 = copy ((*_1).1: usize); StorageLive(_5); From cc93132ae4f5477297ecddb0c07d2e8c74075f0c Mon Sep 17 00:00:00 2001 From: dianqk Date: Thu, 10 Jul 2025 21:20:54 +0800 Subject: [PATCH 105/226] simplifycfg: Preserve debuginfos when merging bbs --- compiler/rustc_middle/src/mir/terminator.rs | 8 + compiler/rustc_mir_transform/src/simplify.rs | 30 ++- ...ycfg.drop_debuginfo.SimplifyCfg-final.diff | 2 + ...reserve_debuginfo_1.SimplifyCfg-final.diff | 3 + ...reserve_debuginfo_2.SimplifyCfg-final.diff | 3 + ...reserve_debuginfo_3.SimplifyCfg-final.diff | 40 ++++ ...nfo_identical_succs.SimplifyCfg-final.diff | 32 +++ tests/mir-opt/debuginfo/simplifycfg.rs | 207 ++++++++++++++++++ 8 files changed, 322 insertions(+), 3 deletions(-) create mode 100644 tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_3.SimplifyCfg-final.diff create mode 100644 tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_identical_succs.SimplifyCfg-final.diff create mode 100644 tests/mir-opt/debuginfo/simplifycfg.rs diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs index 4034a3a06e94..4249914346cd 100644 --- a/compiler/rustc_middle/src/mir/terminator.rs +++ b/compiler/rustc_middle/src/mir/terminator.rs @@ -444,6 +444,14 @@ impl<'tcx> Terminator<'tcx> { self.kind.successors() } + /// Return `Some` if all successors are identical. + #[inline] + pub fn identical_successor(&self) -> Option { + let mut successors = self.successors(); + let first_succ = successors.next()?; + if successors.all(|succ| first_succ == succ) { Some(first_succ) } else { None } + } + #[inline] pub fn successors_mut<'a>(&'a mut self, f: impl FnMut(&'a mut BasicBlock)) { self.kind.successors_mut(f) diff --git a/compiler/rustc_mir_transform/src/simplify.rs b/compiler/rustc_mir_transform/src/simplify.rs index 9f7bb3b03795..edea5cb2c724 100644 --- a/compiler/rustc_mir_transform/src/simplify.rs +++ b/compiler/rustc_mir_transform/src/simplify.rs @@ -144,7 +144,7 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> { // statements itself to avoid moving the (relatively) large statements twice. // We do not push the statements directly into the target block (`bb`) as that is slower // due to additional reallocations - let mut merged_blocks = Vec::new(); + let mut merged_blocks: Vec = Vec::new(); let mut outer_changed = false; loop { let mut changed = false; @@ -159,8 +159,9 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> { let mut terminator = self.basic_blocks[bb].terminator.take().expect("invalid terminator state"); - terminator - .successors_mut(|successor| self.collapse_goto_chain(successor, &mut changed)); + terminator.successors_mut(|successor| { + self.collapse_goto_chain(successor, &mut changed); + }); let mut inner_changed = true; merged_blocks.clear(); @@ -177,10 +178,18 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> { if statements_to_merge > 0 { let mut statements = std::mem::take(&mut self.basic_blocks[bb].statements); statements.reserve(statements_to_merge); + let mut parent_bb_last_debuginfos = + std::mem::take(&mut self.basic_blocks[bb].after_last_stmt_debuginfos); for &from in &merged_blocks { + if let Some(stmt) = self.basic_blocks[from].statements.first_mut() { + stmt.debuginfos.prepend(&mut parent_bb_last_debuginfos); + } statements.append(&mut self.basic_blocks[from].statements); + parent_bb_last_debuginfos = + std::mem::take(&mut self.basic_blocks[from].after_last_stmt_debuginfos); } self.basic_blocks[bb].statements = statements; + self.basic_blocks[bb].after_last_stmt_debuginfos = parent_bb_last_debuginfos; } self.basic_blocks[bb].terminator = Some(terminator); @@ -220,10 +229,14 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> { // goto chains. We should probably benchmark different sizes. let mut terminators: SmallVec<[_; 1]> = Default::default(); let mut current = *start; + // If each successor has only one predecessor, it's a trivial goto chain. + // We can move all debuginfos to the last basic block. + let mut trivial_goto_chain = true; while let Some(terminator) = self.take_terminator_if_simple_goto(current) { let Terminator { kind: TerminatorKind::Goto { target }, .. } = terminator else { unreachable!(); }; + trivial_goto_chain &= self.pred_count[target] == 1; terminators.push((current, terminator)); current = target; } @@ -235,6 +248,17 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> { else { unreachable!(); }; + if trivial_goto_chain { + let mut pred_debuginfos = + std::mem::take(&mut self.basic_blocks[current].after_last_stmt_debuginfos); + let debuginfos = if let Some(stmt) = self.basic_blocks[last].statements.first_mut() + { + &mut stmt.debuginfos + } else { + &mut self.basic_blocks[last].after_last_stmt_debuginfos + }; + debuginfos.prepend(&mut pred_debuginfos); + } *changed |= *target != last; *target = last; debug!("collapsing goto chain from {:?} to {:?}", current, target); diff --git a/tests/mir-opt/debuginfo/simplifycfg.drop_debuginfo.SimplifyCfg-final.diff b/tests/mir-opt/debuginfo/simplifycfg.drop_debuginfo.SimplifyCfg-final.diff index d4a73351ee4a..7a1ac92f6dde 100644 --- a/tests/mir-opt/debuginfo/simplifycfg.drop_debuginfo.SimplifyCfg-final.diff +++ b/tests/mir-opt/debuginfo/simplifycfg.drop_debuginfo.SimplifyCfg-final.diff @@ -14,11 +14,13 @@ - - bb1: { - // DBG: _3 = &((*_1).0: i32); +- nop; - goto -> bb2; - } - - bb2: { // DBG: _4 = &((*_1).1: i64); +- nop; _0 = copy ((*_1).2: i32); return; } diff --git a/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_1.SimplifyCfg-final.diff b/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_1.SimplifyCfg-final.diff index 1c12358ad893..fa4d3aebf838 100644 --- a/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_1.SimplifyCfg-final.diff +++ b/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_1.SimplifyCfg-final.diff @@ -17,13 +17,16 @@ - bb1: { (*_2) = const true; // DBG: _3 = &((*_1).0: i32); +- nop; - goto -> bb2; - } - - bb2: { // DBG: _4 = &((*_1).1: i64); +- nop; _0 = copy ((*_1).2: i32); // DBG: _5 = &((*_1).2: i32); +- nop; return; } } diff --git a/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_2.SimplifyCfg-final.diff b/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_2.SimplifyCfg-final.diff index de8e5612c878..b01c91f196b6 100644 --- a/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_2.SimplifyCfg-final.diff +++ b/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_2.SimplifyCfg-final.diff @@ -16,13 +16,16 @@ - - bb1: { // DBG: _2 = &((*_1).0: i32); +- nop; - goto -> bb2; - } - - bb2: { // DBG: _3 = &((*_1).1: i64); +- nop; _0 = copy ((*_1).2: i32); // DBG: _4 = &((*_1).2: i32); +- nop; return; } } diff --git a/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_3.SimplifyCfg-final.diff b/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_3.SimplifyCfg-final.diff new file mode 100644 index 000000000000..caa90d79a1c2 --- /dev/null +++ b/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_3.SimplifyCfg-final.diff @@ -0,0 +1,40 @@ +- // MIR for `preserve_debuginfo_3` before SimplifyCfg-final ++ // MIR for `preserve_debuginfo_3` after SimplifyCfg-final + + fn preserve_debuginfo_3(_1: &Foo, _2: bool) -> i32 { + debug foo_a => _3; + debug foo_b => _4; + debug foo_c => _5; + let mut _0: i32; + let mut _3: &i32; + let mut _4: &i64; + let mut _5: &i32; + + bb0: { +- switchInt(copy _2) -> [1: bb1, otherwise: bb2]; ++ switchInt(copy _2) -> [1: bb2, otherwise: bb1]; + } + + bb1: { +- // DBG: _3 = &((*_1).0: i32); +- nop; +- goto -> bb3; +- } +- +- bb2: { + // DBG: _4 = &((*_1).1: i64); +- nop; + _0 = copy ((*_1).2: i32); + return; + } + +- bb3: { ++ bb2: { ++ // DBG: _3 = &((*_1).0: i32); + // DBG: _5 = &((*_1).2: i32); +- nop; + _0 = copy ((*_1).0: i32); + return; + } + } + diff --git a/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_identical_succs.SimplifyCfg-final.diff b/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_identical_succs.SimplifyCfg-final.diff new file mode 100644 index 000000000000..d8c36990a5ff --- /dev/null +++ b/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_identical_succs.SimplifyCfg-final.diff @@ -0,0 +1,32 @@ +- // MIR for `preserve_debuginfo_identical_succs` before SimplifyCfg-final ++ // MIR for `preserve_debuginfo_identical_succs` after SimplifyCfg-final + + fn preserve_debuginfo_identical_succs(_1: &Foo, _2: bool) -> i32 { + debug foo_a => _3; + debug foo_b => _4; + debug foo_c => _5; + let mut _0: i32; + let mut _3: &i32; + let mut _4: &i64; + let mut _5: &i32; + + bb0: { +- switchInt(copy _2) -> [1: bb1, otherwise: bb1]; +- } +- +- bb1: { + // DBG: _3 = &((*_1).0: i32); +- nop; +- goto -> bb2; +- } +- +- bb2: { + // DBG: _4 = &((*_1).1: i64); +- nop; + _0 = copy ((*_1).2: i32); + // DBG: _5 = &((*_1).2: i32); +- nop; + return; + } + } + diff --git a/tests/mir-opt/debuginfo/simplifycfg.rs b/tests/mir-opt/debuginfo/simplifycfg.rs new file mode 100644 index 000000000000..2bd510fd3b9d --- /dev/null +++ b/tests/mir-opt/debuginfo/simplifycfg.rs @@ -0,0 +1,207 @@ +//@ test-mir-pass: SimplifyCfg-final +//@ compile-flags: -Zmir-enable-passes=+DeadStoreElimination-initial + +#![feature(core_intrinsics, custom_mir)] +#![crate_type = "lib"] + +use std::intrinsics::mir::*; + +pub struct Foo { + a: i32, + b: i64, + c: i32, +} + +// EMIT_MIR simplifycfg.drop_debuginfo.SimplifyCfg-final.diff +#[custom_mir(dialect = "runtime", phase = "post-cleanup")] +pub fn drop_debuginfo(foo: &Foo, c: bool) -> i32 { + // CHECK-LABEL: fn drop_debuginfo + // CHECK: debug foo_b => [[foo_b:_[0-9]+]]; + // CHECK: bb0: { + // CHECK-NEXT: DBG: [[foo_b]] = &((*_1).1: i64) + // CHECK-NEXT: _0 = copy ((*_1).2: i32); + // CHECK-NEXT: return; + mir! { + let _foo_a: &i32; + let _foo_b: &i64; + debug foo_a => _foo_a; + debug foo_b => _foo_b; + { + match c { + true => tmp, + _ => ret, + } + } + tmp = { + // Because we don't know if `c` is always true, we must drop this debuginfo. + _foo_a = &(*foo).a; + Goto(ret) + } + ret = { + _foo_b = &(*foo).b; + RET = (*foo).c; + Return() + } + } +} + +// EMIT_MIR simplifycfg.preserve_debuginfo_1.SimplifyCfg-final.diff +#[custom_mir(dialect = "runtime", phase = "post-cleanup")] +pub fn preserve_debuginfo_1(foo: &Foo, v: &mut bool) -> i32 { + // CHECK-LABEL: fn preserve_debuginfo_1 + // CHECK: debug foo_a => [[foo_a:_[0-9]+]]; + // CHECK: debug foo_b => [[foo_b:_[0-9]+]]; + // CHECK: debug foo_c => [[foo_c:_[0-9]+]]; + // CHECK: bb0: { + // CHECK-NEXT: (*_2) = const true; + // CHECK-NEXT: DBG: [[foo_a]] = &((*_1).0: i32) + // CHECK-NEXT: DBG: [[foo_b]] = &((*_1).1: i64) + // CHECK-NEXT: _0 = copy ((*_1).2: i32); + // CHECK-NEXT: DBG: [[foo_c]] = &((*_1).2: i32) + // CHECK-NEXT: return; + mir! { + let _foo_a: &i32; + let _foo_b: &i64; + let _foo_c: &i32; + debug foo_a => _foo_a; + debug foo_b => _foo_b; + debug foo_c => _foo_c; + { + Goto(tmp) + } + tmp = { + *v = true; + _foo_a = &(*foo).a; + Goto(ret) + } + ret = { + _foo_b = &(*foo).b; + RET = (*foo).c; + _foo_c = &(*foo).c; + Return() + } + } +} + +// EMIT_MIR simplifycfg.preserve_debuginfo_2.SimplifyCfg-final.diff +#[custom_mir(dialect = "runtime", phase = "post-cleanup")] +pub fn preserve_debuginfo_2(foo: &Foo) -> i32 { + // CHECK-LABEL: fn preserve_debuginfo_2 + // CHECK: debug foo_a => [[foo_a:_[0-9]+]]; + // CHECK: debug foo_b => [[foo_b:_[0-9]+]]; + // CHECK: debug foo_c => [[foo_c:_[0-9]+]]; + // CHECK: bb0: { + // CHECK-NEXT: DBG: [[foo_a]] = &((*_1).0: i32) + // CHECK-NEXT: DBG: [[foo_b]] = &((*_1).1: i64) + // CHECK-NEXT: _0 = copy ((*_1).2: i32); + // CHECK-NEXT: DBG: [[foo_c]] = &((*_1).2: i32) + // CHECK-NEXT: return; + mir! { + let _foo_a: &i32; + let _foo_b: &i64; + let _foo_c: &i32; + debug foo_a => _foo_a; + debug foo_b => _foo_b; + debug foo_c => _foo_c; + { + Goto(tmp) + } + tmp = { + _foo_a = &(*foo).a; + Goto(ret) + } + ret = { + _foo_b = &(*foo).b; + RET = (*foo).c; + _foo_c = &(*foo).c; + Return() + } + } +} + +// EMIT_MIR simplifycfg.preserve_debuginfo_3.SimplifyCfg-final.diff +#[custom_mir(dialect = "runtime", phase = "post-cleanup")] +pub fn preserve_debuginfo_3(foo: &Foo, c: bool) -> i32 { + // CHECK-LABEL: fn preserve_debuginfo_3 + // CHECK: debug foo_a => [[foo_a:_[0-9]+]]; + // CHECK: debug foo_b => [[foo_b:_[0-9]+]]; + // CHECK: debug foo_c => [[foo_c:_[0-9]+]]; + // CHECK: bb0: { + // CHECK-NEXT: switchInt(copy _2) -> [1: bb2, otherwise: bb1]; + // CHECK: bb1: { + // CHECK-NEXT: DBG: [[foo_b]] = &((*_1).1: i64) + // CHECK-NEXT: _0 = copy ((*_1).2: i32); + // CHECK-NEXT: return; + // CHECK: bb2: { + // CHECK-NEXT: DBG: [[foo_a]] = &((*_1).0: i32) + // CHECK-NEXT: DBG: [[foo_c]] = &((*_1).2: i32) + // CHECK-NEXT: _0 = copy ((*_1).0: i32); + // CHECK-NEXT: return; + mir! { + let _foo_a: &i32; + let _foo_b: &i64; + let _foo_c: &i32; + debug foo_a => _foo_a; + debug foo_b => _foo_b; + debug foo_c => _foo_c; + { + match c { + true => tmp, + _ => ret, + } + } + tmp = { + _foo_a = &(*foo).a; + Goto(ret_1) + } + ret = { + _foo_b = &(*foo).b; + RET = (*foo).c; + Return() + } + ret_1 = { + _foo_c = &(*foo).c; + RET = (*foo).a; + Return() + } + } +} + +// EMIT_MIR simplifycfg.preserve_debuginfo_identical_succs.SimplifyCfg-final.diff +#[custom_mir(dialect = "runtime", phase = "post-cleanup")] +pub fn preserve_debuginfo_identical_succs(foo: &Foo, c: bool) -> i32 { + // CHECK-LABEL: fn preserve_debuginfo_identical_succs + // CHECK: debug foo_a => [[foo_a:_[0-9]+]]; + // CHECK: debug foo_b => [[foo_b:_[0-9]+]]; + // CHECK: debug foo_c => [[foo_c:_[0-9]+]]; + // CHECK: bb0: { + // CHECK-NEXT: DBG: [[foo_a]] = &((*_1).0: i32) + // CHECK-NEXT: DBG: [[foo_b]] = &((*_1).1: i64) + // CHECK-NEXT: _0 = copy ((*_1).2: i32); + // CHECK-NEXT: DBG: [[foo_c]] = &((*_1).2: i32) + // CHECK-NEXT: return; + mir! { + let _foo_a: &i32; + let _foo_b: &i64; + let _foo_c: &i32; + debug foo_a => _foo_a; + debug foo_b => _foo_b; + debug foo_c => _foo_c; + { + match c { + true => tmp, + _ => tmp, + } + } + tmp = { + _foo_a = &(*foo).a; + Goto(ret) + } + ret = { + _foo_b = &(*foo).b; + RET = (*foo).c; + _foo_c = &(*foo).c; + Return() + } + } +} From 85b2f706939528b5796d98e82c183637f8338cd1 Mon Sep 17 00:00:00 2001 From: dianqk Date: Tue, 15 Jul 2025 22:54:54 +0800 Subject: [PATCH 106/226] mir-opt: Eliminate trivial unnecessary storage annotations --- compiler/rustc_middle/src/mir/statement.rs | 14 +-- .../rustc_mir_dataflow/src/impls/liveness.rs | 4 +- .../src/dead_store_elimination.rs | 16 ++- compiler/rustc_mir_transform/src/inline.rs | 8 +- compiler/rustc_mir_transform/src/simplify.rs | 118 ++++++++++++------ ...le.cycle.DeadStoreElimination-initial.diff | 4 - ...ad_first.DeadStoreElimination-initial.diff | 1 - ...ef.tuple.DeadStoreElimination-initial.diff | 5 +- ...ycfg.drop_debuginfo.SimplifyCfg-final.diff | 2 - ...reserve_debuginfo_1.SimplifyCfg-final.diff | 3 - ...reserve_debuginfo_2.SimplifyCfg-final.diff | 3 - ...reserve_debuginfo_3.SimplifyCfg-final.diff | 3 - ...nfo_identical_succs.SimplifyCfg-final.diff | 3 - .../inline_shims.drop.Inline.panic-abort.diff | 4 - ...inline_shims.drop.Inline.panic-unwind.diff | 2 - ...y.run2-{closure#0}.Inline.panic-abort.diff | 11 -- ....run2-{closure#0}.Inline.panic-unwind.diff | 11 -- .../issue_101973.inner.GVN.panic-abort.diff | 1 - .../issue_101973.inner.GVN.panic-unwind.diff | 1 - ...implifyComparisonIntegral.panic-abort.diff | 6 - ...mplifyComparisonIntegral.panic-unwind.diff | 6 - ...git.PreCodegen.after.32bit.panic-abort.mir | 4 - ...it.PreCodegen.after.32bit.panic-unwind.mir | 4 - ...git.PreCodegen.after.64bit.panic-abort.mir | 4 - ...it.PreCodegen.after.64bit.panic-unwind.mir | 4 - ...p_forward.PreCodegen.after.panic-abort.mir | 40 ++---- ..._forward.PreCodegen.after.panic-unwind.mir | 40 ++---- ...as_copy.clone_as_copy.PreCodegen.after.mir | 2 - ...py.enum_clone_as_copy.PreCodegen.after.mir | 46 ++----- tests/mir-opt/pre-codegen/clone_as_copy.rs | 16 +-- ...ace.PreCodegen.after.32bit.panic-abort.mir | 38 ++---- ...ce.PreCodegen.after.32bit.panic-unwind.mir | 38 ++---- ...ace.PreCodegen.after.64bit.panic-abort.mir | 38 ++---- ...ce.PreCodegen.after.64bit.panic-unwind.mir | 38 ++---- tests/mir-opt/pre-codegen/drop_boxed_slice.rs | 2 +- .../loops.int_range.PreCodegen.after.mir | 6 - .../loops.vec_move.PreCodegen.after.mir | 28 ----- ...variant_a-{closure#0}.PreCodegen.after.mir | 22 ---- ...ated_loop.PreCodegen.after.panic-abort.mir | 14 --- ...ward_loop.PreCodegen.after.panic-abort.mir | 14 --- ...ard_loop.PreCodegen.after.panic-unwind.mir | 14 --- ...erse_loop.PreCodegen.after.panic-abort.mir | 12 -- ...rse_loop.PreCodegen.after.panic-unwind.mir | 12 -- ..._is_empty.PreCodegen.after.panic-abort.mir | 8 -- ...is_empty.PreCodegen.after.panic-unwind.mir | 8 -- ...next_back.PreCodegen.after.panic-abort.mir | 12 -- ...ext_back.PreCodegen.after.panic-unwind.mir | 12 -- ...iter_next.PreCodegen.after.panic-abort.mir | 14 --- ...ter_next.PreCodegen.after.panic-unwind.mir | 14 --- tests/ui/extern/extern-types-field-offset.rs | 5 + 50 files changed, 179 insertions(+), 556 deletions(-) diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index 9deb43eb7084..67b4448cfcdd 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -30,16 +30,10 @@ impl<'tcx> Statement<'tcx> { } let replaced_stmt = std::mem::replace(&mut self.kind, StatementKind::Nop); if !drop_debuginfo { - match replaced_stmt { - StatementKind::Assign(box (place, Rvalue::Ref(_, _, ref_place))) - if let Some(local) = place.as_local() => - { - self.debuginfos.push(StmtDebugInfo::AssignRef(local, ref_place)); - } - _ => { - bug!("debuginfo is not yet supported.") - } - } + let Some(debuginfo) = replaced_stmt.as_debuginfo() else { + bug!("debuginfo is not yet supported.") + }; + self.debuginfos.push(debuginfo); } } diff --git a/compiler/rustc_mir_dataflow/src/impls/liveness.rs b/compiler/rustc_mir_dataflow/src/impls/liveness.rs index 24da4b0bea28..037e2720f365 100644 --- a/compiler/rustc_mir_dataflow/src/impls/liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/liveness.rs @@ -233,8 +233,10 @@ impl<'a> MaybeTransitiveLiveLocals<'a> { // Compute the place that we are storing to, if any let destination = match stmt_kind { StatementKind::Assign(box (place, rvalue)) => (rvalue.is_safe_to_remove() + // FIXME: We are not sure how we should represent this debugging information for some statements, + // keep it for now. && (!debuginfo_locals.contains(place.local) - || (place.as_local().is_some() && matches!(rvalue, mir::Rvalue::Ref(..))))) + || (place.as_local().is_some() && stmt_kind.as_debuginfo().is_some()))) .then_some(*place), StatementKind::SetDiscriminant { place, .. } | StatementKind::Deinit(place) => { (!debuginfo_locals.contains(place.local)).then_some(**place) diff --git a/compiler/rustc_mir_transform/src/dead_store_elimination.rs b/compiler/rustc_mir_transform/src/dead_store_elimination.rs index a5f8a22e83c8..732c3dcd44ab 100644 --- a/compiler/rustc_mir_transform/src/dead_store_elimination.rs +++ b/compiler/rustc_mir_transform/src/dead_store_elimination.rs @@ -22,13 +22,15 @@ use rustc_mir_dataflow::impls::{ LivenessTransferFunction, MaybeTransitiveLiveLocals, borrowed_locals, }; +use crate::simplify::UsedInStmtLocals; use crate::util::is_within_packed; /// Performs the optimization on the body /// /// The `borrowed` set must be a `DenseBitSet` of all the locals that are ever borrowed in this /// body. It can be generated via the [`borrowed_locals`] function. -fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { +/// Returns true if any instruction is eliminated. +fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> bool { let borrowed_locals = borrowed_locals(body); // If the user requests complete debuginfo, mark the locals that appear in it as live, so @@ -97,8 +99,9 @@ fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { } if patch.is_empty() && call_operands_to_move.is_empty() { - return; + return false; } + let eliminated = !patch.is_empty(); let bbs = body.basic_blocks.as_mut_preserves_cfg(); for (Location { block, statement_index }, drop_debuginfo) in patch { @@ -112,6 +115,8 @@ fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let Operand::Copy(place) = *arg else { bug!() }; *arg = Operand::Move(place); } + + eliminated } pub(super) enum DeadStoreElimination { @@ -132,7 +137,12 @@ impl<'tcx> crate::MirPass<'tcx> for DeadStoreElimination { } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - eliminate(tcx, body); + if eliminate(tcx, body) { + UsedInStmtLocals::new(body).remove_unused_storage_annotations(body); + for data in body.basic_blocks.as_mut_preserves_cfg() { + data.strip_nops(); + } + } } fn is_required(&self) -> bool { diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 3d49eb4e8ef7..8593e25d6aa5 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -21,7 +21,7 @@ use tracing::{debug, instrument, trace, trace_span}; use crate::cost_checker::{CostChecker, is_call_like}; use crate::deref_separator::deref_finder; -use crate::simplify::simplify_cfg; +use crate::simplify::{UsedInStmtLocals, simplify_cfg}; use crate::validate::validate_types; use crate::{check_inline, util}; @@ -935,7 +935,7 @@ fn inline_call<'tcx, I: Inliner<'tcx>>( in_cleanup_block: false, return_block, tcx, - always_live_locals: DenseBitSet::new_filled(callee_body.local_decls.len()), + always_live_locals: UsedInStmtLocals::new(&callee_body).locals, }; // Map all `Local`s, `SourceScope`s and `BasicBlock`s to new ones @@ -995,6 +995,10 @@ fn inline_call<'tcx, I: Inliner<'tcx>>( // people working on rust can build with or without debuginfo while // still getting consistent results from the mir-opt tests. caller_body.var_debug_info.append(&mut callee_body.var_debug_info); + } else { + for bb in callee_body.basic_blocks_mut() { + bb.drop_debuginfo(); + } } caller_body.basic_blocks_mut().append(callee_body.basic_blocks_mut()); diff --git a/compiler/rustc_mir_transform/src/simplify.rs b/compiler/rustc_mir_transform/src/simplify.rs index edea5cb2c724..0f23482ea8b1 100644 --- a/compiler/rustc_mir_transform/src/simplify.rs +++ b/compiler/rustc_mir_transform/src/simplify.rs @@ -35,12 +35,12 @@ //! pre-"runtime" MIR! use itertools::Itertools as _; +use rustc_index::bit_set::DenseBitSet; use rustc_index::{Idx, IndexSlice, IndexVec}; -use rustc_middle::mir::visit::{ - MutVisitor, MutatingUseContext, NonUseContext, PlaceContext, Visitor, -}; +use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; +use rustc_mir_dataflow::debuginfo::debuginfo_locals; use rustc_span::DUMMY_SP; use smallvec::SmallVec; use tracing::{debug, trace}; @@ -329,7 +329,7 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> { fn strip_nops(&mut self) { for blk in self.basic_blocks.iter_mut() { - blk.retain_statements(|stmt| !matches!(stmt.kind, StatementKind::Nop)) + blk.strip_nops(); } } } @@ -502,17 +502,22 @@ fn make_local_map( /// Keeps track of used & unused locals. struct UsedLocals { increment: bool, - arg_count: u32, use_count: IndexVec, + always_used: DenseBitSet, } impl UsedLocals { /// Determines which locals are used & unused in the given body. fn new(body: &Body<'_>) -> Self { + let mut always_used = debuginfo_locals(body); + always_used.insert(RETURN_PLACE); + for arg in body.args_iter() { + always_used.insert(arg); + } let mut this = Self { increment: true, - arg_count: body.arg_count.try_into().unwrap(), use_count: IndexVec::from_elem(0, &body.local_decls), + always_used, }; this.visit_body(body); this @@ -520,10 +525,16 @@ impl UsedLocals { /// Checks if local is used. /// - /// Return place and arguments are always considered used. + /// Return place, arguments, var debuginfo are always considered used. fn is_used(&self, local: Local) -> bool { - trace!("is_used({:?}): use_count: {:?}", local, self.use_count[local]); - local.as_u32() <= self.arg_count || self.use_count[local] != 0 + trace!( + "is_used({:?}): use_count: {:?}, always_used: {}", + local, + self.use_count[local], + self.always_used.contains(local) + ); + // To keep things simple, we don't handle debugging information here, these are in DSE. + self.always_used.contains(local) || self.use_count[local] != 0 } /// Updates the use counts to reflect the removal of given statement. @@ -568,17 +579,9 @@ impl<'tcx> Visitor<'tcx> for UsedLocals { StatementKind::ConstEvalCounter | StatementKind::Nop | StatementKind::StorageLive(..) - | StatementKind::StorageDead(..) => { - for debuginfo in statement.debuginfos.iter() { - self.visit_statement_debuginfo(debuginfo, location); - } - } - + | StatementKind::StorageDead(..) => {} StatementKind::Assign(box (ref place, ref rvalue)) => { if rvalue.is_safe_to_remove() { - for debuginfo in statement.debuginfos.iter() { - self.visit_statement_debuginfo(debuginfo, location); - } self.visit_lhs(place, location); self.visit_rvalue(rvalue, location); } else { @@ -589,18 +592,18 @@ impl<'tcx> Visitor<'tcx> for UsedLocals { StatementKind::SetDiscriminant { ref place, variant_index: _ } | StatementKind::Deinit(ref place) | StatementKind::BackwardIncompatibleDropHint { ref place, reason: _ } => { - for debuginfo in statement.debuginfos.iter() { - self.visit_statement_debuginfo(debuginfo, location); - } self.visit_lhs(place, location); } } } fn visit_local(&mut self, local: Local, ctx: PlaceContext, _location: Location) { + if matches!(ctx, PlaceContext::NonUse(_)) { + return; + } if self.increment { self.use_count[local] += 1; - } else if ctx != PlaceContext::NonUse(NonUseContext::VarDebugInfo) { + } else { assert_ne!(self.use_count[local], 0); self.use_count[local] -= 1; } @@ -620,28 +623,26 @@ fn remove_unused_definitions_helper(used_locals: &mut UsedLocals, body: &mut Bod for data in body.basic_blocks.as_mut_preserves_cfg() { // Remove unnecessary StorageLive and StorageDead annotations. - data.retain_statements(|statement| { - let keep = match &statement.kind { + for statement in data.statements.iter_mut() { + let keep_statement = match &statement.kind { StatementKind::StorageLive(local) | StatementKind::StorageDead(local) => { used_locals.is_used(*local) } - StatementKind::Assign(box (place, _)) => used_locals.is_used(place.local), - - StatementKind::SetDiscriminant { place, .. } - | StatementKind::BackwardIncompatibleDropHint { place, reason: _ } - | StatementKind::Deinit(place) => used_locals.is_used(place.local), - StatementKind::Nop => false, - _ => true, + StatementKind::Assign(box (place, _)) + | StatementKind::SetDiscriminant { box place, .. } + | StatementKind::BackwardIncompatibleDropHint { box place, .. } + | StatementKind::Deinit(box place) => used_locals.is_used(place.local), + _ => continue, }; - - if !keep { - trace!("removing statement {:?}", statement); - modified = true; - used_locals.statement_removed(statement); + if keep_statement { + continue; } - - keep - }); + trace!("removing statement {:?}", statement); + modified = true; + used_locals.statement_removed(statement); + statement.make_nop(true); + } + data.strip_nops(); } } } @@ -660,3 +661,42 @@ impl<'tcx> MutVisitor<'tcx> for LocalUpdater<'tcx> { *l = self.map[*l].unwrap(); } } + +pub(crate) struct UsedInStmtLocals { + pub(crate) locals: DenseBitSet, +} + +impl UsedInStmtLocals { + pub(crate) fn new(body: &Body<'_>) -> Self { + let mut this = Self { locals: DenseBitSet::new_empty(body.local_decls.len()) }; + this.visit_body(body); + this + } + + pub(crate) fn remove_unused_storage_annotations<'tcx>(&self, body: &mut Body<'tcx>) { + for data in body.basic_blocks.as_mut_preserves_cfg() { + // Remove unnecessary StorageLive and StorageDead annotations. + for statement in data.statements.iter_mut() { + let keep_statement = match &statement.kind { + StatementKind::StorageLive(local) | StatementKind::StorageDead(local) => { + self.locals.contains(*local) + } + _ => continue, + }; + if keep_statement { + continue; + } + statement.make_nop(true); + } + } + } +} + +impl<'tcx> Visitor<'tcx> for UsedInStmtLocals { + fn visit_local(&mut self, local: Local, context: PlaceContext, _: Location) { + if matches!(context, PlaceContext::NonUse(_)) { + return; + } + self.locals.insert(local); + } +} diff --git a/tests/mir-opt/dead-store-elimination/cycle.cycle.DeadStoreElimination-initial.diff b/tests/mir-opt/dead-store-elimination/cycle.cycle.DeadStoreElimination-initial.diff index ff18df1efcfc..d584de6861c0 100644 --- a/tests/mir-opt/dead-store-elimination/cycle.cycle.DeadStoreElimination-initial.diff +++ b/tests/mir-opt/dead-store-elimination/cycle.cycle.DeadStoreElimination-initial.diff @@ -19,10 +19,6 @@ - _3 = copy _2; - _2 = copy _1; - _1 = copy _5; -+ nop; -+ nop; -+ nop; -+ nop; _4 = cond() -> [return: bb1, unwind continue]; } diff --git a/tests/mir-opt/dead-store-elimination/ref.dead_first.DeadStoreElimination-initial.diff b/tests/mir-opt/dead-store-elimination/ref.dead_first.DeadStoreElimination-initial.diff index d823241bc620..2a793e24990a 100644 --- a/tests/mir-opt/dead-store-elimination/ref.dead_first.DeadStoreElimination-initial.diff +++ b/tests/mir-opt/dead-store-elimination/ref.dead_first.DeadStoreElimination-initial.diff @@ -15,7 +15,6 @@ StorageLive(_2); - _2 = &((*_1).2: i32); + // DBG: _2 = &((*_1).2: i32); -+ nop; StorageLive(_3); StorageLive(_4); _4 = &((*_1).0: i32); diff --git a/tests/mir-opt/dead-store-elimination/ref.tuple.DeadStoreElimination-initial.diff b/tests/mir-opt/dead-store-elimination/ref.tuple.DeadStoreElimination-initial.diff index 0547a42cab24..af28c1b9aa87 100644 --- a/tests/mir-opt/dead-store-elimination/ref.tuple.DeadStoreElimination-initial.diff +++ b/tests/mir-opt/dead-store-elimination/ref.tuple.DeadStoreElimination-initial.diff @@ -12,14 +12,13 @@ } bb0: { - StorageLive(_2); +- StorageLive(_2); _3 = deref_copy (_1.1: &Foo); - _2 = &((*_3).2: i32); + // DBG: _2 = &((*_3).2: i32); -+ nop; _4 = deref_copy (_1.1: &Foo); _0 = copy ((*_4).0: i32); - StorageDead(_2); +- StorageDead(_2); return; } } diff --git a/tests/mir-opt/debuginfo/simplifycfg.drop_debuginfo.SimplifyCfg-final.diff b/tests/mir-opt/debuginfo/simplifycfg.drop_debuginfo.SimplifyCfg-final.diff index 7a1ac92f6dde..d4a73351ee4a 100644 --- a/tests/mir-opt/debuginfo/simplifycfg.drop_debuginfo.SimplifyCfg-final.diff +++ b/tests/mir-opt/debuginfo/simplifycfg.drop_debuginfo.SimplifyCfg-final.diff @@ -14,13 +14,11 @@ - - bb1: { - // DBG: _3 = &((*_1).0: i32); -- nop; - goto -> bb2; - } - - bb2: { // DBG: _4 = &((*_1).1: i64); -- nop; _0 = copy ((*_1).2: i32); return; } diff --git a/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_1.SimplifyCfg-final.diff b/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_1.SimplifyCfg-final.diff index fa4d3aebf838..1c12358ad893 100644 --- a/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_1.SimplifyCfg-final.diff +++ b/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_1.SimplifyCfg-final.diff @@ -17,16 +17,13 @@ - bb1: { (*_2) = const true; // DBG: _3 = &((*_1).0: i32); -- nop; - goto -> bb2; - } - - bb2: { // DBG: _4 = &((*_1).1: i64); -- nop; _0 = copy ((*_1).2: i32); // DBG: _5 = &((*_1).2: i32); -- nop; return; } } diff --git a/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_2.SimplifyCfg-final.diff b/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_2.SimplifyCfg-final.diff index b01c91f196b6..de8e5612c878 100644 --- a/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_2.SimplifyCfg-final.diff +++ b/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_2.SimplifyCfg-final.diff @@ -16,16 +16,13 @@ - - bb1: { // DBG: _2 = &((*_1).0: i32); -- nop; - goto -> bb2; - } - - bb2: { // DBG: _3 = &((*_1).1: i64); -- nop; _0 = copy ((*_1).2: i32); // DBG: _4 = &((*_1).2: i32); -- nop; return; } } diff --git a/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_3.SimplifyCfg-final.diff b/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_3.SimplifyCfg-final.diff index caa90d79a1c2..11372a262a76 100644 --- a/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_3.SimplifyCfg-final.diff +++ b/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_3.SimplifyCfg-final.diff @@ -17,13 +17,11 @@ bb1: { - // DBG: _3 = &((*_1).0: i32); -- nop; - goto -> bb3; - } - - bb2: { // DBG: _4 = &((*_1).1: i64); -- nop; _0 = copy ((*_1).2: i32); return; } @@ -32,7 +30,6 @@ + bb2: { + // DBG: _3 = &((*_1).0: i32); // DBG: _5 = &((*_1).2: i32); -- nop; _0 = copy ((*_1).0: i32); return; } diff --git a/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_identical_succs.SimplifyCfg-final.diff b/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_identical_succs.SimplifyCfg-final.diff index d8c36990a5ff..0c6a75237d8b 100644 --- a/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_identical_succs.SimplifyCfg-final.diff +++ b/tests/mir-opt/debuginfo/simplifycfg.preserve_debuginfo_identical_succs.SimplifyCfg-final.diff @@ -16,16 +16,13 @@ - - bb1: { // DBG: _3 = &((*_1).0: i32); -- nop; - goto -> bb2; - } - - bb2: { // DBG: _4 = &((*_1).1: i64); -- nop; _0 = copy ((*_1).2: i32); // DBG: _5 = &((*_1).2: i32); -- nop; return; } } diff --git a/tests/mir-opt/inline/inline_shims.drop.Inline.panic-abort.diff b/tests/mir-opt/inline/inline_shims.drop.Inline.panic-abort.diff index f7729c7c5fa6..7fa22524f008 100644 --- a/tests/mir-opt/inline/inline_shims.drop.Inline.panic-abort.diff +++ b/tests/mir-opt/inline/inline_shims.drop.Inline.panic-abort.diff @@ -64,9 +64,7 @@ + StorageLive(_8); + StorageLive(_9); + StorageLive(_11); -+ // DBG: _11 = &((*_6).0: alloc::raw_vec::RawVec); + StorageLive(_12); -+ // DBG: _12 = &(((*_6).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner); + StorageLive(_13); + _13 = copy (((((*_6).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).0: std::ptr::Unique).0: std::ptr::NonNull); + _9 = copy _13 as *mut A (Transmute); @@ -92,7 +90,6 @@ _5 = copy _2; - _0 = drop_in_place::>(move _5) -> [return: bb2, unwind unreachable]; + StorageLive(_17); -+ StorageLive(_18); + _17 = discriminant((*_5)); + switchInt(move _17) -> [0: bb5, otherwise: bb6]; } @@ -118,7 +115,6 @@ + } + + bb5: { -+ StorageDead(_18); + StorageDead(_17); StorageDead(_5); return; diff --git a/tests/mir-opt/inline/inline_shims.drop.Inline.panic-unwind.diff b/tests/mir-opt/inline/inline_shims.drop.Inline.panic-unwind.diff index 183242764252..34f89da19f51 100644 --- a/tests/mir-opt/inline/inline_shims.drop.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline/inline_shims.drop.Inline.panic-unwind.diff @@ -27,13 +27,11 @@ _5 = copy _2; - _0 = drop_in_place::>(move _5) -> [return: bb2, unwind continue]; + StorageLive(_6); -+ StorageLive(_7); + _6 = discriminant((*_5)); + switchInt(move _6) -> [0: bb2, otherwise: bb3]; } bb2: { -+ StorageDead(_7); + StorageDead(_6); StorageDead(_5); return; diff --git a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff index 649b5a652027..bb81cb4e2600 100644 --- a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff +++ b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff @@ -129,11 +129,8 @@ _10 = deref_copy (_1.1: &mut std::task::Context<'_>); _9 = &mut (*_10); - _7 = <{async fn body of ActionPermit<'_, T>::perform()} as Future>::poll(move _8, move _9) -> [return: bb3, unwind unreachable]; -+ StorageLive(_11); -+ StorageLive(_15); + StorageLive(_16); + StorageLive(_25); -+ StorageLive(_27); + StorageLive(_30); + StorageLive(_31); + StorageLive(_32); @@ -166,11 +163,8 @@ + StorageDead(_32); + StorageDead(_31); + StorageDead(_30); -+ StorageDead(_27); + StorageDead(_25); + StorageDead(_16); -+ StorageDead(_15); -+ StorageDead(_11); StorageDead(_9); StorageDead(_8); StorageDead(_7); @@ -223,17 +217,13 @@ + _22 = &mut (*_23); + StorageDead(_24); + StorageLive(_44); -+ StorageLive(_45); + StorageLive(_49); + StorageLive(_41); + StorageLive(_42); + StorageLive(_43); -+ // DBG: _45 = &_19; + StorageLive(_46); -+ // DBG: _46 = &(_19.0: &mut std::future::Ready<()>); + _44 = copy (_19.0: &mut std::future::Ready<()>); + StorageDead(_46); -+ // DBG: _43 = &((*_44).0: std::option::Option<()>); + StorageLive(_47); + _47 = Option::<()>::None; + _42 = copy ((*_44).0: std::option::Option<()>); @@ -315,7 +305,6 @@ + _18 = Poll::<()>::Ready(move _41); + StorageDead(_41); + StorageDead(_49); -+ StorageDead(_45); + StorageDead(_44); + StorageDead(_22); + StorageDead(_19); diff --git a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff index 32021b20ef44..520c54824e15 100644 --- a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff @@ -131,11 +131,8 @@ _10 = deref_copy (_1.1: &mut std::task::Context<'_>); _9 = &mut (*_10); - _7 = <{async fn body of ActionPermit<'_, T>::perform()} as Future>::poll(move _8, move _9) -> [return: bb3, unwind: bb5]; -+ StorageLive(_11); -+ StorageLive(_15); + StorageLive(_16); + StorageLive(_25); -+ StorageLive(_27); + StorageLive(_30); + StorageLive(_31); + StorageLive(_32); @@ -180,11 +177,8 @@ + StorageDead(_32); + StorageDead(_31); + StorageDead(_30); -+ StorageDead(_27); + StorageDead(_25); + StorageDead(_16); -+ StorageDead(_15); -+ StorageDead(_11); StorageDead(_9); StorageDead(_8); StorageDead(_7); @@ -240,17 +234,13 @@ + _22 = &mut (*_23); + StorageDead(_24); + StorageLive(_46); -+ StorageLive(_47); + StorageLive(_51); + StorageLive(_43); + StorageLive(_44); + StorageLive(_45); -+ // DBG: _47 = &_19; + StorageLive(_48); -+ // DBG: _48 = &(_19.0: &mut std::future::Ready<()>); + _46 = copy (_19.0: &mut std::future::Ready<()>); + StorageDead(_48); -+ // DBG: _45 = &((*_46).0: std::option::Option<()>); + StorageLive(_49); + _49 = Option::<()>::None; + _44 = copy ((*_46).0: std::option::Option<()>); @@ -356,7 +346,6 @@ + _18 = Poll::<()>::Ready(move _43); + StorageDead(_43); + StorageDead(_51); -+ StorageDead(_47); + StorageDead(_46); + StorageDead(_22); + StorageDead(_19); diff --git a/tests/mir-opt/issue_101973.inner.GVN.panic-abort.diff b/tests/mir-opt/issue_101973.inner.GVN.panic-abort.diff index ac88fe67bb86..3ea7387a48d3 100644 --- a/tests/mir-opt/issue_101973.inner.GVN.panic-abort.diff +++ b/tests/mir-opt/issue_101973.inner.GVN.panic-abort.diff @@ -30,7 +30,6 @@ StorageLive(_4); StorageLive(_5); _5 = copy _1; - nop; - StorageLive(_14); - _14 = BitAnd(copy _5, const 255_u32); - _4 = BitOr(const 0_u32, move _14); diff --git a/tests/mir-opt/issue_101973.inner.GVN.panic-unwind.diff b/tests/mir-opt/issue_101973.inner.GVN.panic-unwind.diff index 96c3cae2d334..832db856b2cf 100644 --- a/tests/mir-opt/issue_101973.inner.GVN.panic-unwind.diff +++ b/tests/mir-opt/issue_101973.inner.GVN.panic-unwind.diff @@ -30,7 +30,6 @@ StorageLive(_4); StorageLive(_5); _5 = copy _1; - nop; - StorageLive(_14); - _14 = BitAnd(copy _5, const 255_u32); - _4 = BitOr(const 0_u32, move _14); diff --git a/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-abort.diff b/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-abort.diff index a294c1b4588d..614d9ad440d2 100644 --- a/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-abort.diff +++ b/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-abort.diff @@ -33,15 +33,9 @@ } bb2: { - StorageLive(_5); // DBG: _5 = &(*_2)[0 of 3]; - StorageLive(_6); // DBG: _6 = &(*_2)[1 of 3]; - StorageLive(_7); // DBG: _7 = &(*_2)[2 of 3]; - StorageDead(_7); - StorageDead(_6); - StorageDead(_5); StorageDead(_4); return; } diff --git a/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-unwind.diff b/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-unwind.diff index 691271cf94ed..57a88cf89841 100644 --- a/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-unwind.diff +++ b/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-unwind.diff @@ -33,15 +33,9 @@ } bb2: { - StorageLive(_5); // DBG: _5 = &(*_2)[0 of 3]; - StorageLive(_6); // DBG: _6 = &(*_2)[1 of 3]; - StorageLive(_7); // DBG: _7 = &(*_2)[2 of 3]; - StorageDead(_7); - StorageDead(_6); - StorageDead(_5); StorageDead(_4); return; } diff --git a/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.32bit.panic-abort.mir b/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.32bit.panic-abort.mir index d4248f5d87fb..b5c23822162c 100644 --- a/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.32bit.panic-abort.mir +++ b/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.32bit.panic-abort.mir @@ -6,7 +6,6 @@ fn num_to_digit(_1: char) -> u32 { let mut _4: std::option::Option; scope 1 (inlined char::methods::::is_digit) { let _2: std::option::Option; - let mut _7: &std::option::Option; scope 2 (inlined Option::::is_some) { let mut _3: isize; scope 3 { @@ -21,17 +20,14 @@ fn num_to_digit(_1: char) -> u32 { } bb0: { - StorageLive(_7); StorageLive(_2); _2 = char::methods::::to_digit(copy _1, const 8_u32) -> [return: bb1, unwind unreachable]; } bb1: { - // DBG: _7 = &_2; StorageLive(_3); _3 = discriminant(_2); StorageDead(_2); - StorageDead(_7); switchInt(move _3) -> [1: bb2, otherwise: bb7]; } diff --git a/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.32bit.panic-unwind.mir b/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.32bit.panic-unwind.mir index a834932df5a8..f22b8835735d 100644 --- a/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.32bit.panic-unwind.mir +++ b/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.32bit.panic-unwind.mir @@ -6,7 +6,6 @@ fn num_to_digit(_1: char) -> u32 { let mut _4: std::option::Option; scope 1 (inlined char::methods::::is_digit) { let _2: std::option::Option; - let mut _7: &std::option::Option; scope 2 (inlined Option::::is_some) { let mut _3: isize; scope 3 { @@ -21,17 +20,14 @@ fn num_to_digit(_1: char) -> u32 { } bb0: { - StorageLive(_7); StorageLive(_2); _2 = char::methods::::to_digit(copy _1, const 8_u32) -> [return: bb1, unwind continue]; } bb1: { - // DBG: _7 = &_2; StorageLive(_3); _3 = discriminant(_2); StorageDead(_2); - StorageDead(_7); switchInt(move _3) -> [1: bb2, otherwise: bb7]; } diff --git a/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.64bit.panic-abort.mir b/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.64bit.panic-abort.mir index d4248f5d87fb..b5c23822162c 100644 --- a/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.64bit.panic-abort.mir +++ b/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.64bit.panic-abort.mir @@ -6,7 +6,6 @@ fn num_to_digit(_1: char) -> u32 { let mut _4: std::option::Option; scope 1 (inlined char::methods::::is_digit) { let _2: std::option::Option; - let mut _7: &std::option::Option; scope 2 (inlined Option::::is_some) { let mut _3: isize; scope 3 { @@ -21,17 +20,14 @@ fn num_to_digit(_1: char) -> u32 { } bb0: { - StorageLive(_7); StorageLive(_2); _2 = char::methods::::to_digit(copy _1, const 8_u32) -> [return: bb1, unwind unreachable]; } bb1: { - // DBG: _7 = &_2; StorageLive(_3); _3 = discriminant(_2); StorageDead(_2); - StorageDead(_7); switchInt(move _3) -> [1: bb2, otherwise: bb7]; } diff --git a/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.64bit.panic-unwind.mir b/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.64bit.panic-unwind.mir index a834932df5a8..f22b8835735d 100644 --- a/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.64bit.panic-unwind.mir +++ b/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.64bit.panic-unwind.mir @@ -6,7 +6,6 @@ fn num_to_digit(_1: char) -> u32 { let mut _4: std::option::Option; scope 1 (inlined char::methods::::is_digit) { let _2: std::option::Option; - let mut _7: &std::option::Option; scope 2 (inlined Option::::is_some) { let mut _3: isize; scope 3 { @@ -21,17 +20,14 @@ fn num_to_digit(_1: char) -> u32 { } bb0: { - StorageLive(_7); StorageLive(_2); _2 = char::methods::::to_digit(copy _1, const 8_u32) -> [return: bb1, unwind continue]; } bb1: { - // DBG: _7 = &_2; StorageLive(_3); _3 = discriminant(_2); StorageDead(_2); - StorageDead(_7); switchInt(move _3) -> [1: bb2, otherwise: bb7]; } diff --git a/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-abort.mir index 23dbc066dd52..83478e60b5d4 100644 --- a/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-abort.mir @@ -5,9 +5,7 @@ fn step_forward(_1: u16, _2: usize) -> u16 { debug n => _2; let mut _0: u16; scope 1 (inlined ::forward) { - let _8: std::option::Option; - let mut _10: u16; - let mut _11: &std::option::Option; + let mut _8: u16; scope 2 { } scope 3 (inlined ::forward_checked) { @@ -15,9 +13,8 @@ fn step_forward(_1: u16, _2: usize) -> u16 { scope 6 (inlined core::num::::checked_add) { let mut _5: (u16, bool); let mut _6: bool; - let mut _7: u16; scope 7 (inlined std::intrinsics::unlikely) { - let _9: (); + let _7: (); } } } @@ -38,8 +35,6 @@ fn step_forward(_1: u16, _2: usize) -> u16 { bb0: { StorageLive(_4); - StorageLive(_11); - StorageLive(_8); StorageLive(_3); _3 = Gt(copy _2, const 65535_usize); switchInt(move _3) -> [0: bb1, otherwise: bb5]; @@ -58,55 +53,34 @@ fn step_forward(_1: u16, _2: usize) -> u16 { bb2: { StorageDead(_5); StorageDead(_6); - StorageLive(_7); - _7 = AddUnchecked(copy _1, copy _4); - _8 = Option::::Some(move _7); - StorageDead(_7); - // DBG: _11 = &_8; - StorageDead(_8); - StorageDead(_11); goto -> bb7; } bb3: { - _9 = std::intrinsics::cold_path() -> [return: bb4, unwind unreachable]; + _7 = std::intrinsics::cold_path() -> [return: bb4, unwind unreachable]; } bb4: { StorageDead(_5); StorageDead(_6); - _8 = const Option::::None; - // DBG: _11 = &_8; goto -> bb6; } bb5: { StorageDead(_3); - _8 = const Option::::None; - // DBG: _11 = &_8; goto -> bb6; } bb6: { - StorageDead(_8); - StorageDead(_11); assert(!const true, "attempt to compute `{} + {}`, which would overflow", const core::num::::MAX, const 1_u16) -> [success: bb7, unwind unreachable]; } bb7: { - StorageLive(_10); - _10 = copy _2 as u16 (IntToInt); - _0 = Add(copy _1, copy _10); - StorageDead(_10); + StorageLive(_8); + _8 = copy _2 as u16 (IntToInt); + _0 = Add(copy _1, copy _8); + StorageDead(_8); StorageDead(_4); return; } } - -ALLOC0 (size: 4, align: 2) { - 00 00 __ __ │ ..░░ -} - -ALLOC1 (size: 4, align: 2) { - 00 00 __ __ │ ..░░ -} diff --git a/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-unwind.mir index ac15f070597e..ac7a6e044519 100644 --- a/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-unwind.mir @@ -5,9 +5,7 @@ fn step_forward(_1: u16, _2: usize) -> u16 { debug n => _2; let mut _0: u16; scope 1 (inlined ::forward) { - let _8: std::option::Option; - let mut _10: u16; - let mut _11: &std::option::Option; + let mut _8: u16; scope 2 { } scope 3 (inlined ::forward_checked) { @@ -15,9 +13,8 @@ fn step_forward(_1: u16, _2: usize) -> u16 { scope 6 (inlined core::num::::checked_add) { let mut _5: (u16, bool); let mut _6: bool; - let mut _7: u16; scope 7 (inlined std::intrinsics::unlikely) { - let _9: (); + let _7: (); } } } @@ -38,8 +35,6 @@ fn step_forward(_1: u16, _2: usize) -> u16 { bb0: { StorageLive(_4); - StorageLive(_11); - StorageLive(_8); StorageLive(_3); _3 = Gt(copy _2, const 65535_usize); switchInt(move _3) -> [0: bb1, otherwise: bb5]; @@ -58,55 +53,34 @@ fn step_forward(_1: u16, _2: usize) -> u16 { bb2: { StorageDead(_5); StorageDead(_6); - StorageLive(_7); - _7 = AddUnchecked(copy _1, copy _4); - _8 = Option::::Some(move _7); - StorageDead(_7); - // DBG: _11 = &_8; - StorageDead(_8); - StorageDead(_11); goto -> bb7; } bb3: { - _9 = std::intrinsics::cold_path() -> [return: bb4, unwind unreachable]; + _7 = std::intrinsics::cold_path() -> [return: bb4, unwind unreachable]; } bb4: { StorageDead(_5); StorageDead(_6); - _8 = const Option::::None; - // DBG: _11 = &_8; goto -> bb6; } bb5: { StorageDead(_3); - _8 = const Option::::None; - // DBG: _11 = &_8; goto -> bb6; } bb6: { - StorageDead(_8); - StorageDead(_11); assert(!const true, "attempt to compute `{} + {}`, which would overflow", const core::num::::MAX, const 1_u16) -> [success: bb7, unwind continue]; } bb7: { - StorageLive(_10); - _10 = copy _2 as u16 (IntToInt); - _0 = Add(copy _1, copy _10); - StorageDead(_10); + StorageLive(_8); + _8 = copy _2 as u16 (IntToInt); + _0 = Add(copy _1, copy _8); + StorageDead(_8); StorageDead(_4); return; } } - -ALLOC0 (size: 4, align: 2) { - 00 00 __ __ │ ..░░ -} - -ALLOC1 (size: 4, align: 2) { - 00 00 __ __ │ ..░░ -} diff --git a/tests/mir-opt/pre-codegen/clone_as_copy.clone_as_copy.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/clone_as_copy.clone_as_copy.PreCodegen.after.mir index 66239a0ef663..1e6e2ee1b8b7 100644 --- a/tests/mir-opt/pre-codegen/clone_as_copy.clone_as_copy.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/clone_as_copy.clone_as_copy.PreCodegen.after.mir @@ -12,10 +12,8 @@ fn clone_as_copy(_1: &NestCopy) -> NestCopy { } bb0: { - StorageLive(_2); // DBG: _2 = &((*_1).1: AllCopy); _0 = copy (*_1); - StorageDead(_2); return; } } diff --git a/tests/mir-opt/pre-codegen/clone_as_copy.enum_clone_as_copy.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/clone_as_copy.enum_clone_as_copy.PreCodegen.after.mir index aa03cec070ad..76bb49bc9c1b 100644 --- a/tests/mir-opt/pre-codegen/clone_as_copy.enum_clone_as_copy.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/clone_as_copy.enum_clone_as_copy.PreCodegen.after.mir @@ -5,58 +5,28 @@ fn enum_clone_as_copy(_1: &Enum1) -> Enum1 { let mut _0: Enum1; scope 1 (inlined ::clone) { debug self => _1; - let mut _2: isize; - let _3: &AllCopy; - let _4: &NestCopy; + let _2: &AllCopy; + let _3: &NestCopy; scope 2 { - debug __self_0 => _3; + debug __self_0 => _2; scope 6 (inlined ::clone) { - debug self => _3; + debug self => _2; } } scope 3 { - debug __self_0 => _4; + debug __self_0 => _3; scope 4 (inlined ::clone) { - debug self => _4; - let _5: &AllCopy; + debug self => _3; + let _4: &AllCopy; scope 5 (inlined ::clone) { - debug self => _5; + debug self => _4; } } } } bb0: { - StorageLive(_2); - StorageLive(_3); - StorageLive(_4); - _2 = discriminant((*_1)); - switchInt(move _2) -> [0: bb1, 1: bb2, otherwise: bb4]; - } - - bb1: { - // DBG: _3 = &(((*_1) as A).0: AllCopy); _0 = copy (*_1); - goto -> bb3; - } - - bb2: { - // DBG: _4 = &(((*_1) as B).0: NestCopy); - StorageLive(_5); - // DBG: _5 = &((((*_1) as B).0: NestCopy).1: AllCopy); - StorageDead(_5); - _0 = copy (*_1); - goto -> bb3; - } - - bb3: { - StorageDead(_4); - StorageDead(_3); - StorageDead(_2); return; } - - bb4: { - unreachable; - } } diff --git a/tests/mir-opt/pre-codegen/clone_as_copy.rs b/tests/mir-opt/pre-codegen/clone_as_copy.rs index f5ff1854d387..00f24754d591 100644 --- a/tests/mir-opt/pre-codegen/clone_as_copy.rs +++ b/tests/mir-opt/pre-codegen/clone_as_copy.rs @@ -25,19 +25,19 @@ enum Enum1 { // EMIT_MIR clone_as_copy.clone_as_copy.PreCodegen.after.mir fn clone_as_copy(v: &NestCopy) -> NestCopy { // CHECK-LABEL: fn clone_as_copy( - // CHECK-NOT: = AllCopy { {{.*}} }; - // CHECK-NOT: = NestCopy { {{.*}} }; - // CHECK: _0 = copy (*_1); - // CHECK: return; + // CHECK: let [[DEAD_VAR:_.*]]: &AllCopy; + // CHECK: bb0: { + // CHECK-NEXT: DBG: [[DEAD_VAR]] = &((*_1).1: AllCopy) + // CHECK-NEXT: _0 = copy (*_1); + // CHECK-NEXT: return; v.clone() } -// FIXME: We can merge into exactly one assignment statement. // EMIT_MIR clone_as_copy.enum_clone_as_copy.PreCodegen.after.mir fn enum_clone_as_copy(v: &Enum1) -> Enum1 { // CHECK-LABEL: fn enum_clone_as_copy( - // CHECK-NOT: = Enum1:: - // CHECK: _0 = copy (*_1); - // CHECK: _0 = copy (*_1); + // CHECK: bb0: { + // CHECK-NEXT: _0 = copy (*_1); + // CHECK-NEXT: return; v.clone() } diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-abort.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-abort.mir index 0dd2125dbe0d..ba6ce0ee5286 100644 --- a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-abort.mir @@ -8,10 +8,9 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { let _2: std::ptr::NonNull<[T]>; let mut _3: *mut [T]; let mut _4: *const [T]; - let _12: (); - let mut _13: &std::alloc::Layout; + let _11: (); scope 3 { - let _8: std::alloc::Layout; + let _8: std::ptr::alignment::AlignmentEnum; scope 4 { scope 12 (inlined Layout::size) { } @@ -27,19 +26,15 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } scope 18 (inlined ::deallocate) { let mut _9: *mut u8; - let mut _14: &std::alloc::Layout; scope 19 (inlined Layout::size) { } scope 20 (inlined NonNull::::as_ptr) { } scope 21 (inlined std::alloc::dealloc) { - let mut _11: usize; - let mut _15: &std::alloc::Layout; - let mut _16: &std::alloc::Layout; + let mut _10: usize; scope 22 (inlined Layout::size) { } scope 23 (inlined Layout::align) { - let mut _10: std::ptr::alignment::AlignmentEnum; scope 24 (inlined std::ptr::Alignment::as_usize) { } } @@ -68,7 +63,6 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } bb0: { - StorageLive(_8); StorageLive(_2); _2 = copy (((*_1).0: std::ptr::Unique<[T]>).0: std::ptr::NonNull<[T]>); StorageLive(_4); @@ -80,45 +74,31 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { bb1: { _6 = AlignOf(T); + StorageLive(_7); _7 = copy _6 as std::ptr::Alignment (Transmute); - _8 = Layout { size: copy _5, align: copy _7 }; + _8 = move (_7.0: std::ptr::alignment::AlignmentEnum); + StorageDead(_7); StorageDead(_6); StorageDead(_4); - StorageLive(_13); - // DBG: _13 = &_8; - StorageDead(_13); switchInt(copy _5) -> [0: bb4, otherwise: bb2]; } bb2: { - StorageLive(_14); - // DBG: _14 = &_8; - StorageDead(_14); StorageLive(_9); _9 = copy _3 as *mut u8 (PtrToPtr); - StorageLive(_15); - // DBG: _15 = &_8; - StorageDead(_15); - StorageLive(_11); - StorageLive(_16); - // DBG: _16 = &_8; StorageLive(_10); - _10 = copy (_7.0: std::ptr::alignment::AlignmentEnum); - _11 = discriminant(_10); - StorageDead(_10); - StorageDead(_16); - _12 = alloc::alloc::__rust_dealloc(move _9, move _5, move _11) -> [return: bb3, unwind unreachable]; + _10 = discriminant(_8); + _11 = alloc::alloc::__rust_dealloc(move _9, move _5, move _10) -> [return: bb3, unwind unreachable]; } bb3: { - StorageDead(_11); + StorageDead(_10); StorageDead(_9); goto -> bb4; } bb4: { StorageDead(_2); - StorageDead(_8); return; } } diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-unwind.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-unwind.mir index 0dd2125dbe0d..ba6ce0ee5286 100644 --- a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-unwind.mir @@ -8,10 +8,9 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { let _2: std::ptr::NonNull<[T]>; let mut _3: *mut [T]; let mut _4: *const [T]; - let _12: (); - let mut _13: &std::alloc::Layout; + let _11: (); scope 3 { - let _8: std::alloc::Layout; + let _8: std::ptr::alignment::AlignmentEnum; scope 4 { scope 12 (inlined Layout::size) { } @@ -27,19 +26,15 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } scope 18 (inlined ::deallocate) { let mut _9: *mut u8; - let mut _14: &std::alloc::Layout; scope 19 (inlined Layout::size) { } scope 20 (inlined NonNull::::as_ptr) { } scope 21 (inlined std::alloc::dealloc) { - let mut _11: usize; - let mut _15: &std::alloc::Layout; - let mut _16: &std::alloc::Layout; + let mut _10: usize; scope 22 (inlined Layout::size) { } scope 23 (inlined Layout::align) { - let mut _10: std::ptr::alignment::AlignmentEnum; scope 24 (inlined std::ptr::Alignment::as_usize) { } } @@ -68,7 +63,6 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } bb0: { - StorageLive(_8); StorageLive(_2); _2 = copy (((*_1).0: std::ptr::Unique<[T]>).0: std::ptr::NonNull<[T]>); StorageLive(_4); @@ -80,45 +74,31 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { bb1: { _6 = AlignOf(T); + StorageLive(_7); _7 = copy _6 as std::ptr::Alignment (Transmute); - _8 = Layout { size: copy _5, align: copy _7 }; + _8 = move (_7.0: std::ptr::alignment::AlignmentEnum); + StorageDead(_7); StorageDead(_6); StorageDead(_4); - StorageLive(_13); - // DBG: _13 = &_8; - StorageDead(_13); switchInt(copy _5) -> [0: bb4, otherwise: bb2]; } bb2: { - StorageLive(_14); - // DBG: _14 = &_8; - StorageDead(_14); StorageLive(_9); _9 = copy _3 as *mut u8 (PtrToPtr); - StorageLive(_15); - // DBG: _15 = &_8; - StorageDead(_15); - StorageLive(_11); - StorageLive(_16); - // DBG: _16 = &_8; StorageLive(_10); - _10 = copy (_7.0: std::ptr::alignment::AlignmentEnum); - _11 = discriminant(_10); - StorageDead(_10); - StorageDead(_16); - _12 = alloc::alloc::__rust_dealloc(move _9, move _5, move _11) -> [return: bb3, unwind unreachable]; + _10 = discriminant(_8); + _11 = alloc::alloc::__rust_dealloc(move _9, move _5, move _10) -> [return: bb3, unwind unreachable]; } bb3: { - StorageDead(_11); + StorageDead(_10); StorageDead(_9); goto -> bb4; } bb4: { StorageDead(_2); - StorageDead(_8); return; } } diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-abort.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-abort.mir index 0dd2125dbe0d..ba6ce0ee5286 100644 --- a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-abort.mir @@ -8,10 +8,9 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { let _2: std::ptr::NonNull<[T]>; let mut _3: *mut [T]; let mut _4: *const [T]; - let _12: (); - let mut _13: &std::alloc::Layout; + let _11: (); scope 3 { - let _8: std::alloc::Layout; + let _8: std::ptr::alignment::AlignmentEnum; scope 4 { scope 12 (inlined Layout::size) { } @@ -27,19 +26,15 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } scope 18 (inlined ::deallocate) { let mut _9: *mut u8; - let mut _14: &std::alloc::Layout; scope 19 (inlined Layout::size) { } scope 20 (inlined NonNull::::as_ptr) { } scope 21 (inlined std::alloc::dealloc) { - let mut _11: usize; - let mut _15: &std::alloc::Layout; - let mut _16: &std::alloc::Layout; + let mut _10: usize; scope 22 (inlined Layout::size) { } scope 23 (inlined Layout::align) { - let mut _10: std::ptr::alignment::AlignmentEnum; scope 24 (inlined std::ptr::Alignment::as_usize) { } } @@ -68,7 +63,6 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } bb0: { - StorageLive(_8); StorageLive(_2); _2 = copy (((*_1).0: std::ptr::Unique<[T]>).0: std::ptr::NonNull<[T]>); StorageLive(_4); @@ -80,45 +74,31 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { bb1: { _6 = AlignOf(T); + StorageLive(_7); _7 = copy _6 as std::ptr::Alignment (Transmute); - _8 = Layout { size: copy _5, align: copy _7 }; + _8 = move (_7.0: std::ptr::alignment::AlignmentEnum); + StorageDead(_7); StorageDead(_6); StorageDead(_4); - StorageLive(_13); - // DBG: _13 = &_8; - StorageDead(_13); switchInt(copy _5) -> [0: bb4, otherwise: bb2]; } bb2: { - StorageLive(_14); - // DBG: _14 = &_8; - StorageDead(_14); StorageLive(_9); _9 = copy _3 as *mut u8 (PtrToPtr); - StorageLive(_15); - // DBG: _15 = &_8; - StorageDead(_15); - StorageLive(_11); - StorageLive(_16); - // DBG: _16 = &_8; StorageLive(_10); - _10 = copy (_7.0: std::ptr::alignment::AlignmentEnum); - _11 = discriminant(_10); - StorageDead(_10); - StorageDead(_16); - _12 = alloc::alloc::__rust_dealloc(move _9, move _5, move _11) -> [return: bb3, unwind unreachable]; + _10 = discriminant(_8); + _11 = alloc::alloc::__rust_dealloc(move _9, move _5, move _10) -> [return: bb3, unwind unreachable]; } bb3: { - StorageDead(_11); + StorageDead(_10); StorageDead(_9); goto -> bb4; } bb4: { StorageDead(_2); - StorageDead(_8); return; } } diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-unwind.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-unwind.mir index 0dd2125dbe0d..ba6ce0ee5286 100644 --- a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-unwind.mir @@ -8,10 +8,9 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { let _2: std::ptr::NonNull<[T]>; let mut _3: *mut [T]; let mut _4: *const [T]; - let _12: (); - let mut _13: &std::alloc::Layout; + let _11: (); scope 3 { - let _8: std::alloc::Layout; + let _8: std::ptr::alignment::AlignmentEnum; scope 4 { scope 12 (inlined Layout::size) { } @@ -27,19 +26,15 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } scope 18 (inlined ::deallocate) { let mut _9: *mut u8; - let mut _14: &std::alloc::Layout; scope 19 (inlined Layout::size) { } scope 20 (inlined NonNull::::as_ptr) { } scope 21 (inlined std::alloc::dealloc) { - let mut _11: usize; - let mut _15: &std::alloc::Layout; - let mut _16: &std::alloc::Layout; + let mut _10: usize; scope 22 (inlined Layout::size) { } scope 23 (inlined Layout::align) { - let mut _10: std::ptr::alignment::AlignmentEnum; scope 24 (inlined std::ptr::Alignment::as_usize) { } } @@ -68,7 +63,6 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } bb0: { - StorageLive(_8); StorageLive(_2); _2 = copy (((*_1).0: std::ptr::Unique<[T]>).0: std::ptr::NonNull<[T]>); StorageLive(_4); @@ -80,45 +74,31 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { bb1: { _6 = AlignOf(T); + StorageLive(_7); _7 = copy _6 as std::ptr::Alignment (Transmute); - _8 = Layout { size: copy _5, align: copy _7 }; + _8 = move (_7.0: std::ptr::alignment::AlignmentEnum); + StorageDead(_7); StorageDead(_6); StorageDead(_4); - StorageLive(_13); - // DBG: _13 = &_8; - StorageDead(_13); switchInt(copy _5) -> [0: bb4, otherwise: bb2]; } bb2: { - StorageLive(_14); - // DBG: _14 = &_8; - StorageDead(_14); StorageLive(_9); _9 = copy _3 as *mut u8 (PtrToPtr); - StorageLive(_15); - // DBG: _15 = &_8; - StorageDead(_15); - StorageLive(_11); - StorageLive(_16); - // DBG: _16 = &_8; StorageLive(_10); - _10 = copy (_7.0: std::ptr::alignment::AlignmentEnum); - _11 = discriminant(_10); - StorageDead(_10); - StorageDead(_16); - _12 = alloc::alloc::__rust_dealloc(move _9, move _5, move _11) -> [return: bb3, unwind unreachable]; + _10 = discriminant(_8); + _11 = alloc::alloc::__rust_dealloc(move _9, move _5, move _10) -> [return: bb3, unwind unreachable]; } bb3: { - StorageDead(_11); + StorageDead(_10); StorageDead(_9); goto -> bb4; } bb4: { StorageDead(_2); - StorageDead(_8); return; } } diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.rs b/tests/mir-opt/pre-codegen/drop_boxed_slice.rs index c291366a694e..9ceba9444b8d 100644 --- a/tests/mir-opt/pre-codegen/drop_boxed_slice.rs +++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.rs @@ -11,7 +11,7 @@ pub unsafe fn generic_in_place(ptr: *mut Box<[T]>) { // CHECK: [[SIZE:_.+]] = std::intrinsics::size_of_val::<[T]> // CHECK: [[ALIGN:_.+]] = AlignOf(T); // CHECK: [[B:_.+]] = copy [[ALIGN]] as std::ptr::Alignment (Transmute); - // CHECK: [[C:_.+]] = copy ([[B]].0: std::ptr::alignment::AlignmentEnum); + // CHECK: [[C:_.+]] = move ([[B]].0: std::ptr::alignment::AlignmentEnum); // CHECK: [[D:_.+]] = discriminant([[C]]); // CHECK: = alloc::alloc::__rust_dealloc({{.+}}, move [[SIZE]], move [[D]]) -> std::ptr::drop_in_place(ptr) diff --git a/tests/mir-opt/pre-codegen/loops.int_range.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/loops.int_range.PreCodegen.after.mir index b1af3141ee61..beb7b936ccf7 100644 --- a/tests/mir-opt/pre-codegen/loops.int_range.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/loops.int_range.PreCodegen.after.mir @@ -60,9 +60,7 @@ fn int_range(_1: usize, _2: usize) -> () { StorageLive(_9); // DBG: _12 = &_3; StorageLive(_6); - StorageLive(_13); // DBG: _13 = &(_3.0: usize); - StorageLive(_14); // DBG: _14 = &(_3.1: usize); StorageLive(_4); _4 = copy (_3.0: usize); @@ -75,16 +73,12 @@ fn int_range(_1: usize, _2: usize) -> () { } bb2: { - StorageDead(_14); - StorageDead(_13); StorageDead(_6); StorageDead(_9); return; } bb3: { - StorageDead(_14); - StorageDead(_13); _7 = copy (_3.0: usize); StorageLive(_8); _8 = AddUnchecked(copy _7, const 1_usize); diff --git a/tests/mir-opt/pre-codegen/loops.vec_move.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/loops.vec_move.PreCodegen.after.mir index 03cdc221c308..f453741dc6c7 100644 --- a/tests/mir-opt/pre-codegen/loops.vec_move.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/loops.vec_move.PreCodegen.after.mir @@ -171,12 +171,9 @@ fn vec_move(_1: Vec) -> () { bb0: { StorageLive(_22); - StorageLive(_29); StorageLive(_6); StorageLive(_7); - StorageLive(_33); StorageLive(_11); - StorageLive(_35); StorageLive(_20); StorageLive(_5); StorageLive(_4); @@ -184,30 +181,18 @@ fn vec_move(_1: Vec) -> () { StorageLive(_2); _2 = ManuallyDrop::> { value: copy _1 }; StorageLive(_3); - StorageLive(_30); // DBG: _30 = &_2; // DBG: _29 = &(_2.0: std::vec::Vec); - StorageDead(_30); - StorageLive(_39); // DBG: _39 = &((_2.0: std::vec::Vec).0: alloc::raw_vec::RawVec); - StorageLive(_40); // DBG: _40 = &(((_2.0: std::vec::Vec).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner); - StorageDead(_40); - StorageDead(_39); _3 = &raw const ((((_2.0: std::vec::Vec).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).2: std::alloc::Global); StorageDead(_3); - StorageLive(_31); - StorageLive(_32); // DBG: _32 = &_2; - StorageDead(_32); // DBG: _31 = &((_2.0: std::vec::Vec).0: alloc::raw_vec::RawVec); - StorageLive(_41); // DBG: _41 = &(((_2.0: std::vec::Vec).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner); _4 = copy (((((_2.0: std::vec::Vec).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).0: std::ptr::Unique).0: std::ptr::NonNull); _5 = copy _4 as *const impl Sized (Transmute); _6 = NonNull:: { pointer: copy _5 }; - StorageDead(_41); - StorageDead(_31); _7 = copy _4 as *mut impl Sized (Transmute); switchInt(const ::IS_ZST) -> [0: bb1, otherwise: bb2]; } @@ -215,10 +200,8 @@ fn vec_move(_1: Vec) -> () { bb1: { StorageLive(_10); StorageLive(_8); - StorageLive(_36); // DBG: _36 = &_2; // DBG: _35 = &(_2.0: std::vec::Vec); - StorageDead(_36); _8 = copy ((_2.0: std::vec::Vec).1: usize); StorageLive(_9); _9 = Le(copy _8, const ::MAX_SLICE_LEN); @@ -233,10 +216,8 @@ fn vec_move(_1: Vec) -> () { bb2: { StorageLive(_12); - StorageLive(_34); // DBG: _34 = &_2; // DBG: _33 = &(_2.0: std::vec::Vec); - StorageDead(_34); _12 = copy ((_2.0: std::vec::Vec).1: usize); StorageLive(_13); _13 = Le(copy _12, const ::MAX_SLICE_LEN); @@ -264,12 +245,8 @@ fn vec_move(_1: Vec) -> () { } bb4: { - StorageLive(_37); - StorageLive(_38); // DBG: _38 = &_2; - StorageDead(_38); // DBG: _37 = &((_2.0: std::vec::Vec).0: alloc::raw_vec::RawVec); - StorageLive(_42); // DBG: _42 = &(((_2.0: std::vec::Vec).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner); StorageLive(_19); _19 = SizeOf(impl Sized); @@ -291,20 +268,15 @@ fn vec_move(_1: Vec) -> () { bb7: { StorageDead(_19); - StorageDead(_42); - StorageDead(_37); _22 = std::vec::IntoIter:: { buf: copy _6, phantom: const ZeroSized: PhantomData, cap: move _20, alloc: const ManuallyDrop:: {{ value: std::alloc::Global }}, ptr: copy _6, end: copy _11 }; StorageDead(_2); StorageDead(_17); StorageDead(_4); StorageDead(_5); StorageDead(_20); - StorageDead(_35); StorageDead(_11); - StorageDead(_33); StorageDead(_7); StorageDead(_6); - StorageDead(_29); StorageLive(_23); _23 = move _22; goto -> bb8; diff --git a/tests/mir-opt/pre-codegen/slice_filter.variant_a-{closure#0}.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/slice_filter.variant_a-{closure#0}.PreCodegen.after.mir index 9d4bacef954e..86c729b3e95e 100644 --- a/tests/mir-opt/pre-codegen/slice_filter.variant_a-{closure#0}.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/slice_filter.variant_a-{closure#0}.PreCodegen.after.mir @@ -70,9 +70,7 @@ fn variant_a::{closure#0}(_1: &mut {closure@$DIR/slice_filter.rs:7:25: 7:39}, _2 // DBG: _15 = &((*_3).2: usize); // DBG: _16 = &((*_3).3: usize); StorageLive(_6); - StorageLive(_17); // DBG: _17 = &_13; - StorageLive(_18); // DBG: _18 = &_15; _4 = copy ((*_3).0: usize); _5 = copy ((*_3).2: usize); @@ -81,18 +79,12 @@ fn variant_a::{closure#0}(_1: &mut {closure@$DIR/slice_filter.rs:7:25: 7:39}, _2 } bb1: { - StorageDead(_18); - StorageDead(_17); goto -> bb4; } bb2: { - StorageDead(_18); - StorageDead(_17); StorageLive(_9); - StorageLive(_19); // DBG: _19 = &_16; - StorageLive(_20); // DBG: _20 = &_14; StorageLive(_7); _7 = copy ((*_3).3: usize); @@ -105,34 +97,24 @@ fn variant_a::{closure#0}(_1: &mut {closure@$DIR/slice_filter.rs:7:25: 7:39}, _2 } bb3: { - StorageDead(_20); - StorageDead(_19); goto -> bb4; } bb4: { StorageLive(_10); - StorageLive(_21); // DBG: _21 = &_15; - StorageLive(_22); // DBG: _22 = &_13; _10 = Le(copy _5, copy _4); switchInt(move _10) -> [0: bb5, otherwise: bb6]; } bb5: { - StorageDead(_22); - StorageDead(_21); _0 = const false; goto -> bb7; } bb6: { - StorageDead(_22); - StorageDead(_21); - StorageLive(_23); // DBG: _23 = &_14; - StorageLive(_24); // DBG: _24 = &_16; StorageLive(_11); _11 = copy ((*_3).1: usize); @@ -141,8 +123,6 @@ fn variant_a::{closure#0}(_1: &mut {closure@$DIR/slice_filter.rs:7:25: 7:39}, _2 _0 = Le(move _11, move _12); StorageDead(_12); StorageDead(_11); - StorageDead(_24); - StorageDead(_23); goto -> bb7; } @@ -152,8 +132,6 @@ fn variant_a::{closure#0}(_1: &mut {closure@$DIR/slice_filter.rs:7:25: 7:39}, _2 } bb8: { - StorageDead(_20); - StorageDead(_19); _0 = const true; goto -> bb9; } diff --git a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir index 1b2e7b7163cd..104987b0fdda 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir @@ -51,9 +51,6 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { let mut _16: bool; let mut _20: usize; let _22: &T; - let mut _34: &std::ptr::NonNull; - let mut _35: &std::ptr::NonNull; - let mut _36: &std::ptr::NonNull; scope 29 { let _12: *const T; scope 30 { @@ -189,11 +186,7 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { bb5: { StorageLive(_16); - StorageLive(_34); - // DBG: _34 = &_11; - StorageLive(_35); _13 = copy _12 as std::ptr::NonNull (Transmute); - // DBG: _35 = &_13; StorageLive(_14); _14 = copy _11 as *mut T (Transmute); StorageLive(_15); @@ -205,8 +198,6 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { } bb6: { - StorageDead(_35); - StorageDead(_34); StorageDead(_16); StorageLive(_18); StorageLive(_17); @@ -219,8 +210,6 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { } bb7: { - StorageDead(_35); - StorageDead(_34); StorageDead(_16); StorageDead(_22); StorageDead(_13); @@ -266,13 +255,10 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { } bb13: { - StorageLive(_36); - // DBG: _36 = &_11; StorageLive(_21); _21 = copy _11 as *const T (Transmute); _22 = &(*_21); StorageDead(_21); - StorageDead(_36); _23 = Option::<&T>::Some(copy _22); StorageDead(_22); StorageDead(_13); diff --git a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir index c18dd01a189a..4d0e3548e7d6 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir @@ -23,9 +23,6 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { let mut _15: bool; let mut _19: usize; let _21: &T; - let mut _27: &std::ptr::NonNull; - let mut _28: &std::ptr::NonNull; - let mut _29: &std::ptr::NonNull; scope 17 { let _11: *const T; scope 18 { @@ -151,11 +148,7 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { bb5: { StorageLive(_15); - StorageLive(_27); - // DBG: _27 = &_10; - StorageLive(_28); _12 = copy _11 as std::ptr::NonNull (Transmute); - // DBG: _28 = &_12; StorageLive(_13); _13 = copy _10 as *mut T (Transmute); StorageLive(_14); @@ -167,8 +160,6 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb6: { - StorageDead(_28); - StorageDead(_27); StorageDead(_15); StorageLive(_17); StorageLive(_16); @@ -181,8 +172,6 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb7: { - StorageDead(_28); - StorageDead(_27); StorageDead(_15); StorageDead(_21); StorageDead(_12); @@ -224,13 +213,10 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb13: { - StorageLive(_29); - // DBG: _29 = &_10; StorageLive(_20); _20 = copy _10 as *const T (Transmute); _21 = &(*_20); StorageDead(_20); - StorageDead(_29); _22 = Option::<&T>::Some(copy _21); StorageDead(_21); StorageDead(_12); diff --git a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir index 6334c611430a..2b5d8c27d710 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir @@ -23,9 +23,6 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { let mut _15: bool; let mut _19: usize; let _21: &T; - let mut _27: &std::ptr::NonNull; - let mut _28: &std::ptr::NonNull; - let mut _29: &std::ptr::NonNull; scope 17 { let _11: *const T; scope 18 { @@ -151,11 +148,7 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { bb5: { StorageLive(_15); - StorageLive(_27); - // DBG: _27 = &_10; - StorageLive(_28); _12 = copy _11 as std::ptr::NonNull (Transmute); - // DBG: _28 = &_12; StorageLive(_13); _13 = copy _10 as *mut T (Transmute); StorageLive(_14); @@ -167,8 +160,6 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb6: { - StorageDead(_28); - StorageDead(_27); StorageDead(_15); StorageLive(_17); StorageLive(_16); @@ -181,8 +172,6 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb7: { - StorageDead(_28); - StorageDead(_27); StorageDead(_15); StorageDead(_21); StorageDead(_12); @@ -224,13 +213,10 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb13: { - StorageLive(_29); - // DBG: _29 = &_10; StorageLive(_20); _20 = copy _10 as *const T (Transmute); _21 = &(*_20); StorageDead(_20); - StorageDead(_29); _22 = Option::<&T>::Some(copy _21); StorageDead(_21); StorageDead(_12); diff --git a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir index 24c6618d31ca..3009be3f9dc6 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir @@ -23,8 +23,6 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { let mut _18: bool; let mut _19: *const T; let _32: &T; - let mut _38: &std::ptr::NonNull; - let mut _39: &std::ptr::NonNull; scope 20 { let _14: std::ptr::NonNull; let _20: usize; @@ -48,7 +46,6 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { } scope 28 (inlined std::slice::Iter::<'_, T>::next_back_unchecked) { let _26: std::ptr::NonNull; - let mut _40: &std::ptr::NonNull; scope 29 (inlined std::slice::Iter::<'_, T>::pre_dec_end) { let mut _21: *mut *const T; let mut _22: *mut std::ptr::NonNull; @@ -190,10 +187,6 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { _13 = copy ((_12.0: std::slice::Iter<'_, T>).1: *const T); _14 = copy _13 as std::ptr::NonNull (Transmute); StorageDead(_13); - StorageLive(_38); - // DBG: _38 = &((_12.0: std::slice::Iter<'_, T>).0: std::ptr::NonNull); - StorageLive(_39); - // DBG: _39 = &_14; StorageLive(_16); StorageLive(_15); _15 = copy ((_12.0: std::slice::Iter<'_, T>).0: std::ptr::NonNull); @@ -204,8 +197,6 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { _18 = Eq(copy _16, copy _17); StorageDead(_17); StorageDead(_16); - StorageDead(_39); - StorageDead(_38); goto -> bb7; } @@ -222,7 +213,6 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { bb8: { StorageLive(_26); - StorageLive(_40); StorageLive(_28); StorageLive(_22); StorageLive(_23); @@ -275,12 +265,10 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { StorageDead(_23); StorageDead(_22); StorageDead(_28); - // DBG: _40 = &_26; StorageLive(_31); _31 = copy _26 as *const T (Transmute); _32 = &(*_31); StorageDead(_31); - StorageDead(_40); StorageDead(_26); _33 = Option::<&T>::Some(copy _32); StorageDead(_18); diff --git a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir index e5c7bcca44b6..e40bff5ea350 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir @@ -23,8 +23,6 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { let mut _18: bool; let mut _19: *const T; let _32: &T; - let mut _38: &std::ptr::NonNull; - let mut _39: &std::ptr::NonNull; scope 20 { let _14: std::ptr::NonNull; let _20: usize; @@ -48,7 +46,6 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { } scope 28 (inlined std::slice::Iter::<'_, T>::next_back_unchecked) { let _26: std::ptr::NonNull; - let mut _40: &std::ptr::NonNull; scope 29 (inlined std::slice::Iter::<'_, T>::pre_dec_end) { let mut _21: *mut *const T; let mut _22: *mut std::ptr::NonNull; @@ -190,10 +187,6 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { _13 = copy ((_12.0: std::slice::Iter<'_, T>).1: *const T); _14 = copy _13 as std::ptr::NonNull (Transmute); StorageDead(_13); - StorageLive(_38); - // DBG: _38 = &((_12.0: std::slice::Iter<'_, T>).0: std::ptr::NonNull); - StorageLive(_39); - // DBG: _39 = &_14; StorageLive(_16); StorageLive(_15); _15 = copy ((_12.0: std::slice::Iter<'_, T>).0: std::ptr::NonNull); @@ -204,8 +197,6 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { _18 = Eq(copy _16, copy _17); StorageDead(_17); StorageDead(_16); - StorageDead(_39); - StorageDead(_38); goto -> bb7; } @@ -222,7 +213,6 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { bb8: { StorageLive(_26); - StorageLive(_40); StorageLive(_28); StorageLive(_22); StorageLive(_23); @@ -275,12 +265,10 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { StorageDead(_23); StorageDead(_22); StorageDead(_28); - // DBG: _40 = &_26; StorageLive(_31); _31 = copy _26 as *const T (Transmute); _32 = &(*_31); StorageDead(_31); - StorageDead(_40); StorageDead(_26); _33 = Option::<&T>::Some(copy _32); StorageDead(_18); diff --git a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_generic_is_empty.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_generic_is_empty.PreCodegen.after.panic-abort.mir index 18a3d8e7c4ad..9b510380b10b 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_generic_is_empty.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_generic_is_empty.PreCodegen.after.panic-abort.mir @@ -6,8 +6,6 @@ fn slice_iter_generic_is_empty(_1: &std::slice::Iter<'_, T>) -> bool { scope 1 (inlined as ExactSizeIterator>::is_empty) { let mut _2: *const T; let mut _7: *const T; - let mut _9: &std::ptr::NonNull; - let mut _10: &std::ptr::NonNull; scope 2 { let _3: std::ptr::NonNull; let _8: usize; @@ -43,10 +41,6 @@ fn slice_iter_generic_is_empty(_1: &std::slice::Iter<'_, T>) -> bool { _2 = copy ((*_1).1: *const T); _3 = copy _2 as std::ptr::NonNull (Transmute); StorageDead(_2); - StorageLive(_9); - // DBG: _9 = &((*_1).0: std::ptr::NonNull); - StorageLive(_10); - // DBG: _10 = &_3; StorageLive(_5); StorageLive(_4); _4 = copy ((*_1).0: std::ptr::NonNull); @@ -57,8 +51,6 @@ fn slice_iter_generic_is_empty(_1: &std::slice::Iter<'_, T>) -> bool { _0 = Eq(copy _5, copy _6); StorageDead(_6); StorageDead(_5); - StorageDead(_10); - StorageDead(_9); goto -> bb3; } diff --git a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_generic_is_empty.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_generic_is_empty.PreCodegen.after.panic-unwind.mir index 18a3d8e7c4ad..9b510380b10b 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_generic_is_empty.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_generic_is_empty.PreCodegen.after.panic-unwind.mir @@ -6,8 +6,6 @@ fn slice_iter_generic_is_empty(_1: &std::slice::Iter<'_, T>) -> bool { scope 1 (inlined as ExactSizeIterator>::is_empty) { let mut _2: *const T; let mut _7: *const T; - let mut _9: &std::ptr::NonNull; - let mut _10: &std::ptr::NonNull; scope 2 { let _3: std::ptr::NonNull; let _8: usize; @@ -43,10 +41,6 @@ fn slice_iter_generic_is_empty(_1: &std::slice::Iter<'_, T>) -> bool { _2 = copy ((*_1).1: *const T); _3 = copy _2 as std::ptr::NonNull (Transmute); StorageDead(_2); - StorageLive(_9); - // DBG: _9 = &((*_1).0: std::ptr::NonNull); - StorageLive(_10); - // DBG: _10 = &_3; StorageLive(_5); StorageLive(_4); _4 = copy ((*_1).0: std::ptr::NonNull); @@ -57,8 +51,6 @@ fn slice_iter_generic_is_empty(_1: &std::slice::Iter<'_, T>) -> bool { _0 = Eq(copy _5, copy _6); StorageDead(_6); StorageDead(_5); - StorageDead(_10); - StorageDead(_9); goto -> bb3; } diff --git a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-abort.mir index 590d4ac2c838..62b738c36bf4 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-abort.mir @@ -8,8 +8,6 @@ fn slice_iter_mut_next_back(_1: &mut std::slice::IterMut<'_, T>) -> Option<&mut let mut _7: bool; let mut _8: *mut T; let mut _21: &mut T; - let mut _22: &std::ptr::NonNull; - let mut _23: &std::ptr::NonNull; scope 2 { let _3: std::ptr::NonNull; let _9: usize; @@ -33,7 +31,6 @@ fn slice_iter_mut_next_back(_1: &mut std::slice::IterMut<'_, T>) -> Option<&mut } scope 10 (inlined std::slice::IterMut::<'_, T>::next_back_unchecked) { let mut _15: std::ptr::NonNull; - let mut _24: &mut std::ptr::NonNull; scope 11 (inlined std::slice::IterMut::<'_, T>::pre_dec_end) { let mut _10: *mut *mut T; let mut _11: *mut std::ptr::NonNull; @@ -88,10 +85,6 @@ fn slice_iter_mut_next_back(_1: &mut std::slice::IterMut<'_, T>) -> Option<&mut bb1: { _2 = copy ((*_1).1: *mut T); _3 = copy _2 as std::ptr::NonNull (Transmute); - StorageLive(_22); - // DBG: _22 = &((*_1).0: std::ptr::NonNull); - StorageLive(_23); - // DBG: _23 = &_3; StorageLive(_5); StorageLive(_4); _4 = copy ((*_1).0: std::ptr::NonNull); @@ -102,8 +95,6 @@ fn slice_iter_mut_next_back(_1: &mut std::slice::IterMut<'_, T>) -> Option<&mut _7 = Eq(copy _5, copy _6); StorageDead(_6); StorageDead(_5); - StorageDead(_23); - StorageDead(_22); goto -> bb3; } @@ -120,7 +111,6 @@ fn slice_iter_mut_next_back(_1: &mut std::slice::IterMut<'_, T>) -> Option<&mut bb4: { StorageLive(_15); - StorageLive(_24); StorageLive(_17); StorageLive(_11); StorageLive(_12); @@ -173,12 +163,10 @@ fn slice_iter_mut_next_back(_1: &mut std::slice::IterMut<'_, T>) -> Option<&mut StorageDead(_12); StorageDead(_11); StorageDead(_17); - // DBG: _24 = &_15; StorageLive(_20); _20 = copy _15 as *mut T (Transmute); _21 = &mut (*_20); StorageDead(_20); - StorageDead(_24); StorageDead(_15); _0 = Option::<&mut T>::Some(copy _21); goto -> bb11; diff --git a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-unwind.mir index 590d4ac2c838..62b738c36bf4 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-unwind.mir @@ -8,8 +8,6 @@ fn slice_iter_mut_next_back(_1: &mut std::slice::IterMut<'_, T>) -> Option<&mut let mut _7: bool; let mut _8: *mut T; let mut _21: &mut T; - let mut _22: &std::ptr::NonNull; - let mut _23: &std::ptr::NonNull; scope 2 { let _3: std::ptr::NonNull; let _9: usize; @@ -33,7 +31,6 @@ fn slice_iter_mut_next_back(_1: &mut std::slice::IterMut<'_, T>) -> Option<&mut } scope 10 (inlined std::slice::IterMut::<'_, T>::next_back_unchecked) { let mut _15: std::ptr::NonNull; - let mut _24: &mut std::ptr::NonNull; scope 11 (inlined std::slice::IterMut::<'_, T>::pre_dec_end) { let mut _10: *mut *mut T; let mut _11: *mut std::ptr::NonNull; @@ -88,10 +85,6 @@ fn slice_iter_mut_next_back(_1: &mut std::slice::IterMut<'_, T>) -> Option<&mut bb1: { _2 = copy ((*_1).1: *mut T); _3 = copy _2 as std::ptr::NonNull (Transmute); - StorageLive(_22); - // DBG: _22 = &((*_1).0: std::ptr::NonNull); - StorageLive(_23); - // DBG: _23 = &_3; StorageLive(_5); StorageLive(_4); _4 = copy ((*_1).0: std::ptr::NonNull); @@ -102,8 +95,6 @@ fn slice_iter_mut_next_back(_1: &mut std::slice::IterMut<'_, T>) -> Option<&mut _7 = Eq(copy _5, copy _6); StorageDead(_6); StorageDead(_5); - StorageDead(_23); - StorageDead(_22); goto -> bb3; } @@ -120,7 +111,6 @@ fn slice_iter_mut_next_back(_1: &mut std::slice::IterMut<'_, T>) -> Option<&mut bb4: { StorageLive(_15); - StorageLive(_24); StorageLive(_17); StorageLive(_11); StorageLive(_12); @@ -173,12 +163,10 @@ fn slice_iter_mut_next_back(_1: &mut std::slice::IterMut<'_, T>) -> Option<&mut StorageDead(_12); StorageDead(_11); StorageDead(_17); - // DBG: _24 = &_15; StorageLive(_20); _20 = copy _15 as *mut T (Transmute); _21 = &mut (*_20); StorageDead(_20); - StorageDead(_24); StorageDead(_15); _0 = Option::<&mut T>::Some(copy _21); goto -> bb11; diff --git a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-abort.mir index e9ed932cbafb..cc0fce26149e 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-abort.mir @@ -10,9 +10,6 @@ fn slice_iter_next(_1: &mut std::slice::Iter<'_, T>) -> Option<&T> { let mut _10: std::ptr::NonNull; let mut _12: usize; let _14: &T; - let mut _15: &std::ptr::NonNull; - let mut _16: &std::ptr::NonNull; - let mut _17: &std::ptr::NonNull; scope 2 { let _3: *const T; scope 3 { @@ -70,11 +67,7 @@ fn slice_iter_next(_1: &mut std::slice::Iter<'_, T>) -> Option<&T> { bb1: { StorageLive(_7); - StorageLive(_15); - // DBG: _15 = &_2; - StorageLive(_16); _4 = copy _3 as std::ptr::NonNull (Transmute); - // DBG: _16 = &_4; StorageLive(_5); _5 = copy _2 as *mut T (Transmute); StorageLive(_6); @@ -86,8 +79,6 @@ fn slice_iter_next(_1: &mut std::slice::Iter<'_, T>) -> Option<&T> { } bb2: { - StorageDead(_16); - StorageDead(_15); StorageDead(_7); StorageLive(_10); StorageLive(_9); @@ -103,8 +94,6 @@ fn slice_iter_next(_1: &mut std::slice::Iter<'_, T>) -> Option<&T> { } bb3: { - StorageDead(_16); - StorageDead(_15); _0 = const {transmute(0x0000000000000000): Option<&T>}; StorageDead(_7); goto -> bb8; @@ -127,13 +116,10 @@ fn slice_iter_next(_1: &mut std::slice::Iter<'_, T>) -> Option<&T> { } bb7: { - StorageLive(_17); - // DBG: _17 = &_2; StorageLive(_13); _13 = copy _2 as *const T (Transmute); _14 = &(*_13); StorageDead(_13); - StorageDead(_17); _0 = Option::<&T>::Some(copy _14); goto -> bb8; } diff --git a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-unwind.mir index e9ed932cbafb..cc0fce26149e 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-unwind.mir @@ -10,9 +10,6 @@ fn slice_iter_next(_1: &mut std::slice::Iter<'_, T>) -> Option<&T> { let mut _10: std::ptr::NonNull; let mut _12: usize; let _14: &T; - let mut _15: &std::ptr::NonNull; - let mut _16: &std::ptr::NonNull; - let mut _17: &std::ptr::NonNull; scope 2 { let _3: *const T; scope 3 { @@ -70,11 +67,7 @@ fn slice_iter_next(_1: &mut std::slice::Iter<'_, T>) -> Option<&T> { bb1: { StorageLive(_7); - StorageLive(_15); - // DBG: _15 = &_2; - StorageLive(_16); _4 = copy _3 as std::ptr::NonNull (Transmute); - // DBG: _16 = &_4; StorageLive(_5); _5 = copy _2 as *mut T (Transmute); StorageLive(_6); @@ -86,8 +79,6 @@ fn slice_iter_next(_1: &mut std::slice::Iter<'_, T>) -> Option<&T> { } bb2: { - StorageDead(_16); - StorageDead(_15); StorageDead(_7); StorageLive(_10); StorageLive(_9); @@ -103,8 +94,6 @@ fn slice_iter_next(_1: &mut std::slice::Iter<'_, T>) -> Option<&T> { } bb3: { - StorageDead(_16); - StorageDead(_15); _0 = const {transmute(0x0000000000000000): Option<&T>}; StorageDead(_7); goto -> bb8; @@ -127,13 +116,10 @@ fn slice_iter_next(_1: &mut std::slice::Iter<'_, T>) -> Option<&T> { } bb7: { - StorageLive(_17); - // DBG: _17 = &_2; StorageLive(_13); _13 = copy _2 as *const T (Transmute); _14 = &(*_13); StorageDead(_13); - StorageDead(_17); _0 = Option::<&T>::Some(copy _14); goto -> bb8; } diff --git a/tests/ui/extern/extern-types-field-offset.rs b/tests/ui/extern/extern-types-field-offset.rs index 7a5f36da2091..470ae07a0b55 100644 --- a/tests/ui/extern/extern-types-field-offset.rs +++ b/tests/ui/extern/extern-types-field-offset.rs @@ -23,12 +23,17 @@ fn main() { let x: &Newtype = unsafe { &*(&buf as *const _ as *const Newtype) }; // Projecting to the newtype works, because it is always at offset 0. let field = &x.0; + // Avoid being eliminated by DSE. + std::hint::black_box(field); let x: &S = unsafe { &*(&buf as *const _ as *const S) }; // Accessing sized fields is perfectly fine, even at non-zero offsets. let field = &x.i; + std::hint::black_box(field); let field = &x.j; + std::hint::black_box(field); // This needs to compute the field offset, but we don't know the type's alignment, // so this panics. let field = &x.a; + std::hint::black_box(field); } From 1bd89bd42e0bb6f29b8af5d6bdf3f756196bb8ee Mon Sep 17 00:00:00 2001 From: dianqk Date: Wed, 18 Jun 2025 22:04:48 +0800 Subject: [PATCH 107/226] codegen: Generate `dbg_value` for the ref statement --- compiler/rustc_codegen_gcc/src/debuginfo.rs | 13 +- .../src/debuginfo/dwarf_const.rs | 3 + .../rustc_codegen_llvm/src/debuginfo/mod.rs | 53 +++++- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 9 + compiler/rustc_codegen_ssa/src/mir/block.rs | 1 + .../rustc_codegen_ssa/src/mir/debuginfo.rs | 54 +++++- compiler/rustc_codegen_ssa/src/mir/operand.rs | 17 +- .../rustc_codegen_ssa/src/mir/statement.rs | 70 +++++++- .../rustc_codegen_ssa/src/traits/debuginfo.rs | 14 +- .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 1 + tests/codegen-llvm/debuginfo-dse.rs | 160 ++++++++++++++++++ tests/debuginfo/opt/dead_refs.rs | 50 ++++++ 12 files changed, 431 insertions(+), 14 deletions(-) create mode 100644 tests/codegen-llvm/debuginfo-dse.rs create mode 100644 tests/debuginfo/opt/dead_refs.rs diff --git a/compiler/rustc_codegen_gcc/src/debuginfo.rs b/compiler/rustc_codegen_gcc/src/debuginfo.rs index 4c8585192a1b..0f015cc23f52 100644 --- a/compiler/rustc_codegen_gcc/src/debuginfo.rs +++ b/compiler/rustc_codegen_gcc/src/debuginfo.rs @@ -29,13 +29,24 @@ impl<'a, 'gcc, 'tcx> DebugInfoBuilderMethods for Builder<'a, 'gcc, 'tcx> { _variable_alloca: Self::Value, _direct_offset: Size, _indirect_offsets: &[Size], - _fragment: Option>, + _fragment: &Option>, ) { // FIXME(tempdragon): Not sure if this is correct, probably wrong but still keep it here. #[cfg(feature = "master")] _variable_alloca.set_location(_dbg_loc); } + fn dbg_var_value( + &mut self, + _dbg_var: Self::DIVariable, + _dbg_loc: Self::DILocation, + _value: Self::Value, + _direct_offset: Size, + _indirect_offsets: &[Size], + _fragment: &Option>, + ) { + } + fn insert_reference_to_gdb_debug_scripts_section_global(&mut self) { // TODO(antoyo): insert reference to gdb debug scripts section global. } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/dwarf_const.rs b/compiler/rustc_codegen_llvm/src/debuginfo/dwarf_const.rs index 408429152223..52d04625749b 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/dwarf_const.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/dwarf_const.rs @@ -35,3 +35,6 @@ declare_constant!(DW_OP_plus_uconst: u64); /// Double-checked by a static assertion in `RustWrapper.cpp`. #[allow(non_upper_case_globals)] pub(crate) const DW_OP_LLVM_fragment: u64 = 0x1000; +// It describes the actual value of a source variable which might not exist in registers or in memory. +#[allow(non_upper_case_globals)] +pub(crate) const DW_OP_stack_value: u64 = 0x9f; diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index af64e4ebed0f..c6ad1c2e18ec 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -156,7 +156,7 @@ impl<'ll> DebugInfoBuilderMethods for Builder<'_, 'll, '_> { variable_alloca: Self::Value, direct_offset: Size, indirect_offsets: &[Size], - fragment: Option>, + fragment: &Option>, ) { use dwarf_const::{DW_OP_LLVM_fragment, DW_OP_deref, DW_OP_plus_uconst}; @@ -187,7 +187,6 @@ impl<'ll> DebugInfoBuilderMethods for Builder<'_, 'll, '_> { llvm::LLVMDIBuilderCreateExpression(di_builder, addr_ops.as_ptr(), addr_ops.len()) }; unsafe { - // FIXME(eddyb) replace `llvm.dbg.declare` with `llvm.dbg.addr`. llvm::LLVMDIBuilderInsertDeclareRecordAtEnd( di_builder, variable_alloca, @@ -199,6 +198,56 @@ impl<'ll> DebugInfoBuilderMethods for Builder<'_, 'll, '_> { }; } + fn dbg_var_value( + &mut self, + dbg_var: &'ll DIVariable, + dbg_loc: &'ll DILocation, + value: Self::Value, + direct_offset: Size, + indirect_offsets: &[Size], + fragment: &Option>, + ) { + use dwarf_const::{DW_OP_LLVM_fragment, DW_OP_deref, DW_OP_plus_uconst, DW_OP_stack_value}; + + // Convert the direct and indirect offsets and fragment byte range to address ops. + let mut addr_ops = SmallVec::<[u64; 8]>::new(); + + if direct_offset.bytes() > 0 { + addr_ops.push(DW_OP_plus_uconst); + addr_ops.push(direct_offset.bytes() as u64); + addr_ops.push(DW_OP_stack_value); + } + for &offset in indirect_offsets { + addr_ops.push(DW_OP_deref); + if offset.bytes() > 0 { + addr_ops.push(DW_OP_plus_uconst); + addr_ops.push(offset.bytes() as u64); + } + } + if let Some(fragment) = fragment { + // `DW_OP_LLVM_fragment` takes as arguments the fragment's + // offset and size, both of them in bits. + addr_ops.push(DW_OP_LLVM_fragment); + addr_ops.push(fragment.start.bits() as u64); + addr_ops.push((fragment.end - fragment.start).bits() as u64); + } + + let di_builder = DIB(self.cx()); + let addr_expr = unsafe { + llvm::LLVMDIBuilderCreateExpression(di_builder, addr_ops.as_ptr(), addr_ops.len()) + }; + unsafe { + llvm::LLVMDIBuilderInsertDbgValueRecordAtEnd( + di_builder, + value, + dbg_var, + addr_expr, + dbg_loc, + self.llbb(), + ); + } + } + fn set_dbg_loc(&mut self, dbg_loc: &'ll DILocation) { unsafe { llvm::LLVMSetCurrentDebugLocation2(self.llbuilder, dbg_loc); diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index e9f92267a7d4..7fbba0294073 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1991,6 +1991,15 @@ unsafe extern "C" { Block: &'ll BasicBlock, ) -> &'ll DbgRecord; + pub(crate) fn LLVMDIBuilderInsertDbgValueRecordAtEnd<'ll>( + Builder: &DIBuilder<'ll>, + Val: &'ll Value, + VarInfo: &'ll Metadata, + Expr: &'ll Metadata, + DebugLoc: &'ll Metadata, + Block: &'ll BasicBlock, + ) -> &'ll DbgRecord; + pub(crate) fn LLVMDIBuilderCreateAutoVariable<'ll>( Builder: &DIBuilder<'ll>, Scope: &'ll Metadata, diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index b2dc4fe32b0f..e371f1a76231 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -1320,6 +1320,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { for statement in &data.statements { self.codegen_statement(bx, statement); } + self.codegen_stmt_debuginfos(bx, &data.after_last_stmt_debuginfos); let merging_succ = self.codegen_terminator(bx, bb, data.terminator()); if let MergingSucc::False = merging_succ { diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs index b8f635ab7816..38bb6f24b1c3 100644 --- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs @@ -253,6 +253,54 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { spill_slot } + // Indicates that local is set to a new value. The `layout` and `projection` are used to + // calculate the offset. + pub(crate) fn debug_new_val_to_local( + &self, + bx: &mut Bx, + local: mir::Local, + base: PlaceValue, + layout: TyAndLayout<'tcx>, + projection: &[mir::PlaceElem<'tcx>], + ) { + let full_debug_info = bx.sess().opts.debuginfo == DebugInfo::Full; + if !full_debug_info { + return; + } + + let vars = match &self.per_local_var_debug_info { + Some(per_local) => &per_local[local], + None => return, + }; + + let DebugInfoOffset { direct_offset, indirect_offsets, result: _ } = + calculate_debuginfo_offset(bx, projection, layout); + for var in vars.iter() { + let Some(dbg_var) = var.dbg_var else { + continue; + }; + let Some(dbg_loc) = self.dbg_loc(var.source_info) else { + continue; + }; + bx.dbg_var_value( + dbg_var, + dbg_loc, + base.llval, + direct_offset, + &indirect_offsets, + &var.fragment, + ); + } + } + + pub(crate) fn debug_poison_to_local(&self, bx: &mut Bx, local: mir::Local) { + let ty = self.monomorphize(self.mir.local_decls[local].ty); + let layout = bx.cx().layout_of(ty); + let to_backend_ty = bx.cx().immediate_backend_type(layout); + let place_ref = PlaceRef::new_sized(bx.cx().const_poison(to_backend_ty), layout); + self.debug_new_val_to_local(bx, local, place_ref.val, layout, &[]); + } + /// Apply debuginfo and/or name, after creating the `alloca` for a local, /// or initializing the local with an operand (whichever applies). pub(crate) fn debug_introduce_local(&self, bx: &mut Bx, local: mir::Local) { @@ -424,7 +472,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { alloca.val.llval, Size::ZERO, &[Size::ZERO], - var.fragment, + &var.fragment, ); } else { bx.dbg_var_addr( @@ -433,7 +481,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { base.val.llval, direct_offset, &indirect_offsets, - var.fragment, + &var.fragment, ); } } @@ -455,7 +503,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let base = FunctionCx::spill_operand_to_stack(operand, Some(name), bx); bx.clear_dbg_loc(); - bx.dbg_var_addr(dbg_var, dbg_loc, base.val.llval, Size::ZERO, &[], fragment); + bx.dbg_var_addr(dbg_var, dbg_loc, base.val.llval, Size::ZERO, &[], &fragment); } } } diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs index 5f7f87fc6920..88a8e2a844cb 100644 --- a/compiler/rustc_codegen_ssa/src/mir/operand.rs +++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs @@ -71,16 +71,23 @@ pub enum OperandValue { } impl OperandValue { + /// Return the data pointer and optional metadata as backend values + /// if this value can be treat as a pointer. + pub(crate) fn try_pointer_parts(self) -> Option<(V, Option)> { + match self { + OperandValue::Immediate(llptr) => Some((llptr, None)), + OperandValue::Pair(llptr, llextra) => Some((llptr, Some(llextra))), + OperandValue::Ref(_) | OperandValue::ZeroSized => None, + } + } + /// Treat this value as a pointer and return the data pointer and /// optional metadata as backend values. /// /// If you're making a place, use [`Self::deref`] instead. pub(crate) fn pointer_parts(self) -> (V, Option) { - match self { - OperandValue::Immediate(llptr) => (llptr, None), - OperandValue::Pair(llptr, llextra) => (llptr, Some(llextra)), - _ => bug!("OperandValue cannot be a pointer: {self:?}"), - } + self.try_pointer_parts() + .unwrap_or_else(|| bug!("OperandValue cannot be a pointer: {self:?}")) } /// Treat this value as a pointer and return the place to which it points. diff --git a/compiler/rustc_codegen_ssa/src/mir/statement.rs b/compiler/rustc_codegen_ssa/src/mir/statement.rs index 0a50d7f18dbe..99bb31a68b68 100644 --- a/compiler/rustc_codegen_ssa/src/mir/statement.rs +++ b/compiler/rustc_codegen_ssa/src/mir/statement.rs @@ -1,13 +1,17 @@ -use rustc_middle::mir::{self, NonDivergingIntrinsic}; -use rustc_middle::span_bug; +use rustc_middle::mir::{self, NonDivergingIntrinsic, RETURN_PLACE, StmtDebugInfo}; +use rustc_middle::{bug, span_bug}; +use rustc_target::callconv::PassMode; use tracing::instrument; use super::{FunctionCx, LocalRef}; +use crate::common::TypeKind; +use crate::mir::place::PlaceRef; use crate::traits::*; impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { #[instrument(level = "debug", skip(self, bx))] pub(crate) fn codegen_statement(&mut self, bx: &mut Bx, statement: &mir::Statement<'tcx>) { + self.codegen_stmt_debuginfos(bx, &statement.debuginfos); self.set_debug_loc(bx, statement.source_info); match statement.kind { mir::StatementKind::Assign(box (ref place, ref rvalue)) => { @@ -101,4 +105,66 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { | mir::StatementKind::Nop => {} } } + + pub(crate) fn codegen_stmt_debuginfo(&mut self, bx: &mut Bx, debuginfo: &StmtDebugInfo<'tcx>) { + match debuginfo { + StmtDebugInfo::AssignRef(dest, place) => { + let local_ref = match self.locals[place.local] { + LocalRef::Place(place_ref) | LocalRef::UnsizedPlace(place_ref) => { + Some(place_ref) + } + LocalRef::Operand(operand_ref) => operand_ref + .val + .try_pointer_parts() + .map(|(pointer, _)| PlaceRef::new_sized(pointer, operand_ref.layout)), + LocalRef::PendingOperand => None, + } + .filter(|place_ref| { + // For the reference of an argument (e.x. `&_1`), it's only valid if the pass mode is indirect, and its reference is + // llval. + let local_ref_pass_mode = place.as_local().and_then(|local| { + if local == RETURN_PLACE { + None + } else { + self.fn_abi.args.get(local.as_usize() - 1).map(|arg| &arg.mode) + } + }); + matches!(local_ref_pass_mode, Some(&PassMode::Indirect {..}) | None) && + // Drop unsupported projections. + place.projection.iter().all(|p| p.can_use_in_debuginfo()) && + // Only pointers can be calculated addresses. + bx.type_kind(bx.val_ty(place_ref.val.llval)) == TypeKind::Pointer + }); + if let Some(local_ref) = local_ref { + let (base_layout, projection) = if place.is_indirect_first_projection() { + // For `_n = &((*_1).0: i32);`, we are calculating the address of `_1.0`, so + // we should drop the deref projection. + let projected_ty = local_ref + .layout + .ty + .builtin_deref(true) + .unwrap_or_else(|| bug!("deref of non-pointer {:?}", local_ref)); + let layout = bx.cx().layout_of(projected_ty); + (layout, &place.projection[1..]) + } else { + (local_ref.layout, place.projection.as_slice()) + }; + self.debug_new_val_to_local(bx, *dest, local_ref.val, base_layout, projection); + } else { + // If the address cannot be computed, use poison to indicate that the value has been optimized out. + self.debug_poison_to_local(bx, *dest); + } + } + } + } + + pub(crate) fn codegen_stmt_debuginfos( + &mut self, + bx: &mut Bx, + debuginfos: &[StmtDebugInfo<'tcx>], + ) { + for debuginfo in debuginfos { + self.codegen_stmt_debuginfo(bx, debuginfo); + } + } } diff --git a/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs b/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs index b9d4950e0ad3..a4da6c915dee 100644 --- a/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs @@ -77,7 +77,19 @@ pub trait DebugInfoBuilderMethods: BackendTypes { indirect_offsets: &[Size], // Byte range in the `dbg_var` covered by this fragment, // if this is a fragment of a composite `DIVariable`. - fragment: Option>, + fragment: &Option>, + ); + fn dbg_var_value( + &mut self, + dbg_var: Self::DIVariable, + dbg_loc: Self::DILocation, + value: Self::Value, + direct_offset: Size, + // NB: each offset implies a deref (i.e. they're steps in a pointer chain). + indirect_offsets: &[Size], + // Byte range in the `dbg_var` covered by this fragment, + // if this is a fragment of a composite `DIVariable`. + fragment: &Option>, ); fn set_dbg_loc(&mut self, dbg_loc: Self::DILocation); fn clear_dbg_loc(&mut self); diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 2b83ea24ac61..e38474f09ff7 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -58,6 +58,7 @@ using namespace llvm::object; // This opcode is an LLVM detail that could hypothetically change (?), so // verify that the hard-coded value in `dwarf_const.rs` still agrees with LLVM. static_assert(dwarf::DW_OP_LLVM_fragment == 0x1000); +static_assert(dwarf::DW_OP_stack_value == 0x9f); // LLVMAtomicOrdering is already an enum - don't create another // one. diff --git a/tests/codegen-llvm/debuginfo-dse.rs b/tests/codegen-llvm/debuginfo-dse.rs new file mode 100644 index 000000000000..1fcb5fcfc543 --- /dev/null +++ b/tests/codegen-llvm/debuginfo-dse.rs @@ -0,0 +1,160 @@ +//@ compile-flags: -Copt-level=3 -g -Zverify-llvm-ir +//@ revisions: CODEGEN OPTIMIZED +//@[CODEGEN] compile-flags: -Cno-prepopulate-passes +// ignore-tidy-linelength + +#![crate_type = "lib"] + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct Foo(i32, i64, i32); + +#[no_mangle] +fn r#ref(ref_foo: &Foo) -> i32 { + // CHECK-LABEL: define{{.*}} i32 @ref + // CHECK-SAME: (ptr {{.*}} [[ARG_ref_foo:%.*]]) + // OPTIMIZED: #dbg_value(ptr [[ARG_ref_foo]], [[VAR_ref_foo:![0-9]+]], !DIExpression() + // CHECK: #dbg_value(ptr poison, [[VAR_invalid_ref_of_ref_foo:![0-9]+]], !DIExpression() + // CHECK: #dbg_value(ptr [[ARG_ref_foo]], [[VAR_ref_v0:![0-9]+]], !DIExpression() + // CHECK: #dbg_value(ptr [[ARG_ref_foo]], [[VAR_ref_v1:![0-9]+]], !DIExpression(DW_OP_plus_uconst, 8, DW_OP_stack_value) + // CHECK: #dbg_value(ptr [[ARG_ref_foo]], [[VAR_ref_v2:![0-9]+]], !DIExpression(DW_OP_plus_uconst, 16, DW_OP_stack_value) + let invalid_ref_of_ref_foo = &ref_foo; + let ref_v0 = &ref_foo.0; + let ref_v1 = &ref_foo.1; + let ref_v2 = &ref_foo.2; + ref_foo.0 +} + +#[no_mangle] +pub fn dead_first(dead_first_foo: &Foo) -> &i32 { + // CHECK-LABEL: def {{.*}} ptr @dead_first + // CHECK-SAME: (ptr {{.*}} [[ARG_dead_first_foo:%.*]]) + // CODEGEN: #dbg_declare(ptr %dead_first_foo.dbg.spill, [[ARG_dead_first_foo:![0-9]+]], !DIExpression() + // OPTIMIZED: #dbg_value(ptr %dead_first_foo, [[ARG_dead_first_foo:![0-9]+]], !DIExpression() + // CHECK: #dbg_value(ptr %dead_first_foo, [[VAR_dead_first_v0:![0-9]+]], !DIExpression() + // CHECK: %dead_first_v0 = getelementptr{{.*}} i8, ptr %dead_first_foo, i64 16 + // CODEGEN: #dbg_declare(ptr %dead_first_v0.dbg.spill, [[VAR_dead_first_v0]], !DIExpression() + // OPTIMIZED: #dbg_value(ptr %dead_first_v0, [[VAR_dead_first_v0]], !DIExpression() + let mut dead_first_v0 = &dead_first_foo.0; + dead_first_v0 = &dead_first_foo.2; + dead_first_v0 +} + +#[no_mangle] +fn ptr(ptr_foo: Foo) -> i32 { + // CHECK-LABEL: define{{.*}} i32 @ptr + // CHECK-SAME: (ptr {{.*}} [[ARG_ptr_foo:%.*]]) + // CHECK: #dbg_value(ptr [[ARG_ptr_foo]], [[ref_ptr_foo:![0-9]+]], !DIExpression() + // CHECK: #dbg_value(ptr [[ARG_ptr_foo]], [[VAR_ptr_v0:![0-9]+]], !DIExpression() + // CHECK: #dbg_value(ptr [[ARG_ptr_foo]], [[VAR_ptr_v1:![0-9]+]], !DIExpression(DW_OP_plus_uconst, 8, DW_OP_stack_value) + // CHECK: #dbg_value(ptr [[ARG_ptr_foo]], [[VAR_ptr_v2:![0-9]+]], !DIExpression(DW_OP_plus_uconst, 16, DW_OP_stack_value) + let ref_ptr_foo = &ptr_foo; + let ptr_v0 = &ptr_foo.0; + let ptr_v1 = &ptr_foo.1; + let ptr_v2 = &ptr_foo.2; + ptr_foo.2 +} + +#[no_mangle] +fn no_ptr(val: i32) -> i32 { + // CHECK-LABEL: define{{.*}} i32 @no_ptr + // CODEGEN: #dbg_value(ptr poison, [[VAR_val_ref:![0-9]+]], !DIExpression() + let val_ref = &val; + val +} + +#[no_mangle] +pub fn fragment(fragment_v1: Foo, mut fragment_v2: Foo) -> Foo { + // CHECK-LABEL: define void @fragment + // CHECK-SAME: (ptr {{.*}}, ptr {{.*}} [[ARG_fragment_v1:%.*]], ptr {{.*}} [[ARG_fragment_v2:%.*]]) + // CHECK: #dbg_declare(ptr [[ARG_fragment_v1]] + // CHECK-NEXT: #dbg_declare(ptr [[ARG_fragment_v2]] + // CHECK-NEXT: #dbg_value(ptr [[ARG_fragment_v2]], [[VAR_fragment_f:![0-9]+]], !DIExpression(DW_OP_LLVM_fragment, 0, 64) + // CHECK-NEXT: #dbg_value(ptr [[ARG_fragment_v1]], [[VAR_fragment_f:![0-9]+]], !DIExpression(DW_OP_LLVM_fragment, 64, 64) + let fragment_f = || { + fragment_v2 = fragment_v1; + }; + fragment_v2 = fragment_v1; + fragment_v2 +} + +#[no_mangle] +pub fn tuple(foo: (i32, &Foo)) -> i32 { + // CHECK-LABEL: define{{.*}} i32 @tuple + // CHECK-SAME: (i32 {{.*}}, ptr {{.*}} [[ARG_tuple_foo_1:%.*]]) + // CHECK: #dbg_value(ptr [[ARG_tuple_foo_1]], [[VAR_tuple_dead:![0-9]+]], !DIExpression(DW_OP_plus_uconst, 16, DW_OP_stack_value) + let tuple_dead = &foo.1.2; + foo.1.0 +} + +pub struct ZST; + +#[no_mangle] +pub fn zst(zst: ZST, v: &i32) -> i32 { + // CHECK-LABEL: define{{.*}} i32 @zst + // CHECK: #dbg_value(ptr poison, [[VAR_zst_ref:![0-9]+]], !DIExpression() + let zst_ref = &zst; + *v +} + +#[no_mangle] +fn index(slice: &[i32; 4], idx: usize) -> i32 { + // CHECK-LABEL: define{{.*}} i32 @index + // CHECK: bb1: + // CHECK-NEXT: #dbg_value(ptr poison, [[VAR_index_from_var:![0-9]+]], !DIExpression() + // CODEGEN: bb3: + // CHECK-NEXT: #dbg_value(ptr %slice, [[VAR_const_index_from_start:![0-9]+]], !DIExpression() + // CHECK-NEXT: #dbg_value(ptr poison, [[VAR_const_index_from_end:![0-9]+]], !DIExpression() + let index_from_var = &slice[idx]; + let [ref const_index_from_start, .., ref const_index_from_end] = slice[..] else { + return 0; + }; + slice[0] +} + +unsafe extern "Rust" { + safe fn opaque_inner(_: *const core::ffi::c_void); +} + +#[inline(never)] +pub fn opaque_use(p: &T) { + opaque_inner(&raw const p as *const _); +} + +#[no_mangle] +pub fn non_arg_ref(scalar: i32, foo: Foo, a: &i32) -> i32 { + // CHECK-LABEL: define{{.*}} i32 @non_arg_ref + // CHECK: #dbg_value(ptr %non_arg_ref_scalar, [[VAR_non_arg_ref_scalar_ref:![0-9]+]], !DIExpression() + // CHECK-NEXT: #dbg_value(ptr %non_arg_ref_foo, [[VAR_non_arg_ref_foo_ref:![0-9]+]], !DIExpression() + // CHECK-NEXT: #dbg_value(ptr %non_arg_ref_foo, [[VAR_non_arg_ref_foo_ref_2:![0-9]+]], !DIExpression(DW_OP_plus_uconst, 16, DW_OP_stack_value) + let non_arg_ref_scalar = scalar; + let non_arg_ref_foo = foo; + opaque_use(&non_arg_ref_scalar); + opaque_use(&non_arg_ref_foo); + let non_arg_ref_scalar_ref = &non_arg_ref_scalar; + let non_arg_ref_foo_ref = &non_arg_ref_foo; + let non_arg_ref_foo_ref_2 = &non_arg_ref_foo.2; + *a +} + +// CHECK-DAG: [[VAR_invalid_ref_of_ref_foo]] = !DILocalVariable(name: "invalid_ref_of_ref_foo" +// OPTIMIZED-DAG: [[VAR_ref_foo]] = !DILocalVariable(name: "ref_foo" +// CHECK-DAG: [[VAR_ref_v0]] = !DILocalVariable(name: "ref_v0" +// CHECK-DAG: [[VAR_ref_v1]] = !DILocalVariable(name: "ref_v1" +// CHECK-DAG: [[VAR_ref_v2]] = !DILocalVariable(name: "ref_v2" +// CHECK-DAG: [[ref_ptr_foo]] = !DILocalVariable(name: "ref_ptr_foo" +// CHECK-DAG: [[VAR_ptr_v0]] = !DILocalVariable(name: "ptr_v0" +// CHECK-DAG: [[VAR_ptr_v1]] = !DILocalVariable(name: "ptr_v1" +// CHECK-DAG: [[VAR_ptr_v2]] = !DILocalVariable(name: "ptr_v2" +// CODEGEN-DAG: [[VAR_val_ref]] = !DILocalVariable(name: "val_ref" +// CHECK-DAG: [[VAR_fragment_f]] = !DILocalVariable(name: "fragment_f" +// CHECK-DAG: [[VAR_tuple_dead]] = !DILocalVariable(name: "tuple_dead" +// CHECK-DAG: [[ARG_dead_first_foo]] = !DILocalVariable(name: "dead_first_foo" +// CHECK-DAG: [[VAR_dead_first_v0]] = !DILocalVariable(name: "dead_first_v0" +// CHECK-DAG: [[VAR_index_from_var]] = !DILocalVariable(name: "index_from_var" +// CHECK-DAG: [[VAR_const_index_from_start]] = !DILocalVariable(name: "const_index_from_start" +// CHECK-DAG: [[VAR_const_index_from_end]] = !DILocalVariable(name: "const_index_from_end" +// CHECK-DAG: [[VAR_zst_ref]] = !DILocalVariable(name: "zst_ref" +// CHECK-DAG: [[VAR_non_arg_ref_scalar_ref]] = !DILocalVariable(name: "non_arg_ref_scalar_ref" +// CHECK-DAG: [[VAR_non_arg_ref_foo_ref]] = !DILocalVariable(name: "non_arg_ref_foo_ref" +// CHECK-DAG: [[VAR_non_arg_ref_foo_ref_2]] = !DILocalVariable(name: "non_arg_ref_foo_ref_2" diff --git a/tests/debuginfo/opt/dead_refs.rs b/tests/debuginfo/opt/dead_refs.rs new file mode 100644 index 000000000000..61e741573346 --- /dev/null +++ b/tests/debuginfo/opt/dead_refs.rs @@ -0,0 +1,50 @@ +//@ min-lldb-version: 1800 +//@ min-gdb-version: 13.0 +//@ compile-flags: -g -Copt-level=3 +//@ disable-gdb-pretty-printers + +// Checks that we still can access dead variables from debuginfos. + +// === GDB TESTS =================================================================================== + +// gdb-command:run +// gdb-command:print *ref_v0 +// gdb-check:$1 = 0 + +// gdb-command:print *ref_v1 +// gdb-check:$2 = 1 + +// gdb-command:print *ref_v2 +// gdb-check:$3 = 2 + +// === LLDB TESTS ================================================================================== + +// lldb-command:run +// lldb-command:v *ref_v0 +// lldb-check:[...] 0 + +// lldb-command:v *ref_v1 +// lldb-check:[...] 1 + +// lldb-command:v *ref_v2 +// lldb-check:[...] 2 + +#![allow(unused_variables)] + +use std::hint::black_box; + +pub struct Foo(i32, i64, i32); + +#[inline(never)] +#[no_mangle] +fn test_ref(ref_foo: &Foo) -> i32 { + let ref_v0 = &ref_foo.0; + let ref_v1 = &ref_foo.1; + let ref_v2 = &ref_foo.2; + ref_foo.0 // #break +} + +fn main() { + let foo = black_box(Foo(0, 1, 2)); + black_box(test_ref(&foo)); +} From 8da04285cf6fe61587e16155a8b224dba64bf0be Mon Sep 17 00:00:00 2001 From: dianqk Date: Sat, 19 Jul 2025 18:49:47 +0800 Subject: [PATCH 108/226] mir-opt: Eliminate dead statements even if they are used by debuginfos --- .../rustc_codegen_ssa/src/mir/statement.rs | 3 + compiler/rustc_middle/src/mir/pretty.rs | 3 + compiler/rustc_middle/src/mir/statement.rs | 15 +++++ compiler/rustc_middle/src/mir/visit.rs | 7 +++ compiler/rustc_mir_dataflow/src/debuginfo.rs | 10 ++-- .../rustc_mir_dataflow/src/impls/liveness.rs | 3 - compiler/rustc_mir_transform/src/simplify.rs | 16 +++++ .../src/strip_debuginfo.rs | 17 ++++++ tests/codegen-llvm/debuginfo-dse.rs | 23 +++++++- tests/mir-opt/dead-store-elimination/ref.rs | 4 +- ...ef.tuple.DeadStoreElimination-initial.diff | 2 +- .../inline_shims.drop.Inline.panic-abort.diff | 50 +++++++--------- ...y.run2-{closure#0}.Inline.panic-abort.diff | 4 -- ....run2-{closure#0}.Inline.panic-unwind.diff | 4 -- ...d_place.invalid_place.PreCodegen.after.mir | 13 +++++ .../pre-codegen/dead_on_invalid_place.rs | 27 +++++++++ .../loops.vec_move.PreCodegen.after.mir | 12 +--- ...variant_a-{closure#0}.PreCodegen.after.mir | 58 ++++++++----------- ..._to_slice.PreCodegen.after.panic-abort.mir | 8 --- ...to_slice.PreCodegen.after.panic-unwind.mir | 8 --- 20 files changed, 178 insertions(+), 109 deletions(-) create mode 100644 tests/mir-opt/pre-codegen/dead_on_invalid_place.invalid_place.PreCodegen.after.mir create mode 100644 tests/mir-opt/pre-codegen/dead_on_invalid_place.rs diff --git a/compiler/rustc_codegen_ssa/src/mir/statement.rs b/compiler/rustc_codegen_ssa/src/mir/statement.rs index 99bb31a68b68..80f4f0fcda1c 100644 --- a/compiler/rustc_codegen_ssa/src/mir/statement.rs +++ b/compiler/rustc_codegen_ssa/src/mir/statement.rs @@ -155,6 +155,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { self.debug_poison_to_local(bx, *dest); } } + StmtDebugInfo::InvalidAssign(local) => { + self.debug_poison_to_local(bx, *local); + } } } diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 8cf89f778a2a..d87e3abe3b2e 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -844,6 +844,9 @@ impl Debug for StmtDebugInfo<'_> { StmtDebugInfo::AssignRef(local, place) => { write!(fmt, "{local:?} = &{place:?}") } + StmtDebugInfo::InvalidAssign(local) => { + write!(fmt, "{local:?} = &?") + } } } } diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index 67b4448cfcdd..f310e1e57625 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -527,6 +527,20 @@ impl<'tcx> PlaceRef<'tcx> { }) } + /// Return the place accessed locals that include the base local. + pub fn accessed_locals(self) -> impl Iterator { + std::iter::once(self.local).chain(self.projection.iter().filter_map(|proj| match proj { + ProjectionElem::Index(local) => Some(*local), + ProjectionElem::Deref + | ProjectionElem::Field(_, _) + | ProjectionElem::ConstantIndex { .. } + | ProjectionElem::Subslice { .. } + | ProjectionElem::Downcast(_, _) + | ProjectionElem::OpaqueCast(_) + | ProjectionElem::UnwrapUnsafeBinder(_) => None, + })) + } + /// Generates a new place by appending `more_projections` to the existing ones /// and interning the result. pub fn project_deeper( @@ -1057,4 +1071,5 @@ impl<'tcx> ops::DerefMut for StmtDebugInfos<'tcx> { #[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)] pub enum StmtDebugInfo<'tcx> { AssignRef(Local, Place<'tcx>), + InvalidAssign(Local), } diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 47ae23afd556..9654e189f2ed 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -406,6 +406,13 @@ macro_rules! make_mir_visitor { location ); }, + StmtDebugInfo::InvalidAssign(local) => { + self.visit_local( + $(& $mutability)? *local, + PlaceContext::NonUse(NonUseContext::VarDebugInfo), + location + ); + } } } diff --git a/compiler/rustc_mir_dataflow/src/debuginfo.rs b/compiler/rustc_mir_dataflow/src/debuginfo.rs index 274c5943946c..0d25ce91c9a9 100644 --- a/compiler/rustc_mir_dataflow/src/debuginfo.rs +++ b/compiler/rustc_mir_dataflow/src/debuginfo.rs @@ -5,16 +5,16 @@ use rustc_middle::mir::*; /// Return the set of locals that appear in debuginfo. pub fn debuginfo_locals(body: &Body<'_>) -> DenseBitSet { let mut visitor = DebuginfoLocals(DenseBitSet::new_empty(body.local_decls.len())); - visitor.visit_body(body); + for debuginfo in body.var_debug_info.iter() { + visitor.visit_var_debug_info(debuginfo); + } visitor.0 } struct DebuginfoLocals(DenseBitSet); impl Visitor<'_> for DebuginfoLocals { - fn visit_local(&mut self, local: Local, place_context: PlaceContext, _: Location) { - if place_context == PlaceContext::NonUse(NonUseContext::VarDebugInfo) { - self.0.insert(local); - } + fn visit_local(&mut self, local: Local, _: PlaceContext, _: Location) { + self.0.insert(local); } } diff --git a/compiler/rustc_mir_dataflow/src/impls/liveness.rs b/compiler/rustc_mir_dataflow/src/impls/liveness.rs index 037e2720f365..f6aaa65ad9fd 100644 --- a/compiler/rustc_mir_dataflow/src/impls/liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/liveness.rs @@ -287,9 +287,6 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> { if let Some(destination) = Self::can_be_removed_if_dead(&statement.kind, &self.always_live, &self.debuginfo_locals) && !state.contains(destination.local) - // FIXME: We can eliminate the statement, but we'll need the statements it depends on - // for debuginfos. We need a way to handle this. - && !self.debuginfo_locals.contains(destination.local) { // This store is dead return; diff --git a/compiler/rustc_mir_transform/src/simplify.rs b/compiler/rustc_mir_transform/src/simplify.rs index 0f23482ea8b1..8b5efb742058 100644 --- a/compiler/rustc_mir_transform/src/simplify.rs +++ b/compiler/rustc_mir_transform/src/simplify.rs @@ -657,6 +657,22 @@ impl<'tcx> MutVisitor<'tcx> for LocalUpdater<'tcx> { self.tcx } + fn visit_statement_debuginfo( + &mut self, + stmt_debuginfo: &mut StmtDebugInfo<'tcx>, + location: Location, + ) { + match stmt_debuginfo { + StmtDebugInfo::AssignRef(local, place) => { + if place.as_ref().accessed_locals().any(|local| self.map[local].is_none()) { + *stmt_debuginfo = StmtDebugInfo::InvalidAssign(*local); + } + } + StmtDebugInfo::InvalidAssign(_) => {} + } + self.super_statement_debuginfo(stmt_debuginfo, location); + } + fn visit_local(&mut self, l: &mut Local, _: PlaceContext, _: Location) { *l = self.map[*l].unwrap(); } diff --git a/compiler/rustc_mir_transform/src/strip_debuginfo.rs b/compiler/rustc_mir_transform/src/strip_debuginfo.rs index 9ede8aa79c44..7fec25ccb521 100644 --- a/compiler/rustc_mir_transform/src/strip_debuginfo.rs +++ b/compiler/rustc_mir_transform/src/strip_debuginfo.rs @@ -1,5 +1,6 @@ use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; +use rustc_mir_dataflow::debuginfo::debuginfo_locals; use rustc_session::config::MirStripDebugInfo; /// Conditionally remove some of the VarDebugInfo in MIR. @@ -30,6 +31,22 @@ impl<'tcx> crate::MirPass<'tcx> for StripDebugInfo { if place.local.as_usize() <= body.arg_count && place.local != RETURN_PLACE, ) }); + + let debuginfo_locals = debuginfo_locals(body); + for data in body.basic_blocks.as_mut_preserves_cfg() { + for stmt in data.statements.iter_mut() { + stmt.debuginfos.retain(|debuginfo| match debuginfo { + StmtDebugInfo::AssignRef(local, _) | StmtDebugInfo::InvalidAssign(local) => { + debuginfo_locals.contains(*local) + } + }); + } + data.after_last_stmt_debuginfos.retain(|debuginfo| match debuginfo { + StmtDebugInfo::AssignRef(local, _) | StmtDebugInfo::InvalidAssign(local) => { + debuginfo_locals.contains(*local) + } + }); + } } fn is_required(&self) -> bool { diff --git a/tests/codegen-llvm/debuginfo-dse.rs b/tests/codegen-llvm/debuginfo-dse.rs index 1fcb5fcfc543..78e145a3cdf1 100644 --- a/tests/codegen-llvm/debuginfo-dse.rs +++ b/tests/codegen-llvm/debuginfo-dse.rs @@ -1,4 +1,4 @@ -//@ compile-flags: -Copt-level=3 -g -Zverify-llvm-ir +//@ compile-flags: -Copt-level=3 -g -Zverify-llvm-ir -Zmerge-functions=disabled //@ revisions: CODEGEN OPTIMIZED //@[CODEGEN] compile-flags: -Cno-prepopulate-passes // ignore-tidy-linelength @@ -9,6 +9,13 @@ #[derive(Clone, Copy)] pub struct Foo(i32, i64, i32); +#[repr(C)] +pub struct Bar<'a> { + a: i32, + b: i64, + foo: &'a Foo, +} + #[no_mangle] fn r#ref(ref_foo: &Foo) -> i32 { // CHECK-LABEL: define{{.*}} i32 @ref @@ -78,11 +85,20 @@ pub fn fragment(fragment_v1: Foo, mut fragment_v2: Foo) -> Foo { fragment_v2 } +#[no_mangle] +pub fn deref(bar: Bar) -> i32 { + // CHECK-LABEL: define {{.*}} i32 @deref + // We are unable to represent dereference within this expression. + // CHECK: #dbg_value(ptr poison, [[VAR_deref_dead:![0-9]+]], !DIExpression() + let deref_dead = &bar.foo.2; + bar.a +} + #[no_mangle] pub fn tuple(foo: (i32, &Foo)) -> i32 { // CHECK-LABEL: define{{.*}} i32 @tuple - // CHECK-SAME: (i32 {{.*}}, ptr {{.*}} [[ARG_tuple_foo_1:%.*]]) - // CHECK: #dbg_value(ptr [[ARG_tuple_foo_1]], [[VAR_tuple_dead:![0-9]+]], !DIExpression(DW_OP_plus_uconst, 16, DW_OP_stack_value) + // Although there is no dereference here, there is a dereference in the MIR. + // CHECK: #dbg_value(ptr poison, [[VAR_tuple_dead:![0-9]+]], !DIExpression() let tuple_dead = &foo.1.2; foo.1.0 } @@ -148,6 +164,7 @@ pub fn non_arg_ref(scalar: i32, foo: Foo, a: &i32) -> i32 { // CHECK-DAG: [[VAR_ptr_v2]] = !DILocalVariable(name: "ptr_v2" // CODEGEN-DAG: [[VAR_val_ref]] = !DILocalVariable(name: "val_ref" // CHECK-DAG: [[VAR_fragment_f]] = !DILocalVariable(name: "fragment_f" +// CHECK-DAG: [[VAR_deref_dead]] = !DILocalVariable(name: "deref_dead" // CHECK-DAG: [[VAR_tuple_dead]] = !DILocalVariable(name: "tuple_dead" // CHECK-DAG: [[ARG_dead_first_foo]] = !DILocalVariable(name: "dead_first_foo" // CHECK-DAG: [[VAR_dead_first_v0]] = !DILocalVariable(name: "dead_first_v0" diff --git a/tests/mir-opt/dead-store-elimination/ref.rs b/tests/mir-opt/dead-store-elimination/ref.rs index 18d9ea8b84d7..2d3200edab9f 100644 --- a/tests/mir-opt/dead-store-elimination/ref.rs +++ b/tests/mir-opt/dead-store-elimination/ref.rs @@ -11,9 +11,7 @@ pub fn tuple(v: (i32, &Foo)) -> i32 { // CHECK-LABEL: fn tuple // CHECK: debug _dead => [[dead:_[0-9]+]]; // CHECK: bb0: - // FIXME: Preserve `tmp` for debuginfo, but we can merge it into the debuginfo. - // CHECK: [[tmp:_[0-9]+]] = deref_copy (_1.1: &Foo); - // CHECK-NEXT: DBG: [[dead]] = &((*[[tmp]]).2: i32) + // CHECK: DBG: [[dead]] = &((*_3).2: i32) let _dead = &v.1.c; v.1.a } diff --git a/tests/mir-opt/dead-store-elimination/ref.tuple.DeadStoreElimination-initial.diff b/tests/mir-opt/dead-store-elimination/ref.tuple.DeadStoreElimination-initial.diff index af28c1b9aa87..0b96569cbe4b 100644 --- a/tests/mir-opt/dead-store-elimination/ref.tuple.DeadStoreElimination-initial.diff +++ b/tests/mir-opt/dead-store-elimination/ref.tuple.DeadStoreElimination-initial.diff @@ -13,7 +13,7 @@ bb0: { - StorageLive(_2); - _3 = deref_copy (_1.1: &Foo); +- _3 = deref_copy (_1.1: &Foo); - _2 = &((*_3).2: i32); + // DBG: _2 = &((*_3).2: i32); _4 = deref_copy (_1.1: &Foo); diff --git a/tests/mir-opt/inline/inline_shims.drop.Inline.panic-abort.diff b/tests/mir-opt/inline/inline_shims.drop.Inline.panic-abort.diff index 7fa22524f008..9509739413b7 100644 --- a/tests/mir-opt/inline/inline_shims.drop.Inline.panic-abort.diff +++ b/tests/mir-opt/inline/inline_shims.drop.Inline.panic-abort.diff @@ -16,12 +16,10 @@ + let mut _9: *mut A; + let mut _10: usize; + scope 3 (inlined Vec::::as_mut_ptr) { -+ let mut _11: &alloc::raw_vec::RawVec; + scope 4 (inlined alloc::raw_vec::RawVec::::ptr) { -+ let mut _12: &alloc::raw_vec::RawVecInner; + scope 5 (inlined alloc::raw_vec::RawVecInner::ptr::) { + scope 6 (inlined alloc::raw_vec::RawVecInner::non_null::) { -+ let mut _13: std::ptr::NonNull; ++ let mut _11: std::ptr::NonNull; + scope 7 (inlined Unique::::cast::) { + scope 8 (inlined NonNull::::cast::) { + scope 9 (inlined NonNull::::as_ptr) { @@ -41,15 +39,15 @@ + } + } + scope 14 (inlined drop_in_place::<[A]> - shim(Some([A]))) { -+ let mut _14: usize; -+ let mut _15: *mut A; -+ let mut _16: bool; ++ let mut _12: usize; ++ let mut _13: *mut A; ++ let mut _14: bool; + } + } + } + scope 15 (inlined drop_in_place::> - shim(Some(Option))) { -+ let mut _17: isize; -+ let mut _18: isize; ++ let mut _15: isize; ++ let mut _16: isize; + } bb0: { @@ -64,20 +62,16 @@ + StorageLive(_8); + StorageLive(_9); + StorageLive(_11); -+ StorageLive(_12); -+ StorageLive(_13); -+ _13 = copy (((((*_6).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).0: std::ptr::Unique).0: std::ptr::NonNull); -+ _9 = copy _13 as *mut A (Transmute); -+ StorageDead(_13); -+ StorageDead(_12); ++ _11 = copy (((((*_6).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).0: std::ptr::Unique).0: std::ptr::NonNull); ++ _9 = copy _11 as *mut A (Transmute); + StorageDead(_11); + _10 = copy ((*_6).1: usize); + _8 = *mut [A] from (copy _9, copy _10); + StorageDead(_9); ++ StorageLive(_12); ++ StorageLive(_13); + StorageLive(_14); -+ StorageLive(_15); -+ StorageLive(_16); -+ _14 = const 0_usize; ++ _12 = const 0_usize; + goto -> bb4; } @@ -89,33 +83,33 @@ StorageLive(_5); _5 = copy _2; - _0 = drop_in_place::>(move _5) -> [return: bb2, unwind unreachable]; -+ StorageLive(_17); -+ _17 = discriminant((*_5)); -+ switchInt(move _17) -> [0: bb5, otherwise: bb6]; ++ StorageLive(_15); ++ _15 = discriminant((*_5)); ++ switchInt(move _15) -> [0: bb5, otherwise: bb6]; } bb2: { -+ StorageDead(_16); -+ StorageDead(_15); + StorageDead(_14); ++ StorageDead(_13); ++ StorageDead(_12); + StorageDead(_8); + StorageDead(_10); + drop(((*_4).0: alloc::raw_vec::RawVec)) -> [return: bb1, unwind unreachable]; + } + + bb3: { -+ _15 = &raw mut (*_8)[_14]; -+ _14 = Add(move _14, const 1_usize); -+ drop((*_15)) -> [return: bb4, unwind unreachable]; ++ _13 = &raw mut (*_8)[_12]; ++ _12 = Add(move _12, const 1_usize); ++ drop((*_13)) -> [return: bb4, unwind unreachable]; + } + + bb4: { -+ _16 = Eq(copy _14, copy _10); -+ switchInt(move _16) -> [0: bb3, otherwise: bb2]; ++ _14 = Eq(copy _12, copy _10); ++ switchInt(move _14) -> [0: bb3, otherwise: bb2]; + } + + bb5: { -+ StorageDead(_17); ++ StorageDead(_15); StorageDead(_5); return; + } diff --git a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff index bb81cb4e2600..0acb33febe52 100644 --- a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff +++ b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff @@ -220,16 +220,12 @@ + StorageLive(_49); + StorageLive(_41); + StorageLive(_42); -+ StorageLive(_43); -+ StorageLive(_46); + _44 = copy (_19.0: &mut std::future::Ready<()>); -+ StorageDead(_46); + StorageLive(_47); + _47 = Option::<()>::None; + _42 = copy ((*_44).0: std::option::Option<()>); + ((*_44).0: std::option::Option<()>) = copy _47; + StorageDead(_47); -+ StorageDead(_43); + StorageLive(_48); + _48 = discriminant(_42); + switchInt(move _48) -> [0: bb11, 1: bb12, otherwise: bb5]; diff --git a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff index 520c54824e15..98ee46c29b1b 100644 --- a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff @@ -237,16 +237,12 @@ + StorageLive(_51); + StorageLive(_43); + StorageLive(_44); -+ StorageLive(_45); -+ StorageLive(_48); + _46 = copy (_19.0: &mut std::future::Ready<()>); -+ StorageDead(_48); + StorageLive(_49); + _49 = Option::<()>::None; + _44 = copy ((*_46).0: std::option::Option<()>); + ((*_46).0: std::option::Option<()>) = copy _49; + StorageDead(_49); -+ StorageDead(_45); + StorageLive(_50); + _50 = discriminant(_44); + switchInt(move _50) -> [0: bb16, 1: bb17, otherwise: bb7]; diff --git a/tests/mir-opt/pre-codegen/dead_on_invalid_place.invalid_place.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/dead_on_invalid_place.invalid_place.PreCodegen.after.mir new file mode 100644 index 000000000000..4a2127178fb0 --- /dev/null +++ b/tests/mir-opt/pre-codegen/dead_on_invalid_place.invalid_place.PreCodegen.after.mir @@ -0,0 +1,13 @@ +// MIR for `invalid_place` after PreCodegen + +fn invalid_place(_1: bool) -> bool { + debug c1_ref => _2; + let mut _0: bool; + let mut _2: &bool; + + bb0: { + // DBG: _2 = &?; + _0 = copy _1; + return; + } +} diff --git a/tests/mir-opt/pre-codegen/dead_on_invalid_place.rs b/tests/mir-opt/pre-codegen/dead_on_invalid_place.rs new file mode 100644 index 000000000000..5abe9fa43a5d --- /dev/null +++ b/tests/mir-opt/pre-codegen/dead_on_invalid_place.rs @@ -0,0 +1,27 @@ +#![feature(core_intrinsics, custom_mir)] +#![crate_type = "lib"] + +use std::intrinsics::mir::*; + +// EMIT_MIR dead_on_invalid_place.invalid_place.PreCodegen.after.mir +#[custom_mir(dialect = "runtime")] +pub fn invalid_place(c: bool) -> bool { + // CHECK-LABEL: fn invalid_place + // CHECK: debug c1_ref => [[c1_ref:_[0-9]+]]; + // CHECK: bb0: { + // We cannot read the reference, since `c1` is dead. + // CHECK-NEXT: DBG: [[c1_ref]] = &? + // CHECK-NEXT: _0 = copy _1; + // CHECK-NEXT: return; + mir! { + let _c1_ref: &bool; + let c1: bool; + debug c1_ref => _c1_ref; + { + c1 = c; + _c1_ref = &c1; + RET = c; + Return() + } + } +} diff --git a/tests/mir-opt/pre-codegen/loops.vec_move.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/loops.vec_move.PreCodegen.after.mir index f453741dc6c7..66eb1bcfaa66 100644 --- a/tests/mir-opt/pre-codegen/loops.vec_move.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/loops.vec_move.PreCodegen.after.mir @@ -56,11 +56,11 @@ fn vec_move(_1: Vec) -> () { scope 40 (inlined alloc::raw_vec::RawVec::::capacity) { debug self => _37; let mut _19: usize; - let mut _42: &alloc::raw_vec::RawVecInner; + let mut _39: &alloc::raw_vec::RawVecInner; scope 41 (inlined std::mem::size_of::) { } scope 42 (inlined alloc::raw_vec::RawVecInner::capacity) { - debug self => _42; + debug self => _39; debug elem_size => _19; let mut _21: core::num::niche_types::UsizeNoHighBit; scope 43 (inlined core::num::niche_types::UsizeNoHighBit::as_inner) { @@ -130,7 +130,6 @@ fn vec_move(_1: Vec) -> () { } scope 18 (inlined alloc::raw_vec::RawVec::::non_null) { debug self => _31; - let mut _41: &alloc::raw_vec::RawVecInner; scope 19 (inlined alloc::raw_vec::RawVecInner::non_null::) { let mut _4: std::ptr::NonNull; scope 20 (inlined Unique::::cast::) { @@ -150,9 +149,7 @@ fn vec_move(_1: Vec) -> () { } scope 12 (inlined Vec::::allocator) { debug self => _29; - let mut _39: &alloc::raw_vec::RawVec; scope 13 (inlined alloc::raw_vec::RawVec::::allocator) { - let mut _40: &alloc::raw_vec::RawVecInner; scope 14 (inlined alloc::raw_vec::RawVecInner::allocator) { } } @@ -183,13 +180,10 @@ fn vec_move(_1: Vec) -> () { StorageLive(_3); // DBG: _30 = &_2; // DBG: _29 = &(_2.0: std::vec::Vec); - // DBG: _39 = &((_2.0: std::vec::Vec).0: alloc::raw_vec::RawVec); - // DBG: _40 = &(((_2.0: std::vec::Vec).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner); _3 = &raw const ((((_2.0: std::vec::Vec).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).2: std::alloc::Global); StorageDead(_3); // DBG: _32 = &_2; // DBG: _31 = &((_2.0: std::vec::Vec).0: alloc::raw_vec::RawVec); - // DBG: _41 = &(((_2.0: std::vec::Vec).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner); _4 = copy (((((_2.0: std::vec::Vec).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).0: std::ptr::Unique).0: std::ptr::NonNull); _5 = copy _4 as *const impl Sized (Transmute); _6 = NonNull:: { pointer: copy _5 }; @@ -247,7 +241,7 @@ fn vec_move(_1: Vec) -> () { bb4: { // DBG: _38 = &_2; // DBG: _37 = &((_2.0: std::vec::Vec).0: alloc::raw_vec::RawVec); - // DBG: _42 = &(((_2.0: std::vec::Vec).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner); + // DBG: _39 = &(((_2.0: std::vec::Vec).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner); StorageLive(_19); _19 = SizeOf(impl Sized); switchInt(move _19) -> [0: bb5, otherwise: bb6]; diff --git a/tests/mir-opt/pre-codegen/slice_filter.variant_a-{closure#0}.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/slice_filter.variant_a-{closure#0}.PreCodegen.after.mir index 86c729b3e95e..2cab88182962 100644 --- a/tests/mir-opt/pre-codegen/slice_filter.variant_a-{closure#0}.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/slice_filter.variant_a-{closure#0}.PreCodegen.after.mir @@ -71,21 +71,17 @@ fn variant_a::{closure#0}(_1: &mut {closure@$DIR/slice_filter.rs:7:25: 7:39}, _2 // DBG: _16 = &((*_3).3: usize); StorageLive(_6); // DBG: _17 = &_13; - // DBG: _18 = &_15; + // DBG: _18 = &?; _4 = copy ((*_3).0: usize); _5 = copy ((*_3).2: usize); _6 = Le(copy _4, copy _5); - switchInt(move _6) -> [0: bb1, otherwise: bb2]; + switchInt(move _6) -> [0: bb2, otherwise: bb1]; } bb1: { - goto -> bb4; - } - - bb2: { StorageLive(_9); // DBG: _19 = &_16; - // DBG: _20 = &_14; + // DBG: _20 = &?; StorageLive(_7); _7 = copy ((*_3).3: usize); StorageLive(_8); @@ -93,29 +89,25 @@ fn variant_a::{closure#0}(_1: &mut {closure@$DIR/slice_filter.rs:7:25: 7:39}, _2 _9 = Le(move _7, move _8); StorageDead(_8); StorageDead(_7); - switchInt(move _9) -> [0: bb3, otherwise: bb8]; + switchInt(move _9) -> [0: bb2, otherwise: bb6]; + } + + bb2: { + StorageLive(_10); + // DBG: _21 = &_15; + // DBG: _22 = &?; + _10 = Le(copy _5, copy _4); + switchInt(move _10) -> [0: bb3, otherwise: bb4]; } bb3: { - goto -> bb4; + _0 = const false; + goto -> bb5; } bb4: { - StorageLive(_10); - // DBG: _21 = &_15; - // DBG: _22 = &_13; - _10 = Le(copy _5, copy _4); - switchInt(move _10) -> [0: bb5, otherwise: bb6]; - } - - bb5: { - _0 = const false; - goto -> bb7; - } - - bb6: { // DBG: _23 = &_14; - // DBG: _24 = &_16; + // DBG: _24 = &?; StorageLive(_11); _11 = copy ((*_3).1: usize); StorageLive(_12); @@ -123,20 +115,20 @@ fn variant_a::{closure#0}(_1: &mut {closure@$DIR/slice_filter.rs:7:25: 7:39}, _2 _0 = Le(move _11, move _12); StorageDead(_12); StorageDead(_11); + goto -> bb5; + } + + bb5: { + StorageDead(_10); + goto -> bb7; + } + + bb6: { + _0 = const true; goto -> bb7; } bb7: { - StorageDead(_10); - goto -> bb9; - } - - bb8: { - _0 = const true; - goto -> bb9; - } - - bb9: { StorageDead(_9); StorageDead(_6); return; diff --git a/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir index eebd4a7eb50e..2eee8a97db0d 100644 --- a/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir @@ -11,9 +11,7 @@ fn vec_deref_to_slice(_1: &Vec) -> &[u8] { let mut _4: usize; scope 3 (inlined Vec::::as_ptr) { debug self => _1; - let mut _6: &alloc::raw_vec::RawVec; scope 4 (inlined alloc::raw_vec::RawVec::::ptr) { - let mut _7: &alloc::raw_vec::RawVecInner; scope 5 (inlined alloc::raw_vec::RawVecInner::ptr::) { scope 6 (inlined alloc::raw_vec::RawVecInner::non_null::) { let mut _2: std::ptr::NonNull; @@ -57,14 +55,8 @@ fn vec_deref_to_slice(_1: &Vec) -> &[u8] { bb0: { StorageLive(_2); StorageLive(_3); - StorageLive(_6); - // DBG: _6 = &((*_1).0: alloc::raw_vec::RawVec); - StorageLive(_7); - // DBG: _7 = &(((*_1).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner); _2 = copy (((((*_1).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).0: std::ptr::Unique).0: std::ptr::NonNull); - StorageDead(_7); _3 = copy _2 as *const u8 (Transmute); - StorageDead(_6); StorageLive(_4); _4 = copy ((*_1).1: usize); StorageLive(_5); diff --git a/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir index eebd4a7eb50e..2eee8a97db0d 100644 --- a/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir @@ -11,9 +11,7 @@ fn vec_deref_to_slice(_1: &Vec) -> &[u8] { let mut _4: usize; scope 3 (inlined Vec::::as_ptr) { debug self => _1; - let mut _6: &alloc::raw_vec::RawVec; scope 4 (inlined alloc::raw_vec::RawVec::::ptr) { - let mut _7: &alloc::raw_vec::RawVecInner; scope 5 (inlined alloc::raw_vec::RawVecInner::ptr::) { scope 6 (inlined alloc::raw_vec::RawVecInner::non_null::) { let mut _2: std::ptr::NonNull; @@ -57,14 +55,8 @@ fn vec_deref_to_slice(_1: &Vec) -> &[u8] { bb0: { StorageLive(_2); StorageLive(_3); - StorageLive(_6); - // DBG: _6 = &((*_1).0: alloc::raw_vec::RawVec); - StorageLive(_7); - // DBG: _7 = &(((*_1).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner); _2 = copy (((((*_1).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).0: std::ptr::Unique).0: std::ptr::NonNull); - StorageDead(_7); _3 = copy _2 as *const u8 (Transmute); - StorageDead(_6); StorageLive(_4); _4 = copy ((*_1).1: usize); StorageLive(_5); From 6e225424a66886fa44e671dfa94063fb3a9f4529 Mon Sep 17 00:00:00 2001 From: Scott Schafer Date: Sun, 28 Sep 2025 16:45:24 -0600 Subject: [PATCH 109/226] test: Add empty line to tests getting new flag in next commit --- .../non-1-width-unicode-multiline-label.ascii.stderr | 8 ++++---- .../non-1-width-unicode-multiline-label.rs | 1 + .../non-1-width-unicode-multiline-label.unicode.stderr | 8 ++++---- .../non-whitespace-trimming-2.ascii.stderr | 2 +- tests/ui/diagnostic-width/non-whitespace-trimming-2.rs | 1 + .../non-whitespace-trimming-2.unicode.stderr | 2 +- .../diagnostic-width/non-whitespace-trimming-unicode.rs | 1 + .../non-whitespace-trimming-unicode.stderr | 2 +- tests/ui/diagnostic-width/non-whitespace-trimming.rs | 1 + tests/ui/diagnostic-width/non-whitespace-trimming.stderr | 2 +- 10 files changed, 16 insertions(+), 12 deletions(-) diff --git a/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.ascii.stderr b/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.ascii.stderr index 60ce0d9a1483..fe1ecfbe71d4 100644 --- a/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.ascii.stderr +++ b/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.ascii.stderr @@ -1,5 +1,5 @@ error[E0369]: cannot add `&str` to `&str` - --> $DIR/non-1-width-unicode-multiline-label.rs:7:237 + --> $DIR/non-1-width-unicode-multiline-label.rs:8:237 | LL | ...👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦"; let _a = unicode_is_fun + " really fun!"; | -------------- ^ -------------- &str @@ -14,7 +14,7 @@ LL | let _ = "👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧 | +++++++++++ error[E0369]: cannot add `&str` to `&str` - --> $DIR/non-1-width-unicode-multiline-label.rs:9:384 + --> $DIR/non-1-width-unicode-multiline-label.rs:10:384 | LL | ...👧👦👨👩👧👦👨👩👧👦"; let _a = unicode_is_fun + " really fun!"; | -------------- ^ -------------- &str @@ -29,7 +29,7 @@ LL | let _ = "👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧 | +++++++++++ error[E0369]: cannot add `&str` to `&str` - --> $DIR/non-1-width-unicode-multiline-label.rs:11:260 + --> $DIR/non-1-width-unicode-multiline-label.rs:12:260 | LL | ...࿇࿈࿉࿊࿋࿌࿍࿎࿏࿐࿑࿒࿓࿔࿕࿖࿗࿘࿙࿚"; let _a = unicode_is_fun + " really fun!"; | -------------- ^ -------------- &str @@ -44,7 +44,7 @@ LL | let _ = "ༀ༁༂༃༄༅༆༇༈༉༊་༌།༎༏༐༑༒༓ | +++++++++++ error[E0369]: cannot add `&str` to `&str` - --> $DIR/non-1-width-unicode-multiline-label.rs:13:219 + --> $DIR/non-1-width-unicode-multiline-label.rs:14:219 | LL | ...xxxxxxxxxxxxxxxxxxxx"; let _a = unicode_is_fun + " really fun!"; | -------------- ^ -------------- &str diff --git a/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.rs b/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.rs index 6b9b27f6297f..0940b987fea1 100644 --- a/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.rs +++ b/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.rs @@ -1,4 +1,5 @@ //@ revisions: ascii unicode + //@[unicode] compile-flags: -Zunstable-options --error-format=human-unicode // ignore-tidy-linelength diff --git a/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.unicode.stderr b/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.unicode.stderr index 15b5dd9d7e2d..4dff15642aeb 100644 --- a/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.unicode.stderr +++ b/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.unicode.stderr @@ -1,5 +1,5 @@ error[E0369]: cannot add `&str` to `&str` - ╭▸ $DIR/non-1-width-unicode-multiline-label.rs:7:237 + ╭▸ $DIR/non-1-width-unicode-multiline-label.rs:8:237 │ LL │ …👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦"; let _a = unicode_is_fun + " really fun!"; │ ┬───────────── ┯ ────────────── &str @@ -14,7 +14,7 @@ LL │ let _ = "👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧 ╰╴ +++++++++++ error[E0369]: cannot add `&str` to `&str` - ╭▸ $DIR/non-1-width-unicode-multiline-label.rs:9:384 + ╭▸ $DIR/non-1-width-unicode-multiline-label.rs:10:384 │ LL │ …👨👩👧👦👨👩👧👦👨👩👧👦"; let _a = unicode_is_fun + " really fun!"; │ ┬───────────── ┯ ────────────── &str @@ -29,7 +29,7 @@ LL │ let _ = "👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧 ╰╴ +++++++++++ error[E0369]: cannot add `&str` to `&str` - ╭▸ $DIR/non-1-width-unicode-multiline-label.rs:11:260 + ╭▸ $DIR/non-1-width-unicode-multiline-label.rs:12:260 │ LL │ …࿅࿆࿇࿈࿉࿊࿋࿌࿍࿎࿏࿐࿑࿒࿓࿔࿕࿖࿗࿘࿙࿚"; let _a = unicode_is_fun + " really fun!"; │ ┬───────────── ┯ ────────────── &str @@ -44,7 +44,7 @@ LL │ let _ = "ༀ༁༂༃༄༅༆༇༈༉༊་༌།༎༏༐༑༒༓ ╰╴ +++++++++++ error[E0369]: cannot add `&str` to `&str` - ╭▸ $DIR/non-1-width-unicode-multiline-label.rs:13:219 + ╭▸ $DIR/non-1-width-unicode-multiline-label.rs:14:219 │ LL │ …xxxxxxxxxxxxxxxxxxxxxx"; let _a = unicode_is_fun + " really fun!"; │ ┬───────────── ┯ ────────────── &str diff --git a/tests/ui/diagnostic-width/non-whitespace-trimming-2.ascii.stderr b/tests/ui/diagnostic-width/non-whitespace-trimming-2.ascii.stderr index 70bd149545cf..fe90cfc7b416 100644 --- a/tests/ui/diagnostic-width/non-whitespace-trimming-2.ascii.stderr +++ b/tests/ui/diagnostic-width/non-whitespace-trimming-2.ascii.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/non-whitespace-trimming-2.rs:6:311 + --> $DIR/non-whitespace-trimming-2.rs:7:311 | LL | ...13; let _: usize = 14; let _: usize = 15; let _: () = 42; let _: usize = 0; let _: usize = 1; let _: usize = 2; let _: usize = 3; let ... | -- ^^ expected `()`, found integer diff --git a/tests/ui/diagnostic-width/non-whitespace-trimming-2.rs b/tests/ui/diagnostic-width/non-whitespace-trimming-2.rs index de2f42a4a729..cdbef418f888 100644 --- a/tests/ui/diagnostic-width/non-whitespace-trimming-2.rs +++ b/tests/ui/diagnostic-width/non-whitespace-trimming-2.rs @@ -1,4 +1,5 @@ //@ revisions: ascii unicode + //@[unicode] compile-flags: -Zunstable-options --error-format=human-unicode // ignore-tidy-linelength diff --git a/tests/ui/diagnostic-width/non-whitespace-trimming-2.unicode.stderr b/tests/ui/diagnostic-width/non-whitespace-trimming-2.unicode.stderr index 600d196de167..b0151272cd12 100644 --- a/tests/ui/diagnostic-width/non-whitespace-trimming-2.unicode.stderr +++ b/tests/ui/diagnostic-width/non-whitespace-trimming-2.unicode.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - ╭▸ $DIR/non-whitespace-trimming-2.rs:6:311 + ╭▸ $DIR/non-whitespace-trimming-2.rs:7:311 │ LL │ …= 13; let _: usize = 14; let _: usize = 15; let _: () = 42; let _: usize = 0; let _: usize = 1; let _: usize = 2; let _: usize = 3; let _:… │ ┬─ ━━ expected `()`, found integer diff --git a/tests/ui/diagnostic-width/non-whitespace-trimming-unicode.rs b/tests/ui/diagnostic-width/non-whitespace-trimming-unicode.rs index 8d4d1b162794..f5d1926e1eb6 100644 --- a/tests/ui/diagnostic-width/non-whitespace-trimming-unicode.rs +++ b/tests/ui/diagnostic-width/non-whitespace-trimming-unicode.rs @@ -1,3 +1,4 @@ + // ignore-tidy-linelength fn main() { diff --git a/tests/ui/diagnostic-width/non-whitespace-trimming-unicode.stderr b/tests/ui/diagnostic-width/non-whitespace-trimming-unicode.stderr index 18d80810ab0c..e74afbfeac47 100644 --- a/tests/ui/diagnostic-width/non-whitespace-trimming-unicode.stderr +++ b/tests/ui/diagnostic-width/non-whitespace-trimming-unicode.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/non-whitespace-trimming-unicode.rs:4:415 + --> $DIR/non-whitespace-trimming-unicode.rs:5:415 | LL | ...♣♤♥♦♧♨♩♪♫♬♭♮♯♰♱♲♳♴♵♶♷♸♹♺♻♼♽♾♿⚀⚁⚂⚃⚄⚅⚆⚈⚉4"; let _: () = 42; let _: &str = "🦀☀☁☂☃☄★☆☇☈☉☊☋☌☍☎☏☐☑☒☓ ☖☗☘☙☚☛☜☝☞☟☠☡☢☣☤☥☦☧☨☩☪☫☬☭☮☯☰☱☲☳☴☵☶☷☸☹☺☻☼☽ ... | -- ^^ expected `()`, found integer diff --git a/tests/ui/diagnostic-width/non-whitespace-trimming.rs b/tests/ui/diagnostic-width/non-whitespace-trimming.rs index f6c8d345c652..67066157a60a 100644 --- a/tests/ui/diagnostic-width/non-whitespace-trimming.rs +++ b/tests/ui/diagnostic-width/non-whitespace-trimming.rs @@ -1,3 +1,4 @@ + // ignore-tidy-linelength fn main() { diff --git a/tests/ui/diagnostic-width/non-whitespace-trimming.stderr b/tests/ui/diagnostic-width/non-whitespace-trimming.stderr index 872c5ae3b764..3403dc0e6f1f 100644 --- a/tests/ui/diagnostic-width/non-whitespace-trimming.stderr +++ b/tests/ui/diagnostic-width/non-whitespace-trimming.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/non-whitespace-trimming.rs:4:241 + --> $DIR/non-whitespace-trimming.rs:5:241 | LL | ... () = (); let _: () = (); let _: () = (); let _: () = 42; let _: () = (); let _: () = (); let _: () = (); let _: () = (); let _: () = ... | -- ^^ expected `()`, found integer From 0a5817aa2eb82f03fb8246eedaec089a81291aba Mon Sep 17 00:00:00 2001 From: Scott Schafer Date: Tue, 29 Jul 2025 18:56:32 -0600 Subject: [PATCH 110/226] test: Set diagnostic-width flag for long diagnostic width tests --- .../ui/diagnostic-width/non-1-width-unicode-multiline-label.rs | 2 +- tests/ui/diagnostic-width/non-whitespace-trimming-2.rs | 2 +- tests/ui/diagnostic-width/non-whitespace-trimming-unicode.rs | 2 +- tests/ui/diagnostic-width/non-whitespace-trimming.rs | 2 +- tests/ui/diagnostic-width/tabs-trimming.rs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.rs b/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.rs index 0940b987fea1..7fb95a4ac1e2 100644 --- a/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.rs +++ b/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.rs @@ -1,5 +1,5 @@ //@ revisions: ascii unicode - +//@ compile-flags: --diagnostic-width=145 //@[unicode] compile-flags: -Zunstable-options --error-format=human-unicode // ignore-tidy-linelength diff --git a/tests/ui/diagnostic-width/non-whitespace-trimming-2.rs b/tests/ui/diagnostic-width/non-whitespace-trimming-2.rs index cdbef418f888..6e80649fee86 100644 --- a/tests/ui/diagnostic-width/non-whitespace-trimming-2.rs +++ b/tests/ui/diagnostic-width/non-whitespace-trimming-2.rs @@ -1,5 +1,5 @@ //@ revisions: ascii unicode - +//@ compile-flags: --diagnostic-width=145 //@[unicode] compile-flags: -Zunstable-options --error-format=human-unicode // ignore-tidy-linelength diff --git a/tests/ui/diagnostic-width/non-whitespace-trimming-unicode.rs b/tests/ui/diagnostic-width/non-whitespace-trimming-unicode.rs index f5d1926e1eb6..258eb090aae3 100644 --- a/tests/ui/diagnostic-width/non-whitespace-trimming-unicode.rs +++ b/tests/ui/diagnostic-width/non-whitespace-trimming-unicode.rs @@ -1,4 +1,4 @@ - +//@ compile-flags: --diagnostic-width=145 // ignore-tidy-linelength fn main() { diff --git a/tests/ui/diagnostic-width/non-whitespace-trimming.rs b/tests/ui/diagnostic-width/non-whitespace-trimming.rs index 67066157a60a..cb9450f0de0b 100644 --- a/tests/ui/diagnostic-width/non-whitespace-trimming.rs +++ b/tests/ui/diagnostic-width/non-whitespace-trimming.rs @@ -1,4 +1,4 @@ - +//@ compile-flags: --diagnostic-width=145 // ignore-tidy-linelength fn main() { diff --git a/tests/ui/diagnostic-width/tabs-trimming.rs b/tests/ui/diagnostic-width/tabs-trimming.rs index 96babde33e9a..5cf6bc89a167 100644 --- a/tests/ui/diagnostic-width/tabs-trimming.rs +++ b/tests/ui/diagnostic-width/tabs-trimming.rs @@ -1,5 +1,5 @@ // Test for #78438: ensure underline alignment with many tabs on the left, long line on the right - +//@ compile-flags: --diagnostic-width=145 // ignore-tidy-linelength // ignore-tidy-tab From 9c6897bd07ddb519d0c2f809bb1f83044a170b92 Mon Sep 17 00:00:00 2001 From: Scott Schafer Date: Fri, 30 May 2025 16:22:56 -0600 Subject: [PATCH 111/226] test: Subtract code_offset from width for ui_testing --- compiler/rustc_errors/src/emitter.rs | 2 +- .../clippy/tests/ui/option_env_unwrap.stderr | 2 +- .../ui/too_long_first_doc_paragraph.stderr | 2 +- .../libc_pthread_join_joined.stderr | 4 +- .../concurrency/libc_pthread_join_main.stderr | 4 +- .../fail/data_race/dealloc_read_race1.stderr | 14 +++---- .../data_race/dealloc_read_race_stack.stderr | 4 +- .../fail/data_race/dealloc_write_race1.stderr | 14 +++---- .../data_race/dealloc_write_race_stack.stderr | 4 +- .../fail/intrinsics/copy_unaligned.stderr | 4 +- .../fail/intrinsics/simd-float-to-int.stderr | 4 +- .../retag_data_race_read.stderr | 4 +- .../ui/async-await/async-drop/ex-ice1.stderr | 8 ++-- .../ui/cast/func-pointer-issue-140491.stderr | 4 +- tests/ui/consts/offset_ub.stderr | 4 +- tests/ui/deprecation/deprecation-lint.stderr | 24 +++++------ .../ui/lint/lint-stability-deprecated.stderr | 40 +++++++++---------- tests/ui/parser/raw/too-many-hash.stderr | 4 +- .../empty-match.exhaustive_patterns.stderr | 4 +- .../usefulness/empty-match.normal.stderr | 4 +- tests/ui/simd/not-out-of-bounds.stderr | 4 +- .../alignment/align-fail.stderr | 4 +- 22 files changed, 81 insertions(+), 81 deletions(-) diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 93b1e6b76152..cc3db167316e 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -2021,7 +2021,7 @@ impl HumanEmitter { if let Some(width) = self.diagnostic_width { width.saturating_sub(code_offset) } else if self.ui_testing || cfg!(miri) { - DEFAULT_COLUMN_WIDTH + DEFAULT_COLUMN_WIDTH.saturating_sub(code_offset) } else { termize::dimensions() .map(|(w, _)| w.saturating_sub(code_offset)) diff --git a/src/tools/clippy/tests/ui/option_env_unwrap.stderr b/src/tools/clippy/tests/ui/option_env_unwrap.stderr index bbcbfedb7882..c14a3ea23ff3 100644 --- a/src/tools/clippy/tests/ui/option_env_unwrap.stderr +++ b/src/tools/clippy/tests/ui/option_env_unwrap.stderr @@ -19,7 +19,7 @@ LL | let _ = option_env!("PATH").expect("environment variable PATH isn't set error: this will panic at run-time if the environment variable doesn't exist at compile-time --> tests/ui/option_env_unwrap.rs:14:13 | -LL | let _ = option_env!("__Y__do_not_use").unwrap(); // This test only works if you don't have a __Y__do_not_use env variable in your env... +LL | let _ = option_env!("__Y__do_not_use").unwrap(); // This test only works if you don't have a __Y__do_not_use env variable in you... | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider using the `env!` macro instead diff --git a/src/tools/clippy/tests/ui/too_long_first_doc_paragraph.stderr b/src/tools/clippy/tests/ui/too_long_first_doc_paragraph.stderr index f3f182aa5422..949ada30dc97 100644 --- a/src/tools/clippy/tests/ui/too_long_first_doc_paragraph.stderr +++ b/src/tools/clippy/tests/ui/too_long_first_doc_paragraph.stderr @@ -42,7 +42,7 @@ LL | | /// gravida non lacinia at, rhoncus eu lacus. error: first doc comment paragraph is too long --> tests/ui/too_long_first_doc_paragraph.rs:65:1 | -LL | / /// Some function. This doc-string paragraph is too long. Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lore... +LL | / /// Some function. This doc-string paragraph is too long. Lorem Ipsum is simply dummy text of the printing and typesetting industr... LL | | LL | | /// LL | | /// Here's a second paragraph. It would be preferable to put the details here. diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_joined.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_joined.stderr index 354ad5dfc602..e22f4dadf3f6 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_joined.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_joined.stderr @@ -1,8 +1,8 @@ error: Undefined Behavior: trying to join an already joined thread --> tests/fail-dep/concurrency/libc_pthread_join_joined.rs:LL:CC | -LL | assert_eq!(libc::pthread_join(native, ptr::null_mut()), 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here +LL | ... assert_eq!(libc::pthread_join(native, ptr::null_mut()), 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_main.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_main.stderr index 41190f3f903e..978a9bdac94d 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_main.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_main.stderr @@ -1,8 +1,8 @@ error: Undefined Behavior: trying to join a detached thread --> tests/fail-dep/concurrency/libc_pthread_join_main.rs:LL:CC | -LL | assert_eq!(libc::pthread_join(thread_id, ptr::null_mut()), 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here +LL | ... assert_eq!(libc::pthread_join(thread_id, ptr::null_mut()), 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail/data_race/dealloc_read_race1.stderr b/src/tools/miri/tests/fail/data_race/dealloc_read_race1.stderr index 55e4c4a0d9e8..006946f3f508 100644 --- a/src/tools/miri/tests/fail/data_race/dealloc_read_race1.stderr +++ b/src/tools/miri/tests/fail/data_race/dealloc_read_race1.stderr @@ -1,13 +1,13 @@ error: Undefined Behavior: Data race detected between (1) non-atomic read on thread `unnamed-ID` and (2) deallocation on thread `unnamed-ID` at ALLOC --> tests/fail/data_race/dealloc_read_race1.rs:LL:CC | -LL | / __rust_dealloc( -LL | | -LL | | ptr.0 as *mut _, -LL | | std::mem::size_of::(), -LL | | std::mem::align_of::(), -LL | | ); - | |_____________^ (2) just happened here +LL | / ... __rust_dealloc( +LL | | ... +LL | | ... ptr.0 as *mut _, +LL | | ... std::mem::size_of::(), +LL | | ... std::mem::align_of::(), +LL | | ... ); + | |_______^ (2) just happened here | help: and (1) occurred earlier here --> tests/fail/data_race/dealloc_read_race1.rs:LL:CC diff --git a/src/tools/miri/tests/fail/data_race/dealloc_read_race_stack.stderr b/src/tools/miri/tests/fail/data_race/dealloc_read_race_stack.stderr index ea8fd73393c4..f80e36d4b8fb 100644 --- a/src/tools/miri/tests/fail/data_race/dealloc_read_race_stack.stderr +++ b/src/tools/miri/tests/fail/data_race/dealloc_read_race_stack.stderr @@ -1,8 +1,8 @@ error: Undefined Behavior: Data race detected between (1) non-atomic read on thread `unnamed-ID` and (2) deallocation on thread `unnamed-ID` at ALLOC --> tests/fail/data_race/dealloc_read_race_stack.rs:LL:CC | -LL | } - | ^ (2) just happened here +LL | ... } + | ^ (2) just happened here | help: and (1) occurred earlier here --> tests/fail/data_race/dealloc_read_race_stack.rs:LL:CC diff --git a/src/tools/miri/tests/fail/data_race/dealloc_write_race1.stderr b/src/tools/miri/tests/fail/data_race/dealloc_write_race1.stderr index b306f1812312..41482e4941c1 100644 --- a/src/tools/miri/tests/fail/data_race/dealloc_write_race1.stderr +++ b/src/tools/miri/tests/fail/data_race/dealloc_write_race1.stderr @@ -1,13 +1,13 @@ error: Undefined Behavior: Data race detected between (1) non-atomic write on thread `unnamed-ID` and (2) deallocation on thread `unnamed-ID` at ALLOC --> tests/fail/data_race/dealloc_write_race1.rs:LL:CC | -LL | / __rust_dealloc( -LL | | -LL | | ptr.0 as *mut _, -LL | | std::mem::size_of::(), -LL | | std::mem::align_of::(), -LL | | ); - | |_____________^ (2) just happened here +LL | / ... __rust_dealloc( +LL | | ... +LL | | ... ptr.0 as *mut _, +LL | | ... std::mem::size_of::(), +LL | | ... std::mem::align_of::(), +LL | | ... ); + | |_______^ (2) just happened here | help: and (1) occurred earlier here --> tests/fail/data_race/dealloc_write_race1.rs:LL:CC diff --git a/src/tools/miri/tests/fail/data_race/dealloc_write_race_stack.stderr b/src/tools/miri/tests/fail/data_race/dealloc_write_race_stack.stderr index f3a8707ed14e..e6ebd5b6278e 100644 --- a/src/tools/miri/tests/fail/data_race/dealloc_write_race_stack.stderr +++ b/src/tools/miri/tests/fail/data_race/dealloc_write_race_stack.stderr @@ -1,8 +1,8 @@ error: Undefined Behavior: Data race detected between (1) non-atomic write on thread `unnamed-ID` and (2) deallocation on thread `unnamed-ID` at ALLOC --> tests/fail/data_race/dealloc_write_race_stack.rs:LL:CC | -LL | } - | ^ (2) just happened here +LL | ... } + | ^ (2) just happened here | help: and (1) occurred earlier here --> tests/fail/data_race/dealloc_write_race_stack.rs:LL:CC diff --git a/src/tools/miri/tests/fail/intrinsics/copy_unaligned.stderr b/src/tools/miri/tests/fail/intrinsics/copy_unaligned.stderr index f561e502b514..d9b41f5b0581 100644 --- a/src/tools/miri/tests/fail/intrinsics/copy_unaligned.stderr +++ b/src/tools/miri/tests/fail/intrinsics/copy_unaligned.stderr @@ -1,8 +1,8 @@ error: Undefined Behavior: accessing memory with alignment ALIGN, but alignment ALIGN is required --> tests/fail/intrinsics/copy_unaligned.rs:LL:CC | -LL | std::intrinsics::copy_nonoverlapping(&data[5], ptr, 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here +LL | ... std::intrinsics::copy_nonoverlapping(&data[5], ptr, 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail/intrinsics/simd-float-to-int.stderr b/src/tools/miri/tests/fail/intrinsics/simd-float-to-int.stderr index 04e9cb338cdd..64b8e166c166 100644 --- a/src/tools/miri/tests/fail/intrinsics/simd-float-to-int.stderr +++ b/src/tools/miri/tests/fail/intrinsics/simd-float-to-int.stderr @@ -1,8 +1,8 @@ error: Undefined Behavior: `simd_cast` intrinsic called on 3.40282347E+38f32 which cannot be represented in target type `i32` --> tests/fail/intrinsics/simd-float-to-int.rs:LL:CC | -LL | let _x: i32x2 = f32x2::from_array([f32::MAX, f32::MIN]).to_int_unchecked(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here +LL | ... let _x: i32x2 = f32x2::from_array([f32::MAX, f32::MIN]).to_int_unchecked(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_read.stderr b/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_read.stderr index d84fbc76056d..ee82bbbb1ed1 100644 --- a/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_read.stderr +++ b/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_read.stderr @@ -1,8 +1,8 @@ error: Undefined Behavior: Data race detected between (1) retag read on thread `unnamed-ID` and (2) non-atomic write on thread `unnamed-ID` at ALLOC --> tests/fail/stacked_borrows/retag_data_race_read.rs:LL:CC | -LL | *p = 5; - | ^^^^^^ (2) just happened here +LL | ... *p = 5; + | ^^^^^^ (2) just happened here | help: and (1) occurred earlier here --> tests/fail/stacked_borrows/retag_data_race_read.rs:LL:CC diff --git a/tests/ui/async-await/async-drop/ex-ice1.stderr b/tests/ui/async-await/async-drop/ex-ice1.stderr index 68533edeb086..a1fa89f2c400 100644 --- a/tests/ui/async-await/async-drop/ex-ice1.stderr +++ b/tests/ui/async-await/async-drop/ex-ice1.stderr @@ -1,14 +1,14 @@ error[E0423]: expected function, found module `core::future::async_drop` --> $DIR/ex-ice1.rs:9:35 | -LL | let async_drop_fut = pin!(core::future::async_drop(async {})); - | ^^^^^^^^^^^^^^^^^^^^^^^^ not a function +LL | ... let async_drop_fut = pin!(core::future::async_drop(async {})); + | ^^^^^^^^^^^^^^^^^^^^^^^^ not a function error[E0603]: module `async_drop` is private --> $DIR/ex-ice1.rs:9:49 | -LL | let async_drop_fut = pin!(core::future::async_drop(async {})); - | ^^^^^^^^^^ private module +LL | ... let async_drop_fut = pin!(core::future::async_drop(async {})); + | ^^^^^^^^^^ private module | note: the module `async_drop` is defined here --> $SRC_DIR/core/src/future/mod.rs:LL:COL diff --git a/tests/ui/cast/func-pointer-issue-140491.stderr b/tests/ui/cast/func-pointer-issue-140491.stderr index e1c07010e691..0b777905da1f 100644 --- a/tests/ui/cast/func-pointer-issue-140491.stderr +++ b/tests/ui/cast/func-pointer-issue-140491.stderr @@ -1,8 +1,8 @@ error[E0605]: non-primitive cast: `&for<'a, 'b> fn(&'a Event<'b>) {my_fn}` as `&for<'a, 'b> fn(&'a Event<'b>)` --> $DIR/func-pointer-issue-140491.rs:6:34 | -LL | ..._>) = &my_fn as _; - | ^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object +LL | ... = &my_fn as _; + | ^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object | = note: casting reference expression `&my_fn` because `&` binds tighter than `as` diff --git a/tests/ui/consts/offset_ub.stderr b/tests/ui/consts/offset_ub.stderr index d92ca09223de..f40d34275836 100644 --- a/tests/ui/consts/offset_ub.stderr +++ b/tests/ui/consts/offset_ub.stderr @@ -43,8 +43,8 @@ LL | pub const UNDERFLOW_ADDRESS_SPACE: *const u8 = unsafe { (1 as *const u8).of error[E0080]: in-bounds pointer arithmetic failed: attempting to offset pointer by -$BYTES bytes, but got ALLOC3-0x2 which points to before the beginning of the allocation --> $DIR/offset_ub.rs:16:49 | -LL | ...*const u8 = unsafe { [0u8; 1].as_ptr().wrapping_offset(-2).offset(-2) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `NEGATIVE_OFFSET` failed here +LL | ...nst u8 = unsafe { [0u8; 1].as_ptr().wrapping_offset(-2).offset(-2) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `NEGATIVE_OFFSET` failed here error[E0080]: in-bounds pointer arithmetic failed: attempting to offset pointer by 1 byte, but got ALLOC4 which is at or beyond the end of the allocation of size $BYTES bytes --> $DIR/offset_ub.rs:18:50 diff --git a/tests/ui/deprecation/deprecation-lint.stderr b/tests/ui/deprecation/deprecation-lint.stderr index 95ae1b04d861..5a39b0cf3151 100644 --- a/tests/ui/deprecation/deprecation-lint.stderr +++ b/tests/ui/deprecation/deprecation-lint.stderr @@ -73,8 +73,8 @@ LL | let _ = nested::DeprecatedStruct { error: use of deprecated unit struct `deprecation_lint::nested::DeprecatedUnitStruct`: text --> $DIR/deprecation-lint.rs:48:25 | -LL | let _ = nested::DeprecatedUnitStruct; - | ^^^^^^^^^^^^^^^^^^^^ +LL | ... let _ = nested::DeprecatedUnitStruct; + | ^^^^^^^^^^^^^^^^^^^^ error: use of deprecated unit variant `deprecation_lint::nested::Enum::DeprecatedVariant`: text --> $DIR/deprecation-lint.rs:50:31 @@ -97,8 +97,8 @@ LL | macro_test_arg!(deprecated_text()); error: use of deprecated function `deprecation_lint::deprecated_text`: text --> $DIR/deprecation-lint.rs:60:41 | -LL | macro_test_arg!(macro_test_arg!(deprecated_text())); - | ^^^^^^^^^^^^^^^ +LL | ... macro_test_arg!(macro_test_arg!(deprecated_text())); + | ^^^^^^^^^^^^^^^ error: use of deprecated method `deprecation_lint::Trait::trait_deprecated`: text --> $DIR/deprecation-lint.rs:65:16 @@ -211,8 +211,8 @@ LL | Trait::trait_deprecated_text(&foo); error: use of deprecated method `this_crate::Trait::trait_deprecated_text`: text --> $DIR/deprecation-lint.rs:261:25 | -LL | ::trait_deprecated_text(&foo); - | ^^^^^^^^^^^^^^^^^^^^^ +LL | ... ::trait_deprecated_text(&foo); + | ^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated function `this_crate::deprecated_future`: text --> $DIR/deprecation-lint.rs:264:9 @@ -295,8 +295,8 @@ LL | Trait::trait_deprecated_text(&foo); error: use of deprecated method `this_crate::Trait::trait_deprecated_text`: text --> $DIR/deprecation-lint.rs:299:25 | -LL | ::trait_deprecated_text(&foo); - | ^^^^^^^^^^^^^^^^^^^^^ +LL | ... ::trait_deprecated_text(&foo); + | ^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated function `this_crate::test_fn_closure_body::{closure#0}::bar` --> $DIR/deprecation-lint.rs:317:13 @@ -391,8 +391,8 @@ LL | foo.method_deprecated_text(); error: use of deprecated method `deprecation_lint::MethodTester::method_deprecated_text`: text --> $DIR/deprecation-lint.rs:27:14 | -LL | Foo::method_deprecated_text(&foo); - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | ... Foo::method_deprecated_text(&foo); + | ^^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated method `deprecation_lint::MethodTester::method_deprecated_text`: text --> $DIR/deprecation-lint.rs:28:16 @@ -589,8 +589,8 @@ LL | Foo::method_deprecated_text(&foo); error: use of deprecated method `this_crate::MethodTester::method_deprecated_text`: text --> $DIR/deprecation-lint.rs:257:16 | -LL | ::method_deprecated_text(&foo); - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | ... ::method_deprecated_text(&foo); + | ^^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated method `this_crate::Trait::trait_deprecated_text`: text --> $DIR/deprecation-lint.rs:258:13 diff --git a/tests/ui/lint/lint-stability-deprecated.stderr b/tests/ui/lint/lint-stability-deprecated.stderr index 0399fab746ec..bda4ee82d1fc 100644 --- a/tests/ui/lint/lint-stability-deprecated.stderr +++ b/tests/ui/lint/lint-stability-deprecated.stderr @@ -43,8 +43,8 @@ LL | Trait::trait_deprecated_text(&foo); warning: use of deprecated method `lint_stability::Trait::trait_deprecated_text`: text --> $DIR/lint-stability-deprecated.rs:40:25 | -LL | ::trait_deprecated_text(&foo); - | ^^^^^^^^^^^^^^^^^^^^^ +LL | ... ::trait_deprecated_text(&foo); + | ^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated function `lint_stability::deprecated_unstable`: text --> $DIR/lint-stability-deprecated.rs:42:9 @@ -115,8 +115,8 @@ LL | let _ = Enum::DeprecatedVariant; warning: use of deprecated unit variant `lint_stability::Enum::DeprecatedUnstableVariant`: text --> $DIR/lint-stability-deprecated.rs:121:23 | -LL | let _ = Enum::DeprecatedUnstableVariant; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ... let _ = Enum::DeprecatedUnstableVariant; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated tuple struct `lint_stability::DeprecatedTupleStruct`: text --> $DIR/lint-stability-deprecated.rs:125:17 @@ -127,8 +127,8 @@ LL | let _ = DeprecatedTupleStruct (1); warning: use of deprecated tuple struct `lint_stability::DeprecatedUnstableTupleStruct`: text --> $DIR/lint-stability-deprecated.rs:126:17 | -LL | let _ = DeprecatedUnstableTupleStruct (1); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ... let _ = DeprecatedUnstableTupleStruct (1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated function `lint_stability::deprecated_text`: text --> $DIR/lint-stability-deprecated.rs:135:25 @@ -139,14 +139,14 @@ LL | macro_test_arg!(deprecated_text()); warning: use of deprecated function `lint_stability::deprecated_unstable_text`: text --> $DIR/lint-stability-deprecated.rs:136:25 | -LL | macro_test_arg!(deprecated_unstable_text()); - | ^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ... macro_test_arg!(deprecated_unstable_text()); + | ^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated function `lint_stability::deprecated_text`: text --> $DIR/lint-stability-deprecated.rs:137:41 | -LL | macro_test_arg!(macro_test_arg!(deprecated_text())); - | ^^^^^^^^^^^^^^^ +LL | ... macro_test_arg!(macro_test_arg!(deprecated_text())); + | ^^^^^^^^^^^^^^^ warning: use of deprecated method `lint_stability::Trait::trait_deprecated`: text --> $DIR/lint-stability-deprecated.rs:142:16 @@ -169,8 +169,8 @@ LL | Trait::trait_deprecated_text(&foo); warning: use of deprecated method `lint_stability::Trait::trait_deprecated_text`: text --> $DIR/lint-stability-deprecated.rs:148:25 | -LL | ::trait_deprecated_text(&foo); - | ^^^^^^^^^^^^^^^^^^^^^ +LL | ... ::trait_deprecated_text(&foo); + | ^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated method `lint_stability::Trait::trait_deprecated_unstable`: text --> $DIR/lint-stability-deprecated.rs:150:16 @@ -211,8 +211,8 @@ LL | trait LocalTrait2 : DeprecatedTrait { } warning: use of deprecated function `inheritance::inherited_stability::unstable_mod::deprecated`: text --> $DIR/lint-stability-deprecated.rs:205:23 | -LL | unstable_mod::deprecated(); - | ^^^^^^^^^^ +LL | ... unstable_mod::deprecated(); + | ^^^^^^^^^^ warning: use of deprecated function `this_crate::deprecated`: text --> $DIR/lint-stability-deprecated.rs:327:9 @@ -367,14 +367,14 @@ LL | foo.method_deprecated_text(); warning: use of deprecated method `lint_stability::MethodTester::method_deprecated_text`: text --> $DIR/lint-stability-deprecated.rs:35:14 | -LL | Foo::method_deprecated_text(&foo); - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | ... Foo::method_deprecated_text(&foo); + | ^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated method `lint_stability::MethodTester::method_deprecated_text`: text --> $DIR/lint-stability-deprecated.rs:36:16 | -LL | ::method_deprecated_text(&foo); - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | ... ::method_deprecated_text(&foo); + | ^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated method `lint_stability::Trait::trait_deprecated_text`: text --> $DIR/lint-stability-deprecated.rs:37:13 @@ -397,8 +397,8 @@ LL | foo.method_deprecated_unstable(); warning: use of deprecated method `lint_stability::MethodTester::method_deprecated_unstable`: text --> $DIR/lint-stability-deprecated.rs:44:14 | -LL | Foo::method_deprecated_unstable(&foo); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ... Foo::method_deprecated_unstable(&foo); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated method `lint_stability::MethodTester::method_deprecated_unstable`: text --> $DIR/lint-stability-deprecated.rs:45:16 diff --git a/tests/ui/parser/raw/too-many-hash.stderr b/tests/ui/parser/raw/too-many-hash.stderr index 6b3854eb4a23..499b21d3982c 100644 --- a/tests/ui/parser/raw/too-many-hash.stderr +++ b/tests/ui/parser/raw/too-many-hash.stderr @@ -1,8 +1,8 @@ error: too many `#` symbols: raw strings may be delimited by up to 255 `#` symbols, but found 256 --> $DIR/too-many-hash.rs:4:19 | -LL | ... = r####################################################...#######################################; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^...^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ... = r###################################################...######################################; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^...^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/pattern/usefulness/empty-match.exhaustive_patterns.stderr b/tests/ui/pattern/usefulness/empty-match.exhaustive_patterns.stderr index f2067f0341fc..b649aa122b25 100644 --- a/tests/ui/pattern/usefulness/empty-match.exhaustive_patterns.stderr +++ b/tests/ui/pattern/usefulness/empty-match.exhaustive_patterns.stderr @@ -346,8 +346,8 @@ LL + NonEmptyEnum2::Foo(_) | NonEmptyEnum2::Bar => todo!() error[E0004]: non-exhaustive patterns: `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered --> $DIR/empty-match.rs:71:24 | -LL | match_guarded_arm!(NonEmptyEnum5::V1); - | ^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered +LL | ...ded_arm!(NonEmptyEnum5::V1); + | ^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered | note: `NonEmptyEnum5` defined here --> $DIR/empty-match.rs:38:10 diff --git a/tests/ui/pattern/usefulness/empty-match.normal.stderr b/tests/ui/pattern/usefulness/empty-match.normal.stderr index f2067f0341fc..b649aa122b25 100644 --- a/tests/ui/pattern/usefulness/empty-match.normal.stderr +++ b/tests/ui/pattern/usefulness/empty-match.normal.stderr @@ -346,8 +346,8 @@ LL + NonEmptyEnum2::Foo(_) | NonEmptyEnum2::Bar => todo!() error[E0004]: non-exhaustive patterns: `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered --> $DIR/empty-match.rs:71:24 | -LL | match_guarded_arm!(NonEmptyEnum5::V1); - | ^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered +LL | ...ded_arm!(NonEmptyEnum5::V1); + | ^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered | note: `NonEmptyEnum5` defined here --> $DIR/empty-match.rs:38:10 diff --git a/tests/ui/simd/not-out-of-bounds.stderr b/tests/ui/simd/not-out-of-bounds.stderr index 734c21fbf415..d5bcd15d97d6 100644 --- a/tests/ui/simd/not-out-of-bounds.stderr +++ b/tests/ui/simd/not-out-of-bounds.stderr @@ -79,8 +79,8 @@ LL | simd_insert(v, 2, 0u8); error[E0511]: invalid monomorphization of `simd_extract` intrinsic: SIMD index #1 is out of bounds (limit 2) --> $DIR/not-out-of-bounds.rs:85:24 | -LL | let _val: u8 = simd_extract(v, 2); - | ^^^^^^^^^^^^^^^^^^ +LL | ... let _val: u8 = simd_extract(v, 2); + | ^^^^^^^^^^^^^^^^^^ error: aborting due to 9 previous errors diff --git a/tests/ui/transmutability/alignment/align-fail.stderr b/tests/ui/transmutability/alignment/align-fail.stderr index 7b69820a3c6d..164c57a36244 100644 --- a/tests/ui/transmutability/alignment/align-fail.stderr +++ b/tests/ui/transmutability/alignment/align-fail.stderr @@ -1,8 +1,8 @@ error[E0277]: `&[u8; 0]` cannot be safely transmuted into `&[u16; 0]` --> $DIR/align-fail.rs:21:55 | -LL | ...tatic [u8; 0], &'static [u16; 0]>(); - | ^^^^^^^^^^^^^^^^^ the minimum alignment of `&[u8; 0]` (1) should be greater than that of `&[u16; 0]` (2) +LL | ...ic [u8; 0], &'static [u16; 0]>(); + | ^^^^^^^^^^^^^^^^^ the minimum alignment of `&[u8; 0]` (1) should be greater than that of `&[u16; 0]` (2) | note: required by a bound in `is_maybe_transmutable` --> $DIR/align-fail.rs:9:14 From 78c2e58a7acb3a73e65b9ccfe4a73e79baa0d9ca Mon Sep 17 00:00:00 2001 From: Yotam Ofek Date: Wed, 1 Oct 2025 19:58:49 +0300 Subject: [PATCH 112/226] [rustdoc] Optimize "highlight::end_expansion" --- src/librustdoc/html/highlight.rs | 45 ++++++++++++++------------------ 1 file changed, 19 insertions(+), 26 deletions(-) diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 1dcb4dcc3ff8..eeb916494d1e 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -8,7 +8,7 @@ use std::borrow::Cow; use std::collections::VecDeque; use std::fmt::{self, Display, Write}; -use std::iter; +use std::{cmp, iter}; use rustc_data_structures::fx::FxIndexMap; use rustc_lexer::{Cursor, FrontmatterAllowed, LiteralKind, TokenKind}; @@ -345,33 +345,26 @@ fn end_expansion<'a, W: Write>( token_handler.pending_elems.push((Cow::Borrowed(""), Some(Class::Expansion))); return Some(expanded_code); } - if expansion_start_tags.is_empty() && token_handler.closing_tags.is_empty() { - // No need tag opened so we can just close expansion. - token_handler.pending_elems.push((Cow::Borrowed(""), Some(Class::Expansion))); - return None; + + let skip = iter::zip(token_handler.closing_tags.as_slice(), expansion_start_tags) + .position(|(tag, start_tag)| tag != start_tag) + .unwrap_or_else(|| cmp::min(token_handler.closing_tags.len(), expansion_start_tags.len())); + + let tags = iter::chain( + expansion_start_tags.iter().skip(skip), + token_handler.closing_tags.iter().skip(skip), + ); + + let mut elem = Cow::Borrowed(""); + + for (tag, _) in tags.clone() { + elem.to_mut().push_str(tag); + } + for (_, class) in tags { + write!(elem.to_mut(), "", class.as_html()).unwrap(); } - // If tags were opened inside the expansion, we need to close them and re-open them outside - // of the expansion span. - let mut out = String::new(); - let mut end = String::new(); - - let mut closing_tags = token_handler.closing_tags.iter().peekable(); - let mut start_closing_tags = expansion_start_tags.iter().peekable(); - - while let (Some(tag), Some(start_tag)) = (closing_tags.peek(), start_closing_tags.peek()) - && tag == start_tag - { - closing_tags.next(); - start_closing_tags.next(); - } - for (tag, class) in start_closing_tags.chain(closing_tags) { - out.push_str(tag); - end.push_str(&format!("", class.as_html())); - } - token_handler - .pending_elems - .push((Cow::Owned(format!("{out}{end}")), Some(Class::Expansion))); + token_handler.pending_elems.push((elem, Some(Class::Expansion))); None } From fe66eaa67acc47525db6f13cf97d54780d87b805 Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Mon, 7 Jul 2025 22:36:23 +0200 Subject: [PATCH 113/226] Fix backtraces with `-C panic=abort` on linux; emit unwind tables by default The linux backtrace unwinder relies on unwind tables to work properly, and generating and printing a backtrace is done by for example the default panic hook. Begin emitting unwind tables by default again with `-C panic=abort` (see history below) so that backtraces work. History ======= Backtraces with `-C panic=abort` used to work in Rust 1.22 but broke in Rust 1.23, because in 1.23 we stopped emitting unwind tables with `-C panic=abort` (see 24cc38e3b00). In 1.45 (see cda994633ee) a workaround in the form of `-C force-unwind-tables=yes` was added. `-C panic=abort` was added in [Rust 1.10](https://blog.rust-lang.org/2016/07/07/Rust-1.10/#what-s-in-1-10-stable) and the motivation was binary size and compile time. But given how confusing that behavior has turned out to be, it is better to make binary size optimization opt-in with `-C force-unwind-tables=no` rather than default since the current default breaks backtraces. Besides, if binary size is a primary concern, there are many other tricks that can be used that has a higher impact. --- compiler/rustc_session/src/session.rs | 3 +- .../rustc_target/src/spec/base/android.rs | 4 -- compiler/rustc_target/src/spec/base/linux.rs | 3 + .../targets/arm_unknown_linux_gnueabihf.rs | 5 ++ tests/run-make/panic-abort-eh_frame/rmake.rs | 11 ++-- ...panic-abort-backtrace-without-debuginfo.rs | 57 +++++++++++++++++++ 6 files changed, 74 insertions(+), 9 deletions(-) create mode 100644 tests/ui/panics/panic-abort-backtrace-without-debuginfo.rs diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 25b46241c52d..172672a80fbd 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -758,7 +758,8 @@ impl Session { // ELF x86-64 abi, but it can be disabled for some compilation units. // // Typically when we're compiling with `-C panic=abort` we don't need - // `uwtable` because we can't generate any exceptions! + // `uwtable` because we can't generate any exceptions! But note that + // some targets require unwind tables to generate backtraces. // Unwind tables are needed when compiling with `-C panic=unwind`, but // LLVM won't omit unwind tables unless the function is also marked as // `nounwind`, so users are allowed to disable `uwtable` emission. diff --git a/compiler/rustc_target/src/spec/base/android.rs b/compiler/rustc_target/src/spec/base/android.rs index 0426ea44c6a2..df2757aaabf6 100644 --- a/compiler/rustc_target/src/spec/base/android.rs +++ b/compiler/rustc_target/src/spec/base/android.rs @@ -8,10 +8,6 @@ pub(crate) fn opts() -> TargetOptions { base.tls_model = TlsModel::Emulated; base.has_thread_local = false; base.supported_sanitizers = SanitizerSet::ADDRESS; - // This is for backward compatibility, see https://github.com/rust-lang/rust/issues/49867 - // for context. (At that time, there was no `-C force-unwind-tables`, so the only solution - // was to always emit `uwtable`). - base.default_uwtable = true; base.crt_static_respected = true; base } diff --git a/compiler/rustc_target/src/spec/base/linux.rs b/compiler/rustc_target/src/spec/base/linux.rs index 9982c254eca5..26e4590cf5e6 100644 --- a/compiler/rustc_target/src/spec/base/linux.rs +++ b/compiler/rustc_target/src/spec/base/linux.rs @@ -12,6 +12,9 @@ pub(crate) fn opts() -> TargetOptions { relro_level: RelroLevel::Full, has_thread_local: true, crt_static_respected: true, + // We want backtraces to work by default and they rely on unwind tables + // (regardless of `-C panic` strategy). + default_uwtable: true, supported_split_debuginfo: Cow::Borrowed(&[ SplitDebuginfo::Packed, SplitDebuginfo::Unpacked, diff --git a/compiler/rustc_target/src/spec/targets/arm_unknown_linux_gnueabihf.rs b/compiler/rustc_target/src/spec/targets/arm_unknown_linux_gnueabihf.rs index a3f5389f0aa7..0c711d5e71ab 100644 --- a/compiler/rustc_target/src/spec/targets/arm_unknown_linux_gnueabihf.rs +++ b/compiler/rustc_target/src/spec/targets/arm_unknown_linux_gnueabihf.rs @@ -19,6 +19,11 @@ pub(crate) fn target() -> Target { max_atomic_width: Some(64), mcount: "\u{1}__gnu_mcount_nc".into(), llvm_mcount_intrinsic: Some("llvm.arm.gnu.eabi.mcount".into()), + // The default on linux is to have `default_uwtable=true`, but on + // this target we get an "`__aeabi_unwind_cpp_pr0` not defined" + // linker error, so set it to `true` here. + // FIXME(#146996): Remove this override once #146996 has been fixed. + default_uwtable: false, ..base::linux_gnu::opts() }, } diff --git a/tests/run-make/panic-abort-eh_frame/rmake.rs b/tests/run-make/panic-abort-eh_frame/rmake.rs index 23d95dc57749..2eccde627955 100644 --- a/tests/run-make/panic-abort-eh_frame/rmake.rs +++ b/tests/run-make/panic-abort-eh_frame/rmake.rs @@ -1,9 +1,11 @@ // An `.eh_frame` section in an object file is a symptom of an UnwindAction::Terminate // being inserted, useful for determining whether or not unwinding is necessary. -// This is useless when panics would NEVER unwind due to -C panic=abort. This section should -// therefore never appear in the emit file of a -C panic=abort compilation, and this test -// checks that this is respected. -// See https://github.com/rust-lang/rust/pull/112403 +// This is useless when panics would NEVER unwind due to -C panic=abort and when we don't need +// being able to generate backtraces (which depend on unwind tables on linux). This section should +// therefore never appear in the emit file of a -C panic=abort compilation +// with -C force-unwind-tables=no, and this test checks that this is respected. +// See https://github.com/rust-lang/rust/pull/112403 and +// https://github.com/rust-lang/rust/pull/143613. //@ only-linux // FIXME(Oneirical): the DW_CFA symbol appears on Windows-gnu, because uwtable @@ -19,6 +21,7 @@ fn main() { .panic("abort") .edition("2021") .arg("-Zvalidate-mir") + .arg("-Cforce-unwind-tables=no") .run(); llvm_objdump().arg("--dwarf=frames").input("foo.o").run().assert_stdout_not_contains("DW_CFA"); } diff --git a/tests/ui/panics/panic-abort-backtrace-without-debuginfo.rs b/tests/ui/panics/panic-abort-backtrace-without-debuginfo.rs new file mode 100644 index 000000000000..a29afd68523b --- /dev/null +++ b/tests/ui/panics/panic-abort-backtrace-without-debuginfo.rs @@ -0,0 +1,57 @@ +//! Test that with `-C panic=abort` the backtrace is not cut off by default +//! (i.e. without using `-C force-unwind-tables=yes`) by ensuring that our own +//! functions are in the backtrace. If we just check one function it might be +//! the last function, so make sure the backtrace can continue by checking for +//! two functions. Regression test for +//! . + +//@ run-pass +//@ needs-subprocess +// We want to test if unwind tables are emitted by default. We must make sure +// to disable debuginfo to test that, because enabling debuginfo also means that +// unwind tables are emitted, which prevents us from testing what we want. +// We also need to set opt-level=0 to avoid optimizing away our functions. +//@ compile-flags: -C panic=abort -C opt-level=0 -C debuginfo=0 +//@ no-prefer-dynamic +//@ ignore-apple +//@ ignore-arm-unknown-linux-gnueabihf FIXME(#146996) Try removing this once #146996 has been fixed. +//@ ignore-msvc Backtraces on Windows requires debuginfo which we can't use here + +static FN_1: &str = "this_function_must_be_in_the_backtrace"; +fn this_function_must_be_in_the_backtrace() { + and_this_function_too(); +} + +static FN_2: &str = "and_this_function_too"; +fn and_this_function_too() { + panic!("generate panic backtrace"); +} + +fn run_test() { + let output = std::process::Command::new(std::env::current_exe().unwrap()) + .arg("whatever") + .env("RUST_BACKTRACE", "full") + .output() + .unwrap(); + let backtrace = std::str::from_utf8(&output.stderr).unwrap(); + + fn assert(function_name: &str, backtrace: &str) { + assert!( + backtrace.contains(function_name), + "ERROR: no `{}` in stderr! actual stderr: {}", + function_name, + backtrace + ); + } + assert(FN_1, backtrace); + assert(FN_2, backtrace); +} + +fn main() { + let args: Vec = std::env::args().collect(); + if args.len() == 1 { + run_test(); + } else { + this_function_must_be_in_the_backtrace(); + } +} From 5a8f963426c72c5bd306b2620a6c148b720217d0 Mon Sep 17 00:00:00 2001 From: Karol Zwolak Date: Thu, 2 Oct 2025 20:13:18 +0200 Subject: [PATCH 114/226] fix wording; we're replacing coma with a bar --- ...ixed => only-replace-intended-coma-not-all-in-pattern.fixed} | 2 +- ...tern.rs => only-replace-intended-coma-not-all-in-pattern.rs} | 2 +- ...err => only-replace-intended-coma-not-all-in-pattern.stderr} | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename tests/ui/suggestions/{only-replace-intended-bar-not-all-in-pattern.fixed => only-replace-intended-coma-not-all-in-pattern.fixed} (84%) rename tests/ui/suggestions/{only-replace-intended-bar-not-all-in-pattern.rs => only-replace-intended-coma-not-all-in-pattern.rs} (84%) rename tests/ui/suggestions/{only-replace-intended-bar-not-all-in-pattern.stderr => only-replace-intended-coma-not-all-in-pattern.stderr} (89%) diff --git a/tests/ui/suggestions/only-replace-intended-bar-not-all-in-pattern.fixed b/tests/ui/suggestions/only-replace-intended-coma-not-all-in-pattern.fixed similarity index 84% rename from tests/ui/suggestions/only-replace-intended-bar-not-all-in-pattern.fixed rename to tests/ui/suggestions/only-replace-intended-coma-not-all-in-pattern.fixed index 5abc1edd381b..0258f868f007 100644 --- a/tests/ui/suggestions/only-replace-intended-bar-not-all-in-pattern.fixed +++ b/tests/ui/suggestions/only-replace-intended-coma-not-all-in-pattern.fixed @@ -1,7 +1,7 @@ //@ run-rustfix // Regression test for issue #143330. -// Ensure we suggest to replace only the intended bar with a comma, not all bars in the pattern. +// Ensure we suggest to replace only the intended coma with a bar, not all commas in the pattern. fn main() { struct Foo { x: i32, ch: char } diff --git a/tests/ui/suggestions/only-replace-intended-bar-not-all-in-pattern.rs b/tests/ui/suggestions/only-replace-intended-coma-not-all-in-pattern.rs similarity index 84% rename from tests/ui/suggestions/only-replace-intended-bar-not-all-in-pattern.rs rename to tests/ui/suggestions/only-replace-intended-coma-not-all-in-pattern.rs index 5bd267c3bc40..7d5087fa0ffa 100644 --- a/tests/ui/suggestions/only-replace-intended-bar-not-all-in-pattern.rs +++ b/tests/ui/suggestions/only-replace-intended-coma-not-all-in-pattern.rs @@ -1,7 +1,7 @@ //@ run-rustfix // Regression test for issue #143330. -// Ensure we suggest to replace only the intended bar with a comma, not all bars in the pattern. +// Ensure we suggest to replace only the intended coma with a bar, not all commas in the pattern. fn main() { struct Foo { x: i32, ch: char } diff --git a/tests/ui/suggestions/only-replace-intended-bar-not-all-in-pattern.stderr b/tests/ui/suggestions/only-replace-intended-coma-not-all-in-pattern.stderr similarity index 89% rename from tests/ui/suggestions/only-replace-intended-bar-not-all-in-pattern.stderr rename to tests/ui/suggestions/only-replace-intended-coma-not-all-in-pattern.stderr index db0bb8c50e86..050f1b8a2b33 100644 --- a/tests/ui/suggestions/only-replace-intended-bar-not-all-in-pattern.stderr +++ b/tests/ui/suggestions/only-replace-intended-coma-not-all-in-pattern.stderr @@ -1,5 +1,5 @@ error: unexpected `,` in pattern - --> $DIR/only-replace-intended-bar-not-all-in-pattern.rs:12:30 + --> $DIR/only-replace-intended-coma-not-all-in-pattern.rs:12:30 | LL | Foo { x: 2, ch: ',' }, Foo { x: 3, ch: '@' } => (), | ^ From d1d7b9472a18780499162b4a91beea729bc2c13b Mon Sep 17 00:00:00 2001 From: Karol Zwolak Date: Thu, 2 Oct 2025 20:24:34 +0200 Subject: [PATCH 115/226] bring back plural 'alternatives' in suggestion message --- compiler/rustc_parse/src/parser/diagnostics.rs | 2 +- .../issue-48492-tuple-destructure-missing-parens.stderr | 2 +- tests/ui/feature-gates/feature-gate-never_patterns.stderr | 2 +- tests/ui/parser/match-arm-without-body.stderr | 4 ++-- .../only-replace-intended-coma-not-all-in-pattern.stderr | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 5d6da6093120..1cb1618e5844 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -2961,7 +2961,7 @@ impl<'a> Parser<'a> { if let CommaRecoveryMode::EitherTupleOrPipe = rt { err.span_suggestion( comma_span, - "...or a vertical bar to match on alternative", + "...or a vertical bar to match on alternatives", " |", Applicability::MachineApplicable, ); diff --git a/tests/ui/did_you_mean/issue-48492-tuple-destructure-missing-parens.stderr b/tests/ui/did_you_mean/issue-48492-tuple-destructure-missing-parens.stderr index dc875ad27ea6..286d60213966 100644 --- a/tests/ui/did_you_mean/issue-48492-tuple-destructure-missing-parens.stderr +++ b/tests/ui/did_you_mean/issue-48492-tuple-destructure-missing-parens.stderr @@ -32,7 +32,7 @@ help: try adding parentheses to match on a tuple... | LL | (Nucleotide::Adenine, Nucleotide::Cytosine, _) => true | + + -help: ...or a vertical bar to match on alternative +help: ...or a vertical bar to match on alternatives | LL - Nucleotide::Adenine, Nucleotide::Cytosine, _ => true LL + Nucleotide::Adenine | Nucleotide::Cytosine, _ => true diff --git a/tests/ui/feature-gates/feature-gate-never_patterns.stderr b/tests/ui/feature-gates/feature-gate-never_patterns.stderr index 5e810249b906..e655209924a0 100644 --- a/tests/ui/feature-gates/feature-gate-never_patterns.stderr +++ b/tests/ui/feature-gates/feature-gate-never_patterns.stderr @@ -8,7 +8,7 @@ help: try adding parentheses to match on a tuple... | LL | (Some(_),) | + + -help: ...or a vertical bar to match on alternative +help: ...or a vertical bar to match on alternatives | LL - Some(_), LL + Some(_) | diff --git a/tests/ui/parser/match-arm-without-body.stderr b/tests/ui/parser/match-arm-without-body.stderr index 85ff7db8d4aa..59a323f2cc1c 100644 --- a/tests/ui/parser/match-arm-without-body.stderr +++ b/tests/ui/parser/match-arm-without-body.stderr @@ -16,7 +16,7 @@ help: try adding parentheses to match on a tuple... | LL | (Some(_),) | + + -help: ...or a vertical bar to match on alternative +help: ...or a vertical bar to match on alternatives | LL - Some(_), LL + Some(_) | @@ -36,7 +36,7 @@ LL | LL | LL ~ _) => {} | -help: ...or a vertical bar to match on alternative +help: ...or a vertical bar to match on alternatives | LL - Some(_), LL + Some(_) | diff --git a/tests/ui/suggestions/only-replace-intended-coma-not-all-in-pattern.stderr b/tests/ui/suggestions/only-replace-intended-coma-not-all-in-pattern.stderr index 050f1b8a2b33..ee75e2db1334 100644 --- a/tests/ui/suggestions/only-replace-intended-coma-not-all-in-pattern.stderr +++ b/tests/ui/suggestions/only-replace-intended-coma-not-all-in-pattern.stderr @@ -8,7 +8,7 @@ help: try adding parentheses to match on a tuple... | LL | (Foo { x: 2, ch: ',' }, Foo { x: 3, ch: '@' }) => (), | + + -help: ...or a vertical bar to match on alternative +help: ...or a vertical bar to match on alternatives | LL - Foo { x: 2, ch: ',' }, Foo { x: 3, ch: '@' } => (), LL + Foo { x: 2, ch: ',' } | Foo { x: 3, ch: '@' } => (), From fab9906c21e8c751943760d44c4a12cac2bceb05 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Thu, 2 Oct 2025 22:15:37 +0900 Subject: [PATCH 116/226] test: Add regression test for ICE when projecting associated types through dyn traits --- .../projection-dyn-associated-type.rs | 28 ++++++++++ .../projection-dyn-associated-type.stderr | 52 +++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 tests/ui/associated-types/projection-dyn-associated-type.rs create mode 100644 tests/ui/associated-types/projection-dyn-associated-type.stderr diff --git a/tests/ui/associated-types/projection-dyn-associated-type.rs b/tests/ui/associated-types/projection-dyn-associated-type.rs new file mode 100644 index 000000000000..3b981e7987e0 --- /dev/null +++ b/tests/ui/associated-types/projection-dyn-associated-type.rs @@ -0,0 +1,28 @@ +// Regression test for the projection bug in +// +//@ compile-flags: -Zincremental-verify-ich=yes +//@ incremental + +pub trait A {} +pub trait B: A {} + +pub trait Mirror { + type Assoc: ?Sized; +} + +impl Mirror for A { + //~^ ERROR the type parameter `T` is not constrained by the impl trait, self type, or predicates [E0207] + //~| WARN trait objects without an explicit `dyn` are deprecated [bare_trait_objects] + //~| WARN this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + type Assoc = T; +} + +pub fn foo<'a>( + x: &'a ::Assoc +) -> &'a ::Assoc { + //~^ ERROR the trait bound `(dyn B + 'static): Mirror` is not satisfied [E0277] + //~| ERROR the trait bound `(dyn B + 'static): Mirror` is not satisfied [E0277] + static +} //~ ERROR expected identifier, found `}` + +pub fn main() {} diff --git a/tests/ui/associated-types/projection-dyn-associated-type.stderr b/tests/ui/associated-types/projection-dyn-associated-type.stderr new file mode 100644 index 000000000000..1ac2beb0414e --- /dev/null +++ b/tests/ui/associated-types/projection-dyn-associated-type.stderr @@ -0,0 +1,52 @@ +error: expected identifier, found `}` + --> $DIR/projection-dyn-associated-type.rs:26:1 + | +LL | } + | ^ expected identifier + +warning: trait objects without an explicit `dyn` are deprecated + --> $DIR/projection-dyn-associated-type.rs:13:28 + | +LL | impl Mirror for A { + | ^ + | + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + = note: for more information, see + = note: `#[warn(bare_trait_objects)]` (part of `#[warn(rust_2021_compatibility)]`) on by default +help: if this is a dyn-compatible trait, use `dyn` + | +LL | impl Mirror for dyn A { + | +++ +help: alternatively use a blanket implementation to implement `Mirror` for all types that also implement `A` + | +LL - impl Mirror for A { +LL + impl Mirror for U { + | + +error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates + --> $DIR/projection-dyn-associated-type.rs:13:6 + | +LL | impl Mirror for A { + | ^ unconstrained type parameter + +error[E0277]: the trait bound `(dyn B + 'static): Mirror` is not satisfied + --> $DIR/projection-dyn-associated-type.rs:22:6 + | +LL | ) -> &'a ::Assoc { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Mirror` is not implemented for `(dyn B + 'static)` + | + = help: the trait `Mirror` is implemented for `dyn A` + +error[E0277]: the trait bound `(dyn B + 'static): Mirror` is not satisfied + --> $DIR/projection-dyn-associated-type.rs:22:6 + | +LL | ) -> &'a ::Assoc { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Mirror` is not implemented for `(dyn B + 'static)` + | + = help: the trait `Mirror` is implemented for `dyn A` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 4 previous errors; 1 warning emitted + +Some errors have detailed explanations: E0207, E0277. +For more information about an error, try `rustc --explain E0207`. From 3d2a8df077dcf6f84a15a76dec511af286435e84 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 2 Oct 2025 13:29:14 -0400 Subject: [PATCH 117/226] Upgrade `toml` to 0.9.7 and remove the `serde` feature. --- Cargo.toml | 7 +- clippy_lints/Cargo.toml | 7 +- .../src/cargo/lint_groups_priority.rs | 190 +++++++++--------- lintcheck/Cargo.toml | 2 +- tests/compile-test.rs | 18 +- 5 files changed, 119 insertions(+), 105 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e06383499893..bedcc300f856 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,13 +38,18 @@ ui_test = "0.30.2" regex = "1.5.5" serde = { version = "1.0.145", features = ["derive"] } serde_json = "1.0.122" -toml = "0.7.3" walkdir = "2.3" filetime = "0.2.9" itertools = "0.12" pulldown-cmark = { version = "0.11", default-features = false, features = ["html"] } askama = { version = "0.14", default-features = false, features = ["alloc", "config", "derive"] } +[dev-dependencies.toml] +version = "0.9.7" +default-features = false +# preserve_order keeps diagnostic output in file order +features = ["parse", "preserve_order"] + [build-dependencies] rustc_tools_util = { path = "rustc_tools_util", version = "0.4.2" } diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 51e59ae20507..42486e182ee3 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -18,12 +18,17 @@ itertools = "0.12" quine-mc_cluskey = "0.2" regex-syntax = "0.8" serde = { version = "1.0", features = ["derive"] } -toml = "0.7.3" unicode-normalization = "0.1" unicode-script = { version = "0.5", default-features = false } semver = "1.0" url = "2.2" +[dependencies.toml] +version = "0.9.7" +default-features = false +# preserve_order keeps diagnostic output in file order +features = ["parse", "preserve_order"] + [dev-dependencies] walkdir = "2.3" diff --git a/clippy_lints/src/cargo/lint_groups_priority.rs b/clippy_lints/src/cargo/lint_groups_priority.rs index ffd6c520c9ae..14c5e22fb9cd 100644 --- a/clippy_lints/src/cargo/lint_groups_priority.rs +++ b/clippy_lints/src/cargo/lint_groups_priority.rs @@ -4,72 +4,10 @@ use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_lint::{LateContext, unerased_lint_store}; use rustc_span::{BytePos, Pos, SourceFile, Span, SyntaxContext}; -use serde::{Deserialize, Serialize}; -use std::collections::BTreeMap; use std::ops::Range; use std::path::Path; use toml::Spanned; - -#[derive(Deserialize, Serialize, Debug)] -struct LintConfigTable { - level: String, - priority: Option, -} - -#[derive(Deserialize, Debug)] -#[serde(untagged)] -enum LintConfig { - Level(String), - Table(LintConfigTable), -} - -impl LintConfig { - fn level(&self) -> &str { - match self { - LintConfig::Level(level) => level, - LintConfig::Table(table) => &table.level, - } - } - - fn priority(&self) -> i64 { - match self { - LintConfig::Level(_) => 0, - LintConfig::Table(table) => table.priority.unwrap_or(0), - } - } - - fn is_implicit(&self) -> bool { - if let LintConfig::Table(table) = self { - table.priority.is_none() - } else { - true - } - } -} - -type LintTable = BTreeMap, Spanned>; - -#[derive(Deserialize, Debug, Default)] -struct Lints { - #[serde(default)] - rust: LintTable, - #[serde(default)] - clippy: LintTable, -} - -#[derive(Deserialize, Debug, Default)] -struct Workspace { - #[serde(default)] - lints: Lints, -} - -#[derive(Deserialize, Debug)] -struct CargoToml { - #[serde(default)] - lints: Lints, - #[serde(default)] - workspace: Workspace, -} +use toml::de::{DeTable, DeValue}; fn toml_span(range: Range, file: &SourceFile) -> Span { Span::new( @@ -80,66 +18,89 @@ fn toml_span(range: Range, file: &SourceFile) -> Span { ) } -fn check_table(cx: &LateContext<'_>, table: LintTable, known_groups: &FxHashSet<&str>, file: &SourceFile) { +struct LintConfig<'a> { + sp: Range, + level: &'a str, + priority: Option, +} +impl<'a> LintConfig<'a> { + fn priority(&self) -> i64 { + self.priority.unwrap_or(0) + } + + fn is_implicit(&self) -> bool { + self.priority.is_none() + } + + fn parse(value: &'a Spanned>) -> Option { + let sp = value.span(); + let (level, priority) = match value.get_ref() { + DeValue::String(level) => (&**level, None), + DeValue::Table(tbl) => { + let level = tbl.get("level")?.get_ref().as_str()?; + let priority = if let Some(priority) = tbl.get("priority") { + let priority = priority.get_ref().as_integer()?; + Some(i64::from_str_radix(priority.as_str(), priority.radix()).ok()?) + } else { + None + }; + (level, priority) + }, + _ => return None, + }; + Some(Self { sp, level, priority }) + } +} + +fn check_table(cx: &LateContext<'_>, table: &DeTable<'_>, known_groups: &FxHashSet<&str>, file: &SourceFile) { let mut lints = Vec::new(); let mut groups = Vec::new(); for (name, config) in table { - if name.get_ref() == "warnings" { - continue; - } - - if known_groups.contains(name.get_ref().as_str()) { - groups.push((name, config)); - } else { - lints.push((name, config.into_inner())); + if name.get_ref() != "warnings" + && let Some(config) = LintConfig::parse(config) + { + if known_groups.contains(&**name.get_ref()) { + groups.push((name, config)); + } else { + lints.push((name, config)); + } } } for (group, group_config) in groups { - let priority = group_config.get_ref().priority(); - let level = group_config.get_ref().level(); - if let Some((conflict, _)) = lints - .iter() - .rfind(|(_, lint_config)| lint_config.priority() == priority && lint_config.level() != level) - { + if let Some((conflict, _)) = lints.iter().rfind(|(_, lint_config)| { + lint_config.priority() == group_config.priority() && lint_config.level != group_config.level + }) { span_lint_and_then( cx, LINT_GROUPS_PRIORITY, toml_span(group.span(), file), format!( - "lint group `{}` has the same priority ({priority}) as a lint", - group.as_ref() + "lint group `{}` has the same priority ({}) as a lint", + group.as_ref(), + group_config.priority(), ), |diag| { - let config_span = toml_span(group_config.span(), file); + let config_span = toml_span(group_config.sp.clone(), file); - if group_config.as_ref().is_implicit() { + if group_config.is_implicit() { diag.span_label(config_span, "has an implicit priority of 0"); } diag.span_label(toml_span(conflict.span(), file), "has the same priority as this lint"); diag.note("the order of the lints in the table is ignored by Cargo"); - let mut suggestion = String::new(); let low_priority = lints .iter() - .map(|(_, config)| config.priority().saturating_sub(1)) + .map(|(_, lint_config)| lint_config.priority().saturating_sub(1)) .min() .unwrap_or(-1); - Serialize::serialize( - &LintConfigTable { - level: level.into(), - priority: Some(low_priority), - }, - toml::ser::ValueSerializer::new(&mut suggestion), - ) - .unwrap(); diag.span_suggestion_verbose( config_span, format!( "to have lints override the group set `{}` to a lower priority", group.as_ref() ), - suggestion, + format!("{{ level = {:?}, priority = {low_priority} }}", group_config.level,), Applicability::MaybeIncorrect, ); }, @@ -148,10 +109,29 @@ fn check_table(cx: &LateContext<'_>, table: LintTable, known_groups: &FxHashSet< } } +struct LintTbls<'a> { + rust: Option<&'a DeTable<'a>>, + clippy: Option<&'a DeTable<'a>>, +} +fn get_lint_tbls<'a>(tbl: &'a DeTable<'a>) -> LintTbls<'a> { + if let Some(lints) = tbl.get("lints") + && let Some(lints) = lints.get_ref().as_table() + { + let rust = lints.get("rust").and_then(|x| x.get_ref().as_table()); + let clippy = lints.get("clippy").and_then(|x| x.get_ref().as_table()); + LintTbls { rust, clippy } + } else { + LintTbls { + rust: None, + clippy: None, + } + } +} + pub fn check(cx: &LateContext<'_>) { if let Ok(file) = cx.tcx.sess.source_map().load_file(Path::new("Cargo.toml")) && let Some(src) = file.src.as_deref() - && let Ok(cargo_toml) = toml::from_str::(src) + && let Ok(cargo_toml) = DeTable::parse(src) { let mut rustc_groups = FxHashSet::default(); let mut clippy_groups = FxHashSet::default(); @@ -167,9 +147,23 @@ pub fn check(cx: &LateContext<'_>) { } } - check_table(cx, cargo_toml.lints.rust, &rustc_groups, &file); - check_table(cx, cargo_toml.lints.clippy, &clippy_groups, &file); - check_table(cx, cargo_toml.workspace.lints.rust, &rustc_groups, &file); - check_table(cx, cargo_toml.workspace.lints.clippy, &clippy_groups, &file); + let lints = get_lint_tbls(cargo_toml.get_ref()); + if let Some(lints) = lints.rust { + check_table(cx, lints, &rustc_groups, &file); + } + if let Some(lints) = lints.clippy { + check_table(cx, lints, &clippy_groups, &file); + } + if let Some(tbl) = cargo_toml.get_ref().get("workspace") + && let Some(tbl) = tbl.get_ref().as_table() + { + let lints = get_lint_tbls(tbl); + if let Some(lints) = lints.rust { + check_table(cx, lints, &rustc_groups, &file); + } + if let Some(lints) = lints.clippy { + check_table(cx, lints, &clippy_groups, &file); + } + } } } diff --git a/lintcheck/Cargo.toml b/lintcheck/Cargo.toml index 55e588f5ec73..0d0b80c309dd 100644 --- a/lintcheck/Cargo.toml +++ b/lintcheck/Cargo.toml @@ -22,6 +22,6 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.85" strip-ansi-escapes = "0.2.0" tar = "0.4" -toml = "0.7.3" +toml = "0.9.7" ureq = { version = "2.2", features = ["json"] } walkdir = "2.3" diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 6b6dfd7b81ea..71cd8a6c03cc 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -405,11 +405,18 @@ fn ui_cargo_toml_metadata() { continue; } - let toml = fs::read_to_string(path).unwrap().parse::().unwrap(); + let toml = fs::read_to_string(path).unwrap(); + let toml = toml::de::DeTable::parse(&toml).unwrap(); - let package = toml.as_table().unwrap().get("package").unwrap().as_table().unwrap(); + let package = toml.get_ref().get("package").unwrap().get_ref().as_table().unwrap(); - let name = package.get("name").unwrap().as_str().unwrap().replace('-', "_"); + let name = package + .get("name") + .unwrap() + .as_ref() + .as_str() + .unwrap() + .replace('-', "_"); assert!( path.parent() .unwrap() @@ -421,7 +428,10 @@ fn ui_cargo_toml_metadata() { path.display(), ); - let publish = package.get("publish").and_then(toml::Value::as_bool).unwrap_or(true); + let publish = package + .get("publish") + .and_then(|x| x.get_ref().as_bool()) + .unwrap_or(true); assert!( !publish || publish_exceptions.contains(&path.parent().unwrap().to_path_buf()), "`{}` lacks `publish = false`", From c407d340de1717a88537725c899c153c6fdda051 Mon Sep 17 00:00:00 2001 From: binarycat Date: Tue, 26 Aug 2025 13:41:33 -0500 Subject: [PATCH 118/226] if a trait item is shown in search results, hide the impl item for example, if we're showing `Iterator::next`, we don't need to also show `Range::next` in the results. Co-authored-by: Michael Howell --- src/librustdoc/formats/cache.rs | 31 ++++--- src/librustdoc/html/render/mod.rs | 2 + src/librustdoc/html/render/search_index.rs | 78 ++++++++++------ src/librustdoc/html/static/js/rustdoc.d.ts | 22 ++++- src/librustdoc/html/static/js/search.js | 102 +++++++++++++++++---- 5 files changed, 174 insertions(+), 61 deletions(-) diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index 29b4c4caaf86..75c848742ea5 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -148,6 +148,14 @@ impl Cache { Cache { document_private, document_hidden, ..Cache::default() } } + fn parent_stack_last_impl_and_trait_id(&self) -> (Option, Option) { + if let Some(ParentStackItem::Impl { item_id, trait_, .. }) = self.parent_stack.last() { + (item_id.as_def_id(), trait_.as_ref().map(|tr| tr.def_id())) + } else { + (None, None) + } + } + /// Populates the `Cache` with more data. The returned `Crate` will be missing some data that was /// in `krate` due to the data being moved into the `Cache`. pub(crate) fn populate(cx: &mut DocContext<'_>, mut krate: clean::Crate) -> clean::Crate { @@ -574,11 +582,7 @@ fn add_item_to_search_index(tcx: TyCtxt<'_>, cache: &mut Cache, item: &clean::It clean::ItemKind::ImportItem(import) => import.source.did.unwrap_or(item_def_id), _ => item_def_id, }; - let impl_id = if let Some(ParentStackItem::Impl { item_id, .. }) = cache.parent_stack.last() { - item_id.as_def_id() - } else { - None - }; + let (impl_id, trait_parent) = cache.parent_stack_last_impl_and_trait_id(); let search_type = get_function_type_for_search( item, tcx, @@ -596,12 +600,15 @@ fn add_item_to_search_index(tcx: TyCtxt<'_>, cache: &mut Cache, item: &clean::It desc, parent: parent_did, parent_idx: None, + trait_parent, + trait_parent_idx: None, exact_module_path: None, impl_id, search_type, aliases, deprecation, }; + cache.search_index.push(index_item); } @@ -610,19 +617,21 @@ fn add_item_to_search_index(tcx: TyCtxt<'_>, cache: &mut Cache, item: &clean::It /// See [`Cache::orphan_impl_items`]. fn handle_orphan_impl_child(cache: &mut Cache, item: &clean::Item, parent_did: DefId) { let impl_generics = clean_impl_generics(cache.parent_stack.last()); - let impl_id = if let Some(ParentStackItem::Impl { item_id, .. }) = cache.parent_stack.last() { - item_id.as_def_id() - } else { - None + let (impl_id, trait_parent) = cache.parent_stack_last_impl_and_trait_id(); + let orphan_item = OrphanImplItem { + parent: parent_did, + trait_parent, + item: item.clone(), + impl_generics, + impl_id, }; - let orphan_item = - OrphanImplItem { parent: parent_did, item: item.clone(), impl_generics, impl_id }; cache.orphan_impl_items.push(orphan_item); } pub(crate) struct OrphanImplItem { pub(crate) parent: DefId, pub(crate) impl_id: Option, + pub(crate) trait_parent: Option, pub(crate) item: clean::Item, pub(crate) impl_generics: Option<(clean::Type, clean::Generics)>, } diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 6d684449b6d2..88184c9611ff 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -134,6 +134,8 @@ pub(crate) struct IndexItem { pub(crate) desc: String, pub(crate) parent: Option, pub(crate) parent_idx: Option, + pub(crate) trait_parent: Option, + pub(crate) trait_parent_idx: Option, pub(crate) exact_module_path: Option>, pub(crate) impl_id: Option, pub(crate) search_type: Option, diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index 2984f3ab50ee..81b38d157a86 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -580,6 +580,7 @@ impl SerializedSearchIndex { module_path, exact_module_path, parent, + trait_parent, deprecated, associated_item_disambiguator, }| EntryData { @@ -589,6 +590,7 @@ impl SerializedSearchIndex { exact_module_path: exact_module_path .and_then(|path_id| map.get(&path_id).copied()), parent: parent.and_then(|path_id| map.get(&path_id).copied()), + trait_parent: trait_parent.and_then(|path_id| map.get(&path_id).copied()), deprecated: *deprecated, associated_item_disambiguator: associated_item_disambiguator.clone(), }, @@ -870,6 +872,7 @@ struct EntryData { module_path: Option, exact_module_path: Option, parent: Option, + trait_parent: Option, deprecated: bool, associated_item_disambiguator: Option, } @@ -885,6 +888,7 @@ impl Serialize for EntryData { seq.serialize_element(&self.module_path.map(|id| id + 1).unwrap_or(0))?; seq.serialize_element(&self.exact_module_path.map(|id| id + 1).unwrap_or(0))?; seq.serialize_element(&self.parent.map(|id| id + 1).unwrap_or(0))?; + seq.serialize_element(&self.trait_parent.map(|id| id + 1).unwrap_or(0))?; seq.serialize_element(&if self.deprecated { 1 } else { 0 })?; if let Some(disambig) = &self.associated_item_disambiguator { seq.serialize_element(&disambig)?; @@ -916,6 +920,9 @@ impl<'de> Deserialize<'de> for EntryData { .ok_or_else(|| A::Error::missing_field("exact_module_path"))?; let parent: SerializedOptional32 = v.next_element()?.ok_or_else(|| A::Error::missing_field("parent"))?; + let trait_parent: SerializedOptional32 = + v.next_element()?.ok_or_else(|| A::Error::missing_field("trait_parent"))?; + let deprecated: u32 = v.next_element()?.unwrap_or(0); let associated_item_disambiguator: Option = v.next_element()?; Ok(EntryData { @@ -925,6 +932,7 @@ impl<'de> Deserialize<'de> for EntryData { exact_module_path: Option::::from(exact_module_path) .map(|path| path as usize), parent: Option::::from(parent).map(|path| path as usize), + trait_parent: Option::::from(trait_parent).map(|path| path as usize), deprecated: deprecated != 0, associated_item_disambiguator, }) @@ -1275,7 +1283,8 @@ pub(crate) fn build_index( // Attach all orphan items to the type's definition if the type // has since been learned. - for &OrphanImplItem { impl_id, parent, ref item, ref impl_generics } in &cache.orphan_impl_items + for &OrphanImplItem { impl_id, parent, trait_parent, ref item, ref impl_generics } in + &cache.orphan_impl_items { if let Some((fqp, _)) = cache.paths.get(&parent) { let desc = short_markdown_summary(&item.doc_value(), &item.link_names(cache)); @@ -1287,6 +1296,8 @@ pub(crate) fn build_index( desc, parent: Some(parent), parent_idx: None, + trait_parent, + trait_parent_idx: None, exact_module_path: None, impl_id, search_type: get_function_type_for_search( @@ -1391,6 +1402,7 @@ pub(crate) fn build_index( module_path: None, exact_module_path: None, parent: None, + trait_parent: None, deprecated: false, associated_item_disambiguator: None, }), @@ -1404,39 +1416,46 @@ pub(crate) fn build_index( } }; - // First, populate associated item parents + // First, populate associated item parents and trait parents let crate_items: Vec<&mut IndexItem> = search_index .iter_mut() .map(|item| { - item.parent_idx = item.parent.and_then(|defid| { - cache.paths.get(&defid).map(|&(ref fqp, ty)| { - let pathid = serialized_index.names.len(); - match serialized_index.crate_paths_index.entry((ty, fqp.clone())) { - Entry::Occupied(entry) => *entry.get(), - Entry::Vacant(entry) => { - entry.insert(pathid); - let (name, path) = fqp.split_last().unwrap(); - serialized_index.push_path( - name.as_str().to_string(), - PathData { - ty, - module_path: path.to_vec(), - exact_module_path: if let Some(exact_path) = - cache.exact_paths.get(&defid) - && let Some((name2, exact_path)) = exact_path.split_last() - && name == name2 - { - Some(exact_path.to_vec()) - } else { - None + let mut defid_to_rowid = |defid, check_external: bool| { + cache + .paths + .get(&defid) + .or_else(|| check_external.then(|| cache.external_paths.get(&defid)).flatten()) + .map(|&(ref fqp, ty)| { + let pathid = serialized_index.names.len(); + match serialized_index.crate_paths_index.entry((ty, fqp.clone())) { + Entry::Occupied(entry) => *entry.get(), + Entry::Vacant(entry) => { + entry.insert(pathid); + let (name, path) = fqp.split_last().unwrap(); + serialized_index.push_path( + name.as_str().to_string(), + PathData { + ty, + module_path: path.to_vec(), + exact_module_path: if let Some(exact_path) = + cache.exact_paths.get(&defid) + && let Some((name2, exact_path)) = + exact_path.split_last() + && name == name2 + { + Some(exact_path.to_vec()) + } else { + None + }, }, - }, - ); - usize::try_from(pathid).unwrap() + ); + usize::try_from(pathid).unwrap() + } } - } - }) - }); + }) + }; + item.parent_idx = item.parent.and_then(|p| defid_to_rowid(p, false)); + item.trait_parent_idx = item.trait_parent.and_then(|p| defid_to_rowid(p, true)); if let Some(defid) = item.defid && item.parent_idx.is_none() @@ -1519,6 +1538,7 @@ pub(crate) fn build_index( Some(EntryData { ty: item.ty, parent: item.parent_idx, + trait_parent: item.trait_parent_idx, module_path, exact_module_path, deprecated: item.deprecation.is_some(), diff --git a/src/librustdoc/html/static/js/rustdoc.d.ts b/src/librustdoc/html/static/js/rustdoc.d.ts index 951eb2291b89..e206d6633e63 100644 --- a/src/librustdoc/html/static/js/rustdoc.d.ts +++ b/src/librustdoc/html/static/js/rustdoc.d.ts @@ -241,6 +241,7 @@ declare namespace rustdoc { modulePath: number?, exactModulePath: number?, parent: number?, + traitParent: number?, deprecated: boolean, associatedItemDisambiguator: string?, } @@ -291,9 +292,12 @@ declare namespace rustdoc { path: PathData?, functionData: FunctionData?, deprecated: boolean, - parent: { path: PathData, name: string}?, + parent: RowParent, + traitParent: RowParent, } + type RowParent = { path: PathData, name: string } | null; + type ItemType = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26; @@ -316,7 +320,23 @@ declare namespace rustdoc { interface ResultObject { desc: Promise, displayPath: string, + /** + * path to where the item was defined (not inlined), + * then `|`, then the `ItemType` of the item. + * + * This is often a private path, so it should not be displayed, + * but this allows us to use it to reliably deduplicate reexported and inlined items + */ fullPath: string, + /** + * The `fullPath` of the corresponding item within a trait. + * For example, for `File::read`, this would be `std::io::Read::read|12` + * + * This is used to hide items from trait impls when the trait itself is in the search results. + * + * `null` if the item is not from a trait impl block. + */ + traitPath: string | null, href: string, id: number, dist: number, diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 482134933a61..71bad967026e 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -1598,6 +1598,7 @@ class DocSearch { * module_path, * exact_module_path, * parent, + * trait_parent, * deprecated, * associated_item_disambiguator * @type {rustdoc.ArrayWithOptionals<[ @@ -1607,6 +1608,7 @@ class DocSearch { * number, * number, * number, + * number, * ], [string]>} */ const raw = JSON.parse(encoded); @@ -1616,8 +1618,9 @@ class DocSearch { modulePath: raw[2] === 0 ? null : raw[2] - 1, exactModulePath: raw[3] === 0 ? null : raw[3] - 1, parent: raw[4] === 0 ? null : raw[4] - 1, - deprecated: raw[5] === 1 ? true : false, - associatedItemDisambiguator: raw.length === 6 ? null : raw[6], + traitParent: raw[5] === 0 ? null : raw[5] - 1, + deprecated: raw[6] === 1 ? true : false, + associatedItemDisambiguator: raw.length === 7 ? null : raw[7], }; } @@ -1853,14 +1856,25 @@ class DocSearch { if (!entry && !path) { return null; } + /** @type {function("parent" | "traitParent"): Promise} */ + const buildParentLike = async field => { + const [name, path] = entry !== null && entry[field] !== null ? + await Promise.all([this.getName(entry[field]), this.getPathData(entry[field])]) : + [null, null]; + if (name !== null && path !== null) { + return { name, path }; + } + return null; + }; + const [ moduleName, modulePathData, exactModuleName, exactModulePathData, - parentName, - parentPath, - crate, + parent, + traitParent, + crateOrNull, ] = await Promise.all([ entry && entry.modulePath !== null ? this.getName(entry.modulePath) : null, entry && entry.modulePath !== null ? this.getPathData(entry.modulePath) : null, @@ -1870,14 +1884,11 @@ class DocSearch { entry && entry.exactModulePath !== null ? this.getPathData(entry.exactModulePath) : null, - entry && entry.parent !== null ? - this.getName(entry.parent) : - null, - entry && entry.parent !== null ? - this.getPathData(entry.parent) : - null, - entry ? nonnull(await this.getName(entry.krate)) : "", + buildParentLike("parent"), + buildParentLike("traitParent"), + entry ? this.getName(entry.krate) : "", ]); + const crate = crateOrNull === null ? "" : crateOrNull; const name = name_ === null ? "" : name_; const normalizedName = (name.indexOf("_") === -1 ? name : @@ -1886,6 +1897,7 @@ class DocSearch { (modulePathData.modulePath === "" ? moduleName : `${modulePathData.modulePath}::${moduleName}`); + return { id, crate, @@ -1901,9 +1913,8 @@ class DocSearch { path, functionData, deprecated: entry ? entry.deprecated : false, - parent: parentName !== null && parentPath !== null ? - { name: parentName, path: parentPath } : - null, + parent, + traitParent, }; } @@ -2101,11 +2112,12 @@ class DocSearch { /** * @param {rustdoc.Row} item - * @returns {[string, string, string]} + * @returns {[string, string, string, string|null]} */ const buildHrefAndPath = item => { let displayPath; let href; + let traitPath = null; const type = itemTypes[item.ty]; const name = item.name; let path = item.modulePath; @@ -2163,7 +2175,11 @@ class DocSearch { href = this.rootPath + item.modulePath.replace(/::/g, "/") + "/" + type + "." + name + ".html"; } - return [displayPath, href, `${exactPath}::${name}`]; + if (item.traitParent) { + const tparent = item.traitParent; + traitPath = `${tparent.path.exactModulePath}::${tparent.name}::${name}`; + } + return [displayPath, href, `${exactPath}::${name}`, traitPath]; }; /** @@ -2598,7 +2614,13 @@ class DocSearch { * @returns {rustdoc.ResultObject[]} */ const transformResults = (results, typeInfo, duplicates) => { - const out = []; + /** @type {rustdoc.ResultObject[]} */ + let out = []; + + // if we match a trait-associated item, we want to go back and + // remove all the items that are their equivalent but in an impl block. + /** @type {Map} */ + const traitImplIdxMap = new Map(); for (const result of results) { const item = result.item; @@ -2630,17 +2652,35 @@ class DocSearch { item, displayPath: pathSplitter(res[0]), fullPath: "", + traitPath: null, href: "", displayTypeSignature: null, }, result); + // unlike other items, methods have a different ty when they are + // in an impl block vs a trait. want to normalize this away. + let ty = obj.item.ty; + if (ty === TY_TYMETHOD) { + ty = TY_METHOD; + } // To be sure than it some items aren't considered as duplicate. - obj.fullPath = res[2] + "|" + obj.item.ty; + obj.fullPath = res[2] + "|" + ty; + if (res[3]) { + // "tymethod" is never used on impl blocks + // (this is the reason we need to normalize tymethod away). + obj.traitPath = res[3] + "|" + obj.item.ty; + } if (duplicates.has(obj.fullPath)) { continue; } + // If we're showing something like `Iterator::next`, + // we don't want to also show a bunch of `::next` + if (obj.traitPath && duplicates.has(obj.traitPath)) { + continue; + } + // Exports are specifically not shown if the items they point at // are already in the results. if (obj.item.ty === TY_IMPORT && duplicates.has(res[2])) { @@ -2661,14 +2701,36 @@ class DocSearch { ); } + // FIXME: if the trait item matches but is cut off due to MAX_RESULTS, + // this deduplication will not happen. obj.href = res[1]; + if (obj.traitPath) { + let list = traitImplIdxMap.get(obj.traitPath); + if (list === undefined) { + list = []; + } + list.push(out.length); + traitImplIdxMap.set(obj.traitPath, list); + } else { + // FIXME: this is `O(n*m)` because we're repeatedly + // shifting with Array.splice, but could be `O(n+m)` if + // we did the shifting manually in a more clever way. + const toRemoveList = traitImplIdxMap.get(obj.fullPath); + if (toRemoveList) { + // iterate in reverse order so we don't shift the indexes + for (let i = toRemoveList.length - 1; i >= 0; i--) { + const rmIdx = toRemoveList[i]; + out = out.splice(rmIdx, 1); + } + } + traitImplIdxMap.delete(obj.fullPath); + } out.push(obj); if (out.length >= MAX_RESULTS) { break; } } } - return out; }; From 2dd00d3391b82dd0928f50589eb37f6fbbe32980 Mon Sep 17 00:00:00 2001 From: binarycat Date: Tue, 2 Sep 2025 11:41:21 -0500 Subject: [PATCH 119/226] fix rustdoc tests broke by trait item filtering --- tests/rustdoc-gui/search-result-color.goml | 45 +++++++++++-------- tests/rustdoc-gui/search-tab.goml | 2 +- tests/rustdoc-gui/sidebar.goml | 2 + tests/rustdoc-js-std/asrawfd.js | 4 +- tests/rustdoc-js-std/quoted.js | 18 ++++---- .../{bufread-fill-buf.js => trait-unbox.js} | 6 +-- 6 files changed, 42 insertions(+), 35 deletions(-) rename tests/rustdoc-js-std/{bufread-fill-buf.js => trait-unbox.js} (58%) diff --git a/tests/rustdoc-gui/search-result-color.goml b/tests/rustdoc-gui/search-result-color.goml index fe0f64010895..e5c11651bd27 100644 --- a/tests/rustdoc-gui/search-result-color.goml +++ b/tests/rustdoc-gui/search-result-color.goml @@ -5,7 +5,7 @@ include: "utils.goml" define-function: ( "check-search-color", [ - theme, count_color, desc_color, path_color, bottom_border_color, keyword_color, + theme, count_color, path_color, bottom_border_color, keyword_color, struct_color, associatedtype_color, tymethod_color, method_color, structfield_color, structfield_hover_color, macro_color, fn_color, hover_path_color, hover_background, attribute_color, grey @@ -21,10 +21,6 @@ define-function: ( {"color": |count_color|}, ALL, ) - assert-css: ( - "//*[@class='desc'][normalize-space()='Just a normal struct.']", - {"color": |desc_color|}, - ) assert-css: ( "//*[@class='result-name']//*[normalize-space()='test_docs::']", {"color": |path_color|}, @@ -97,16 +93,6 @@ define-function: ( ALL, ) - // Checking color and background on hover. - move-cursor-to: "//*[@class='desc'][normalize-space()='Just a normal struct.']" - assert-css: ( - "//*[@class='result-name']//*[normalize-space()='test_docs::']", - {"color": |hover_path_color|}, - ) - assert-css: ( - "//*[@class='result-name']//*[normalize-space()='test_docs::']/ancestor::a", - {"color": |hover_path_color|, "background-color": |hover_background|}, - ) } ) @@ -157,7 +143,6 @@ show-text: true call-function: ("check-search-color", { "theme": "ayu", "count_color": "#888", - "desc_color": "#c5c5c5", "path_color": "#0096cf", "bottom_border_color": "#aaa3", "keyword_color": "#39afd7", @@ -179,7 +164,6 @@ call-function: ("check-search-color", { call-function: ("check-search-color", { "theme": "dark", "count_color": "#888", - "desc_color": "#ddd", "path_color": "#ddd", "bottom_border_color": "#aaa3", "keyword_color": "#d2991d", @@ -201,7 +185,6 @@ call-function: ("check-search-color", { call-function: ("check-search-color", { "theme": "light", "count_color": "#888", - "desc_color": "#000", "path_color": "#000", "bottom_border_color": "#aaa3", "keyword_color": "#3873ad", @@ -226,12 +209,27 @@ call-function: ("perform-search", {"query": "thisisanalias"}) define-function: ( "check-alias", - [theme, alias, grey], + [theme, alias, grey, desc_color, hover_path_color, hover_background], block { call-function: ("switch-theme", {"theme": |theme|}) // Checking that the colors for the alias element are the ones expected. assert-css: (".result-name .path .alias", {"color": |alias|}) assert-css: (".result-name .path .alias > .grey", {"color": |grey|}) + assert-css: ( + "//*[@class='desc'][normalize-space()='Just a normal enum.']", + {"color": |desc_color|}, + ) + // Checking color and background on hover. + move-cursor-to: "//*[@class='desc'][normalize-space()='Just a normal enum.']" + assert-css: ( + "//*[@class='result-name']//*[normalize-space()='test_docs::']", + {"color": |hover_path_color|}, + ) + assert-css: ( + "//*[@class='result-name']//*[normalize-space()='test_docs::']/ancestor::a", + {"color": |hover_path_color|, "background-color": |hover_background|}, + ) + }, ) @@ -239,14 +237,23 @@ call-function: ("check-alias", { "theme": "ayu", "alias": "#c5c5c5", "grey": "#999", + "desc_color": "#c5c5c5", + "hover_path_color": "#fff", + "hover_background": "#3c3c3c", }) call-function: ("check-alias", { "theme": "dark", "alias": "#fff", "grey": "#ccc", + "desc_color": "#ddd", + "hover_path_color": "#ddd", + "hover_background": "#616161", }) call-function: ("check-alias", { "theme": "light", "alias": "#000", "grey": "#999", + "desc_color": "#000", + "hover_path_color": "#000", + "hover_background": "#ccc", }) diff --git a/tests/rustdoc-gui/search-tab.goml b/tests/rustdoc-gui/search-tab.goml index 00ca952033dc..0a3cfc231e50 100644 --- a/tests/rustdoc-gui/search-tab.goml +++ b/tests/rustdoc-gui/search-tab.goml @@ -79,7 +79,7 @@ call-function: ("check-colors", { set-window-size: (851, 600) // Check the size and count in tabs -assert-text: ("#search-tabs > button:nth-child(1) > .count", " (27) ") +assert-text: ("#search-tabs > button:nth-child(1) > .count", " (25) ") assert-text: ("#search-tabs > button:nth-child(2) > .count", " (7)  ") assert-text: ("#search-tabs > button:nth-child(3) > .count", " (0)  ") store-property: ("#search-tabs > button:nth-child(1)", {"offsetWidth": buttonWidth}) diff --git a/tests/rustdoc-gui/sidebar.goml b/tests/rustdoc-gui/sidebar.goml index 5ec0008ad8af..0d371c8c6a46 100644 --- a/tests/rustdoc-gui/sidebar.goml +++ b/tests/rustdoc-gui/sidebar.goml @@ -1,5 +1,7 @@ // Checks multiple things on the sidebar display (width of its elements, colors, etc). include: "utils.goml" +// Disable animations so they don't mess up color assertions later. +emulate-media-features: { "prefers-reduced-motion": "reduce" } go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" assert-property: (".sidebar", {"clientWidth": "199"}) show-text: true diff --git a/tests/rustdoc-js-std/asrawfd.js b/tests/rustdoc-js-std/asrawfd.js index 5dbc4ba95d9a..da08eeb8a53c 100644 --- a/tests/rustdoc-js-std/asrawfd.js +++ b/tests/rustdoc-js-std/asrawfd.js @@ -1,12 +1,10 @@ // ignore-order const EXPECTED = { - 'query': 'RawFd::as_raw_fd', + 'query': 'method:RawFd::as_raw_fd', 'others': [ // Reproduction test for https://github.com/rust-lang/rust/issues/78724 // Validate that type alias methods get the correct path. - { 'path': 'std::os::fd::AsRawFd', 'name': 'as_raw_fd' }, - { 'path': 'std::os::fd::AsRawFd', 'name': 'as_raw_fd' }, { 'path': 'std::os::fd::RawFd', 'name': 'as_raw_fd' }, ], }; diff --git a/tests/rustdoc-js-std/quoted.js b/tests/rustdoc-js-std/quoted.js index 8a9275019255..a8ca6521208e 100644 --- a/tests/rustdoc-js-std/quoted.js +++ b/tests/rustdoc-js-std/quoted.js @@ -1,21 +1,21 @@ +// make sure quoted search works both for items and and without generics // ignore-order const FILTER_CRATE = 'std'; const EXPECTED = { - 'query': '"error"', + 'query': '"result"', 'others': [ - { 'path': 'std', 'name': 'error' }, - { 'path': 'std::fmt', 'name': 'Error' }, - { 'path': 'std::io', 'name': 'Error' }, + { 'path': 'std', 'name': 'result' }, + { 'path': 'std::result', 'name': 'Result' }, + { 'path': 'std::fmt', 'name': 'Result' }, ], 'in_args': [ - { 'path': 'std::fmt::Error', 'name': 'eq' }, - { 'path': 'std::fmt::Error', 'name': 'cmp' }, - { 'path': 'std::fmt::Error', 'name': 'partial_cmp' }, - + { 'path': 'std::result::Result', 'name': 'branch' }, + { 'path': 'std::result::Result', 'name': 'ok' }, + { 'path': 'std::result::Result', 'name': 'unwrap' }, ], 'returned': [ - { 'path': 'std::fmt::LowerExp', 'name': 'fmt' }, + { 'path': 'std::bool', 'name': 'try_into' }, ], }; diff --git a/tests/rustdoc-js-std/bufread-fill-buf.js b/tests/rustdoc-js-std/trait-unbox.js similarity index 58% rename from tests/rustdoc-js-std/bufread-fill-buf.js rename to tests/rustdoc-js-std/trait-unbox.js index 6b9309f68640..44ddc0c1e75c 100644 --- a/tests/rustdoc-js-std/bufread-fill-buf.js +++ b/tests/rustdoc-js-std/trait-unbox.js @@ -1,10 +1,10 @@ -// ignore-order +// make sure type-based searches with traits get unboxed too const EXPECTED = [ { - 'query': 'bufread -> result<[u8]>', + 'query': 'any -> result', 'others': [ - { 'path': 'std::boxed::Box', 'name': 'fill_buf' }, + { 'path': 'std::boxed::Box', 'name': 'downcast' }, ], }, { From a145dfff03d562584a4a00552fb5df67cdd0d2b6 Mon Sep 17 00:00:00 2001 From: binarycat Date: Sun, 21 Sep 2025 16:10:32 -0500 Subject: [PATCH 120/226] search.js: introduce optimized removeIdxListAsc routine --- src/librustdoc/html/static/js/search.js | 39 +++++++++++++++++++------ 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 71bad967026e..9a6d4c710ff5 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -1076,6 +1076,34 @@ function isPathSeparator(c) { return c === ":" || c === " "; } +/** + * Given an array and an ascending list of indices, + * efficiently removes each index in the array. + * + * @template T + * @param {Array} a + * @param {Array} idxList + */ +function removeIdxListAsc(a, idxList) { + if (idxList.length === 0) { + return; + } + let removed = 0; + let i = idxList[0]; + let nextToRemove = idxList[0]; + while (i < a.length - idxList.length) { + while (i === nextToRemove && removed < idxList.length) { + removed++; + i++; + nextToRemove = idxList[removed]; + } + a[i] = a[i + removed]; + i++; + } + // truncate array + a.length -= idxList.length; +} + /** * @template T */ @@ -2615,7 +2643,7 @@ class DocSearch { */ const transformResults = (results, typeInfo, duplicates) => { /** @type {rustdoc.ResultObject[]} */ - let out = []; + const out = []; // if we match a trait-associated item, we want to go back and // remove all the items that are their equivalent but in an impl block. @@ -2712,16 +2740,9 @@ class DocSearch { list.push(out.length); traitImplIdxMap.set(obj.traitPath, list); } else { - // FIXME: this is `O(n*m)` because we're repeatedly - // shifting with Array.splice, but could be `O(n+m)` if - // we did the shifting manually in a more clever way. const toRemoveList = traitImplIdxMap.get(obj.fullPath); if (toRemoveList) { - // iterate in reverse order so we don't shift the indexes - for (let i = toRemoveList.length - 1; i >= 0; i--) { - const rmIdx = toRemoveList[i]; - out = out.splice(rmIdx, 1); - } + removeIdxListAsc(out, toRemoveList); } traitImplIdxMap.delete(obj.fullPath); } From d0dc603c3992cc4903bb77f949b0b9bebf1ea7a8 Mon Sep 17 00:00:00 2001 From: binarycat Date: Sun, 21 Sep 2025 16:42:12 -0500 Subject: [PATCH 121/226] rustdoc: add testcase for traitParent deduplication --- tests/rustdoc-js/trait-methods.js | 20 ++++++++++++++++++++ tests/rustdoc-js/trait-methods.rs | 18 ++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/tests/rustdoc-js/trait-methods.js b/tests/rustdoc-js/trait-methods.js index dafad5e43784..083e52439f4a 100644 --- a/tests/rustdoc-js/trait-methods.js +++ b/tests/rustdoc-js/trait-methods.js @@ -9,4 +9,24 @@ const EXPECTED = [ { 'path': 'trait_methods::MyTrait', 'name': 'next' }, ], }, + // the traitParent deduplication pass should remove + // Empty::next, as it would be redundant + { + 'query': 'next', + 'correction': null, + 'in_args': [], + 'others': [ + { 'path': 'trait_methods::MyTrait', 'name': 'next' }, + ], + }, + // if the trait does not match, no deduplication happens + { + 'query': '-> option<()>', + 'correction': null, + 'in_args': [], + 'others': [ + { 'path': 'trait_methods::Empty', 'name': 'next' }, + { 'path': 'trait_methods::Void', 'name': 'next' }, + ], + }, ]; diff --git a/tests/rustdoc-js/trait-methods.rs b/tests/rustdoc-js/trait-methods.rs index c88f5edfd55b..a741b361a339 100644 --- a/tests/rustdoc-js/trait-methods.rs +++ b/tests/rustdoc-js/trait-methods.rs @@ -2,3 +2,21 @@ pub trait MyTrait { type Item; fn next(&mut self) -> Option; } + +pub struct Empty; + +impl MyTrait for Empty { + type Item = (); + fn next(&mut self) -> Option<()> { + None + } +} + +pub struct Void; + +impl MyTrait for Void { + type Item = (); + fn next(&mut self) -> Option<()> { + Some(()) + } +} From c7d6857f750a8c957ebca81340faa379cc3d110f Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Thu, 2 Oct 2025 10:19:42 -0700 Subject: [PATCH 122/226] rustdoc: use same stage for all pr check docs --- src/ci/docker/host-x86_64/pr-check-2/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ci/docker/host-x86_64/pr-check-2/Dockerfile b/src/ci/docker/host-x86_64/pr-check-2/Dockerfile index d6470e4deb8d..0a95f428f5bd 100644 --- a/src/ci/docker/host-x86_64/pr-check-2/Dockerfile +++ b/src/ci/docker/host-x86_64/pr-check-2/Dockerfile @@ -32,7 +32,7 @@ ENV SCRIPT \ python3 ../x.py clippy ci --stage 2 && \ python3 ../x.py test --stage 1 core alloc std test proc_macro && \ python3 ../x.py test --stage 1 src/tools/compiletest && \ - python3 ../x.py doc bootstrap && \ + python3 ../x.py doc bootstrap --stage 1 && \ # Build both public and internal documentation. RUSTDOCFLAGS=\"--document-private-items --document-hidden-items\" python3 ../x.py doc compiler --stage 1 && \ RUSTDOCFLAGS=\"--document-private-items --document-hidden-items\" python3 ../x.py doc library --stage 1 && \ From 99550fbc3ea172a3cf90d2776a217e1ba19a196f Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Thu, 2 Oct 2025 14:36:57 -0700 Subject: [PATCH 123/226] Return to needs-llvm-components being info-only Partially revert a535042e80a38196a58c27a8c95552546affe5dc Even with non-LLVM codegen backends, we want to allow for annotations that express dependencies to LLVM-specific parts of the test suite. This includes `//@ needs-llvm-components`, which just allows checking that LLVM is built with relevant target support before the test is run. It does not assert the test cannot work with another codegen backend. --- src/tools/compiletest/src/directives/needs.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/tools/compiletest/src/directives/needs.rs b/src/tools/compiletest/src/directives/needs.rs index c8a729d8aab6..9d72492e5b07 100644 --- a/src/tools/compiletest/src/directives/needs.rs +++ b/src/tools/compiletest/src/directives/needs.rs @@ -281,10 +281,7 @@ pub(super) fn handle_needs( // Handled elsewhere. if name == "needs-llvm-components" { - if config.default_codegen_backend.is_llvm() { - return IgnoreDecision::Continue; - } - return IgnoreDecision::Ignore { reason: "LLVM specific test".into() }; + return IgnoreDecision::Continue; } let mut found_valid = false; From a5c9030271d48456ffefca83a416f0ee863ba965 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Thu, 2 Oct 2025 20:54:47 +0000 Subject: [PATCH 124/226] Extract common logic for iterating over features Two places doing the same thing is enough to motivate me to extract this to a method :) --- compiler/rustc_ast_passes/src/feature_gate.rs | 6 +----- compiler/rustc_feature/src/unstable.rs | 10 ++++++++++ compiler/rustc_lint/src/builtin.rs | 8 ++------ 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 608ccfefeb69..b8a29a9a08f4 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -622,11 +622,7 @@ fn maybe_stage_features(sess: &Session, features: &Features, krate: &ast::Crate) } fn check_incompatible_features(sess: &Session, features: &Features) { - let enabled_lang_features = - features.enabled_lang_features().iter().map(|feat| (feat.gate_name, feat.attr_sp)); - let enabled_lib_features = - features.enabled_lib_features().iter().map(|feat| (feat.gate_name, feat.attr_sp)); - let enabled_features = enabled_lang_features.chain(enabled_lib_features); + let enabled_features = features.enabled_features_iter_stable_order(); for (f1, f2) in rustc_feature::INCOMPATIBLE_FEATURES .iter() diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index e63f29a9570d..8397cd294e0a 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -93,6 +93,16 @@ impl Features { &self.enabled_features } + /// Returns a iterator of enabled features in stable order. + pub fn enabled_features_iter_stable_order( + &self, + ) -> impl Iterator + Clone { + self.enabled_lang_features + .iter() + .map(|feat| (feat.gate_name, feat.attr_sp)) + .chain(self.enabled_lib_features.iter().map(|feat| (feat.gate_name, feat.attr_sp))) + } + /// Is the given feature enabled (via `#[feature(...)]`)? pub fn enabled(&self, feature: Symbol) -> bool { self.enabled_features.contains(&feature) diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 75a0f89321b9..8a525eb11f7b 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -2331,13 +2331,9 @@ declare_lint_pass!( impl EarlyLintPass for IncompleteInternalFeatures { fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) { let features = cx.builder.features(); - let lang_features = - features.enabled_lang_features().iter().map(|feat| (feat.gate_name, feat.attr_sp)); - let lib_features = - features.enabled_lib_features().iter().map(|feat| (feat.gate_name, feat.attr_sp)); - lang_features - .chain(lib_features) + features + .enabled_features_iter_stable_order() .filter(|(name, _)| features.incomplete(*name) || features.internal(*name)) .for_each(|(name, span)| { if features.incomplete(name) { From ce2087692f3cb6ff25a2b56f33cef20f6aa437cb Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 30 Sep 2025 13:40:27 -0700 Subject: [PATCH 125/226] Add a new `wasm32-wasip3` target to Rust This commit adds a new tier 3 target to rustc, `wasm32-wasip3`. This follows in the footsteps of the previous `wasm32-wasip2` target and is used to represent binding to the WASIp3 set of APIs managed by the WASI subgroup to the WebAssembly Community Group. As of now the WASIp3 set of APIs are not finalized nor standardized. They're in the process of doing so and the current trajectory is to have the APIs published in December of this year. The goal here is to get the wheels turning in Rust to have the target in a more-ready-than-nonexistent state by the time this happens in December. For now the `wasm32-wasip3` target looks exactly the same as `wasm32-wasip2` except that `target_env = "p3"` is specified. This indicates to crates in the ecosystem that WASIp3 APIs should be used, such as the [`wasip3` crate]. Over time this target will evolve as implementation in guest toolchains progress, notably: * The standard library will use WASIp3 APIs natively once they're finalized in the WASI subgroup. * Support through `wasi-libc` will be updated to use WASIp3 natively which Rust will then transitively use. * Longer-term, features such as cooperative multithreading will be added to the WASIp3-track of targets to enable using `std::thread`, for example, on this target. These changes are all expected to be non-breaking changes for users of this target. Runtimes supporting WASIp3, currently Wasmtime and Jco, support WASIp2 APIs as well and will work with components whether or not they import WASIp2, both WASIp2 and WASIp3, or just WASIp3 APIs. This means that changing the internal implementation details of libstd over time is expected to be a non-breaking change. [`wasip3` crate]: https://crates.io/crates/wasip3 --- compiler/rustc_target/src/spec/mod.rs | 1 + .../src/spec/targets/wasm32_wasip3.rs | 20 +++++ library/std/Cargo.toml | 5 ++ library/std/src/os/mod.rs | 2 +- library/std/src/sys/args/mod.rs | 2 +- library/std/src/sys/net/connection/mod.rs | 2 +- .../std/src/sys/net/connection/socket/mod.rs | 2 +- library/std/src/sys/pal/mod.rs | 2 +- library/std/src/sys/random/mod.rs | 4 +- library/std/src/sys/stdio/mod.rs | 2 +- library/std/src/sys/thread/mod.rs | 4 +- src/bootstrap/src/core/build_steps/compile.rs | 10 +++ src/doc/rustc/src/SUMMARY.md | 1 + src/doc/rustc/src/platform-support.md | 1 + .../src/platform-support/wasm32-wasip3.md | 83 +++++++++++++++++++ tests/assembly-llvm/targets/targets-elf.rs | 3 + tests/ui/check-cfg/well-known-values.stderr | 2 +- 17 files changed, 135 insertions(+), 11 deletions(-) create mode 100644 compiler/rustc_target/src/spec/targets/wasm32_wasip3.rs create mode 100644 src/doc/rustc/src/platform-support/wasm32-wasip3.md diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 4a82a8bd888e..e4b44e8b686c 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1624,6 +1624,7 @@ supported_targets! { ("wasm32v1-none", wasm32v1_none), ("wasm32-wasip1", wasm32_wasip1), ("wasm32-wasip2", wasm32_wasip2), + ("wasm32-wasip3", wasm32_wasip3), ("wasm32-wasip1-threads", wasm32_wasip1_threads), ("wasm32-wali-linux-musl", wasm32_wali_linux_musl), ("wasm64-unknown-unknown", wasm64_unknown_unknown), diff --git a/compiler/rustc_target/src/spec/targets/wasm32_wasip3.rs b/compiler/rustc_target/src/spec/targets/wasm32_wasip3.rs new file mode 100644 index 000000000000..e3d5e6542c26 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/wasm32_wasip3.rs @@ -0,0 +1,20 @@ +//! The `wasm32-wasip3` target is the next in the chain of `wasm32-wasip1`, then +//! `wasm32-wasip2`, then WASIp3. The main feature of WASIp3 is native async +//! support in the component model itself. +//! +//! Like `wasm32-wasip2` this target produces a component by default. Support +//! for `wasm32-wasip3` is very early as of the time of this writing so +//! components produced will still import WASIp2 APIs, but that's ok since it's +//! all component-model-level imports anyway. Over time the imports of the +//! standard library will change to WASIp3. + +use crate::spec::Target; + +pub(crate) fn target() -> Target { + // As of now WASIp3 is a lightly edited wasip2 target, so start with that + // and this may grow over time as more features are supported. + let mut target = super::wasm32_wasip2::target(); + target.llvm_target = "wasm32-wasip3".into(); + target.options.env = "p3".into(); + target +} diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 888914a2f772..779b07ce240a 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -85,6 +85,11 @@ wasip2 = { version = '0.14.4', features = [ 'rustc-dep-of-std', ], default-features = false, package = 'wasi' } +[target.'cfg(all(target_os = "wasi", target_env = "p3"))'.dependencies] +wasip2 = { version = '0.14.4', features = [ + 'rustc-dep-of-std', +], default-features = false, package = 'wasi' } + [target.'cfg(target_os = "uefi")'.dependencies] r-efi = { version = "5.2.0", features = ['rustc-dep-of-std'] } r-efi-alloc = { version = "2.0.0", features = ['rustc-dep-of-std'] } diff --git a/library/std/src/os/mod.rs b/library/std/src/os/mod.rs index 96d9bfae8ca3..fd7a11433af1 100644 --- a/library/std/src/os/mod.rs +++ b/library/std/src/os/mod.rs @@ -103,7 +103,7 @@ pub mod linux; all(target_vendor = "fortanix", target_env = "sgx") ) )))] -#[cfg(any(target_os = "wasi", doc))] +#[cfg(any(target_os = "wasi", any(target_env = "p1", target_env = "p2"), doc))] pub mod wasi; #[cfg(any(all(target_os = "wasi", target_env = "p2"), doc))] diff --git a/library/std/src/sys/args/mod.rs b/library/std/src/sys/args/mod.rs index e11e8e5430f0..75c59da721e1 100644 --- a/library/std/src/sys/args/mod.rs +++ b/library/std/src/sys/args/mod.rs @@ -36,7 +36,7 @@ cfg_select! { mod wasip1; pub use wasip1::*; } - all(target_os = "wasi", target_env = "p2") => { + all(target_os = "wasi", any(target_env = "p2", target_env = "p3")) => { mod wasip2; pub use wasip2::*; } diff --git a/library/std/src/sys/net/connection/mod.rs b/library/std/src/sys/net/connection/mod.rs index 7f9636a8ccfd..41e7159f909a 100644 --- a/library/std/src/sys/net/connection/mod.rs +++ b/library/std/src/sys/net/connection/mod.rs @@ -3,7 +3,7 @@ cfg_select! { all(target_family = "unix", not(target_os = "l4re")), target_os = "windows", target_os = "hermit", - all(target_os = "wasi", target_env = "p2"), + all(target_os = "wasi", any(target_env = "p2", target_env = "p3")), target_os = "solid_asp3", ) => { mod socket; diff --git a/library/std/src/sys/net/connection/socket/mod.rs b/library/std/src/sys/net/connection/socket/mod.rs index 1dd06e97bbab..e59d01bdd9bb 100644 --- a/library/std/src/sys/net/connection/socket/mod.rs +++ b/library/std/src/sys/net/connection/socket/mod.rs @@ -25,7 +25,7 @@ cfg_select! { mod unix; pub use unix::*; } - all(target_os = "wasi", target_env = "p2") => { + all(target_os = "wasi", any(target_env = "p2", target_env = "p3")) => { mod wasip2; pub use wasip2::*; } diff --git a/library/std/src/sys/pal/mod.rs b/library/std/src/sys/pal/mod.rs index dd5e83ee570b..9e964540a87c 100644 --- a/library/std/src/sys/pal/mod.rs +++ b/library/std/src/sys/pal/mod.rs @@ -49,7 +49,7 @@ cfg_select! { mod vexos; pub use self::vexos::*; } - all(target_os = "wasi", target_env = "p2") => { + all(target_os = "wasi", any(target_env = "p2", target_env = "p3")) => { mod wasip2; pub use self::wasip2::*; } diff --git a/library/std/src/sys/random/mod.rs b/library/std/src/sys/random/mod.rs index 3c5a4c82a9f1..ec81d89a0f3a 100644 --- a/library/std/src/sys/random/mod.rs +++ b/library/std/src/sys/random/mod.rs @@ -90,7 +90,7 @@ cfg_select! { mod wasip1; pub use wasip1::fill_bytes; } - all(target_os = "wasi", target_env = "p2") => { + all(target_os = "wasi", any(target_env = "p2", target_env = "p3")) => { mod wasip2; pub use wasip2::{fill_bytes, hashmap_random_keys}; } @@ -115,7 +115,7 @@ cfg_select! { target_os = "linux", target_os = "android", all(target_family = "wasm", target_os = "unknown"), - all(target_os = "wasi", target_env = "p2"), + all(target_os = "wasi", not(target_env = "p1")), target_os = "xous", target_os = "vexos", )))] diff --git a/library/std/src/sys/stdio/mod.rs b/library/std/src/sys/stdio/mod.rs index 404ac8779269..660317e3ea84 100644 --- a/library/std/src/sys/stdio/mod.rs +++ b/library/std/src/sys/stdio/mod.rs @@ -37,7 +37,7 @@ cfg_select! { mod wasip1; pub use wasip1::*; } - all(target_os = "wasi", target_env = "p2") => { + all(target_os = "wasi", any(target_env = "p2", target_env = "p3")) => { mod wasip2; pub use wasip2::*; } diff --git a/library/std/src/sys/thread/mod.rs b/library/std/src/sys/thread/mod.rs index 3bd83dd760ac..a20b2a3ddd8c 100644 --- a/library/std/src/sys/thread/mod.rs +++ b/library/std/src/sys/thread/mod.rs @@ -99,7 +99,7 @@ cfg_select! { #[cfg(not(target_feature = "atomics"))] pub use unsupported::{Thread, available_parallelism}; } - all(target_os = "wasi", target_env = "p2") => { + all(target_os = "wasi", any(target_env = "p2", target_env = "p3")) => { mod wasip2; pub use wasip2::{sleep, sleep_until}; #[expect(dead_code)] @@ -146,7 +146,7 @@ cfg_select! { target_os = "hurd", target_os = "fuchsia", target_os = "vxworks", - all(target_os = "wasi", target_env = "p2"), + all(target_os = "wasi", not(target_env = "p1")), )))] pub fn sleep_until(deadline: crate::time::Instant) { use crate::time::Instant; diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 96b4e15433f7..715f7abe9477 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -430,6 +430,16 @@ fn copy_self_contained_objects( target.triple ) }); + + // wasm32-wasip3 doesn't exist in wasi-libc yet, so instead use libs + // from the wasm32-wasip2 target. Once wasi-libc supports wasip3 this + // should be deleted and the native objects should be used. + let srcdir = if target == "wasm32-wasip3" { + assert!(!srcdir.exists(), "wasip3 support is in wasi-libc, this should be updated now"); + builder.wasi_libdir(TargetSelection::from_user("wasm32-wasip2")).unwrap() + } else { + srcdir + }; for &obj in &["libc.a", "crt1-command.o", "crt1-reactor.o"] { copy_and_stamp( builder, diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index 67882bb38132..1965a1b25498 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -132,6 +132,7 @@ - [wasm32-wasip1](platform-support/wasm32-wasip1.md) - [wasm32-wasip1-threads](platform-support/wasm32-wasip1-threads.md) - [wasm32-wasip2](platform-support/wasm32-wasip2.md) + - [wasm32-wasip3](platform-support/wasm32-wasip3.md) - [wasm32-wali-linux-musl](platform-support/wasm32-wali-linux.md) - [wasm32-unknown-emscripten](platform-support/wasm32-unknown-emscripten.md) - [wasm32-unknown-unknown](platform-support/wasm32-unknown-unknown.md) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index d0b6ed51bc16..c23d7563aedb 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -198,6 +198,7 @@ target | std | notes [`wasm32-wasip1`](platform-support/wasm32-wasip1.md) | ✓ | WebAssembly with WASIp1 [`wasm32-wasip1-threads`](platform-support/wasm32-wasip1-threads.md) | ✓ | WebAssembly with WASI Preview 1 and threads [`wasm32-wasip2`](platform-support/wasm32-wasip2.md) | ✓ | WebAssembly with WASIp2 +[`wasm32-wasip3`](platform-support/wasm32-wasip3.md) | ✓ | WebAssembly with WASIp3 [`wasm32v1-none`](platform-support/wasm32v1-none.md) | * | WebAssembly limited to 1.0 features and no imports [`x86_64-apple-ios`](platform-support/apple-ios.md) | ✓ | 64-bit x86 iOS [`x86_64-apple-ios-macabi`](platform-support/apple-ios-macabi.md) | ✓ | Mac Catalyst on x86_64 diff --git a/src/doc/rustc/src/platform-support/wasm32-wasip3.md b/src/doc/rustc/src/platform-support/wasm32-wasip3.md new file mode 100644 index 000000000000..e8063a1bdcfe --- /dev/null +++ b/src/doc/rustc/src/platform-support/wasm32-wasip3.md @@ -0,0 +1,83 @@ +# `wasm32-wasip3` + +**Tier: 3** + +The `wasm32-wasip3` target is the next stage of evolution of the +[`wasm32-wasip2`](./wasm32-wasip2.md) target. The `wasm32-wasip3` target enables +the Rust standard library to use WASIp3 APIs to implement various pieces of +functionality. WASIp3 brings native async support over WASIp2, which integrates +well with Rust's `async` ecosystem. + +> **Note**: As of 2025-10-01 WASIp3 has not yet been approved by the WASI +> subgroup of the WebAssembly Community Group. Development is expected to +> conclude in late 2025 or early 2026. Until then the Rust standard library +> won't actually use WASIp3 APIs on the `wasm32-wasip3` target as they are not +> yet stable and would reduce the stability of this target. Once WASIp3 is +> approved, however, the standard library will update to use WASIp3 natively. + +> **Note**: This target does not yet build as of 2025-10-01 due to and update +> needed in the `libc` crate. Using it will require a `[patch]` for now. + +> **Note**: Until the standard library is fully migrated to use the `wasip3` +> crate then components produced for `wasm32-wasip3` may import WASIp2 APIs. +> This is considered a transitionary phase until fully support of libstd is +> implemented. + +## Target maintainers + +[@alexcrichton](https://github.com/alexcrichton) + +## Requirements + +This target is cross-compiled. The target supports `std` fully. + +## Platform requirements + +The WebAssembly runtime should support both WASIp2 and WASIp3. Runtimes also +are required to support components since this target outputs a component as +opposed to a core wasm module. Two example runtimes for WASIp3 are [Wasmtime] +and [Jco]. + +[Wasmtime]: https://wasmtime.dev/ +[Jco]: https://github.com/bytecodealliance/jco + +## Building the target + +To build this target first acquire a copy of +[`wasi-sdk`](https://github.com/WebAssembly/wasi-sdk/). At this time version 22 +is the minimum needed. + +Next configure the `WASI_SDK_PATH` environment variable to point to where this +is installed. For example: + +```text +export WASI_SDK_PATH=/path/to/wasi-sdk-22.0 +``` + +Next be sure to enable LLD when building Rust from source as LLVM's `wasm-ld` +driver for LLD is required when linking WebAssembly code together. Rust's build +system will automatically pick up any necessary binaries and programs from +`WASI_SDK_PATH`. + +## Testing + +This target is not tested in CI at this time. Locally it can be tested with a +`wasmtime` binary in `PATH` like so: + +```text +./x.py test --target wasm32-wasip3 tests/ui +``` + +## Conditionally compiling code + +It's recommended to conditionally compile code for this target with: + +```text +#[cfg(all(target_os = "wasi", target_env = "p3"))] +``` + +## Enabled WebAssembly features + +The default set of WebAssembly features enabled for compilation is currently the +same as [`wasm32-unknown-unknown`](./wasm32-unknown-unknown.md). See the +documentation there for more information. diff --git a/tests/assembly-llvm/targets/targets-elf.rs b/tests/assembly-llvm/targets/targets-elf.rs index ebea9fe40f51..e26c21383706 100644 --- a/tests/assembly-llvm/targets/targets-elf.rs +++ b/tests/assembly-llvm/targets/targets-elf.rs @@ -586,6 +586,9 @@ //@ revisions: wasm32_wasip2 //@ [wasm32_wasip2] compile-flags: --target wasm32-wasip2 //@ [wasm32_wasip2] needs-llvm-components: webassembly +//@ revisions: wasm32_wasip3 +//@ [wasm32_wasip3] compile-flags: --target wasm32-wasip3 +//@ [wasm32_wasip3] needs-llvm-components: webassembly //@ revisions: wasm32_wali_linux_musl //@ [wasm32_wali_linux_musl] compile-flags: --target wasm32-wali-linux-musl //@ [wasm32_wali_linux_musl] needs-llvm-components: webassembly diff --git a/tests/ui/check-cfg/well-known-values.stderr b/tests/ui/check-cfg/well-known-values.stderr index e62f741b3020..3f14c7b08eac 100644 --- a/tests/ui/check-cfg/well-known-values.stderr +++ b/tests/ui/check-cfg/well-known-values.stderr @@ -156,7 +156,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | target_env = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_env` are: ``, `gnu`, `macabi`, `mlibc`, `msvc`, `musl`, `newlib`, `nto70`, `nto71`, `nto71_iosock`, `nto80`, `ohos`, `p1`, `p2`, `relibc`, `sgx`, `sim`, `uclibc`, and `v5` + = note: expected values for `target_env` are: ``, `gnu`, `macabi`, `mlibc`, `msvc`, `musl`, `newlib`, `nto70`, `nto71`, `nto71_iosock`, `nto80`, `ohos`, `p1`, `p2`, `p3`, `relibc`, `sgx`, `sim`, `uclibc`, and `v5` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` From c2a03cefd8899941032940df0c6be3b364de0ed0 Mon Sep 17 00:00:00 2001 From: dianqk Date: Sun, 21 Sep 2025 20:58:34 +0800 Subject: [PATCH 126/226] debuginfo: Use `LocalRef` to simplify reference debuginfos If the `LocalRef` is `LocalRef::Place`, we can refer to it directly, because the local of place is an indirect pointer. Such a statement is `_1 = &(_2.1)`. If the `LocalRef` is `LocalRef::Operand`, the `OperandRef` should provide the pointer of the reference. Such a statement is `_1 = &((*_2).1)`. But there is a special case that hasn't been handled, scalar pairs like `(&[i32; 16], i32)`. --- .../rustc_codegen_ssa/src/mir/debuginfo.rs | 9 +- .../rustc_codegen_ssa/src/mir/statement.rs | 61 +-- tests/codegen-llvm/debuginfo-dse.rs | 401 +++++++++++++----- 3 files changed, 316 insertions(+), 155 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs index 38bb6f24b1c3..0c9acf087f9f 100644 --- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs @@ -259,8 +259,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { &self, bx: &mut Bx, local: mir::Local, - base: PlaceValue, - layout: TyAndLayout<'tcx>, + base: PlaceRef<'tcx, Bx::Value>, projection: &[mir::PlaceElem<'tcx>], ) { let full_debug_info = bx.sess().opts.debuginfo == DebugInfo::Full; @@ -274,7 +273,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { }; let DebugInfoOffset { direct_offset, indirect_offsets, result: _ } = - calculate_debuginfo_offset(bx, projection, layout); + calculate_debuginfo_offset(bx, projection, base.layout); for var in vars.iter() { let Some(dbg_var) = var.dbg_var else { continue; @@ -285,7 +284,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.dbg_var_value( dbg_var, dbg_loc, - base.llval, + base.val.llval, direct_offset, &indirect_offsets, &var.fragment, @@ -298,7 +297,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let layout = bx.cx().layout_of(ty); let to_backend_ty = bx.cx().immediate_backend_type(layout); let place_ref = PlaceRef::new_sized(bx.cx().const_poison(to_backend_ty), layout); - self.debug_new_val_to_local(bx, local, place_ref.val, layout, &[]); + self.debug_new_val_to_local(bx, local, place_ref, &[]); } /// Apply debuginfo and/or name, after creating the `alloca` for a local, diff --git a/compiler/rustc_codegen_ssa/src/mir/statement.rs b/compiler/rustc_codegen_ssa/src/mir/statement.rs index 80f4f0fcda1c..88590b6271bc 100644 --- a/compiler/rustc_codegen_ssa/src/mir/statement.rs +++ b/compiler/rustc_codegen_ssa/src/mir/statement.rs @@ -1,11 +1,8 @@ -use rustc_middle::mir::{self, NonDivergingIntrinsic, RETURN_PLACE, StmtDebugInfo}; -use rustc_middle::{bug, span_bug}; -use rustc_target::callconv::PassMode; +use rustc_middle::mir::{self, NonDivergingIntrinsic, StmtDebugInfo}; +use rustc_middle::span_bug; use tracing::instrument; use super::{FunctionCx, LocalRef}; -use crate::common::TypeKind; -use crate::mir::place::PlaceRef; use crate::traits::*; impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { @@ -110,48 +107,28 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { match debuginfo { StmtDebugInfo::AssignRef(dest, place) => { let local_ref = match self.locals[place.local] { - LocalRef::Place(place_ref) | LocalRef::UnsizedPlace(place_ref) => { - Some(place_ref) + // For an rvalue like `&(_1.1)`, when `BackendRepr` is `BackendRepr::Memory`, we allocate a block of memory to this place. + // The place is an indirect pointer, we can refer to it directly. + LocalRef::Place(place_ref) => Some((place_ref, place.projection.as_slice())), + // For an rvalue like `&((*_1).1)`, we are calculating the address of `_1.1`. + // The deref projection is no-op here. + LocalRef::Operand(operand_ref) if place.is_indirect_first_projection() => { + Some((operand_ref.deref(bx.cx()), &place.projection[1..])) } - LocalRef::Operand(operand_ref) => operand_ref - .val - .try_pointer_parts() - .map(|(pointer, _)| PlaceRef::new_sized(pointer, operand_ref.layout)), - LocalRef::PendingOperand => None, + // For an rvalue like `&1`, when `BackendRepr` is `BackendRepr::Scalar`, + // we cannot get the address. + // N.B. `non_ssa_locals` returns that this is an SSA local. + LocalRef::Operand(_) => None, + LocalRef::UnsizedPlace(_) | LocalRef::PendingOperand => None, } - .filter(|place_ref| { - // For the reference of an argument (e.x. `&_1`), it's only valid if the pass mode is indirect, and its reference is - // llval. - let local_ref_pass_mode = place.as_local().and_then(|local| { - if local == RETURN_PLACE { - None - } else { - self.fn_abi.args.get(local.as_usize() - 1).map(|arg| &arg.mode) - } - }); - matches!(local_ref_pass_mode, Some(&PassMode::Indirect {..}) | None) && + .filter(|(_, projection)| { // Drop unsupported projections. - place.projection.iter().all(|p| p.can_use_in_debuginfo()) && - // Only pointers can be calculated addresses. - bx.type_kind(bx.val_ty(place_ref.val.llval)) == TypeKind::Pointer + projection.iter().all(|p| p.can_use_in_debuginfo()) }); - if let Some(local_ref) = local_ref { - let (base_layout, projection) = if place.is_indirect_first_projection() { - // For `_n = &((*_1).0: i32);`, we are calculating the address of `_1.0`, so - // we should drop the deref projection. - let projected_ty = local_ref - .layout - .ty - .builtin_deref(true) - .unwrap_or_else(|| bug!("deref of non-pointer {:?}", local_ref)); - let layout = bx.cx().layout_of(projected_ty); - (layout, &place.projection[1..]) - } else { - (local_ref.layout, place.projection.as_slice()) - }; - self.debug_new_val_to_local(bx, *dest, local_ref.val, base_layout, projection); + if let Some((base, projection)) = local_ref { + self.debug_new_val_to_local(bx, *dest, base, projection); } else { - // If the address cannot be computed, use poison to indicate that the value has been optimized out. + // If the address cannot be calculated, use poison to indicate that the value has been optimized out. self.debug_poison_to_local(bx, *dest); } } diff --git a/tests/codegen-llvm/debuginfo-dse.rs b/tests/codegen-llvm/debuginfo-dse.rs index 78e145a3cdf1..fd0c9f1c676f 100644 --- a/tests/codegen-llvm/debuginfo-dse.rs +++ b/tests/codegen-llvm/debuginfo-dse.rs @@ -1,9 +1,249 @@ //@ compile-flags: -Copt-level=3 -g -Zverify-llvm-ir -Zmerge-functions=disabled //@ revisions: CODEGEN OPTIMIZED //@[CODEGEN] compile-flags: -Cno-prepopulate-passes +//@ only-64bit // ignore-tidy-linelength #![crate_type = "lib"] +#![feature(repr_simd, rustc_attrs)] + +// The pass mode is direct and the backend represent is scalar. +type Scalar = i32; // scalar(i32) +type Scalar_Ref = &'static i32; // scalar(ptr) + +// The pass modes are pair and the backend represents are scalar pair. +type Tuple_Scalar_Scalar = (i32, i32); +struct Tuple_Ref_Scalar(&'static i32, i32); +struct Tuple_ArrayRef_Scalar(&'static [i32; 16], i32); // pair(ptr, i32) +impl Default for Tuple_ArrayRef_Scalar { + fn default() -> Tuple_ArrayRef_Scalar { + Tuple_ArrayRef_Scalar(&[0; 16], 0) + } +} +struct Tuple_Scalar_ArrayRef(i32, &'static [i32; 16]); // pair(i32, ptr) +impl Default for Tuple_Scalar_ArrayRef { + fn default() -> Tuple_Scalar_ArrayRef { + Tuple_Scalar_ArrayRef(0, &[0; 16]) + } +} +// The pass mode is indirect and the backend represent is memory. +type Tuple_SliceRef_Scalar = (&'static [i32], i32); + +// The pass mode is pair and the backend represent is scalar pair. +type SliceRef = &'static [i32]; // pair(ptr, i32) +// The pass mode is indirect and the backend represent is memory. +type Array = [i32; 16]; +// The pass mode is direct and the backend represent is scalar. +type ArrayRef = &'static [i32; 16]; + +// The pass mode is indirect and the backend represent is memory. +type Typle_i32_i64_i8 = (i32, i64, i8); +// The pass mode is indirect and the backend represent is memory. +#[repr(C)] +struct Aggregate_i32_Array_i8(i32, &'static [i32; 16], i8); + +type ZST = (); + +impl Default for Aggregate_i32_Array_i8 { + fn default() -> Aggregate_i32_Array_i8 { + Aggregate_i32_Array_i8(0, &[0; 16], 0) + } +} +// The pass mode is cast and the backend represent is scalar. +#[derive(Default)] +struct Aggregate_4xi8(i8, i8, i8, i8); // scalar(i32) + +// The pass mode is indirect and the backend represent is simd vector. +#[repr(simd)] +struct Simd_i32x4([i32; 4]); + +unsafe extern "Rust" { + #[rustc_nounwind] + safe fn opaque_fn(); + #[rustc_nounwind] + safe fn opaque_ptr(_: *const core::ffi::c_void); +} + +#[inline(never)] +#[rustc_nounwind] +fn opaque_use(p: &T) { + opaque_ptr(&raw const p as *const _); +} + +#[inline(never)] +#[rustc_nounwind] +fn opaque_read() -> T { + core::hint::black_box(T::default()) +} + +#[unsafe(no_mangle)] +fn local_var() { + // CHECK-LABEL: define{{( dso_local)?}} void @local_var + let local_var_scalar: Scalar = opaque_read(); + opaque_use(&local_var_scalar); + let dead_local_var_scalar: Scalar = opaque_read(); + let local_var_aggregate_4xi8: Aggregate_4xi8 = opaque_read(); + opaque_use(&local_var_aggregate_4xi8); + let local_var_aggregate_i32_array_i8: Aggregate_i32_Array_i8 = opaque_read(); + opaque_use(&local_var_aggregate_i32_array_i8); + // CHECK: call void @opaque_fn() + opaque_fn(); + // CHECK-NEXT: #dbg_value(ptr %local_var_scalar, [[ref_local_var_scalar:![0-9]+]], !DIExpression() + let ref_local_var_scalar = &local_var_scalar; + // CHECK-NEXT: #dbg_value(ptr poison, [[ref_dead_local_var_scalar:![0-9]+]], !DIExpression() + let ref_dead_local_var_scalar = &dead_local_var_scalar; + // CHECK-NEXT: #dbg_value(ptr %local_var_aggregate_4xi8, [[ref_local_var_aggregate_4xi8:![0-9]+]], !DIExpression() + let ref_local_var_aggregate_4xi8 = &local_var_aggregate_4xi8; + // CHECK-NEXT: #dbg_value(ptr %local_var_aggregate_4xi8, [[ref_0_local_var_aggregate_4xi8:![0-9]+]], !DIExpression() + let ref_0_local_var_aggregate_4xi8 = &local_var_aggregate_4xi8.0; + // CHECK-NEXT: #dbg_value(ptr %local_var_aggregate_4xi8, [[ref_2_local_var_aggregate_4xi8:![0-9]+]], !DIExpression(DW_OP_plus_uconst, 2, DW_OP_stack_value) + let ref_2_local_var_aggregate_4xi8 = &local_var_aggregate_4xi8.2; + // This introduces an extra load instruction. + // CHECK-NEXT: #dbg_value(ptr poison, [[ref_1_1_local_var_aggregate_i32_array_i8:![0-9]+]], !DIExpression() + let ref_1_1_local_var_aggregate_i32_array_i8 = &local_var_aggregate_i32_array_i8.1[1]; + // CHECK-NEXT: #dbg_value(ptr %local_var_aggregate_i32_array_i8, [[ref_2_local_var_aggregate_i32_array_i8:![0-9]+]], !DIExpression(DW_OP_plus_uconst, 16, DW_OP_stack_value) + let ref_2_local_var_aggregate_i32_array_i8 = &local_var_aggregate_i32_array_i8.2; + // CHECK: call void @opaque_fn() + opaque_fn(); +} + +#[unsafe(no_mangle)] +fn zst(zst: ZST, zst_ref: &ZST) { + // CHECK-LABEL: define{{( dso_local)?}} void @zst + // CHECK: call void @opaque_fn() + opaque_fn(); + // CHECK-NEXT: #dbg_value(ptr poison, [[ref_zst:![0-9]+]], !DIExpression() + let ref_zst = &zst; + // CHECK-NEXT: #dbg_value(ptr poison, [[ref_zst_ref:![0-9]+]], !DIExpression() + let ref_zst_ref = &zst_ref; + // CHECK: call void @opaque_fn() + opaque_fn(); +} + +// It only makes sense if the argument is a reference and it refer to projections. +#[unsafe(no_mangle)] +fn direct( + scalar: Scalar, + scalar_ref: Scalar_Ref, + array_ref: ArrayRef, + aggregate_4xi8_ref: &Aggregate_4xi8, +) { + // CHECK-LABEL: define{{( dso_local)?}} void @direct + // CHECK: call void @opaque_fn() + opaque_fn(); + // CHECK-NEXT: #dbg_value(ptr poison, [[ref_scalar:![0-9]+]], !DIExpression() + let ref_scalar = &scalar; + // CHECK-NEXT: #dbg_value(ptr poison, [[ref_scalar_ref:![0-9]+]], !DIExpression() + let ref_scalar_ref = &scalar_ref; + // CHECK-NEXT: #dbg_value(ptr %array_ref, [[ref_0_array_ref:![0-9]+]], !DIExpression() + let ref_0_array_ref = &array_ref[0]; + // CHECK-NEXT: #dbg_value(ptr %array_ref, [[ref_1_array_ref:![0-9]+]], !DIExpression(DW_OP_plus_uconst, 4, DW_OP_stack_value) + let ref_1_array_ref = &array_ref[1]; + // CHECK-NEXT: #dbg_value(ptr %aggregate_4xi8_ref, [[ref_1_aggregate_4xi8_ref:![0-9]+]], !DIExpression(DW_OP_plus_uconst, 1, DW_OP_stack_value) + let ref_1_aggregate_4xi8_ref = &aggregate_4xi8_ref.1; + // CHECK: call void @opaque_fn() + opaque_fn(); +} + +// Arguments are passed through registers, the final values are poison. +#[unsafe(no_mangle)] +fn cast(aggregate_4xi8: Aggregate_4xi8) { + // CHECK-LABEL: define{{( dso_local)?}} void @cast(i32 %0) + // CHECK: call void @opaque_fn() + opaque_fn(); + // The temporary allocated variable is eliminated. + // CODEGEN-NEXT: #dbg_value(ptr %aggregate_4xi8, [[ref_aggregate_4xi8:![0-9]+]], !DIExpression() + // OPTIMIZED-NEXT: #dbg_value(ptr undef, [[ref_aggregate_4xi8:![0-9]+]], !DIExpression() + let ref_aggregate_4xi8 = &aggregate_4xi8; + // CODEGEN-NEXT: #dbg_value(ptr %aggregate_4xi8, [[ref_0_aggregate_4xi8:![0-9]+]], !DIExpression(DW_OP_plus_uconst, 1, DW_OP_stack_value) + // OPTIMIZED-NEXT: #dbg_value(ptr undef, [[ref_0_aggregate_4xi8:![0-9]+]], !DIExpression(DW_OP_plus_uconst, 1, DW_OP_stack_value) + let ref_0_aggregate_4xi8 = &aggregate_4xi8.1; + // CHECK: call void @opaque_fn() + opaque_fn(); +} + +// Arguments are passed indirectly via a pointer. +// The reference of argument is the pointer itself. +#[unsafe(no_mangle)] +fn indirect( + tuple_sliceref_scalar: Tuple_SliceRef_Scalar, + array: Array, + typle_i32_i64_i8: Typle_i32_i64_i8, + simd_i32x4: Simd_i32x4, +) { + // CHECK-LABEL: define{{( dso_local)?}} void @indirect + // CHECK-SAME: (ptr{{.*}} %tuple_sliceref_scalar, ptr{{.*}} %array, ptr{{.*}} %typle_i32_i64_i8, ptr{{.*}} %simd_i32x4) + // CHECK: call void @opaque_fn() + opaque_fn(); + // CHECK-NEXT: #dbg_value(ptr %tuple_sliceref_scalar, [[ref_tuple_sliceref_scalar:![0-9]+]], !DIExpression() + let ref_tuple_sliceref_scalar = &tuple_sliceref_scalar; + // CHECK-NEXT: #dbg_value(ptr %tuple_sliceref_scalar, [[ref_1_tuple_sliceref_scalar:![0-9]+]], !DIExpression(DW_OP_plus_uconst, 16, DW_OP_stack_value) + let ref_1_tuple_sliceref_scalar = &tuple_sliceref_scalar.1; + // CHECK-NEXT: #dbg_value(ptr %array, [[ref_1_array:![0-9]+]], !DIExpression(DW_OP_plus_uconst, 4, DW_OP_stack_value) + let ref_1_array = &array[1]; + // CHECK-NEXT: #dbg_value(ptr %typle_i32_i64_i8, [[ref_1_typle_i32_i64_i8:![0-9]+]], !DIExpression() + let ref_1_typle_i32_i64_i8 = &typle_i32_i64_i8.1; + // CHECK-NEXT: #dbg_value(ptr %simd_i32x4, [[ref_simd_i32x4:![0-9]+]], !DIExpression() + let ref_simd_i32x4 = &simd_i32x4; + // CHECK: call void @opaque_fn() + opaque_fn(); +} + +// They are different MIR statements, but they have the same LLVM IR statement due to the ABI of arguments. +// Both `direct_ref` and `indirect_byval` are passed as a pointer here. +#[unsafe(no_mangle)] +fn direct_ref_and_indirect( + direct_ref: &Aggregate_i32_Array_i8, + indirect_byval: Aggregate_i32_Array_i8, +) { + // CHECK-LABEL: define{{( dso_local)?}} void @direct_ref_and_indirect + // CHECK-SAME: (ptr{{.*}} %direct_ref, ptr{{.*}} %indirect_byval) + // CHECK: call void @opaque_fn() + opaque_fn(); + // CHECK-NEXT: #dbg_value(ptr poison, [[ref_direct_ref:![0-9]+]], !DIExpression() + let ref_direct_ref: &&Aggregate_i32_Array_i8 = &direct_ref; + // CHECK-NEXT: #dbg_value(ptr %direct_ref, [[ref_1_direct_ref:![0-9]+]], !DIExpression(DW_OP_plus_uconst, 8, DW_OP_stack_value) + let ref_1_direct_ref = &direct_ref.1; + // CHECK-NEXT: #dbg_value(ptr %indirect_byval, [[ref_indirect_byval:![0-9]+]], !DIExpression() + let ref_indirect_byval: &Aggregate_i32_Array_i8 = &indirect_byval; + // CHECK-NEXT: #dbg_value(ptr %indirect_byval, [[ref_1_indirect_byval:![0-9]+]], !DIExpression(DW_OP_plus_uconst, 8, DW_OP_stack_value) + let ref_1_indirect_byval = &indirect_byval.1; + // CHECK: call void @opaque_fn() + opaque_fn(); +} + +#[unsafe(no_mangle)] +fn pair( + tuple_scalar_scalar: Tuple_Scalar_Scalar, + tuple_ref_scalar: Tuple_Ref_Scalar, + tuple_arrayref_scalar: Tuple_ArrayRef_Scalar, + tuple_scalar_arrayref: Tuple_Scalar_ArrayRef, + sliceref: SliceRef, +) { + // CHECK-LABEL: define{{( dso_local)?}} void @pair + // CHECK: call void @opaque_fn() + opaque_fn(); + // CHECK-NEXT: #dbg_value(ptr poison, [[ref_0_tuple_scalar_scalar:![0-9]+]], !DIExpression() + let ref_0_tuple_scalar_scalar = &tuple_scalar_scalar.0; + // CHECK-NEXT: #dbg_value(ptr poison, [[ref_0_tuple_ref_scalar:![0-9]+]], !DIExpression() + let ref_0_tuple_ref_scalar = &tuple_ref_scalar.0; + // CHECK-NEXT: #dbg_value(ptr poison, [[ref_1_tuple_ref_scalar:![0-9]+]], !DIExpression() + let ref_1_tuple_ref_scalar = &tuple_ref_scalar.1; + // CHECK-NEXT: #dbg_value(ptr poison, [[ref_0_tuple_arrayref_scalar:![0-9]+]], !DIExpression() + let ref_0_tuple_arrayref_scalar = &tuple_arrayref_scalar.0; + // CHECK-NEXT: #dbg_value(ptr poison, [[ref_1_tuple_arrayref_scalar:![0-9]+]], !DIExpression() + let ref_1_tuple_arrayref_scalar = &tuple_arrayref_scalar.1; + // FIXME: This can be a valid value. + // CHECK-NEXT: #dbg_value(ptr poison, [[ref_0_1_tuple_arrayref_scalar:![0-9]+]], !DIExpression() + let ref_0_1_tuple_arrayref_scalar = &tuple_arrayref_scalar.0[1]; + // FIXME: This can be a valid value. + // CHECK-NEXT: #dbg_value(ptr poison, [[ref_1_1_tuple_scalar_arrayref:![0-9]+]], !DIExpression() + let ref_1_1_tuple_scalar_arrayref = &tuple_scalar_arrayref.1[1]; + // CHECK: #dbg_value(ptr %sliceref.0, [[ref_1_sliceref:![0-9]+]], !DIExpression(DW_OP_plus_uconst, 4, DW_OP_stack_value) + let ref_1_sliceref = &sliceref[1]; + // CHECK: call void @opaque_fn() + opaque_fn(); +} #[repr(C)] #[derive(Clone, Copy)] @@ -16,23 +256,7 @@ pub struct Bar<'a> { foo: &'a Foo, } -#[no_mangle] -fn r#ref(ref_foo: &Foo) -> i32 { - // CHECK-LABEL: define{{.*}} i32 @ref - // CHECK-SAME: (ptr {{.*}} [[ARG_ref_foo:%.*]]) - // OPTIMIZED: #dbg_value(ptr [[ARG_ref_foo]], [[VAR_ref_foo:![0-9]+]], !DIExpression() - // CHECK: #dbg_value(ptr poison, [[VAR_invalid_ref_of_ref_foo:![0-9]+]], !DIExpression() - // CHECK: #dbg_value(ptr [[ARG_ref_foo]], [[VAR_ref_v0:![0-9]+]], !DIExpression() - // CHECK: #dbg_value(ptr [[ARG_ref_foo]], [[VAR_ref_v1:![0-9]+]], !DIExpression(DW_OP_plus_uconst, 8, DW_OP_stack_value) - // CHECK: #dbg_value(ptr [[ARG_ref_foo]], [[VAR_ref_v2:![0-9]+]], !DIExpression(DW_OP_plus_uconst, 16, DW_OP_stack_value) - let invalid_ref_of_ref_foo = &ref_foo; - let ref_v0 = &ref_foo.0; - let ref_v1 = &ref_foo.1; - let ref_v2 = &ref_foo.2; - ref_foo.0 -} - -#[no_mangle] +#[unsafe(no_mangle)] pub fn dead_first(dead_first_foo: &Foo) -> &i32 { // CHECK-LABEL: def {{.*}} ptr @dead_first // CHECK-SAME: (ptr {{.*}} [[ARG_dead_first_foo:%.*]]) @@ -47,32 +271,9 @@ pub fn dead_first(dead_first_foo: &Foo) -> &i32 { dead_first_v0 } -#[no_mangle] -fn ptr(ptr_foo: Foo) -> i32 { - // CHECK-LABEL: define{{.*}} i32 @ptr - // CHECK-SAME: (ptr {{.*}} [[ARG_ptr_foo:%.*]]) - // CHECK: #dbg_value(ptr [[ARG_ptr_foo]], [[ref_ptr_foo:![0-9]+]], !DIExpression() - // CHECK: #dbg_value(ptr [[ARG_ptr_foo]], [[VAR_ptr_v0:![0-9]+]], !DIExpression() - // CHECK: #dbg_value(ptr [[ARG_ptr_foo]], [[VAR_ptr_v1:![0-9]+]], !DIExpression(DW_OP_plus_uconst, 8, DW_OP_stack_value) - // CHECK: #dbg_value(ptr [[ARG_ptr_foo]], [[VAR_ptr_v2:![0-9]+]], !DIExpression(DW_OP_plus_uconst, 16, DW_OP_stack_value) - let ref_ptr_foo = &ptr_foo; - let ptr_v0 = &ptr_foo.0; - let ptr_v1 = &ptr_foo.1; - let ptr_v2 = &ptr_foo.2; - ptr_foo.2 -} - -#[no_mangle] -fn no_ptr(val: i32) -> i32 { - // CHECK-LABEL: define{{.*}} i32 @no_ptr - // CODEGEN: #dbg_value(ptr poison, [[VAR_val_ref:![0-9]+]], !DIExpression() - let val_ref = &val; - val -} - -#[no_mangle] +#[unsafe(no_mangle)] pub fn fragment(fragment_v1: Foo, mut fragment_v2: Foo) -> Foo { - // CHECK-LABEL: define void @fragment + // CHECK-LABEL: define{{( dso_local)?}} void @fragment // CHECK-SAME: (ptr {{.*}}, ptr {{.*}} [[ARG_fragment_v1:%.*]], ptr {{.*}} [[ARG_fragment_v2:%.*]]) // CHECK: #dbg_declare(ptr [[ARG_fragment_v1]] // CHECK-NEXT: #dbg_declare(ptr [[ARG_fragment_v2]] @@ -85,93 +286,77 @@ pub fn fragment(fragment_v1: Foo, mut fragment_v2: Foo) -> Foo { fragment_v2 } -#[no_mangle] +#[unsafe(no_mangle)] pub fn deref(bar: Bar) -> i32 { - // CHECK-LABEL: define {{.*}} i32 @deref + // CHECK-LABEL: define{{.*}} i32 @deref // We are unable to represent dereference within this expression. // CHECK: #dbg_value(ptr poison, [[VAR_deref_dead:![0-9]+]], !DIExpression() let deref_dead = &bar.foo.2; bar.a } -#[no_mangle] -pub fn tuple(foo: (i32, &Foo)) -> i32 { - // CHECK-LABEL: define{{.*}} i32 @tuple - // Although there is no dereference here, there is a dereference in the MIR. - // CHECK: #dbg_value(ptr poison, [[VAR_tuple_dead:![0-9]+]], !DIExpression() - let tuple_dead = &foo.1.2; - foo.1.0 -} - -pub struct ZST; - -#[no_mangle] -pub fn zst(zst: ZST, v: &i32) -> i32 { - // CHECK-LABEL: define{{.*}} i32 @zst - // CHECK: #dbg_value(ptr poison, [[VAR_zst_ref:![0-9]+]], !DIExpression() - let zst_ref = &zst; - *v -} - -#[no_mangle] +#[unsafe(no_mangle)] fn index(slice: &[i32; 4], idx: usize) -> i32 { // CHECK-LABEL: define{{.*}} i32 @index - // CHECK: bb1: - // CHECK-NEXT: #dbg_value(ptr poison, [[VAR_index_from_var:![0-9]+]], !DIExpression() - // CODEGEN: bb3: - // CHECK-NEXT: #dbg_value(ptr %slice, [[VAR_const_index_from_start:![0-9]+]], !DIExpression() - // CHECK-NEXT: #dbg_value(ptr poison, [[VAR_const_index_from_end:![0-9]+]], !DIExpression() + // CHECK: call void @opaque_fn() + opaque_fn(); + // CHECK: #dbg_value(ptr poison, [[VAR_index_from_var:![0-9]+]], !DIExpression() let index_from_var = &slice[idx]; + // CHECK: #dbg_value(ptr %slice, [[VAR_const_index_from_start:![0-9]+]], !DIExpression() + // CHECK-NEXT: #dbg_value(ptr poison, [[VAR_const_index_from_end:![0-9]+]], !DIExpression() let [ref const_index_from_start, .., ref const_index_from_end] = slice[..] else { return 0; }; slice[0] } -unsafe extern "Rust" { - safe fn opaque_inner(_: *const core::ffi::c_void); -} +// CHECK-DAG: [[ref_local_var_scalar]] = !DILocalVariable(name: "ref_local_var_scalar" +// CHECK-DAG: [[ref_dead_local_var_scalar]] = !DILocalVariable(name: "ref_dead_local_var_scalar" +// CHECK-DAG: [[ref_local_var_aggregate_4xi8]] = !DILocalVariable(name: "ref_local_var_aggregate_4xi8" +// CHECK-DAG: [[ref_0_local_var_aggregate_4xi8]] = !DILocalVariable(name: "ref_0_local_var_aggregate_4xi8" +// CHECK-DAG: [[ref_2_local_var_aggregate_4xi8]] = !DILocalVariable(name: "ref_2_local_var_aggregate_4xi8" +// CHECK-DAG: [[ref_1_1_local_var_aggregate_i32_array_i8]] = !DILocalVariable(name: "ref_1_1_local_var_aggregate_i32_array_i8" +// CHECK-DAG: [[ref_2_local_var_aggregate_i32_array_i8]] = !DILocalVariable(name: "ref_2_local_var_aggregate_i32_array_i8" -#[inline(never)] -pub fn opaque_use(p: &T) { - opaque_inner(&raw const p as *const _); -} +// CHECK-DAG: [[ref_zst]] = !DILocalVariable(name: "ref_zst" +// CHECK-DAG: [[ref_zst_ref]] = !DILocalVariable(name: "ref_zst_ref" -#[no_mangle] -pub fn non_arg_ref(scalar: i32, foo: Foo, a: &i32) -> i32 { - // CHECK-LABEL: define{{.*}} i32 @non_arg_ref - // CHECK: #dbg_value(ptr %non_arg_ref_scalar, [[VAR_non_arg_ref_scalar_ref:![0-9]+]], !DIExpression() - // CHECK-NEXT: #dbg_value(ptr %non_arg_ref_foo, [[VAR_non_arg_ref_foo_ref:![0-9]+]], !DIExpression() - // CHECK-NEXT: #dbg_value(ptr %non_arg_ref_foo, [[VAR_non_arg_ref_foo_ref_2:![0-9]+]], !DIExpression(DW_OP_plus_uconst, 16, DW_OP_stack_value) - let non_arg_ref_scalar = scalar; - let non_arg_ref_foo = foo; - opaque_use(&non_arg_ref_scalar); - opaque_use(&non_arg_ref_foo); - let non_arg_ref_scalar_ref = &non_arg_ref_scalar; - let non_arg_ref_foo_ref = &non_arg_ref_foo; - let non_arg_ref_foo_ref_2 = &non_arg_ref_foo.2; - *a -} +// CHECK-DAG: [[ref_scalar]] = !DILocalVariable(name: "ref_scalar" +// CHECK-DAG: [[ref_scalar_ref]] = !DILocalVariable(name: "ref_scalar_ref" +// CHECK-DAG: [[ref_0_array_ref]] = !DILocalVariable(name: "ref_0_array_ref" +// CHECK-DAG: [[ref_1_array_ref]] = !DILocalVariable(name: "ref_1_array_ref" +// CHECK-DAG: [[ref_1_aggregate_4xi8_ref]] = !DILocalVariable(name: "ref_1_aggregate_4xi8_ref" + +// CHECK-DAG: [[ref_aggregate_4xi8]] = !DILocalVariable(name: "ref_aggregate_4xi8" +// CHECK-DAG: [[ref_0_aggregate_4xi8]] = !DILocalVariable(name: "ref_0_aggregate_4xi8" + +// CHECK-DAG: [[ref_tuple_sliceref_scalar]] = !DILocalVariable(name: "ref_tuple_sliceref_scalar" +// CHECK-DAG: [[ref_1_tuple_sliceref_scalar]] = !DILocalVariable(name: "ref_1_tuple_sliceref_scalar" +// CHECK-DAG: [[ref_1_array]] = !DILocalVariable(name: "ref_1_array" +// CHECK-DAG: [[ref_1_typle_i32_i64_i8]] = !DILocalVariable(name: "ref_1_typle_i32_i64_i8" +// CHECK-DAG: [[ref_simd_i32x4]] = !DILocalVariable(name: "ref_simd_i32x4" + +// CHECK-DAG: [[ref_direct_ref]] = !DILocalVariable(name: "ref_direct_ref" +// CHECK-DAG: [[ref_1_direct_ref]] = !DILocalVariable(name: "ref_1_direct_ref" +// CHECK-DAG: [[ref_indirect_byval]] = !DILocalVariable(name: "ref_indirect_byval" +// CHECK-DAG: [[ref_1_indirect_byval]] = !DILocalVariable(name: "ref_1_indirect_byval" + +// CHECK-DAG: [[ref_0_tuple_scalar_scalar]] = !DILocalVariable(name: "ref_0_tuple_scalar_scalar" +// CHECK-DAG: [[ref_0_tuple_ref_scalar]] = !DILocalVariable(name: "ref_0_tuple_ref_scalar" +// CHECK-DAG: [[ref_1_tuple_ref_scalar]] = !DILocalVariable(name: "ref_1_tuple_ref_scalar" +// CHECK-DAG: [[ref_0_tuple_arrayref_scalar]] = !DILocalVariable(name: "ref_0_tuple_arrayref_scalar" +// CHECK-DAG: [[ref_1_tuple_arrayref_scalar]] = !DILocalVariable(name: "ref_1_tuple_arrayref_scalar" +// CHECK-DAG: [[ref_0_1_tuple_arrayref_scalar]] = !DILocalVariable(name: "ref_0_1_tuple_arrayref_scalar" +// CHECK-DAG: [[ref_1_1_tuple_scalar_arrayref]] = !DILocalVariable(name: "ref_1_1_tuple_scalar_arrayref" +// CHECK-DAG: [[ref_1_sliceref]] = !DILocalVariable(name: "ref_1_sliceref" -// CHECK-DAG: [[VAR_invalid_ref_of_ref_foo]] = !DILocalVariable(name: "invalid_ref_of_ref_foo" -// OPTIMIZED-DAG: [[VAR_ref_foo]] = !DILocalVariable(name: "ref_foo" -// CHECK-DAG: [[VAR_ref_v0]] = !DILocalVariable(name: "ref_v0" -// CHECK-DAG: [[VAR_ref_v1]] = !DILocalVariable(name: "ref_v1" -// CHECK-DAG: [[VAR_ref_v2]] = !DILocalVariable(name: "ref_v2" -// CHECK-DAG: [[ref_ptr_foo]] = !DILocalVariable(name: "ref_ptr_foo" -// CHECK-DAG: [[VAR_ptr_v0]] = !DILocalVariable(name: "ptr_v0" -// CHECK-DAG: [[VAR_ptr_v1]] = !DILocalVariable(name: "ptr_v1" -// CHECK-DAG: [[VAR_ptr_v2]] = !DILocalVariable(name: "ptr_v2" -// CODEGEN-DAG: [[VAR_val_ref]] = !DILocalVariable(name: "val_ref" -// CHECK-DAG: [[VAR_fragment_f]] = !DILocalVariable(name: "fragment_f" -// CHECK-DAG: [[VAR_deref_dead]] = !DILocalVariable(name: "deref_dead" -// CHECK-DAG: [[VAR_tuple_dead]] = !DILocalVariable(name: "tuple_dead" // CHECK-DAG: [[ARG_dead_first_foo]] = !DILocalVariable(name: "dead_first_foo" // CHECK-DAG: [[VAR_dead_first_v0]] = !DILocalVariable(name: "dead_first_v0" + +// CHECK-DAG: [[VAR_fragment_f]] = !DILocalVariable(name: "fragment_f" + +// CHECK-DAG: [[VAR_deref_dead]] = !DILocalVariable(name: "deref_dead" + // CHECK-DAG: [[VAR_index_from_var]] = !DILocalVariable(name: "index_from_var" // CHECK-DAG: [[VAR_const_index_from_start]] = !DILocalVariable(name: "const_index_from_start" // CHECK-DAG: [[VAR_const_index_from_end]] = !DILocalVariable(name: "const_index_from_end" -// CHECK-DAG: [[VAR_zst_ref]] = !DILocalVariable(name: "zst_ref" -// CHECK-DAG: [[VAR_non_arg_ref_scalar_ref]] = !DILocalVariable(name: "non_arg_ref_scalar_ref" -// CHECK-DAG: [[VAR_non_arg_ref_foo_ref]] = !DILocalVariable(name: "non_arg_ref_foo_ref" -// CHECK-DAG: [[VAR_non_arg_ref_foo_ref_2]] = !DILocalVariable(name: "non_arg_ref_foo_ref_2" From 3d5f54ad55a32c0f53271597145b5cd7bae9c3e2 Mon Sep 17 00:00:00 2001 From: Urgau Date: Fri, 3 Oct 2025 11:27:57 +0200 Subject: [PATCH 127/226] Respect `-Z` unstable options in `rustdoc --test` --- src/librustdoc/doctest.rs | 1 + .../rustdoc-ui/doctest/check-attr-test.stderr | 164 +++++++++--------- ...led-doctest-extra-semicolon-on-item.stderr | 2 +- .../doctest/main-alongside-stmts.stderr | 4 +- .../doctest/standalone-warning-2024.stderr | 30 ++-- .../doctest/test-compile-fail1.stderr | 18 +- .../doctest/test-compile-fail2.stderr | 8 +- .../doctest/test-compile-fail3.stderr | 8 +- .../doctest/warn-main-not-called.stderr | 4 +- 9 files changed, 120 insertions(+), 119 deletions(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 95bd31729de1..38a0c925d2a4 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -173,6 +173,7 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, input: Input, options: RustdocOptions target_triple: options.target.clone(), crate_name: options.crate_name.clone(), remap_path_prefix: options.remap_path_prefix.clone(), + unstable_opts: options.unstable_opts.clone(), ..config::Options::default() }; diff --git a/tests/rustdoc-ui/doctest/check-attr-test.stderr b/tests/rustdoc-ui/doctest/check-attr-test.stderr index 257136d1633d..4cf93033ae8a 100644 --- a/tests/rustdoc-ui/doctest/check-attr-test.stderr +++ b/tests/rustdoc-ui/doctest/check-attr-test.stderr @@ -1,55 +1,55 @@ error: unknown attribute `compile-fail` - --> $DIR/check-attr-test.rs:5:1 - | -5 | / /// foo -6 | | /// -7 | | /// ```compile-fail,compilefail,comPile_fail -8 | | /// boo -9 | | /// ``` - | |_______^ - | - = help: use `compile_fail` to invert the results of this test, so that it passes if it cannot be compiled and fails if it can - = help: this code block may be skipped during testing, because unknown attributes are treated as markers for code samples written in other programming languages, unless it is also explicitly marked as `rust` + --> $DIR/check-attr-test.rs:5:1 + | +LL | / /// foo +LL | | /// +LL | | /// ```compile-fail,compilefail,comPile_fail +LL | | /// boo +LL | | /// ``` + | |_______^ + | + = help: use `compile_fail` to invert the results of this test, so that it passes if it cannot be compiled and fails if it can + = help: this code block may be skipped during testing, because unknown attributes are treated as markers for code samples written in other programming languages, unless it is also explicitly marked as `rust` note: the lint level is defined here - --> $DIR/check-attr-test.rs:3:9 - | -3 | #![deny(rustdoc::invalid_codeblock_attributes)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + --> $DIR/check-attr-test.rs:3:9 + | +LL | #![deny(rustdoc::invalid_codeblock_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: unknown attribute `compilefail` - --> $DIR/check-attr-test.rs:5:1 - | -5 | / /// foo -6 | | /// -7 | | /// ```compile-fail,compilefail,comPile_fail -8 | | /// boo -9 | | /// ``` - | |_______^ - | - = help: use `compile_fail` to invert the results of this test, so that it passes if it cannot be compiled and fails if it can - = help: this code block may be skipped during testing, because unknown attributes are treated as markers for code samples written in other programming languages, unless it is also explicitly marked as `rust` + --> $DIR/check-attr-test.rs:5:1 + | +LL | / /// foo +LL | | /// +LL | | /// ```compile-fail,compilefail,comPile_fail +LL | | /// boo +LL | | /// ``` + | |_______^ + | + = help: use `compile_fail` to invert the results of this test, so that it passes if it cannot be compiled and fails if it can + = help: this code block may be skipped during testing, because unknown attributes are treated as markers for code samples written in other programming languages, unless it is also explicitly marked as `rust` error: unknown attribute `comPile_fail` - --> $DIR/check-attr-test.rs:5:1 - | -5 | / /// foo -6 | | /// -7 | | /// ```compile-fail,compilefail,comPile_fail -8 | | /// boo -9 | | /// ``` - | |_______^ - | - = help: use `compile_fail` to invert the results of this test, so that it passes if it cannot be compiled and fails if it can - = help: this code block may be skipped during testing, because unknown attributes are treated as markers for code samples written in other programming languages, unless it is also explicitly marked as `rust` + --> $DIR/check-attr-test.rs:5:1 + | +LL | / /// foo +LL | | /// +LL | | /// ```compile-fail,compilefail,comPile_fail +LL | | /// boo +LL | | /// ``` + | |_______^ + | + = help: use `compile_fail` to invert the results of this test, so that it passes if it cannot be compiled and fails if it can + = help: this code block may be skipped during testing, because unknown attributes are treated as markers for code samples written in other programming languages, unless it is also explicitly marked as `rust` error: unknown attribute `should-panic` --> $DIR/check-attr-test.rs:12:1 | -12 | / /// bar -13 | | /// -14 | | /// ```should-panic,shouldpanic,shOuld_panic -15 | | /// boo -16 | | /// ``` +LL | / /// bar +LL | | /// +LL | | /// ```should-panic,shouldpanic,shOuld_panic +LL | | /// boo +LL | | /// ``` | |_______^ | = help: use `should_panic` to invert the results of this test, so that if passes if it panics and fails if it does not @@ -58,11 +58,11 @@ error: unknown attribute `should-panic` error: unknown attribute `shouldpanic` --> $DIR/check-attr-test.rs:12:1 | -12 | / /// bar -13 | | /// -14 | | /// ```should-panic,shouldpanic,shOuld_panic -15 | | /// boo -16 | | /// ``` +LL | / /// bar +LL | | /// +LL | | /// ```should-panic,shouldpanic,shOuld_panic +LL | | /// boo +LL | | /// ``` | |_______^ | = help: use `should_panic` to invert the results of this test, so that if passes if it panics and fails if it does not @@ -71,11 +71,11 @@ error: unknown attribute `shouldpanic` error: unknown attribute `shOuld_panic` --> $DIR/check-attr-test.rs:12:1 | -12 | / /// bar -13 | | /// -14 | | /// ```should-panic,shouldpanic,shOuld_panic -15 | | /// boo -16 | | /// ``` +LL | / /// bar +LL | | /// +LL | | /// ```should-panic,shouldpanic,shOuld_panic +LL | | /// boo +LL | | /// ``` | |_______^ | = help: use `should_panic` to invert the results of this test, so that if passes if it panics and fails if it does not @@ -84,11 +84,11 @@ error: unknown attribute `shOuld_panic` error: unknown attribute `no-run` --> $DIR/check-attr-test.rs:19:1 | -19 | / /// foobar -20 | | /// -21 | | /// ```no-run,norun,nO_run -22 | | /// boo -23 | | /// ``` +LL | / /// foobar +LL | | /// +LL | | /// ```no-run,norun,nO_run +LL | | /// boo +LL | | /// ``` | |_______^ | = help: use `no_run` to compile, but not run, the code sample during testing @@ -97,11 +97,11 @@ error: unknown attribute `no-run` error: unknown attribute `norun` --> $DIR/check-attr-test.rs:19:1 | -19 | / /// foobar -20 | | /// -21 | | /// ```no-run,norun,nO_run -22 | | /// boo -23 | | /// ``` +LL | / /// foobar +LL | | /// +LL | | /// ```no-run,norun,nO_run +LL | | /// boo +LL | | /// ``` | |_______^ | = help: use `no_run` to compile, but not run, the code sample during testing @@ -110,11 +110,11 @@ error: unknown attribute `norun` error: unknown attribute `nO_run` --> $DIR/check-attr-test.rs:19:1 | -19 | / /// foobar -20 | | /// -21 | | /// ```no-run,norun,nO_run -22 | | /// boo -23 | | /// ``` +LL | / /// foobar +LL | | /// +LL | | /// ```no-run,norun,nO_run +LL | | /// boo +LL | | /// ``` | |_______^ | = help: use `no_run` to compile, but not run, the code sample during testing @@ -123,11 +123,11 @@ error: unknown attribute `nO_run` error: unknown attribute `test-harness` --> $DIR/check-attr-test.rs:26:1 | -26 | / /// b -27 | | /// -28 | | /// ```test-harness,testharness,tesT_harness -29 | | /// boo -30 | | /// ``` +LL | / /// b +LL | | /// +LL | | /// ```test-harness,testharness,tesT_harness +LL | | /// boo +LL | | /// ``` | |_______^ | = help: use `test_harness` to run functions marked `#[test]` instead of a potentially-implicit `main` function @@ -136,11 +136,11 @@ error: unknown attribute `test-harness` error: unknown attribute `testharness` --> $DIR/check-attr-test.rs:26:1 | -26 | / /// b -27 | | /// -28 | | /// ```test-harness,testharness,tesT_harness -29 | | /// boo -30 | | /// ``` +LL | / /// b +LL | | /// +LL | | /// ```test-harness,testharness,tesT_harness +LL | | /// boo +LL | | /// ``` | |_______^ | = help: use `test_harness` to run functions marked `#[test]` instead of a potentially-implicit `main` function @@ -149,11 +149,11 @@ error: unknown attribute `testharness` error: unknown attribute `tesT_harness` --> $DIR/check-attr-test.rs:26:1 | -26 | / /// b -27 | | /// -28 | | /// ```test-harness,testharness,tesT_harness -29 | | /// boo -30 | | /// ``` +LL | / /// b +LL | | /// +LL | | /// ```test-harness,testharness,tesT_harness +LL | | /// boo +LL | | /// ``` | |_______^ | = help: use `test_harness` to run functions marked `#[test]` instead of a potentially-implicit `main` function diff --git a/tests/rustdoc-ui/doctest/failed-doctest-extra-semicolon-on-item.stderr b/tests/rustdoc-ui/doctest/failed-doctest-extra-semicolon-on-item.stderr index 113fb7ccb60e..cffda43ba1c9 100644 --- a/tests/rustdoc-ui/doctest/failed-doctest-extra-semicolon-on-item.stderr +++ b/tests/rustdoc-ui/doctest/failed-doctest-extra-semicolon-on-item.stderr @@ -1,7 +1,7 @@ warning: the `main` function of this doctest won't be run as it contains expressions at the top level, meaning that the whole doctest code will be wrapped in a function --> $DIR/failed-doctest-extra-semicolon-on-item.rs:11:1 | -11 | /// ```rust +LL | /// ```rust | ^^^^^^^^^^^ warning: 1 warning emitted diff --git a/tests/rustdoc-ui/doctest/main-alongside-stmts.stderr b/tests/rustdoc-ui/doctest/main-alongside-stmts.stderr index d90a289ca698..3f347d96ab06 100644 --- a/tests/rustdoc-ui/doctest/main-alongside-stmts.stderr +++ b/tests/rustdoc-ui/doctest/main-alongside-stmts.stderr @@ -1,13 +1,13 @@ warning: the `main` function of this doctest won't be run as it contains expressions at the top level, meaning that the whole doctest code will be wrapped in a function --> $DIR/main-alongside-stmts.rs:17:1 | -17 | //! ``` +LL | //! ``` | ^^^^^^^ warning: the `main` function of this doctest won't be run as it contains expressions at the top level, meaning that the whole doctest code will be wrapped in a function --> $DIR/main-alongside-stmts.rs:26:1 | -26 | //! ``` +LL | //! ``` | ^^^^^^^ warning: 2 warnings emitted diff --git a/tests/rustdoc-ui/doctest/standalone-warning-2024.stderr b/tests/rustdoc-ui/doctest/standalone-warning-2024.stderr index ce65557c2c4a..1af2ded58e8b 100644 --- a/tests/rustdoc-ui/doctest/standalone-warning-2024.stderr +++ b/tests/rustdoc-ui/doctest/standalone-warning-2024.stderr @@ -1,13 +1,13 @@ error: unknown attribute `standalone` --> $DIR/standalone-warning-2024.rs:11:1 | -11 | / //! ```standalone -12 | | //! bla -13 | | //! ``` -14 | | //! -15 | | //! ```standalone-crate -16 | | //! bla -17 | | //! ``` +LL | / //! ```standalone +LL | | //! bla +LL | | //! ``` +LL | | //! +LL | | //! ```standalone-crate +LL | | //! bla +LL | | //! ``` | |_______^ | = help: use `standalone_crate` to compile this code block separately @@ -15,20 +15,20 @@ error: unknown attribute `standalone` note: the lint level is defined here --> $DIR/standalone-warning-2024.rs:9:9 | - 9 | #![deny(warnings)] +LL | #![deny(warnings)] | ^^^^^^^^ = note: `#[deny(rustdoc::invalid_codeblock_attributes)]` implied by `#[deny(warnings)]` error: unknown attribute `standalone-crate` --> $DIR/standalone-warning-2024.rs:11:1 | -11 | / //! ```standalone -12 | | //! bla -13 | | //! ``` -14 | | //! -15 | | //! ```standalone-crate -16 | | //! bla -17 | | //! ``` +LL | / //! ```standalone +LL | | //! bla +LL | | //! ``` +LL | | //! +LL | | //! ```standalone-crate +LL | | //! bla +LL | | //! ``` | |_______^ | = help: use `standalone_crate` to compile this code block separately diff --git a/tests/rustdoc-ui/doctest/test-compile-fail1.stderr b/tests/rustdoc-ui/doctest/test-compile-fail1.stderr index 02f4d8d754f9..aa5cc2e14d6d 100644 --- a/tests/rustdoc-ui/doctest/test-compile-fail1.stderr +++ b/tests/rustdoc-ui/doctest/test-compile-fail1.stderr @@ -1,13 +1,13 @@ error[E0428]: the name `f` is defined multiple times - --> $DIR/test-compile-fail1.rs:8:1 - | -6 | pub fn f() {} - | ---------- previous definition of the value `f` here -7 | -8 | pub fn f() {} - | ^^^^^^^^^^ `f` redefined here - | - = note: `f` must be defined only once in the value namespace of this module + --> $DIR/test-compile-fail1.rs:8:1 + | +LL | pub fn f() {} + | ---------- previous definition of the value `f` here +LL | +LL | pub fn f() {} + | ^^^^^^^^^^ `f` redefined here + | + = note: `f` must be defined only once in the value namespace of this module error: aborting due to 1 previous error diff --git a/tests/rustdoc-ui/doctest/test-compile-fail2.stderr b/tests/rustdoc-ui/doctest/test-compile-fail2.stderr index f0ad40eb6ca8..9f50c857275c 100644 --- a/tests/rustdoc-ui/doctest/test-compile-fail2.stderr +++ b/tests/rustdoc-ui/doctest/test-compile-fail2.stderr @@ -1,8 +1,8 @@ error: expected one of `!` or `::`, found `` - --> $DIR/test-compile-fail2.rs:3:1 - | -3 | fail - | ^^^^ expected one of `!` or `::` + --> $DIR/test-compile-fail2.rs:3:1 + | +LL | fail + | ^^^^ expected one of `!` or `::` error: aborting due to 1 previous error diff --git a/tests/rustdoc-ui/doctest/test-compile-fail3.stderr b/tests/rustdoc-ui/doctest/test-compile-fail3.stderr index 09d78b2f3467..9f90a428d480 100644 --- a/tests/rustdoc-ui/doctest/test-compile-fail3.stderr +++ b/tests/rustdoc-ui/doctest/test-compile-fail3.stderr @@ -1,8 +1,8 @@ error[E0765]: unterminated double quote string - --> $DIR/test-compile-fail3.rs:3:1 - | -3 | "fail - | ^^^^^ + --> $DIR/test-compile-fail3.rs:3:1 + | +LL | "fail + | ^^^^^ error: aborting due to 1 previous error diff --git a/tests/rustdoc-ui/doctest/warn-main-not-called.stderr b/tests/rustdoc-ui/doctest/warn-main-not-called.stderr index 3a079f47555b..b5b7bc57644a 100644 --- a/tests/rustdoc-ui/doctest/warn-main-not-called.stderr +++ b/tests/rustdoc-ui/doctest/warn-main-not-called.stderr @@ -1,13 +1,13 @@ warning: the `main` function of this doctest won't be run as it contains expressions at the top level, meaning that the whole doctest code will be wrapped in a function --> $DIR/warn-main-not-called.rs:10:1 | -10 | //! ``` +LL | //! ``` | ^^^^^^^ warning: the `main` function of this doctest won't be run as it contains expressions at the top level, meaning that the whole doctest code will be wrapped in a function --> $DIR/warn-main-not-called.rs:19:1 | -19 | //! ``` +LL | //! ``` | ^^^^^^^ warning: 2 warnings emitted From cb0f969b623a7e12a0d8166c9a498e17a8b5a3c4 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 3 Oct 2025 16:57:24 +1000 Subject: [PATCH 128/226] Avoid getting `dep_dep_node` unnecessarily. It's gotten on a hot path but only for use within `debug!`. --- compiler/rustc_query_system/src/dep_graph/graph.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index 7e258aaa54f7..eafb38bd25bb 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -876,14 +876,19 @@ impl DepGraphData { frame: Option<&MarkFrame<'_>>, ) -> Option<()> { let dep_dep_node_color = self.colors.get(parent_dep_node_index); - let dep_dep_node = &self.previous.index_to_node(parent_dep_node_index); + + let get_dep_dep_node = || self.previous.index_to_node(parent_dep_node_index); match dep_dep_node_color { Some(DepNodeColor::Green(_)) => { // This dependency has been marked as green before, we are // still fine and can continue with checking the other // dependencies. - debug!("dependency {dep_dep_node:?} was immediately green"); + // + // This path is extremely hot. We don't want to get the + // `dep_dep_node` unless it's necessary. Hence the + // `get_dep_dep_node` closure. + debug!("dependency {:?} was immediately green", get_dep_dep_node()); return Some(()); } Some(DepNodeColor::Red) => { @@ -891,12 +896,14 @@ impl DepGraphData { // compared to the previous compilation session. We cannot // mark the DepNode as green and also don't need to bother // with checking any of the other dependencies. - debug!("dependency {dep_dep_node:?} was immediately red"); + debug!("dependency {:?} was immediately red", get_dep_dep_node()); return None; } None => {} } + let dep_dep_node = &get_dep_dep_node(); + // We don't know the state of this dependency. If it isn't // an eval_always node, let's try to mark it green recursively. if !qcx.dep_context().is_eval_always(dep_dep_node.kind) { From 3a287e6034e1339198908cd7253fa414447c97d3 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 3 Oct 2025 16:58:03 +1000 Subject: [PATCH 129/226] Remove some unnecessary locals. They both have a single use. (They can't be united, though, because `self.colors` might change between the two `get` calls.) --- compiler/rustc_query_system/src/dep_graph/graph.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index eafb38bd25bb..5e62dab0722c 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -875,11 +875,9 @@ impl DepGraphData { parent_dep_node_index: SerializedDepNodeIndex, frame: Option<&MarkFrame<'_>>, ) -> Option<()> { - let dep_dep_node_color = self.colors.get(parent_dep_node_index); - let get_dep_dep_node = || self.previous.index_to_node(parent_dep_node_index); - match dep_dep_node_color { + match self.colors.get(parent_dep_node_index) { Some(DepNodeColor::Green(_)) => { // This dependency has been marked as green before, we are // still fine and can continue with checking the other @@ -929,9 +927,7 @@ impl DepGraphData { return None; } - let dep_dep_node_color = self.colors.get(parent_dep_node_index); - - match dep_dep_node_color { + match self.colors.get(parent_dep_node_index) { Some(DepNodeColor::Green(_)) => { debug!("managed to FORCE dependency {dep_dep_node:?} to green"); return Some(()); From 74caed9a4502c97c44d9c578e8ba50500da3d8fb Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Fri, 3 Oct 2025 15:15:45 +0200 Subject: [PATCH 130/226] cg_gcc.md: add missing quote --- .../rustc-dev-guide/src/tests/codegen-backend-tests/cg_gcc.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/tests/codegen-backend-tests/cg_gcc.md b/src/doc/rustc-dev-guide/src/tests/codegen-backend-tests/cg_gcc.md index 74dbfb7d31b3..aa337754186d 100644 --- a/src/doc/rustc-dev-guide/src/tests/codegen-backend-tests/cg_gcc.md +++ b/src/doc/rustc-dev-guide/src/tests/codegen-backend-tests/cg_gcc.md @@ -20,7 +20,7 @@ rust.codegen-backends = ["llvm", "gcc"] ``` If you don't want to change your `bootstrap.toml` file, you can alternatively run your `x` -commands with `--set rust.codegen-backends=["llvm", "gcc"]'`. For example: +commands with `--set 'rust.codegen-backends=["llvm", "gcc"]'`. For example: ```bash ./x build --set 'rust.codegen-backends=["llvm", "gcc"]' From 87d2891e82707c8c9730997f2f4771d4cebdbb22 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Mon, 29 Sep 2025 22:31:41 +0200 Subject: [PATCH 131/226] Cleanup `should_implement_trait` lint code --- .../src/methods/should_implement_trait.rs | 153 ++++++++---------- 1 file changed, 69 insertions(+), 84 deletions(-) diff --git a/clippy_lints/src/methods/should_implement_trait.rs b/clippy_lints/src/methods/should_implement_trait.rs index 5f13b8c7e91d..599ff696f6ae 100644 --- a/clippy_lints/src/methods/should_implement_trait.rs +++ b/clippy_lints/src/methods/should_implement_trait.rs @@ -1,8 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::{is_bool, sym}; use rustc_abi::ExternAbi; -use rustc_hir as hir; -use rustc_hir::{FnSig, ImplItem}; +use rustc_hir::{self as hir, FnRetTy, FnSig, GenericParamKind, ImplItem, LifetimeParamKind}; use rustc_lint::LateContext; use rustc_middle::ty::Ty; use rustc_span::edition::Edition::{self, Edition2015, Edition2021}; @@ -20,51 +19,43 @@ pub(super) fn check_impl_item<'tcx>( sig: &FnSig<'_>, ) { // if this impl block implements a trait, lint in trait definition instead - if !impl_implements_trait && cx.effective_visibilities.is_exported(impl_item.owner_id.def_id) { + if !impl_implements_trait && cx.effective_visibilities.is_exported(impl_item.owner_id.def_id) // check missing trait implementations - for method_config in &TRAIT_METHODS { - if impl_item.ident.name == method_config.method_name - && sig.decl.inputs.len() == method_config.param_count - && method_config.output_type.matches(&sig.decl.output) - // in case there is no first arg, since we already have checked the number of arguments - // it's should be always true - && first_arg_ty_opt - .is_none_or(|first_arg_ty| method_config.self_kind.matches(cx, self_ty, first_arg_ty)) - && fn_header_equals(method_config.fn_header, sig.header) - && method_config.lifetime_param_cond(impl_item) - && method_config.in_prelude_since <= cx.tcx.sess.edition() - { - span_lint_and_help( - cx, - SHOULD_IMPLEMENT_TRAIT, - impl_item.span, - format!( - "method `{}` can be confused for the standard trait method `{}::{}`", - method_config.method_name, method_config.trait_name, method_config.method_name - ), - None, - format!( - "consider implementing the trait `{}` or choosing a less ambiguous method name", - method_config.trait_name - ), - ); - } - } + && let Some(method_config) = TRAIT_METHODS.iter().find(|case| case.method_name == impl_item.ident.name) + && sig.decl.inputs.len() == method_config.param_count + && method_config.output_type.matches(&sig.decl.output) + // in case there is no first arg, since we already have checked the number of arguments + // it's should be always true + && first_arg_ty_opt + .is_none_or(|first_arg_ty| method_config.self_kind.matches(cx, self_ty, first_arg_ty)) + && sig.header.is_safe() + && !sig.header.is_const() + && !sig.header.is_async() + && sig.header.abi == ExternAbi::Rust + && method_config.lifetime_param_cond(impl_item) + && method_config.in_prelude_since <= cx.tcx.sess.edition() + { + span_lint_and_help( + cx, + SHOULD_IMPLEMENT_TRAIT, + impl_item.span, + format!( + "method `{}` can be confused for the standard trait method `{}::{}`", + method_config.method_name, method_config.trait_name, method_config.method_name + ), + None, + format!( + "consider implementing the trait `{}` or choosing a less ambiguous method name", + method_config.trait_name + ), + ); } } -const FN_HEADER: hir::FnHeader = hir::FnHeader { - safety: hir::HeaderSafety::Normal(hir::Safety::Safe), - constness: hir::Constness::NotConst, - asyncness: hir::IsAsync::NotAsync, - abi: ExternAbi::Rust, -}; - struct ShouldImplTraitCase { trait_name: &'static str, method_name: Symbol, param_count: usize, - fn_header: hir::FnHeader, // implicit self kind expected (none, self, &self, ...) self_kind: SelfKind, // checks against the output type @@ -73,13 +64,12 @@ struct ShouldImplTraitCase { lint_explicit_lifetime: bool, in_prelude_since: Edition, } + impl ShouldImplTraitCase { - #[expect(clippy::too_many_arguments)] const fn new( trait_name: &'static str, method_name: Symbol, param_count: usize, - fn_header: hir::FnHeader, self_kind: SelfKind, output_type: OutType, lint_explicit_lifetime: bool, @@ -89,7 +79,6 @@ impl ShouldImplTraitCase { trait_name, method_name, param_count, - fn_header, self_kind, output_type, lint_explicit_lifetime, @@ -102,8 +91,8 @@ impl ShouldImplTraitCase { || !impl_item.generics.params.iter().any(|p| { matches!( p.kind, - hir::GenericParamKind::Lifetime { - kind: hir::LifetimeParamKind::Explicit + GenericParamKind::Lifetime { + kind: LifetimeParamKind::Explicit } ) }) @@ -112,36 +101,36 @@ impl ShouldImplTraitCase { #[rustfmt::skip] const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [ - ShouldImplTraitCase::new("std::ops::Add", sym::add, 2, FN_HEADER, SelfKind::Value, OutType::Any, true, Edition2015), - ShouldImplTraitCase::new("std::convert::AsMut", sym::as_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true, Edition2015), - ShouldImplTraitCase::new("std::convert::AsRef", sym::as_ref, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true, Edition2015), - ShouldImplTraitCase::new("std::ops::BitAnd", sym::bitand, 2, FN_HEADER, SelfKind::Value, OutType::Any, true, Edition2015), - ShouldImplTraitCase::new("std::ops::BitOr", sym::bitor, 2, FN_HEADER, SelfKind::Value, OutType::Any, true, Edition2015), - ShouldImplTraitCase::new("std::ops::BitXor", sym::bitxor, 2, FN_HEADER, SelfKind::Value, OutType::Any, true, Edition2015), - ShouldImplTraitCase::new("std::borrow::Borrow", sym::borrow, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true, Edition2015), - ShouldImplTraitCase::new("std::borrow::BorrowMut", sym::borrow_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true, Edition2015), - ShouldImplTraitCase::new("std::clone::Clone", sym::clone, 1, FN_HEADER, SelfKind::Ref, OutType::Any, true, Edition2015), - ShouldImplTraitCase::new("std::cmp::Ord", sym::cmp, 2, FN_HEADER, SelfKind::Ref, OutType::Any, true, Edition2015), - ShouldImplTraitCase::new("std::default::Default", kw::Default, 0, FN_HEADER, SelfKind::No, OutType::Any, true, Edition2015), - ShouldImplTraitCase::new("std::ops::Deref", sym::deref, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true, Edition2015), - ShouldImplTraitCase::new("std::ops::DerefMut", sym::deref_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true, Edition2015), - ShouldImplTraitCase::new("std::ops::Div", sym::div, 2, FN_HEADER, SelfKind::Value, OutType::Any, true, Edition2015), - ShouldImplTraitCase::new("std::ops::Drop", sym::drop, 1, FN_HEADER, SelfKind::RefMut, OutType::Unit, true, Edition2015), - ShouldImplTraitCase::new("std::cmp::PartialEq", sym::eq, 2, FN_HEADER, SelfKind::Ref, OutType::Bool, true, Edition2015), - ShouldImplTraitCase::new("std::iter::FromIterator", sym::from_iter, 1, FN_HEADER, SelfKind::No, OutType::Any, true, Edition2021), - ShouldImplTraitCase::new("std::str::FromStr", sym::from_str, 1, FN_HEADER, SelfKind::No, OutType::Any, true, Edition2015), - ShouldImplTraitCase::new("std::hash::Hash", sym::hash, 2, FN_HEADER, SelfKind::Ref, OutType::Unit, true, Edition2015), - ShouldImplTraitCase::new("std::ops::Index", sym::index, 2, FN_HEADER, SelfKind::Ref, OutType::Ref, true, Edition2015), - ShouldImplTraitCase::new("std::ops::IndexMut", sym::index_mut, 2, FN_HEADER, SelfKind::RefMut, OutType::Ref, true, Edition2015), - ShouldImplTraitCase::new("std::iter::IntoIterator", sym::into_iter, 1, FN_HEADER, SelfKind::Value, OutType::Any, true, Edition2015), - ShouldImplTraitCase::new("std::ops::Mul", sym::mul, 2, FN_HEADER, SelfKind::Value, OutType::Any, true, Edition2015), - ShouldImplTraitCase::new("std::ops::Neg", sym::neg, 1, FN_HEADER, SelfKind::Value, OutType::Any, true, Edition2015), - ShouldImplTraitCase::new("std::iter::Iterator", sym::next, 1, FN_HEADER, SelfKind::RefMut, OutType::Any, false, Edition2015), - ShouldImplTraitCase::new("std::ops::Not", sym::not, 1, FN_HEADER, SelfKind::Value, OutType::Any, true, Edition2015), - ShouldImplTraitCase::new("std::ops::Rem", sym::rem, 2, FN_HEADER, SelfKind::Value, OutType::Any, true, Edition2015), - ShouldImplTraitCase::new("std::ops::Shl", sym::shl, 2, FN_HEADER, SelfKind::Value, OutType::Any, true, Edition2015), - ShouldImplTraitCase::new("std::ops::Shr", sym::shr, 2, FN_HEADER, SelfKind::Value, OutType::Any, true, Edition2015), - ShouldImplTraitCase::new("std::ops::Sub", sym::sub, 2, FN_HEADER, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Add", sym::add, 2, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::convert::AsMut", sym::as_mut, 1, SelfKind::RefMut, OutType::Ref, true, Edition2015), + ShouldImplTraitCase::new("std::convert::AsRef", sym::as_ref, 1, SelfKind::Ref, OutType::Ref, true, Edition2015), + ShouldImplTraitCase::new("std::ops::BitAnd", sym::bitand, 2, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::BitOr", sym::bitor, 2, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::BitXor", sym::bitxor, 2, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::borrow::Borrow", sym::borrow, 1, SelfKind::Ref, OutType::Ref, true, Edition2015), + ShouldImplTraitCase::new("std::borrow::BorrowMut", sym::borrow_mut, 1, SelfKind::RefMut, OutType::Ref, true, Edition2015), + ShouldImplTraitCase::new("std::clone::Clone", sym::clone, 1, SelfKind::Ref, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::cmp::Ord", sym::cmp, 2, SelfKind::Ref, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::default::Default", kw::Default, 0, SelfKind::No, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Deref", sym::deref, 1, SelfKind::Ref, OutType::Ref, true, Edition2015), + ShouldImplTraitCase::new("std::ops::DerefMut", sym::deref_mut, 1, SelfKind::RefMut, OutType::Ref, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Div", sym::div, 2, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Drop", sym::drop, 1, SelfKind::RefMut, OutType::Unit, true, Edition2015), + ShouldImplTraitCase::new("std::cmp::PartialEq", sym::eq, 2, SelfKind::Ref, OutType::Bool, true, Edition2015), + ShouldImplTraitCase::new("std::iter::FromIterator", sym::from_iter, 1, SelfKind::No, OutType::Any, true, Edition2021), + ShouldImplTraitCase::new("std::str::FromStr", sym::from_str, 1, SelfKind::No, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::hash::Hash", sym::hash, 2, SelfKind::Ref, OutType::Unit, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Index", sym::index, 2, SelfKind::Ref, OutType::Ref, true, Edition2015), + ShouldImplTraitCase::new("std::ops::IndexMut", sym::index_mut, 2, SelfKind::RefMut, OutType::Ref, true, Edition2015), + ShouldImplTraitCase::new("std::iter::IntoIterator", sym::into_iter, 1, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Mul", sym::mul, 2, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Neg", sym::neg, 1, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::iter::Iterator", sym::next, 1, SelfKind::RefMut, OutType::Any, false, Edition2015), + ShouldImplTraitCase::new("std::ops::Not", sym::not, 1, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Rem", sym::rem, 2, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Shl", sym::shl, 2, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Shr", sym::shr, 2, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Sub", sym::sub, 2, SelfKind::Value, OutType::Any, true, Edition2015), ]; #[derive(Clone, Copy)] @@ -153,19 +142,15 @@ enum OutType { } impl OutType { - fn matches(self, ty: &hir::FnRetTy<'_>) -> bool { + fn matches(self, ty: &FnRetTy<'_>) -> bool { let is_unit = |ty: &hir::Ty<'_>| matches!(ty.kind, hir::TyKind::Tup(&[])); match (self, ty) { - (Self::Unit, &hir::FnRetTy::DefaultReturn(_)) => true, - (Self::Unit, &hir::FnRetTy::Return(ty)) if is_unit(ty) => true, - (Self::Bool, &hir::FnRetTy::Return(ty)) if is_bool(ty) => true, - (Self::Any, &hir::FnRetTy::Return(ty)) if !is_unit(ty) => true, - (Self::Ref, &hir::FnRetTy::Return(ty)) => matches!(ty.kind, hir::TyKind::Ref(_, _)), + (Self::Unit, &FnRetTy::DefaultReturn(_)) => true, + (Self::Unit, &FnRetTy::Return(ty)) if is_unit(ty) => true, + (Self::Bool, &FnRetTy::Return(ty)) if is_bool(ty) => true, + (Self::Any, &FnRetTy::Return(ty)) if !is_unit(ty) => true, + (Self::Ref, &FnRetTy::Return(ty)) => matches!(ty.kind, hir::TyKind::Ref(_, _)), _ => false, } } } - -fn fn_header_equals(expected: hir::FnHeader, actual: hir::FnHeader) -> bool { - expected.constness == actual.constness && expected.safety == actual.safety && expected.asyncness == actual.asyncness -} From 1485e7887b6407ce6326e044d9b3983364db52ad Mon Sep 17 00:00:00 2001 From: Fletcher Porter Date: Wed, 18 Jun 2025 15:04:54 +0300 Subject: [PATCH 132/226] Document fully-qualified syntax in `as`' keyword doc --- library/std/src/keyword_docs.rs | 35 ++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/library/std/src/keyword_docs.rs b/library/std/src/keyword_docs.rs index 7ff4af8ede84..dc0d11b07a9f 100644 --- a/library/std/src/keyword_docs.rs +++ b/library/std/src/keyword_docs.rs @@ -1,6 +1,8 @@ #[doc(keyword = "as")] // -/// Cast between types, or rename an import. +/// Cast between types, rename an import, or qualify paths to associated items. +/// +/// # Type casting /// /// `as` is most commonly used to turn primitive types into other primitive types, but it has other /// uses that include turning pointers into addresses, addresses into pointers, and pointers into @@ -30,6 +32,8 @@ /// `as *mut _` though the [`cast`][const-cast] method is recommended over `as *const _` and it is /// [the same][mut-cast] for `as *mut _`: those methods make the intent clearer. /// +/// # Renaming imports +/// /// `as` is also used to rename imports in [`use`] and [`extern crate`][`crate`] statements: /// /// ``` @@ -37,9 +41,34 @@ /// use std::{mem as memory, net as network}; /// // Now you can use the names `memory` and `network` to refer to `std::mem` and `std::net`. /// ``` -/// For more information on what `as` is capable of, see the [Reference]. /// -/// [Reference]: ../reference/expressions/operator-expr.html#type-cast-expressions +/// # Qualifying paths +/// +/// You'll also find with `From` and `Into`, and indeed all traits, that `as` is used for the +/// _fully qualified path_, a means of disambiguating associated items, i.e. functions, +/// constants, and types. For example, if you have a type which implements two traits with identical +/// method names (e.g. `Into::::into` and `Into::::into`), you can clarify which method +/// you'll use with `>::into(my_thing)`[^as-use-from]. This is quite verbose, +/// but fortunately, Rust's type inference usually saves you from needing this, although it is +/// occasionally necessary, especially with methods that return a generic type like `Into::into` or +/// methods that don't take `self`. It's more common to use in macros where it can provide necessary +/// hygiene. +/// +/// [^as-use-from]: You should probably never use this syntax with `Into` and instead write +/// `T::from(my_thing)`. It just happens that there aren't any great examples for this syntax in +/// the standard library. Also, at time of writing, the compiler tends to suggest fully-qualified +/// paths to fix ambiguous `Into::into` calls, so the example should hopefully be familiar. +/// +/// # Further reading +/// +/// For more information on what `as` is capable of, see the Reference on [type cast expressions], +/// [renaming imported entities], [renaming `extern` crates] +/// and [qualified paths]. +/// +/// [type cast expressions]: ../reference/expressions/operator-expr.html#type-cast-expressions +/// [renaming imported entities]: https://doc.rust-lang.org/reference/items/use-declarations.html#as-renames +/// [renaming `extern` crates]: https://doc.rust-lang.org/reference/items/extern-crates.html#r-items.extern-crate.as +/// [qualified paths]: ../reference/paths.html#qualified-paths /// [`crate`]: keyword.crate.html /// [`use`]: keyword.use.html /// [const-cast]: pointer::cast From e3f104608c0ad26e80b1ebedef2ab8a748189e52 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 3 Oct 2025 10:32:11 -0400 Subject: [PATCH 133/226] Don't normalize higher-ranked assumptions if they're not used --- .../src/traits/select/confirmation.rs | 31 ++++++----- .../higher-ranked-normalize-assumptions-2.rs | 38 ++++++++++++++ .../higher-ranked-normalize-assumptions.rs | 51 +++++++++++++++++++ 3 files changed, 107 insertions(+), 13 deletions(-) create mode 100644 tests/ui/async-await/higher-ranked-normalize-assumptions-2.rs create mode 100644 tests/ui/async-await/higher-ranked-normalize-assumptions.rs diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 7ad65a1df8e9..708a53f6c650 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -423,19 +423,24 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { constituents.types, ); - // FIXME(coroutine_clone): We could uplift this into `collect_predicates_for_types` - // and do this for `Copy`/`Clone` too, but that's feature-gated so it doesn't really - // matter yet. - for assumption in constituents.assumptions { - let assumption = normalize_with_depth_to( - self, - obligation.param_env, - cause.clone(), - obligation.recursion_depth + 1, - assumption, - &mut obligations, - ); - self.infcx.register_region_assumption(assumption); + // Only normalize these goals if `-Zhigher-ranked-assumptions` is enabled, since + // we don't want to cause ourselves to do extra work if we're not even able to + // take advantage of these assumption clauses. + if self.tcx().sess.opts.unstable_opts.higher_ranked_assumptions { + // FIXME(coroutine_clone): We could uplift this into `collect_predicates_for_types` + // and do this for `Copy`/`Clone` too, but that's feature-gated so it doesn't really + // matter yet. + for assumption in constituents.assumptions { + let assumption = normalize_with_depth_to( + self, + obligation.param_env, + cause.clone(), + obligation.recursion_depth + 1, + assumption, + &mut obligations, + ); + self.infcx.register_region_assumption(assumption); + } } Ok(obligations) diff --git a/tests/ui/async-await/higher-ranked-normalize-assumptions-2.rs b/tests/ui/async-await/higher-ranked-normalize-assumptions-2.rs new file mode 100644 index 000000000000..410d4e503b7c --- /dev/null +++ b/tests/ui/async-await/higher-ranked-normalize-assumptions-2.rs @@ -0,0 +1,38 @@ +//@ revisions: stock hr +//@[hr] compile-flags: -Zhigher-ranked-assumptions +//@ edition: 2024 +//@ check-pass + +// Test that we don't normalize the higher-ranked assumptions of an auto trait goal +// unless we have `-Zhigher-ranked-assumptions`, since obligations that result from +// this normalization may lead to higher-ranked lifetime errors when the flag is not +// enabled. + +// Regression test for . + +pub fn a() -> impl Future + Send { + async { + let queries = core::iter::empty().map(Thing::f); + b(queries).await; + } +} + +async fn b(queries: impl IntoIterator) { + c(queries).await; +} + +fn c<'a, I>(_queries: I) -> impl Future +where + I: IntoIterator, + I::IntoIter: 'a, +{ + async {} +} + +pub struct Thing<'a>(pub &'a ()); + +impl Thing<'_> { + fn f(_: &Self) {} +} + +fn main() {} diff --git a/tests/ui/async-await/higher-ranked-normalize-assumptions.rs b/tests/ui/async-await/higher-ranked-normalize-assumptions.rs new file mode 100644 index 000000000000..ec9cf3a15221 --- /dev/null +++ b/tests/ui/async-await/higher-ranked-normalize-assumptions.rs @@ -0,0 +1,51 @@ +//@ revisions: stock hr +//@[hr] compile-flags: -Zhigher-ranked-assumptions +//@ edition: 2024 +//@ check-pass + +// Test that we don't normalize the higher-ranked assumptions of an auto trait goal +// unless we have `-Zhigher-ranked-assumptions`, since obligations that result from +// this normalization may lead to higher-ranked lifetime errors when the flag is not +// enabled. + +// Regression test for . + +pub trait Service { + type Response; +} + +impl Service for T +where + T: FnMut() -> R, + R: 'static, +{ + type Response = R; +} + +async fn serve(_: C) +where + C: Service, + C::Response: 'static, +{ + connect::().await; +} + +async fn connect() +where + C: Service, + C::Response: 'static, +{ +} + +fn repro() -> impl Send { + async { + let server = || do_something(); + serve(server).await; + } +} + +fn do_something() -> Box { + unimplemented!() +} + +fn main() {} From e914a1a6e026d2c9db43300735a75ba25746e14c Mon Sep 17 00:00:00 2001 From: Urgau Date: Fri, 3 Oct 2025 11:57:04 +0200 Subject: [PATCH 134/226] Respect `--error-format` in `rustdoc --test` --- src/librustdoc/doctest.rs | 1 + tests/rustdoc-ui/doctest/check-attr-test.rs | 12 ++++++++++ .../rustdoc-ui/doctest/check-attr-test.stderr | 24 +++++++++---------- .../failed-doctest-extra-semicolon-on-item.rs | 1 + .../doctest/main-alongside-stmts.rs | 2 ++ .../doctest/main-alongside-stmts.stderr | 8 +++---- .../doctest/main-alongside-stmts.stdout | 4 ++-- .../doctest/standalone-warning-2024.rs | 2 ++ .../doctest/standalone-warning-2024.stderr | 12 +++++----- .../rustdoc-ui/doctest/test-compile-fail1.rs | 1 + .../rustdoc-ui/doctest/test-compile-fail2.rs | 1 + .../rustdoc-ui/doctest/test-compile-fail3.rs | 1 + .../doctest/test-compile-fail3.stderr | 5 ++-- .../doctest/warn-main-not-called.rs | 2 ++ .../doctest/warn-main-not-called.stderr | 4 ++-- 15 files changed, 52 insertions(+), 28 deletions(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 38a0c925d2a4..27761511038e 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -174,6 +174,7 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, input: Input, options: RustdocOptions crate_name: options.crate_name.clone(), remap_path_prefix: options.remap_path_prefix.clone(), unstable_opts: options.unstable_opts.clone(), + error_format: options.error_format.clone(), ..config::Options::default() }; diff --git a/tests/rustdoc-ui/doctest/check-attr-test.rs b/tests/rustdoc-ui/doctest/check-attr-test.rs index 81281db624b3..d69dae63860e 100644 --- a/tests/rustdoc-ui/doctest/check-attr-test.rs +++ b/tests/rustdoc-ui/doctest/check-attr-test.rs @@ -2,6 +2,9 @@ #![deny(rustdoc::invalid_codeblock_attributes)] +//~vvv ERROR unknown attribute `compile-fail` +//~| ERROR unknown attribute `compilefail` +//~| ERROR unknown attribute `comPile_fail` /// foo /// /// ```compile-fail,compilefail,comPile_fail @@ -9,6 +12,9 @@ /// ``` pub fn foo() {} +//~vvv ERROR unknown attribute `should-panic` +//~| ERROR unknown attribute `shouldpanic` +//~| ERROR unknown attribute `shOuld_panic` /// bar /// /// ```should-panic,shouldpanic,shOuld_panic @@ -16,6 +22,9 @@ pub fn foo() {} /// ``` pub fn bar() {} +//~vvv ERROR unknown attribute `no-run` +//~| ERROR unknown attribute `norun` +//~| ERROR unknown attribute `nO_run` /// foobar /// /// ```no-run,norun,nO_run @@ -23,6 +32,9 @@ pub fn bar() {} /// ``` pub fn foobar() {} +//~vvv ERROR unknown attribute `test-harness` +//~| ERROR unknown attribute `testharness` +//~| ERROR unknown attribute `tesT_harness` /// b /// /// ```test-harness,testharness,tesT_harness diff --git a/tests/rustdoc-ui/doctest/check-attr-test.stderr b/tests/rustdoc-ui/doctest/check-attr-test.stderr index 4cf93033ae8a..1fc7ab592de0 100644 --- a/tests/rustdoc-ui/doctest/check-attr-test.stderr +++ b/tests/rustdoc-ui/doctest/check-attr-test.stderr @@ -1,5 +1,5 @@ error: unknown attribute `compile-fail` - --> $DIR/check-attr-test.rs:5:1 + --> $DIR/check-attr-test.rs:8:1 | LL | / /// foo LL | | /// @@ -17,7 +17,7 @@ LL | #![deny(rustdoc::invalid_codeblock_attributes)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: unknown attribute `compilefail` - --> $DIR/check-attr-test.rs:5:1 + --> $DIR/check-attr-test.rs:8:1 | LL | / /// foo LL | | /// @@ -30,7 +30,7 @@ LL | | /// ``` = help: this code block may be skipped during testing, because unknown attributes are treated as markers for code samples written in other programming languages, unless it is also explicitly marked as `rust` error: unknown attribute `comPile_fail` - --> $DIR/check-attr-test.rs:5:1 + --> $DIR/check-attr-test.rs:8:1 | LL | / /// foo LL | | /// @@ -43,7 +43,7 @@ LL | | /// ``` = help: this code block may be skipped during testing, because unknown attributes are treated as markers for code samples written in other programming languages, unless it is also explicitly marked as `rust` error: unknown attribute `should-panic` - --> $DIR/check-attr-test.rs:12:1 + --> $DIR/check-attr-test.rs:18:1 | LL | / /// bar LL | | /// @@ -56,7 +56,7 @@ LL | | /// ``` = help: this code block may be skipped during testing, because unknown attributes are treated as markers for code samples written in other programming languages, unless it is also explicitly marked as `rust` error: unknown attribute `shouldpanic` - --> $DIR/check-attr-test.rs:12:1 + --> $DIR/check-attr-test.rs:18:1 | LL | / /// bar LL | | /// @@ -69,7 +69,7 @@ LL | | /// ``` = help: this code block may be skipped during testing, because unknown attributes are treated as markers for code samples written in other programming languages, unless it is also explicitly marked as `rust` error: unknown attribute `shOuld_panic` - --> $DIR/check-attr-test.rs:12:1 + --> $DIR/check-attr-test.rs:18:1 | LL | / /// bar LL | | /// @@ -82,7 +82,7 @@ LL | | /// ``` = help: this code block may be skipped during testing, because unknown attributes are treated as markers for code samples written in other programming languages, unless it is also explicitly marked as `rust` error: unknown attribute `no-run` - --> $DIR/check-attr-test.rs:19:1 + --> $DIR/check-attr-test.rs:28:1 | LL | / /// foobar LL | | /// @@ -95,7 +95,7 @@ LL | | /// ``` = help: this code block may be skipped during testing, because unknown attributes are treated as markers for code samples written in other programming languages, unless it is also explicitly marked as `rust` error: unknown attribute `norun` - --> $DIR/check-attr-test.rs:19:1 + --> $DIR/check-attr-test.rs:28:1 | LL | / /// foobar LL | | /// @@ -108,7 +108,7 @@ LL | | /// ``` = help: this code block may be skipped during testing, because unknown attributes are treated as markers for code samples written in other programming languages, unless it is also explicitly marked as `rust` error: unknown attribute `nO_run` - --> $DIR/check-attr-test.rs:19:1 + --> $DIR/check-attr-test.rs:28:1 | LL | / /// foobar LL | | /// @@ -121,7 +121,7 @@ LL | | /// ``` = help: this code block may be skipped during testing, because unknown attributes are treated as markers for code samples written in other programming languages, unless it is also explicitly marked as `rust` error: unknown attribute `test-harness` - --> $DIR/check-attr-test.rs:26:1 + --> $DIR/check-attr-test.rs:38:1 | LL | / /// b LL | | /// @@ -134,7 +134,7 @@ LL | | /// ``` = help: this code block may be skipped during testing, because unknown attributes are treated as markers for code samples written in other programming languages, unless it is also explicitly marked as `rust` error: unknown attribute `testharness` - --> $DIR/check-attr-test.rs:26:1 + --> $DIR/check-attr-test.rs:38:1 | LL | / /// b LL | | /// @@ -147,7 +147,7 @@ LL | | /// ``` = help: this code block may be skipped during testing, because unknown attributes are treated as markers for code samples written in other programming languages, unless it is also explicitly marked as `rust` error: unknown attribute `tesT_harness` - --> $DIR/check-attr-test.rs:26:1 + --> $DIR/check-attr-test.rs:38:1 | LL | / /// b LL | | /// diff --git a/tests/rustdoc-ui/doctest/failed-doctest-extra-semicolon-on-item.rs b/tests/rustdoc-ui/doctest/failed-doctest-extra-semicolon-on-item.rs index ca5dd7874678..05e4a348d119 100644 --- a/tests/rustdoc-ui/doctest/failed-doctest-extra-semicolon-on-item.rs +++ b/tests/rustdoc-ui/doctest/failed-doctest-extra-semicolon-on-item.rs @@ -9,6 +9,7 @@ /// /// /// ```rust +//~^ WARN the `main` function of this doctest won't be run /// struct S {}; /// /// fn main() { diff --git a/tests/rustdoc-ui/doctest/main-alongside-stmts.rs b/tests/rustdoc-ui/doctest/main-alongside-stmts.rs index 5965f928cdd1..595de1339329 100644 --- a/tests/rustdoc-ui/doctest/main-alongside-stmts.rs +++ b/tests/rustdoc-ui/doctest/main-alongside-stmts.rs @@ -14,6 +14,7 @@ //@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" //@ check-pass +//~v WARN the `main` function of this doctest won't be run //! ``` //! # if cfg!(miri) { return; } //! use std::ops::Deref; @@ -22,6 +23,7 @@ //! assert!(false); //! } //! ``` +//~v WARN the `main` function of this doctest won't be run //! //! ``` //! let x = 2; diff --git a/tests/rustdoc-ui/doctest/main-alongside-stmts.stderr b/tests/rustdoc-ui/doctest/main-alongside-stmts.stderr index 3f347d96ab06..b7a5421f8f73 100644 --- a/tests/rustdoc-ui/doctest/main-alongside-stmts.stderr +++ b/tests/rustdoc-ui/doctest/main-alongside-stmts.stderr @@ -1,14 +1,14 @@ warning: the `main` function of this doctest won't be run as it contains expressions at the top level, meaning that the whole doctest code will be wrapped in a function - --> $DIR/main-alongside-stmts.rs:17:1 + --> $DIR/main-alongside-stmts.rs:18:1 | LL | //! ``` | ^^^^^^^ warning: the `main` function of this doctest won't be run as it contains expressions at the top level, meaning that the whole doctest code will be wrapped in a function - --> $DIR/main-alongside-stmts.rs:26:1 + --> $DIR/main-alongside-stmts.rs:27:1 | -LL | //! ``` - | ^^^^^^^ +LL | //! + | ^^^ warning: 2 warnings emitted diff --git a/tests/rustdoc-ui/doctest/main-alongside-stmts.stdout b/tests/rustdoc-ui/doctest/main-alongside-stmts.stdout index 9b9a3fe8a68f..bebaeb49c5a9 100644 --- a/tests/rustdoc-ui/doctest/main-alongside-stmts.stdout +++ b/tests/rustdoc-ui/doctest/main-alongside-stmts.stdout @@ -1,7 +1,7 @@ running 2 tests -test $DIR/main-alongside-stmts.rs - (line 17) ... ok -test $DIR/main-alongside-stmts.rs - (line 26) ... ok +test $DIR/main-alongside-stmts.rs - (line 18) ... ok +test $DIR/main-alongside-stmts.rs - (line 27) ... ok test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME diff --git a/tests/rustdoc-ui/doctest/standalone-warning-2024.rs b/tests/rustdoc-ui/doctest/standalone-warning-2024.rs index c53a8b48749c..3bb0083d849f 100644 --- a/tests/rustdoc-ui/doctest/standalone-warning-2024.rs +++ b/tests/rustdoc-ui/doctest/standalone-warning-2024.rs @@ -9,6 +9,8 @@ #![deny(warnings)] //! ```standalone +//~^ ERROR unknown attribute `standalone` +//~| ERROR unknown attribute `standalone-crate` //! bla //! ``` //! diff --git a/tests/rustdoc-ui/doctest/standalone-warning-2024.stderr b/tests/rustdoc-ui/doctest/standalone-warning-2024.stderr index 1af2ded58e8b..db0d53a204ce 100644 --- a/tests/rustdoc-ui/doctest/standalone-warning-2024.stderr +++ b/tests/rustdoc-ui/doctest/standalone-warning-2024.stderr @@ -2,10 +2,10 @@ error: unknown attribute `standalone` --> $DIR/standalone-warning-2024.rs:11:1 | LL | / //! ```standalone +LL | | +LL | | LL | | //! bla -LL | | //! ``` -LL | | //! -LL | | //! ```standalone-crate +... | LL | | //! bla LL | | //! ``` | |_______^ @@ -23,10 +23,10 @@ error: unknown attribute `standalone-crate` --> $DIR/standalone-warning-2024.rs:11:1 | LL | / //! ```standalone +LL | | +LL | | LL | | //! bla -LL | | //! ``` -LL | | //! -LL | | //! ```standalone-crate +... | LL | | //! bla LL | | //! ``` | |_______^ diff --git a/tests/rustdoc-ui/doctest/test-compile-fail1.rs b/tests/rustdoc-ui/doctest/test-compile-fail1.rs index 278f01f4c838..c692c4c61bcc 100644 --- a/tests/rustdoc-ui/doctest/test-compile-fail1.rs +++ b/tests/rustdoc-ui/doctest/test-compile-fail1.rs @@ -6,3 +6,4 @@ pub fn f() {} pub fn f() {} +//~^ ERROR the name `f` is defined multiple times diff --git a/tests/rustdoc-ui/doctest/test-compile-fail2.rs b/tests/rustdoc-ui/doctest/test-compile-fail2.rs index 7432cc9f8263..8239440262d8 100644 --- a/tests/rustdoc-ui/doctest/test-compile-fail2.rs +++ b/tests/rustdoc-ui/doctest/test-compile-fail2.rs @@ -1,3 +1,4 @@ //@ compile-flags:--test fail +//~^ ERROR diff --git a/tests/rustdoc-ui/doctest/test-compile-fail3.rs b/tests/rustdoc-ui/doctest/test-compile-fail3.rs index a2486d9dc6f2..272ba95396c6 100644 --- a/tests/rustdoc-ui/doctest/test-compile-fail3.rs +++ b/tests/rustdoc-ui/doctest/test-compile-fail3.rs @@ -1,3 +1,4 @@ //@ compile-flags:--test "fail +//~^ ERROR diff --git a/tests/rustdoc-ui/doctest/test-compile-fail3.stderr b/tests/rustdoc-ui/doctest/test-compile-fail3.stderr index 9f90a428d480..8061097e73ac 100644 --- a/tests/rustdoc-ui/doctest/test-compile-fail3.stderr +++ b/tests/rustdoc-ui/doctest/test-compile-fail3.stderr @@ -1,8 +1,9 @@ error[E0765]: unterminated double quote string --> $DIR/test-compile-fail3.rs:3:1 | -LL | "fail - | ^^^^^ +LL | / "fail +LL | | + | |___________^ error: aborting due to 1 previous error diff --git a/tests/rustdoc-ui/doctest/warn-main-not-called.rs b/tests/rustdoc-ui/doctest/warn-main-not-called.rs index 25d92e9cee9f..ec762486d5d4 100644 --- a/tests/rustdoc-ui/doctest/warn-main-not-called.rs +++ b/tests/rustdoc-ui/doctest/warn-main-not-called.rs @@ -8,6 +8,7 @@ // won't be called. //! ``` +//~^ WARN the `main` function of this doctest won't be run //! macro_rules! bla { //! ($($x:tt)*) => {} //! } @@ -17,6 +18,7 @@ //! ``` //! //! ``` +//~^^ WARN the `main` function of this doctest won't be run //! let x = 12; //! fn main() {} //! ``` diff --git a/tests/rustdoc-ui/doctest/warn-main-not-called.stderr b/tests/rustdoc-ui/doctest/warn-main-not-called.stderr index b5b7bc57644a..5feca6f9175f 100644 --- a/tests/rustdoc-ui/doctest/warn-main-not-called.stderr +++ b/tests/rustdoc-ui/doctest/warn-main-not-called.stderr @@ -7,8 +7,8 @@ LL | //! ``` warning: the `main` function of this doctest won't be run as it contains expressions at the top level, meaning that the whole doctest code will be wrapped in a function --> $DIR/warn-main-not-called.rs:19:1 | -LL | //! ``` - | ^^^^^^^ +LL | //! + | ^^^ warning: 2 warnings emitted From c1443e2591c284f6ac8372d15c88f8d6c9470e4c Mon Sep 17 00:00:00 2001 From: Urgau Date: Fri, 3 Oct 2025 12:02:35 +0200 Subject: [PATCH 135/226] Add regression test for `-Zcrate-attr` in `rustdoc --test` --- .../rustdoc-ui/doctest/unstable-opts-143930.rs | 14 ++++++++++++++ .../doctest/unstable-opts-143930.stdout | 5 +++++ .../unstable-opts-147276.crate_attr.stdout | 5 +++++ .../doctest/unstable-opts-147276.normal.stderr | 13 +++++++++++++ .../rustdoc-ui/doctest/unstable-opts-147276.rs | 17 +++++++++++++++++ 5 files changed, 54 insertions(+) create mode 100644 tests/rustdoc-ui/doctest/unstable-opts-143930.rs create mode 100644 tests/rustdoc-ui/doctest/unstable-opts-143930.stdout create mode 100644 tests/rustdoc-ui/doctest/unstable-opts-147276.crate_attr.stdout create mode 100644 tests/rustdoc-ui/doctest/unstable-opts-147276.normal.stderr create mode 100644 tests/rustdoc-ui/doctest/unstable-opts-147276.rs diff --git a/tests/rustdoc-ui/doctest/unstable-opts-143930.rs b/tests/rustdoc-ui/doctest/unstable-opts-143930.rs new file mode 100644 index 000000000000..30c47f5b7e9e --- /dev/null +++ b/tests/rustdoc-ui/doctest/unstable-opts-143930.rs @@ -0,0 +1,14 @@ +// This test verifies that unstable options like `-Zcrate-attr` are respected when `--test` is +// passed. +// +// +// +// NOTE: If any of these command line arguments or features get stabilized, please replace with +// another unstable one. + +//@ check-pass +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ compile-flags: --test -Zcrate-attr=feature(register_tool) -Zcrate-attr=register_tool(rapx) + +#[rapx::tag] +fn f() {} diff --git a/tests/rustdoc-ui/doctest/unstable-opts-143930.stdout b/tests/rustdoc-ui/doctest/unstable-opts-143930.stdout new file mode 100644 index 000000000000..7326c0a25a06 --- /dev/null +++ b/tests/rustdoc-ui/doctest/unstable-opts-143930.stdout @@ -0,0 +1,5 @@ + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-ui/doctest/unstable-opts-147276.crate_attr.stdout b/tests/rustdoc-ui/doctest/unstable-opts-147276.crate_attr.stdout new file mode 100644 index 000000000000..7326c0a25a06 --- /dev/null +++ b/tests/rustdoc-ui/doctest/unstable-opts-147276.crate_attr.stdout @@ -0,0 +1,5 @@ + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-ui/doctest/unstable-opts-147276.normal.stderr b/tests/rustdoc-ui/doctest/unstable-opts-147276.normal.stderr new file mode 100644 index 000000000000..eebf4f307f12 --- /dev/null +++ b/tests/rustdoc-ui/doctest/unstable-opts-147276.normal.stderr @@ -0,0 +1,13 @@ +error[E0658]: `#[used(linker)]` is currently unstable + --> $DIR/unstable-opts-147276.rs:15:1 + | +LL | #[used(linker)] + | ^^^^^^^^^^^^^^^ + | + = note: see issue #93798 for more information + = help: add `#![feature(used_with_arg)]` 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 + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/rustdoc-ui/doctest/unstable-opts-147276.rs b/tests/rustdoc-ui/doctest/unstable-opts-147276.rs new file mode 100644 index 000000000000..64cafcaaff4e --- /dev/null +++ b/tests/rustdoc-ui/doctest/unstable-opts-147276.rs @@ -0,0 +1,17 @@ +// This test verifies that unstable options like `-Zcrate-attr` are respected when `--test` is +// passed. +// +// +// +// NOTE: If any of these command line arguments or features get stabilized, please replace with +// another unstable one. + +//@ revisions: normal crate_attr +//@ compile-flags: --test +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@[crate_attr] check-pass +//@[crate_attr] compile-flags: -Zcrate-attr=feature(used_with_arg) + +#[used(linker)] +//[normal]~^ ERROR `#[used(linker)]` is currently unstable +static REPRO: isize = 1; From 3f9c493da1fa46ff0ef6d23e60a37c89ef3f5933 Mon Sep 17 00:00:00 2001 From: Scott Mabin Date: Fri, 3 Oct 2025 16:10:26 +0100 Subject: [PATCH 136/226] Add xtensa arch to object file creation --- compiler/rustc_target/src/spec/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 4a82a8bd888e..2f7109de804a 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -3182,6 +3182,7 @@ impl Target { "avr" => (Architecture::Avr, None), "msp430" => (Architecture::Msp430, None), "hexagon" => (Architecture::Hexagon, None), + "xtensa" => (Architecture::Xtensa, None), "bpf" => (Architecture::Bpf, None), "loongarch32" => (Architecture::LoongArch32, None), "loongarch64" => (Architecture::LoongArch64, None), From e43a0b6bdbdf5a4bab5be6b925698971c55dae1a Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Fri, 3 Oct 2025 11:58:30 -0400 Subject: [PATCH 137/226] Update cargo submodule --- src/tools/cargo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/cargo b/src/tools/cargo index f2932725b045..2394ea6cea8b 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit f2932725b045d361ff5f18ba02b1409dd1f44e71 +Subproject commit 2394ea6cea8b26d717aa67eb1663a2dbf2d26078 From 58889457cac8225e6bae4855ca1911dd0a378f65 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 4 Aug 2025 16:08:14 +0200 Subject: [PATCH 138/226] Display merged doctests times as expected depending on the format specified --- src/librustdoc/doctest.rs | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 9499258f983a..5ea3ede78bc4 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -12,7 +12,7 @@ use std::process::{self, Command, Stdio}; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex}; use std::time::{Duration, Instant}; -use std::{fmt, panic, str}; +use std::{panic, str}; pub(crate) use make::{BuildDocTestBuilder, DocTestBuilder}; pub(crate) use markdown::test as test_markdown; @@ -60,24 +60,15 @@ impl MergedDoctestTimes { self.added_compilation_times += 1; } - fn display_times(&self) { + /// Returns `(total_time, compilation_time)`. + fn times_in_secs(&self) -> Option<(f64, f64)> { // If no merged doctest was compiled, then there is nothing to display since the numbers // displayed by `libtest` for standalone tests are already accurate (they include both // compilation and runtime). - if self.added_compilation_times > 0 { - println!("{self}"); + if self.added_compilation_times == 0 { + return None; } - } -} - -impl fmt::Display for MergedDoctestTimes { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "all doctests ran in {:.2}s; merged doctests compilation took {:.2}s", - self.total_time.elapsed().as_secs_f64(), - self.compilation_time.as_secs_f64(), - ) + Some((self.total_time.elapsed().as_secs_f64(), self.compilation_time.as_secs_f64())) } } @@ -400,15 +391,20 @@ pub(crate) fn run_tests( if ran_edition_tests == 0 || !standalone_tests.is_empty() { standalone_tests.sort_by(|a, b| a.desc.name.as_slice().cmp(b.desc.name.as_slice())); test::test_main_with_exit_callback(&test_args, standalone_tests, None, || { + let times = times.times_in_secs(); // We ensure temp dir destructor is called. std::mem::drop(temp_dir.take()); - times.display_times(); + if let Some((total_time, compilation_time)) = times { + test::print_merged_doctests_times(&test_args, total_time, compilation_time); + } }); } else { // If the first condition branch exited successfully, `test_main_with_exit_callback` will // not exit the process. So to prevent displaying the times twice, we put it behind an // `else` condition. - times.display_times(); + if let Some((total_time, compilation_time)) = times.times_in_secs() { + test::print_merged_doctests_times(&test_args, total_time, compilation_time); + } } // We ensure temp dir destructor is called. std::mem::drop(temp_dir); From a9ab29cdb45005d69738c3a7cbec4c5cd0fceb73 Mon Sep 17 00:00:00 2001 From: Jeremy Smart Date: Fri, 12 Sep 2025 13:10:01 -0400 Subject: [PATCH 139/226] add mem::conjure_zst --- library/core/src/mem/mod.rs | 58 ++++++++++++++++++++++++++ tests/ui/consts/std/conjure_zst.rs | 10 +++++ tests/ui/consts/std/conjure_zst.stderr | 12 ++++++ 3 files changed, 80 insertions(+) create mode 100644 tests/ui/consts/std/conjure_zst.rs create mode 100644 tests/ui/consts/std/conjure_zst.stderr diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index db4c8e9e5515..c484551187cc 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -7,6 +7,7 @@ use crate::alloc::Layout; use crate::marker::DiscriminantKind; +use crate::panic::const_assert; use crate::{clone, cmp, fmt, hash, intrinsics, ptr}; mod manually_drop; @@ -1407,3 +1408,60 @@ pub macro offset_of($Container:ty, $($fields:expr)+ $(,)?) { // The `{}` is for better error messages {builtin # offset_of($Container, $($fields)+)} } + +/// Create a fresh instance of the inhabited ZST type `T`. +/// +/// Prefer this to [`zeroed`] or [`uninitialized`] or [`transmute_copy`] +/// in places where you know that `T` is zero-sized, but don't have a bound +/// (such as [`Default`]) that would allow you to instantiate it using safe code. +/// +/// If you're not sure whether `T` is an inhabited ZST, then you should be +/// using [`MaybeUninit`], not this function. +/// +/// # Panics +/// +/// If `size_of::() != 0`. +/// +/// # Safety +/// +/// - `T` must be *[inhabited]*, i.e. possible to construct. This means that types +/// like zero-variant enums and [`!`] are unsound to conjure. +/// - You must use the value only in ways which do not violate any *safety* +/// invariants of the type. +/// +/// While it's easy to create a *valid* instance of an inhabited ZST, since having +/// no bits in its representation means there's only one possible value, that +/// doesn't mean that it's always *sound* to do so. +/// +/// For example, a library could design zero-sized tokens that are `!Default + !Clone`, limiting +/// their creation to functions that initialize some state or establish a scope. Conjuring such a +/// token could break invariants and lead to unsoundness. +/// +/// # Examples +/// +/// ``` +/// #![feature(mem_conjure_zst)] +/// use std::mem::conjure_zst; +/// +/// assert_eq!(unsafe { conjure_zst::<()>() }, ()); +/// assert_eq!(unsafe { conjure_zst::<[i32; 0]>() }, []); +/// ``` +/// +/// [inhabited]: https://doc.rust-lang.org/reference/glossary.html#inhabited +#[unstable(feature = "mem_conjure_zst", issue = "95383")] +pub const unsafe fn conjure_zst() -> T { + const_assert!( + size_of::() == 0, + "mem::conjure_zst invoked on a nonzero-sized type", + "mem::conjure_zst invoked on type {t}, which is not zero-sized", + t: &str = stringify!(T) + ); + + // SAFETY: because the caller must guarantee that it's inhabited and zero-sized, + // there's nothing in the representation that needs to be set. + // `assume_init` calls `assert_inhabited`, so we don't need to here. + unsafe { + #[allow(clippy::uninit_assumed_init)] + MaybeUninit::uninit().assume_init() + } +} diff --git a/tests/ui/consts/std/conjure_zst.rs b/tests/ui/consts/std/conjure_zst.rs new file mode 100644 index 000000000000..c04deae502b0 --- /dev/null +++ b/tests/ui/consts/std/conjure_zst.rs @@ -0,0 +1,10 @@ +#![feature(mem_conjure_zst)] + +use std::{convert::Infallible, mem}; + +const INVALID: Infallible = unsafe { mem::conjure_zst() }; +//~^ ERROR attempted to instantiate uninhabited type + +const VALID: () = unsafe { mem::conjure_zst() }; + +fn main() {} diff --git a/tests/ui/consts/std/conjure_zst.stderr b/tests/ui/consts/std/conjure_zst.stderr new file mode 100644 index 000000000000..0c4a978b81ee --- /dev/null +++ b/tests/ui/consts/std/conjure_zst.stderr @@ -0,0 +1,12 @@ +error[E0080]: evaluation panicked: aborted execution: attempted to instantiate uninhabited type `Infallible` + --> $DIR/conjure_zst.rs:5:38 + | +LL | const INVALID: Infallible = unsafe { mem::conjure_zst() }; + | ^^^^^^^^^^^^^^^^^^ evaluation of `INVALID` failed inside this call + | +note: inside `conjure_zst::` + --> $SRC_DIR/core/src/mem/mod.rs:LL:COL + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. From d5ba5c1c925f82de2efbfbbe92ffee56a6a6a334 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Fri, 3 Oct 2025 16:00:15 -0400 Subject: [PATCH 140/226] Mark `PatternTypo` suggestion as maybe incorrect --- compiler/rustc_passes/src/errors.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index f0726014e0af..e01e9e384c03 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -1365,7 +1365,7 @@ pub(crate) struct UnusedVarAssignedOnly { #[multipart_suggestion( passes_unused_var_typo, style = "verbose", - applicability = "machine-applicable" + applicability = "maybe-incorrect" )] pub(crate) struct PatternTypo { #[suggestion_part(code = "{code}")] From fd96a78092e77be45d5b60cedea0806b70d751fd Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 3 Oct 2025 12:14:41 -0700 Subject: [PATCH 141/226] Add documentation about unwinding to wasm targets This commit adds some documentation about the state of `-Cpanic=unwind` for the following wasm targets: * `wasm32-unknown-unknown` * `wasm32-wasip1` * `wasm32-wasip2` * `wasm32v1-none` Notably it's possible to use `-Cpanic=unwind` with `-Zbuild-std` and it's also mentioned that there are no concrete proposals at this time to adding a new set of targets which support unwinding. My hunch is that in a few years' time it would make sense to enable it by default on these targets (except for `wasm32v1-none`) but that's a problem for future folks to debate. For now this is an attempt to document the status quo. --- .../wasm32-unknown-unknown.md | 62 ++++++++++++++++++- .../src/platform-support/wasm32-wasip1.md | 6 ++ .../src/platform-support/wasm32-wasip2.md | 6 ++ .../src/platform-support/wasm32v1-none.md | 11 ++++ 4 files changed, 84 insertions(+), 1 deletion(-) diff --git a/src/doc/rustc/src/platform-support/wasm32-unknown-unknown.md b/src/doc/rustc/src/platform-support/wasm32-unknown-unknown.md index 1e74c47221c7..ec20672f6540 100644 --- a/src/doc/rustc/src/platform-support/wasm32-unknown-unknown.md +++ b/src/doc/rustc/src/platform-support/wasm32-unknown-unknown.md @@ -157,7 +157,7 @@ $ cargo +nightly build -Zbuild-std=panic_abort,std --target wasm32-unknown-unkno ``` Here the `mvp` "cpu" is a placeholder in LLVM for disabling all supported -features by default. Cargo's `-Zbuild-std` feature, a Nightly Rust feature, is +features by default. Cargo's [`-Zbuild-std`] feature, a Nightly Rust feature, is then used to recompile the standard library in addition to your own code. This will produce a binary that uses only the original WebAssembly features by default and no proposals since its inception. @@ -207,3 +207,63 @@ conditionally compile code instead. This is notably different to the way native platforms such as x86\_64 work, and this is due to the fact that WebAssembly binaries must only contain code the engine understands. Native binaries work so long as the CPU doesn't execute unknown code dynamically at runtime. + +## Unwinding + +By default the `wasm32-unknown-unknown` target is compiled with `-Cpanic=abort`. +Historically this was due to the fact that there was no way to catch panics in +wasm, but since mid-2025 the WebAssembly [`exception-handling` +proposal](https://github.com/WebAssembly/exception-handling) reached +stabilization. LLVM has support for this proposal as well and when this is all +combined together it's possible to enable `-Cpanic=unwind` on wasm targets. + +Compiling wasm targets with `-Cpanic=unwind` is not as easy as just passing +`-Cpanic=unwind`, however: + +```sh +$ rustc foo.rs -Cpanic=unwind --target wasm32-unknown-unknown +error: the crate `panic_unwind` does not have the panic strategy `unwind` +``` + +Notably the precompiled standard library that is shipped through Rustup is +compiled with `-Cpanic=abort`, not `-Cpanic=unwind`. While this is the case +you're going to be required to use Cargo's [`-Zbuild-std`] feature to build with +unwinding support: + +```sh +$ RUSTFLAGS='-Cpanic=unwind' cargo +nightly build --target wasm32-unknown-unknown -Zbuild-std +``` + +Note, however, that as of 2025-10-03 LLVM is still using the "legacy exception +instructions" by default, not the officially standard version of the +exception-handling proposal: + +```sh +$ wasm-tools validate target/wasm32-unknown-unknown/debug/foo.wasm +error: /library/std/src/sys/backtrace.rs:161:5 +function `std::sys::backtrace::__rust_begin_short_backtrace` failed to validate + +Caused by: + 0: func 2 failed to validate + 1: legacy_exceptions feature required for try instruction (at offset 0x880) +``` + +Fixing this requires passing `-Cllvm-args=-wasm-use-legacy-eh=false` to the Rust +compiler as well: + +```sh +$ RUSTFLAGS='-Cpanic=unwind -Cllvm-args=-wasm-use-legacy-eh=false' cargo +nightly build --target wasm32-unknown-unknown -Zbuild-std +$ wasm-tools validate target/wasm32-unknown-unknown/debug/foo.wasm +``` + +At this time there are no concrete plans for adding new targets to the Rust +compiler which have `-Cpanic=unwind` enabled-by-default. The most likely route +to having this enabled is that in a few years when the `exception-handling` +target feature is enabled by default in LLVM (due to browsers/runtime support +propagating widely enough) the targets will switch to using `-Cpanic=unwind` by +default. This is not for certain, however, and will likely be accompanied with +either an MCP or an RFC about changing all wasm targets in the same manner. In +the meantime using `-Cpanic=unwind` will require using [`-Zbuild-std`] and +passing the appropriate flags to rustc. + +[`-Zbuild-std`]: ../../cargo/reference/unstable.html#build-std diff --git a/src/doc/rustc/src/platform-support/wasm32-wasip1.md b/src/doc/rustc/src/platform-support/wasm32-wasip1.md index a8a9e5505810..958a34a86928 100644 --- a/src/doc/rustc/src/platform-support/wasm32-wasip1.md +++ b/src/doc/rustc/src/platform-support/wasm32-wasip1.md @@ -133,3 +133,9 @@ to Rust 1.80 the `target_env` condition was not set. The default set of WebAssembly features enabled for compilation is currently the same as [`wasm32-unknown-unknown`](./wasm32-unknown-unknown.md). See the documentation there for more information. + +## Unwinding + +This target is compiled with `-Cpanic=abort` by default. For information on +using `-Cpanic=unwind` see the [documentation about unwinding for +`wasm32-unknown-unknown`](./wasm32-unknown-unknown.md#unwinding). diff --git a/src/doc/rustc/src/platform-support/wasm32-wasip2.md b/src/doc/rustc/src/platform-support/wasm32-wasip2.md index dea33e62f2b4..861083ad4a61 100644 --- a/src/doc/rustc/src/platform-support/wasm32-wasip2.md +++ b/src/doc/rustc/src/platform-support/wasm32-wasip2.md @@ -67,3 +67,9 @@ It's recommended to conditionally compile code for this target with: The default set of WebAssembly features enabled for compilation is currently the same as [`wasm32-unknown-unknown`](./wasm32-unknown-unknown.md). See the documentation there for more information. + +## Unwinding + +This target is compiled with `-Cpanic=abort` by default. For information on +using `-Cpanic=unwind` see the [documentation about unwinding for +`wasm32-unknown-unknown`](./wasm32-unknown-unknown.md#unwinding). diff --git a/src/doc/rustc/src/platform-support/wasm32v1-none.md b/src/doc/rustc/src/platform-support/wasm32v1-none.md index 51b00de5e578..d2f9b3a1f976 100644 --- a/src/doc/rustc/src/platform-support/wasm32v1-none.md +++ b/src/doc/rustc/src/platform-support/wasm32v1-none.md @@ -107,3 +107,14 @@ $ cargo +nightly build -Zbuild-std=panic_abort,std --target wasm32-unknown-unkno Which not only rebuilds `std`, `core` and `alloc` (which is somewhat costly and annoying) but more importantly requires the use of nightly Rust toolchains (for the `-Zbuild-std` flag). This is very undesirable for the target audience, which consists of people targeting WebAssembly implementations that prioritize stability, simplicity and/or security over feature support. This `wasm32v1-none` target exists as an alternative option that works on stable Rust toolchains, without rebuilding the stdlib. + +## Unwinding + +This target is compiled with `-Cpanic=abort` by default. Using `-Cpanic=unwind` +would require using the WebAssembly exception-handling proposal stabilized +mid-2025, and if that's desired then you most likely don't want to use this +target and instead want to use `wasm32-unknown-unknown` instead. It's unlikely +that this target will ever support unwinding with the precompiled artifacts +shipped through rustup. For documentation about using `-Zbuild-std` to enable +using `-Cpanic=unwind` see the [documentation of +`wasm32-unknown-unknown`](./wasm32-unknown-unknown.md#unwinding). From 55aeb1747d97b70c75220ade6cdc53523886defd Mon Sep 17 00:00:00 2001 From: jackh726 Date: Thu, 2 Oct 2025 00:51:44 +0000 Subject: [PATCH 142/226] Do not assert that a change in global cache only happens when concurrent --- compiler/rustc_middle/src/ty/context.rs | 5 +- compiler/rustc_type_ir/src/interner.rs | 8 +-- .../src/search_graph/global_cache.rs | 4 +- .../rustc_type_ir/src/search_graph/mod.rs | 2 +- ...rted.stderr => unsupported.current.stderr} | 28 +++++----- tests/ui/delegation/unsupported.next.stderr | 51 +++++++++++++++++++ tests/ui/delegation/unsupported.rs | 8 +++ 7 files changed, 84 insertions(+), 22 deletions(-) rename tests/ui/delegation/{unsupported.stderr => unsupported.current.stderr} (76%) create mode 100644 tests/ui/delegation/unsupported.next.stderr diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 7d3e2c9965da..9a0d2602a5f1 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -207,8 +207,9 @@ impl<'tcx> Interner for TyCtxt<'tcx> { from_entry(entry) } - fn evaluation_is_concurrent(&self) -> bool { - self.sess.threads() > 1 + fn assert_evaluation_is_concurrent(&self) { + // Turns out, the assumption for this function isn't perfect. + // See trait-system-refactor-initiative#234. } fn expand_abstract_consts>>(self, t: T) -> T { diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index cf58aec14a68..f12dcbfef98b 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -185,7 +185,9 @@ pub trait Interner: from_entry: impl FnOnce(&CanonicalParamEnvCacheEntry) -> R, ) -> R; - fn evaluation_is_concurrent(&self) -> bool; + /// Useful for testing. If a cache entry is replaced, this should + /// (in theory) only happen when concurrent. + fn assert_evaluation_is_concurrent(&self); fn expand_abstract_consts>(self, t: T) -> T; @@ -569,7 +571,7 @@ impl search_graph::Cx for I { fn with_global_cache(self, f: impl FnOnce(&mut search_graph::GlobalCache) -> R) -> R { I::with_global_cache(self, f) } - fn evaluation_is_concurrent(&self) -> bool { - self.evaluation_is_concurrent() + fn assert_evaluation_is_concurrent(&self) { + self.assert_evaluation_is_concurrent() } } diff --git a/compiler/rustc_type_ir/src/search_graph/global_cache.rs b/compiler/rustc_type_ir/src/search_graph/global_cache.rs index eb56c1af408b..7e438fefffca 100644 --- a/compiler/rustc_type_ir/src/search_graph/global_cache.rs +++ b/compiler/rustc_type_ir/src/search_graph/global_cache.rs @@ -56,13 +56,13 @@ impl GlobalCache { let with_overflow = WithOverflow { nested_goals, result }; let prev = entry.with_overflow.insert(required_depth, with_overflow); if let Some(prev) = &prev { - assert!(cx.evaluation_is_concurrent()); + cx.assert_evaluation_is_concurrent(); assert_eq!(cx.get_tracked(&prev.result), evaluation_result.result); } } else { let prev = entry.success.replace(Success { required_depth, nested_goals, result }); if let Some(prev) = &prev { - assert!(cx.evaluation_is_concurrent()); + cx.assert_evaluation_is_concurrent(); assert_eq!(cx.get_tracked(&prev.result), evaluation_result.result); } } diff --git a/compiler/rustc_type_ir/src/search_graph/mod.rs b/compiler/rustc_type_ir/src/search_graph/mod.rs index 8f8f019510fe..d7f91ef692c5 100644 --- a/compiler/rustc_type_ir/src/search_graph/mod.rs +++ b/compiler/rustc_type_ir/src/search_graph/mod.rs @@ -53,7 +53,7 @@ pub trait Cx: Copy { fn with_global_cache(self, f: impl FnOnce(&mut GlobalCache) -> R) -> R; - fn evaluation_is_concurrent(&self) -> bool; + fn assert_evaluation_is_concurrent(&self); } pub trait Delegate: Sized { diff --git a/tests/ui/delegation/unsupported.stderr b/tests/ui/delegation/unsupported.current.stderr similarity index 76% rename from tests/ui/delegation/unsupported.stderr rename to tests/ui/delegation/unsupported.current.stderr index f69be60133e2..55564e8f231f 100644 --- a/tests/ui/delegation/unsupported.stderr +++ b/tests/ui/delegation/unsupported.current.stderr @@ -1,43 +1,43 @@ -error[E0391]: cycle detected when computing type of `opaque::::opaque_ret::{anon_assoc#0}` - --> $DIR/unsupported.rs:22:25 +error[E0391]: cycle detected when computing type of `opaque::::opaque_ret::{anon_assoc#0}` + --> $DIR/unsupported.rs:30:25 | LL | reuse to_reuse::opaque_ret; | ^^^^^^^^^^ | note: ...which requires comparing an impl and trait method signature, inferring any hidden `impl Trait` types in the process... - --> $DIR/unsupported.rs:22:25 + --> $DIR/unsupported.rs:30:25 | LL | reuse to_reuse::opaque_ret; | ^^^^^^^^^^ - = note: ...which again requires computing type of `opaque::::opaque_ret::{anon_assoc#0}`, completing the cycle -note: cycle used when checking assoc item `opaque::::opaque_ret` is compatible with trait definition - --> $DIR/unsupported.rs:22:25 + = note: ...which again requires computing type of `opaque::::opaque_ret::{anon_assoc#0}`, completing the cycle +note: cycle used when checking assoc item `opaque::::opaque_ret` is compatible with trait definition + --> $DIR/unsupported.rs:30:25 | LL | reuse to_reuse::opaque_ret; | ^^^^^^^^^^ = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information -error[E0391]: cycle detected when computing type of `opaque::::opaque_ret::{anon_assoc#0}` - --> $DIR/unsupported.rs:25:24 +error[E0391]: cycle detected when computing type of `opaque::::opaque_ret::{anon_assoc#0}` + --> $DIR/unsupported.rs:33:24 | LL | reuse ToReuse::opaque_ret; | ^^^^^^^^^^ | note: ...which requires comparing an impl and trait method signature, inferring any hidden `impl Trait` types in the process... - --> $DIR/unsupported.rs:25:24 + --> $DIR/unsupported.rs:33:24 | LL | reuse ToReuse::opaque_ret; | ^^^^^^^^^^ - = note: ...which again requires computing type of `opaque::::opaque_ret::{anon_assoc#0}`, completing the cycle -note: cycle used when checking assoc item `opaque::::opaque_ret` is compatible with trait definition - --> $DIR/unsupported.rs:25:24 + = note: ...which again requires computing type of `opaque::::opaque_ret::{anon_assoc#0}`, completing the cycle +note: cycle used when checking assoc item `opaque::::opaque_ret` is compatible with trait definition + --> $DIR/unsupported.rs:33:24 | LL | reuse ToReuse::opaque_ret; | ^^^^^^^^^^ = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information error: recursive delegation is not supported yet - --> $DIR/unsupported.rs:38:22 + --> $DIR/unsupported.rs:46:22 | LL | pub reuse to_reuse2::foo; | --- callee defined here @@ -46,7 +46,7 @@ LL | reuse to_reuse1::foo; | ^^^ error[E0283]: type annotations needed - --> $DIR/unsupported.rs:48:18 + --> $DIR/unsupported.rs:56:18 | LL | reuse Trait::foo; | ^^^ cannot infer type diff --git a/tests/ui/delegation/unsupported.next.stderr b/tests/ui/delegation/unsupported.next.stderr new file mode 100644 index 000000000000..606a25d4269a --- /dev/null +++ b/tests/ui/delegation/unsupported.next.stderr @@ -0,0 +1,51 @@ +error[E0391]: cycle detected when computing type of `opaque::::opaque_ret::{anon_assoc#0}` + --> $DIR/unsupported.rs:30:25 + | +LL | reuse to_reuse::opaque_ret; + | ^^^^^^^^^^ + | +note: ...which requires comparing an impl and trait method signature, inferring any hidden `impl Trait` types in the process... + --> $DIR/unsupported.rs:30:25 + | +LL | reuse to_reuse::opaque_ret; + | ^^^^^^^^^^ + = note: ...which again requires computing type of `opaque::::opaque_ret::{anon_assoc#0}`, completing the cycle + = note: cycle used when computing implied outlives bounds for `::opaque_ret::{anon_assoc#0}` (hack disabled = false) + = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information + +error[E0391]: cycle detected when computing type of `opaque::::opaque_ret::{anon_assoc#0}` + --> $DIR/unsupported.rs:33:24 + | +LL | reuse ToReuse::opaque_ret; + | ^^^^^^^^^^ + | +note: ...which requires comparing an impl and trait method signature, inferring any hidden `impl Trait` types in the process... + --> $DIR/unsupported.rs:33:24 + | +LL | reuse ToReuse::opaque_ret; + | ^^^^^^^^^^ + = note: ...which again requires computing type of `opaque::::opaque_ret::{anon_assoc#0}`, completing the cycle + = note: cycle used when computing implied outlives bounds for `::opaque_ret::{anon_assoc#0}` (hack disabled = false) + = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information + +error: recursive delegation is not supported yet + --> $DIR/unsupported.rs:46:22 + | +LL | pub reuse to_reuse2::foo; + | --- callee defined here +... +LL | reuse to_reuse1::foo; + | ^^^ + +error[E0283]: type annotations needed + --> $DIR/unsupported.rs:56:18 + | +LL | reuse Trait::foo; + | ^^^ cannot infer type + | + = note: cannot satisfy `_: effects::Trait` + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0283, E0391. +For more information about an error, try `rustc --explain E0283`. diff --git a/tests/ui/delegation/unsupported.rs b/tests/ui/delegation/unsupported.rs index af1c20976d7a..79bab342da09 100644 --- a/tests/ui/delegation/unsupported.rs +++ b/tests/ui/delegation/unsupported.rs @@ -1,3 +1,11 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@ check-fail + +// Next solver revision included because of trait-system-refactor-initiative#234. +// If we end up in a query cycle, it should be okay as long as results are the same. + #![feature(const_trait_impl)] #![feature(c_variadic)] #![feature(fn_delegation)] From c721fa243825255c0f195d4282021e7c263c3e7f Mon Sep 17 00:00:00 2001 From: iximeow Date: Sun, 28 Sep 2025 03:21:45 +0000 Subject: [PATCH 143/226] interpret #[used] as #[used(compiler)] on illumos illumos' `ld` does not support a flag like either SHF_GNU_RETAIN or SHF_SUNW_NODISCARD, so there is no way to communicate `#[used(linker)]` for that target. Setting `USED_LINKER` to try results in LLVM setting SHF_SUNW_NODISCARD for Solaris-like targets, which is an unknown section header flag for illumos `ld` and prevents sections from being merged that otherwise would. As a motivating example, the `inventory` crate produces `#[used]` items to merge into `.init_array`. Because those items have an unknown section header flag they are not merged with the default `.init_array` with `frame_dummy`, and end up never executed. Downgrading `#[used]` to `#[used(compiler)]` on illumos keeps so-attributed items as preserved as they had been before https://github.com/rust-lang/rust/pull/140872. As was the case before that change, because rustc passes `-z ignore` to illumos `ld`, it's possible that `used` sections are GC'd at link time. https://github.com/rust-lang/rust/issues/146169 describes this unfortunate circumstance. --- .../src/attributes/codegen_attrs.rs | 36 ++++++++++++++----- .../rustc_codegen_ssa/src/codegen_attrs.rs | 13 +++++++ .../rustc_hir/src/attrs/data_structures.rs | 3 +- 3 files changed, 43 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index 262b8213977b..7978bf28214c 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -370,6 +370,7 @@ impl NoArgsAttributeParser for NoMangleParser { pub(crate) struct UsedParser { first_compiler: Option, first_linker: Option, + first_default: Option, } // A custom `AttributeParser` is used rather than a Simple attribute parser because @@ -382,7 +383,7 @@ impl AttributeParser for UsedParser { template!(Word, List: &["compiler", "linker"]), |group: &mut Self, cx, args| { let used_by = match args { - ArgParser::NoArgs => UsedBy::Linker, + ArgParser::NoArgs => UsedBy::Default, ArgParser::List(list) => { let Some(l) = list.single() else { cx.expected_single_argument(list.span); @@ -423,12 +424,29 @@ impl AttributeParser for UsedParser { ArgParser::NameValue(_) => return, }; + let attr_span = cx.attr_span; + + // `#[used]` is interpreted as `#[used(linker)]` (though depending on target OS the + // circumstances are more complicated). While we're checking `used_by`, also report + // these cross-`UsedBy` duplicates to warn. let target = match used_by { UsedBy::Compiler => &mut group.first_compiler, - UsedBy::Linker => &mut group.first_linker, + UsedBy::Linker => { + if let Some(prev) = group.first_default { + cx.warn_unused_duplicate(prev, attr_span); + return; + } + &mut group.first_linker + } + UsedBy::Default => { + if let Some(prev) = group.first_linker { + cx.warn_unused_duplicate(prev, attr_span); + return; + } + &mut group.first_default + } }; - let attr_span = cx.attr_span; if let Some(prev) = *target { cx.warn_unused_duplicate(prev, attr_span); } else { @@ -440,11 +458,13 @@ impl AttributeParser for UsedParser { AllowedTargets::AllowList(&[Allow(Target::Static), Warn(Target::MacroCall)]); fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option { - // Ratcheting behaviour, if both `linker` and `compiler` are specified, use `linker` - Some(match (self.first_compiler, self.first_linker) { - (_, Some(span)) => AttributeKind::Used { used_by: UsedBy::Linker, span }, - (Some(span), _) => AttributeKind::Used { used_by: UsedBy::Compiler, span }, - (None, None) => return None, + // If a specific form of `used` is specified, it takes precedence over generic `#[used]`. + // If both `linker` and `compiler` are specified, use `linker`. + Some(match (self.first_compiler, self.first_linker, self.first_default) { + (_, Some(span), _) => AttributeKind::Used { used_by: UsedBy::Linker, span }, + (Some(span), _, _) => AttributeKind::Used { used_by: UsedBy::Compiler, span }, + (_, _, Some(span)) => AttributeKind::Used { used_by: UsedBy::Default, span }, + (None, None, None) => return None, }) } } diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 32ae810ecc89..2c7643e46cea 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -263,6 +263,19 @@ fn process_builtin_attrs( AttributeKind::Used { used_by, .. } => match used_by { UsedBy::Compiler => codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_COMPILER, UsedBy::Linker => codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_LINKER, + UsedBy::Default => { + let used_form = if tcx.sess.target.os == "illumos" { + // illumos' `ld` doesn't support a section header that would represent + // `#[used(linker)]`, see + // https://github.com/rust-lang/rust/issues/146169. For that target, + // downgrade as if `#[used(compiler)]` was requested and hope for the + // best. + CodegenFnAttrFlags::USED_COMPILER + } else { + CodegenFnAttrFlags::USED_LINKER + }; + codegen_fn_attrs.flags |= used_form; + } }, AttributeKind::FfiConst(_) => { codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_CONST diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index ddcbaeaad880..beeca7332cb1 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -146,12 +146,13 @@ impl Deprecation { } /// There are three valid forms of the attribute: -/// `#[used]`, which is semantically equivalent to `#[used(linker)]` except that the latter is currently unstable. +/// `#[used]`, which is equivalent to `#[used(linker)]` on targets that support it, but `#[used(compiler)]` if not. /// `#[used(compiler)]` /// `#[used(linker)]` #[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)] #[derive(HashStable_Generic, PrintAttribute)] pub enum UsedBy { + Default, Compiler, Linker, } From 12cfad9a8b6aeed10ddc240b4923913246353ebb Mon Sep 17 00:00:00 2001 From: Manuel Drehwald Date: Fri, 3 Oct 2025 18:12:22 -0400 Subject: [PATCH 144/226] update autodiff batching test --- tests/codegen-llvm/autodiff/batched.rs | 74 +++++++++++++++----------- 1 file changed, 42 insertions(+), 32 deletions(-) diff --git a/tests/codegen-llvm/autodiff/batched.rs b/tests/codegen-llvm/autodiff/batched.rs index dc82403212fb..0ff6134bc07d 100644 --- a/tests/codegen-llvm/autodiff/batched.rs +++ b/tests/codegen-llvm/autodiff/batched.rs @@ -1,4 +1,4 @@ -//@ compile-flags: -Zautodiff=Enable,NoTT -C opt-level=3 -Clto=fat +//@ compile-flags: -Zautodiff=Enable,NoTT,NoPostopt -C opt-level=3 -Clto=fat //@ no-prefer-dynamic //@ needs-enzyme // @@ -23,7 +23,7 @@ fn square(x: &f32) -> f32 { } // d_square2 -// CHECK: define internal fastcc [4 x float] @fwddiffe4square(float %x.0.val, [4 x ptr] %"x'") +// CHECK: define internal [4 x float] @fwddiffe4square(ptr noalias noundef readonly align 4 captures(none) dereferenceable(4) %x, [4 x ptr] %"x'") // CHECK-NEXT: start: // CHECK-NEXT: %0 = extractvalue [4 x ptr] %"x'", 0 // CHECK-NEXT: %"_2'ipl" = load float, ptr %0, align 4 @@ -33,23 +33,28 @@ fn square(x: &f32) -> f32 { // CHECK-NEXT: %"_2'ipl2" = load float, ptr %2, align 4 // CHECK-NEXT: %3 = extractvalue [4 x ptr] %"x'", 3 // CHECK-NEXT: %"_2'ipl3" = load float, ptr %3, align 4 -// CHECK-NEXT: %4 = fmul float %"_2'ipl", 2.000000e+00 -// CHECK-NEXT: %5 = fmul fast float %4, %x.0.val -// CHECK-NEXT: %6 = insertvalue [4 x float] undef, float %5, 0 -// CHECK-NEXT: %7 = fmul float %"_2'ipl1", 2.000000e+00 -// CHECK-NEXT: %8 = fmul fast float %7, %x.0.val -// CHECK-NEXT: %9 = insertvalue [4 x float] %6, float %8, 1 -// CHECK-NEXT: %10 = fmul float %"_2'ipl2", 2.000000e+00 -// CHECK-NEXT: %11 = fmul fast float %10, %x.0.val -// CHECK-NEXT: %12 = insertvalue [4 x float] %9, float %11, 2 -// CHECK-NEXT: %13 = fmul float %"_2'ipl3", 2.000000e+00 -// CHECK-NEXT: %14 = fmul fast float %13, %x.0.val -// CHECK-NEXT: %15 = insertvalue [4 x float] %12, float %14, 3 -// CHECK-NEXT: ret [4 x float] %15 +// CHECK-NEXT: %_2 = load float, ptr %x, align 4 +// CHECK-NEXT: %4 = fmul fast float %"_2'ipl", %_2 +// CHECK-NEXT: %5 = fmul fast float %"_2'ipl1", %_2 +// CHECK-NEXT: %6 = fmul fast float %"_2'ipl2", %_2 +// CHECK-NEXT: %7 = fmul fast float %"_2'ipl3", %_2 +// CHECK-NEXT: %8 = fmul fast float %"_2'ipl", %_2 +// CHECK-NEXT: %9 = fmul fast float %"_2'ipl1", %_2 +// CHECK-NEXT: %10 = fmul fast float %"_2'ipl2", %_2 +// CHECK-NEXT: %11 = fmul fast float %"_2'ipl3", %_2 +// CHECK-NEXT: %12 = fadd fast float %4, %8 +// CHECK-NEXT: %13 = insertvalue [4 x float] undef, float %12, 0 +// CHECK-NEXT: %14 = fadd fast float %5, %9 +// CHECK-NEXT: %15 = insertvalue [4 x float] %13, float %14, 1 +// CHECK-NEXT: %16 = fadd fast float %6, %10 +// CHECK-NEXT: %17 = insertvalue [4 x float] %15, float %16, 2 +// CHECK-NEXT: %18 = fadd fast float %7, %11 +// CHECK-NEXT: %19 = insertvalue [4 x float] %17, float %18, 3 +// CHECK-NEXT: ret [4 x float] %19 // CHECK-NEXT: } // d_square3, the extra float is the original return value (x * x) -// CHECK: define internal fastcc { float, [4 x float] } @fwddiffe4square.1(float %x.0.val, [4 x ptr] %"x'") +// CHECK: define internal { float, [4 x float] } @fwddiffe4square.1(ptr noalias noundef readonly align 4 captures(none) dereferenceable(4) %x, [4 x ptr] %"x'") // CHECK-NEXT: start: // CHECK-NEXT: %0 = extractvalue [4 x ptr] %"x'", 0 // CHECK-NEXT: %"_2'ipl" = load float, ptr %0, align 4 @@ -59,22 +64,27 @@ fn square(x: &f32) -> f32 { // CHECK-NEXT: %"_2'ipl2" = load float, ptr %2, align 4 // CHECK-NEXT: %3 = extractvalue [4 x ptr] %"x'", 3 // CHECK-NEXT: %"_2'ipl3" = load float, ptr %3, align 4 -// CHECK-NEXT: %_0 = fmul float %x.0.val, %x.0.val -// CHECK-NEXT: %4 = fmul float %"_2'ipl", 2.000000e+00 -// CHECK-NEXT: %5 = fmul fast float %4, %x.0.val -// CHECK-NEXT: %6 = insertvalue [4 x float] undef, float %5, 0 -// CHECK-NEXT: %7 = fmul float %"_2'ipl1", 2.000000e+00 -// CHECK-NEXT: %8 = fmul fast float %7, %x.0.val -// CHECK-NEXT: %9 = insertvalue [4 x float] %6, float %8, 1 -// CHECK-NEXT: %10 = fmul float %"_2'ipl2", 2.000000e+00 -// CHECK-NEXT: %11 = fmul fast float %10, %x.0.val -// CHECK-NEXT: %12 = insertvalue [4 x float] %9, float %11, 2 -// CHECK-NEXT: %13 = fmul float %"_2'ipl3", 2.000000e+00 -// CHECK-NEXT: %14 = fmul fast float %13, %x.0.val -// CHECK-NEXT: %15 = insertvalue [4 x float] %12, float %14, 3 -// CHECK-NEXT: %16 = insertvalue { float, [4 x float] } undef, float %_0, 0 -// CHECK-NEXT: %17 = insertvalue { float, [4 x float] } %16, [4 x float] %15, 1 -// CHECK-NEXT: ret { float, [4 x float] } %17 +// CHECK-NEXT: %_2 = load float, ptr %x, align 4 +// CHECK-NEXT: %_0 = fmul float %_2, %_2 +// CHECK-NEXT: %4 = fmul fast float %"_2'ipl", %_2 +// CHECK-NEXT: %5 = fmul fast float %"_2'ipl1", %_2 +// CHECK-NEXT: %6 = fmul fast float %"_2'ipl2", %_2 +// CHECK-NEXT: %7 = fmul fast float %"_2'ipl3", %_2 +// CHECK-NEXT: %8 = fmul fast float %"_2'ipl", %_2 +// CHECK-NEXT: %9 = fmul fast float %"_2'ipl1", %_2 +// CHECK-NEXT: %10 = fmul fast float %"_2'ipl2", %_2 +// CHECK-NEXT: %11 = fmul fast float %"_2'ipl3", %_2 +// CHECK-NEXT: %12 = fadd fast float %4, %8 +// CHECK-NEXT: %13 = insertvalue [4 x float] undef, float %12, 0 +// CHECK-NEXT: %14 = fadd fast float %5, %9 +// CHECK-NEXT: %15 = insertvalue [4 x float] %13, float %14, 1 +// CHECK-NEXT: %16 = fadd fast float %6, %10 +// CHECK-NEXT: %17 = insertvalue [4 x float] %15, float %16, 2 +// CHECK-NEXT: %18 = fadd fast float %7, %11 +// CHECK-NEXT: %19 = insertvalue [4 x float] %17, float %18, 3 +// CHECK-NEXT: %20 = insertvalue { float, [4 x float] } undef, float %_0, 0 +// CHECK-NEXT: %21 = insertvalue { float, [4 x float] } %20, [4 x float] %19, 1 +// CHECK-NEXT: ret { float, [4 x float] } %21 // CHECK-NEXT: } fn main() { From 422b91443f1016aec2a1f00316d01a117ddc7829 Mon Sep 17 00:00:00 2001 From: yukang Date: Sat, 4 Oct 2025 15:02:43 +0800 Subject: [PATCH 145/226] Fix top level ui tests check in tidy --- src/tools/tidy/src/ui_tests.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs index 12eca47c171d..c74ecf3d43f4 100644 --- a/src/tools/tidy/src/ui_tests.rs +++ b/src/tools/tidy/src/ui_tests.rs @@ -81,21 +81,20 @@ fn deny_new_top_level_ui_tests(check: &mut RunningCheck, tests_path: &Path) { // - // - - let top_level_ui_tests = walkdir::WalkDir::new(tests_path) - .min_depth(1) - .max_depth(1) + let top_level_ui_tests = ignore::WalkBuilder::new(tests_path) + .max_depth(Some(1)) .follow_links(false) - .same_file_system(true) - .into_iter() + .build() .flatten() .filter(|e| { let file_name = e.file_name(); file_name != ".gitattributes" && file_name != "README.md" }) - .filter(|e| !e.file_type().is_dir()); + .filter(|e| !e.file_type().is_some_and(|f| f.is_dir())); + for entry in top_level_ui_tests { check.error(format!( - "ui tests should be added under meaningful subdirectories: `{}`", + "ui tests should be added under meaningful subdirectories: `{}`, see https://github.com/rust-lang/compiler-team/issues/902", entry.path().display() )); } From f955c76a5b8ea3265d09139126d7f0df7fcebb18 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Fri, 3 Oct 2025 14:31:32 +1000 Subject: [PATCH 146/226] Remove inherent methods from several LLVM FFI types Using a helper trait allows the conversions to remain in `rustc_codegen_llvm`, even if the FFI types are moved to a different crate. --- compiler/rustc_codegen_llvm/src/allocator.rs | 2 +- compiler/rustc_codegen_llvm/src/builder.rs | 3 +- .../src/debuginfo/metadata.rs | 2 +- compiler/rustc_codegen_llvm/src/declare.rs | 2 +- .../src/llvm/conversions.rs | 115 ++++++++++++++++++ compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 107 ---------------- compiler/rustc_codegen_llvm/src/llvm/mod.rs | 2 + 7 files changed, 122 insertions(+), 111 deletions(-) create mode 100644 compiler/rustc_codegen_llvm/src/llvm/conversions.rs diff --git a/compiler/rustc_codegen_llvm/src/allocator.rs b/compiler/rustc_codegen_llvm/src/allocator.rs index 896d6755c752..824aa501036d 100644 --- a/compiler/rustc_codegen_llvm/src/allocator.rs +++ b/compiler/rustc_codegen_llvm/src/allocator.rs @@ -14,7 +14,7 @@ use rustc_symbol_mangling::mangle_internal_symbol; use crate::attributes::llfn_attrs_from_instance; use crate::builder::SBuilder; use crate::declare::declare_simple_fn; -use crate::llvm::{self, FALSE, TRUE, Type, Value}; +use crate::llvm::{self, FALSE, FromGeneric, TRUE, Type, Value}; use crate::{SimpleCx, attributes, debuginfo}; pub(crate) unsafe fn codegen( diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index e7cb18ab22da..08da90d535da 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -36,7 +36,8 @@ use crate::attributes; use crate::common::Funclet; use crate::context::{CodegenCx, FullCx, GenericCx, SCx}; use crate::llvm::{ - self, AtomicOrdering, AtomicRmwBinOp, BasicBlock, GEPNoWrapFlags, Metadata, TRUE, ToLlvmBool, + self, AtomicOrdering, AtomicRmwBinOp, BasicBlock, FromGeneric, GEPNoWrapFlags, Metadata, TRUE, + ToLlvmBool, }; use crate::type_::Type; use crate::type_of::LayoutLlvmExt; diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 2f9e7cae54f2..b8b5f3d30d7c 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -37,11 +37,11 @@ use crate::common::{AsCCharPtr, CodegenCx}; use crate::debuginfo::dwarf_const; use crate::debuginfo::metadata::type_map::build_type_with_children; use crate::debuginfo::utils::{WidePtrKind, wide_pointer_kind}; -use crate::llvm; use crate::llvm::debuginfo::{ DIBasicType, DIBuilder, DICompositeType, DIDescriptor, DIFile, DIFlags, DILexicalBlock, DIScope, DIType, DebugEmissionKind, DebugNameTableKind, }; +use crate::llvm::{self, FromGeneric}; use crate::value::Value; impl PartialEq for llvm::Metadata { diff --git a/compiler/rustc_codegen_llvm/src/declare.rs b/compiler/rustc_codegen_llvm/src/declare.rs index 36cdb4988395..f598763efcf2 100644 --- a/compiler/rustc_codegen_llvm/src/declare.rs +++ b/compiler/rustc_codegen_llvm/src/declare.rs @@ -26,7 +26,7 @@ use crate::abi::FnAbiLlvmExt; use crate::common::AsCCharPtr; use crate::context::{CodegenCx, GenericCx, SCx, SimpleCx}; use crate::llvm::AttributePlace::Function; -use crate::llvm::Visibility; +use crate::llvm::{FromGeneric, Visibility}; use crate::type_::Type; use crate::value::Value; use crate::{attributes, llvm}; diff --git a/compiler/rustc_codegen_llvm/src/llvm/conversions.rs b/compiler/rustc_codegen_llvm/src/llvm/conversions.rs new file mode 100644 index 000000000000..9e9f9339ade8 --- /dev/null +++ b/compiler/rustc_codegen_llvm/src/llvm/conversions.rs @@ -0,0 +1,115 @@ +//! Conversions from backend-independent data types to/from LLVM FFI types. + +use rustc_codegen_ssa::common::{AtomicRmwBinOp, IntPredicate, RealPredicate}; +use rustc_middle::ty::AtomicOrdering; +use rustc_session::config::DebugInfo; +use rustc_target::spec::SymbolVisibility; + +use crate::llvm; + +/// Helper trait for converting backend-independent types to LLVM-specific +/// types, for FFI purposes. +pub(crate) trait FromGeneric { + fn from_generic(other: T) -> Self; +} + +impl FromGeneric for llvm::Visibility { + fn from_generic(visibility: SymbolVisibility) -> Self { + match visibility { + SymbolVisibility::Hidden => Self::Hidden, + SymbolVisibility::Protected => Self::Protected, + SymbolVisibility::Interposable => Self::Default, + } + } +} + +impl FromGeneric for llvm::IntPredicate { + fn from_generic(int_pred: IntPredicate) -> Self { + match int_pred { + IntPredicate::IntEQ => Self::IntEQ, + IntPredicate::IntNE => Self::IntNE, + IntPredicate::IntUGT => Self::IntUGT, + IntPredicate::IntUGE => Self::IntUGE, + IntPredicate::IntULT => Self::IntULT, + IntPredicate::IntULE => Self::IntULE, + IntPredicate::IntSGT => Self::IntSGT, + IntPredicate::IntSGE => Self::IntSGE, + IntPredicate::IntSLT => Self::IntSLT, + IntPredicate::IntSLE => Self::IntSLE, + } + } +} + +impl FromGeneric for llvm::RealPredicate { + fn from_generic(real_pred: RealPredicate) -> Self { + match real_pred { + RealPredicate::RealPredicateFalse => Self::RealPredicateFalse, + RealPredicate::RealOEQ => Self::RealOEQ, + RealPredicate::RealOGT => Self::RealOGT, + RealPredicate::RealOGE => Self::RealOGE, + RealPredicate::RealOLT => Self::RealOLT, + RealPredicate::RealOLE => Self::RealOLE, + RealPredicate::RealONE => Self::RealONE, + RealPredicate::RealORD => Self::RealORD, + RealPredicate::RealUNO => Self::RealUNO, + RealPredicate::RealUEQ => Self::RealUEQ, + RealPredicate::RealUGT => Self::RealUGT, + RealPredicate::RealUGE => Self::RealUGE, + RealPredicate::RealULT => Self::RealULT, + RealPredicate::RealULE => Self::RealULE, + RealPredicate::RealUNE => Self::RealUNE, + RealPredicate::RealPredicateTrue => Self::RealPredicateTrue, + } + } +} + +impl FromGeneric for llvm::AtomicRmwBinOp { + fn from_generic(op: AtomicRmwBinOp) -> Self { + match op { + AtomicRmwBinOp::AtomicXchg => Self::AtomicXchg, + AtomicRmwBinOp::AtomicAdd => Self::AtomicAdd, + AtomicRmwBinOp::AtomicSub => Self::AtomicSub, + AtomicRmwBinOp::AtomicAnd => Self::AtomicAnd, + AtomicRmwBinOp::AtomicNand => Self::AtomicNand, + AtomicRmwBinOp::AtomicOr => Self::AtomicOr, + AtomicRmwBinOp::AtomicXor => Self::AtomicXor, + AtomicRmwBinOp::AtomicMax => Self::AtomicMax, + AtomicRmwBinOp::AtomicMin => Self::AtomicMin, + AtomicRmwBinOp::AtomicUMax => Self::AtomicUMax, + AtomicRmwBinOp::AtomicUMin => Self::AtomicUMin, + } + } +} + +impl FromGeneric for llvm::AtomicOrdering { + fn from_generic(ordering: AtomicOrdering) -> Self { + match ordering { + AtomicOrdering::Relaxed => Self::Monotonic, + AtomicOrdering::Acquire => Self::Acquire, + AtomicOrdering::Release => Self::Release, + AtomicOrdering::AcqRel => Self::AcquireRelease, + AtomicOrdering::SeqCst => Self::SequentiallyConsistent, + } + } +} + +impl FromGeneric for llvm::debuginfo::DebugEmissionKind { + fn from_generic(kind: DebugInfo) -> Self { + // We should be setting LLVM's emission kind to `LineTablesOnly` if + // we are compiling with "limited" debuginfo. However, some of the + // existing tools relied on slightly more debuginfo being generated than + // would be the case with `LineTablesOnly`, and we did not want to break + // these tools in a "drive-by fix", without a good idea or plan about + // what limited debuginfo should exactly look like. So for now we are + // instead adding a new debuginfo option "line-tables-only" so as to + // not break anything and to allow users to have 'limited' debug info. + // + // See https://github.com/rust-lang/rust/issues/60020 for details. + match kind { + DebugInfo::None => Self::NoDebug, + DebugInfo::LineDirectivesOnly => Self::DebugDirectivesOnly, + DebugInfo::LineTablesOnly => Self::LineTablesOnly, + DebugInfo::Limited | DebugInfo::Full => Self::FullDebug, + } + } +} diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 7fbba0294073..965e775bed70 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -20,7 +20,6 @@ use std::ptr; use bitflags::bitflags; use libc::{c_char, c_int, c_uchar, c_uint, c_ulonglong, c_void, size_t}; use rustc_macros::TryFromU32; -use rustc_target::spec::SymbolVisibility; use super::RustString; use super::debuginfo::{ @@ -220,16 +219,6 @@ pub(crate) enum Visibility { Protected = 2, } -impl Visibility { - pub(crate) fn from_generic(visibility: SymbolVisibility) -> Self { - match visibility { - SymbolVisibility::Hidden => Visibility::Hidden, - SymbolVisibility::Protected => Visibility::Protected, - SymbolVisibility::Interposable => Visibility::Default, - } - } -} - /// LLVMUnnamedAddr #[repr(C)] pub(crate) enum UnnamedAddr { @@ -319,24 +308,6 @@ pub(crate) enum IntPredicate { IntSLE = 41, } -impl IntPredicate { - pub(crate) fn from_generic(intpre: rustc_codegen_ssa::common::IntPredicate) -> Self { - use rustc_codegen_ssa::common::IntPredicate as Common; - match intpre { - Common::IntEQ => Self::IntEQ, - Common::IntNE => Self::IntNE, - Common::IntUGT => Self::IntUGT, - Common::IntUGE => Self::IntUGE, - Common::IntULT => Self::IntULT, - Common::IntULE => Self::IntULE, - Common::IntSGT => Self::IntSGT, - Common::IntSGE => Self::IntSGE, - Common::IntSLT => Self::IntSLT, - Common::IntSLE => Self::IntSLE, - } - } -} - /// LLVMRealPredicate #[derive(Copy, Clone)] #[repr(C)] @@ -359,30 +330,6 @@ pub(crate) enum RealPredicate { RealPredicateTrue = 15, } -impl RealPredicate { - pub(crate) fn from_generic(realp: rustc_codegen_ssa::common::RealPredicate) -> Self { - use rustc_codegen_ssa::common::RealPredicate as Common; - match realp { - Common::RealPredicateFalse => Self::RealPredicateFalse, - Common::RealOEQ => Self::RealOEQ, - Common::RealOGT => Self::RealOGT, - Common::RealOGE => Self::RealOGE, - Common::RealOLT => Self::RealOLT, - Common::RealOLE => Self::RealOLE, - Common::RealONE => Self::RealONE, - Common::RealORD => Self::RealORD, - Common::RealUNO => Self::RealUNO, - Common::RealUEQ => Self::RealUEQ, - Common::RealUGT => Self::RealUGT, - Common::RealUGE => Self::RealUGE, - Common::RealULT => Self::RealULT, - Common::RealULE => Self::RealULE, - Common::RealUNE => Self::RealUNE, - Common::RealPredicateTrue => Self::RealPredicateTrue, - } - } -} - /// Must match the layout of `LLVMTypeKind`. /// /// Use [`RawEnum`] for values of `LLVMTypeKind` returned from LLVM, @@ -458,25 +405,6 @@ pub(crate) enum AtomicRmwBinOp { AtomicUMin = 10, } -impl AtomicRmwBinOp { - pub(crate) fn from_generic(op: rustc_codegen_ssa::common::AtomicRmwBinOp) -> Self { - use rustc_codegen_ssa::common::AtomicRmwBinOp as Common; - match op { - Common::AtomicXchg => Self::AtomicXchg, - Common::AtomicAdd => Self::AtomicAdd, - Common::AtomicSub => Self::AtomicSub, - Common::AtomicAnd => Self::AtomicAnd, - Common::AtomicNand => Self::AtomicNand, - Common::AtomicOr => Self::AtomicOr, - Common::AtomicXor => Self::AtomicXor, - Common::AtomicMax => Self::AtomicMax, - Common::AtomicMin => Self::AtomicMin, - Common::AtomicUMax => Self::AtomicUMax, - Common::AtomicUMin => Self::AtomicUMin, - } - } -} - /// LLVMAtomicOrdering #[derive(Copy, Clone)] #[repr(C)] @@ -493,19 +421,6 @@ pub(crate) enum AtomicOrdering { SequentiallyConsistent = 7, } -impl AtomicOrdering { - pub(crate) fn from_generic(ao: rustc_middle::ty::AtomicOrdering) -> Self { - use rustc_middle::ty::AtomicOrdering as Common; - match ao { - Common::Relaxed => Self::Monotonic, - Common::Acquire => Self::Acquire, - Common::Release => Self::Release, - Common::AcqRel => Self::AcquireRelease, - Common::SeqCst => Self::SequentiallyConsistent, - } - } -} - /// LLVMRustFileType #[derive(Copy, Clone)] #[repr(C)] @@ -940,28 +855,6 @@ pub(crate) mod debuginfo { DebugDirectivesOnly, } - impl DebugEmissionKind { - pub(crate) fn from_generic(kind: rustc_session::config::DebugInfo) -> Self { - // We should be setting LLVM's emission kind to `LineTablesOnly` if - // we are compiling with "limited" debuginfo. However, some of the - // existing tools relied on slightly more debuginfo being generated than - // would be the case with `LineTablesOnly`, and we did not want to break - // these tools in a "drive-by fix", without a good idea or plan about - // what limited debuginfo should exactly look like. So for now we are - // instead adding a new debuginfo option "line-tables-only" so as to - // not break anything and to allow users to have 'limited' debug info. - // - // See https://github.com/rust-lang/rust/issues/60020 for details. - use rustc_session::config::DebugInfo; - match kind { - DebugInfo::None => DebugEmissionKind::NoDebug, - DebugInfo::LineDirectivesOnly => DebugEmissionKind::DebugDirectivesOnly, - DebugInfo::LineTablesOnly => DebugEmissionKind::LineTablesOnly, - DebugInfo::Limited | DebugInfo::Full => DebugEmissionKind::FullDebug, - } - } - } - /// LLVMRustDebugNameTableKind #[derive(Clone, Copy)] #[repr(C)] diff --git a/compiler/rustc_codegen_llvm/src/llvm/mod.rs b/compiler/rustc_codegen_llvm/src/llvm/mod.rs index 9a53dacb1dfd..4c58a92106d5 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs @@ -11,10 +11,12 @@ use rustc_llvm::RustString; pub(crate) use self::CallConv::*; pub(crate) use self::CodeGenOptSize::*; +pub(crate) use self::conversions::*; pub(crate) use self::ffi::*; pub(crate) use self::metadata_kind::*; use crate::common::AsCCharPtr; +mod conversions; pub(crate) mod diagnostic; pub(crate) mod enzyme_ffi; mod ffi; From 0661a3d6fe4efd7127c66aeb6484f06b3441c63f Mon Sep 17 00:00:00 2001 From: Zalathar Date: Fri, 3 Oct 2025 17:11:53 +1000 Subject: [PATCH 147/226] Remove inherent methods from `coverageinfo::ffi::Counter` This patch also moves `Regions` to a different module, since that type can stay put when the FFI bindings move to another crate. --- .../src/coverageinfo/ffi.rs | 45 +------------------ .../src/coverageinfo/llvm_cov.rs | 27 ++++++++++- .../src/coverageinfo/mapgen/covfun.rs | 27 +++++++---- 3 files changed, 45 insertions(+), 54 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs index a4b60d420f3f..caa36c5dd499 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs @@ -1,5 +1,3 @@ -use rustc_middle::mir::coverage::{CounterId, CovTerm, ExpressionId}; - /// Must match the layout of `LLVMRustCounterKind`. #[derive(Copy, Clone, Debug)] #[repr(C)] @@ -26,30 +24,12 @@ pub(crate) enum CounterKind { pub(crate) struct Counter { // Important: The layout (order and types of fields) must match its C++ counterpart. pub(crate) kind: CounterKind, - id: u32, + pub(crate) id: u32, } impl Counter { /// A `Counter` of kind `Zero`. For this counter kind, the `id` is not used. pub(crate) const ZERO: Self = Self { kind: CounterKind::Zero, id: 0 }; - - /// Constructs a new `Counter` of kind `CounterValueReference`. - pub(crate) fn counter_value_reference(counter_id: CounterId) -> Self { - Self { kind: CounterKind::CounterValueReference, id: counter_id.as_u32() } - } - - /// Constructs a new `Counter` of kind `Expression`. - pub(crate) fn expression(expression_id: ExpressionId) -> Self { - Self { kind: CounterKind::Expression, id: expression_id.as_u32() } - } - - pub(crate) fn from_term(term: CovTerm) -> Self { - match term { - CovTerm::Zero => Self::ZERO, - CovTerm::Counter(id) => Self::counter_value_reference(id), - CovTerm::Expression(id) => Self::expression(id), - } - } } /// Corresponds to enum `llvm::coverage::CounterExpression::ExprKind`. @@ -94,29 +74,6 @@ pub(crate) struct CoverageSpan { pub(crate) end_col: u32, } -/// Holds tables of the various region types in one struct. -/// -/// Don't pass this struct across FFI; pass the individual region tables as -/// pointer/length pairs instead. -/// -/// Each field name has a `_regions` suffix for improved readability after -/// exhaustive destructing, which ensures that all region types are handled. -#[derive(Clone, Debug, Default)] -pub(crate) struct Regions { - pub(crate) code_regions: Vec, - pub(crate) expansion_regions: Vec, - pub(crate) branch_regions: Vec, -} - -impl Regions { - /// Returns true if none of this structure's tables contain any regions. - pub(crate) fn has_no_regions(&self) -> bool { - let Self { code_regions, expansion_regions, branch_regions } = self; - - code_regions.is_empty() && expansion_regions.is_empty() && branch_regions.is_empty() - } -} - /// Must match the layout of `LLVMRustCoverageCodeRegion`. #[derive(Clone, Debug)] #[repr(C)] diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/llvm_cov.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/llvm_cov.rs index d50eb533ffdb..a58202834cfa 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/llvm_cov.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/llvm_cov.rs @@ -57,12 +57,35 @@ pub(crate) fn write_filenames_to_buffer(filenames: &[impl AsRef]) -> Vec, + pub(crate) expansion_regions: Vec, + pub(crate) branch_regions: Vec, +} + +impl Regions { + /// Returns true if none of this structure's tables contain any regions. + pub(crate) fn has_no_regions(&self) -> bool { + let Self { code_regions, expansion_regions, branch_regions } = self; + + code_regions.is_empty() && expansion_regions.is_empty() && branch_regions.is_empty() + } +} + pub(crate) fn write_function_mappings_to_buffer( virtual_file_mapping: &[u32], expressions: &[ffi::CounterExpression], - regions: &ffi::Regions, + regions: &Regions, ) -> Vec { - let ffi::Regions { code_regions, expansion_regions, branch_regions } = regions; + let Regions { code_regions, expansion_regions, branch_regions } = regions; // SAFETY: // - All types are FFI-compatible and have matching representations in Rust/C++. diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs index e0da8d368762..7835d1804686 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs @@ -10,8 +10,8 @@ use std::sync::Arc; use rustc_abi::Align; use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods as _, ConstCodegenMethods}; use rustc_middle::mir::coverage::{ - BasicCoverageBlock, CovTerm, CoverageIdsInfo, Expression, FunctionCoverageInfo, Mapping, - MappingKind, Op, + BasicCoverageBlock, CounterId, CovTerm, CoverageIdsInfo, Expression, ExpressionId, + FunctionCoverageInfo, Mapping, MappingKind, Op, }; use rustc_middle::ty::{Instance, TyCtxt}; use rustc_span::{SourceFile, Span}; @@ -36,7 +36,7 @@ pub(crate) struct CovfunRecord<'tcx> { virtual_file_mapping: VirtualFileMapping, expressions: Vec, - regions: ffi::Regions, + regions: llvm_cov::Regions, } impl<'tcx> CovfunRecord<'tcx> { @@ -64,7 +64,7 @@ pub(crate) fn prepare_covfun_record<'tcx>( is_used, virtual_file_mapping: VirtualFileMapping::default(), expressions, - regions: ffi::Regions::default(), + regions: llvm_cov::Regions::default(), }; fill_region_tables(tcx, fn_cov_info, ids_info, &mut covfun); @@ -77,10 +77,21 @@ pub(crate) fn prepare_covfun_record<'tcx>( Some(covfun) } +pub(crate) fn counter_for_term(term: CovTerm) -> ffi::Counter { + use ffi::Counter; + match term { + CovTerm::Zero => Counter::ZERO, + CovTerm::Counter(id) => { + Counter { kind: ffi::CounterKind::CounterValueReference, id: CounterId::as_u32(id) } + } + CovTerm::Expression(id) => { + Counter { kind: ffi::CounterKind::Expression, id: ExpressionId::as_u32(id) } + } + } +} + /// Convert the function's coverage-counter expressions into a form suitable for FFI. fn prepare_expressions(ids_info: &CoverageIdsInfo) -> Vec { - let counter_for_term = ffi::Counter::from_term; - // We know that LLVM will optimize out any unused expressions before // producing the final coverage map, so there's no need to do the same // thing on the Rust side unless we're confident we can do much better. @@ -113,7 +124,7 @@ fn fill_region_tables<'tcx>( } else { CovTerm::Zero }; - ffi::Counter::from_term(term) + counter_for_term(term) }; // Currently a function's mappings must all be in the same file, so use the @@ -136,7 +147,7 @@ fn fill_region_tables<'tcx>( if discard_all { None } else { spans::make_coords(source_map, &source_file, span) } }; - let ffi::Regions { + let llvm_cov::Regions { code_regions, expansion_regions: _, // FIXME(Zalathar): Fill out support for expansion regions branch_regions, From 3831b60ef3dd46d11f63041016bd1e6f6f9039f5 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sat, 4 Oct 2025 17:54:03 +1000 Subject: [PATCH 148/226] Remove inherent methods from `llvm::Type` --- compiler/rustc_codegen_llvm/src/back/write.rs | 4 ++-- compiler/rustc_codegen_llvm/src/context.rs | 2 +- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 4 ++-- compiler/rustc_codegen_llvm/src/type_.rs | 24 +++++++++---------- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 1950b8288d14..6ce1cbe072c0 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -44,7 +44,7 @@ use crate::errors::{ }; use crate::llvm::diagnostic::OptimizationDiagnosticKind::*; use crate::llvm::{self, DiagnosticInfo}; -use crate::type_::Type; +use crate::type_::llvm_type_ptr; use crate::{LlvmCodegenBackend, ModuleLlvm, base, common, llvm_util}; pub(crate) fn llvm_err<'a>(dcx: DiagCtxtHandle<'_>, err: LlvmError<'a>) -> ! { @@ -1160,7 +1160,7 @@ fn create_msvc_imps( // underscores added in front). let prefix = if cgcx.target_arch == "x86" { "\x01__imp__" } else { "\x01__imp_" }; - let ptr_ty = Type::ptr_llcx(llcx); + let ptr_ty = llvm_type_ptr(llcx); let globals = base::iter_globals(llmod) .filter(|&val| { llvm::get_linkage(val) == llvm::Linkage::ExternalLinkage && !llvm::is_declaration(val) diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 922575dd63cf..cda5b3a5f9aa 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -740,7 +740,7 @@ impl<'ll> SimpleCx<'ll> { llcx: &'ll llvm::Context, pointer_size: Size, ) -> Self { - let isize_ty = llvm::Type::ix_llcx(llcx, pointer_size.bits()); + let isize_ty = llvm::LLVMIntTypeInContext(llcx, pointer_size.bits() as c_uint); Self(SCx { llmod, llcx, isize_ty }, PhantomData) } } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 965e775bed70..ccbc999dad1f 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -954,7 +954,7 @@ unsafe extern "C" { pub(crate) fn LLVMInt16TypeInContext(C: &Context) -> &Type; pub(crate) fn LLVMInt32TypeInContext(C: &Context) -> &Type; pub(crate) fn LLVMInt64TypeInContext(C: &Context) -> &Type; - pub(crate) fn LLVMIntTypeInContext(C: &Context, NumBits: c_uint) -> &Type; + pub(crate) safe fn LLVMIntTypeInContext(C: &Context, NumBits: c_uint) -> &Type; pub(crate) fn LLVMGetIntTypeWidth(IntegerTy: &Type) -> c_uint; @@ -983,7 +983,7 @@ unsafe extern "C" { ) -> &'a Type; // Operations on array, pointer, and vector types (sequence types) - pub(crate) fn LLVMPointerTypeInContext(C: &Context, AddressSpace: c_uint) -> &Type; + pub(crate) safe fn LLVMPointerTypeInContext(C: &Context, AddressSpace: c_uint) -> &Type; pub(crate) fn LLVMVectorType(ElementType: &Type, ElementCount: c_uint) -> &Type; pub(crate) fn LLVMGetElementType(Ty: &Type) -> &Type; diff --git a/compiler/rustc_codegen_llvm/src/type_.rs b/compiler/rustc_codegen_llvm/src/type_.rs index 1e5cf8374e37..9de9e0ec44c6 100644 --- a/compiler/rustc_codegen_llvm/src/type_.rs +++ b/compiler/rustc_codegen_llvm/src/type_.rs @@ -63,7 +63,7 @@ impl<'ll, CX: Borrow>> GenericCx<'ll, CX> { ///x Creates an integer type with the given number of bits, e.g., i24 pub(crate) fn type_ix(&self, num_bits: u64) -> &'ll Type { - unsafe { llvm::LLVMIntTypeInContext(self.llcx(), num_bits as c_uint) } + llvm::LLVMIntTypeInContext(self.llcx(), num_bits as c_uint) } pub(crate) fn type_vector(&self, ty: &'ll Type, len: u64) -> &'ll Type { @@ -178,7 +178,7 @@ impl<'ll, CX: Borrow>> BaseTypeCodegenMethods for GenericCx<'ll, CX> { } fn type_i128(&self) -> &'ll Type { - unsafe { llvm::LLVMIntTypeInContext(self.llcx(), 128) } + self.type_ix(128) } fn type_isize(&self) -> &'ll Type { @@ -210,11 +210,11 @@ impl<'ll, CX: Borrow>> BaseTypeCodegenMethods for GenericCx<'ll, CX> { } fn type_ptr(&self) -> &'ll Type { - self.type_ptr_ext(AddressSpace::ZERO) + llvm_type_ptr(self.llcx()) } fn type_ptr_ext(&self, address_space: AddressSpace) -> &'ll Type { - unsafe { llvm::LLVMPointerTypeInContext(self.llcx(), address_space.0) } + llvm_type_ptr_in_address_space(self.llcx(), address_space) } fn element_type(&self, ty: &'ll Type) -> &'ll Type { @@ -253,15 +253,15 @@ impl<'ll, CX: Borrow>> BaseTypeCodegenMethods for GenericCx<'ll, CX> { } } -impl Type { - /// Creates an integer type with the given number of bits, e.g., i24 - pub(crate) fn ix_llcx(llcx: &llvm::Context, num_bits: u64) -> &Type { - unsafe { llvm::LLVMIntTypeInContext(llcx, num_bits as c_uint) } - } +pub(crate) fn llvm_type_ptr(llcx: &llvm::Context) -> &Type { + llvm_type_ptr_in_address_space(llcx, AddressSpace::ZERO) +} - pub(crate) fn ptr_llcx(llcx: &llvm::Context) -> &Type { - unsafe { llvm::LLVMPointerTypeInContext(llcx, AddressSpace::ZERO.0) } - } +pub(crate) fn llvm_type_ptr_in_address_space<'ll>( + llcx: &'ll llvm::Context, + addr_space: AddressSpace, +) -> &'ll Type { + llvm::LLVMPointerTypeInContext(llcx, addr_space.0) } impl<'ll, 'tcx> LayoutTypeCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { From f8c54d24e2f4ca8539cb6d9b174e4c873bbb3ed7 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Fri, 3 Oct 2025 17:25:03 +1000 Subject: [PATCH 149/226] Remove inherent methods from `llvm::CallConv::from_conv` --- compiler/rustc_codegen_llvm/src/abi.rs | 78 +++++++++++----------- compiler/rustc_codegen_llvm/src/context.rs | 6 +- 2 files changed, 41 insertions(+), 43 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index 1703cab942bd..15b4d5247a97 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -1,4 +1,3 @@ -use std::borrow::Borrow; use std::cmp; use libc::c_uint; @@ -13,7 +12,7 @@ use rustc_codegen_ssa::traits::*; use rustc_middle::ty::Ty; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::{bug, ty}; -use rustc_session::config; +use rustc_session::{Session, config}; use rustc_target::callconv::{ ArgAbi, ArgAttribute, ArgAttributes, ArgExtension, CastTarget, FnAbi, PassMode, }; @@ -400,7 +399,7 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { } fn llvm_cconv(&self, cx: &CodegenCx<'ll, 'tcx>) -> llvm::CallConv { - llvm::CallConv::from_conv(self.conv, cx.tcx.sess.target.arch.borrow()) + to_llvm_calling_convention(cx.tcx.sess, self.conv) } fn apply_attrs_llfn( @@ -663,43 +662,44 @@ impl AbiBuilderMethods for Builder<'_, '_, '_> { } } -impl llvm::CallConv { - pub(crate) fn from_conv(conv: CanonAbi, arch: &str) -> Self { - match conv { - CanonAbi::C | CanonAbi::Rust => llvm::CCallConv, - CanonAbi::RustCold => llvm::PreserveMost, - // Functions with this calling convention can only be called from assembly, but it is - // possible to declare an `extern "custom"` block, so the backend still needs a calling - // convention for declaring foreign functions. - CanonAbi::Custom => llvm::CCallConv, - CanonAbi::GpuKernel => { - if arch == "amdgpu" { - llvm::AmdgpuKernel - } else if arch == "nvptx64" { - llvm::PtxKernel - } else { - panic!("Architecture {arch} does not support GpuKernel calling convention"); - } +/// Determines the appropriate [`llvm::CallConv`] to use for a given function +/// ABI, for the current target. +pub(crate) fn to_llvm_calling_convention(sess: &Session, abi: CanonAbi) -> llvm::CallConv { + match abi { + CanonAbi::C | CanonAbi::Rust => llvm::CCallConv, + CanonAbi::RustCold => llvm::PreserveMost, + // Functions with this calling convention can only be called from assembly, but it is + // possible to declare an `extern "custom"` block, so the backend still needs a calling + // convention for declaring foreign functions. + CanonAbi::Custom => llvm::CCallConv, + CanonAbi::GpuKernel => { + let arch = sess.target.arch.as_ref(); + if arch == "amdgpu" { + llvm::AmdgpuKernel + } else if arch == "nvptx64" { + llvm::PtxKernel + } else { + panic!("Architecture {arch} does not support GpuKernel calling convention"); } - CanonAbi::Interrupt(interrupt_kind) => match interrupt_kind { - InterruptKind::Avr => llvm::AvrInterrupt, - InterruptKind::AvrNonBlocking => llvm::AvrNonBlockingInterrupt, - InterruptKind::Msp430 => llvm::Msp430Intr, - InterruptKind::RiscvMachine | InterruptKind::RiscvSupervisor => llvm::CCallConv, - InterruptKind::X86 => llvm::X86_Intr, - }, - CanonAbi::Arm(arm_call) => match arm_call { - ArmCall::Aapcs => llvm::ArmAapcsCallConv, - ArmCall::CCmseNonSecureCall | ArmCall::CCmseNonSecureEntry => llvm::CCallConv, - }, - CanonAbi::X86(x86_call) => match x86_call { - X86Call::Fastcall => llvm::X86FastcallCallConv, - X86Call::Stdcall => llvm::X86StdcallCallConv, - X86Call::SysV64 => llvm::X86_64_SysV, - X86Call::Thiscall => llvm::X86_ThisCall, - X86Call::Vectorcall => llvm::X86_VectorCall, - X86Call::Win64 => llvm::X86_64_Win64, - }, } + CanonAbi::Interrupt(interrupt_kind) => match interrupt_kind { + InterruptKind::Avr => llvm::AvrInterrupt, + InterruptKind::AvrNonBlocking => llvm::AvrNonBlockingInterrupt, + InterruptKind::Msp430 => llvm::Msp430Intr, + InterruptKind::RiscvMachine | InterruptKind::RiscvSupervisor => llvm::CCallConv, + InterruptKind::X86 => llvm::X86_Intr, + }, + CanonAbi::Arm(arm_call) => match arm_call { + ArmCall::Aapcs => llvm::ArmAapcsCallConv, + ArmCall::CCmseNonSecureCall | ArmCall::CCmseNonSecureEntry => llvm::CCallConv, + }, + CanonAbi::X86(x86_call) => match x86_call { + X86Call::Fastcall => llvm::X86FastcallCallConv, + X86Call::Stdcall => llvm::X86StdcallCallConv, + X86Call::SysV64 => llvm::X86_64_SysV, + X86Call::Thiscall => llvm::X86_ThisCall, + X86Call::Vectorcall => llvm::X86_VectorCall, + X86Call::Win64 => llvm::X86_64_Win64, + }, } } diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index cda5b3a5f9aa..3ddbd81ef758 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -31,6 +31,7 @@ use rustc_symbol_mangling::mangle_internal_symbol; use rustc_target::spec::{HasTargetSpec, RelocModel, SmallDataThresholdSupport, Target, TlsModel}; use smallvec::SmallVec; +use crate::abi::to_llvm_calling_convention; use crate::back::write::to_llvm_code_model; use crate::callee::get_fn; use crate::debuginfo::metadata::apply_vcall_visibility_metadata; @@ -901,10 +902,7 @@ impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { if self.get_declared_value(entry_name).is_none() { let llfn = self.declare_entry_fn( entry_name, - llvm::CallConv::from_conv( - self.sess().target.entry_abi, - self.sess().target.arch.borrow(), - ), + to_llvm_calling_convention(self.sess(), self.sess().target.entry_abi), llvm::UnnamedAddr::Global, fn_type, ); From e9a45e66461f3c324a249d7352eb2f4d184a6b2a Mon Sep 17 00:00:00 2001 From: yukang Date: Sat, 4 Oct 2025 14:48:55 +0800 Subject: [PATCH 150/226] Avoid to suggest pattern match on the similarly named in fn signature --- compiler/rustc_passes/src/liveness.rs | 7 +++++-- .../ui/fn/invalid-sugg-for-unused-fn-arg-147303.rs | 13 +++++++++++++ .../invalid-sugg-for-unused-fn-arg-147303.stderr | 14 ++++++++++++++ .../non-snake-case/lint-uppercase-variables.stderr | 11 +---------- 4 files changed, 33 insertions(+), 12 deletions(-) create mode 100644 tests/ui/fn/invalid-sugg-for-unused-fn-arg-147303.rs create mode 100644 tests/ui/fn/invalid-sugg-for-unused-fn-arg-147303.stderr diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index 1b2ffb5b3db2..b628ff8a12df 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -1691,7 +1691,10 @@ impl<'tcx> Liveness<'_, 'tcx> { if ln == self.exit_ln { false } else { self.assigned_on_exit(ln, var) }; let mut typo = None; - for (hir_id, _, span) in &hir_ids_and_spans { + let filtered_hir_ids_and_spans = hir_ids_and_spans.iter().filter(|(hir_id, ..)| { + !matches!(self.ir.tcx.parent_hir_node(*hir_id), hir::Node::Param(_)) + }); + for (hir_id, _, span) in filtered_hir_ids_and_spans.clone() { let ty = self.typeck_results.node_type(*hir_id); if let ty::Adt(adt, _) = ty.peel_refs().kind() { let name = Symbol::intern(&name); @@ -1717,7 +1720,7 @@ impl<'tcx> Liveness<'_, 'tcx> { } } if typo.is_none() { - for (hir_id, _, span) in &hir_ids_and_spans { + for (hir_id, _, span) in filtered_hir_ids_and_spans { let ty = self.typeck_results.node_type(*hir_id); // Look for consts of the same type with similar names as well, not just unit // structs and variants. diff --git a/tests/ui/fn/invalid-sugg-for-unused-fn-arg-147303.rs b/tests/ui/fn/invalid-sugg-for-unused-fn-arg-147303.rs new file mode 100644 index 000000000000..137ba8dc103e --- /dev/null +++ b/tests/ui/fn/invalid-sugg-for-unused-fn-arg-147303.rs @@ -0,0 +1,13 @@ +// Regression test for . + +#![deny(unused_assignments, unused_variables)] + +mod m1 { + const _MAX_FMTVER_X1X_EVENTNUM: i32 = 0; +} + +mod m2 { + fn fun(rough: i32) {} //~ERROR unused variable +} + +fn main() {} diff --git a/tests/ui/fn/invalid-sugg-for-unused-fn-arg-147303.stderr b/tests/ui/fn/invalid-sugg-for-unused-fn-arg-147303.stderr new file mode 100644 index 000000000000..30e4a758c13c --- /dev/null +++ b/tests/ui/fn/invalid-sugg-for-unused-fn-arg-147303.stderr @@ -0,0 +1,14 @@ +error: unused variable: `rough` + --> $DIR/invalid-sugg-for-unused-fn-arg-147303.rs:10:12 + | +LL | fn fun(rough: i32) {} + | ^^^^^ help: if this is intentional, prefix it with an underscore: `_rough` + | +note: the lint level is defined here + --> $DIR/invalid-sugg-for-unused-fn-arg-147303.rs:3:29 + | +LL | #![deny(unused_assignments, unused_variables)] + | ^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/lint/non-snake-case/lint-uppercase-variables.stderr b/tests/ui/lint/non-snake-case/lint-uppercase-variables.stderr index e5f2e65fd91d..5dec20b2ac7e 100644 --- a/tests/ui/lint/non-snake-case/lint-uppercase-variables.stderr +++ b/tests/ui/lint/non-snake-case/lint-uppercase-variables.stderr @@ -58,16 +58,7 @@ warning: unused variable: `Foo` --> $DIR/lint-uppercase-variables.rs:33:17 | LL | fn in_param(Foo: foo::Foo) {} - | ^^^ - | -help: if this is intentional, prefix it with an underscore - | -LL | fn in_param(_Foo: foo::Foo) {} - | + -help: you might have meant to pattern match on the similarly named variant `Foo` - | -LL | fn in_param(foo::Foo::Foo: foo::Foo) {} - | ++++++++++ + | ^^^ help: if this is intentional, prefix it with an underscore: `_Foo` error: structure field `X` should have a snake case name --> $DIR/lint-uppercase-variables.rs:10:5 From ba42380142e73765eab501b3ebc2b3e05ee6b0c0 Mon Sep 17 00:00:00 2001 From: EFanZh Date: Sat, 4 Oct 2025 17:16:00 +0800 Subject: [PATCH 151/226] Implement non-poisoning `Mutex::with_mut`, `RwLock::with` and `RwLock::with_mut` ACP: https://github.com/rust-lang/libs-team/issues/497. --- library/std/src/sync/nonpoison/mutex.rs | 34 +++++++++++++ library/std/src/sync/nonpoison/rwlock.rs | 62 ++++++++++++++++++++++++ library/std/tests/sync/mutex.rs | 14 ++++++ library/std/tests/sync/rwlock.rs | 22 +++++++++ 4 files changed, 132 insertions(+) diff --git a/library/std/src/sync/nonpoison/mutex.rs b/library/std/src/sync/nonpoison/mutex.rs index eeecf5d71076..ed3f8cfed821 100644 --- a/library/std/src/sync/nonpoison/mutex.rs +++ b/library/std/src/sync/nonpoison/mutex.rs @@ -376,6 +376,40 @@ impl Mutex { pub const fn data_ptr(&self) -> *mut T { self.data.get() } + + /// Acquires the mutex and provides mutable access to the underlying data by passing + /// a mutable reference to the given closure. + /// + /// This method acquires the lock, calls the provided closure with a mutable reference + /// to the data, and returns the result of the closure. The lock is released after + /// the closure completes, even if it panics. + /// + /// # Examples + /// + /// ``` + /// #![feature(lock_value_accessors, nonpoison_mutex)] + /// + /// use std::sync::nonpoison::Mutex; + /// + /// let mutex = Mutex::new(2); + /// + /// let result = mutex.with_mut(|data| { + /// *data += 3; + /// + /// *data + 5 + /// }); + /// + /// assert_eq!(*mutex.lock(), 5); + /// assert_eq!(result, 10); + /// ``` + #[unstable(feature = "lock_value_accessors", issue = "133407")] + // #[unstable(feature = "nonpoison_mutex", issue = "134645")] + pub fn with_mut(&self, f: F) -> R + where + F: FnOnce(&mut T) -> R, + { + f(&mut self.lock()) + } } #[unstable(feature = "nonpoison_mutex", issue = "134645")] diff --git a/library/std/src/sync/nonpoison/rwlock.rs b/library/std/src/sync/nonpoison/rwlock.rs index b2f26edc083e..568c7f386847 100644 --- a/library/std/src/sync/nonpoison/rwlock.rs +++ b/library/std/src/sync/nonpoison/rwlock.rs @@ -498,6 +498,68 @@ impl RwLock { pub const fn data_ptr(&self) -> *mut T { self.data.get() } + + /// Locks this `RwLock` with shared read access to the underlying data by passing + /// a reference to the given closure. + /// + /// This method acquires the lock, calls the provided closure with a reference + /// to the data, and returns the result of the closure. The lock is released after + /// the closure completes, even if it panics. + /// + /// # Examples + /// + /// ``` + /// #![feature(lock_value_accessors, nonpoison_rwlock)] + /// + /// use std::sync::nonpoison::RwLock; + /// + /// let rwlock = RwLock::new(2); + /// let result = rwlock.with(|data| *data + 3); + /// + /// assert_eq!(result, 5); + /// ``` + #[unstable(feature = "lock_value_accessors", issue = "133407")] + // #[unstable(feature = "nonpoison_rwlock", issue = "134645")] + pub fn with(&self, f: F) -> R + where + F: FnOnce(&T) -> R, + { + f(&self.read()) + } + + /// Locks this `RwLock` with exclusive write access to the underlying data by passing + /// a mutable reference to the given closure. + /// + /// This method acquires the lock, calls the provided closure with a mutable reference + /// to the data, and returns the result of the closure. The lock is released after + /// the closure completes, even if it panics. + /// + /// # Examples + /// + /// ``` + /// #![feature(lock_value_accessors, nonpoison_rwlock)] + /// + /// use std::sync::nonpoison::RwLock; + /// + /// let rwlock = RwLock::new(2); + /// + /// let result = rwlock.with_mut(|data| { + /// *data += 3; + /// + /// *data + 5 + /// }); + /// + /// assert_eq!(*rwlock.read(), 5); + /// assert_eq!(result, 10); + /// ``` + #[unstable(feature = "lock_value_accessors", issue = "133407")] + // #[unstable(feature = "nonpoison_rwlock", issue = "134645")] + pub fn with_mut(&self, f: F) -> R + where + F: FnOnce(&mut T) -> R, + { + f(&mut self.write()) + } } #[unstable(feature = "nonpoison_rwlock", issue = "134645")] diff --git a/library/std/tests/sync/mutex.rs b/library/std/tests/sync/mutex.rs index 2445764001b8..ff6aef717936 100644 --- a/library/std/tests/sync/mutex.rs +++ b/library/std/tests/sync/mutex.rs @@ -549,3 +549,17 @@ fn panic_while_mapping_unlocked_poison() { drop(lock); } + +#[test] +fn test_mutex_with_mut() { + let mutex = std::sync::nonpoison::Mutex::new(2); + + let result = mutex.with_mut(|value| { + *value += 3; + + *value + 5 + }); + + assert_eq!(*mutex.lock(), 5); + assert_eq!(result, 10); +} diff --git a/library/std/tests/sync/rwlock.rs b/library/std/tests/sync/rwlock.rs index 65d8bac71945..392c45c8ba05 100644 --- a/library/std/tests/sync/rwlock.rs +++ b/library/std/tests/sync/rwlock.rs @@ -861,3 +861,25 @@ fn panic_while_mapping_write_unlocked_poison() { drop(lock); } + +#[test] +fn test_rwlock_with() { + let rwlock = std::sync::nonpoison::RwLock::new(2); + let result = rwlock.with(|value| *value + 3); + + assert_eq!(result, 5); +} + +#[test] +fn test_rwlock_with_mut() { + let rwlock = std::sync::nonpoison::RwLock::new(2); + + let result = rwlock.with_mut(|value| { + *value += 3; + + *value + 5 + }); + + assert_eq!(*rwlock.read(), 5); + assert_eq!(result, 10); +} From 5c95f8bea6ea37d750910e5b0ac7ae049579e437 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Thu, 2 Oct 2025 10:15:00 +0200 Subject: [PATCH 152/226] Add failing regression test for #[link="dl"] --- .../ui/attributes/link-dl.default_fcw.stderr | 26 +++++++++++++++++++ tests/ui/attributes/link-dl.rs | 19 ++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 tests/ui/attributes/link-dl.default_fcw.stderr create mode 100644 tests/ui/attributes/link-dl.rs diff --git a/tests/ui/attributes/link-dl.default_fcw.stderr b/tests/ui/attributes/link-dl.default_fcw.stderr new file mode 100644 index 000000000000..6bff216ad03f --- /dev/null +++ b/tests/ui/attributes/link-dl.default_fcw.stderr @@ -0,0 +1,26 @@ +error[E0539]: malformed `link` attribute input + --> $DIR/link-dl.rs:14:1 + | +LL | #[link="dl"] + | ^^^^^^^^^^^^ expected this to be a list + | + = note: for more information, visit +help: try changing it to one of the following valid forms of the attribute + | +LL - #[link="dl"] +LL + #[link(name = "...")] + | +LL - #[link="dl"] +LL + #[link(name = "...", import_name_type = "decorated|noprefix|undecorated")] + | +LL - #[link="dl"] +LL + #[link(name = "...", kind = "dylib|static|...")] + | +LL - #[link="dl"] +LL + #[link(name = "...", kind = "dylib|static|...", wasm_import_module = "...", import_name_type = "decorated|noprefix|undecorated")] + | + = and 1 other candidate + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0539`. diff --git a/tests/ui/attributes/link-dl.rs b/tests/ui/attributes/link-dl.rs new file mode 100644 index 000000000000..0785c83cb442 --- /dev/null +++ b/tests/ui/attributes/link-dl.rs @@ -0,0 +1,19 @@ +// Regression test for an issue discovered in https://github.com/rust-lang/rust/pull/143193/files and rediscovered in https://github.com/rust-lang/rust/issues/147254#event-20049906781 +// Malformed #[link] attribute was supposed to be deny-by-default report-in-deps FCW, +// but accidentally was landed as a hard error. +// +// revision `default_fcw` tests that with `ill_formed_attribute_input` (the default) denied, +// the attribute produces an FCW +// revision `allowed` tests that with `ill_formed_attribute_input` allowed the test passes + +//@ revisions: default_fcw allowed +//@[allowed] check-pass + +#[cfg_attr(allowed, allow(ill_formed_attribute_input))] + +#[link="dl"] +//[default_fcw]~^ ERROR valid forms for the attribute are +//[default_fcw]~| WARN previously accepted +extern "C" { } + +fn main() {} From 1c85a1dc2eaa2cbc55b5d0a6bbde1ca73706385d Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Thu, 2 Oct 2025 10:20:08 +0200 Subject: [PATCH 153/226] Make `#[link="dl"]` a warning rather than an error --- .../src/attributes/link_attrs.rs | 29 ++++++++++----- compiler/rustc_span/src/symbol.rs | 1 + tests/ui/attributes/link-dl.allowed.stderr | 10 ++++++ .../ui/attributes/link-dl.default_fcw.stderr | 35 +++++++++---------- 4 files changed, 47 insertions(+), 28 deletions(-) create mode 100644 tests/ui/attributes/link-dl.allowed.stderr diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index d4942e56f429..04eaa485f736 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -65,10 +65,22 @@ impl CombineAttributeParser for LinkParser { cx: &'c mut AcceptContext<'_, '_, S>, args: &'c ArgParser<'_>, ) -> impl IntoIterator + 'c { - let mut result = None; - let Some(items) = args.list() else { - cx.expected_list(cx.attr_span); - return result; + let items = match args { + ArgParser::List(list) => list, + // This is an edgecase added because making this a hard error would break too many crates + // Specifically `#[link = "dl"]` is accepted with a FCW + // For more information, see https://github.com/rust-lang/rust/pull/143193 + ArgParser::NameValue(nv) if nv.value_as_str().is_some_and(|v| v == sym::dl) => { + let suggestions = >::TEMPLATE + .suggestions(cx.attr_style, "link"); + let span = cx.attr_span; + cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span); + return None; + } + _ => { + cx.expected_list(cx.attr_span); + return None; + } }; let sess = cx.sess(); @@ -113,7 +125,7 @@ impl CombineAttributeParser for LinkParser { } }; if !cont { - return result; + return None; } } @@ -202,7 +214,7 @@ impl CombineAttributeParser for LinkParser { } let Some((name, name_span)) = name else { cx.emit_err(LinkRequiresName { span: cx.attr_span }); - return result; + return None; }; // Do this outside of the loop so that `import_name_type` can be specified before `kind`. @@ -218,15 +230,14 @@ impl CombineAttributeParser for LinkParser { cx.emit_err(RawDylibNoNul { span: name_span }); } - result = Some(LinkEntry { + Some(LinkEntry { span: cx.attr_span, kind: kind.unwrap_or(NativeLibKind::Unspecified), name, cfg, verbatim, import_name_type, - }); - result + }) } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index b34a64108e3b..20cdd79c3470 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -873,6 +873,7 @@ symbols! { div, div_assign, diverging_block_default, + dl, do_not_recommend, doc, doc_alias, diff --git a/tests/ui/attributes/link-dl.allowed.stderr b/tests/ui/attributes/link-dl.allowed.stderr new file mode 100644 index 000000000000..e0070d970595 --- /dev/null +++ b/tests/ui/attributes/link-dl.allowed.stderr @@ -0,0 +1,10 @@ +Future incompatibility report: Future breakage diagnostic: +warning: valid forms for the attribute are `#[link(name = "...")]`, `#[link(name = "...", import_name_type = "decorated|noprefix|undecorated")]`, `#[link(name = "...", kind = "dylib|static|...")]`, `#[link(name = "...", kind = "dylib|static|...", wasm_import_module = "...", import_name_type = "decorated|noprefix|undecorated")]`, and `#[link(name = "...", wasm_import_module = "...")]` + --> $DIR/link-dl.rs:14:1 + | +LL | #[link="dl"] + | ^^^^^^^^^^^^ + | + = warning: 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 #57571 + diff --git a/tests/ui/attributes/link-dl.default_fcw.stderr b/tests/ui/attributes/link-dl.default_fcw.stderr index 6bff216ad03f..249895fd17d0 100644 --- a/tests/ui/attributes/link-dl.default_fcw.stderr +++ b/tests/ui/attributes/link-dl.default_fcw.stderr @@ -1,26 +1,23 @@ -error[E0539]: malformed `link` attribute input +error: valid forms for the attribute are `#[link(name = "...")]`, `#[link(name = "...", import_name_type = "decorated|noprefix|undecorated")]`, `#[link(name = "...", kind = "dylib|static|...")]`, `#[link(name = "...", kind = "dylib|static|...", wasm_import_module = "...", import_name_type = "decorated|noprefix|undecorated")]`, and `#[link(name = "...", wasm_import_module = "...")]` --> $DIR/link-dl.rs:14:1 | LL | #[link="dl"] - | ^^^^^^^^^^^^ expected this to be a list + | ^^^^^^^^^^^^ | - = note: for more information, visit -help: try changing it to one of the following valid forms of the attribute - | -LL - #[link="dl"] -LL + #[link(name = "...")] - | -LL - #[link="dl"] -LL + #[link(name = "...", import_name_type = "decorated|noprefix|undecorated")] - | -LL - #[link="dl"] -LL + #[link(name = "...", kind = "dylib|static|...")] - | -LL - #[link="dl"] -LL + #[link(name = "...", kind = "dylib|static|...", wasm_import_module = "...", import_name_type = "decorated|noprefix|undecorated")] - | - = and 1 other candidate + = warning: 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 #57571 + = note: `#[deny(ill_formed_attribute_input)]` (part of `#[deny(future_incompatible)]`) on by default error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0539`. +Future incompatibility report: Future breakage diagnostic: +error: valid forms for the attribute are `#[link(name = "...")]`, `#[link(name = "...", import_name_type = "decorated|noprefix|undecorated")]`, `#[link(name = "...", kind = "dylib|static|...")]`, `#[link(name = "...", kind = "dylib|static|...", wasm_import_module = "...", import_name_type = "decorated|noprefix|undecorated")]`, and `#[link(name = "...", wasm_import_module = "...")]` + --> $DIR/link-dl.rs:14:1 + | +LL | #[link="dl"] + | ^^^^^^^^^^^^ + | + = warning: 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 #57571 + = note: `#[deny(ill_formed_attribute_input)]` (part of `#[deny(future_incompatible)]`) on by default + From 24befed7279ae8be1c52e698172c1366e41e06b1 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Fri, 3 Oct 2025 22:56:05 +0200 Subject: [PATCH 154/226] clean-up - inline `array_span_lint` - use `match` instead of `if-let` - give a more descriptive help message --- clippy_lints/src/zero_repeat_side_effects.rs | 104 ++++++------------- tests/ui/zero_repeat_side_effects.fixed | 4 +- tests/ui/zero_repeat_side_effects.rs | 4 +- tests/ui/zero_repeat_side_effects.stderr | 95 +++++++++++++---- 4 files changed, 108 insertions(+), 99 deletions(-) diff --git a/clippy_lints/src/zero_repeat_side_effects.rs b/clippy_lints/src/zero_repeat_side_effects.rs index 30fdf22fdbb0..e51c4a689816 100644 --- a/clippy_lints/src/zero_repeat_side_effects.rs +++ b/clippy_lints/src/zero_repeat_side_effects.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::VecArgs; use clippy_utils::source::snippet; use clippy_utils::visitors::for_each_expr_without_closures; @@ -7,9 +7,7 @@ use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; use rustc_hir::{ConstArgKind, ExprKind, Node}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::Ty; use rustc_session::declare_lint_pass; -use rustc_span::Span; declare_clippy_lint! { /// ### What it does @@ -85,77 +83,39 @@ fn inner_check(cx: &LateContext<'_>, expr: &'_ rustc_hir::Expr<'_>, inner_expr: let parent_hir_node = cx.tcx.parent_hir_node(expr.hir_id); let return_type = cx.typeck_results().expr_ty(expr); - if let Node::LetStmt(l) = parent_hir_node { - array_span_lint( - cx, + let inner_expr = snippet(cx, inner_expr.span.source_callsite(), ".."); + let vec = if is_vec { "vec!" } else { "" }; + + let (span, sugg) = match parent_hir_node { + Node::LetStmt(l) => ( l.span, - inner_expr.span, - l.pat.span, - Some(return_type), - is_vec, - false, - ); - } else if let Node::Expr(x) = parent_hir_node - && let ExprKind::Assign(l, _, _) = x.kind - { - array_span_lint(cx, x.span, inner_expr.span, l.span, Some(return_type), is_vec, true); - } else { - span_lint_and_sugg( - cx, - ZERO_REPEAT_SIDE_EFFECTS, - expr.span.source_callsite(), - "function or method calls as the initial value in zero-sized array initializers may cause side effects", - "consider using", format!( - "{{ {}; {}[] as {return_type} }}", - snippet(cx, inner_expr.span.source_callsite(), ".."), - if is_vec { "vec!" } else { "" }, + "{inner_expr}; let {var_name}: {return_type} = {vec}[];", + var_name = snippet(cx, l.pat.span.source_callsite(), "..") ), - Applicability::Unspecified, - ); - } + ), + Node::Expr(x) if let ExprKind::Assign(l, _, _) = x.kind => ( + x.span, + format!( + "{inner_expr}; {var_name} = {vec}[] as {return_type}", + var_name = snippet(cx, l.span.source_callsite(), "..") + ), + ), + _ => (expr.span, format!("{{ {inner_expr}; {vec}[] as {return_type} }}")), + }; + span_lint_and_then( + cx, + ZERO_REPEAT_SIDE_EFFECTS, + span.source_callsite(), + "function or method calls as the initial value in zero-sized array initializers may cause side effects", + |diag| { + diag.span_suggestion_verbose( + span.source_callsite(), + "consider performing the side effect separately", + sugg, + Applicability::Unspecified, + ); + }, + ); } } - -fn array_span_lint( - cx: &LateContext<'_>, - expr_span: Span, - func_call_span: Span, - variable_name_span: Span, - expr_ty: Option>, - is_vec: bool, - is_assign: bool, -) { - let has_ty = expr_ty.is_some(); - - span_lint_and_sugg( - cx, - ZERO_REPEAT_SIDE_EFFECTS, - expr_span.source_callsite(), - "function or method calls as the initial value in zero-sized array initializers may cause side effects", - "consider using", - format!( - "{}; {}{}{} = {}[]{}{}", - snippet(cx, func_call_span.source_callsite(), ".."), - if has_ty && !is_assign { "let " } else { "" }, - snippet(cx, variable_name_span.source_callsite(), ".."), - if let Some(ty) = expr_ty - && !is_assign - { - format!(": {ty}") - } else { - String::new() - }, - if is_vec { "vec!" } else { "" }, - if let Some(ty) = expr_ty - && is_assign - { - format!(" as {ty}") - } else { - String::new() - }, - if is_assign { "" } else { ";" } - ), - Applicability::Unspecified, - ); -} diff --git a/tests/ui/zero_repeat_side_effects.fixed b/tests/ui/zero_repeat_side_effects.fixed index fb9d7880a4a7..5184249e942d 100644 --- a/tests/ui/zero_repeat_side_effects.fixed +++ b/tests/ui/zero_repeat_side_effects.fixed @@ -1,7 +1,5 @@ #![warn(clippy::zero_repeat_side_effects)] -#![allow(clippy::unnecessary_operation)] -#![allow(clippy::useless_vec)] -#![allow(clippy::needless_late_init)] +#![expect(clippy::unnecessary_operation, clippy::useless_vec, clippy::needless_late_init)] fn f() -> i32 { println!("side effect"); diff --git a/tests/ui/zero_repeat_side_effects.rs b/tests/ui/zero_repeat_side_effects.rs index 8b22ff840244..9c6e758f2e28 100644 --- a/tests/ui/zero_repeat_side_effects.rs +++ b/tests/ui/zero_repeat_side_effects.rs @@ -1,7 +1,5 @@ #![warn(clippy::zero_repeat_side_effects)] -#![allow(clippy::unnecessary_operation)] -#![allow(clippy::useless_vec)] -#![allow(clippy::needless_late_init)] +#![expect(clippy::unnecessary_operation, clippy::useless_vec, clippy::needless_late_init)] fn f() -> i32 { println!("side effect"); diff --git a/tests/ui/zero_repeat_side_effects.stderr b/tests/ui/zero_repeat_side_effects.stderr index 2dba52e2112e..dc99110932d8 100644 --- a/tests/ui/zero_repeat_side_effects.stderr +++ b/tests/ui/zero_repeat_side_effects.stderr @@ -1,59 +1,112 @@ error: function or method calls as the initial value in zero-sized array initializers may cause side effects - --> tests/ui/zero_repeat_side_effects.rs:18:5 + --> tests/ui/zero_repeat_side_effects.rs:16:5 | LL | let a = [f(); 0]; - | ^^^^^^^^^^^^^^^^^ help: consider using: `f(); let a: [i32; 0] = [];` + | ^^^^^^^^^^^^^^^^^ | = note: `-D clippy::zero-repeat-side-effects` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::zero_repeat_side_effects)]` +help: consider performing the side effect separately + | +LL - let a = [f(); 0]; +LL + f(); let a: [i32; 0] = []; + | error: function or method calls as the initial value in zero-sized array initializers may cause side effects - --> tests/ui/zero_repeat_side_effects.rs:21:5 + --> tests/ui/zero_repeat_side_effects.rs:19:5 | LL | b = [f(); 0]; - | ^^^^^^^^^^^^ help: consider using: `f(); b = [] as [i32; 0]` + | ^^^^^^^^^^^^ + | +help: consider performing the side effect separately + | +LL - b = [f(); 0]; +LL + f(); b = [] as [i32; 0]; + | error: function or method calls as the initial value in zero-sized array initializers may cause side effects - --> tests/ui/zero_repeat_side_effects.rs:26:5 + --> tests/ui/zero_repeat_side_effects.rs:24:5 | LL | let c = vec![f(); 0]; - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f(); let c: std::vec::Vec = vec![];` + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: consider performing the side effect separately + | +LL - let c = vec![f(); 0]; +LL + f(); let c: std::vec::Vec = vec![]; + | error: function or method calls as the initial value in zero-sized array initializers may cause side effects - --> tests/ui/zero_repeat_side_effects.rs:29:5 + --> tests/ui/zero_repeat_side_effects.rs:27:5 | LL | d = vec![f(); 0]; - | ^^^^^^^^^^^^^^^^ help: consider using: `f(); d = vec![] as std::vec::Vec` + | ^^^^^^^^^^^^^^^^ + | +help: consider performing the side effect separately + | +LL - d = vec![f(); 0]; +LL + f(); d = vec![] as std::vec::Vec; + | error: function or method calls as the initial value in zero-sized array initializers may cause side effects - --> tests/ui/zero_repeat_side_effects.rs:33:5 + --> tests/ui/zero_repeat_side_effects.rs:31:5 | LL | let e = [println!("side effect"); 0]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `println!("side effect"); let e: [(); 0] = [];` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider performing the side effect separately + | +LL - let e = [println!("side effect"); 0]; +LL + println!("side effect"); let e: [(); 0] = []; + | error: function or method calls as the initial value in zero-sized array initializers may cause side effects - --> tests/ui/zero_repeat_side_effects.rs:37:5 + --> tests/ui/zero_repeat_side_effects.rs:35:5 | LL | let g = [{ f() }; 0]; - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `{ f() }; let g: [i32; 0] = [];` + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: consider performing the side effect separately + | +LL - let g = [{ f() }; 0]; +LL + { f() }; let g: [i32; 0] = []; + | error: function or method calls as the initial value in zero-sized array initializers may cause side effects - --> tests/ui/zero_repeat_side_effects.rs:41:10 + --> tests/ui/zero_repeat_side_effects.rs:39:10 | LL | drop(vec![f(); 0]); - | ^^^^^^^^^^^^ help: consider using: `{ f(); vec![] as std::vec::Vec }` + | ^^^^^^^^^^^^ + | +help: consider performing the side effect separately + | +LL - drop(vec![f(); 0]); +LL + drop({ f(); vec![] as std::vec::Vec }); + | + +error: function or method calls as the initial value in zero-sized array initializers may cause side effects + --> tests/ui/zero_repeat_side_effects.rs:43:5 + | +LL | vec![f(); 0]; + | ^^^^^^^^^^^^ + | +help: consider performing the side effect separately + | +LL - vec![f(); 0]; +LL + { f(); vec![] as std::vec::Vec }; + | error: function or method calls as the initial value in zero-sized array initializers may cause side effects --> tests/ui/zero_repeat_side_effects.rs:45:5 | -LL | vec![f(); 0]; - | ^^^^^^^^^^^^ help: consider using: `{ f(); vec![] as std::vec::Vec }` - -error: function or method calls as the initial value in zero-sized array initializers may cause side effects - --> tests/ui/zero_repeat_side_effects.rs:47:5 - | LL | [f(); 0]; - | ^^^^^^^^ help: consider using: `{ f(); [] as [i32; 0] }` + | ^^^^^^^^ + | +help: consider performing the side effect separately + | +LL - [f(); 0]; +LL + { f(); [] as [i32; 0] }; + | error: aborting due to 9 previous errors From adff9baeb3d16c5d9de8146a6d67f3b276429762 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sat, 4 Oct 2025 00:10:12 +0200 Subject: [PATCH 155/226] fix(zero_repeat_side_effects): better identify exprs with side effects --- clippy_lints/src/zero_repeat_side_effects.rs | 15 ++----- tests/ui/zero_repeat_side_effects.fixed | 24 +++++++++++ tests/ui/zero_repeat_side_effects.rs | 24 +++++++++++ tests/ui/zero_repeat_side_effects.stderr | 44 +++++++++++++++----- 4 files changed, 85 insertions(+), 22 deletions(-) diff --git a/clippy_lints/src/zero_repeat_side_effects.rs b/clippy_lints/src/zero_repeat_side_effects.rs index e51c4a689816..cd6c11b51274 100644 --- a/clippy_lints/src/zero_repeat_side_effects.rs +++ b/clippy_lints/src/zero_repeat_side_effects.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::VecArgs; use clippy_utils::source::snippet; -use clippy_utils::visitors::for_each_expr_without_closures; use rustc_ast::LitKind; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; @@ -11,7 +10,7 @@ use rustc_session::declare_lint_pass; declare_clippy_lint! { /// ### What it does - /// Checks for array or vec initializations which call a function or method, + /// Checks for array or vec initializations which contain an expression with side effects, /// but which have a repeat count of zero. /// /// ### Why is this bad? @@ -71,15 +70,7 @@ impl LateLintPass<'_> for ZeroRepeatSideEffects { fn inner_check(cx: &LateContext<'_>, expr: &'_ rustc_hir::Expr<'_>, inner_expr: &'_ rustc_hir::Expr<'_>, is_vec: bool) { // check if expr is a call or has a call inside it - if for_each_expr_without_closures(inner_expr, |x| { - if let ExprKind::Call(_, _) | ExprKind::MethodCall(_, _, _, _) = x.kind { - std::ops::ControlFlow::Break(()) - } else { - std::ops::ControlFlow::Continue(()) - } - }) - .is_some() - { + if inner_expr.can_have_side_effects() { let parent_hir_node = cx.tcx.parent_hir_node(expr.hir_id); let return_type = cx.typeck_results().expr_ty(expr); @@ -107,7 +98,7 @@ fn inner_check(cx: &LateContext<'_>, expr: &'_ rustc_hir::Expr<'_>, inner_expr: cx, ZERO_REPEAT_SIDE_EFFECTS, span.source_callsite(), - "function or method calls as the initial value in zero-sized array initializers may cause side effects", + "expression with side effects as the initial value in a zero-sized array initializer", |diag| { diag.span_suggestion_verbose( span.source_callsite(), diff --git a/tests/ui/zero_repeat_side_effects.fixed b/tests/ui/zero_repeat_side_effects.fixed index 5184249e942d..e6c451ce7399 100644 --- a/tests/ui/zero_repeat_side_effects.fixed +++ b/tests/ui/zero_repeat_side_effects.fixed @@ -77,3 +77,27 @@ fn issue_13110() { const LENGTH: usize = LEN!(); let _data = [f(); LENGTH]; } + +// TODO: consider moving the defintion+impl inside `issue_14681` +// once https://github.com/rust-lang/rust/issues/146786 is fixed +#[derive(Clone, Copy)] +struct S; + +impl S { + fn new() -> Self { + println!("This is a side effect"); + S + } +} + +// should not trigger on non-function calls +fn issue_14681() { + fn foo(_s: &[Option]) {} + + foo(&[Some(0i64); 0]); + foo(&[Some(Some(0i64)); 0]); + foo(&{ Some(f()); [] as [std::option::Option; 0] }); + //~^ zero_repeat_side_effects + foo(&{ Some(Some(S::new())); [] as [std::option::Option>; 0] }); + //~^ zero_repeat_side_effects +} diff --git a/tests/ui/zero_repeat_side_effects.rs b/tests/ui/zero_repeat_side_effects.rs index 9c6e758f2e28..f8a497976aa4 100644 --- a/tests/ui/zero_repeat_side_effects.rs +++ b/tests/ui/zero_repeat_side_effects.rs @@ -77,3 +77,27 @@ fn issue_13110() { const LENGTH: usize = LEN!(); let _data = [f(); LENGTH]; } + +// TODO: consider moving the defintion+impl inside `issue_14681` +// once https://github.com/rust-lang/rust/issues/146786 is fixed +#[derive(Clone, Copy)] +struct S; + +impl S { + fn new() -> Self { + println!("This is a side effect"); + S + } +} + +// should not trigger on non-function calls +fn issue_14681() { + fn foo(_s: &[Option]) {} + + foo(&[Some(0i64); 0]); + foo(&[Some(Some(0i64)); 0]); + foo(&[Some(f()); 0]); + //~^ zero_repeat_side_effects + foo(&[Some(Some(S::new())); 0]); + //~^ zero_repeat_side_effects +} diff --git a/tests/ui/zero_repeat_side_effects.stderr b/tests/ui/zero_repeat_side_effects.stderr index dc99110932d8..771b71c686ae 100644 --- a/tests/ui/zero_repeat_side_effects.stderr +++ b/tests/ui/zero_repeat_side_effects.stderr @@ -1,4 +1,4 @@ -error: function or method calls as the initial value in zero-sized array initializers may cause side effects +error: expression with side effects as the initial value in a zero-sized array initializer --> tests/ui/zero_repeat_side_effects.rs:16:5 | LL | let a = [f(); 0]; @@ -12,7 +12,7 @@ LL - let a = [f(); 0]; LL + f(); let a: [i32; 0] = []; | -error: function or method calls as the initial value in zero-sized array initializers may cause side effects +error: expression with side effects as the initial value in a zero-sized array initializer --> tests/ui/zero_repeat_side_effects.rs:19:5 | LL | b = [f(); 0]; @@ -24,7 +24,7 @@ LL - b = [f(); 0]; LL + f(); b = [] as [i32; 0]; | -error: function or method calls as the initial value in zero-sized array initializers may cause side effects +error: expression with side effects as the initial value in a zero-sized array initializer --> tests/ui/zero_repeat_side_effects.rs:24:5 | LL | let c = vec![f(); 0]; @@ -36,7 +36,7 @@ LL - let c = vec![f(); 0]; LL + f(); let c: std::vec::Vec = vec![]; | -error: function or method calls as the initial value in zero-sized array initializers may cause side effects +error: expression with side effects as the initial value in a zero-sized array initializer --> tests/ui/zero_repeat_side_effects.rs:27:5 | LL | d = vec![f(); 0]; @@ -48,7 +48,7 @@ LL - d = vec![f(); 0]; LL + f(); d = vec![] as std::vec::Vec; | -error: function or method calls as the initial value in zero-sized array initializers may cause side effects +error: expression with side effects as the initial value in a zero-sized array initializer --> tests/ui/zero_repeat_side_effects.rs:31:5 | LL | let e = [println!("side effect"); 0]; @@ -60,7 +60,7 @@ LL - let e = [println!("side effect"); 0]; LL + println!("side effect"); let e: [(); 0] = []; | -error: function or method calls as the initial value in zero-sized array initializers may cause side effects +error: expression with side effects as the initial value in a zero-sized array initializer --> tests/ui/zero_repeat_side_effects.rs:35:5 | LL | let g = [{ f() }; 0]; @@ -72,7 +72,7 @@ LL - let g = [{ f() }; 0]; LL + { f() }; let g: [i32; 0] = []; | -error: function or method calls as the initial value in zero-sized array initializers may cause side effects +error: expression with side effects as the initial value in a zero-sized array initializer --> tests/ui/zero_repeat_side_effects.rs:39:10 | LL | drop(vec![f(); 0]); @@ -84,7 +84,7 @@ LL - drop(vec![f(); 0]); LL + drop({ f(); vec![] as std::vec::Vec }); | -error: function or method calls as the initial value in zero-sized array initializers may cause side effects +error: expression with side effects as the initial value in a zero-sized array initializer --> tests/ui/zero_repeat_side_effects.rs:43:5 | LL | vec![f(); 0]; @@ -96,7 +96,7 @@ LL - vec![f(); 0]; LL + { f(); vec![] as std::vec::Vec }; | -error: function or method calls as the initial value in zero-sized array initializers may cause side effects +error: expression with side effects as the initial value in a zero-sized array initializer --> tests/ui/zero_repeat_side_effects.rs:45:5 | LL | [f(); 0]; @@ -108,5 +108,29 @@ LL - [f(); 0]; LL + { f(); [] as [i32; 0] }; | -error: aborting due to 9 previous errors +error: expression with side effects as the initial value in a zero-sized array initializer + --> tests/ui/zero_repeat_side_effects.rs:99:10 + | +LL | foo(&[Some(f()); 0]); + | ^^^^^^^^^^^^^^ + | +help: consider performing the side effect separately + | +LL - foo(&[Some(f()); 0]); +LL + foo(&{ Some(f()); [] as [std::option::Option; 0] }); + | + +error: expression with side effects as the initial value in a zero-sized array initializer + --> tests/ui/zero_repeat_side_effects.rs:101:10 + | +LL | foo(&[Some(Some(S::new())); 0]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider performing the side effect separately + | +LL - foo(&[Some(Some(S::new())); 0]); +LL + foo(&{ Some(Some(S::new())); [] as [std::option::Option>; 0] }); + | + +error: aborting due to 11 previous errors From 2688f601ddc29b75caa2c48a4badc00d15042d7a Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sat, 4 Oct 2025 16:07:06 +0200 Subject: [PATCH 156/226] Make `fmt::Write` a diagnostic item --- compiler/rustc_span/src/symbol.rs | 1 + library/core/src/fmt/mod.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index b34a64108e3b..083e04730bc3 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -236,6 +236,7 @@ symbols! { File, FileType, FmtArgumentsNew, + FmtWrite, Fn, FnMut, FnOnce, diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index fcd2e52101ff..0f255e57fe58 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -115,6 +115,7 @@ pub struct Error; /// [`std::io::Write`]: ../../std/io/trait.Write.html /// [flushable]: ../../std/io/trait.Write.html#tymethod.flush #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_diagnostic_item = "FmtWrite"] pub trait Write { /// Writes a string slice into this writer, returning whether the write /// succeeded. From b4578775b471b723b2210a838d6a8521f45de1db Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Sat, 4 Oct 2025 10:22:41 -0400 Subject: [PATCH 157/226] Update cargo submodule --- src/tools/cargo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/cargo b/src/tools/cargo index 2394ea6cea8b..801d9b4981dd 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit 2394ea6cea8b26d717aa67eb1663a2dbf2d26078 +Subproject commit 801d9b4981dd07e3aecdca1ab86834c13615737e From 3d351c839dcb045adcc91372f78f0f0cbaf81033 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sat, 23 Aug 2025 09:45:53 -0400 Subject: [PATCH 158/226] Const eval changes: * Remove `CoreConstant`. * Treat most constants from core as though they were inlined. * Don't evaluate `is_empty` for named constants. --- clippy_lints/src/enum_clike.rs | 8 +- clippy_lints/src/floating_point_arithmetic.rs | 2 +- clippy_lints/src/manual_float_methods.rs | 4 +- clippy_lints/src/matches/overlapping_arms.rs | 6 +- .../src/methods/unnecessary_min_or_max.rs | 6 +- clippy_lints/src/minmax.rs | 4 +- .../src/operators/const_comparisons.rs | 2 +- clippy_lints/src/operators/float_cmp.rs | 2 +- clippy_lints/src/ranges.rs | 6 +- clippy_utils/src/consts.rs | 485 ++++++++++++------ clippy_utils/src/lib.rs | 10 +- clippy_utils/src/paths.rs | 18 +- clippy_utils/src/sym.rs | 1 + tests/ui/const_comparisons.rs | 14 +- tests/ui/const_comparisons.stderr | 62 +-- tests/ui/const_is_empty.rs | 50 +- tests/ui/const_is_empty.stderr | 108 +--- 17 files changed, 395 insertions(+), 393 deletions(-) diff --git a/clippy_lints/src/enum_clike.rs b/clippy_lints/src/enum_clike.rs index c828fc57f760..1a56c8f810ee 100644 --- a/clippy_lints/src/enum_clike.rs +++ b/clippy_lints/src/enum_clike.rs @@ -43,12 +43,8 @@ impl<'tcx> LateLintPass<'tcx> for UnportableVariant { if let Some(anon_const) = &var.disr_expr { let def_id = cx.tcx.hir_body_owner_def_id(anon_const.body); let mut ty = cx.tcx.type_of(def_id.to_def_id()).instantiate_identity(); - let constant = cx - .tcx - .const_eval_poly(def_id.to_def_id()) - .ok() - .map(|val| rustc_middle::mir::Const::from_value(val, ty)); - if let Some(Constant::Int(val)) = constant.and_then(|c| mir_to_const(cx.tcx, c)) { + let constant = cx.tcx.const_eval_poly(def_id.to_def_id()).ok(); + if let Some(Constant::Int(val)) = constant.and_then(|c| mir_to_const(cx.tcx, c, ty)) { if let ty::Adt(adt, _) = ty.kind() && adt.is_enum() { diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 5052bbb3ca03..3ecc232caff4 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -206,7 +206,7 @@ fn check_ln1p(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>) { // ranges [-16777215, 16777216) for type f32 as whole number floats outside // this range are lossy and ambiguous. #[expect(clippy::cast_possible_truncation)] -fn get_integer_from_float_constant(value: &Constant<'_>) -> Option { +fn get_integer_from_float_constant(value: &Constant) -> Option { match value { F32(num) if num.fract() == 0.0 => { if (-16_777_215.0..16_777_216.0).contains(num) { diff --git a/clippy_lints/src/manual_float_methods.rs b/clippy_lints/src/manual_float_methods.rs index bd2785fea270..7d6c30aa74dd 100644 --- a/clippy_lints/src/manual_float_methods.rs +++ b/clippy_lints/src/manual_float_methods.rs @@ -202,7 +202,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods { } } -fn is_infinity(constant: &Constant<'_>) -> bool { +fn is_infinity(constant: &Constant) -> bool { match constant { // FIXME(f16_f128): add f16 and f128 when constants are available Constant::F32(float) => *float == f32::INFINITY, @@ -211,7 +211,7 @@ fn is_infinity(constant: &Constant<'_>) -> bool { } } -fn is_neg_infinity(constant: &Constant<'_>) -> bool { +fn is_neg_infinity(constant: &Constant) -> bool { match constant { // FIXME(f16_f128): add f16 and f128 when constants are available Constant::F32(float) => *float == f32::NEG_INFINITY, diff --git a/clippy_lints/src/matches/overlapping_arms.rs b/clippy_lints/src/matches/overlapping_arms.rs index d3136c89178e..d76218e6305b 100644 --- a/clippy_lints/src/matches/overlapping_arms.rs +++ b/clippy_lints/src/matches/overlapping_arms.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{ConstEvalCtxt, FullInt, mir_to_const}; +use clippy_utils::consts::{ConstEvalCtxt, Constant, FullInt}; use clippy_utils::diagnostics::span_lint_and_note; use core::cmp::Ordering; use rustc_hir::{Arm, Expr, PatKind, RangeEnd}; @@ -35,12 +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 { - mir_to_const(cx.tcx, ty.numeric_min_val(cx.tcx)?)? + Constant::new_numeric_min(cx.tcx, ty)? }; let rhs_const = if let Some(rhs) = rhs { ConstEvalCtxt::new(cx).eval_pat_expr(rhs)? } else { - mir_to_const(cx.tcx, ty.numeric_max_val(cx.tcx)?)? + Constant::new_numeric_max(cx.tcx, ty)? }; let lhs_val = lhs_const.int_value(cx.tcx, ty)?; let rhs_val = rhs_const.int_value(cx.tcx, ty)?; diff --git a/clippy_lints/src/methods/unnecessary_min_or_max.rs b/clippy_lints/src/methods/unnecessary_min_or_max.rs index 130e4970a716..bf91a469e7f0 100644 --- a/clippy_lints/src/methods/unnecessary_min_or_max.rs +++ b/clippy_lints/src/methods/unnecessary_min_or_max.rs @@ -1,7 +1,7 @@ use std::cmp::Ordering; use super::UNNECESSARY_MIN_OR_MAX; -use clippy_utils::consts::{ConstEvalCtxt, Constant, ConstantSource, FullInt}; +use clippy_utils::consts::{ConstEvalCtxt, Constant, FullInt}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; @@ -26,8 +26,8 @@ pub(super) fn check<'tcx>( && matches!(fn_name, sym::cmp_ord_min | sym::cmp_ord_max) { let ctxt = expr.span.ctxt(); - if let Some((left, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(recv, ctxt) - && let Some((right, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(arg, ctxt) + if let Some(left) = ecx.eval_local(recv, ctxt) + && let Some(right) = ecx.eval_local(arg, ctxt) { let Some(ord) = Constant::partial_cmp(cx.tcx, typeck_results.expr_ty(recv), &left, &right) else { return; diff --git a/clippy_lints/src/minmax.rs b/clippy_lints/src/minmax.rs index 8f76e6c9048e..35b6af0c20f7 100644 --- a/clippy_lints/src/minmax.rs +++ b/clippy_lints/src/minmax.rs @@ -61,7 +61,7 @@ enum MinMax { Max, } -fn min_max<'a, 'tcx>(cx: &LateContext<'tcx>, expr: &'a Expr<'a>) -> Option<(MinMax, Constant<'tcx>, &'a Expr<'a>)> { +fn min_max<'a, 'tcx>(cx: &LateContext<'tcx>, expr: &'a Expr<'a>) -> Option<(MinMax, Constant, &'a Expr<'a>)> { match expr.kind { ExprKind::Call(path, args) => { if let ExprKind::Path(ref qpath) = path.kind { @@ -98,7 +98,7 @@ fn fetch_const<'a, 'tcx>( receiver: Option<&'a Expr<'a>>, args: &'a [Expr<'a>], m: MinMax, -) -> Option<(MinMax, Constant<'tcx>, &'a Expr<'a>)> { +) -> Option<(MinMax, Constant, &'a Expr<'a>)> { let mut args = receiver.into_iter().chain(args); let first_arg = args.next()?; let second_arg = args.next()?; diff --git a/clippy_lints/src/operators/const_comparisons.rs b/clippy_lints/src/operators/const_comparisons.rs index 10455d3b93a0..56001a185771 100644 --- a/clippy_lints/src/operators/const_comparisons.rs +++ b/clippy_lints/src/operators/const_comparisons.rs @@ -22,7 +22,7 @@ fn comparison_to_const<'tcx>( cx: &LateContext<'tcx>, typeck: &'tcx TypeckResults<'tcx>, expr: &'tcx Expr<'tcx>, -) -> Option<(CmpOp, &'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Constant<'tcx>, Ty<'tcx>)> { +) -> Option<(CmpOp, &'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Constant, Ty<'tcx>)> { if let ExprKind::Binary(operator, left, right) = expr.kind && let Ok(cmp_op) = CmpOp::try_from(operator.node) { diff --git a/clippy_lints/src/operators/float_cmp.rs b/clippy_lints/src/operators/float_cmp.rs index 03b2cf055d91..eb2353cfd90b 100644 --- a/clippy_lints/src/operators/float_cmp.rs +++ b/clippy_lints/src/operators/float_cmp.rs @@ -85,7 +85,7 @@ fn get_lint_and_message(is_local: bool, is_comparing_arrays: bool) -> (&'static } } -fn is_allowed(val: &Constant<'_>) -> bool { +fn is_allowed(val: &Constant) -> bool { match val { // FIXME(f16_f128): add when equality check is available on all platforms &Constant::F32(f) => f == 0.0 || f.is_infinite(), diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 03d00ba849f3..8a216afb82c9 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -299,8 +299,8 @@ fn check_possible_range_contains( } } -struct RangeBounds<'a, 'tcx> { - val: Constant<'tcx>, +struct RangeBounds<'a> { + val: Constant, expr: &'a Expr<'a>, id: HirId, name_span: Span, @@ -312,7 +312,7 @@ struct RangeBounds<'a, 'tcx> { // Takes a binary expression such as x <= 2 as input // Breaks apart into various pieces, such as the value of the number, // hir id of the variable, and direction/inclusiveness of the operator -fn check_range_bounds<'a, 'tcx>(cx: &'a LateContext<'tcx>, ex: &'a Expr<'_>) -> Option> { +fn check_range_bounds<'a, 'tcx>(cx: &'a LateContext<'tcx>, ex: &'a Expr<'_>) -> Option> { if let ExprKind::Binary(ref op, l, r) = ex.kind { let (inclusive, ordering) = match op.node { BinOpKind::Gt => (false, Ordering::Greater), diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 29f4988b4211..f5f378616460 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -5,25 +5,21 @@ #![allow(clippy::float_cmp)] use crate::source::{SpanRangeExt, walk_span_to_context}; -use crate::{clip, is_direct_expn_of, sext, unsext}; +use crate::{clip, is_direct_expn_of, paths, sext, sym, unsext}; use rustc_abi::Size; use rustc_apfloat::Float; use rustc_apfloat::ieee::{Half, Quad}; use rustc_ast::ast::{LitFloatType, LitKind}; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{ - BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, Item, ItemKind, Node, PatExpr, PatExprKind, QPath, UnOp, -}; +use rustc_hir::{BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, PatExpr, PatExprKind, QPath, TyKind, UnOp}; use rustc_lexer::{FrontmatterAllowed, tokenize}; use rustc_lint::LateContext; use rustc_middle::mir::ConstValue; use rustc_middle::mir::interpret::{Scalar, alloc_range}; use rustc_middle::ty::{self, FloatTy, IntTy, ScalarInt, Ty, TyCtxt, TypeckResults, UintTy}; use rustc_middle::{bug, mir, span_bug}; -use rustc_span::def_id::DefId; -use rustc_span::symbol::Ident; -use rustc_span::{SyntaxContext, sym}; +use rustc_span::{Symbol, SyntaxContext}; use std::cell::Cell; use std::cmp::Ordering; use std::hash::{Hash, Hasher}; @@ -31,8 +27,8 @@ use std::iter; /// A `LitKind`-like enum to fold constant `Expr`s into. #[derive(Debug, Clone)] -pub enum Constant<'tcx> { - Adt(mir::Const<'tcx>), +pub enum Constant { + Adt(ConstValue), /// A `String` (e.g., "abc"). Str(String), /// A binary string (e.g., `b"abc"`). @@ -54,15 +50,15 @@ pub enum Constant<'tcx> { /// `true` or `false`. Bool(bool), /// An array of constants. - Vec(Vec>), + Vec(Vec), /// Also an array, but with only one constant, repeated N times. - Repeat(Box>, u64), + Repeat(Box, u64), /// A tuple of constants. - Tuple(Vec>), + Tuple(Vec), /// A raw pointer. RawPtr(u128), /// A reference - Ref(Box>), + Ref(Box), /// A literal with syntax error. Err, } @@ -124,7 +120,7 @@ impl IntTypeBounds for IntTy { } } -impl PartialEq for Constant<'_> { +impl PartialEq for Constant { fn eq(&self, other: &Self) -> bool { match (self, other) { (Self::Str(ls), Self::Str(rs)) => ls == rs, @@ -135,13 +131,13 @@ impl PartialEq for Constant<'_> { // We want `Fw32 == FwAny` and `FwAny == Fw64`, and by transitivity we must have // `Fw32 == Fw64`, so don’t compare them. // `to_bits` is required to catch non-matching 0.0, -0.0, and NaNs. - l.to_bits() == r.to_bits() + l.to_bits() == r.to_bits() && !l.is_nan() }, (&Self::F32(l), &Self::F32(r)) => { // We want `Fw32 == FwAny` and `FwAny == Fw64`, and by transitivity we must have // `Fw32 == Fw64`, so don’t compare them. // `to_bits` is required to catch non-matching 0.0, -0.0, and NaNs. - f64::from(l).to_bits() == f64::from(r).to_bits() + l.to_bits() == r.to_bits() && !l.is_nan() }, (&Self::Bool(l), &Self::Bool(r)) => l == r, (&Self::Vec(ref l), &Self::Vec(ref r)) | (&Self::Tuple(ref l), &Self::Tuple(ref r)) => l == r, @@ -153,7 +149,7 @@ impl PartialEq for Constant<'_> { } } -impl Hash for Constant<'_> { +impl Hash for Constant { fn hash(&self, state: &mut H) where H: Hasher, @@ -209,7 +205,7 @@ impl Hash for Constant<'_> { } } -impl Constant<'_> { +impl Constant { pub fn partial_cmp(tcx: TyCtxt<'_>, cmp_type: Ty<'_>, left: &Self, right: &Self) -> Option { match (left, right) { (Self::Str(ls), Self::Str(rs)) => Some(ls.cmp(rs)), @@ -297,10 +293,129 @@ impl Constant<'_> { let f: Quad = s.parse().unwrap(); Self::F128(f.to_bits()) } + + pub fn new_numeric_min<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option { + match *ty.kind() { + ty::Uint(_) => Some(Self::Int(0)), + ty::Int(ty) => { + let val = match ty.normalize(tcx.sess.target.pointer_width) { + IntTy::I8 => i128::from(i8::MIN), + IntTy::I16 => i128::from(i16::MIN), + IntTy::I32 => i128::from(i32::MIN), + IntTy::I64 => i128::from(i64::MIN), + IntTy::I128 => i128::MIN, + IntTy::Isize => return None, + }; + Some(Self::Int(val.cast_unsigned())) + }, + ty::Char => Some(Self::Char(char::MIN)), + ty::Float(FloatTy::F32) => Some(Self::F32(f32::NEG_INFINITY)), + ty::Float(FloatTy::F64) => Some(Self::F64(f64::NEG_INFINITY)), + _ => None, + } + } + + pub fn new_numeric_max<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option { + match *ty.kind() { + ty::Uint(ty) => Some(Self::Int(match ty.normalize(tcx.sess.target.pointer_width) { + UintTy::U8 => u128::from(u8::MAX), + UintTy::U16 => u128::from(u16::MAX), + UintTy::U32 => u128::from(u32::MAX), + UintTy::U64 => u128::from(u64::MAX), + UintTy::U128 => u128::MAX, + UintTy::Usize => return None, + })), + ty::Int(ty) => { + let val = match ty.normalize(tcx.sess.target.pointer_width) { + IntTy::I8 => i128::from(i8::MAX), + IntTy::I16 => i128::from(i16::MAX), + IntTy::I32 => i128::from(i32::MAX), + IntTy::I64 => i128::from(i64::MAX), + IntTy::I128 => i128::MAX, + IntTy::Isize => return None, + }; + Some(Self::Int(val.cast_unsigned())) + }, + ty::Char => Some(Self::Char(char::MAX)), + ty::Float(FloatTy::F32) => Some(Self::F32(f32::INFINITY)), + ty::Float(FloatTy::F64) => Some(Self::F64(f64::INFINITY)), + _ => None, + } + } + + pub fn is_numeric_min<'tcx>(&self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { + match (self, ty.kind()) { + (&Self::Int(x), &ty::Uint(_)) => x == 0, + (&Self::Int(x), &ty::Int(ty)) => { + let limit = match ty.normalize(tcx.sess.target.pointer_width) { + IntTy::I8 => i128::from(i8::MIN), + IntTy::I16 => i128::from(i16::MIN), + IntTy::I32 => i128::from(i32::MIN), + IntTy::I64 => i128::from(i64::MIN), + IntTy::I128 => i128::MIN, + IntTy::Isize => return false, + }; + x.cast_signed() == limit + }, + (&Self::Char(x), &ty::Char) => x == char::MIN, + (&Self::F32(x), &ty::Float(FloatTy::F32)) => x == f32::NEG_INFINITY, + (&Self::F64(x), &ty::Float(FloatTy::F64)) => x == f64::NEG_INFINITY, + _ => false, + } + } + + pub fn is_numeric_max<'tcx>(&self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { + match (self, ty.kind()) { + (&Self::Int(x), &ty::Uint(ty)) => { + let limit = match ty.normalize(tcx.sess.target.pointer_width) { + UintTy::U8 => u128::from(u8::MAX), + UintTy::U16 => u128::from(u16::MAX), + UintTy::U32 => u128::from(u32::MAX), + UintTy::U64 => u128::from(u64::MAX), + UintTy::U128 => u128::MAX, + UintTy::Usize => return false, + }; + x == limit + }, + (&Self::Int(x), &ty::Int(ty)) => { + let limit = match ty.normalize(tcx.sess.target.pointer_width) { + IntTy::I8 => i128::from(i8::MAX), + IntTy::I16 => i128::from(i16::MAX), + IntTy::I32 => i128::from(i32::MAX), + IntTy::I64 => i128::from(i64::MAX), + IntTy::I128 => i128::MAX, + IntTy::Isize => return false, + }; + x.cast_signed() == limit + }, + (&Self::Char(x), &ty::Char) => x == char::MAX, + (&Self::F32(x), &ty::Float(FloatTy::F32)) => x == f32::INFINITY, + (&Self::F64(x), &ty::Float(FloatTy::F64)) => x == f64::INFINITY, + _ => false, + } + } + + pub fn is_pos_infinity(&self) -> bool { + match *self { + // FIXME(f16_f128): add f16 and f128 when constants are available + Constant::F32(x) => x == f32::INFINITY, + Constant::F64(x) => x == f64::INFINITY, + _ => false, + } + } + + pub fn is_neg_infinity(&self) -> bool { + match *self { + // FIXME(f16_f128): add f16 and f128 when constants are available + Constant::F32(x) => x == f32::NEG_INFINITY, + Constant::F64(x) => x == f64::NEG_INFINITY, + _ => false, + } + } } /// Parses a `LitKind` to a `Constant`. -pub fn lit_to_mir_constant<'tcx>(lit: &LitKind, ty: Option>) -> Constant<'tcx> { +pub fn lit_to_mir_constant(lit: &LitKind, ty: Option>) -> Constant { match *lit { LitKind::Str(ref is, _) => Constant::Str(is.to_string()), LitKind::Byte(b) => Constant::Int(u128::from(b)), @@ -331,10 +446,9 @@ pub fn lit_to_mir_constant<'tcx>(lit: &LitKind, ty: Option>) -> Constan pub enum ConstantSource { /// The value is determined solely from the expression. Local, - /// The value is dependent on a defined constant. - Constant, - /// The value is dependent on a constant defined in `core` crate. - CoreConstant, + /// The value is dependent on another definition that may change independently from the local + /// expression. + NonLocal, } impl ConstantSource { pub fn is_local(self) -> bool { @@ -416,19 +530,24 @@ impl<'tcx> ConstEvalCtxt<'tcx> { /// Attempts to evaluate the expression and returns both the value and whether it's dependant on /// other items. - pub fn eval_with_source(&self, e: &Expr<'_>, ctxt: SyntaxContext) -> Option<(Constant<'tcx>, ConstantSource)> { + pub fn eval_with_source(&self, e: &Expr<'_>, ctxt: SyntaxContext) -> Option<(Constant, ConstantSource)> { self.source.set(ConstantSource::Local); self.ctxt.set(ctxt); self.expr(e).map(|c| (c, self.source.get())) } /// Attempts to evaluate the expression. - pub fn eval(&self, e: &Expr<'_>) -> Option> { + pub fn eval(&self, e: &Expr<'_>) -> Option { self.expr(e) } /// Attempts to evaluate the expression without accessing other items. - pub fn eval_local(&self, e: &Expr<'_>, ctxt: SyntaxContext) -> Option> { + /// + /// The context argument is the context used to view the evaluated expression. e.g. when + /// evaluating the argument in `f(m!(1))` the context of the call expression should be used. + /// This is need so the const evaluator can see the `m` macro and marke the evaluation as + /// non-local independant of what the macro expands to. + pub fn eval_local(&self, e: &Expr<'_>, ctxt: SyntaxContext) -> Option { match self.eval_with_source(e, ctxt) { Some((x, ConstantSource::Local)) => Some(x), _ => None, @@ -436,6 +555,11 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } /// Attempts to evaluate the expression as an integer without accessing other items. + /// + /// The context argument is the context used to view the evaluated expression. e.g. when + /// evaluating the argument in `f(m!(1))` the context of the call expression should be used. + /// This is need so the const evaluator can see the `m` macro and marke the evaluation as + /// non-local independant of what the macro expands to. pub fn eval_full_int(&self, e: &Expr<'_>, ctxt: SyntaxContext) -> Option { match self.eval_with_source(e, ctxt) { Some((x, ConstantSource::Local)) => x.int_value(self.tcx, self.typeck.expr_ty(e)), @@ -443,7 +567,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } } - pub fn eval_pat_expr(&self, pat_expr: &PatExpr<'_>) -> Option> { + 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); @@ -460,35 +584,18 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } fn check_ctxt(&self, ctxt: SyntaxContext) { - self.source.set(if self.ctxt.get() != ctxt { - ConstantSource::Constant - } else { - self.source.get() - }); + if self.ctxt.get() != ctxt { + self.source.set(ConstantSource::NonLocal); + } } - 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) - }) + fn qpath(&self, qpath: &QPath<'_>, hir_id: HirId) -> Option { + self.fetch_path(qpath, hir_id) + .and_then(|c| mir_to_const(self.tcx, c, self.typeck.node_type(hir_id))) } /// Simple constant folding: Insert an expression, get a constant or none. - fn expr(&self, e: &Expr<'_>) -> Option> { + fn expr(&self, e: &Expr<'_>) -> Option { self.check_ctxt(e.span.ctxt()); match e.kind { ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr(self.tcx.hir_body(body).value), @@ -540,18 +647,20 @@ impl<'tcx> ConstEvalCtxt<'tcx> { }, ExprKind::Index(arr, index, _) => self.index(arr, index), ExprKind::AddrOf(_, _, inner) => self.expr(inner).map(|r| Constant::Ref(Box::new(r))), - ExprKind::Field(local_expr, ref field) => { - self.check_ctxt(field.span.ctxt()); - let result = self.expr(local_expr); - if let Some(Constant::Adt(constant)) = &self.expr(local_expr) - && let ty::Adt(adt_def, _) = constant.ty().kind() + ExprKind::Field(base, ref field) + if let base_ty = self.typeck.expr_ty(base) + && match self.typeck.expr_adjustments(base) { + [] => true, + [.., a] => a.target == base_ty, + } + && let Some(Constant::Adt(constant)) = self.expr(base) + && let ty::Adt(adt_def, _) = *base_ty.kind() && adt_def.is_struct() - && let Some(desired_field) = field_of_struct(*adt_def, self.tcx, *constant, field) - { - mir_to_const(self.tcx, desired_field) - } else { - result - } + && let Some((desired_field, ty)) = + field_of_struct(adt_def, self.tcx, constant, base_ty, field.name) => + { + self.check_ctxt(field.span.ctxt()); + mir_to_const(self.tcx, desired_field, ty) }, _ => None, } @@ -564,19 +673,6 @@ impl<'tcx> ConstEvalCtxt<'tcx> { match e.kind { ExprKind::ConstBlock(ConstBlock { body, .. }) => self.eval_is_empty(self.tcx.hir_body(body).value), ExprKind::DropTemps(e) => self.eval_is_empty(e), - ExprKind::Path(ref qpath) => { - if !self - .typeck - .qpath_res(qpath, e.hir_id) - .opt_def_id() - .is_some_and(DefId::is_local) - { - return None; - } - self.fetch_path_and_apply(qpath, e.hir_id, self.typeck.expr_ty(e), |self_, result| { - mir_is_empty(self_.tcx, result) - }) - }, ExprKind::Lit(lit) => { if is_direct_expn_of(e.span, sym::cfg).is_some() { None @@ -601,7 +697,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } #[expect(clippy::cast_possible_wrap)] - fn constant_not(&self, o: &Constant<'tcx>, ty: Ty<'_>) -> Option> { + fn constant_not(&self, o: &Constant, ty: Ty<'_>) -> Option { use self::Constant::{Bool, Int}; match *o { Bool(b) => Some(Bool(!b)), @@ -617,7 +713,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } } - fn constant_negate(&self, o: &Constant<'tcx>, ty: Ty<'_>) -> Option> { + fn constant_negate(&self, o: &Constant, ty: Ty<'_>) -> Option { use self::Constant::{F32, F64, Int}; match *o { Int(value) => { @@ -643,48 +739,128 @@ impl<'tcx> ConstEvalCtxt<'tcx> { /// Create `Some(Vec![..])` of all constants, unless there is any /// non-constant part. - fn multi(&self, vec: &[Expr<'_>]) -> Option>> { + fn multi(&self, vec: &[Expr<'_>]) -> Option> { vec.iter().map(|elem| self.expr(elem)).collect::>() } /// Lookup a possibly constant expression from an `ExprKind::Path` and apply a function on it. - fn fetch_path_and_apply(&self, qpath: &QPath<'_>, id: HirId, ty: Ty<'tcx>, f: F) -> Option - where - F: FnOnce(&Self, mir::Const<'tcx>) -> Option, - { - let res = self.typeck.qpath_res(qpath, id); - match res { - Res::Def(DefKind::Const | DefKind::AssocConst, def_id) => { - // Check if this constant is based on `cfg!(..)`, - // which is NOT constant for our purposes. - if let Some(node) = self.tcx.hir_get_if_local(def_id) - && let Node::Item(Item { - kind: ItemKind::Const(.., body_id), - .. - }) = node - && let Node::Expr(Expr { - kind: ExprKind::Lit(_), - span, - .. - }) = self.tcx.hir_node(body_id.hir_id) - && is_direct_expn_of(*span, sym::cfg).is_some() - { - return None; - } - - let args = self.typeck.node_args(id); - let result = self - .tcx - .const_eval_resolve(self.typing_env, mir::UnevaluatedConst::new(def_id, args), qpath.span()) - .ok() - .map(|val| mir::Const::from_value(val, ty))?; - f(self, result) + #[expect(clippy::too_many_lines)] + fn fetch_path(&self, qpath: &QPath<'_>, id: HirId) -> Option { + // Resolve the path to a constant and check if that constant is known to + // not change based on the target. + // + // This should be replaced with an attribute at some point. + let did = match *qpath { + QPath::Resolved(None, path) + if path.span.ctxt() == self.ctxt.get() + && path.segments.iter().all(|s| self.ctxt.get() == s.ident.span.ctxt()) + && let Res::Def(DefKind::Const, did) = path.res + && (matches!( + self.tcx.get_diagnostic_name(did), + Some( + sym::f32_legacy_const_digits + | sym::f32_legacy_const_epsilon + | sym::f32_legacy_const_infinity + | sym::f32_legacy_const_mantissa_dig + | sym::f32_legacy_const_max + | sym::f32_legacy_const_max_10_exp + | sym::f32_legacy_const_max_exp + | sym::f32_legacy_const_min + | sym::f32_legacy_const_min_10_exp + | sym::f32_legacy_const_min_exp + | sym::f32_legacy_const_min_positive + | sym::f32_legacy_const_nan + | sym::f32_legacy_const_neg_infinity + | sym::f32_legacy_const_radix + | sym::f64_legacy_const_digits + | sym::f64_legacy_const_epsilon + | sym::f64_legacy_const_infinity + | sym::f64_legacy_const_mantissa_dig + | sym::f64_legacy_const_max + | sym::f64_legacy_const_max_10_exp + | sym::f64_legacy_const_max_exp + | sym::f64_legacy_const_min + | sym::f64_legacy_const_min_10_exp + | sym::f64_legacy_const_min_exp + | sym::f64_legacy_const_min_positive + | sym::f64_legacy_const_nan + | sym::f64_legacy_const_neg_infinity + | sym::f64_legacy_const_radix + | sym::u8_legacy_const_min + | sym::u16_legacy_const_min + | sym::u32_legacy_const_min + | sym::u64_legacy_const_min + | sym::u128_legacy_const_min + | sym::usize_legacy_const_min + | sym::u8_legacy_const_max + | sym::u16_legacy_const_max + | sym::u32_legacy_const_max + | sym::u64_legacy_const_max + | sym::u128_legacy_const_max + | sym::i8_legacy_const_min + | sym::i16_legacy_const_min + | sym::i32_legacy_const_min + | sym::i64_legacy_const_min + | sym::i128_legacy_const_min + | sym::i8_legacy_const_max + | sym::i16_legacy_const_max + | sym::i32_legacy_const_max + | sym::i64_legacy_const_max + | sym::i128_legacy_const_max + ) + ) || self.tcx.opt_parent(did).is_some_and(|parent| { + paths::F16_CONSTS.matches(&self.tcx, parent) + || paths::F32_CONSTS.matches(&self.tcx, parent) + || paths::F64_CONSTS.matches(&self.tcx, parent) + || paths::F128_CONSTS.matches(&self.tcx, parent) + })) => + { + did }, - _ => None, - } + QPath::TypeRelative(ty, const_name) + if let TyKind::Path(QPath::Resolved(None, ty_path)) = ty.kind + && let [.., ty_name] = ty_path.segments + && (matches!( + ty_name.ident.name, + sym::i8 + | sym::i16 + | sym::i32 + | sym::i64 + | sym::i128 + | sym::u8 + | sym::u16 + | sym::u32 + | sym::u64 + | sym::u128 + | sym::f32 + | sym::f64 + | sym::char + ) || (ty_name.ident.name == sym::usize && const_name.ident.name == sym::MIN)) + && const_name.ident.span.ctxt() == self.ctxt.get() + && ty.span.ctxt() == self.ctxt.get() + && ty_name.ident.span.ctxt() == self.ctxt.get() + && matches!(ty_path.res, Res::PrimTy(_)) + && let Some((DefKind::AssocConst, did)) = self.typeck.type_dependent_def(id) => + { + did + }, + _ if let Res::Def(DefKind::Const | DefKind::AssocConst, did) = self.typeck.qpath_res(qpath, id) => { + self.source.set(ConstantSource::NonLocal); + did + }, + _ => return None, + }; + + self.tcx + .const_eval_resolve( + self.typing_env, + mir::UnevaluatedConst::new(did, self.typeck.node_args(id)), + qpath.span(), + ) + .ok() } - fn index(&self, lhs: &'_ Expr<'_>, index: &'_ Expr<'_>) -> Option> { + fn index(&self, lhs: &'_ Expr<'_>, index: &'_ Expr<'_>) -> Option { let lhs = self.expr(lhs); let index = self.expr(index); @@ -714,7 +890,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } /// A block can only yield a constant if it has exactly one constant expression. - fn block(&self, block: &Block<'_>) -> Option> { + fn block(&self, block: &Block<'_>) -> Option { if block.stmts.is_empty() && let Some(expr) = block.expr { @@ -733,11 +909,11 @@ impl<'tcx> ConstEvalCtxt<'tcx> { .filter(|t| !matches!(t, Whitespace | LineComment { .. } | BlockComment { .. } | Semi)) .eq([OpenBrace]) { - self.source.set(ConstantSource::Constant); + self.source.set(ConstantSource::NonLocal); } } else { // Unable to access the source. Assume a non-local dependency. - self.source.set(ConstantSource::Constant); + self.source.set(ConstantSource::NonLocal); } } @@ -747,7 +923,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } } - fn ifthenelse(&self, cond: &Expr<'_>, then: &Expr<'_>, otherwise: Option<&Expr<'_>>) -> Option> { + fn ifthenelse(&self, cond: &Expr<'_>, then: &Expr<'_>, otherwise: Option<&Expr<'_>>) -> Option { if let Some(Constant::Bool(b)) = self.expr(cond) { if b { self.expr(then) @@ -759,7 +935,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } } - fn binop(&self, op: BinOpKind, left: &Expr<'_>, right: &Expr<'_>) -> Option> { + fn binop(&self, op: BinOpKind, left: &Expr<'_>, right: &Expr<'_>) -> Option { let l = self.expr(left)?; let r = self.expr(right); match (l, r) { @@ -795,6 +971,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { BinOpKind::BitXor => Some(zext(l ^ r)), BinOpKind::BitOr => Some(zext(l | r)), BinOpKind::BitAnd => Some(zext(l & r)), + // FIXME: f32/f64 currently consider `0.0` and `-0.0` as different. BinOpKind::Eq => Some(Constant::Bool(l == r)), BinOpKind::Ne => Some(Constant::Bool(l != r)), BinOpKind::Lt => Some(Constant::Bool(l < r)), @@ -873,14 +1050,10 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } } -pub fn mir_to_const<'tcx>(tcx: TyCtxt<'tcx>, result: mir::Const<'tcx>) -> Option> { - let mir::Const::Val(val, _) = result else { - // We only work on evaluated consts. - return None; - }; - match (val, result.ty().kind()) { - (ConstValue::Scalar(Scalar::Int(int)), _) => match result.ty().kind() { - ty::Adt(adt_def, _) if adt_def.is_struct() => Some(Constant::Adt(result)), +pub fn mir_to_const<'tcx>(tcx: TyCtxt<'tcx>, val: ConstValue, ty: Ty<'tcx>) -> Option { + match (val, ty.kind()) { + (_, &ty::Adt(adt_def, _)) if adt_def.is_struct() => Some(Constant::Adt(val)), + (ConstValue::Scalar(Scalar::Int(int)), _) => match ty.kind() { ty::Bool => Some(Constant::Bool(int == ScalarInt::TRUE)), ty::Uint(_) | ty::Int(_) => Some(Constant::Int(int.to_bits(int.size()))), ty::Float(FloatTy::F16) => Some(Constant::F16(int.into())), @@ -894,7 +1067,6 @@ pub fn mir_to_const<'tcx>(tcx: TyCtxt<'tcx>, result: mir::Const<'tcx>) -> Option let data = val.try_get_slice_bytes_for_diagnostics(tcx)?; String::from_utf8(data.to_owned()).ok().map(Constant::Str) }, - (_, ty::Adt(adt_def, _)) if adt_def.is_struct() => Some(Constant::Adt(result)), (ConstValue::Indirect { alloc_id, offset }, ty::Array(sub_type, len)) => { let alloc = tcx.global_alloc(alloc_id).unwrap_memory().inner(); let len = len.try_to_target_usize(tcx)?; @@ -919,62 +1091,30 @@ pub fn mir_to_const<'tcx>(tcx: TyCtxt<'tcx>, result: mir::Const<'tcx>) -> Option } } -fn mir_is_empty<'tcx>(tcx: TyCtxt<'tcx>, result: mir::Const<'tcx>) -> Option { - let mir::Const::Val(val, _) = result else { - // We only work on evaluated consts. - return None; - }; - match (val, result.ty().kind()) { - (_, ty::Ref(_, inner_ty, _)) => match inner_ty.kind() { - ty::Str | ty::Slice(_) => { - if let ConstValue::Indirect { alloc_id, offset } = val { - // Get the length from the slice, using the same formula as - // [`ConstValue::try_get_slice_bytes_for_diagnostics`]. - let a = tcx.global_alloc(alloc_id).unwrap_memory().inner(); - let ptr_size = tcx.data_layout.pointer_size(); - if a.size() < offset + 2 * ptr_size { - // (partially) dangling reference - return None; - } - let len = a - .read_scalar(&tcx, alloc_range(offset + ptr_size, ptr_size), false) - .ok()? - .to_target_usize(&tcx) - .discard_err()?; - Some(len == 0) - } else { - None - } - }, - ty::Array(_, len) => Some(len.try_to_target_usize(tcx)? == 0), - _ => None, - }, - (ConstValue::Indirect { .. }, ty::Array(_, len)) => Some(len.try_to_target_usize(tcx)? == 0), - (ConstValue::ZeroSized, _) => Some(true), - _ => None, - } -} - fn field_of_struct<'tcx>( adt_def: ty::AdtDef<'tcx>, tcx: TyCtxt<'tcx>, - result: mir::Const<'tcx>, - field: &Ident, -) -> Option> { - if let mir::Const::Val(result, ty) = result - && let Some(dc) = tcx.try_destructure_mir_constant_for_user_output(result, ty) + value: ConstValue, + ty: Ty<'tcx>, + field: Symbol, +) -> Option<(ConstValue, Ty<'tcx>)> { + if let Some(dc) = tcx.try_destructure_mir_constant_for_user_output(value, ty) && let Some(dc_variant) = dc.variant && let Some(variant) = adt_def.variants().get(dc_variant) - && let Some(field_idx) = variant.fields.iter().position(|el| el.name == field.name) - && let Some(&(val, ty)) = dc.fields.get(field_idx) + && let Some(field_idx) = variant.fields.iter().position(|el| el.name == field) { - Some(mir::Const::Val(val, ty)) + dc.fields.get(field_idx).copied() } else { None } } /// If `expr` evaluates to an integer constant, return its value. +/// +/// The context argument is the context used to view the evaluated expression. e.g. when evaluating +/// the argument in `f(m!(1))` the context of the call expression should be used. This is need so +/// the const evaluator can see the `m` macro and marke the evaluation as non-local independant of +/// what the macro expands to. pub fn integer_const(cx: &LateContext<'_>, expr: &Expr<'_>, ctxt: SyntaxContext) -> Option { if let Some(Constant::Int(value)) = ConstEvalCtxt::new(cx).eval_local(expr, ctxt) { Some(value) @@ -984,6 +1124,11 @@ pub fn integer_const(cx: &LateContext<'_>, expr: &Expr<'_>, ctxt: SyntaxContext) } /// Check if `expr` evaluates to an integer constant of 0. +/// +/// The context argument is the context used to view the evaluated expression. e.g. when evaluating +/// the argument in `f(m!(1))` the context of the call expression should be used. This is need so +/// the const evaluator can see the `m` macro and marke the evaluation as non-local independant of +/// what the macro expands to. #[inline] pub fn is_zero_integer_const(cx: &LateContext<'_>, expr: &Expr<'_>, ctxt: SyntaxContext) -> bool { integer_const(cx, expr, ctxt) == Some(0) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 708491df7707..a2c8ec34c6be 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -126,7 +126,7 @@ use source::{SpanRangeExt, walk_span_to_context}; use visitors::{Visitable, for_each_unconsumed_temporary}; use crate::ast_utils::unordered_over; -use crate::consts::{ConstEvalCtxt, Constant, mir_to_const}; +use crate::consts::{ConstEvalCtxt, Constant}; use crate::higher::Range; use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type}; use crate::visitors::for_each_expr_without_closures; @@ -1422,11 +1422,9 @@ 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_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 + start_const.is_numeric_min(cx.tcx, bnd_ty) } else { false } @@ -1435,11 +1433,9 @@ 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_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 + end_const.is_numeric_max(cx.tcx, bnd_ty) } else { false } diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index ea8cfc59356a..5ab8e16d88ed 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -13,6 +13,7 @@ use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::{ItemKind, Node, UseKind}; use rustc_lint::LateContext; use rustc_middle::ty::fast_reject::SimplifiedType; +use rustc_middle::ty::layout::HasTyCtxt; use rustc_middle::ty::{FloatTy, IntTy, Ty, TyCtxt, UintTy}; use rustc_span::{Ident, STDLIB_STABLE_CRATES, Symbol}; use std::sync::OnceLock; @@ -74,8 +75,8 @@ impl PathLookup { } /// Returns the list of [`DefId`]s that the path resolves to - pub fn get(&self, cx: &LateContext<'_>) -> &[DefId] { - self.once.get_or_init(|| lookup_path(cx.tcx, self.ns, self.path)) + pub fn get<'tcx>(&self, tcx: &impl HasTyCtxt<'tcx>) -> &[DefId] { + self.once.get_or_init(|| lookup_path(tcx.tcx(), self.ns, self.path)) } /// Returns the single [`DefId`] that the path resolves to, this can only be used for paths into @@ -90,8 +91,8 @@ impl PathLookup { } /// Checks if the path resolves to the given `def_id` - pub fn matches(&self, cx: &LateContext<'_>, def_id: DefId) -> bool { - self.get(cx).contains(&def_id) + pub fn matches<'tcx>(&self, tcx: &impl HasTyCtxt<'tcx>, def_id: DefId) -> bool { + self.get(&tcx.tcx()).contains(&def_id) } /// Resolves `maybe_path` to a [`DefId`] and checks if the [`PathLookup`] matches it @@ -100,8 +101,8 @@ impl PathLookup { } /// Checks if the path resolves to `ty`'s definition, must be an `Adt` - pub fn matches_ty(&self, cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - ty.ty_adt_def().is_some_and(|adt| self.matches(cx, adt.did())) + pub fn matches_ty<'tcx>(&self, tcx: &impl HasTyCtxt<'tcx>, ty: Ty<'_>) -> bool { + ty.ty_adt_def().is_some_and(|adt| self.matches(&tcx.tcx(), adt.did())) } } @@ -126,6 +127,11 @@ path_macros! { macro_path: PathNS::Macro, } +pub static F16_CONSTS: PathLookup = type_path!(core::f16::consts); +pub static F32_CONSTS: PathLookup = type_path!(core::f32::consts); +pub static F64_CONSTS: PathLookup = type_path!(core::f64::consts); +pub static F128_CONSTS: PathLookup = type_path!(core::f128::consts); + // Paths in external crates pub static FUTURES_IO_ASYNCREADEXT: PathLookup = type_path!(futures_util::AsyncReadExt); pub static FUTURES_IO_ASYNCWRITEEXT: PathLookup = type_path!(futures_util::AsyncWriteExt); diff --git a/clippy_utils/src/sym.rs b/clippy_utils/src/sym.rs index 4ba0e52572dd..2d0d4a5319f3 100644 --- a/clippy_utils/src/sym.rs +++ b/clippy_utils/src/sym.rs @@ -115,6 +115,7 @@ generate! { collapsible_if, collect, const_ptr, + consts, contains, copied, copy_from, diff --git a/tests/ui/const_comparisons.rs b/tests/ui/const_comparisons.rs index b732d7d142fc..a2a1f0a7b4c0 100644 --- a/tests/ui/const_comparisons.rs +++ b/tests/ui/const_comparisons.rs @@ -1,9 +1,11 @@ -#![allow(unused)] -#![warn(clippy::impossible_comparisons)] -#![warn(clippy::redundant_comparisons)] -#![allow(clippy::no_effect)] -#![allow(clippy::short_circuit_statement)] -#![allow(clippy::manual_range_contains)] +#![allow( + unused, + clippy::identity_op, + clippy::manual_range_contains, + clippy::no_effect, + clippy::short_circuit_statement +)] +#![warn(clippy::impossible_comparisons, clippy::redundant_comparisons)] const STATUS_BAD_REQUEST: u16 = 400; const STATUS_SERVER_ERROR: u16 = 500; diff --git a/tests/ui/const_comparisons.stderr b/tests/ui/const_comparisons.stderr index 48a2c6e8d487..1ce62c23ff29 100644 --- a/tests/ui/const_comparisons.stderr +++ b/tests/ui/const_comparisons.stderr @@ -1,5 +1,5 @@ error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:45:5 + --> tests/ui/const_comparisons.rs:47:5 | LL | status_code <= 400 && status_code > 500; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | status_code <= 400 && status_code > 500; = help: to override `-D warnings` add `#[allow(clippy::impossible_comparisons)]` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:48:5 + --> tests/ui/const_comparisons.rs:50:5 | LL | status_code > 500 && status_code < 400; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | status_code > 500 && status_code < 400; = note: since `500` > `400`, the expression evaluates to false for any value of `status_code` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:51:5 + --> tests/ui/const_comparisons.rs:53:5 | LL | status_code < 500 && status_code > 500; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL | status_code < 500 && status_code > 500; = note: `status_code` cannot simultaneously be greater than and less than `500` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:55:5 + --> tests/ui/const_comparisons.rs:57:5 | LL | status_code < { 400 } && status_code > { 500 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -33,7 +33,7 @@ LL | status_code < { 400 } && status_code > { 500 }; = note: since `{ 400 }` < `{ 500 }`, the expression evaluates to false for any value of `status_code` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:58:5 + --> tests/ui/const_comparisons.rs:60:5 | LL | status_code < STATUS_BAD_REQUEST && status_code > STATUS_SERVER_ERROR; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -41,7 +41,7 @@ LL | status_code < STATUS_BAD_REQUEST && status_code > STATUS_SERVER_ERROR; = note: since `STATUS_BAD_REQUEST` < `STATUS_SERVER_ERROR`, the expression evaluates to false for any value of `status_code` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:61:5 + --> tests/ui/const_comparisons.rs:63:5 | LL | status_code <= u16::MIN + 1 && status_code > STATUS_SERVER_ERROR; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -49,7 +49,7 @@ LL | status_code <= u16::MIN + 1 && status_code > STATUS_SERVER_ERROR; = note: since `u16::MIN + 1` < `STATUS_SERVER_ERROR`, the expression evaluates to false for any value of `status_code` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:64:5 + --> tests/ui/const_comparisons.rs:66:5 | LL | status_code < STATUS_SERVER_ERROR && status_code > STATUS_SERVER_ERROR; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -57,7 +57,7 @@ LL | status_code < STATUS_SERVER_ERROR && status_code > STATUS_SERVER_ERROR; = note: `status_code` cannot simultaneously be greater than and less than `STATUS_SERVER_ERROR` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:68:5 + --> tests/ui/const_comparisons.rs:70:5 | LL | status < { 400 } && status > { 500 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -65,7 +65,7 @@ LL | status < { 400 } && status > { 500 }; = note: since `{ 400 }` < `{ 500 }`, the expression evaluates to false for any value of `status` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:71:5 + --> tests/ui/const_comparisons.rs:73:5 | LL | status < STATUS_BAD_REQUEST && status > STATUS_SERVER_ERROR; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -73,7 +73,7 @@ LL | status < STATUS_BAD_REQUEST && status > STATUS_SERVER_ERROR; = note: since `STATUS_BAD_REQUEST` < `STATUS_SERVER_ERROR`, the expression evaluates to false for any value of `status` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:74:5 + --> tests/ui/const_comparisons.rs:76:5 | LL | status <= u16::MIN + 1 && status > STATUS_SERVER_ERROR; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -81,7 +81,7 @@ LL | status <= u16::MIN + 1 && status > STATUS_SERVER_ERROR; = note: since `u16::MIN + 1` < `STATUS_SERVER_ERROR`, the expression evaluates to false for any value of `status` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:77:5 + --> tests/ui/const_comparisons.rs:79:5 | LL | status < STATUS_SERVER_ERROR && status > STATUS_SERVER_ERROR; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -89,7 +89,7 @@ LL | status < STATUS_SERVER_ERROR && status > STATUS_SERVER_ERROR; = note: `status` cannot simultaneously be greater than and less than `STATUS_SERVER_ERROR` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:86:5 + --> tests/ui/const_comparisons.rs:88:5 | LL | 500 >= status_code && 600 < status_code; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -97,7 +97,7 @@ LL | 500 >= status_code && 600 < status_code; = note: since `500` < `600`, the expression evaluates to false for any value of `status_code` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:90:5 + --> tests/ui/const_comparisons.rs:92:5 | LL | 500 >= status_code && status_code > 600; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -105,7 +105,7 @@ LL | 500 >= status_code && status_code > 600; = note: since `500` < `600`, the expression evaluates to false for any value of `status_code` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:99:5 + --> tests/ui/const_comparisons.rs:101:5 | LL | 500 >= status && 600 < status; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -113,7 +113,7 @@ LL | 500 >= status && 600 < status; = note: since `500` < `600`, the expression evaluates to false for any value of `status` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:103:5 + --> tests/ui/const_comparisons.rs:105:5 | LL | 500 >= status && status > 600; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -121,13 +121,13 @@ LL | 500 >= status && status > 600; = note: since `500` < `600`, the expression evaluates to false for any value of `status` error: right-hand side of `&&` operator has no effect - --> tests/ui/const_comparisons.rs:107:5 + --> tests/ui/const_comparisons.rs:109:5 | LL | status_code < 200 && status_code <= 299; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: `if `status_code < 200` evaluates to true, status_code <= 299` will always evaluate to true as well - --> tests/ui/const_comparisons.rs:107:23 + --> tests/ui/const_comparisons.rs:109:23 | LL | status_code < 200 && status_code <= 299; | ^^^^^^^^^^^^^^^^^^^^^ @@ -135,67 +135,67 @@ LL | status_code < 200 && status_code <= 299; = help: to override `-D warnings` add `#[allow(clippy::redundant_comparisons)]` error: left-hand side of `&&` operator has no effect - --> tests/ui/const_comparisons.rs:110:5 + --> tests/ui/const_comparisons.rs:112:5 | LL | status_code > 200 && status_code >= 299; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: `if `status_code >= 299` evaluates to true, status_code > 200` will always evaluate to true as well - --> tests/ui/const_comparisons.rs:110:5 + --> tests/ui/const_comparisons.rs:112:5 | LL | status_code > 200 && status_code >= 299; | ^^^^^^^^^^^^^^^^^^^^^ error: left-hand side of `&&` operator has no effect - --> tests/ui/const_comparisons.rs:114:5 + --> tests/ui/const_comparisons.rs:116:5 | LL | status_code >= 500 && status_code > 500; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: `if `status_code > 500` evaluates to true, status_code >= 500` will always evaluate to true as well - --> tests/ui/const_comparisons.rs:114:5 + --> tests/ui/const_comparisons.rs:116:5 | LL | status_code >= 500 && status_code > 500; | ^^^^^^^^^^^^^^^^^^^^^^ error: right-hand side of `&&` operator has no effect - --> tests/ui/const_comparisons.rs:118:5 + --> tests/ui/const_comparisons.rs:120:5 | LL | status_code > 500 && status_code >= 500; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: `if `status_code > 500` evaluates to true, status_code >= 500` will always evaluate to true as well - --> tests/ui/const_comparisons.rs:118:23 + --> tests/ui/const_comparisons.rs:120:23 | LL | status_code > 500 && status_code >= 500; | ^^^^^^^^^^^^^^^^^^^^^ error: left-hand side of `&&` operator has no effect - --> tests/ui/const_comparisons.rs:122:5 + --> tests/ui/const_comparisons.rs:124:5 | LL | status_code <= 500 && status_code < 500; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: `if `status_code < 500` evaluates to true, status_code <= 500` will always evaluate to true as well - --> tests/ui/const_comparisons.rs:122:5 + --> tests/ui/const_comparisons.rs:124:5 | LL | status_code <= 500 && status_code < 500; | ^^^^^^^^^^^^^^^^^^^^^^ error: right-hand side of `&&` operator has no effect - --> tests/ui/const_comparisons.rs:126:5 + --> tests/ui/const_comparisons.rs:128:5 | LL | status_code < 500 && status_code <= 500; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: `if `status_code < 500` evaluates to true, status_code <= 500` will always evaluate to true as well - --> tests/ui/const_comparisons.rs:126:23 + --> tests/ui/const_comparisons.rs:128:23 | LL | status_code < 500 && status_code <= 500; | ^^^^^^^^^^^^^^^^^^^^^ error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:131:5 + --> tests/ui/const_comparisons.rs:133:5 | LL | name < "Jennifer" && name > "Shannon"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -203,7 +203,7 @@ LL | name < "Jennifer" && name > "Shannon"; = note: since `"Jennifer"` < `"Shannon"`, the expression evaluates to false for any value of `name` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:135:5 + --> tests/ui/const_comparisons.rs:137:5 | LL | numbers < [3, 4] && numbers > [5, 6]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -211,7 +211,7 @@ LL | numbers < [3, 4] && numbers > [5, 6]; = note: since `[3, 4]` < `[5, 6]`, the expression evaluates to false for any value of `numbers` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:139:5 + --> tests/ui/const_comparisons.rs:141:5 | LL | letter < 'b' && letter > 'c'; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -219,7 +219,7 @@ LL | letter < 'b' && letter > 'c'; = note: since `'b'` < `'c'`, the expression evaluates to false for any value of `letter` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:143:5 + --> tests/ui/const_comparisons.rs:145:5 | LL | area < std::f32::consts::E && area > std::f32::consts::PI; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/const_is_empty.rs b/tests/ui/const_is_empty.rs index 8bb4f0e5d975..2ad1b5276def 100644 --- a/tests/ui/const_is_empty.rs +++ b/tests/ui/const_is_empty.rs @@ -55,53 +55,6 @@ const NON_EMPTY_ARRAY_REPEAT: [u32; 2] = [1; 2]; const EMPTY_REF_ARRAY: &[u32; 0] = &[]; const NON_EMPTY_REF_ARRAY: &[u32; 3] = &[1, 2, 3]; -fn test_from_const() { - let _ = EMPTY_STR.is_empty(); - //~^ const_is_empty - - let _ = NON_EMPTY_STR.is_empty(); - //~^ const_is_empty - - let _ = EMPTY_BSTR.is_empty(); - //~^ const_is_empty - - let _ = NON_EMPTY_BSTR.is_empty(); - //~^ const_is_empty - - let _ = EMPTY_ARRAY.is_empty(); - //~^ const_is_empty - - let _ = EMPTY_ARRAY_REPEAT.is_empty(); - //~^ const_is_empty - - let _ = EMPTY_U8_SLICE.is_empty(); - //~^ const_is_empty - - let _ = NON_EMPTY_U8_SLICE.is_empty(); - //~^ const_is_empty - - let _ = NON_EMPTY_ARRAY.is_empty(); - //~^ const_is_empty - - let _ = NON_EMPTY_ARRAY_REPEAT.is_empty(); - //~^ const_is_empty - - let _ = EMPTY_REF_ARRAY.is_empty(); - //~^ const_is_empty - - let _ = NON_EMPTY_REF_ARRAY.is_empty(); - //~^ const_is_empty - - let _ = EMPTY_SLICE.is_empty(); - //~^ const_is_empty - - let _ = NON_EMPTY_SLICE.is_empty(); - //~^ const_is_empty - - let _ = NON_EMPTY_SLICE_REPEAT.is_empty(); - //~^ const_is_empty -} - fn main() { let value = "foobar"; let _ = value.is_empty(); @@ -120,7 +73,7 @@ fn main() { fn str_from_arg(var: &str) { var.is_empty(); - // Do not lint, we know nothiny about var + // Do not lint, we know nothing about var } fn update_str() { @@ -200,6 +153,5 @@ fn issue_13106() { const { EMPTY_STR.is_empty(); - //~^ const_is_empty } } diff --git a/tests/ui/const_is_empty.stderr b/tests/ui/const_is_empty.stderr index 2ba189058e83..e1837695bc1c 100644 --- a/tests/ui/const_is_empty.stderr +++ b/tests/ui/const_is_empty.stderr @@ -37,131 +37,35 @@ error: this expression always evaluates to false LL | if non_empty2.is_empty() { | ^^^^^^^^^^^^^^^^^^^^^ -error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:59:13 - | -LL | let _ = EMPTY_STR.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^ - error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:62:13 - | -LL | let _ = NON_EMPTY_STR.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:65:13 - | -LL | let _ = EMPTY_BSTR.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:68:13 - | -LL | let _ = NON_EMPTY_BSTR.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:71:13 - | -LL | let _ = EMPTY_ARRAY.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:74:13 - | -LL | let _ = EMPTY_ARRAY_REPEAT.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:77:13 - | -LL | let _ = EMPTY_U8_SLICE.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:80:13 - | -LL | let _ = NON_EMPTY_U8_SLICE.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:83:13 - | -LL | let _ = NON_EMPTY_ARRAY.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:86:13 - | -LL | let _ = NON_EMPTY_ARRAY_REPEAT.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:89:13 - | -LL | let _ = EMPTY_REF_ARRAY.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:92:13 - | -LL | let _ = NON_EMPTY_REF_ARRAY.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:95:13 - | -LL | let _ = EMPTY_SLICE.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:98:13 - | -LL | let _ = NON_EMPTY_SLICE.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:101:13 - | -LL | let _ = NON_EMPTY_SLICE_REPEAT.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:107:13 + --> tests/ui/const_is_empty.rs:60:13 | LL | let _ = value.is_empty(); | ^^^^^^^^^^^^^^^^ error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:111:13 + --> tests/ui/const_is_empty.rs:64:13 | LL | let _ = x.is_empty(); | ^^^^^^^^^^^^ error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:114:13 + --> tests/ui/const_is_empty.rs:67:13 | LL | let _ = "".is_empty(); | ^^^^^^^^^^^^^ error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:117:13 + --> tests/ui/const_is_empty.rs:70:13 | LL | let _ = b"".is_empty(); | ^^^^^^^^^^^^^^ error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:171:13 + --> tests/ui/const_is_empty.rs:124:13 | LL | let _ = val.is_empty(); | ^^^^^^^^^^^^^^ -error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:202:9 - | -LL | EMPTY_STR.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 27 previous errors +error: aborting due to 11 previous errors From 47b0f903a3b0e2a863f34a6bd1c33196f851c796 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sun, 24 Aug 2025 10:38:06 -0400 Subject: [PATCH 159/226] Use `eval_local` in more places. --- clippy_lints/src/floating_point_arithmetic.rs | 6 +- clippy_lints/src/implicit_saturating_add.rs | 5 +- clippy_lints/src/manual_float_methods.rs | 29 +--- clippy_lints/src/manual_rotate.rs | 2 +- clippy_lints/src/manual_strip.rs | 24 ++- clippy_lints/src/methods/ip_constant.rs | 4 +- clippy_lints/src/methods/iter_nth_zero.rs | 2 +- clippy_lints/src/methods/iter_skip_zero.rs | 16 +- clippy_lints/src/methods/repeat_once.rs | 2 +- clippy_lints/src/methods/str_splitn.rs | 2 +- clippy_lints/src/minmax.rs | 6 +- clippy_lints/src/operators/duration_subsec.rs | 2 +- .../src/operators/modulo_arithmetic.rs | 6 +- clippy_lints/src/ranges.rs | 2 +- clippy_utils/src/consts.rs | 8 +- tests/ui/duration_subsec.fixed | 3 +- tests/ui/duration_subsec.rs | 1 - tests/ui/duration_subsec.stderr | 8 +- tests/ui/floating_point_log.fixed | 6 +- tests/ui/floating_point_log.rs | 2 - tests/ui/floating_point_log.stderr | 60 +++---- tests/ui/ip_constant.fixed | 43 ++++-- tests/ui/ip_constant.rs | 9 -- tests/ui/ip_constant.stderr | 146 +----------------- tests/ui/manual_float_methods.rs | 15 +- tests/ui/manual_float_methods.stderr | 40 +---- tests/ui/manual_strip.stderr | 3 - tests/ui/repeat_once.fixed | 3 +- tests/ui/repeat_once.rs | 1 - tests/ui/repeat_once.stderr | 14 +- 30 files changed, 131 insertions(+), 339 deletions(-) diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 3ecc232caff4..407a3f130673 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -111,8 +111,8 @@ declare_lint_pass!(FloatingPointArithmetic => [ // Returns the specialized log method for a given base if base is constant // and is one of 2, 10 and e -fn get_specialized_log_method(cx: &LateContext<'_>, base: &Expr<'_>) -> Option<&'static str> { - if let Some(value) = ConstEvalCtxt::new(cx).eval(base) { +fn get_specialized_log_method(cx: &LateContext<'_>, base: &Expr<'_>, ctxt: SyntaxContext) -> Option<&'static str> { + if let Some(value) = ConstEvalCtxt::new(cx).eval_local(base, ctxt) { if F32(2.0) == value || F64(2.0) == value { return Some("log2"); } else if F32(10.0) == value || F64(10.0) == value { @@ -158,7 +158,7 @@ fn prepare_receiver_sugg<'a>(cx: &LateContext<'_>, mut expr: &'a Expr<'a>) -> Su } fn check_log_base(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) { - if let Some(method) = get_specialized_log_method(cx, &args[0]) { + if let Some(method) = get_specialized_log_method(cx, &args[0], expr.span.ctxt()) { span_lint_and_sugg( cx, SUBOPTIMAL_FLOPS, diff --git a/clippy_lints/src/implicit_saturating_add.rs b/clippy_lints/src/implicit_saturating_add.rs index 0fdbf6797381..4bf3a390b050 100644 --- a/clippy_lints/src/implicit_saturating_add.rs +++ b/clippy_lints/src/implicit_saturating_add.rs @@ -117,10 +117,11 @@ fn get_int_max(ty: Ty<'_>) -> Option { fn get_const<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<(u128, BinOpKind, &'tcx Expr<'tcx>)> { if let ExprKind::Binary(op, l, r) = expr.kind { let ecx = ConstEvalCtxt::new(cx); - if let Some(Constant::Int(c)) = ecx.eval(r) { + let ctxt = expr.span.ctxt(); + if let Some(Constant::Int(c)) = ecx.eval_local(r, ctxt) { return Some((c, op.node, l)); } - if let Some(Constant::Int(c)) = ecx.eval(l) { + if let Some(Constant::Int(c)) = ecx.eval_local(l, ctxt) { return Some((c, invert_op(op.node)?, r)); } } diff --git a/clippy_lints/src/manual_float_methods.rs b/clippy_lints/src/manual_float_methods.rs index 7d6c30aa74dd..60782f445ab9 100644 --- a/clippy_lints/src/manual_float_methods.rs +++ b/clippy_lints/src/manual_float_methods.rs @@ -1,5 +1,5 @@ use clippy_config::Conf; -use clippy_utils::consts::{ConstEvalCtxt, Constant}; +use clippy_utils::consts::ConstEvalCtxt; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::SpanRangeExt; @@ -146,13 +146,14 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods { ) && let [first, second, const_1, const_2] = exprs && let ecx = ConstEvalCtxt::new(cx) - && let Some(const_1) = ecx.eval(const_1) - && let Some(const_2) = ecx.eval(const_2) + && let ctxt = expr.span.ctxt() + && let Some(const_1) = ecx.eval_local(const_1, ctxt) + && let Some(const_2) = ecx.eval_local(const_2, ctxt) && path_to_local(first).is_some_and(|f| path_to_local(second).is_some_and(|s| f == s)) // The actual infinity check, we also allow `NEG_INFINITY` before` INFINITY` just in // case somebody does that for some reason - && (is_infinity(&const_1) && is_neg_infinity(&const_2) - || is_neg_infinity(&const_1) && is_infinity(&const_2)) + && (const_1.is_pos_infinity() && const_2.is_neg_infinity() + || const_1.is_neg_infinity() && const_2.is_pos_infinity()) && let Some(local_snippet) = first.span.get_source_text(cx) { let variant = match (kind.node, lhs_kind.node, rhs_kind.node) { @@ -201,21 +202,3 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods { } } } - -fn is_infinity(constant: &Constant) -> bool { - match constant { - // FIXME(f16_f128): add f16 and f128 when constants are available - Constant::F32(float) => *float == f32::INFINITY, - Constant::F64(float) => *float == f64::INFINITY, - _ => false, - } -} - -fn is_neg_infinity(constant: &Constant) -> bool { - match constant { - // FIXME(f16_f128): add f16 and f128 when constants are available - Constant::F32(float) => *float == f32::NEG_INFINITY, - Constant::F64(float) => *float == f64::NEG_INFINITY, - _ => false, - } -} diff --git a/clippy_lints/src/manual_rotate.rs b/clippy_lints/src/manual_rotate.rs index 06ee00c2cef3..22e3407303f0 100644 --- a/clippy_lints/src/manual_rotate.rs +++ b/clippy_lints/src/manual_rotate.rs @@ -66,7 +66,7 @@ fn parse_shift<'tcx>( BinOpKind::Shr => ShiftDirection::Right, _ => return None, }; - let const_expr = ConstEvalCtxt::new(cx).eval(r)?; + let const_expr = ConstEvalCtxt::new(cx).eval_local(r, expr.span.ctxt())?; if let Constant::Int(shift) = const_expr { return Some((dir, shift, l)); } diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index 07cce4046ca4..f5d15310879a 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -16,7 +16,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext as _}; use rustc_middle::ty; use rustc_session::impl_lint_pass; use rustc_span::source_map::Spanned; -use rustc_span::{Symbol, sym}; +use rustc_span::{Symbol, SyntaxContext, sym}; use std::iter; declare_clippy_lint! { @@ -92,7 +92,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip { return; } - let (strippings, bindings) = find_stripping(cx, strip_kind, target_res, pattern, then); + let (strippings, bindings) = find_stripping(cx, strip_kind, target_res, pattern, then, expr.span.ctxt()); if !strippings.is_empty() && self.msrv.meets(cx, msrvs::STR_STRIP_PREFIX) { let kind_word = match strip_kind { StripKind::Prefix => "prefix", @@ -166,8 +166,8 @@ fn len_arg<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx E } // Returns the length of the `expr` if it's a constant string or char. -fn constant_length(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { - let value = ConstEvalCtxt::new(cx).eval(expr)?; +fn constant_length(cx: &LateContext<'_>, expr: &Expr<'_>, ctxt: SyntaxContext) -> Option { + let value = ConstEvalCtxt::new(cx).eval_local(expr, ctxt)?; match value { Constant::Str(value) => Some(value.len() as u128), Constant::Char(value) => Some(value.len_utf8() as u128), @@ -176,13 +176,18 @@ fn constant_length(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { } // Tests if `expr` equals the length of the pattern. -fn eq_pattern_length<'tcx>(cx: &LateContext<'tcx>, pattern: &Expr<'_>, expr: &'tcx Expr<'_>) -> bool { +fn eq_pattern_length<'tcx>( + cx: &LateContext<'tcx>, + pattern: &Expr<'_>, + expr: &'tcx Expr<'_>, + ctxt: SyntaxContext, +) -> bool { if let ExprKind::Lit(Spanned { node: LitKind::Int(n, _), .. }) = expr.kind { - constant_length(cx, pattern).is_some_and(|length| n == length) + constant_length(cx, pattern, ctxt).is_some_and(|length| n == length) } else { len_arg(cx, expr).is_some_and(|arg| eq_expr_value(cx, pattern, arg)) } @@ -215,6 +220,7 @@ fn find_stripping<'tcx>( target: Res, pattern: &'tcx Expr<'_>, expr: &'tcx Expr<'tcx>, + ctxt: SyntaxContext, ) -> (Vec<&'tcx Expr<'tcx>>, FxHashMap) { struct StrippingFinder<'a, 'tcx> { cx: &'a LateContext<'tcx>, @@ -223,6 +229,7 @@ fn find_stripping<'tcx>( pattern: &'tcx Expr<'tcx>, results: Vec<&'tcx Expr<'tcx>>, bindings: FxHashMap, + ctxt: SyntaxContext, } impl<'tcx> Visitor<'tcx> for StrippingFinder<'_, 'tcx> { @@ -236,7 +243,7 @@ fn find_stripping<'tcx>( { match (self.strip_kind, start, end) { (StripKind::Prefix, Some(start), None) => { - if eq_pattern_length(self.cx, self.pattern, start) { + if eq_pattern_length(self.cx, self.pattern, start, self.ctxt) { self.results.push(ex); return; } @@ -252,7 +259,7 @@ fn find_stripping<'tcx>( && let Some(left_arg) = len_arg(self.cx, left) && let ExprKind::Path(left_path) = &left_arg.kind && self.cx.qpath_res(left_path, left_arg.hir_id) == self.target - && eq_pattern_length(self.cx, self.pattern, right) + && eq_pattern_length(self.cx, self.pattern, right, self.ctxt) { self.results.push(ex); return; @@ -280,6 +287,7 @@ fn find_stripping<'tcx>( pattern, results: vec![], bindings: FxHashMap::default(), + ctxt, }; walk_expr(&mut finder, expr); (finder.results, finder.bindings) diff --git a/clippy_lints/src/methods/ip_constant.rs b/clippy_lints/src/methods/ip_constant.rs index a2ac4e54334e..bf602811009a 100644 --- a/clippy_lints/src/methods/ip_constant.rs +++ b/clippy_lints/src/methods/ip_constant.rs @@ -17,10 +17,12 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, func: &Expr<'_>, args cx.tcx.get_diagnostic_name(func_def_id), Some(sym::Ipv4Addr | sym::Ipv6Addr) ) + && let ecx = ConstEvalCtxt::new(cx) + && let ctxt = expr.span.ctxt() && let Some(args) = args .iter() .map(|arg| { - if let Some(Constant::Int(constant @ (0 | 1 | 127 | 255))) = ConstEvalCtxt::new(cx).eval(arg) { + if let Some(Constant::Int(constant @ (0 | 1 | 127 | 255))) = ecx.eval_local(arg, ctxt) { u8::try_from(constant).ok() } else { None diff --git a/clippy_lints/src/methods/iter_nth_zero.rs b/clippy_lints/src/methods/iter_nth_zero.rs index 4bdf589f4876..0f8abd017242 100644 --- a/clippy_lints/src/methods/iter_nth_zero.rs +++ b/clippy_lints/src/methods/iter_nth_zero.rs @@ -14,7 +14,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr if let OwnerNode::Item(item) = cx.tcx.hir_owner_node(cx.tcx.hir_get_parent_item(expr.hir_id)) && let def_id = item.owner_id.to_def_id() && is_trait_method(cx, expr, sym::Iterator) - && let Some(Constant::Int(0)) = ConstEvalCtxt::new(cx).eval(arg) + && let Some(Constant::Int(0)) = ConstEvalCtxt::new(cx).eval_local(arg, expr.span.ctxt()) && !is_lang_item_or_ctor(cx, def_id, LangItem::IteratorNext) { let mut app = Applicability::MachineApplicable; diff --git a/clippy_lints/src/methods/iter_skip_zero.rs b/clippy_lints/src/methods/iter_skip_zero.rs index 39e440e784f6..663e34437a30 100644 --- a/clippy_lints/src/methods/iter_skip_zero.rs +++ b/clippy_lints/src/methods/iter_skip_zero.rs @@ -11,13 +11,15 @@ use super::ITER_SKIP_ZERO; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg_expr: &Expr<'_>) { if !expr.span.from_expansion() && is_trait_method(cx, expr, sym::Iterator) - && let Some(arg) = ConstEvalCtxt::new(cx).eval(arg_expr).and_then(|constant| { - if let Constant::Int(arg) = constant { - Some(arg) - } else { - None - } - }) + && let Some(arg) = ConstEvalCtxt::new(cx) + .eval_local(arg_expr, expr.span.ctxt()) + .and_then(|constant| { + if let Constant::Int(arg) = constant { + Some(arg) + } else { + None + } + }) && arg == 0 && !is_from_proc_macro(cx, expr) { diff --git a/clippy_lints/src/methods/repeat_once.rs b/clippy_lints/src/methods/repeat_once.rs index 7837517ed5d8..9111604ef53b 100644 --- a/clippy_lints/src/methods/repeat_once.rs +++ b/clippy_lints/src/methods/repeat_once.rs @@ -14,7 +14,7 @@ pub(super) fn check<'tcx>( recv: &'tcx Expr<'_>, repeat_arg: &'tcx Expr<'_>, ) { - if ConstEvalCtxt::new(cx).eval(repeat_arg) == Some(Constant::Int(1)) { + if ConstEvalCtxt::new(cx).eval_local(repeat_arg, expr.span.ctxt()) == Some(Constant::Int(1)) { let ty = cx.typeck_results().expr_ty(recv).peel_refs(); if ty.is_str() { span_lint_and_sugg( diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs index 51dd4ac313a6..8daa5db887ac 100644 --- a/clippy_lints/src/methods/str_splitn.rs +++ b/clippy_lints/src/methods/str_splitn.rs @@ -304,7 +304,7 @@ fn parse_iter_usage<'tcx>( }; }, (sym::nth | sym::skip, [idx_expr]) if cx.tcx.trait_of_assoc(did) == Some(iter_id) => { - if let Some(Constant::Int(idx)) = ConstEvalCtxt::new(cx).eval(idx_expr) { + if let Some(Constant::Int(idx)) = ConstEvalCtxt::new(cx).eval_local(idx_expr, ctxt) { let span = if name.ident.as_str() == "nth" { e.span } else if let Some((_, Node::Expr(next_expr))) = iter.next() diff --git a/clippy_lints/src/minmax.rs b/clippy_lints/src/minmax.rs index 35b6af0c20f7..f9a7c562c7a5 100644 --- a/clippy_lints/src/minmax.rs +++ b/clippy_lints/src/minmax.rs @@ -61,7 +61,7 @@ enum MinMax { Max, } -fn min_max<'a, 'tcx>(cx: &LateContext<'tcx>, expr: &'a Expr<'a>) -> Option<(MinMax, Constant, &'a Expr<'a>)> { +fn min_max<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(MinMax, Constant, &'a Expr<'a>)> { match expr.kind { ExprKind::Call(path, args) => { if let ExprKind::Path(ref qpath) = path.kind { @@ -92,8 +92,8 @@ fn min_max<'a, 'tcx>(cx: &LateContext<'tcx>, expr: &'a Expr<'a>) -> Option<(MinM } } -fn fetch_const<'a, 'tcx>( - cx: &LateContext<'tcx>, +fn fetch_const<'a>( + cx: &LateContext<'_>, ctxt: SyntaxContext, receiver: Option<&'a Expr<'a>>, args: &'a [Expr<'a>], diff --git a/clippy_lints/src/operators/duration_subsec.rs b/clippy_lints/src/operators/duration_subsec.rs index 6c9be7c5e90b..d897b0e8dd91 100644 --- a/clippy_lints/src/operators/duration_subsec.rs +++ b/clippy_lints/src/operators/duration_subsec.rs @@ -19,7 +19,7 @@ pub(crate) fn check<'tcx>( if op == BinOpKind::Div && let ExprKind::MethodCall(method_path, self_arg, [], _) = left.kind && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_arg).peel_refs(), sym::Duration) - && let Some(Constant::Int(divisor)) = ConstEvalCtxt::new(cx).eval(right) + && let Some(Constant::Int(divisor)) = ConstEvalCtxt::new(cx).eval_local(right, expr.span.ctxt()) { let suggested_fn = match (method_path.ident.name, divisor) { (sym::subsec_micros, 1_000) | (sym::subsec_nanos, 1_000_000) => "subsec_millis", diff --git a/clippy_lints/src/operators/modulo_arithmetic.rs b/clippy_lints/src/operators/modulo_arithmetic.rs index b79461663d7b..ffe91fc2cef6 100644 --- a/clippy_lints/src/operators/modulo_arithmetic.rs +++ b/clippy_lints/src/operators/modulo_arithmetic.rs @@ -39,7 +39,9 @@ fn used_in_comparison_with_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { && let BinOpKind::Eq | BinOpKind::Ne = op.node { let ecx = ConstEvalCtxt::new(cx); - matches!(ecx.eval(lhs), Some(Constant::Int(0))) || matches!(ecx.eval(rhs), Some(Constant::Int(0))) + let ctxt = expr.span.ctxt(); + matches!(ecx.eval_local(lhs, ctxt), Some(Constant::Int(0))) + || matches!(ecx.eval_local(rhs, ctxt), Some(Constant::Int(0))) } else { false } @@ -55,7 +57,7 @@ fn analyze_operand(operand: &Expr<'_>, cx: &LateContext<'_>, expr: &Expr<'_>) -> match ConstEvalCtxt::new(cx).eval(operand)? { Constant::Int(v) => match *cx.typeck_results().expr_ty(expr).kind() { ty::Int(ity) => { - let value = sext(cx.tcx, v, ity); + let value: i128 = sext(cx.tcx, v, ity); Some(OperandInfo { string_representation: Some(value.to_string()), is_negative: value < 0, diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 8a216afb82c9..0b2313cb7eeb 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -312,7 +312,7 @@ struct RangeBounds<'a> { // Takes a binary expression such as x <= 2 as input // Breaks apart into various pieces, such as the value of the number, // hir id of the variable, and direction/inclusiveness of the operator -fn check_range_bounds<'a, 'tcx>(cx: &'a LateContext<'tcx>, ex: &'a Expr<'_>) -> Option> { +fn check_range_bounds<'a>(cx: &'a LateContext<'_>, ex: &'a Expr<'_>) -> Option> { if let ExprKind::Binary(ref op, l, r) = ex.kind { let (inclusive, ordering) = match op.node { BinOpKind::Gt => (false, Ordering::Greater), diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index f5f378616460..9ba796137cc3 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -128,15 +128,11 @@ impl PartialEq for Constant { (&Self::Char(l), &Self::Char(r)) => l == r, (&Self::Int(l), &Self::Int(r)) => l == r, (&Self::F64(l), &Self::F64(r)) => { - // We want `Fw32 == FwAny` and `FwAny == Fw64`, and by transitivity we must have - // `Fw32 == Fw64`, so don’t compare them. - // `to_bits` is required to catch non-matching 0.0, -0.0, and NaNs. + // `to_bits` is required to catch non-matching `0.0` and `-0.0`. l.to_bits() == r.to_bits() && !l.is_nan() }, (&Self::F32(l), &Self::F32(r)) => { - // We want `Fw32 == FwAny` and `FwAny == Fw64`, and by transitivity we must have - // `Fw32 == Fw64`, so don’t compare them. - // `to_bits` is required to catch non-matching 0.0, -0.0, and NaNs. + // `to_bits` is required to catch non-matching `0.0` and `-0.0`. l.to_bits() == r.to_bits() && !l.is_nan() }, (&Self::Bool(l), &Self::Bool(r)) => l == r, diff --git a/tests/ui/duration_subsec.fixed b/tests/ui/duration_subsec.fixed index a8c2f78ca383..b6b2d156c0e0 100644 --- a/tests/ui/duration_subsec.fixed +++ b/tests/ui/duration_subsec.fixed @@ -25,8 +25,7 @@ fn main() { // Handle constants const NANOS_IN_MICRO: u32 = 1_000; - let _ = dur.subsec_micros(); - //~^ duration_subsec + let _ = dur.subsec_nanos() / NANOS_IN_MICRO; // Other literals aren't linted let _ = dur.subsec_nanos() / 699; diff --git a/tests/ui/duration_subsec.rs b/tests/ui/duration_subsec.rs index 582f4717de27..1061e6003c35 100644 --- a/tests/ui/duration_subsec.rs +++ b/tests/ui/duration_subsec.rs @@ -26,7 +26,6 @@ fn main() { // Handle constants const NANOS_IN_MICRO: u32 = 1_000; let _ = dur.subsec_nanos() / NANOS_IN_MICRO; - //~^ duration_subsec // Other literals aren't linted let _ = dur.subsec_nanos() / 699; diff --git a/tests/ui/duration_subsec.stderr b/tests/ui/duration_subsec.stderr index 1a41742e1fa6..27756bc1c20f 100644 --- a/tests/ui/duration_subsec.stderr +++ b/tests/ui/duration_subsec.stderr @@ -25,11 +25,5 @@ error: calling `subsec_micros()` is more concise than this calculation LL | let _ = (&dur).subsec_nanos() / 1_000; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(&dur).subsec_micros()` -error: calling `subsec_micros()` is more concise than this calculation - --> tests/ui/duration_subsec.rs:28:13 - | -LL | let _ = dur.subsec_nanos() / NANOS_IN_MICRO; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `dur.subsec_micros()` - -error: aborting due to 5 previous errors +error: aborting due to 4 previous errors diff --git a/tests/ui/floating_point_log.fixed b/tests/ui/floating_point_log.fixed index 275c9b4a3ab9..e831e30a71d8 100644 --- a/tests/ui/floating_point_log.fixed +++ b/tests/ui/floating_point_log.fixed @@ -14,10 +14,8 @@ fn check_log_base() { //~^ suboptimal_flops let _ = x.ln(); //~^ suboptimal_flops - let _ = x.log2(); - //~^ suboptimal_flops - let _ = x.ln(); - //~^ suboptimal_flops + let _ = x.log(TWO); + let _ = x.log(E); let _ = (x as f32).log2(); //~^ suboptimal_flops diff --git a/tests/ui/floating_point_log.rs b/tests/ui/floating_point_log.rs index a372ccbb9fb0..06cb1c8d9603 100644 --- a/tests/ui/floating_point_log.rs +++ b/tests/ui/floating_point_log.rs @@ -15,9 +15,7 @@ fn check_log_base() { let _ = x.log(std::f32::consts::E); //~^ suboptimal_flops let _ = x.log(TWO); - //~^ suboptimal_flops let _ = x.log(E); - //~^ suboptimal_flops let _ = (x as f32).log(2f32); //~^ suboptimal_flops diff --git a/tests/ui/floating_point_log.stderr b/tests/ui/floating_point_log.stderr index e93b3af851cb..3e141de626d9 100644 --- a/tests/ui/floating_point_log.stderr +++ b/tests/ui/floating_point_log.stderr @@ -19,44 +19,32 @@ error: logarithm for bases 2, 10 and e can be computed more accurately LL | let _ = x.log(std::f32::consts::E); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.ln()` -error: logarithm for bases 2, 10 and e can be computed more accurately - --> tests/ui/floating_point_log.rs:17:13 - | -LL | let _ = x.log(TWO); - | ^^^^^^^^^^ help: consider using: `x.log2()` - error: logarithm for bases 2, 10 and e can be computed more accurately --> tests/ui/floating_point_log.rs:19:13 | -LL | let _ = x.log(E); - | ^^^^^^^^ help: consider using: `x.ln()` - -error: logarithm for bases 2, 10 and e can be computed more accurately - --> tests/ui/floating_point_log.rs:21:13 - | LL | let _ = (x as f32).log(2f32); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x as f32).log2()` error: logarithm for bases 2, 10 and e can be computed more accurately - --> tests/ui/floating_point_log.rs:25:13 + --> tests/ui/floating_point_log.rs:23:13 | LL | let _ = x.log(2f64); | ^^^^^^^^^^^ help: consider using: `x.log2()` error: logarithm for bases 2, 10 and e can be computed more accurately - --> tests/ui/floating_point_log.rs:27:13 + --> tests/ui/floating_point_log.rs:25:13 | LL | let _ = x.log(10f64); | ^^^^^^^^^^^^ help: consider using: `x.log10()` error: logarithm for bases 2, 10 and e can be computed more accurately - --> tests/ui/floating_point_log.rs:29:13 + --> tests/ui/floating_point_log.rs:27:13 | LL | let _ = x.log(std::f64::consts::E); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.ln()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:35:13 + --> tests/ui/floating_point_log.rs:33:13 | LL | let _ = (1f32 + 2.).ln(); | ^^^^^^^^^^^^^^^^ help: consider using: `2.0f32.ln_1p()` @@ -65,118 +53,118 @@ LL | let _ = (1f32 + 2.).ln(); = help: to override `-D warnings` add `#[allow(clippy::imprecise_flops)]` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:37:13 + --> tests/ui/floating_point_log.rs:35:13 | LL | let _ = (1f32 + 2.0).ln(); | ^^^^^^^^^^^^^^^^^ help: consider using: `2.0f32.ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:39:13 + --> tests/ui/floating_point_log.rs:37:13 | LL | let _ = (1.0 + x).ln(); | ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:41:13 + --> tests/ui/floating_point_log.rs:39:13 | LL | let _ = (1.0 + x / 2.0).ln(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:43:13 + --> tests/ui/floating_point_log.rs:41:13 | LL | let _ = (1.0 + x.powi(3)).ln(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:45:13 + --> tests/ui/floating_point_log.rs:43:13 | LL | let _ = (1.0 + x.powi(3) / 2.0).ln(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x.powi(3) / 2.0).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:47:13 + --> tests/ui/floating_point_log.rs:45:13 | LL | let _ = (1.0 + (std::f32::consts::E - 1.0)).ln(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(std::f32::consts::E - 1.0).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:49:13 + --> tests/ui/floating_point_log.rs:47:13 | LL | let _ = (x + 1.0).ln(); | ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:51:13 + --> tests/ui/floating_point_log.rs:49:13 | LL | let _ = (x.powi(3) + 1.0).ln(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:53:13 + --> tests/ui/floating_point_log.rs:51:13 | LL | let _ = (x + 2.0 + 1.0).ln(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x + 2.0).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:55:13 + --> tests/ui/floating_point_log.rs:53:13 | LL | let _ = (x / 2.0 + 1.0).ln(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:64:13 + --> tests/ui/floating_point_log.rs:62:13 | LL | let _ = (1f64 + 2.).ln(); | ^^^^^^^^^^^^^^^^ help: consider using: `2.0f64.ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:66:13 + --> tests/ui/floating_point_log.rs:64:13 | LL | let _ = (1f64 + 2.0).ln(); | ^^^^^^^^^^^^^^^^^ help: consider using: `2.0f64.ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:68:13 + --> tests/ui/floating_point_log.rs:66:13 | LL | let _ = (1.0 + x).ln(); | ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:70:13 + --> tests/ui/floating_point_log.rs:68:13 | LL | let _ = (1.0 + x / 2.0).ln(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:72:13 + --> tests/ui/floating_point_log.rs:70:13 | LL | let _ = (1.0 + x.powi(3)).ln(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:74:13 + --> tests/ui/floating_point_log.rs:72:13 | LL | let _ = (x + 1.0).ln(); | ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:76:13 + --> tests/ui/floating_point_log.rs:74:13 | LL | let _ = (x.powi(3) + 1.0).ln(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:78:13 + --> tests/ui/floating_point_log.rs:76:13 | LL | let _ = (x + 2.0 + 1.0).ln(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x + 2.0).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:80:13 + --> tests/ui/floating_point_log.rs:78:13 | LL | let _ = (x / 2.0 + 1.0).ln(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()` -error: aborting due to 29 previous errors +error: aborting due to 27 previous errors diff --git a/tests/ui/ip_constant.fixed b/tests/ui/ip_constant.fixed index c94796821394..afdf581bacf7 100644 --- a/tests/ui/ip_constant.fixed +++ b/tests/ui/ip_constant.fixed @@ -72,33 +72,44 @@ const CONST_U16_1: u16 = 1; fn const_test1() { use std::net::Ipv4Addr; - let _ = Ipv4Addr::LOCALHOST; - //~^ ip_constant - let _ = Ipv4Addr::BROADCAST; - //~^ ip_constant - let _ = Ipv4Addr::UNSPECIFIED; - //~^ ip_constant + let _ = Ipv4Addr::new(CONST_U8_127, CONST_U8_0, CONST_U8_0, CONST_U8_1); + let _ = Ipv4Addr::new(CONST_U8_255, CONST_U8_255, CONST_U8_255, CONST_U8_255); + let _ = Ipv4Addr::new(CONST_U8_0, CONST_U8_0, CONST_U8_0, CONST_U8_0); use std::net::Ipv6Addr; - let _ = Ipv6Addr::LOCALHOST; + let _ = Ipv6Addr::new( + CONST_U16_0, + CONST_U16_0, + CONST_U16_0, + CONST_U16_0, + CONST_U16_0, + CONST_U16_0, + CONST_U16_0, + CONST_U16_1, + ); - let _ = Ipv6Addr::UNSPECIFIED; + let _ = Ipv6Addr::new( + CONST_U16_0, + CONST_U16_0, + CONST_U16_0, + CONST_U16_0, + CONST_U16_0, + CONST_U16_0, + CONST_U16_0, + CONST_U16_0, + ); } fn const_test2() { use std::net::Ipv4Addr; let _ = Ipv4Addr::LOCALHOST; //~^ ip_constant - let _ = Ipv4Addr::BROADCAST; - //~^ ip_constant - let _ = Ipv4Addr::UNSPECIFIED; - //~^ ip_constant + let _ = Ipv4Addr::new(254 + CONST_U8_1, 255, { 255 - CONST_U8_0 }, CONST_U8_255); + let _ = Ipv4Addr::new(0, CONST_U8_255 - 255, 0, { 1 + 0 - 1 }); use std::net::Ipv6Addr; - let _ = Ipv6Addr::LOCALHOST; - //~^ ip_constant - let _ = Ipv6Addr::LOCALHOST; - //~^ ip_constant + let _ = Ipv6Addr::new(0 + CONST_U16_0, 0, 0, 0, 0, 0, 0, 1); + let _ = Ipv6Addr::new(0 + 0, 0, 0, 0, 0, { 2 - 1 - CONST_U16_1 }, 0, 1); } macro_rules! ipv4_new { diff --git a/tests/ui/ip_constant.rs b/tests/ui/ip_constant.rs index 69a5c3b4e923..04fc2f0f6fda 100644 --- a/tests/ui/ip_constant.rs +++ b/tests/ui/ip_constant.rs @@ -73,15 +73,11 @@ const CONST_U16_1: u16 = 1; fn const_test1() { use std::net::Ipv4Addr; let _ = Ipv4Addr::new(CONST_U8_127, CONST_U8_0, CONST_U8_0, CONST_U8_1); - //~^ ip_constant let _ = Ipv4Addr::new(CONST_U8_255, CONST_U8_255, CONST_U8_255, CONST_U8_255); - //~^ ip_constant let _ = Ipv4Addr::new(CONST_U8_0, CONST_U8_0, CONST_U8_0, CONST_U8_0); - //~^ ip_constant use std::net::Ipv6Addr; let _ = Ipv6Addr::new( - //~^ ip_constant CONST_U16_0, CONST_U16_0, CONST_U16_0, @@ -93,7 +89,6 @@ fn const_test1() { ); let _ = Ipv6Addr::new( - //~^ ip_constant CONST_U16_0, CONST_U16_0, CONST_U16_0, @@ -110,15 +105,11 @@ fn const_test2() { let _ = Ipv4Addr::new(126 + 1, 0, 0, 1); //~^ ip_constant let _ = Ipv4Addr::new(254 + CONST_U8_1, 255, { 255 - CONST_U8_0 }, CONST_U8_255); - //~^ ip_constant let _ = Ipv4Addr::new(0, CONST_U8_255 - 255, 0, { 1 + 0 - 1 }); - //~^ ip_constant use std::net::Ipv6Addr; let _ = Ipv6Addr::new(0 + CONST_U16_0, 0, 0, 0, 0, 0, 0, 1); - //~^ ip_constant let _ = Ipv6Addr::new(0 + 0, 0, 0, 0, 0, { 2 - 1 - CONST_U16_1 }, 0, 1); - //~^ ip_constant } macro_rules! ipv4_new { diff --git a/tests/ui/ip_constant.stderr b/tests/ui/ip_constant.stderr index 07d912b18a57..44e3d6448dbd 100644 --- a/tests/ui/ip_constant.stderr +++ b/tests/ui/ip_constant.stderr @@ -241,101 +241,7 @@ LL + let _ = std::net::Ipv6Addr::UNSPECIFIED; | error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:75:13 - | -LL | let _ = Ipv4Addr::new(CONST_U8_127, CONST_U8_0, CONST_U8_0, CONST_U8_1); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: use - | -LL - let _ = Ipv4Addr::new(CONST_U8_127, CONST_U8_0, CONST_U8_0, CONST_U8_1); -LL + let _ = Ipv4Addr::LOCALHOST; - | - -error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:77:13 - | -LL | let _ = Ipv4Addr::new(CONST_U8_255, CONST_U8_255, CONST_U8_255, CONST_U8_255); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: use - | -LL - let _ = Ipv4Addr::new(CONST_U8_255, CONST_U8_255, CONST_U8_255, CONST_U8_255); -LL + let _ = Ipv4Addr::BROADCAST; - | - -error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:79:13 - | -LL | let _ = Ipv4Addr::new(CONST_U8_0, CONST_U8_0, CONST_U8_0, CONST_U8_0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: use - | -LL - let _ = Ipv4Addr::new(CONST_U8_0, CONST_U8_0, CONST_U8_0, CONST_U8_0); -LL + let _ = Ipv4Addr::UNSPECIFIED; - | - -error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:83:13 - | -LL | let _ = Ipv6Addr::new( - | _____________^ -LL | | -LL | | CONST_U16_0, -LL | | CONST_U16_0, -... | -LL | | CONST_U16_1, -LL | | ); - | |_____^ - | -help: use - | -LL - let _ = Ipv6Addr::new( -LL - -LL - CONST_U16_0, -LL - CONST_U16_0, -LL - CONST_U16_0, -LL - CONST_U16_0, -LL - CONST_U16_0, -LL - CONST_U16_0, -LL - CONST_U16_0, -LL - CONST_U16_1, -LL - ); -LL + let _ = Ipv6Addr::LOCALHOST; - | - -error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:95:13 - | -LL | let _ = Ipv6Addr::new( - | _____________^ -LL | | -LL | | CONST_U16_0, -LL | | CONST_U16_0, -... | -LL | | CONST_U16_0, -LL | | ); - | |_____^ - | -help: use - | -LL - let _ = Ipv6Addr::new( -LL - -LL - CONST_U16_0, -LL - CONST_U16_0, -LL - CONST_U16_0, -LL - CONST_U16_0, -LL - CONST_U16_0, -LL - CONST_U16_0, -LL - CONST_U16_0, -LL - CONST_U16_0, -LL - ); -LL + let _ = Ipv6Addr::UNSPECIFIED; - | - -error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:110:13 + --> tests/ui/ip_constant.rs:105:13 | LL | let _ = Ipv4Addr::new(126 + 1, 0, 0, 1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -346,53 +252,5 @@ LL - let _ = Ipv4Addr::new(126 + 1, 0, 0, 1); LL + let _ = Ipv4Addr::LOCALHOST; | -error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:112:13 - | -LL | let _ = Ipv4Addr::new(254 + CONST_U8_1, 255, { 255 - CONST_U8_0 }, CONST_U8_255); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: use - | -LL - let _ = Ipv4Addr::new(254 + CONST_U8_1, 255, { 255 - CONST_U8_0 }, CONST_U8_255); -LL + let _ = Ipv4Addr::BROADCAST; - | - -error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:114:13 - | -LL | let _ = Ipv4Addr::new(0, CONST_U8_255 - 255, 0, { 1 + 0 - 1 }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: use - | -LL - let _ = Ipv4Addr::new(0, CONST_U8_255 - 255, 0, { 1 + 0 - 1 }); -LL + let _ = Ipv4Addr::UNSPECIFIED; - | - -error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:118:13 - | -LL | let _ = Ipv6Addr::new(0 + CONST_U16_0, 0, 0, 0, 0, 0, 0, 1); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: use - | -LL - let _ = Ipv6Addr::new(0 + CONST_U16_0, 0, 0, 0, 0, 0, 0, 1); -LL + let _ = Ipv6Addr::LOCALHOST; - | - -error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:120:13 - | -LL | let _ = Ipv6Addr::new(0 + 0, 0, 0, 0, 0, { 2 - 1 - CONST_U16_1 }, 0, 1); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: use - | -LL - let _ = Ipv6Addr::new(0 + 0, 0, 0, 0, 0, { 2 - 1 - CONST_U16_1 }, 0, 1); -LL + let _ = Ipv6Addr::LOCALHOST; - | - -error: aborting due to 30 previous errors +error: aborting due to 21 previous errors diff --git a/tests/ui/manual_float_methods.rs b/tests/ui/manual_float_methods.rs index 62cdc1c141d0..4b496a493283 100644 --- a/tests/ui/manual_float_methods.rs +++ b/tests/ui/manual_float_methods.rs @@ -8,9 +8,6 @@ #[macro_use] extern crate proc_macros; -const INFINITE: f32 = f32::INFINITY; -const NEG_INFINITE: f32 = f32::NEG_INFINITY; - fn fn_test() -> f64 { f64::NEG_INFINITY } @@ -25,10 +22,6 @@ fn main() { //~^ manual_is_infinite if x != f32::INFINITY && x != f32::NEG_INFINITY {} //~^ manual_is_finite - if x == INFINITE || x == NEG_INFINITE {} - //~^ manual_is_infinite - if x != INFINITE && x != NEG_INFINITE {} - //~^ manual_is_finite let x = 1.0f64; if x == f64::INFINITY || x == f64::NEG_INFINITY {} //~^ manual_is_infinite @@ -64,4 +57,12 @@ fn main() { if x == f32::INFINITY || x == f32::NEG_INFINITY {} if x != f32::INFINITY && x != f32::NEG_INFINITY {} } + + { + let x = 1.0f32; + const X: f32 = f32::INFINITY; + const Y: f32 = f32::NEG_INFINITY; + if x == X || x == Y {} + if x != X && x != Y {} + } } diff --git a/tests/ui/manual_float_methods.stderr b/tests/ui/manual_float_methods.stderr index 352c879c87d7..0a27e0eac48b 100644 --- a/tests/ui/manual_float_methods.stderr +++ b/tests/ui/manual_float_methods.stderr @@ -1,5 +1,5 @@ error: manually checking if a float is infinite - --> tests/ui/manual_float_methods.rs:24:8 + --> tests/ui/manual_float_methods.rs:21:8 | LL | if x == f32::INFINITY || x == f32::NEG_INFINITY {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the dedicated method instead: `x.is_infinite()` @@ -8,7 +8,7 @@ LL | if x == f32::INFINITY || x == f32::NEG_INFINITY {} = help: to override `-D warnings` add `#[allow(clippy::manual_is_infinite)]` error: manually checking if a float is finite - --> tests/ui/manual_float_methods.rs:26:8 + --> tests/ui/manual_float_methods.rs:23:8 | LL | if x != f32::INFINITY && x != f32::NEG_INFINITY {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -32,41 +32,13 @@ LL + if !x.is_infinite() {} | error: manually checking if a float is infinite - --> tests/ui/manual_float_methods.rs:28:8 - | -LL | if x == INFINITE || x == NEG_INFINITE {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the dedicated method instead: `x.is_infinite()` - -error: manually checking if a float is finite - --> tests/ui/manual_float_methods.rs:30:8 - | -LL | if x != INFINITE && x != NEG_INFINITE {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: use the dedicated method instead - | -LL - if x != INFINITE && x != NEG_INFINITE {} -LL + if x.is_finite() {} - | -help: this will alter how it handles NaN; if that is a problem, use instead - | -LL - if x != INFINITE && x != NEG_INFINITE {} -LL + if x.is_finite() || x.is_nan() {} - | -help: or, for conciseness - | -LL - if x != INFINITE && x != NEG_INFINITE {} -LL + if !x.is_infinite() {} - | - -error: manually checking if a float is infinite - --> tests/ui/manual_float_methods.rs:33:8 + --> tests/ui/manual_float_methods.rs:26:8 | LL | if x == f64::INFINITY || x == f64::NEG_INFINITY {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the dedicated method instead: `x.is_infinite()` error: manually checking if a float is finite - --> tests/ui/manual_float_methods.rs:35:8 + --> tests/ui/manual_float_methods.rs:28:8 | LL | if x != f64::INFINITY && x != f64::NEG_INFINITY {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -88,10 +60,10 @@ LL + if !x.is_infinite() {} | error: manually checking if a float is infinite - --> tests/ui/manual_float_methods.rs:50:12 + --> tests/ui/manual_float_methods.rs:43:12 | LL | if x == f64::INFINITY || x == f64::NEG_INFINITY {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the dedicated method instead: `x.is_infinite()` -error: aborting due to 7 previous errors +error: aborting due to 5 previous errors diff --git a/tests/ui/manual_strip.stderr b/tests/ui/manual_strip.stderr index a323ef700e76..d147cdae1f3b 100644 --- a/tests/ui/manual_strip.stderr +++ b/tests/ui/manual_strip.stderr @@ -97,9 +97,6 @@ LL | if s.starts_with(PREFIX) { help: try using the `strip_prefix` method | LL ~ if let Some() = s.strip_prefix(PREFIX) { -LL ~ str::to_string(); -LL | -LL | LL ~ str::to_string(); | diff --git a/tests/ui/repeat_once.fixed b/tests/ui/repeat_once.fixed index e739e176f0ac..c08d630a32f7 100644 --- a/tests/ui/repeat_once.fixed +++ b/tests/ui/repeat_once.fixed @@ -10,8 +10,7 @@ fn main() { //~^ repeat_once let b = slice.to_vec(); //~^ repeat_once - let c = "hello".to_string(); - //~^ repeat_once + let c = "hello".repeat(N); let d = "hi".to_string(); //~^ repeat_once let e = s.to_string(); diff --git a/tests/ui/repeat_once.rs b/tests/ui/repeat_once.rs index 89ab94bbaee8..d967fdc466ed 100644 --- a/tests/ui/repeat_once.rs +++ b/tests/ui/repeat_once.rs @@ -11,7 +11,6 @@ fn main() { let b = slice.repeat(1); //~^ repeat_once let c = "hello".repeat(N); - //~^ repeat_once let d = "hi".repeat(1); //~^ repeat_once let e = s.repeat(1); diff --git a/tests/ui/repeat_once.stderr b/tests/ui/repeat_once.stderr index 3db7a3568f8e..62dbf7d23375 100644 --- a/tests/ui/repeat_once.stderr +++ b/tests/ui/repeat_once.stderr @@ -14,28 +14,22 @@ LL | let b = slice.repeat(1); | ^^^^^^^^^^^^^^^ help: consider using `.to_vec()` instead: `slice.to_vec()` error: calling `repeat(1)` on str - --> tests/ui/repeat_once.rs:13:13 - | -LL | let c = "hello".repeat(N); - | ^^^^^^^^^^^^^^^^^ help: consider using `.to_string()` instead: `"hello".to_string()` - -error: calling `repeat(1)` on str - --> tests/ui/repeat_once.rs:15:13 + --> tests/ui/repeat_once.rs:14:13 | LL | let d = "hi".repeat(1); | ^^^^^^^^^^^^^^ help: consider using `.to_string()` instead: `"hi".to_string()` error: calling `repeat(1)` on str - --> tests/ui/repeat_once.rs:17:13 + --> tests/ui/repeat_once.rs:16:13 | LL | let e = s.repeat(1); | ^^^^^^^^^^^ help: consider using `.to_string()` instead: `s.to_string()` error: calling `repeat(1)` on a string literal - --> tests/ui/repeat_once.rs:19:13 + --> tests/ui/repeat_once.rs:18:13 | LL | let f = string.repeat(1); | ^^^^^^^^^^^^^^^^ help: consider using `.clone()` instead: `string.clone()` -error: aborting due to 6 previous errors +error: aborting due to 5 previous errors From 11802781f7afc504f926c09a324ac5065a1746dc Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Wed, 2 Jul 2025 12:33:37 +0000 Subject: [PATCH 160/226] Make Value Copy by arena-allocating vectors. --- compiler/rustc_mir_transform/src/gvn.rs | 129 ++++++++++++------------ 1 file changed, 66 insertions(+), 63 deletions(-) diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 3ff8dc6dbb37..da36c3e6926b 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -91,6 +91,7 @@ use either::Either; use hashbrown::hash_table::{Entry, HashTable}; use itertools::Itertools as _; use rustc_abi::{self as abi, BackendRepr, FIRST_VARIANT, FieldIdx, Primitive, Size, VariantIdx}; +use rustc_arena::DroplessArena; use rustc_const_eval::const_eval::DummyMachine; use rustc_const_eval::interpret::{ ImmTy, Immediate, InterpCx, MemPlaceMeta, MemoryKind, OpTy, Projectable, Scalar, @@ -129,7 +130,9 @@ impl<'tcx> crate::MirPass<'tcx> for GVN { // Clone dominators because we need them while mutating the body. let dominators = body.basic_blocks.dominators().clone(); - let mut state = VnState::new(tcx, body, typing_env, &ssa, dominators, &body.local_decls); + let arena = DroplessArena::default(); + let mut state = + VnState::new(tcx, body, typing_env, &ssa, dominators, &body.local_decls, &arena); for local in body.args_iter().filter(|&local| ssa.is_ssa(local)) { let opaque = state.new_opaque(body.local_decls[local].ty); @@ -183,8 +186,8 @@ enum AddressKind { Address(RawPtrKind), } -#[derive(Debug, PartialEq, Eq, Hash)] -enum Value<'tcx> { +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +enum Value<'a, 'tcx> { // Root values. /// Used to represent values we know nothing about. /// The `usize` is a counter incremented by `new_opaque`. @@ -201,7 +204,7 @@ enum Value<'tcx> { // Aggregates. /// An aggregate value, either tuple/closure/struct/enum. /// This does not contain unions, as we cannot reason with the value. - Aggregate(VariantIdx, Vec), + Aggregate(VariantIdx, &'a [VnIndex]), /// A raw pointer aggregate built from a thin pointer and metadata. RawPtr { /// Thin pointer component. This is field 0 in MIR. @@ -240,15 +243,15 @@ enum Value<'tcx> { /// This data structure is mostly a partial reimplementation of `FxIndexMap`. /// We do not use a regular `FxIndexMap` to skip hashing values that are unique by construction, /// like opaque values, address with provenance and non-deterministic constants. -struct ValueSet<'tcx> { +struct ValueSet<'a, 'tcx> { indices: HashTable, hashes: IndexVec, - values: IndexVec>, + values: IndexVec>, types: IndexVec>, } -impl<'tcx> ValueSet<'tcx> { - fn new(num_values: usize) -> ValueSet<'tcx> { +impl<'a, 'tcx> ValueSet<'a, 'tcx> { + fn new(num_values: usize) -> ValueSet<'a, 'tcx> { ValueSet { indices: HashTable::with_capacity(num_values), hashes: IndexVec::with_capacity(num_values), @@ -263,7 +266,7 @@ impl<'tcx> ValueSet<'tcx> { fn insert_unique( &mut self, ty: Ty<'tcx>, - value: impl FnOnce(VnOpaque) -> Value<'tcx>, + value: impl FnOnce(VnOpaque) -> Value<'a, 'tcx>, ) -> VnIndex { let value = value(VnOpaque); @@ -284,7 +287,7 @@ impl<'tcx> ValueSet<'tcx> { /// Insert a `(Value, Ty)` pair to be deduplicated. /// Returns `true` as second tuple field if this value did not exist previously. #[allow(rustc::pass_by_value)] // closures take `&VnIndex` - fn insert(&mut self, ty: Ty<'tcx>, value: Value<'tcx>) -> (VnIndex, bool) { + fn insert(&mut self, ty: Ty<'tcx>, value: Value<'a, 'tcx>) -> (VnIndex, bool) { debug_assert!(match value { Value::Opaque(_) | Value::Address { .. } => false, Value::Constant { disambiguator, .. } => disambiguator.is_none(), @@ -319,8 +322,8 @@ impl<'tcx> ValueSet<'tcx> { /// Return the `Value` associated with the given `VnIndex`. #[inline] - fn value(&self, index: VnIndex) -> &Value<'tcx> { - &self.values[index] + fn value(&self, index: VnIndex) -> Value<'a, 'tcx> { + self.values[index] } /// Return the type associated with the given `VnIndex`. @@ -336,7 +339,7 @@ impl<'tcx> ValueSet<'tcx> { } } -struct VnState<'body, 'tcx> { +struct VnState<'body, 'a, 'tcx> { tcx: TyCtxt<'tcx>, ecx: InterpCx<'tcx, DummyMachine>, local_decls: &'body LocalDecls<'tcx>, @@ -346,7 +349,7 @@ struct VnState<'body, 'tcx> { /// Locals that are assigned that value. // This vector does not hold all the values of `VnIndex` that we create. rev_locals: IndexVec>, - values: ValueSet<'tcx>, + values: ValueSet<'a, 'tcx>, /// Values evaluated as constants if possible. evaluated: IndexVec>>, /// Cache the deref values. @@ -354,9 +357,10 @@ struct VnState<'body, 'tcx> { ssa: &'body SsaLocals, dominators: Dominators, reused_locals: DenseBitSet, + arena: &'a DroplessArena, } -impl<'body, 'tcx> VnState<'body, 'tcx> { +impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { fn new( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, @@ -364,6 +368,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { ssa: &'body SsaLocals, dominators: Dominators, local_decls: &'body LocalDecls<'tcx>, + arena: &'a DroplessArena, ) -> Self { // Compute a rough estimate of the number of values in the body from the number of // statements. This is meant to reduce the number of allocations, but it's all right if @@ -385,6 +390,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { ssa, dominators, reused_locals: DenseBitSet::new_empty(local_decls.len()), + arena, } } @@ -393,7 +399,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } #[instrument(level = "trace", skip(self), ret)] - fn insert(&mut self, ty: Ty<'tcx>, value: Value<'tcx>) -> VnIndex { + fn insert(&mut self, ty: Ty<'tcx>, value: Value<'a, 'tcx>) -> VnIndex { let (index, new) = self.values.insert(ty, value); if new { // Grow `evaluated` and `rev_locals` here to amortize the allocations. @@ -464,7 +470,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } #[inline] - fn get(&self, index: VnIndex) -> &Value<'tcx> { + fn get(&self, index: VnIndex) -> Value<'a, 'tcx> { self.values.value(index) } @@ -495,8 +501,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { self.insert(ty, Value::Constant { value, disambiguator: None }) } - fn insert_tuple(&mut self, ty: Ty<'tcx>, values: Vec) -> VnIndex { - self.insert(ty, Value::Aggregate(VariantIdx::ZERO, values)) + fn insert_tuple(&mut self, ty: Ty<'tcx>, values: &[VnIndex]) -> VnIndex { + self.insert(ty, Value::Aggregate(VariantIdx::ZERO, self.arena.alloc_slice(values))) } fn insert_deref(&mut self, ty: Ty<'tcx>, value: VnIndex) -> VnIndex { @@ -521,7 +527,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } else { return None; }; - let op = match *self.get(value) { + let op = match self.get(value) { _ if ty.is_zst() => ImmTy::uninit(ty).into(), Opaque(_) => return None, @@ -736,7 +742,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { if let Value::Aggregate(_, fields) = self.get(value) { return Some((projection_ty, fields[f.as_usize()])); } else if let Value::Projection(outer_value, ProjectionElem::Downcast(_, read_variant)) = self.get(value) - && let Value::Aggregate(written_variant, fields) = self.get(*outer_value) + && let Value::Aggregate(written_variant, fields) = self.get(outer_value) // This pass is not aware of control-flow, so we do not know whether the // replacement we are doing is actually reachable. We could be in any arm of // ``` @@ -761,7 +767,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { ProjectionElem::Index(idx) => { if let Value::Repeat(inner, _) = self.get(value) { *from_non_ssa_index |= self.locals[idx].is_none(); - return Some((projection_ty, *inner)); + return Some((projection_ty, inner)); } let idx = self.locals[idx]?; ProjectionElem::Index(idx) @@ -769,7 +775,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { ProjectionElem::ConstantIndex { offset, min_length, from_end } => { match self.get(value) { Value::Repeat(inner, _) => { - return Some((projection_ty, *inner)); + return Some((projection_ty, inner)); } Value::Aggregate(_, operands) => { let offset = if from_end { @@ -858,8 +864,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { let mut place_ty = PlaceTy::from_ty(self.local_decls[place.local].ty); let mut from_non_ssa_index = false; for (index, proj) in place.projection.iter().enumerate() { - if let Value::Projection(pointer, ProjectionElem::Deref) = *self.get(value) - && let Value::Address { place: mut pointee, kind, .. } = *self.get(pointer) + if let Value::Projection(pointer, ProjectionElem::Deref) = self.get(value) + && let Value::Address { place: mut pointee, kind, .. } = self.get(pointer) && let AddressKind::Ref(BorrowKind::Shared) = kind && let Some(v) = self.simplify_place_value(&mut pointee, location) { @@ -882,8 +888,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { (place_ty, value) = self.project(place_ty, value, proj, &mut from_non_ssa_index)?; } - if let Value::Projection(pointer, ProjectionElem::Deref) = *self.get(value) - && let Value::Address { place: mut pointee, kind, .. } = *self.get(pointer) + if let Value::Projection(pointer, ProjectionElem::Deref) = self.get(value) + && let Value::Address { place: mut pointee, kind, .. } = self.get(pointer) && let AddressKind::Ref(BorrowKind::Shared) = kind && let Some(v) = self.simplify_place_value(&mut pointee, location) { @@ -994,7 +1000,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { fn simplify_discriminant(&mut self, place: VnIndex) -> Option { let enum_ty = self.ty(place); if enum_ty.is_enum() - && let Value::Aggregate(variant, _) = *self.get(place) + && let Value::Aggregate(variant, _) = self.get(place) { let discr = self.ecx.discriminant_for_variant(enum_ty, variant).discard_err()?; return Some(self.insert_scalar(discr.layout.ty, discr.to_scalar())); @@ -1026,11 +1032,11 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { fields: &[VnIndex], ) -> Option { let Some(&first_field) = fields.first() else { return None }; - let Value::Projection(copy_from_value, _) = *self.get(first_field) else { return None }; + let Value::Projection(copy_from_value, _) = self.get(first_field) else { return None }; // All fields must correspond one-to-one and come from the same aggregate value. if fields.iter().enumerate().any(|(index, &v)| { - if let Value::Projection(pointer, ProjectionElem::Field(from_index, _)) = *self.get(v) + if let Value::Projection(pointer, ProjectionElem::Field(from_index, _)) = self.get(v) && copy_from_value == pointer && from_index.index() == index { @@ -1042,7 +1048,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } let mut copy_from_local_value = copy_from_value; - if let Value::Projection(pointer, proj) = *self.get(copy_from_value) + if let Value::Projection(pointer, proj) = self.get(copy_from_value) && let ProjectionElem::Downcast(_, read_variant) = proj { if variant_index == read_variant { @@ -1087,13 +1093,10 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } } - let fields: Vec<_> = field_ops - .iter_mut() - .map(|op| { - self.simplify_operand(op, location) - .unwrap_or_else(|| self.new_opaque(op.ty(self.local_decls, self.tcx))) - }) - .collect(); + let fields = self.arena.alloc_from_iter(field_ops.iter_mut().map(|op| { + self.simplify_operand(op, location) + .unwrap_or_else(|| self.new_opaque(op.ty(self.local_decls, self.tcx))) + })); let variant_index = match *kind { AggregateKind::Array(..) | AggregateKind::Tuple => { @@ -1114,12 +1117,12 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { let mut was_updated = false; while let Value::Cast { kind: CastKind::PtrToPtr, value: cast_value } = self.get(pointer) - && let ty::RawPtr(from_pointee_ty, from_mtbl) = self.ty(*cast_value).kind() + && let ty::RawPtr(from_pointee_ty, from_mtbl) = self.ty(cast_value).kind() && let ty::RawPtr(_, output_mtbl) = ty.kind() && from_mtbl == output_mtbl && from_pointee_ty.is_sized(self.tcx, self.typing_env()) { - pointer = *cast_value; + pointer = cast_value; was_updated = true; } @@ -1184,16 +1187,16 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { // To allow things like `*mut (?A, ?T)` <-> `*mut (?B, ?T)`, // it's fine to get a projection as the type. Value::Cast { kind: CastKind::PtrToPtr, value: inner } - if self.pointers_have_same_metadata(self.ty(*inner), arg_ty) => + if self.pointers_have_same_metadata(self.ty(inner), arg_ty) => { - *inner + inner } // We have an unsizing cast, which assigns the length to wide pointer metadata. Value::Cast { kind: CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize, _), value: from, - } if let Some(from) = self.ty(*from).builtin_deref(true) + } if let Some(from) = self.ty(from).builtin_deref(true) && let ty::Array(_, len) = from.kind() && let Some(to) = self.ty(arg_index).builtin_deref(true) && let ty::Slice(..) = to.kind() => @@ -1221,15 +1224,15 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } let value = match (op, self.get(arg_index)) { - (UnOp::Not, Value::UnaryOp(UnOp::Not, inner)) => return Some(*inner), - (UnOp::Neg, Value::UnaryOp(UnOp::Neg, inner)) => return Some(*inner), + (UnOp::Not, Value::UnaryOp(UnOp::Not, inner)) => return Some(inner), + (UnOp::Neg, Value::UnaryOp(UnOp::Neg, inner)) => return Some(inner), (UnOp::Not, Value::BinaryOp(BinOp::Eq, lhs, rhs)) => { - Value::BinaryOp(BinOp::Ne, *lhs, *rhs) + Value::BinaryOp(BinOp::Ne, lhs, rhs) } (UnOp::Not, Value::BinaryOp(BinOp::Ne, lhs, rhs)) => { - Value::BinaryOp(BinOp::Eq, *lhs, *rhs) + Value::BinaryOp(BinOp::Eq, lhs, rhs) } - (UnOp::PtrMetadata, Value::RawPtr { metadata, .. }) => return Some(*metadata), + (UnOp::PtrMetadata, Value::RawPtr { metadata, .. }) => return Some(metadata), // We have an unsizing cast, which assigns the length to wide pointer metadata. ( UnOp::PtrMetadata, @@ -1238,7 +1241,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { value: inner, }, ) if let ty::Slice(..) = arg_ty.builtin_deref(true).unwrap().kind() - && let ty::Array(_, len) = self.ty(*inner).builtin_deref(true).unwrap().kind() => + && let ty::Array(_, len) = self.ty(inner).builtin_deref(true).unwrap().kind() => { return Some(self.insert_constant(Const::Ty(self.tcx.types.usize, *len))); } @@ -1271,12 +1274,12 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { && lhs_ty.is_any_ptr() && let Value::Cast { kind: CastKind::PtrToPtr, value: lhs_value } = self.get(lhs) && let Value::Cast { kind: CastKind::PtrToPtr, value: rhs_value } = self.get(rhs) - && let lhs_from = self.ty(*lhs_value) - && lhs_from == self.ty(*rhs_value) + && let lhs_from = self.ty(lhs_value) + && lhs_from == self.ty(rhs_value) && self.pointers_have_same_metadata(lhs_from, lhs_ty) { - lhs = *lhs_value; - rhs = *rhs_value; + lhs = lhs_value; + rhs = rhs_value; if let Some(lhs_op) = self.try_as_operand(lhs, location) && let Some(rhs_op) = self.try_as_operand(rhs, location) { @@ -1410,7 +1413,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { if op.is_overflowing() { let ty = Ty::new_tup(self.tcx, &[self.ty(result), self.tcx.types.bool]); let false_val = self.insert_bool(false); - Some(self.insert_tuple(ty, vec![result, false_val])) + Some(self.insert_tuple(ty, &[result, false_val])) } else { Some(result) } @@ -1463,11 +1466,11 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { && let ty::RawPtr(to_pointee, _) = to.kind() && to_pointee.is_sized(self.tcx, self.typing_env()) { - from = self.ty(*pointer); - value = *pointer; + from = self.ty(pointer); + value = pointer; was_updated_this_iteration = true; if from == to { - return Some(*pointer); + return Some(pointer); } } @@ -1476,7 +1479,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { if let Transmute = kind && let Value::Aggregate(variant_idx, field_values) = self.get(value) && let Some((field_idx, field_ty)) = - self.value_is_all_in_one_field(from, *variant_idx) + self.value_is_all_in_one_field(from, variant_idx) { from = field_ty; value = field_values[field_idx.as_usize()]; @@ -1487,7 +1490,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } // Various cast-then-cast cases can be simplified. - if let Value::Cast { kind: inner_kind, value: inner_value } = *self.get(value) { + if let Value::Cast { kind: inner_kind, value: inner_value } = self.get(value) { let inner_from = self.ty(inner_value); let new_kind = match (inner_kind, kind) { // Even if there's a narrowing cast in here that's fine, because @@ -1686,7 +1689,7 @@ fn op_to_prop_const<'tcx>( None } -impl<'tcx> VnState<'_, 'tcx> { +impl<'tcx> VnState<'_, '_, 'tcx> { /// If either [`Self::try_as_constant`] as [`Self::try_as_place`] succeeds, /// returns that result as an [`Operand`]. fn try_as_operand(&mut self, index: VnIndex, location: Location) -> Option> { @@ -1705,7 +1708,7 @@ impl<'tcx> VnState<'_, 'tcx> { // This was already constant in MIR, do not change it. If the constant is not // deterministic, adding an additional mention of it in MIR will not give the same value as // the former mention. - if let Value::Constant { value, disambiguator: None } = *self.get(index) { + if let Value::Constant { value, disambiguator: None } = self.get(index) { debug_assert!(value.is_deterministic()); return Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_: value }); } @@ -1749,7 +1752,7 @@ impl<'tcx> VnState<'_, 'tcx> { // If we are here, we failed to find a local, and we already have a `Deref`. // Trying to add projections will only result in an ill-formed place. return None; - } else if let Value::Projection(pointer, proj) = *self.get(index) + } else if let Value::Projection(pointer, proj) = self.get(index) && (allow_complex_projection || proj.is_stable_offset()) && let Some(proj) = self.try_as_place_elem(self.ty(index), proj, loc) { @@ -1772,7 +1775,7 @@ impl<'tcx> VnState<'_, 'tcx> { } } -impl<'tcx> MutVisitor<'tcx> for VnState<'_, 'tcx> { +impl<'tcx> MutVisitor<'tcx> for VnState<'_, '_, 'tcx> { fn tcx(&self) -> TyCtxt<'tcx> { self.tcx } From 3d96e54656927e4870b7b4aaa0afa099e1fc012a Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Tue, 1 Jul 2025 10:01:12 +0000 Subject: [PATCH 161/226] Use a VnIndex in Address projection. --- compiler/rustc_mir_transform/src/gvn.rs | 217 +++++++++++------- ....dereference_indexing.GVN.panic-abort.diff | 3 +- ...dereference_indexing.GVN.panic-unwind.diff | 3 +- tests/mir-opt/gvn.rs | 4 +- tests/mir-opt/gvn_repeat.index_place.GVN.diff | 15 ++ .../mir-opt/gvn_repeat.repeat_local.GVN.diff | 3 +- tests/mir-opt/gvn_repeat.rs | 21 +- 7 files changed, 179 insertions(+), 87 deletions(-) create mode 100644 tests/mir-opt/gvn_repeat.index_place.GVN.diff diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index da36c3e6926b..03a40f83732a 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -186,6 +186,14 @@ enum AddressKind { Address(RawPtrKind), } +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +enum AddressBase { + /// This address is based on this local. + Local(Local), + /// This address is based on the deref of this pointer. + Deref(VnIndex), +} + #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] enum Value<'a, 'tcx> { // Root values. @@ -216,7 +224,10 @@ enum Value<'a, 'tcx> { Repeat(VnIndex, ty::Const<'tcx>), /// The address of a place. Address { - place: Place<'tcx>, + base: AddressBase, + // We do not use a plain `Place` as we want to be able to reason about indices. + // This does not contain any `Deref` projection. + projection: &'a [ProjectionElem>], kind: AddressKind, /// Give each borrow and pointer a different provenance, so we don't merge them. provenance: VnOpaque, @@ -426,7 +437,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { /// Create a new `Value::Address` distinct from all the others. #[instrument(level = "trace", skip(self), ret)] - fn new_pointer(&mut self, place: Place<'tcx>, kind: AddressKind) -> VnIndex { + fn new_pointer(&mut self, place: Place<'tcx>, kind: AddressKind) -> Option { let pty = place.ty(self.local_decls, self.tcx).ty; let ty = match kind { AddressKind::Ref(bk) => { @@ -434,14 +445,34 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { } AddressKind::Address(mutbl) => Ty::new_ptr(self.tcx, pty, mutbl.to_mutbl_lossy()), }; - let index = - self.values.insert_unique(ty, |provenance| Value::Address { place, kind, provenance }); + + let mut projection = place.projection.iter(); + let base = if place.is_indirect_first_projection() { + let base = self.locals[place.local]?; + // Skip the initial `Deref`. + projection.next(); + AddressBase::Deref(base) + } else { + AddressBase::Local(place.local) + }; + // Do not try evaluating inside `Index`, this has been done by `simplify_place_projection`. + let projection = + projection.map(|proj| proj.try_map(|index| self.locals[index], |ty| ty).ok_or(())); + let projection = self.arena.try_alloc_from_iter(projection).ok()?; + + let index = self.values.insert_unique(ty, |provenance| Value::Address { + base, + projection, + kind, + provenance, + }); let evaluated = self.eval_to_const(index); let _index = self.evaluated.push(evaluated); debug_assert_eq!(index, _index); let _index = self.rev_locals.push(SmallVec::new()); debug_assert_eq!(index, _index); - index + + Some(index) } #[instrument(level = "trace", skip(self), ret)] @@ -591,14 +622,15 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { let elem = elem.try_map(|_| None, |()| ty.ty)?; self.ecx.project(base, elem).discard_err()? } - Address { place, kind: _, provenance: _ } => { - if !place.is_indirect_first_projection() { - return None; - } - let local = self.locals[place.local]?; - let pointer = self.evaluated[local].as_ref()?; + Address { base, projection, .. } => { + debug_assert!(!projection.contains(&ProjectionElem::Deref)); + let pointer = match base { + AddressBase::Deref(pointer) => self.evaluated[pointer].as_ref()?, + // We have no stack to point to. + AddressBase::Local(_) => return None, + }; let mut mplace = self.ecx.deref_pointer(pointer).discard_err()?; - for elem in place.projection.iter().skip(1) { + for elem in projection { // `Index` by constants should have been replaced by `ConstantIndex` by // `simplify_place_projection`. let elem = elem.try_map(|_| None, |ty| ty)?; @@ -717,12 +749,38 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { Some(op) } + /// Represent the *value* we obtain by dereferencing an `Address` value. + #[instrument(level = "trace", skip(self), ret)] + fn dereference_address( + &mut self, + base: AddressBase, + projection: &[ProjectionElem>], + ) -> Option { + let (mut place_ty, mut value) = match base { + // The base is a local, so we take the local's value and project from it. + AddressBase::Local(local) => { + let local = self.locals[local]?; + let place_ty = PlaceTy::from_ty(self.ty(local)); + (place_ty, local) + } + // The base is a pointer's deref, so we introduce the implicit deref. + AddressBase::Deref(reborrow) => { + let place_ty = PlaceTy::from_ty(self.ty(reborrow)); + self.project(place_ty, reborrow, ProjectionElem::Deref)? + } + }; + for &proj in projection { + (place_ty, value) = self.project(place_ty, value, proj)?; + } + Some(value) + } + + #[instrument(level = "trace", skip(self), ret)] fn project( &mut self, place_ty: PlaceTy<'tcx>, value: VnIndex, - proj: PlaceElem<'tcx>, - from_non_ssa_index: &mut bool, + proj: ProjectionElem>, ) -> Option<(PlaceTy<'tcx>, VnIndex)> { let projection_ty = place_ty.projection_ty(self.tcx, proj); let proj = match proj { @@ -730,6 +788,12 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { if let Some(Mutability::Not) = place_ty.ty.ref_mutability() && projection_ty.ty.is_freeze(self.tcx, self.typing_env()) { + if let Value::Address { base, projection, .. } = self.get(value) + && let Some(value) = self.dereference_address(base, projection) + { + return Some((projection_ty, value)); + } + // An immutable borrow `_x` always points to the same value for the // lifetime of the borrow, so we can merge all instances of `*_x`. return Some((projection_ty, self.insert_deref(projection_ty.ty, value))); @@ -766,10 +830,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { } ProjectionElem::Index(idx) => { if let Value::Repeat(inner, _) = self.get(value) { - *from_non_ssa_index |= self.locals[idx].is_none(); return Some((projection_ty, inner)); } - let idx = self.locals[idx]?; ProjectionElem::Index(idx) } ProjectionElem::ConstantIndex { offset, min_length, from_end } => { @@ -844,6 +906,42 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { trace!(?place); } + /// Represent the *value* which would be read from `place`. If we succeed, return it. + /// If we fail, return a `PlaceRef` that contains the same value. + #[instrument(level = "trace", skip(self), ret)] + fn compute_place_value( + &mut self, + place: Place<'tcx>, + location: Location, + ) -> Result> { + // Invariant: `place` and `place_ref` point to the same value, even if they point to + // different memory locations. + let mut place_ref = place.as_ref(); + + // Invariant: `value` holds the value up-to the `index`th projection excluded. + let Some(mut value) = self.locals[place.local] else { return Err(place_ref) }; + // Invariant: `value` has type `place_ty`, with optional downcast variant if needed. + let mut place_ty = PlaceTy::from_ty(self.local_decls[place.local].ty); + for (index, proj) in place.projection.iter().enumerate() { + if let Some(local) = self.try_as_local(value, location) { + // Both `local` and `Place { local: place.local, projection: projection[..index] }` + // hold the same value. Therefore, following place holds the value in the original + // `place`. + place_ref = PlaceRef { local, projection: &place.projection[index..] }; + } + + let Some(proj) = proj.try_map(|value| self.locals[value], |ty| ty) else { + return Err(place_ref); + }; + let Some(ty_and_value) = self.project(place_ty, value, proj) else { + return Err(place_ref); + }; + (place_ty, value) = ty_and_value; + } + + Ok(value) + } + /// Represent the *value* which would be read from `place`, and point `place` to a preexisting /// place with the same value (if that already exists). #[instrument(level = "trace", skip(self), ret)] @@ -854,67 +952,28 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { ) -> Option { self.simplify_place_projection(place, location); - // Invariant: `place` and `place_ref` point to the same value, even if they point to - // different memory locations. - let mut place_ref = place.as_ref(); - - // Invariant: `value` holds the value up-to the `index`th projection excluded. - let mut value = self.locals[place.local]?; - // Invariant: `value` has type `place_ty`, with optional downcast variant if needed. - let mut place_ty = PlaceTy::from_ty(self.local_decls[place.local].ty); - let mut from_non_ssa_index = false; - for (index, proj) in place.projection.iter().enumerate() { - if let Value::Projection(pointer, ProjectionElem::Deref) = self.get(value) - && let Value::Address { place: mut pointee, kind, .. } = self.get(pointer) - && let AddressKind::Ref(BorrowKind::Shared) = kind - && let Some(v) = self.simplify_place_value(&mut pointee, location) - { - value = v; - // `pointee` holds a `Place`, so `ProjectionElem::Index` holds a `Local`. - // That local is SSA, but we otherwise have no guarantee on that local's value at - // the current location compared to its value where `pointee` was borrowed. - if pointee.projection.iter().all(|elem| !matches!(elem, ProjectionElem::Index(_))) { - place_ref = - pointee.project_deeper(&place.projection[index..], self.tcx).as_ref(); + match self.compute_place_value(*place, location) { + Ok(value) => { + if let Some(new_place) = self.try_as_place(value, location, true) + && (new_place.local != place.local + || new_place.projection.len() < place.projection.len()) + { + *place = new_place; + self.reused_locals.insert(new_place.local); } + Some(value) } - if let Some(local) = self.try_as_local(value, location) { - // Both `local` and `Place { local: place.local, projection: projection[..index] }` - // hold the same value. Therefore, following place holds the value in the original - // `place`. - place_ref = PlaceRef { local, projection: &place.projection[index..] }; - } - - (place_ty, value) = self.project(place_ty, value, proj, &mut from_non_ssa_index)?; - } - - if let Value::Projection(pointer, ProjectionElem::Deref) = self.get(value) - && let Value::Address { place: mut pointee, kind, .. } = self.get(pointer) - && let AddressKind::Ref(BorrowKind::Shared) = kind - && let Some(v) = self.simplify_place_value(&mut pointee, location) - { - value = v; - // `pointee` holds a `Place`, so `ProjectionElem::Index` holds a `Local`. - // That local is SSA, but we otherwise have no guarantee on that local's value at - // the current location compared to its value where `pointee` was borrowed. - if pointee.projection.iter().all(|elem| !matches!(elem, ProjectionElem::Index(_))) { - place_ref = pointee.project_deeper(&[], self.tcx).as_ref(); + Err(place_ref) => { + if place_ref.local != place.local + || place_ref.projection.len() < place.projection.len() + { + // By the invariant on `place_ref`. + *place = place_ref.project_deeper(&[], self.tcx); + self.reused_locals.insert(place_ref.local); + } + None } } - if let Some(new_local) = self.try_as_local(value, location) { - place_ref = PlaceRef { local: new_local, projection: &[] }; - } else if from_non_ssa_index { - // If access to non-SSA locals is unavoidable, bail out. - return None; - } - - if place_ref.local != place.local || place_ref.projection.len() < place.projection.len() { - // By the invariant on `place_ref`. - *place = place_ref.project_deeper(&[], self.tcx); - self.reused_locals.insert(place_ref.local); - } - - Some(value) } #[instrument(level = "trace", skip(self), ret)] @@ -961,11 +1020,11 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { Rvalue::Aggregate(..) => return self.simplify_aggregate(lhs, rvalue, location), Rvalue::Ref(_, borrow_kind, ref mut place) => { self.simplify_place_projection(place, location); - return Some(self.new_pointer(*place, AddressKind::Ref(borrow_kind))); + return self.new_pointer(*place, AddressKind::Ref(borrow_kind)); } Rvalue::RawPtr(mutbl, ref mut place) => { self.simplify_place_projection(place, location); - return Some(self.new_pointer(*place, AddressKind::Address(mutbl))); + return self.new_pointer(*place, AddressKind::Address(mutbl)); } Rvalue::WrapUnsafeBinder(ref mut op, _) => { let value = self.simplify_operand(op, location)?; @@ -1205,12 +1264,10 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { } // `&mut *p`, `&raw *p`, etc don't change metadata. - Value::Address { place, kind: _, provenance: _ } - if let PlaceRef { local, projection: [PlaceElem::Deref] } = - place.as_ref() - && let Some(local_index) = self.locals[local] => + Value::Address { base: AddressBase::Deref(reborrowed), projection, .. } + if projection.is_empty() => { - local_index + reborrowed } _ => break, diff --git a/tests/mir-opt/gvn.dereference_indexing.GVN.panic-abort.diff b/tests/mir-opt/gvn.dereference_indexing.GVN.panic-abort.diff index 9bdcc2f108a6..1a07638cbafe 100644 --- a/tests/mir-opt/gvn.dereference_indexing.GVN.panic-abort.diff +++ b/tests/mir-opt/gvn.dereference_indexing.GVN.panic-abort.diff @@ -43,7 +43,8 @@ + nop; StorageLive(_8); StorageLive(_9); - _9 = copy (*_3); +- _9 = copy (*_3); ++ _9 = copy _1[_4]; _8 = opaque::(move _9) -> [return: bb2, unwind unreachable]; } diff --git a/tests/mir-opt/gvn.dereference_indexing.GVN.panic-unwind.diff b/tests/mir-opt/gvn.dereference_indexing.GVN.panic-unwind.diff index f38bc51adc48..6e67b66e783c 100644 --- a/tests/mir-opt/gvn.dereference_indexing.GVN.panic-unwind.diff +++ b/tests/mir-opt/gvn.dereference_indexing.GVN.panic-unwind.diff @@ -43,7 +43,8 @@ + nop; StorageLive(_8); StorageLive(_9); - _9 = copy (*_3); +- _9 = copy (*_3); ++ _9 = copy _1[_4]; _8 = opaque::(move _9) -> [return: bb2, unwind continue]; } diff --git a/tests/mir-opt/gvn.rs b/tests/mir-opt/gvn.rs index 98f94fbf0b14..3c3241fefe22 100644 --- a/tests/mir-opt/gvn.rs +++ b/tests/mir-opt/gvn.rs @@ -1065,8 +1065,8 @@ fn dereference_indexing(array: [u8; 2], index: usize) { &array[i] }; - // CHECK-NOT: [{{.*}}] - // CHECK: [[tmp:_.*]] = copy (*[[a]]); + // CHECK-NOT: StorageDead([[i]]); + // CHECK: [[tmp:_.*]] = copy _1[[[i]]]; // CHECK: opaque::(move [[tmp]]) opaque(*a); } diff --git a/tests/mir-opt/gvn_repeat.index_place.GVN.diff b/tests/mir-opt/gvn_repeat.index_place.GVN.diff new file mode 100644 index 000000000000..1eb7e9015cc3 --- /dev/null +++ b/tests/mir-opt/gvn_repeat.index_place.GVN.diff @@ -0,0 +1,15 @@ +- // MIR for `index_place` before GVN ++ // MIR for `index_place` after GVN + + fn index_place(_1: usize, _2: usize, _3: [i32; 5]) -> i32 { + let mut _0: i32; + let mut _4: &i32; + + bb0: { + _4 = &_3[_1]; + _1 = copy _2; + _0 = copy (*_4); + return; + } + } + diff --git a/tests/mir-opt/gvn_repeat.repeat_local.GVN.diff b/tests/mir-opt/gvn_repeat.repeat_local.GVN.diff index fd0478252811..eb3f885b8665 100644 --- a/tests/mir-opt/gvn_repeat.repeat_local.GVN.diff +++ b/tests/mir-opt/gvn_repeat.repeat_local.GVN.diff @@ -10,8 +10,7 @@ _4 = [copy _3; 5]; _5 = &_4[_1]; _1 = copy _2; -- _0 = copy (*_5); -+ _0 = copy _3; + _0 = copy (*_5); return; } } diff --git a/tests/mir-opt/gvn_repeat.rs b/tests/mir-opt/gvn_repeat.rs index bbbb2a7ccbaf..49364c6bfd23 100644 --- a/tests/mir-opt/gvn_repeat.rs +++ b/tests/mir-opt/gvn_repeat.rs @@ -6,6 +6,23 @@ use std::intrinsics::mir::*; +// EMIT_MIR gvn_repeat.index_place.GVN.diff +#[custom_mir(dialect = "runtime")] +pub fn index_place(mut idx1: usize, idx2: usize, array: [i32; 5]) -> i32 { + // CHECK-LABEL: fn index_place( + // CHECK: let mut [[ELEM:.*]]: &i32; + // CHECK: _0 = copy (*[[ELEM]]) + mir! { + let elem; + { + elem = &array[idx1]; + idx1 = idx2; + RET = *elem; + Return() + } + } +} + // EMIT_MIR gvn_repeat.repeat_place.GVN.diff #[custom_mir(dialect = "runtime")] pub fn repeat_place(mut idx1: usize, idx2: usize, val: &i32) -> i32 { @@ -29,7 +46,8 @@ pub fn repeat_place(mut idx1: usize, idx2: usize, val: &i32) -> i32 { #[custom_mir(dialect = "runtime")] pub fn repeat_local(mut idx1: usize, idx2: usize, val: i32) -> i32 { // CHECK-LABEL: fn repeat_local( - // CHECK: _0 = copy _3 + // CHECK: let mut [[ELEM:.*]]: &i32; + // CHECK: _0 = copy (*[[ELEM]]); mir! { let array; let elem; @@ -44,6 +62,7 @@ pub fn repeat_local(mut idx1: usize, idx2: usize, val: i32) -> i32 { } fn main() { + assert_eq!(index_place(0, 5, [0; 5]), 0); assert_eq!(repeat_place(0, 5, &0), 0); assert_eq!(repeat_local(0, 5, 0), 0); } From cf341eac195d85f8193de5052b68960605247a29 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Thu, 18 Sep 2025 00:36:52 +0000 Subject: [PATCH 162/226] Refactor remove_noop_landing_pads in two loops. --- .../src/remove_noop_landing_pads.rs | 43 ++++++++++--------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs index b53c1f6d2020..bbfa565a7b60 100644 --- a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs +++ b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs @@ -1,7 +1,7 @@ use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; -use tracing::debug; +use tracing::{debug, instrument}; use crate::patch::MirPatch; @@ -15,6 +15,7 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveNoopLandingPads { sess.panic_strategy().unwinds() } + #[instrument(level = "debug", skip(self, _tcx, body))] fn run_pass(&self, _tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let def_id = body.source.def_id(); debug!(?def_id); @@ -25,7 +26,7 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveNoopLandingPads { .iter_enumerated() .any(|(_bb, block)| matches!(block.terminator().kind, TerminatorKind::UnwindResume)); if !has_resume { - debug!("remove_noop_landing_pads: no resume block in MIR"); + debug!("no resume block in MIR"); return; } @@ -36,42 +37,44 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveNoopLandingPads { patch.apply(body); resume_block }; - debug!("remove_noop_landing_pads: resume block is {:?}", resume_block); + debug!(?resume_block); - let mut jumps_folded = 0; - let mut landing_pads_removed = 0; let mut nop_landing_pads = DenseBitSet::new_empty(body.basic_blocks.len()); // This is a post-order traversal, so that if A post-dominates B // then A will be visited before B. - let postorder: Vec<_> = traversal::postorder(body).map(|(bb, _)| bb).collect(); - for bb in postorder { - debug!(" processing {:?}", bb); - if let Some(unwind) = body[bb].terminator_mut().unwind_mut() + for &bb in body.basic_blocks.reverse_postorder().iter().rev() { + let is_nop_landing_pad = self.is_nop_landing_pad(bb, body, &nop_landing_pads); + debug!("is_nop_landing_pad({bb:?}) = {is_nop_landing_pad}"); + if is_nop_landing_pad { + nop_landing_pads.insert(bb); + } + } + + if nop_landing_pads.is_empty() { + debug!("no nop landing pads in MIR"); + return; + } + + let basic_blocks = body.basic_blocks.as_mut(); + for (bb, bbdata) in basic_blocks.iter_enumerated_mut() { + debug!("processing {:?}", bb); + + if let Some(unwind) = bbdata.terminator_mut().unwind_mut() && let UnwindAction::Cleanup(unwind_bb) = *unwind && nop_landing_pads.contains(unwind_bb) { debug!(" removing noop landing pad"); - landing_pads_removed += 1; *unwind = UnwindAction::Continue; } - body[bb].terminator_mut().successors_mut(|target| { + bbdata.terminator_mut().successors_mut(|target| { if *target != resume_block && nop_landing_pads.contains(*target) { debug!(" folding noop jump to {:?} to resume block", target); *target = resume_block; - jumps_folded += 1; } }); - - let is_nop_landing_pad = self.is_nop_landing_pad(bb, body, &nop_landing_pads); - if is_nop_landing_pad { - nop_landing_pads.insert(bb); - } - debug!(" is_nop_landing_pad({:?}) = {}", bb, is_nop_landing_pad); } - - debug!("removed {:?} jumps and {:?} landing pads", jumps_folded, landing_pads_removed); } fn is_required(&self) -> bool { From 1ac5d4c2783a3ac23f19c8a71ba6e7fdf21034c7 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Sat, 27 Sep 2025 16:41:32 +0000 Subject: [PATCH 163/226] Create resume block later. --- .../src/remove_noop_landing_pads.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs index bbfa565a7b60..0e6eef38cd2c 100644 --- a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs +++ b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs @@ -30,15 +30,6 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveNoopLandingPads { return; } - // make sure there's a resume block without any statements - let resume_block = { - let mut patch = MirPatch::new(body); - let resume_block = patch.resume_block(); - patch.apply(body); - resume_block - }; - debug!(?resume_block); - let mut nop_landing_pads = DenseBitSet::new_empty(body.basic_blocks.len()); // This is a post-order traversal, so that if A post-dominates B @@ -56,6 +47,15 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveNoopLandingPads { return; } + // make sure there's a resume block without any statements + let resume_block = { + let mut patch = MirPatch::new(body); + let resume_block = patch.resume_block(); + patch.apply(body); + resume_block + }; + debug!(?resume_block); + let basic_blocks = body.basic_blocks.as_mut(); for (bb, bbdata) in basic_blocks.iter_enumerated_mut() { debug!("processing {:?}", bb); From 94b2c303aa9c9c6a1a34d1b5587e0a7a481fa97f Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Sat, 4 Oct 2025 17:02:08 +0000 Subject: [PATCH 164/226] Use traversal::postorder. --- .../src/remove_noop_landing_pads.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs index 0e6eef38cd2c..38752bde6b41 100644 --- a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs +++ b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs @@ -34,8 +34,8 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveNoopLandingPads { // This is a post-order traversal, so that if A post-dominates B // then A will be visited before B. - for &bb in body.basic_blocks.reverse_postorder().iter().rev() { - let is_nop_landing_pad = self.is_nop_landing_pad(bb, body, &nop_landing_pads); + for (bb, bbdata) in traversal::postorder(body) { + let is_nop_landing_pad = self.is_nop_landing_pad(bbdata, &nop_landing_pads); debug!("is_nop_landing_pad({bb:?}) = {is_nop_landing_pad}"); if is_nop_landing_pad { nop_landing_pads.insert(bb); @@ -85,11 +85,10 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveNoopLandingPads { impl RemoveNoopLandingPads { fn is_nop_landing_pad( &self, - bb: BasicBlock, - body: &Body<'_>, + bbdata: &BasicBlockData<'_>, nop_landing_pads: &DenseBitSet, ) -> bool { - for stmt in &body[bb].statements { + for stmt in &bbdata.statements { match &stmt.kind { StatementKind::FakeRead(..) | StatementKind::StorageLive(_) @@ -122,7 +121,7 @@ impl RemoveNoopLandingPads { } } - let terminator = body[bb].terminator(); + let terminator = bbdata.terminator(); match terminator.kind { TerminatorKind::Goto { .. } | TerminatorKind::UnwindResume From 67bc0308336a115c3ea45130d9845a97f33b8196 Mon Sep 17 00:00:00 2001 From: Kivooeo Date: Sat, 4 Oct 2025 17:43:59 +0000 Subject: [PATCH 165/226] change flt back to ftl --- compiler/rustc_attr_parsing/messages.ftl | 157 ++++++++++----------- compiler/rustc_builtin_macros/messages.ftl | 18 +-- compiler/rustc_codegen_ssa/messages.ftl | 4 +- compiler/rustc_const_eval/messages.ftl | 8 +- compiler/rustc_expand/messages.ftl | 10 +- compiler/rustc_lint/messages.ftl | 16 +-- compiler/rustc_metadata/messages.ftl | 25 ++-- compiler/rustc_middle/messages.ftl | 6 +- compiler/rustc_parse/messages.ftl | 15 +- compiler/rustc_passes/messages.ftl | 8 +- compiler/rustc_resolve/messages.ftl | 12 +- compiler/rustc_ty_utils/messages.ftl | 4 - src/tools/tidy/src/fluent_alphabetical.rs | 2 +- 13 files changed, 135 insertions(+), 150 deletions(-) diff --git a/compiler/rustc_attr_parsing/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl index 6c5346e83554..a432fa099ef4 100644 --- a/compiler/rustc_attr_parsing/messages.ftl +++ b/compiler/rustc_attr_parsing/messages.ftl @@ -1,3 +1,9 @@ +attr_parsing_as_needed_compatibility = + linking modifier `as-needed` is only compatible with `dylib` and `framework` linking kinds + +attr_parsing_bundle_needs_static = + linking modifier `bundle` is only compatible with `static` linking kind + attr_parsing_cfg_predicate_identifier = `cfg` predicate key must be an identifier @@ -18,16 +24,12 @@ attr_parsing_empty_attribute = } -attr_parsing_invalid_target = `#[{$name}]` attribute cannot be used on {$target} - .help = `#[{$name}]` can {$only}be applied to {$applied} - .suggestion = remove the attribute -attr_parsing_invalid_target_lint = `#[{$name}]` attribute cannot be used on {$target} - .warn = {-attr_parsing_previously_accepted} - .help = `#[{$name}]` can {$only}be applied to {$applied} - .suggestion = remove the attribute - attr_parsing_empty_confusables = expected at least one confusable name +attr_parsing_empty_link_name = + link name must not be empty + .label = empty link name + attr_parsing_expected_one_cfg_pattern = expected 1 cfg-pattern @@ -48,6 +50,15 @@ attr_parsing_ill_formed_attribute_input = {$num_suggestions -> *[other] valid forms for the attribute are {$suggestions} } +attr_parsing_import_name_type_raw = + import name type can only be used with link kind `raw-dylib` + +attr_parsing_import_name_type_x86 = + import name type is only supported on x86 + +attr_parsing_incompatible_wasm_link = + `wasm_import_module` is incompatible with other arguments in `#[link]` attributes + attr_parsing_incorrect_repr_format_align_one_arg = incorrect `repr(align)` attribute format: `align` takes exactly one argument in parentheses @@ -67,6 +78,11 @@ attr_parsing_incorrect_repr_format_packed_one_or_zero_arg = attr_parsing_invalid_alignment_value = invalid alignment value: {$error_part} +attr_parsing_invalid_attr_unsafe = `{$name}` is not an unsafe attribute + .label = this is not an unsafe attribute + .suggestion = remove the `unsafe(...)` + .note = extraneous unsafe is not allowed in attributes + attr_parsing_invalid_issue_string = `issue` must be a non-zero numeric string or "none" .must_not_be_zero = `issue` must not be "0", use "none" instead @@ -75,6 +91,13 @@ attr_parsing_invalid_issue_string = .pos_overflow = number too large to fit in target type .neg_overflow = number too small to fit in target type +attr_parsing_invalid_link_modifier = + invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed + +attr_parsing_invalid_meta_item = expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found {$descr} + .remove_neg_sugg = negative numbers are not literals, try removing the `-` sign + .quote_ident_sugg = surround the identifier with quotation marks to make it into a string literal + attr_parsing_invalid_predicate = invalid predicate `{$predicate}` @@ -100,9 +123,36 @@ attr_parsing_invalid_style = {$is_used_as_inner -> } .note = This attribute does not have an `!`, which means it is applied to this {$target} +attr_parsing_invalid_target = `#[{$name}]` attribute cannot be used on {$target} + .help = `#[{$name}]` can {$only}be applied to {$applied} + .suggestion = remove the attribute +attr_parsing_invalid_target_lint = `#[{$name}]` attribute cannot be used on {$target} + .warn = {-attr_parsing_previously_accepted} + .help = `#[{$name}]` can {$only}be applied to {$applied} + .suggestion = remove the attribute + +attr_parsing_limit_invalid = + `limit` must be a non-negative integer + .label = {$error_str} +attr_parsing_link_arg_unstable = + link kind `link-arg` is unstable + +attr_parsing_link_cfg_unstable = + link cfg is unstable + +attr_parsing_link_framework_apple = + link kind `framework` is only supported on Apple targets + attr_parsing_link_ordinal_out_of_range = ordinal value in `link_ordinal` is too large: `{$ordinal}` .note = the value may not exceed `u16::MAX` +attr_parsing_link_requires_name = + `#[link]` attribute requires a `name = "string"` argument + .label = missing `name` argument + +attr_parsing_meta_bad_delim = wrong meta list delimiters +attr_parsing_meta_bad_delim_suggestion = the delimiters should be `(` and `)` + attr_parsing_missing_feature = missing 'feature' @@ -115,6 +165,9 @@ attr_parsing_missing_note = attr_parsing_missing_since = missing 'since' +attr_parsing_multiple_modifiers = + multiple `{$modifier}` modifiers in a single `modifiers` argument + attr_parsing_multiple_stability_levels = multiple stability levels @@ -138,6 +191,15 @@ attr_parsing_objc_class_expected_string_literal = `objc::class!` expected a stri attr_parsing_objc_selector_expected_string_literal = `objc::selector!` expected a string literal +attr_parsing_raw_dylib_elf_unstable = + link kind `raw-dylib` is unstable on ELF platforms + +attr_parsing_raw_dylib_no_nul = + link name must not contain NUL characters if link kind is `raw-dylib` + +attr_parsing_raw_dylib_only_windows = + link kind `raw-dylib` is only supported on Windows targets + attr_parsing_repr_ident = meta item in `repr` must be an identifier @@ -152,6 +214,9 @@ attr_parsing_soft_no_args = attr_parsing_stability_outside_std = stability attributes may not be used outside of the standard library +attr_parsing_suffixed_literal_in_attribute = suffixed literals are not allowed in attributes + .help = instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), use an unsuffixed version (`1`, `1.0`, etc.) + attr_parsing_unknown_meta_item = unknown meta item '{$item}' .label = expected one of {$expected} @@ -164,6 +229,10 @@ attr_parsing_unrecognized_repr_hint = .help = valid reprs are `Rust` (default), `C`, `align`, `packed`, `transparent`, `simd`, `i8`, `u8`, `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `isize`, `usize` .note = for more information, visit +attr_parsing_unsafe_attr_outside_unsafe = unsafe attribute used without unsafe + .label = usage of unsafe attribute +attr_parsing_unsafe_attr_outside_unsafe_suggestion = wrap the attribute in `unsafe(...)` + attr_parsing_unstable_cfg_target_compact = compact `cfg(target(..))` is experimental and subject to change @@ -193,77 +262,5 @@ attr_parsing_unused_multiple = -attr_parsing_previously_accepted = this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! -attr_parsing_meta_bad_delim = wrong meta list delimiters -attr_parsing_meta_bad_delim_suggestion = the delimiters should be `(` and `)` - -attr_parsing_unsafe_attr_outside_unsafe = unsafe attribute used without unsafe - .label = usage of unsafe attribute -attr_parsing_unsafe_attr_outside_unsafe_suggestion = wrap the attribute in `unsafe(...)` - -attr_parsing_invalid_attr_unsafe = `{$name}` is not an unsafe attribute - .label = this is not an unsafe attribute - .suggestion = remove the `unsafe(...)` - .note = extraneous unsafe is not allowed in attributes - -attr_parsing_invalid_meta_item = expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found {$descr} - .remove_neg_sugg = negative numbers are not literals, try removing the `-` sign - .quote_ident_sugg = surround the identifier with quotation marks to make it into a string literal - -attr_parsing_suffixed_literal_in_attribute = suffixed literals are not allowed in attributes - .help = instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), use an unsuffixed version (`1`, `1.0`, etc.) - -attr_parsing_as_needed_compatibility = - linking modifier `as-needed` is only compatible with `dylib` and `framework` linking kinds - -attr_parsing_bundle_needs_static = - linking modifier `bundle` is only compatible with `static` linking kind - -attr_parsing_empty_link_name = - link name must not be empty - .label = empty link name - -attr_parsing_import_name_type_raw = - import name type can only be used with link kind `raw-dylib` - -attr_parsing_import_name_type_x86 = - import name type is only supported on x86 - -attr_parsing_incompatible_wasm_link = - `wasm_import_module` is incompatible with other arguments in `#[link]` attributes - -attr_parsing_invalid_link_modifier = - invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed - -attr_parsing_link_arg_unstable = - link kind `link-arg` is unstable - -attr_parsing_link_cfg_unstable = - link cfg is unstable - -attr_parsing_link_framework_apple = - link kind `framework` is only supported on Apple targets - -attr_parsing_link_requires_name = - `#[link]` attribute requires a `name = "string"` argument - .label = missing `name` argument - -attr_parsing_multiple_modifiers = - multiple `{$modifier}` modifiers in a single `modifiers` argument - -attr_parsing_multiple_renamings = - multiple renamings were specified for library `{$lib_name}` -attr_parsing_raw_dylib_no_nul = - link name must not contain NUL characters if link kind is `raw-dylib` - -attr_parsing_raw_dylib_elf_unstable = - link kind `raw-dylib` is unstable on ELF platforms - -attr_parsing_raw_dylib_only_windows = - link kind `raw-dylib` is only supported on Windows targets - attr_parsing_whole_archive_needs_static = linking modifier `whole-archive` is only compatible with `static` linking kind - -attr_parsing_limit_invalid = - `limit` must be a non-negative integer - .label = {$error_str} diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl index 7c1a5f44e165..cbdd8199d7ba 100644 --- a/compiler/rustc_builtin_macros/messages.ftl +++ b/compiler/rustc_builtin_macros/messages.ftl @@ -135,6 +135,15 @@ builtin_macros_concat_missing_literal = expected a literal builtin_macros_default_arg = `#[default]` attribute does not accept a value .suggestion = try using `#[default]` +builtin_macros_derive_from_usage_note = `#[derive(From)]` can only be used on structs with exactly one field + +builtin_macros_derive_from_wrong_field_count = `#[derive(From)]` used on a struct with {$multiple_fields -> + [true] multiple fields + *[false] no fields +} + +builtin_macros_derive_from_wrong_target = `#[derive(From)]` used on {$kind} + builtin_macros_derive_macro_call = `derive` cannot be used on items with type macros builtin_macros_derive_path_args_list = traits in `#[derive(...)]` don't accept arguments @@ -229,15 +238,6 @@ builtin_macros_format_unused_args = multiple unused formatting arguments builtin_macros_format_use_positional = consider using a positional formatting argument instead -builtin_macros_derive_from_wrong_target = `#[derive(From)]` used on {$kind} - -builtin_macros_derive_from_wrong_field_count = `#[derive(From)]` used on a struct with {$multiple_fields -> - [true] multiple fields - *[false] no fields -} - -builtin_macros_derive_from_usage_note = `#[derive(From)]` can only be used on structs with exactly one field - builtin_macros_incomplete_include = include macro expected single expression in source builtin_macros_multiple_default_attrs = multiple `#[default]` attributes diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index 91c3806df4c3..97ff04f66dae 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -97,10 +97,10 @@ codegen_ssa_invalid_literal_value = invalid literal value codegen_ssa_invalid_monomorphization_basic_float_type = invalid monomorphization of `{$name}` intrinsic: expected basic float type, found `{$ty}` -codegen_ssa_invalid_monomorphization_basic_integer_type = invalid monomorphization of `{$name}` intrinsic: expected basic integer type, found `{$ty}` - codegen_ssa_invalid_monomorphization_basic_integer_or_ptr_type = invalid monomorphization of `{$name}` intrinsic: expected basic integer or pointer type, found `{$ty}` +codegen_ssa_invalid_monomorphization_basic_integer_type = invalid monomorphization of `{$name}` intrinsic: expected basic integer type, found `{$ty}` + codegen_ssa_invalid_monomorphization_cannot_return = invalid monomorphization of `{$name}` intrinsic: cannot return `{$ret_ty}`, expected `u{$expected_int_bits}` or `[u8; {$expected_bytes}]` codegen_ssa_invalid_monomorphization_cast_wide_pointer = invalid monomorphization of `{$name}` intrinsic: cannot cast wide pointer `{$ty}` diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index 010ffa60c7a5..13a49e80cf2d 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -231,9 +231,6 @@ const_eval_mutable_borrow_escaping = const_eval_mutable_ptr_in_final = encountered mutable pointer in final value of {const_eval_intern_kind} -const_eval_partial_pointer_in_final = encountered partial pointer in final value of {const_eval_intern_kind} - .note = while pointers can be broken apart into individual bytes during const-evaluation, only complete pointers (with all their bytes in the right order) are supported in the final value - const_eval_nested_static_in_thread_local = #[thread_local] does not support implicit nested statics, please create explicit static items and refer to them instead const_eval_non_const_await = @@ -302,6 +299,9 @@ const_eval_panic = evaluation panicked: {$msg} const_eval_panic_non_str = argument to `panic!()` in a const context must have type `&str` +const_eval_partial_pointer_in_final = encountered partial pointer in final value of {const_eval_intern_kind} + .note = while pointers can be broken apart into individual bytes during const-evaluation, only complete pointers (with all their bytes in the right order) are supported in the final value + const_eval_partial_pointer_read = unable to read parts of a pointer from memory at {$ptr} const_eval_pointer_arithmetic_overflow = @@ -476,6 +476,7 @@ const_eval_validation_invalid_vtable_trait = {$front_matter}: wrong trait in wid const_eval_validation_mutable_ref_in_const = {$front_matter}: encountered mutable reference in `const` value const_eval_validation_mutable_ref_to_immutable = {$front_matter}: encountered mutable reference or box pointing to read-only memory const_eval_validation_never_val = {$front_matter}: encountered a value of the never type `!` +const_eval_validation_nonnull_ptr_out_of_range = {$front_matter}: encountered a maybe-null pointer, but expected something that is definitely non-zero const_eval_validation_null_box = {$front_matter}: encountered a {$maybe -> [true] maybe-null *[false] null @@ -485,7 +486,6 @@ const_eval_validation_null_ref = {$front_matter}: encountered a {$maybe -> [true] maybe-null *[false] null } reference -const_eval_validation_nonnull_ptr_out_of_range = {$front_matter}: encountered a maybe-null pointer, but expected something that is definitely non-zero const_eval_validation_out_of_range = {$front_matter}: encountered {$value}, but expected something {$in_range} const_eval_validation_partial_pointer = {$front_matter}: encountered a partial pointer or a mix of pointers const_eval_validation_pointer_as_int = {$front_matter}: encountered a pointer, but {$expected} diff --git a/compiler/rustc_expand/messages.ftl b/compiler/rustc_expand/messages.ftl index 47c00bff5c95..e914f24e294c 100644 --- a/compiler/rustc_expand/messages.ftl +++ b/compiler/rustc_expand/messages.ftl @@ -95,6 +95,8 @@ expand_malformed_feature_attribute = malformed `feature` attribute input .expected = expected just one word +expand_meta_var_dif_seq_matchers = {$msg} + expand_metavar_still_repeating = variable `{$ident}` is still repeating at this depth .label = expected repetition @@ -102,8 +104,6 @@ expand_metavariable_wrong_operator = meta-variable repeats with different Kleene .binder_label = expected repetition .occurrence_label = conflicting repetition -expand_meta_var_dif_seq_matchers = {$msg} - expand_missing_fragment_specifier = missing fragment specifier .note = fragment specifiers must be provided .suggestion_add_fragspec = try adding a specifier here @@ -198,12 +198,12 @@ expand_trailing_semi_macro = trailing semicolon in macro used in expression posi expand_unknown_macro_variable = unknown macro variable `{$name}` +expand_unsupported_key_value = + key-value macro attributes are not supported + expand_unused_builtin_attribute = unused attribute `{$attr_name}` .note = the built-in attribute `{$attr_name}` will be ignored, since it's applied to the macro invocation `{$macro_name}` .suggestion = remove the attribute -expand_unsupported_key_value = - key-value macro attributes are not supported - expand_wrong_fragment_kind = non-{$kind} macro in {$kind} position: {$name} diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 5b98c9fafaad..bc0a98fc7aa8 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -392,6 +392,14 @@ lint_improper_ctypes_union_layout_help = consider adding a `#[repr(C)]` or `#[re lint_improper_ctypes_union_layout_reason = this union has unspecified layout lint_improper_ctypes_union_non_exhaustive = this union is non-exhaustive +lint_int_to_ptr_transmutes = transmuting an integer to a pointer creates a pointer without provenance + .note = this is dangerous because dereferencing the resulting pointer is undefined behavior + .note_exposed_provenance = exposed provenance semantics can be used to create a pointer based on some previously exposed provenance + .help_transmute = for more information about transmute, see + .help_exposed_provenance = for more information about exposed provenance, see + .suggestion_with_exposed_provenance = use `std::ptr::with_exposed_provenance{$suffix}` instead to use a previously exposed provenance + .suggestion_without_provenance_mut = if you truly mean to create a pointer without provenance, use `std::ptr::without_provenance_mut` + lint_invalid_asm_label_binary = avoid using labels containing only the digits `0` and `1` in inline assembly .label = use a different label that doesn't start with `0` or `1` .help = start numbering with `2` instead @@ -439,14 +447,6 @@ lint_invalid_reference_casting_note_book = for more information, visit - .help_exposed_provenance = for more information about exposed provenance, see - .suggestion_with_exposed_provenance = use `std::ptr::with_exposed_provenance{$suffix}` instead to use a previously exposed provenance - .suggestion_without_provenance_mut = if you truly mean to create a pointer without provenance, use `std::ptr::without_provenance_mut` - lint_lintpass_by_hand = implementing `LintPass` by hand .help = try using `declare_lint_pass!` or `impl_lint_pass!` instead diff --git a/compiler/rustc_metadata/messages.ftl b/compiler/rustc_metadata/messages.ftl index e624bfc5b8bd..fac7b6c21f60 100644 --- a/compiler/rustc_metadata/messages.ftl +++ b/compiler/rustc_metadata/messages.ftl @@ -98,13 +98,6 @@ metadata_full_metadata_not_found = metadata_global_alloc_required = no global memory allocator found but one is required; link to std or add `#[global_allocator]` to a static item that implements the GlobalAlloc trait -metadata_incompatible_with_immediate_abort = - the crate `{$crate_name}` was compiled with a panic strategy which is incompatible with `immediate-abort` - -metadata_incompatible_with_immediate_abort_core = - the crate `core` was compiled with a panic strategy which is incompatible with `immediate-abort` - .help = consider building the standard library from source with `cargo build -Zbuild-std` - metadata_incompatible_panic_in_drop_strategy = the crate `{$crate_name}` is compiled with the panic-in-drop strategy `{$found_strategy}` which is incompatible with this crate's strategy of `{$desired_strategy}` @@ -132,6 +125,13 @@ metadata_incompatible_target_modifiers_r_missed = .note = `{$flag_name_prefixed}={$local_value}` in this crate is incompatible with unset `{$flag_name_prefixed}` in dependency `{$extern_crate}` .help = the `{$flag_name_prefixed}` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely +metadata_incompatible_with_immediate_abort = + the crate `{$crate_name}` was compiled with a panic strategy which is incompatible with `immediate-abort` + +metadata_incompatible_with_immediate_abort_core = + the crate `core` was compiled with a panic strategy which is incompatible with `immediate-abort` + .help = consider building the standard library from source with `cargo build -Zbuild-std` + metadata_install_missing_components = maybe you need to install the missing components with: `rustup component add rust-src rustc-dev llvm-tools-preview` @@ -197,6 +197,8 @@ metadata_prev_alloc_error_handler = metadata_prev_global_alloc = previous global allocator defined here +metadata_raw_dylib_malformed = + link name must be well-formed if link kind is `raw-dylib` metadata_raw_dylib_unsupported_abi = ABI not supported by `#[link(kind = "raw-dylib")]` on this architecture @@ -236,12 +238,3 @@ metadata_unknown_target_modifier_unsafe_allowed = unknown target modifier `{$fla metadata_wasm_c_abi = older versions of the `wasm-bindgen` crate are incompatible with current versions of Rust; please update to `wasm-bindgen` v0.2.88 - -metadata_wasm_import_form = - wasm import module must be of the form `wasm_import_module = "string"` - -metadata_whole_archive_needs_static = - linking modifier `whole-archive` is only compatible with `static` linking kind - -metadata_raw_dylib_malformed = - link name must be well-formed if link kind is `raw-dylib` diff --git a/compiler/rustc_middle/messages.ftl b/compiler/rustc_middle/messages.ftl index 82f3df8da3b1..b46f43841c8e 100644 --- a/compiler/rustc_middle/messages.ftl +++ b/compiler/rustc_middle/messages.ftl @@ -95,15 +95,15 @@ middle_layout_normalization_failure = middle_layout_references_error = the type has an unknown layout -middle_layout_size_overflow = - values of the type `{$ty}` are too big for the target architecture - middle_layout_simd_too_many = the SIMD type `{$ty}` has more elements than the limit {$max_lanes} middle_layout_simd_zero_length = the SIMD type `{$ty}` has zero elements +middle_layout_size_overflow = + values of the type `{$ty}` are too big for the target architecture + middle_layout_too_generic = the type `{$ty}` does not have a fixed layout middle_layout_unknown = diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index f83cf645f829..b1894ab92192 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -68,6 +68,13 @@ parse_attr_after_generic = trailing attribute after generic parameter parse_attr_without_generics = attribute without generic parameters .label = attributes are only permitted when preceding parameters +parse_attribute_on_empty_type = attributes cannot be applied here + .label = attributes are not allowed here + +parse_attribute_on_generic_arg = attributes cannot be applied to generic arguments + .label = attributes are not allowed here + .suggestion = remove attribute from here + parse_attribute_on_param_type = attributes cannot be applied to a function parameter's type .label = attributes are not allowed here @@ -75,13 +82,6 @@ parse_attribute_on_type = attributes cannot be applied to types .label = attributes are not allowed here .suggestion = remove attribute from here -parse_attribute_on_generic_arg = attributes cannot be applied to generic arguments - .label = attributes are not allowed here - .suggestion = remove attribute from here - -parse_attribute_on_empty_type = attributes cannot be applied here - .label = attributes are not allowed here - parse_bad_assoc_type_bounds = bounds on associated types do not belong here .label = belongs in `where` clause @@ -868,7 +868,6 @@ parse_too_many_hashes = too many `#` symbols: raw strings may be delimited by up parse_too_short_hex_escape = numeric character escape is too short parse_trailing_vert_not_allowed = a trailing `{$token}` is not allowed in an or-pattern -parse_trailing_vert_not_allowed_suggestion = remove the `{$token}` parse_trait_alias_cannot_be_auto = trait aliases cannot be `auto` parse_trait_alias_cannot_be_const = trait aliases cannot be `const` diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index df4016dfa1b8..9971f0c3fa32 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -13,10 +13,6 @@ passes_abi_ne = passes_abi_of = fn_abi_of({$fn_name}) = {$fn_abi} -passes_macro_only_attribute = - attribute should be applied to a macro - .label = not a macro - passes_attr_application_enum = attribute should be applied to an enum .label = not an enum @@ -392,6 +388,10 @@ passes_macro_export_on_decl_macro = `#[macro_export]` has no effect on declarative macro definitions .note = declarative macros follow the same exporting rules as regular items +passes_macro_only_attribute = + attribute should be applied to a macro + .label = not a macro + passes_may_dangle = `#[may_dangle]` must be applied to a lifetime or type generic parameter in `Drop` impl diff --git a/compiler/rustc_resolve/messages.ftl b/compiler/rustc_resolve/messages.ftl index cc8fbc18b737..5bf90d2637df 100644 --- a/compiler/rustc_resolve/messages.ftl +++ b/compiler/rustc_resolve/messages.ftl @@ -93,9 +93,6 @@ resolve_consider_adding_a_derive = resolve_consider_adding_macro_export = consider adding a `#[macro_export]` to the macro in the imported module -resolve_consider_marking_as_pub_crate = - in case you want to use the macro within this crate only, reduce the visibility to `pub(crate)` - resolve_consider_declaring_with_pub = consider declaring type or module `{$ident}` with `pub` @@ -108,6 +105,9 @@ resolve_consider_making_the_field_public = resolve_consider_marking_as_pub = consider marking `{$ident}` as `pub` in the imported module +resolve_consider_marking_as_pub_crate = + in case you want to use the macro within this crate only, reduce the visibility to `pub(crate)` + resolve_consider_move_macro_position = consider moving the definition of `{$ident}` before this call @@ -248,15 +248,15 @@ resolve_lowercase_self = attempt to use a non-constant value in a constant .suggestion = try using `Self` -resolve_macro_cannot_use_as_fn_like = - `{$ident}` exists, but has no rules for function-like invocation - resolve_macro_cannot_use_as_attr = `{$ident}` exists, but has no `attr` rules resolve_macro_cannot_use_as_derive = `{$ident}` exists, but has no `derive` rules +resolve_macro_cannot_use_as_fn_like = + `{$ident}` exists, but has no rules for function-like invocation + resolve_macro_defined_later = a macro with the same name exists, but it appears later diff --git a/compiler/rustc_ty_utils/messages.ftl b/compiler/rustc_ty_utils/messages.ftl index 8bc7bf10865f..c1684bfb43b6 100644 --- a/compiler/rustc_ty_utils/messages.ftl +++ b/compiler/rustc_ty_utils/messages.ftl @@ -52,8 +52,6 @@ ty_utils_non_primitive_simd_type = monomorphising SIMD type `{$ty}` with a non-p ty_utils_operation_not_supported = unsupported operation in generic constants -ty_utils_oversized_simd_type = monomorphising SIMD type `{$ty}` of length greater than {$max_lanes} - ty_utils_pointer_not_supported = pointer casts are not allowed in generic constants ty_utils_tuple_not_supported = tuple construction is not supported in generic constants @@ -61,5 +59,3 @@ ty_utils_tuple_not_supported = tuple construction is not supported in generic co ty_utils_unexpected_fnptr_associated_item = `FnPtr` trait with unexpected associated item ty_utils_yield_not_supported = coroutine control flow is not allowed in generic constants - -ty_utils_zero_length_simd_type = monomorphising SIMD type `{$ty}` of zero length diff --git a/src/tools/tidy/src/fluent_alphabetical.rs b/src/tools/tidy/src/fluent_alphabetical.rs index 02b914c21aeb..b1d1c16b4549 100644 --- a/src/tools/tidy/src/fluent_alphabetical.rs +++ b/src/tools/tidy/src/fluent_alphabetical.rs @@ -15,7 +15,7 @@ fn message() -> &'static Regex { } fn is_fluent(path: &Path) -> bool { - path.extension().is_some_and(|ext| ext == "flt") + path.extension().is_some_and(|ext| ext == "ftl") } fn check_alphabetic( From b5fb01d67db9726180fd85b58d00e3b954fc7e45 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Sat, 4 Oct 2025 15:53:00 -0400 Subject: [PATCH 166/226] Improve the advice given by panic_immediate_abort --- library/core/src/panicking.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs index 3f30038dbc03..b5150837e6a9 100644 --- a/library/core/src/panicking.rs +++ b/library/core/src/panicking.rs @@ -35,7 +35,8 @@ use crate::panic::{Location, PanicInfo}; #[cfg(feature = "panic_immediate_abort")] compile_error!( "panic_immediate_abort is now a real panic strategy! \ - Enable it with the compiler flags `-Zunstable-options -Cpanic=immediate-abort`" + Enable it with `panic = \"immediate-abort\"` in Cargo.toml, \ + or with the compiler flags `-Zunstable-options -Cpanic=immediate-abort`" ); // First we define the two main entry points that all panics go through. From 757d98ce2b55162e22051be354418291e88f8d46 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Fri, 3 Oct 2025 13:20:39 +1000 Subject: [PATCH 167/226] Move `DirectiveLine` into its own submodule --- src/tools/compiletest/src/directives.rs | 68 +------------------- src/tools/compiletest/src/directives/line.rs | 67 +++++++++++++++++++ 2 files changed, 69 insertions(+), 66 deletions(-) create mode 100644 src/tools/compiletest/src/directives/line.rs diff --git a/src/tools/compiletest/src/directives.rs b/src/tools/compiletest/src/directives.rs index a79978d036ce..edb06dd7345d 100644 --- a/src/tools/compiletest/src/directives.rs +++ b/src/tools/compiletest/src/directives.rs @@ -15,6 +15,7 @@ use crate::directives::auxiliary::{AuxProps, parse_and_update_aux}; use crate::directives::directive_names::{ KNOWN_DIRECTIVE_NAMES, KNOWN_HTMLDOCCK_DIRECTIVE_NAMES, KNOWN_JSONDOCCK_DIRECTIVE_NAMES, }; +use crate::directives::line::{DirectiveLine, line_directive}; use crate::directives::needs::CachedNeedsConditions; use crate::edition::{Edition, parse_edition}; use crate::errors::ErrorKind; @@ -25,6 +26,7 @@ use crate::{fatal, help}; pub(crate) mod auxiliary; mod cfg; mod directive_names; +mod line; mod needs; #[cfg(test)] mod tests; @@ -824,70 +826,6 @@ impl TestProps { } } -/// If the given line begins with the appropriate comment prefix for a directive, -/// returns a struct containing various parts of the directive. -fn line_directive<'line>( - line_number: usize, - original_line: &'line str, -) -> Option> { - // Ignore lines that don't start with the comment prefix. - let after_comment = - original_line.trim_start().strip_prefix(COMPILETEST_DIRECTIVE_PREFIX)?.trim_start(); - - let revision; - let raw_directive; - - if let Some(after_open_bracket) = after_comment.strip_prefix('[') { - // A comment like `//@[foo]` only applies to revision `foo`. - let Some((line_revision, after_close_bracket)) = after_open_bracket.split_once(']') else { - panic!( - "malformed condition directive: expected `{COMPILETEST_DIRECTIVE_PREFIX}[foo]`, found `{original_line}`" - ) - }; - - revision = Some(line_revision); - raw_directive = after_close_bracket.trim_start(); - } else { - revision = None; - raw_directive = after_comment; - }; - - Some(DirectiveLine { line_number, revision, raw_directive }) -} - -/// The (partly) broken-down contents of a line containing a test directive, -/// which [`iter_directives`] passes to its callback function. -/// -/// For example: -/// -/// ```text -/// //@ compile-flags: -O -/// ^^^^^^^^^^^^^^^^^ raw_directive -/// -/// //@ [foo] compile-flags: -O -/// ^^^ revision -/// ^^^^^^^^^^^^^^^^^ raw_directive -/// ``` -struct DirectiveLine<'ln> { - line_number: usize, - /// Some test directives start with a revision name in square brackets - /// (e.g. `[foo]`), and only apply to that revision of the test. - /// If present, this field contains the revision name (e.g. `foo`). - revision: Option<&'ln str>, - /// The main part of the directive, after removing the comment prefix - /// and the optional revision specifier. - /// - /// This is "raw" because the directive's name and colon-separated value - /// (if present) have not yet been extracted or checked. - raw_directive: &'ln str, -} - -impl<'ln> DirectiveLine<'ln> { - fn applies_to_test_revision(&self, test_revision: Option<&str>) -> bool { - self.revision.is_none() || self.revision == test_revision - } -} - pub(crate) struct CheckDirectiveResult<'ln> { is_known_directive: bool, trailing_directive: Option<&'ln str>, @@ -920,8 +858,6 @@ fn check_directive<'a>( CheckDirectiveResult { is_known_directive, trailing_directive } } -const COMPILETEST_DIRECTIVE_PREFIX: &str = "//@"; - fn iter_directives( mode: TestMode, poisoned: &mut bool, diff --git a/src/tools/compiletest/src/directives/line.rs b/src/tools/compiletest/src/directives/line.rs new file mode 100644 index 000000000000..584d5b5b35be --- /dev/null +++ b/src/tools/compiletest/src/directives/line.rs @@ -0,0 +1,67 @@ +const COMPILETEST_DIRECTIVE_PREFIX: &str = "//@"; + +/// If the given line begins with the appropriate comment prefix for a directive, +/// returns a struct containing various parts of the directive. +pub(crate) fn line_directive<'line>( + line_number: usize, + original_line: &'line str, +) -> Option> { + // Ignore lines that don't start with the comment prefix. + let after_comment = + original_line.trim_start().strip_prefix(COMPILETEST_DIRECTIVE_PREFIX)?.trim_start(); + + let revision; + let raw_directive; + + if let Some(after_open_bracket) = after_comment.strip_prefix('[') { + // A comment like `//@[foo]` only applies to revision `foo`. + let Some((line_revision, after_close_bracket)) = after_open_bracket.split_once(']') else { + panic!( + "malformed condition directive: expected `{COMPILETEST_DIRECTIVE_PREFIX}[foo]`, found `{original_line}`" + ) + }; + + revision = Some(line_revision); + raw_directive = after_close_bracket.trim_start(); + } else { + revision = None; + raw_directive = after_comment; + }; + + Some(DirectiveLine { line_number, revision, raw_directive }) +} + +/// The (partly) broken-down contents of a line containing a test directive, +/// which `iter_directives` passes to its callback function. +/// +/// For example: +/// +/// ```text +/// //@ compile-flags: -O +/// ^^^^^^^^^^^^^^^^^ raw_directive +/// +/// //@ [foo] compile-flags: -O +/// ^^^ revision +/// ^^^^^^^^^^^^^^^^^ raw_directive +/// ``` +pub(crate) struct DirectiveLine<'ln> { + pub(crate) line_number: usize, + + /// Some test directives start with a revision name in square brackets + /// (e.g. `[foo]`), and only apply to that revision of the test. + /// If present, this field contains the revision name (e.g. `foo`). + pub(crate) revision: Option<&'ln str>, + + /// The main part of the directive, after removing the comment prefix + /// and the optional revision specifier. + /// + /// This is "raw" because the directive's name and colon-separated value + /// (if present) have not yet been extracted or checked. + pub(crate) raw_directive: &'ln str, +} + +impl<'ln> DirectiveLine<'ln> { + pub(crate) fn applies_to_test_revision(&self, test_revision: Option<&str>) -> bool { + self.revision.is_none() || self.revision == test_revision + } +} From 6783e9465b62e77c7e3cb76a747d92b1de4339f7 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Thu, 2 Oct 2025 20:08:40 +1000 Subject: [PATCH 168/226] Allow easy extraction of name/value from a `DirectiveLine` --- src/tools/compiletest/src/directives.rs | 10 +++-- src/tools/compiletest/src/directives/line.rs | 45 +++++++++++++++++++- 2 files changed, 50 insertions(+), 5 deletions(-) diff --git a/src/tools/compiletest/src/directives.rs b/src/tools/compiletest/src/directives.rs index edb06dd7345d..bbbeecc1b71b 100644 --- a/src/tools/compiletest/src/directives.rs +++ b/src/tools/compiletest/src/directives.rs @@ -875,15 +875,17 @@ fn iter_directives( // FIXME(jieyouxu): I feel like there's a better way to do this, leaving for later. if mode == TestMode::CoverageRun { let extra_directives: &[&str] = &[ - "needs-profiler-runtime", + "//@ needs-profiler-runtime", // FIXME(pietroalbini): this test currently does not work on cross-compiled targets // because remote-test is not capable of sending back the *.profraw files generated by // the LLVM instrumentation. - "ignore-cross-compile", + "//@ ignore-cross-compile", ]; // Process the extra implied directives, with a dummy line number of 0. - for raw_directive in extra_directives { - it(DirectiveLine { line_number: 0, revision: None, raw_directive }); + for directive_str in extra_directives { + let directive_line = line_directive(0, directive_str) + .unwrap_or_else(|| panic!("bad extra-directive line: {directive_str:?}")); + it(directive_line); } } diff --git a/src/tools/compiletest/src/directives/line.rs b/src/tools/compiletest/src/directives/line.rs index 584d5b5b35be..5c1b592d045d 100644 --- a/src/tools/compiletest/src/directives/line.rs +++ b/src/tools/compiletest/src/directives/line.rs @@ -1,3 +1,7 @@ +#![expect(dead_code)] // (removed later in this PR) + +use std::fmt; + const COMPILETEST_DIRECTIVE_PREFIX: &str = "//@"; /// If the given line begins with the appropriate comment prefix for a directive, @@ -28,7 +32,10 @@ pub(crate) fn line_directive<'line>( raw_directive = after_comment; }; - Some(DirectiveLine { line_number, revision, raw_directive }) + // The directive name ends at the first occurrence of colon, space, or end-of-string. + let name = raw_directive.split([':', ' ']).next().expect("split is never empty"); + + Some(DirectiveLine { line_number, revision, raw_directive, name }) } /// The (partly) broken-down contents of a line containing a test directive, @@ -39,10 +46,12 @@ pub(crate) fn line_directive<'line>( /// ```text /// //@ compile-flags: -O /// ^^^^^^^^^^^^^^^^^ raw_directive +/// ^^^^^^^^^^^^^ name /// /// //@ [foo] compile-flags: -O /// ^^^ revision /// ^^^^^^^^^^^^^^^^^ raw_directive +/// ^^^^^^^^^^^^^ name /// ``` pub(crate) struct DirectiveLine<'ln> { pub(crate) line_number: usize, @@ -58,10 +67,44 @@ pub(crate) struct DirectiveLine<'ln> { /// This is "raw" because the directive's name and colon-separated value /// (if present) have not yet been extracted or checked. pub(crate) raw_directive: &'ln str, + + /// Name of the directive. + /// + /// Invariant: `self.raw_directive.starts_with(self.name)` + pub(crate) name: &'ln str, } impl<'ln> DirectiveLine<'ln> { pub(crate) fn applies_to_test_revision(&self, test_revision: Option<&str>) -> bool { self.revision.is_none() || self.revision == test_revision } + + /// Helper method used by `value_after_colon` and `remark_after_space`. + /// Don't call this directly. + fn rest_after_separator(&self, separator: u8) -> Option<&'ln str> { + let n = self.name.len(); + if self.raw_directive.as_bytes().get(n) != Some(&separator) { + return None; + } + + Some(&self.raw_directive[n + 1..]) + } + + /// If this directive uses `name: value` syntax, returns the part after + /// the colon character. + pub(crate) fn value_after_colon(&self) -> Option<&'ln str> { + self.rest_after_separator(b':') + } + + /// If this directive uses `name remark` syntax, returns the part after + /// the separating space. + pub(crate) fn remark_after_space(&self) -> Option<&'ln str> { + self.rest_after_separator(b' ') + } + + /// Allows callers to print `raw_directive` if necessary, + /// without accessing the field directly. + pub(crate) fn display(&self) -> impl fmt::Display { + self.raw_directive + } } From 33c99a0468302f1bb9bd7dd0d63d460b1a88ed52 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Thu, 2 Oct 2025 20:16:59 +1000 Subject: [PATCH 169/226] Use new `DirectiveLine` features in directive parsing --- src/tools/compiletest/src/directives.rs | 100 +++++++++--------- .../compiletest/src/directives/auxiliary.rs | 4 +- src/tools/compiletest/src/directives/cfg.rs | 26 ++--- src/tools/compiletest/src/directives/line.rs | 4 +- src/tools/compiletest/src/directives/needs.rs | 15 +-- src/tools/compiletest/src/directives/tests.rs | 13 +-- 6 files changed, 73 insertions(+), 89 deletions(-) diff --git a/src/tools/compiletest/src/directives.rs b/src/tools/compiletest/src/directives.rs index bbbeecc1b71b..dab6850e0d62 100644 --- a/src/tools/compiletest/src/directives.rs +++ b/src/tools/compiletest/src/directives.rs @@ -835,9 +835,7 @@ fn check_directive<'a>( directive_ln: &DirectiveLine<'a>, mode: TestMode, ) -> CheckDirectiveResult<'a> { - let &DirectiveLine { raw_directive: directive_ln, .. } = directive_ln; - - let (directive_name, post) = directive_ln.split_once([':', ' ']).unwrap_or((directive_ln, "")); + let &DirectiveLine { name: directive_name, .. } = directive_ln; let is_known_directive = KNOWN_DIRECTIVE_NAMES.contains(&directive_name) || match mode { @@ -846,14 +844,13 @@ fn check_directive<'a>( _ => false, }; - let trailing = post.trim().split_once(' ').map(|(pre, _)| pre).unwrap_or(post); - let trailing_directive = { - // 1. is the directive name followed by a space? (to exclude `:`) - directive_ln.get(directive_name.len()..).is_some_and(|s| s.starts_with(' ')) - // 2. is what is after that directive also a directive (ex: "only-x86 only-arm") - && KNOWN_DIRECTIVE_NAMES.contains(&trailing) - } - .then_some(trailing); + // If it looks like the user tried to put two directives on the same line + // (e.g. `//@ only-linux only-x86_64`), signal an error, because the + // second "directive" would actually be ignored with no effect. + let trailing_directive = directive_ln + .remark_after_space() + .map(|remark| remark.trim_start().split(' ').next().unwrap()) + .filter(|token| KNOWN_DIRECTIVE_NAMES.contains(token)); CheckDirectiveResult { is_known_directive, trailing_directive } } @@ -915,7 +912,7 @@ fn iter_directives( error!( "{testfile}:{line_number}: detected unknown compiletest test directive `{}`", - directive_line.raw_directive, + directive_line.display(), ); return; @@ -1011,13 +1008,9 @@ impl Config { } fn parse_custom_normalization(&self, line: &DirectiveLine<'_>) -> Option { - let &DirectiveLine { raw_directive, .. } = line; + let &DirectiveLine { name, .. } = line; - // FIXME(Zalathar): Integrate name/value splitting into `DirectiveLine` - // instead of doing it here. - let (directive_name, raw_value) = raw_directive.split_once(':')?; - - let kind = match directive_name { + let kind = match name { "normalize-stdout" => NormalizeKind::Stdout, "normalize-stderr" => NormalizeKind::Stderr, "normalize-stderr-32bit" => NormalizeKind::Stderr32bit, @@ -1025,21 +1018,20 @@ impl Config { _ => return None, }; - let Some((regex, replacement)) = parse_normalize_rule(raw_value) else { - error!("couldn't parse custom normalization rule: `{raw_directive}`"); - help!("expected syntax is: `{directive_name}: \"REGEX\" -> \"REPLACEMENT\"`"); + let Some((regex, replacement)) = line.value_after_colon().and_then(parse_normalize_rule) + else { + error!("couldn't parse custom normalization rule: `{}`", line.display()); + help!("expected syntax is: `{name}: \"REGEX\" -> \"REPLACEMENT\"`"); panic!("invalid normalization rule detected"); }; Some(NormalizeRule { kind, regex, replacement }) } fn parse_name_directive(&self, line: &DirectiveLine<'_>, directive: &str) -> bool { - let &DirectiveLine { raw_directive: line, .. } = line; - - // Ensure the directive is a whole word. Do not match "ignore-x86" when - // the line says "ignore-x86_64". - line.starts_with(directive) - && matches!(line.as_bytes().get(directive.len()), None | Some(&b' ') | Some(&b':')) + // FIXME(Zalathar): Ideally, this should raise an error if a name-only + // directive is followed by a colon, since that's the wrong syntax. + // But we would need to fix tests that rely on the current behaviour. + line.name == directive } fn parse_name_value_directive( @@ -1048,22 +1040,26 @@ impl Config { directive: &str, testfile: &Utf8Path, ) -> Option { - let &DirectiveLine { line_number, raw_directive: line, .. } = line; + let &DirectiveLine { line_number, .. } = line; - let colon = directive.len(); - if line.starts_with(directive) && line.as_bytes().get(colon) == Some(&b':') { - let value = line[(colon + 1)..].to_owned(); - debug!("{}: {}", directive, value); - let value = expand_variables(value, self); - if value.is_empty() { - error!("{testfile}:{line_number}: empty value for directive `{directive}`"); - help!("expected syntax is: `{directive}: value`"); - panic!("empty directive value detected"); - } - Some(value) - } else { - None + if line.name != directive { + return None; + }; + + // FIXME(Zalathar): This silently discards directives with a matching + // name but no colon. Unfortunately, some directives (e.g. "pp-exact") + // currently rely on _not_ panicking here. + let value = line.value_after_colon()?; + debug!("{}: {}", directive, value); + let value = expand_variables(value.to_owned(), self); + + if value.is_empty() { + error!("{testfile}:{line_number}: empty value for directive `{directive}`"); + help!("expected syntax is: `{directive}: value`"); + panic!("empty directive value detected"); } + + Some(value) } fn set_name_directive(&self, line: &DirectiveLine<'_>, directive: &str, value: &mut bool) { @@ -1453,14 +1449,14 @@ pub(crate) fn make_test_description( } fn ignore_cdb(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision { - let &DirectiveLine { raw_directive: line, .. } = line; - if config.debugger != Some(Debugger::Cdb) { return IgnoreDecision::Continue; } if let Some(actual_version) = config.cdb_version { - if let Some(rest) = line.strip_prefix("min-cdb-version:").map(str::trim) { + if line.name == "min-cdb-version" + && let Some(rest) = line.value_after_colon().map(str::trim) + { let min_version = extract_cdb_version(rest).unwrap_or_else(|| { panic!("couldn't parse version range: {:?}", rest); }); @@ -1478,14 +1474,14 @@ fn ignore_cdb(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision { } fn ignore_gdb(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision { - let &DirectiveLine { raw_directive: line, .. } = line; - if config.debugger != Some(Debugger::Gdb) { return IgnoreDecision::Continue; } if let Some(actual_version) = config.gdb_version { - if let Some(rest) = line.strip_prefix("min-gdb-version:").map(str::trim) { + if line.name == "min-gdb-version" + && let Some(rest) = line.value_after_colon().map(str::trim) + { let (start_ver, end_ver) = extract_version_range(rest, extract_gdb_version) .unwrap_or_else(|| { panic!("couldn't parse version range: {:?}", rest); @@ -1501,7 +1497,9 @@ fn ignore_gdb(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision { reason: format!("ignored when the GDB version is lower than {rest}"), }; } - } else if let Some(rest) = line.strip_prefix("ignore-gdb-version:").map(str::trim) { + } else if line.name == "ignore-gdb-version" + && let Some(rest) = line.value_after_colon().map(str::trim) + { let (min_version, max_version) = extract_version_range(rest, extract_gdb_version) .unwrap_or_else(|| { panic!("couldn't parse version range: {:?}", rest); @@ -1528,14 +1526,14 @@ fn ignore_gdb(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision { } fn ignore_lldb(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision { - let &DirectiveLine { raw_directive: line, .. } = line; - if config.debugger != Some(Debugger::Lldb) { return IgnoreDecision::Continue; } if let Some(actual_version) = config.lldb_version { - if let Some(rest) = line.strip_prefix("min-lldb-version:").map(str::trim) { + if line.name == "min-lldb-version" + && let Some(rest) = line.value_after_colon().map(str::trim) + { let min_version = rest.parse().unwrap_or_else(|e| { panic!("Unexpected format of LLDB version string: {}\n{:?}", rest, e); }); diff --git a/src/tools/compiletest/src/directives/auxiliary.rs b/src/tools/compiletest/src/directives/auxiliary.rs index 0675a6feac3f..7cf98178e733 100644 --- a/src/tools/compiletest/src/directives/auxiliary.rs +++ b/src/tools/compiletest/src/directives/auxiliary.rs @@ -50,9 +50,7 @@ pub(super) fn parse_and_update_aux( testfile: &Utf8Path, aux: &mut AuxProps, ) { - let &DirectiveLine { raw_directive: ln, .. } = directive_line; - - if !(ln.starts_with("aux-") || ln.starts_with("proc-macro")) { + if !(directive_line.name.starts_with("aux-") || directive_line.name == "proc-macro") { return; } diff --git a/src/tools/compiletest/src/directives/cfg.rs b/src/tools/compiletest/src/directives/cfg.rs index 62a4b88a33a6..3855ba7ac5f4 100644 --- a/src/tools/compiletest/src/directives/cfg.rs +++ b/src/tools/compiletest/src/directives/cfg.rs @@ -6,8 +6,8 @@ use crate::directives::{DirectiveLine, IgnoreDecision}; const EXTRA_ARCHS: &[&str] = &["spirv"]; pub(super) fn handle_ignore(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision { - let parsed = parse_cfg_name_directive(config, line, "ignore"); - let &DirectiveLine { raw_directive: line, .. } = line; + let parsed = parse_cfg_name_directive(config, line, "ignore-"); + let line = line.display(); match parsed.outcome { MatchOutcome::NoMatch => IgnoreDecision::Continue, @@ -24,8 +24,8 @@ pub(super) fn handle_ignore(config: &Config, line: &DirectiveLine<'_>) -> Ignore } pub(super) fn handle_only(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision { - let parsed = parse_cfg_name_directive(config, line, "only"); - let &DirectiveLine { raw_directive: line, .. } = line; + let parsed = parse_cfg_name_directive(config, line, "only-"); + let line = line.display(); match parsed.outcome { MatchOutcome::Match => IgnoreDecision::Continue, @@ -50,18 +50,14 @@ fn parse_cfg_name_directive<'a>( line: &'a DirectiveLine<'a>, prefix: &str, ) -> ParsedNameDirective<'a> { - let &DirectiveLine { raw_directive: line, .. } = line; - - if !line.as_bytes().starts_with(prefix.as_bytes()) { + let Some(name) = line.name.strip_prefix(prefix) else { return ParsedNameDirective::not_a_directive(); - } - if line.as_bytes().get(prefix.len()) != Some(&b'-') { - return ParsedNameDirective::not_a_directive(); - } - let line = &line[prefix.len() + 1..]; + }; - let (name, comment) = - line.split_once(&[':', ' ']).map(|(l, c)| (l, Some(c))).unwrap_or((line, None)); + // FIXME(Zalathar): This currently allows either a space or a colon, and + // treats any "value" after a colon as though it were a remark. + // We should instead forbid the colon syntax for these directives. + let comment = line.remark_after_space().or_else(|| line.value_after_colon()); // Some of the matchers might be "" depending on what the target information is. To avoid // problems we outright reject empty directives. @@ -269,7 +265,7 @@ fn parse_cfg_name_directive<'a>( message: "when performing tests on dist toolchain" } - if prefix == "ignore" && outcome == MatchOutcome::Invalid { + if prefix == "ignore-" && outcome == MatchOutcome::Invalid { // Don't error out for ignore-tidy-* diretives, as those are not handled by compiletest. if name.starts_with("tidy-") { outcome = MatchOutcome::External; diff --git a/src/tools/compiletest/src/directives/line.rs b/src/tools/compiletest/src/directives/line.rs index 5c1b592d045d..49907207d2e6 100644 --- a/src/tools/compiletest/src/directives/line.rs +++ b/src/tools/compiletest/src/directives/line.rs @@ -1,5 +1,3 @@ -#![expect(dead_code)] // (removed later in this PR) - use std::fmt; const COMPILETEST_DIRECTIVE_PREFIX: &str = "//@"; @@ -66,7 +64,7 @@ pub(crate) struct DirectiveLine<'ln> { /// /// This is "raw" because the directive's name and colon-separated value /// (if present) have not yet been extracted or checked. - pub(crate) raw_directive: &'ln str, + raw_directive: &'ln str, /// Name of the directive. /// diff --git a/src/tools/compiletest/src/directives/needs.rs b/src/tools/compiletest/src/directives/needs.rs index 9d72492e5b07..5e9fe59d8d1c 100644 --- a/src/tools/compiletest/src/directives/needs.rs +++ b/src/tools/compiletest/src/directives/needs.rs @@ -181,17 +181,10 @@ pub(super) fn handle_needs( }, ]; - let &DirectiveLine { raw_directive: ln, .. } = ln; + let &DirectiveLine { name, .. } = ln; - let (name, rest) = match ln.split_once([':', ' ']) { - Some((name, rest)) => (name, Some(rest)), - None => (ln, None), - }; - - // FIXME(jieyouxu): tighten up this parsing to reject using both `:` and ` ` as means to - // delineate value. if name == "needs-target-has-atomic" { - let Some(rest) = rest else { + let Some(rest) = ln.value_after_colon() else { return IgnoreDecision::Error { message: "expected `needs-target-has-atomic` to have a comma-separated list of atomic widths".to_string(), }; @@ -233,7 +226,7 @@ pub(super) fn handle_needs( // FIXME(jieyouxu): share multi-value directive logic with `needs-target-has-atomic` above. if name == "needs-crate-type" { - let Some(rest) = rest else { + let Some(rest) = ln.value_after_colon() else { return IgnoreDecision::Error { message: "expected `needs-crate-type` to have a comma-separated list of crate types" @@ -292,7 +285,7 @@ pub(super) fn handle_needs( break; } else { return IgnoreDecision::Ignore { - reason: if let Some(comment) = rest { + reason: if let Some(comment) = ln.remark_after_space() { format!("{} ({})", need.ignore_reason, comment.trim()) } else { need.ignore_reason.into() diff --git a/src/tools/compiletest/src/directives/tests.rs b/src/tools/compiletest/src/directives/tests.rs index 93621192d4bd..95dd46532ba8 100644 --- a/src/tools/compiletest/src/directives/tests.rs +++ b/src/tools/compiletest/src/directives/tests.rs @@ -3,12 +3,11 @@ use std::io::Read; use camino::Utf8Path; use semver::Version; -use super::{ - DirectivesCache, EarlyProps, Edition, EditionRange, extract_llvm_version, - extract_version_range, iter_directives, parse_normalize_rule, -}; use crate::common::{Config, Debugger, TestMode}; -use crate::directives::parse_edition; +use crate::directives::{ + DirectivesCache, EarlyProps, Edition, EditionRange, extract_llvm_version, + extract_version_range, iter_directives, line_directive, parse_edition, parse_normalize_rule, +}; use crate::executor::{CollectedTestDesc, ShouldPanic}; fn make_test_description( @@ -955,7 +954,9 @@ fn test_needs_target_std() { fn parse_edition_range(line: &str) -> Option { let config = cfg().build(); - let line = super::DirectiveLine { line_number: 0, revision: None, raw_directive: line }; + + let line_with_comment = format!("//@ {line}"); + let line = line_directive(0, &line_with_comment).unwrap(); super::parse_edition_range(&config, &line, "tmp.rs".into()) } From 34ad9198584b2245024e8735809e6a3567e504cb Mon Sep 17 00:00:00 2001 From: yukang Date: Sun, 5 Oct 2025 11:11:47 +0800 Subject: [PATCH 170/226] Remove extra space for missing associated type term --- compiler/rustc_parse/src/parser/path.rs | 19 ++++++++----------- .../recover-assoc-eq-missing-term.stderr | 2 +- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index 860456488591..f7757921cd42 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -753,18 +753,13 @@ impl<'a> Parser<'a> { } let kind = if self.eat(exp!(Colon)) { AssocItemConstraintKind::Bound { bounds: self.parse_generic_bounds()? } - } else if self.eat(exp!(Eq)) { - self.parse_assoc_equality_term( - ident, - gen_args.as_ref(), - self.prev_token.span, - )? + } else if self.check(exp!(Eq)) { + self.parse_assoc_equality_term(ident, gen_args.as_ref())? } else { unreachable!(); }; let span = lo.to(self.prev_token.span); - let constraint = AssocItemConstraint { id: ast::DUMMY_NODE_ID, ident, gen_args, kind, span }; Ok(Some(AngleBracketedArg::Constraint(constraint))) @@ -792,8 +787,10 @@ impl<'a> Parser<'a> { &mut self, ident: Ident, gen_args: Option<&GenericArgs>, - eq: Span, ) -> PResult<'a, AssocItemConstraintKind> { + let prev_token_span = self.prev_token.span; + let eq_span = self.token.span; + self.expect(exp!(Eq))?; let arg = self.parse_generic_arg(None)?; let span = ident.span.to(self.prev_token.span); let term = match arg { @@ -814,20 +811,20 @@ impl<'a> Parser<'a> { self.mk_ty(lt.ident.span, ast::TyKind::Err(guar)).into() } None => { - let after_eq = eq.shrink_to_hi(); + let after_eq = eq_span.shrink_to_hi(); let before_next = self.token.span.shrink_to_lo(); let mut err = self .dcx() .struct_span_err(after_eq.to(before_next), "missing type to the right of `=`"); if matches!(self.token.kind, token::Comma | token::Gt) { err.span_suggestion( - self.psess.source_map().next_point(eq).to(before_next), + self.psess.source_map().next_point(eq_span).to(before_next), "to constrain the associated type, add a type after `=`", " TheType", Applicability::HasPlaceholders, ); err.span_suggestion( - eq.to(before_next), + prev_token_span.shrink_to_hi().to(before_next), format!("remove the `=` if `{ident}` is a type"), "", Applicability::MaybeIncorrect, diff --git a/tests/ui/parser/recover/recover-assoc-eq-missing-term.stderr b/tests/ui/parser/recover/recover-assoc-eq-missing-term.stderr index cf50c026665b..2d8ab7a3e2f7 100644 --- a/tests/ui/parser/recover/recover-assoc-eq-missing-term.stderr +++ b/tests/ui/parser/recover/recover-assoc-eq-missing-term.stderr @@ -11,7 +11,7 @@ LL | bar::(); help: remove the `=` if `Item` is a type | LL - bar::(); -LL + bar::(); +LL + bar::(); | error: aborting due to 1 previous error From a175921d5e67997e359e71dc1602bd1e3547c0d7 Mon Sep 17 00:00:00 2001 From: Manuel Drehwald Date: Sat, 4 Oct 2025 23:52:33 -0400 Subject: [PATCH 171/226] update libEnzyme file name, and add missing strict-aliasing flag --- src/doc/rustc-dev-guide/src/autodiff/debugging.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/autodiff/debugging.md b/src/doc/rustc-dev-guide/src/autodiff/debugging.md index bd46a66fade4..a66d775d9826 100644 --- a/src/doc/rustc-dev-guide/src/autodiff/debugging.md +++ b/src/doc/rustc-dev-guide/src/autodiff/debugging.md @@ -16,7 +16,7 @@ Before generating the llvm-ir, keep in mind two techniques that can help ensure ## 1) Generate an llvm-ir reproducer ```sh -rustflags="-z autodiff=enable,printmodbefore" cargo +enzyme build --release &> out.ll +RUSTFLAGS="-Z autodiff=Enable,PrintModbefore" cargo +enzyme build --release &> out.ll ``` This also captures a few warnings and info messages above and below your module. open out.ll and remove every line above `; moduleid = `. Now look at the end of the file and remove everything that's not part of llvm-ir, i.e. remove errors and warnings. The last line of your llvm-ir should now start with `! = `, i.e. `!40831 = !{i32 0, i32 1037508, i32 1037538, i32 1037559}` or `!43760 = !dilocation(line: 297, column: 5, scope: !43746)`. @@ -25,10 +25,10 @@ The actual numbers will depend on your code. ## 2) Check your llvm-ir reproducer -To confirm that your previous step worked, we will use llvm's `opt` tool. find your path to the opt binary, with a path similar to `/rust/build//build/bin/opt`. also find `llvmenzyme-19.` path, similar to `/rust/build/target-tripple/enzyme/build/enzyme/llvmenzyme-19`. Please keep in mind that llvm frequently updates it's llvm backend, so the version number might be higher (20, 21, ...). Once you have both, run the following command: +To confirm that your previous step worked, we will use llvm's `opt` tool. find your path to the opt binary, with a path similar to `/rust/build//build/bin/opt`. also find `llvmenzyme-19.` path, similar to `/rust/build/target-triple/enzyme/build/enzyme/llvmenzyme-19`. Please keep in mind that llvm frequently updates it's llvm backend, so the version number might be higher (20, 21, ...). Once you have both, run the following command: ```sh - out.ll -load-pass-plugin=/path/to/llvmenzyme-19.so -passes="enzyme" -s + out.ll -load-pass-plugin=/path/to/build//stage1/lib/libEnzyme-21.so -passes="enzyme" -enzyme-strict-aliasing=0 -s ``` If the previous step succeeded, you are going to see the same error that you saw when compiling your rust code with cargo. From 765e2fe6eed09e4fb61702ede01362336b6250e7 Mon Sep 17 00:00:00 2001 From: Manuel Drehwald Date: Sun, 5 Oct 2025 01:38:29 -0400 Subject: [PATCH 172/226] Add potential instability warning --- src/doc/rustc-dev-guide/src/autodiff/debugging.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/doc/rustc-dev-guide/src/autodiff/debugging.md b/src/doc/rustc-dev-guide/src/autodiff/debugging.md index a66d775d9826..97893535cfe5 100644 --- a/src/doc/rustc-dev-guide/src/autodiff/debugging.md +++ b/src/doc/rustc-dev-guide/src/autodiff/debugging.md @@ -30,6 +30,7 @@ To confirm that your previous step worked, we will use llvm's `opt` tool. find y ```sh out.ll -load-pass-plugin=/path/to/build//stage1/lib/libEnzyme-21.so -passes="enzyme" -enzyme-strict-aliasing=0 -s ``` +This command might fail for future versions or on your system, in which case you should replace libEnzyme-21.so with LLVMEnzyme-21.so. Look at the Enzyme docs for instructions on how to build it. You might need to also adjust how to build your LLVM version. If the previous step succeeded, you are going to see the same error that you saw when compiling your rust code with cargo. From 7c8fe29fd65d994e25a8eed3d0e2bbed3415ca61 Mon Sep 17 00:00:00 2001 From: Manuel Drehwald Date: Sun, 5 Oct 2025 03:07:51 -0400 Subject: [PATCH 173/226] solve autodiffv2.rs FIXME and make identical_fnc test more robust --- tests/codegen-llvm/autodiff/autodiffv2.rs | 31 ++++++++------------ tests/codegen-llvm/autodiff/identical_fnc.rs | 4 +-- 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/tests/codegen-llvm/autodiff/autodiffv2.rs b/tests/codegen-llvm/autodiff/autodiffv2.rs index 85aed6a183b6..c24a374148c3 100644 --- a/tests/codegen-llvm/autodiff/autodiffv2.rs +++ b/tests/codegen-llvm/autodiff/autodiffv2.rs @@ -18,11 +18,6 @@ // but each shadow argument is `width` times larger (thus 16 and 20 elements here). // `d_square3` instead takes `width` (4) shadow arguments, which are all the same size as the // original function arguments. -// -// FIXME(autodiff): We currently can't test `d_square1` and `d_square3` in the same file, since they -// generate the same dummy functions which get merged by LLVM, breaking pieces of our pipeline which -// try to rewrite the dummy functions later. We should consider to change to pure declarations both -// in our frontend and in the llvm backend to avoid these issues. #![feature(autodiff)] @@ -30,7 +25,7 @@ use std::autodiff::autodiff_forward; // CHECK: ; #[no_mangle] -//#[autodiff(d_square1, Forward, Dual, Dual)] +#[autodiff_forward(d_square1, Dual, Dual)] #[autodiff_forward(d_square2, 4, Dualv, Dualv)] #[autodiff_forward(d_square3, 4, Dual, Dual)] fn square(x: &[f32], y: &mut [f32]) { @@ -79,25 +74,25 @@ fn main() { let mut dy3_4 = std::hint::black_box(vec![0.0; 5]); // scalar. - //d_square1(&x1, &z1, &mut y1, &mut dy1_1); - //d_square1(&x1, &z2, &mut y2, &mut dy1_2); - //d_square1(&x1, &z3, &mut y3, &mut dy1_3); - //d_square1(&x1, &z4, &mut y4, &mut dy1_4); + d_square1(&x1, &z1, &mut y1, &mut dy1_1); + d_square1(&x1, &z2, &mut y2, &mut dy1_2); + d_square1(&x1, &z3, &mut y3, &mut dy1_3); + d_square1(&x1, &z4, &mut y4, &mut dy1_4); // assert y1 == y2 == y3 == y4 - //for i in 0..5 { - // assert_eq!(y1[i], y2[i]); - // assert_eq!(y1[i], y3[i]); - // assert_eq!(y1[i], y4[i]); - //} + for i in 0..5 { + assert_eq!(y1[i], y2[i]); + assert_eq!(y1[i], y3[i]); + assert_eq!(y1[i], y4[i]); + } // batch mode A) d_square2(&x1, &z5, &mut y5, &mut dy2); // assert y1 == y2 == y3 == y4 == y5 - //for i in 0..5 { - // assert_eq!(y1[i], y5[i]); - //} + for i in 0..5 { + assert_eq!(y1[i], y5[i]); + } // batch mode B) d_square3(&x1, &z1, &z2, &z3, &z4, &mut y6, &mut dy3_1, &mut dy3_2, &mut dy3_3, &mut dy3_4); diff --git a/tests/codegen-llvm/autodiff/identical_fnc.rs b/tests/codegen-llvm/autodiff/identical_fnc.rs index 6066f8cb34fb..1c18e7acc4b6 100644 --- a/tests/codegen-llvm/autodiff/identical_fnc.rs +++ b/tests/codegen-llvm/autodiff/identical_fnc.rs @@ -32,9 +32,9 @@ fn square2(x: &f64) -> f64 { // CHECK-NOT:br // CHECK-NOT:ret // CHECK:; call identical_fnc::d_square -// CHECK-NEXT:call fastcc void @_ZN13identical_fnc8d_square17hcb5768e95528c35fE(double %x.val, ptr noalias noundef align 8 dereferenceable(8) %dx1) +// CHECK-NEXT:call fastcc void @_ZN13identical_fnc8d_square[[HASH:.+]](double %x.val, ptr noalias noundef align 8 dereferenceable(8) %dx1) // CHECK:; call identical_fnc::d_square -// CHECK-NEXT:call fastcc void @_ZN13identical_fnc8d_square17hcb5768e95528c35fE(double %x.val, ptr noalias noundef align 8 dereferenceable(8) %dx2) +// CHECK-NEXT:call fastcc void @_ZN13identical_fnc8d_square[[HASH]](double %x.val, ptr noalias noundef align 8 dereferenceable(8) %dx2) fn main() { let x = std::hint::black_box(3.0); From 99c7959bb7fde07f1326c52312c12671239849b7 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 4 Aug 2025 16:59:58 +0200 Subject: [PATCH 174/226] Add regression test for rustdoc output format --- .../rustdoc-doctest-output-format/file.rs | 3 + .../rustdoc-doctest-output-format/rmake.rs | 83 +++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 tests/run-make/rustdoc-doctest-output-format/file.rs create mode 100644 tests/run-make/rustdoc-doctest-output-format/rmake.rs diff --git a/tests/run-make/rustdoc-doctest-output-format/file.rs b/tests/run-make/rustdoc-doctest-output-format/file.rs new file mode 100644 index 000000000000..51d17849fd71 --- /dev/null +++ b/tests/run-make/rustdoc-doctest-output-format/file.rs @@ -0,0 +1,3 @@ +//! ``` +//! let x = 12; +//! ``` diff --git a/tests/run-make/rustdoc-doctest-output-format/rmake.rs b/tests/run-make/rustdoc-doctest-output-format/rmake.rs new file mode 100644 index 000000000000..61b5c0c4fd11 --- /dev/null +++ b/tests/run-make/rustdoc-doctest-output-format/rmake.rs @@ -0,0 +1,83 @@ +//! Regression test to ensure that the output format is respected for doctests. +//! +//! Regression test for . + +//@ ignore-cross-compile + +use run_make_support::{rustdoc, serde_json}; + +fn run_test(edition: &str, format: Option<&str>) -> String { + let mut r = rustdoc(); + r.input("file.rs").edition(edition).arg("--test"); + if let Some(format) = format { + r.args(&[ + "--test-args", + "-Zunstable-options", + "--test-args", + "--format", + "--test-args", + format, + ]); + } + r.run().stdout_utf8() +} + +fn check_json_output(edition: &str, expected_reports: usize) { + let out = run_test(edition, Some("json")); + let mut found_report = 0; + for (line_nb, line) in out.lines().enumerate() { + match serde_json::from_str::(&line) { + Ok(value) => { + if value.get("type") == Some(&serde_json::json!("report")) { + found_report += 1; + } + } + Err(error) => panic!( + "failed for {edition} edition (json format) at line {}: non-JSON value: {error}\n\ + ====== output ======\n{out}", + line_nb + 1, + ), + } + } + if found_report != expected_reports { + panic!( + "failed for {edition} edition (json format): expected {expected_reports} doctest \ + time `report`, found {found_report}\n====== output ======\n{out}", + ); + } +} + +fn check_non_json_output(edition: &str, expected_reports: usize) { + let out = run_test(edition, None); + let mut found_report = 0; + for (line_nb, line) in out.lines().enumerate() { + if line.starts_with('{') && serde_json::from_str::(&line).is_ok() { + panic!( + "failed for {edition} edition: unexpected json at line {}: `{line}`\n\ + ====== output ======\n{out}", + line_nb + 1 + ); + } + if line.starts_with("all doctests ran in") + && line.contains("; merged doctests compilation took ") + { + found_report += 1; + } + } + if found_report != expected_reports { + panic!( + "failed for {edition} edition: expected {expected_reports} doctest time `report`, \ + found {found_report}\n====== output ======\n{out}", + ); + } +} + +fn main() { + // Only the merged doctests generate the "times report". + check_json_output("2021", 0); + check_json_output("2024", 1); + + // Only the merged doctests generate the "times report". + check_non_json_output("2021", 0); + check_non_json_output("2024", 1); +} From 95445f9b9652c24a48493bafdc5a8fce12927aec Mon Sep 17 00:00:00 2001 From: yukang Date: Sun, 5 Oct 2025 17:27:45 +0800 Subject: [PATCH 175/226] Trivial code cleanup in resolve --- compiler/rustc_resolve/src/imports.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index ce90a1bcd313..04eb312a0f8b 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -741,14 +741,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { errors.retain(|(_import, err)| match err.module { // Skip `use` errors for `use foo::Bar;` if `foo.rs` has unrecovered parse errors. Some(def_id) if self.mods_with_parse_errors.contains(&def_id) => false, - _ => true, - }); - errors.retain(|(_import, err)| { // If we've encountered something like `use _;`, we've already emitted an error stating // that `_` is not a valid identifier, so we ignore that resolve error. - err.segment != Some(kw::Underscore) + _ => err.segment != Some(kw::Underscore), }); - if errors.is_empty() { self.tcx.dcx().delayed_bug("expected a parse or \"`_` can't be an identifier\" error"); return; From 9ccaf080cbef74aae96760564fd23447d5aa906b Mon Sep 17 00:00:00 2001 From: nora <48135649+Noratrieb@users.noreply.github.com> Date: Sun, 5 Oct 2025 12:01:23 +0200 Subject: [PATCH 176/226] Fill out AVR target metadata This will make `-Zbuild-std` automatically build the right crates, notably not building `std` by default, which will both be useful for users and also fix the build for https://does-it-build.noratrieb.dev. --- compiler/rustc_target/src/spec/targets/avr_none.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_target/src/spec/targets/avr_none.rs b/compiler/rustc_target/src/spec/targets/avr_none.rs index ad056d023267..65a171e4515a 100644 --- a/compiler/rustc_target/src/spec/targets/avr_none.rs +++ b/compiler/rustc_target/src/spec/targets/avr_none.rs @@ -5,9 +5,9 @@ pub(crate) fn target() -> Target { arch: "avr".into(), metadata: crate::spec::TargetMetadata { description: None, - tier: None, - host_tools: None, - std: None, + tier: Some(3), + host_tools: Some(false), + std: Some(false), }, data_layout: "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8:16-a:8".into(), llvm_target: "avr-unknown-unknown".into(), From a316f5b72e6a9277f0fb912eab8e7a47074bb500 Mon Sep 17 00:00:00 2001 From: Karol Zwolak Date: Sun, 5 Oct 2025 12:57:48 +0200 Subject: [PATCH 177/226] don't make empty ident when printing `'` ident from `extern "'"` --- compiler/rustc_span/src/symbol.rs | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 083e04730bc3..35786d893287 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -2463,9 +2463,10 @@ pub const STDLIB_STABLE_CRATES: &[Symbol] = &[sym::std, sym::core, sym::alloc, s #[derive(Copy, Clone, Eq, HashStable_Generic, Encodable, Decodable)] pub struct Ident { - // `name` should never be the empty symbol. If you are considering that, - // you are probably conflating "empty identifier with "no identifier" and - // you should use `Option` instead. + /// `name` should never be the empty symbol. If you are considering that, + /// you are probably conflating "empty identifier with "no identifier" and + /// you should use `Option` instead. + /// Trying to construct an `Ident` with an empty name will trigger debug assertions. pub name: Symbol, pub span: Span, } @@ -2508,6 +2509,8 @@ impl Ident { Ident::new(self.name, span.with_ctxt(self.span.ctxt())) } + /// Creates a new ident with the same span and name with leading quote removed, if any. + /// If called on an empty ident, or with name just a single quote, returns an empty ident which is invalid. pub fn without_first_quote(self) -> Ident { Ident::new(Symbol::intern(self.as_str().trim_start_matches('\'')), self.span) } @@ -3095,10 +3098,15 @@ impl Ident { } pub fn is_raw_lifetime_guess(self) -> bool { - let name_without_apostrophe = self.without_first_quote(); - name_without_apostrophe.name != self.name - && name_without_apostrophe.name.can_be_raw() - && name_without_apostrophe.is_reserved_lifetime() + // Check that the name isn't just a single quote. + // `self.without_first_quote()` would return empty ident, which triggers debug assert. + if self.name.as_str() == "'" { + return false; + } + let ident_without_apostrophe = self.without_first_quote(); + ident_without_apostrophe.name != self.name + && ident_without_apostrophe.name.can_be_raw() + && ident_without_apostrophe.is_reserved_lifetime() } pub fn guess_print_mode(self) -> IdentPrintMode { From 87e8ec8114532e252fb93df5a115f7fb91c6d668 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 5 Oct 2025 14:12:49 +0200 Subject: [PATCH 178/226] power align: format test file --- tests/ui/layout/reprc-power-alignment.rs | 25 +++++++++---------- tests/ui/layout/reprc-power-alignment.stderr | 26 ++++++++++---------- 2 files changed, 25 insertions(+), 26 deletions(-) diff --git a/tests/ui/layout/reprc-power-alignment.rs b/tests/ui/layout/reprc-power-alignment.rs index f144094d43fb..3456c4ef8029 100644 --- a/tests/ui/layout/reprc-power-alignment.rs +++ b/tests/ui/layout/reprc-power-alignment.rs @@ -5,12 +5,12 @@ #![feature(no_core)] #![no_core] #![no_std] +#![crate_type = "lib"] extern crate minicore; use minicore::*; #[warn(uses_power_alignment)] - #[repr(C)] pub struct Floats { a: f64, @@ -96,32 +96,32 @@ pub struct FloatAgg7 { #[repr(C)] pub struct A { - d: f64, + d: f64, } #[repr(C)] pub struct B { - a: A, - f: f32, - d: f64, //~ WARNING repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type + a: A, + f: f32, + d: f64, //~ WARNING repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type } #[repr(C)] pub struct C { - c: u8, - b: B, //~ WARNING repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type + c: u8, + b: B, //~ WARNING repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type } #[repr(C)] pub struct D { - x: f64, + x: f64, } #[repr(C)] pub struct E { - x: i32, - d: D, //~ WARNING repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type + x: i32, + d: D, //~ WARNING repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type } #[repr(C)] pub struct F { - a: u8, - b: f64, //~ WARNING repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type + a: u8, + b: f64, //~ WARNING repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type } #[repr(C)] pub struct G { @@ -173,4 +173,3 @@ pub struct M { b: K, c: L, } -fn main() { } diff --git a/tests/ui/layout/reprc-power-alignment.stderr b/tests/ui/layout/reprc-power-alignment.stderr index 18664e4d655d..2f4612f3ff8f 100644 --- a/tests/ui/layout/reprc-power-alignment.stderr +++ b/tests/ui/layout/reprc-power-alignment.stderr @@ -5,7 +5,7 @@ LL | c: f64, | ^^^^^^ | note: the lint level is defined here - --> $DIR/reprc-power-alignment.rs:12:8 + --> $DIR/reprc-power-alignment.rs:13:8 | LL | #[warn(uses_power_alignment)] | ^^^^^^^^^^^^^^^^^^^^ @@ -73,28 +73,28 @@ LL | y: Floats, | ^^^^^^^^^ warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type - --> $DIR/reprc-power-alignment.rs:105:3 + --> $DIR/reprc-power-alignment.rs:105:5 | -LL | d: f64, - | ^^^^^^ +LL | d: f64, + | ^^^^^^ warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type - --> $DIR/reprc-power-alignment.rs:110:3 + --> $DIR/reprc-power-alignment.rs:110:5 | -LL | b: B, - | ^^^^ +LL | b: B, + | ^^^^ warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type - --> $DIR/reprc-power-alignment.rs:119:3 + --> $DIR/reprc-power-alignment.rs:119:5 | -LL | d: D, - | ^^^^ +LL | d: D, + | ^^^^ warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type - --> $DIR/reprc-power-alignment.rs:124:3 + --> $DIR/reprc-power-alignment.rs:124:5 | -LL | b: f64, - | ^^^^^^ +LL | b: f64, + | ^^^^^^ warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type --> $DIR/reprc-power-alignment.rs:130:5 From 5a0ea3bfba56e4d1c768e25fa30088964b38277f Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 5 Oct 2025 14:18:17 +0200 Subject: [PATCH 179/226] power align: ignore repr(C) unions and enums --- .../rustc_lint/src/types/improper_ctypes.rs | 20 +++++++++---------- tests/ui/layout/reprc-power-alignment.rs | 16 +++++++++++++++ 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_lint/src/types/improper_ctypes.rs b/compiler/rustc_lint/src/types/improper_ctypes.rs index 7ca57b0094ee..2c8839708669 100644 --- a/compiler/rustc_lint/src/types/improper_ctypes.rs +++ b/compiler/rustc_lint/src/types/improper_ctypes.rs @@ -220,21 +220,21 @@ fn check_struct_for_power_alignment<'tcx>( adt_def: AdtDef<'tcx>, ) { let tcx = cx.tcx; - // repr(C) structs also with packed or aligned representation - // should be ignored. - if adt_def.repr().c() - && !adt_def.repr().packed() - && adt_def.repr().align.is_none() - && tcx.sess.target.os == "aix" - && !adt_def.all_fields().next().is_none() - { + + // Only consider structs (not enums or unions) on AIX. + if tcx.sess.target.os != "aix" || !adt_def.is_struct() { + return; + } + + // The struct must be repr(C), but ignore it if it explicitly specifies its alignment with + // either `align(N)` or `packed(N)`. + if adt_def.repr().c() && !adt_def.repr().packed() && adt_def.repr().align.is_none() { let struct_variant_data = item.expect_struct().2; for field_def in struct_variant_data.fields().iter().skip(1) { // Struct fields (after the first field) are checked for the // power alignment rule, as fields after the first are likely // to be the fields that are misaligned. - let def_id = field_def.def_id; - let ty = tcx.type_of(def_id).instantiate_identity(); + let ty = tcx.type_of(field_def.def_id).instantiate_identity(); if check_arg_for_power_alignment(cx, ty) { cx.emit_span_lint(USES_POWER_ALIGNMENT, field_def.span, UsesPowerAlignment); } diff --git a/tests/ui/layout/reprc-power-alignment.rs b/tests/ui/layout/reprc-power-alignment.rs index 3456c4ef8029..ffe311a9a2a8 100644 --- a/tests/ui/layout/reprc-power-alignment.rs +++ b/tests/ui/layout/reprc-power-alignment.rs @@ -173,3 +173,19 @@ pub struct M { b: K, c: L, } + +// The lint ignores unions +#[repr(C)] +pub union Union { + a: f64, + b: u8, + c: f64, + d: f32, +} + +// The lint ignores enums +#[repr(C)] +pub enum Enum { + A { a: f64, b: u8, c: f64, d: f32 }, + B, +} From 7ed476f51fe6d3240abb4d54435dc12c9da0b91d Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Sun, 5 Oct 2025 19:58:56 +0800 Subject: [PATCH 180/226] bootstrap: don't build redirect pages during dry-run/test --- src/bootstrap/src/core/build_steps/doc.rs | 4 ++++ src/bootstrap/src/core/builder/tests.rs | 8 ++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index 37462c63f1b0..0a0f3b95b2cf 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -274,6 +274,10 @@ impl Step for TheBook { // build the redirect pages let _guard = builder.msg(Kind::Doc, "book redirect pages", None, build_compiler, target); + if builder.config.dry_run() { + return; + } + for file in t!(fs::read_dir(redirect_path)) { let file = t!(file); let path = file.path(); diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 3306435758b4..d0429387f824 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -1270,12 +1270,12 @@ mod snapshot { [doc] book/first-edition (book) [doc] book/second-edition (book) [doc] book/2018-edition (book) - [build] rustdoc 1 [build] rustc 1 -> std 1 [doc] book (book) [doc] book/first-edition (book) [doc] book/second-edition (book) [doc] book/2018-edition (book) + [build] rustdoc 1 [doc] rustc 1 -> standalone 2 [doc] rustc 1 -> standalone 2 [doc] rustc 1 -> std 1 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] @@ -1409,12 +1409,12 @@ mod snapshot { [doc] book/first-edition (book) [doc] book/second-edition (book) [doc] book/2018-edition (book) - [build] rustdoc 1 [build] rustc 1 -> std 1 [doc] book (book) [doc] book/first-edition (book) [doc] book/second-edition (book) [doc] book/2018-edition (book) + [build] rustdoc 1 [doc] rustc 1 -> standalone 2 [doc] rustc 1 -> standalone 2 [doc] rustc 1 -> std 1 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] @@ -1493,8 +1493,8 @@ mod snapshot { [doc] book/first-edition (book) [doc] book/second-edition (book) [doc] book/2018-edition (book) - [build] rustdoc 1 [build] rustc 1 -> std 1 + [build] rustdoc 1 [doc] rustc 1 -> standalone 2 [doc] rustc 1 -> std 1 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] [doc] nomicon (book) @@ -1537,8 +1537,8 @@ mod snapshot { [doc] book/first-edition (book) [doc] book/second-edition (book) [doc] book/2018-edition (book) - [build] rustdoc 1 [build] rustc 1 -> std 1 + [build] rustdoc 1 [doc] rustc 1 -> standalone 2 [doc] rustc 1 -> std 1 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] [build] llvm From 5234d36597b8f22aba25eef67d892315a439e927 Mon Sep 17 00:00:00 2001 From: Karol Zwolak Date: Sun, 5 Oct 2025 13:18:02 +0200 Subject: [PATCH 181/226] add test --- .../extern/extern-single-quote-issue-147365.rs | 9 +++++++++ .../extern-single-quote-issue-147365.stderr | 16 ++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 tests/ui/extern/extern-single-quote-issue-147365.rs create mode 100644 tests/ui/extern/extern-single-quote-issue-147365.stderr diff --git a/tests/ui/extern/extern-single-quote-issue-147365.rs b/tests/ui/extern/extern-single-quote-issue-147365.rs new file mode 100644 index 000000000000..b50016bd65f3 --- /dev/null +++ b/tests/ui/extern/extern-single-quote-issue-147365.rs @@ -0,0 +1,9 @@ +//@ needs-rustc-debug-assertions + +// https://github.com/rust-lang/rust/issues/147365 +// Ensures we don't trigger debug assert by creating an empty Ident when determining whether +// the single quote is a raw lifetime. + +extern "'" {} //~ ERROR invalid ABI: found `'` + +fn main() {} diff --git a/tests/ui/extern/extern-single-quote-issue-147365.stderr b/tests/ui/extern/extern-single-quote-issue-147365.stderr new file mode 100644 index 000000000000..d761bc3ebf32 --- /dev/null +++ b/tests/ui/extern/extern-single-quote-issue-147365.stderr @@ -0,0 +1,16 @@ +error[E0703]: invalid ABI: found `'` + --> $DIR/extern-single-quote-issue-147365.rs:7:8 + | +LL | extern "'" {} + | ^^^ invalid ABI + | + = note: invoke `rustc --print=calling-conventions` for a full list of supported calling conventions +help: there's a similarly named valid ABI `C` + | +LL - extern "'" {} +LL + extern "C" {} + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0703`. From a8b9a576745d4e6a13198952c7b4b4b8d825624d Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Sun, 5 Oct 2025 20:30:24 +0800 Subject: [PATCH 182/226] bootstrap: relax `compiler-rt` root assertion Not needed during tests. --- src/bootstrap/src/core/build_steps/compile.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 96b4e15433f7..d29d1041486b 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -590,7 +590,12 @@ pub fn std_cargo(builder: &Builder<'_>, target: TargetSelection, cargo: &mut Car ), ); let compiler_builtins_root = builder.src.join("src/llvm-project/compiler-rt"); - assert!(compiler_builtins_root.exists()); + if !builder.config.dry_run() { + // This assertion would otherwise trigger during tests if `llvm-project` is not + // checked out. + assert!(compiler_builtins_root.exists()); + } + // The path to `compiler-rt` is also used by `profiler_builtins` (above), // so if you're changing something here please also change that as appropriate. cargo.env("RUST_COMPILER_RT_ROOT", &compiler_builtins_root); From 8a31837054e95417b7356848225583e33ec3c439 Mon Sep 17 00:00:00 2001 From: Shunpoco Date: Sat, 4 Oct 2025 11:29:07 +0100 Subject: [PATCH 183/226] Set opt-level for installing tool only on CI ensure_version_or_cargo_install uses -Copt-level=0 for quicker installation. However, the flag affects the tool's performance. For example, typos-cli with opt-level=0 takes 15 seconds for checking ./compiler, but the tool with default opt-level only takes less than 1 sec. This commit enables the option only when the test tidy is run on CI. --- src/tools/tidy/src/lib.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs index 874a758bd9b3..0acbcd64f067 100644 --- a/src/tools/tidy/src/lib.rs +++ b/src/tools/tidy/src/lib.rs @@ -194,8 +194,8 @@ pub fn ensure_version_or_cargo_install( // use --force to ensure that if the required version is bumped, we update it. // use --target-dir to ensure we have a build cache so repeated invocations aren't slow. // modify PATH so that cargo doesn't print a warning telling the user to modify the path. - let cargo_exit_code = Command::new(cargo) - .args(["install", "--locked", "--force", "--quiet"]) + let mut cmd = Command::new(cargo); + cmd.args(["install", "--locked", "--force", "--quiet"]) .arg("--root") .arg(&tool_root_dir) .arg("--target-dir") @@ -208,10 +208,16 @@ pub fn ensure_version_or_cargo_install( .chain(std::iter::once(tool_bin_dir.clone())), ) .expect("build dir contains invalid char"), - ) - .env("RUSTFLAGS", "-Copt-level=0") - .spawn()? - .wait()?; + ); + + // On CI, we set opt-level flag for quicker installation. + // Since lower opt-level decreases the tool's performance, + // we don't set this option on local. + if CiEnv::is_ci() { + cmd.env("RUSTFLAGS", "-Copt-level=0"); + } + + let cargo_exit_code = cmd.spawn()?.wait()?; if !cargo_exit_code.success() { return Err(io::Error::other("cargo install failed")); } From 7e1c00dbce512b260c396b4425527b00891168a7 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Wed, 17 Sep 2025 17:06:14 +0000 Subject: [PATCH 184/226] Prevent downstream impl DerefMut for Pin --- library/core/src/pin.rs | 59 +++++++++++++++++- ...y.run2-{closure#0}.Inline.panic-abort.diff | 62 ++++++++++--------- ....run2-{closure#0}.Inline.panic-unwind.diff | 62 ++++++++++--------- tests/ui/deref/pin-impl-deref.rs | 4 +- tests/ui/deref/pin-impl-deref.stderr | 22 +++---- .../pin-unsound-issue-85099-derefmut.rs | 4 +- .../pin-unsound-issue-85099-derefmut.stderr | 14 +++++ 7 files changed, 152 insertions(+), 75 deletions(-) create mode 100644 tests/ui/typeck/pin-unsound-issue-85099-derefmut.stderr diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 535830f2e749..d7e348b71e1d 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -1689,9 +1689,66 @@ impl const Deref for Pin { } } +mod helper { + /// Helper that prevents downstream crates from implementing `DerefMut` for `Pin`. + /// + /// This type is not `#[fundamental]`, so it's possible to relax its `DerefMut` impl bounds in + /// the future, so the orphan rules reject downstream impls of `DerefMut` of `Pin`. + #[repr(transparent)] + #[unstable(feature = "pin_derefmut_internals", issue = "none")] + #[allow(missing_debug_implementations)] + pub struct Pin { + pointer: Ptr, + } + + #[unstable(feature = "pin_derefmut_internals", issue = "none")] + #[rustc_const_unstable(feature = "const_convert", issue = "143773")] + pub const trait DerefMut { + type Target: ?Sized; + fn deref_mut(&mut self) -> &mut Self::Target; + } + + #[unstable(feature = "pin_derefmut_internals", issue = "none")] + #[rustc_const_unstable(feature = "const_convert", issue = "143773")] + impl const DerefMut for Pin + where + Ptr::Target: crate::marker::Unpin, + { + type Target = Ptr::Target; + + #[inline(always)] + fn deref_mut(&mut self) -> &mut Ptr::Target { + &mut self.pointer + } + } +} + #[stable(feature = "pin", since = "1.33.0")] #[rustc_const_unstable(feature = "const_convert", issue = "143773")] -impl> const DerefMut for Pin { +#[cfg(not(doc))] +impl const DerefMut for Pin +where + Ptr: [const] Deref, + helper::Pin: [const] helper::DerefMut, +{ + #[inline] + fn deref_mut(&mut self) -> &mut Ptr::Target { + // SAFETY: Pin and helper::Pin have the same layout, so this is equivalent to + // `&mut self.pointer` which is safe because `Target: Unpin`. + helper::DerefMut::deref_mut(unsafe { + &mut *(self as *mut Pin as *mut helper::Pin) + }) + } +} + +#[stable(feature = "pin", since = "1.33.0")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +#[cfg(doc)] +impl const DerefMut for Pin +where + Ptr: [const] DerefMut, + Ptr::Target: Unpin, +{ fn deref_mut(&mut self) -> &mut Ptr::Target { Pin::get_mut(Pin::as_mut(self)) } diff --git a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff index 0acb33febe52..226a16d6eec3 100644 --- a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff +++ b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff @@ -63,27 +63,25 @@ + let mut _44: &mut std::future::Ready<()>; + let mut _45: &mut std::pin::Pin<&mut std::future::Ready<()>>; + scope 14 (inlined > as DerefMut>::deref_mut) { -+ scope 15 (inlined Pin::<&mut std::future::Ready<()>>::as_mut) { -+ let mut _46: &mut &mut std::future::Ready<()>; -+ scope 16 (inlined Pin::<&mut std::future::Ready<()>>::new_unchecked) { -+ } -+ scope 18 (inlined <&mut std::future::Ready<()> as DerefMut>::deref_mut) { -+ } -+ } -+ scope 17 (inlined Pin::<&mut std::future::Ready<()>>::get_mut) { -+ } -+ } -+ scope 19 (inlined Option::<()>::take) { -+ let mut _47: std::option::Option<()>; -+ scope 20 (inlined std::mem::replace::>) { -+ scope 21 { ++ let mut _46: *mut std::pin::helper::Pin<&mut std::future::Ready<()>>; ++ let mut _47: *mut std::pin::Pin<&mut std::future::Ready<()>>; ++ scope 15 (inlined > as pin::helper::DerefMut>::deref_mut) { ++ let mut _48: &mut &mut std::future::Ready<()>; ++ scope 16 (inlined <&mut std::future::Ready<()> as DerefMut>::deref_mut) { + } + } + } -+ scope 22 (inlined #[track_caller] Option::<()>::expect) { -+ let mut _48: isize; -+ let mut _49: !; -+ scope 23 { ++ scope 17 (inlined Option::<()>::take) { ++ let mut _49: std::option::Option<()>; ++ scope 18 (inlined std::mem::replace::>) { ++ scope 19 { ++ } ++ } ++ } ++ scope 20 (inlined #[track_caller] Option::<()>::expect) { ++ let mut _50: isize; ++ let mut _51: !; ++ scope 21 { + } + } + } @@ -217,18 +215,23 @@ + _22 = &mut (*_23); + StorageDead(_24); + StorageLive(_44); -+ StorageLive(_49); ++ StorageLive(_46); ++ StorageLive(_51); + StorageLive(_41); + StorageLive(_42); -+ _44 = copy (_19.0: &mut std::future::Ready<()>); + StorageLive(_47); -+ _47 = Option::<()>::None; -+ _42 = copy ((*_44).0: std::option::Option<()>); -+ ((*_44).0: std::option::Option<()>) = copy _47; ++ _47 = &raw mut _19; ++ _46 = copy _47 as *mut std::pin::helper::Pin<&mut std::future::Ready<()>> (PtrToPtr); + StorageDead(_47); -+ StorageLive(_48); -+ _48 = discriminant(_42); -+ switchInt(move _48) -> [0: bb11, 1: bb12, otherwise: bb5]; ++ _44 = copy ((*_46).0: &mut std::future::Ready<()>); ++ StorageLive(_49); ++ _49 = Option::<()>::None; ++ _42 = copy ((*_44).0: std::option::Option<()>); ++ ((*_44).0: std::option::Option<()>) = copy _49; ++ StorageDead(_49); ++ StorageLive(_50); ++ _50 = discriminant(_42); ++ switchInt(move _50) -> [0: bb11, 1: bb12, otherwise: bb5]; } + + bb5: { @@ -291,16 +294,17 @@ + } + + bb11: { -+ _49 = option::expect_failed(const "`Ready` polled after completion") -> unwind unreachable; ++ _51 = option::expect_failed(const "`Ready` polled after completion") -> unwind unreachable; + } + + bb12: { + _41 = move ((_42 as Some).0: ()); -+ StorageDead(_48); ++ StorageDead(_50); + StorageDead(_42); + _18 = Poll::<()>::Ready(move _41); + StorageDead(_41); -+ StorageDead(_49); ++ StorageDead(_51); ++ StorageDead(_46); + StorageDead(_44); + StorageDead(_22); + StorageDead(_19); diff --git a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff index 98ee46c29b1b..770b6e471ff8 100644 --- a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff @@ -65,27 +65,25 @@ + let mut _46: &mut std::future::Ready<()>; + let mut _47: &mut std::pin::Pin<&mut std::future::Ready<()>>; + scope 14 (inlined > as DerefMut>::deref_mut) { -+ scope 15 (inlined Pin::<&mut std::future::Ready<()>>::as_mut) { -+ let mut _48: &mut &mut std::future::Ready<()>; -+ scope 16 (inlined Pin::<&mut std::future::Ready<()>>::new_unchecked) { -+ } -+ scope 18 (inlined <&mut std::future::Ready<()> as DerefMut>::deref_mut) { -+ } -+ } -+ scope 17 (inlined Pin::<&mut std::future::Ready<()>>::get_mut) { -+ } -+ } -+ scope 19 (inlined Option::<()>::take) { -+ let mut _49: std::option::Option<()>; -+ scope 20 (inlined std::mem::replace::>) { -+ scope 21 { ++ let mut _48: *mut std::pin::helper::Pin<&mut std::future::Ready<()>>; ++ let mut _49: *mut std::pin::Pin<&mut std::future::Ready<()>>; ++ scope 15 (inlined > as pin::helper::DerefMut>::deref_mut) { ++ let mut _50: &mut &mut std::future::Ready<()>; ++ scope 16 (inlined <&mut std::future::Ready<()> as DerefMut>::deref_mut) { + } + } + } -+ scope 22 (inlined #[track_caller] Option::<()>::expect) { -+ let mut _50: isize; -+ let mut _51: !; -+ scope 23 { ++ scope 17 (inlined Option::<()>::take) { ++ let mut _51: std::option::Option<()>; ++ scope 18 (inlined std::mem::replace::>) { ++ scope 19 { ++ } ++ } ++ } ++ scope 20 (inlined #[track_caller] Option::<()>::expect) { ++ let mut _52: isize; ++ let mut _53: !; ++ scope 21 { + } + } + } @@ -234,18 +232,23 @@ + _22 = &mut (*_23); + StorageDead(_24); + StorageLive(_46); -+ StorageLive(_51); ++ StorageLive(_48); ++ StorageLive(_53); + StorageLive(_43); + StorageLive(_44); -+ _46 = copy (_19.0: &mut std::future::Ready<()>); + StorageLive(_49); -+ _49 = Option::<()>::None; -+ _44 = copy ((*_46).0: std::option::Option<()>); -+ ((*_46).0: std::option::Option<()>) = copy _49; ++ _49 = &raw mut _19; ++ _48 = copy _49 as *mut std::pin::helper::Pin<&mut std::future::Ready<()>> (PtrToPtr); + StorageDead(_49); -+ StorageLive(_50); -+ _50 = discriminant(_44); -+ switchInt(move _50) -> [0: bb16, 1: bb17, otherwise: bb7]; ++ _46 = copy ((*_48).0: &mut std::future::Ready<()>); ++ StorageLive(_51); ++ _51 = Option::<()>::None; ++ _44 = copy ((*_46).0: std::option::Option<()>); ++ ((*_46).0: std::option::Option<()>) = copy _51; ++ StorageDead(_51); ++ StorageLive(_52); ++ _52 = discriminant(_44); ++ switchInt(move _52) -> [0: bb16, 1: bb17, otherwise: bb7]; } - bb6 (cleanup): { @@ -332,16 +335,17 @@ + } + + bb16: { -+ _51 = option::expect_failed(const "`Ready` polled after completion") -> bb11; ++ _53 = option::expect_failed(const "`Ready` polled after completion") -> bb11; + } + + bb17: { + _43 = move ((_44 as Some).0: ()); -+ StorageDead(_50); ++ StorageDead(_52); + StorageDead(_44); + _18 = Poll::<()>::Ready(move _43); + StorageDead(_43); -+ StorageDead(_51); ++ StorageDead(_53); ++ StorageDead(_48); + StorageDead(_46); + StorageDead(_22); + StorageDead(_19); diff --git a/tests/ui/deref/pin-impl-deref.rs b/tests/ui/deref/pin-impl-deref.rs index b1dc8dea3f24..ccd8d0dfc72a 100644 --- a/tests/ui/deref/pin-impl-deref.rs +++ b/tests/ui/deref/pin-impl-deref.rs @@ -22,7 +22,7 @@ impl MyPinType { fn impl_deref_mut(_: impl DerefMut) {} fn unpin_impl_ref(r_unpin: Pin<&MyUnpinType>) { impl_deref_mut(r_unpin) - //~^ ERROR: the trait bound `Pin<&MyUnpinType>: DerefMut` is not satisfied + //~^ ERROR: the trait bound `&MyUnpinType: DerefMut` is not satisfied } fn unpin_impl_mut(r_unpin: Pin<&mut MyUnpinType>) { impl_deref_mut(r_unpin) @@ -30,7 +30,7 @@ fn unpin_impl_mut(r_unpin: Pin<&mut MyUnpinType>) { fn pin_impl_ref(r_pin: Pin<&MyPinType>) { impl_deref_mut(r_pin) //~^ ERROR: `PhantomPinned` cannot be unpinned - //~| ERROR: the trait bound `Pin<&MyPinType>: DerefMut` is not satisfied + //~| ERROR: the trait bound `&MyPinType: DerefMut` is not satisfied } fn pin_impl_mut(r_pin: Pin<&mut MyPinType>) { impl_deref_mut(r_pin) diff --git a/tests/ui/deref/pin-impl-deref.stderr b/tests/ui/deref/pin-impl-deref.stderr index 106654641a11..918ff7ca9121 100644 --- a/tests/ui/deref/pin-impl-deref.stderr +++ b/tests/ui/deref/pin-impl-deref.stderr @@ -1,40 +1,36 @@ -error[E0277]: the trait bound `Pin<&MyUnpinType>: DerefMut` is not satisfied +error[E0277]: the trait bound `&MyUnpinType: DerefMut` is not satisfied --> $DIR/pin-impl-deref.rs:24:20 | LL | impl_deref_mut(r_unpin) - | -------------- ^^^^^^^ the trait `DerefMut` is not implemented for `Pin<&MyUnpinType>` + | -------------- ^^^^^^^ the trait `DerefMut` is not implemented for `&MyUnpinType` | | | required by a bound introduced by this call | + = note: `DerefMut` is implemented for `&mut MyUnpinType`, but not for `&MyUnpinType` + = note: required for `pin::helper::Pin<&MyUnpinType>` to implement `pin::helper::DerefMut` = note: required for `Pin<&MyUnpinType>` to implement `DerefMut` note: required by a bound in `impl_deref_mut` --> $DIR/pin-impl-deref.rs:22:27 | LL | fn impl_deref_mut(_: impl DerefMut) {} | ^^^^^^^^ required by this bound in `impl_deref_mut` -help: consider mutably borrowing here - | -LL | impl_deref_mut(&mut r_unpin) - | ++++ -error[E0277]: the trait bound `Pin<&MyPinType>: DerefMut` is not satisfied +error[E0277]: the trait bound `&MyPinType: DerefMut` is not satisfied --> $DIR/pin-impl-deref.rs:31:20 | LL | impl_deref_mut(r_pin) - | -------------- ^^^^^ the trait `DerefMut` is not implemented for `Pin<&MyPinType>` + | -------------- ^^^^^ the trait `DerefMut` is not implemented for `&MyPinType` | | | required by a bound introduced by this call | + = note: `DerefMut` is implemented for `&mut MyPinType`, but not for `&MyPinType` + = note: required for `pin::helper::Pin<&MyPinType>` to implement `pin::helper::DerefMut` = note: required for `Pin<&MyPinType>` to implement `DerefMut` note: required by a bound in `impl_deref_mut` --> $DIR/pin-impl-deref.rs:22:27 | LL | fn impl_deref_mut(_: impl DerefMut) {} | ^^^^^^^^ required by this bound in `impl_deref_mut` -help: consider mutably borrowing here - | -LL | impl_deref_mut(&mut r_pin) - | ++++ error[E0277]: `PhantomPinned` cannot be unpinned --> $DIR/pin-impl-deref.rs:31:20 @@ -51,6 +47,7 @@ note: required because it appears within the type `MyPinType` | LL | struct MyPinType(core::marker::PhantomPinned); | ^^^^^^^^^ + = note: required for `pin::helper::Pin<&MyPinType>` to implement `pin::helper::DerefMut` = note: required for `Pin<&MyPinType>` to implement `DerefMut` note: required by a bound in `impl_deref_mut` --> $DIR/pin-impl-deref.rs:22:27 @@ -73,6 +70,7 @@ note: required because it appears within the type `MyPinType` | LL | struct MyPinType(core::marker::PhantomPinned); | ^^^^^^^^^ + = note: required for `pin::helper::Pin<&mut MyPinType>` to implement `pin::helper::DerefMut` = note: required for `Pin<&mut MyPinType>` to implement `DerefMut` note: required by a bound in `impl_deref_mut` --> $DIR/pin-impl-deref.rs:22:27 diff --git a/tests/ui/typeck/pin-unsound-issue-85099-derefmut.rs b/tests/ui/typeck/pin-unsound-issue-85099-derefmut.rs index f3ece563f540..e8c3bbba1e45 100644 --- a/tests/ui/typeck/pin-unsound-issue-85099-derefmut.rs +++ b/tests/ui/typeck/pin-unsound-issue-85099-derefmut.rs @@ -1,5 +1,4 @@ -//@ check-pass -//@ known-bug: #85099 +//@ check-fail // Should fail. Can coerce `Pin` into `Pin` where // `T: Deref` and `U: Deref`, using the @@ -43,6 +42,7 @@ impl<'a, Fut: Future> SomeTrait<'a, Fut> for Fut { } impl<'b, 'a, Fut> DerefMut for Pin<&'b dyn SomeTrait<'a, Fut>> { +//~^ ERROR: conflicting implementations of trait `DerefMut` fn deref_mut<'c>( self: &'c mut Pin<&'b dyn SomeTrait<'a, Fut>>, ) -> &'c mut (dyn SomeTrait<'a, Fut> + 'b) { diff --git a/tests/ui/typeck/pin-unsound-issue-85099-derefmut.stderr b/tests/ui/typeck/pin-unsound-issue-85099-derefmut.stderr new file mode 100644 index 000000000000..cc56c77809ea --- /dev/null +++ b/tests/ui/typeck/pin-unsound-issue-85099-derefmut.stderr @@ -0,0 +1,14 @@ +error[E0119]: conflicting implementations of trait `DerefMut` for type `Pin<&dyn SomeTrait<'_, _>>` + --> $DIR/pin-unsound-issue-85099-derefmut.rs:44:1 + | +LL | impl<'b, 'a, Fut> DerefMut for Pin<&'b dyn SomeTrait<'a, Fut>> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: conflicting implementation in crate `core`: + - impl DerefMut for Pin + where as pin::helper::DerefMut>::Target == as Deref>::Target, Ptr: Deref, pin::helper::Pin: pin::helper::DerefMut, pin::helper::Pin: ?Sized; + = note: upstream crates may add a new impl of trait `std::pin::helper::DerefMut` for type `std::pin::helper::Pin<&dyn SomeTrait<'_, _>>` in future versions + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0119`. From cd44cd0f63a7e67dcfc3618aa7bf6e2e70643561 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Thu, 25 Sep 2025 08:39:35 +0000 Subject: [PATCH 185/226] Improve docs for module --- library/core/src/pin.rs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index d7e348b71e1d..fdbeaea46419 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -1692,8 +1692,22 @@ impl const Deref for Pin { mod helper { /// Helper that prevents downstream crates from implementing `DerefMut` for `Pin`. /// - /// This type is not `#[fundamental]`, so it's possible to relax its `DerefMut` impl bounds in - /// the future, so the orphan rules reject downstream impls of `DerefMut` of `Pin`. + /// The `Pin` type implements the unsafe trait `PinCoerceUnsized`, which essentially requires + /// that the type does not have a malicious `Deref` or `DerefMut` impl. However, without this + /// helper module, downstream crates are able to write `impl DerefMut for Pin` as + /// long as it does not overlap with the impl provided by stdlib. This is because `Pin` is + /// `#[fundamental]`, so stdlib promises to never implement traits for `Pin` that it does not + /// implement today. + /// + /// However, this is problematic. Downstream crates could implement `DerefMut` for + /// `Pin<&LocalType>`, and they could do so maliciously. To prevent this, the implementation for + /// `Pin` delegates to this helper module. Since `helper::Pin` is not `#[fundamental]`, the + /// orphan rules assume that stdlib might implement `helper::DerefMut` for `helper::Pin<&_>` in + /// the future. Because of this, downstream crates can no longer provide an implementation of + /// `DerefMut` for `Pin<&_>`, as it might overlap with a trait impl that, according to the + /// orphan rules, the stdlib could introduce without a breaking change in a future release. + /// + /// See for the issue this fixes. #[repr(transparent)] #[unstable(feature = "pin_derefmut_internals", issue = "none")] #[allow(missing_debug_implementations)] From f65250b3dd0eada1ab25ad4f766048f2ebe24623 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Mon, 29 Sep 2025 09:34:18 +0000 Subject: [PATCH 186/226] Document workaround in docs --- library/core/src/pin.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index fdbeaea46419..23950acd9761 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -1755,13 +1755,21 @@ where } } +/// The `Target` type is restricted to `Unpin` types as it's not safe to obtain a mutable reference +/// to a pinned value. +/// +/// For soundness reasons, implementations of `DerefMut` for `Pin` are rejected even when `T` is +/// a local type not covered by this impl block. (Since `Pin` is [fundamental], such implementations +/// would normally be possible.) +/// +/// [fundamental]: ../../reference/items/implementations.html#r-items.impl.trait.fundamental #[stable(feature = "pin", since = "1.33.0")] #[rustc_const_unstable(feature = "const_convert", issue = "143773")] #[cfg(doc)] impl const DerefMut for Pin where Ptr: [const] DerefMut, - Ptr::Target: Unpin, + ::Target: Unpin, { fn deref_mut(&mut self) -> &mut Ptr::Target { Pin::get_mut(Pin::as_mut(self)) From 76dcb39c240382a7d62cc8ee475e0c3866ad8025 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Wed, 1 Oct 2025 11:14:08 +0000 Subject: [PATCH 187/226] Adjust error messages --- compiler/rustc_span/src/symbol.rs | 1 + .../src/error_reporting/traits/suggestions.rs | 18 ++++++++++++++++++ library/core/src/pin.rs | 15 ++++++++------- ...dy.run2-{closure#0}.Inline.panic-abort.diff | 6 +++--- ...y.run2-{closure#0}.Inline.panic-unwind.diff | 6 +++--- tests/ui/deref/pin-impl-deref.stderr | 4 ---- .../pin-unsound-issue-85099-derefmut.stderr | 4 ++-- 7 files changed, 35 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 083e04730bc3..7f1ca96b2a75 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -309,6 +309,7 @@ symbols! { PathBuf, Pending, PinCoerceUnsized, + PinDerefMutHelper, Pointer, Poll, ProcMacro, 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 37e622102e70..5665229a4cbe 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -3476,6 +3476,24 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // can do about it. As far as they are concerned, `?` is compiler magic. return; } + if tcx.is_diagnostic_item(sym::PinDerefMutHelper, parent_def_id) { + let parent_predicate = + self.resolve_vars_if_possible(data.derived.parent_trait_pred); + + // Skip PinDerefMutHelper in suggestions, but still show downstream suggestions. + ensure_sufficient_stack(|| { + self.note_obligation_cause_code( + body_id, + err, + parent_predicate, + param_env, + &data.derived.parent_code, + obligated_types, + seen_requirements, + ) + }); + return; + } let self_ty_str = tcx.short_string(parent_trait_pred.skip_binder().self_ty(), err.long_ty_path()); let trait_name = tcx.short_string( diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 23950acd9761..81c2dabf0d1d 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -1711,20 +1711,21 @@ mod helper { #[repr(transparent)] #[unstable(feature = "pin_derefmut_internals", issue = "none")] #[allow(missing_debug_implementations)] - pub struct Pin { + pub struct PinHelper { pointer: Ptr, } #[unstable(feature = "pin_derefmut_internals", issue = "none")] #[rustc_const_unstable(feature = "const_convert", issue = "143773")] - pub const trait DerefMut { + #[rustc_diagnostic_item = "PinDerefMutHelper"] + pub const trait PinDerefMutHelper { type Target: ?Sized; fn deref_mut(&mut self) -> &mut Self::Target; } #[unstable(feature = "pin_derefmut_internals", issue = "none")] #[rustc_const_unstable(feature = "const_convert", issue = "143773")] - impl const DerefMut for Pin + impl const PinDerefMutHelper for PinHelper where Ptr::Target: crate::marker::Unpin, { @@ -1743,14 +1744,14 @@ mod helper { impl const DerefMut for Pin where Ptr: [const] Deref, - helper::Pin: [const] helper::DerefMut, + helper::PinHelper: [const] helper::PinDerefMutHelper, { #[inline] fn deref_mut(&mut self) -> &mut Ptr::Target { - // SAFETY: Pin and helper::Pin have the same layout, so this is equivalent to + // SAFETY: Pin and PinHelper have the same layout, so this is equivalent to // `&mut self.pointer` which is safe because `Target: Unpin`. - helper::DerefMut::deref_mut(unsafe { - &mut *(self as *mut Pin as *mut helper::Pin) + helper::PinDerefMutHelper::deref_mut(unsafe { + &mut *(self as *mut Pin as *mut helper::PinHelper) }) } } diff --git a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff index 226a16d6eec3..0203ff52d63f 100644 --- a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff +++ b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff @@ -63,9 +63,9 @@ + let mut _44: &mut std::future::Ready<()>; + let mut _45: &mut std::pin::Pin<&mut std::future::Ready<()>>; + scope 14 (inlined > as DerefMut>::deref_mut) { -+ let mut _46: *mut std::pin::helper::Pin<&mut std::future::Ready<()>>; ++ let mut _46: *mut std::pin::helper::PinHelper<&mut std::future::Ready<()>>; + let mut _47: *mut std::pin::Pin<&mut std::future::Ready<()>>; -+ scope 15 (inlined > as pin::helper::DerefMut>::deref_mut) { ++ scope 15 (inlined > as pin::helper::PinDerefMutHelper>::deref_mut) { + let mut _48: &mut &mut std::future::Ready<()>; + scope 16 (inlined <&mut std::future::Ready<()> as DerefMut>::deref_mut) { + } @@ -221,7 +221,7 @@ + StorageLive(_42); + StorageLive(_47); + _47 = &raw mut _19; -+ _46 = copy _47 as *mut std::pin::helper::Pin<&mut std::future::Ready<()>> (PtrToPtr); ++ _46 = copy _47 as *mut std::pin::helper::PinHelper<&mut std::future::Ready<()>> (PtrToPtr); + StorageDead(_47); + _44 = copy ((*_46).0: &mut std::future::Ready<()>); + StorageLive(_49); diff --git a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff index 770b6e471ff8..6c8cad3e992e 100644 --- a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff @@ -65,9 +65,9 @@ + let mut _46: &mut std::future::Ready<()>; + let mut _47: &mut std::pin::Pin<&mut std::future::Ready<()>>; + scope 14 (inlined > as DerefMut>::deref_mut) { -+ let mut _48: *mut std::pin::helper::Pin<&mut std::future::Ready<()>>; ++ let mut _48: *mut std::pin::helper::PinHelper<&mut std::future::Ready<()>>; + let mut _49: *mut std::pin::Pin<&mut std::future::Ready<()>>; -+ scope 15 (inlined > as pin::helper::DerefMut>::deref_mut) { ++ scope 15 (inlined > as pin::helper::PinDerefMutHelper>::deref_mut) { + let mut _50: &mut &mut std::future::Ready<()>; + scope 16 (inlined <&mut std::future::Ready<()> as DerefMut>::deref_mut) { + } @@ -238,7 +238,7 @@ + StorageLive(_44); + StorageLive(_49); + _49 = &raw mut _19; -+ _48 = copy _49 as *mut std::pin::helper::Pin<&mut std::future::Ready<()>> (PtrToPtr); ++ _48 = copy _49 as *mut std::pin::helper::PinHelper<&mut std::future::Ready<()>> (PtrToPtr); + StorageDead(_49); + _46 = copy ((*_48).0: &mut std::future::Ready<()>); + StorageLive(_51); diff --git a/tests/ui/deref/pin-impl-deref.stderr b/tests/ui/deref/pin-impl-deref.stderr index 918ff7ca9121..4143d66f4272 100644 --- a/tests/ui/deref/pin-impl-deref.stderr +++ b/tests/ui/deref/pin-impl-deref.stderr @@ -7,7 +7,6 @@ LL | impl_deref_mut(r_unpin) | required by a bound introduced by this call | = note: `DerefMut` is implemented for `&mut MyUnpinType`, but not for `&MyUnpinType` - = note: required for `pin::helper::Pin<&MyUnpinType>` to implement `pin::helper::DerefMut` = note: required for `Pin<&MyUnpinType>` to implement `DerefMut` note: required by a bound in `impl_deref_mut` --> $DIR/pin-impl-deref.rs:22:27 @@ -24,7 +23,6 @@ LL | impl_deref_mut(r_pin) | required by a bound introduced by this call | = note: `DerefMut` is implemented for `&mut MyPinType`, but not for `&MyPinType` - = note: required for `pin::helper::Pin<&MyPinType>` to implement `pin::helper::DerefMut` = note: required for `Pin<&MyPinType>` to implement `DerefMut` note: required by a bound in `impl_deref_mut` --> $DIR/pin-impl-deref.rs:22:27 @@ -47,7 +45,6 @@ note: required because it appears within the type `MyPinType` | LL | struct MyPinType(core::marker::PhantomPinned); | ^^^^^^^^^ - = note: required for `pin::helper::Pin<&MyPinType>` to implement `pin::helper::DerefMut` = note: required for `Pin<&MyPinType>` to implement `DerefMut` note: required by a bound in `impl_deref_mut` --> $DIR/pin-impl-deref.rs:22:27 @@ -70,7 +67,6 @@ note: required because it appears within the type `MyPinType` | LL | struct MyPinType(core::marker::PhantomPinned); | ^^^^^^^^^ - = note: required for `pin::helper::Pin<&mut MyPinType>` to implement `pin::helper::DerefMut` = note: required for `Pin<&mut MyPinType>` to implement `DerefMut` note: required by a bound in `impl_deref_mut` --> $DIR/pin-impl-deref.rs:22:27 diff --git a/tests/ui/typeck/pin-unsound-issue-85099-derefmut.stderr b/tests/ui/typeck/pin-unsound-issue-85099-derefmut.stderr index cc56c77809ea..2bcd92b76a09 100644 --- a/tests/ui/typeck/pin-unsound-issue-85099-derefmut.stderr +++ b/tests/ui/typeck/pin-unsound-issue-85099-derefmut.stderr @@ -6,8 +6,8 @@ LL | impl<'b, 'a, Fut> DerefMut for Pin<&'b dyn SomeTrait<'a, Fut>> { | = note: conflicting implementation in crate `core`: - impl DerefMut for Pin - where as pin::helper::DerefMut>::Target == as Deref>::Target, Ptr: Deref, pin::helper::Pin: pin::helper::DerefMut, pin::helper::Pin: ?Sized; - = note: upstream crates may add a new impl of trait `std::pin::helper::DerefMut` for type `std::pin::helper::Pin<&dyn SomeTrait<'_, _>>` in future versions + where as pin::helper::PinDerefMutHelper>::Target == as Deref>::Target, Ptr: Deref, pin::helper::PinHelper: pin::helper::PinDerefMutHelper, pin::helper::PinHelper: ?Sized; + = note: upstream crates may add a new impl of trait `std::pin::helper::PinDerefMutHelper` for type `std::pin::helper::PinHelper<&dyn SomeTrait<'_, _>>` in future versions error: aborting due to 1 previous error From 21dd997aec1af5200a8b726ae6645f40b87baf7c Mon Sep 17 00:00:00 2001 From: usamoi Date: Tue, 26 Aug 2025 16:24:29 +0800 Subject: [PATCH 188/226] support link modifier `as-needed` for raw-dylib-elf --- compiler/rustc_attr_parsing/messages.ftl | 2 +- .../src/attributes/link_attrs.rs | 9 ++-- compiler/rustc_codegen_ssa/src/back/link.rs | 12 +++--- .../src/back/link/raw_dylib.rs | 24 ++++++----- .../rustc_hir/src/attrs/data_structures.rs | 18 +++++--- compiler/rustc_metadata/src/native_libs.rs | 2 +- .../rustc_session/src/config/native_libs.rs | 3 +- .../linkage-attr/raw-dylib/elf/as_needed.rs | 43 +++++++++++++++++++ 8 files changed, 82 insertions(+), 31 deletions(-) create mode 100644 tests/ui/linkage-attr/raw-dylib/elf/as_needed.rs diff --git a/compiler/rustc_attr_parsing/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl index a432fa099ef4..8b6b762f4310 100644 --- a/compiler/rustc_attr_parsing/messages.ftl +++ b/compiler/rustc_attr_parsing/messages.ftl @@ -1,5 +1,5 @@ attr_parsing_as_needed_compatibility = - linking modifier `as-needed` is only compatible with `dylib` and `framework` linking kinds + linking modifier `as-needed` is only compatible with `dylib`, `framework` and `raw-dylib` linking kinds attr_parsing_bundle_needs_static = linking modifier `bundle` is only compatible with `static` linking kind diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index 04eaa485f736..09f7bfa24e82 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -180,7 +180,8 @@ impl CombineAttributeParser for LinkParser { } (sym::as_dash_needed, Some(NativeLibKind::Dylib { as_needed })) - | (sym::as_dash_needed, Some(NativeLibKind::Framework { as_needed })) => { + | (sym::as_dash_needed, Some(NativeLibKind::Framework { as_needed })) + | (sym::as_dash_needed, Some(NativeLibKind::RawDylib { as_needed })) => { report_unstable_modifier!(native_link_modifiers_as_needed); assign_modifier(as_needed) } @@ -219,12 +220,12 @@ impl CombineAttributeParser for LinkParser { // Do this outside of the loop so that `import_name_type` can be specified before `kind`. if let Some((_, span)) = import_name_type { - if kind != Some(NativeLibKind::RawDylib) { + if !matches!(kind, Some(NativeLibKind::RawDylib { .. })) { cx.emit_err(ImportNameTypeRaw { span }); } } - if let Some(NativeLibKind::RawDylib) = kind + if let Some(NativeLibKind::RawDylib { .. }) = kind && name.as_str().contains('\0') { cx.emit_err(RawDylibNoNul { span: name_span }); @@ -315,7 +316,7 @@ impl LinkParser { cx.emit_err(RawDylibOnlyWindows { span: nv.value_span }); } - NativeLibKind::RawDylib + NativeLibKind::RawDylib { as_needed: None } } sym::link_dash_arg => { if !features.link_arg_attribute() { diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index db2f2dd65b0b..ea538d3d4698 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -1490,7 +1490,7 @@ fn print_native_static_libs( NativeLibKind::Static { bundle: None | Some(true), .. } | NativeLibKind::LinkArg | NativeLibKind::WasmImportModule - | NativeLibKind::RawDylib => None, + | NativeLibKind::RawDylib { .. } => None, } }) // deduplication of consecutive repeated libraries, see rust-lang/rust#113209 @@ -2364,13 +2364,13 @@ fn linker_with_args( cmd.add_object(&output_path); } } else { - for link_path in raw_dylib::create_raw_dylib_elf_stub_shared_objects( + for (link_path, as_needed) in raw_dylib::create_raw_dylib_elf_stub_shared_objects( sess, codegen_results.crate_info.used_libraries.iter(), &raw_dylib_dir, ) { // Always use verbatim linkage, see comments in create_raw_dylib_elf_stub_shared_objects. - cmd.link_dylib_by_name(&link_path, true, false); + cmd.link_dylib_by_name(&link_path, true, as_needed); } } // As with add_upstream_native_libraries, we need to add the upstream raw-dylib symbols in case @@ -2411,13 +2411,13 @@ fn linker_with_args( cmd.add_object(&output_path); } } else { - for link_path in raw_dylib::create_raw_dylib_elf_stub_shared_objects( + for (link_path, as_needed) in raw_dylib::create_raw_dylib_elf_stub_shared_objects( sess, native_libraries_from_nonstatics, &raw_dylib_dir, ) { // Always use verbatim linkage, see comments in create_raw_dylib_elf_stub_shared_objects. - cmd.link_dylib_by_name(&link_path, true, false); + cmd.link_dylib_by_name(&link_path, true, as_needed); } } @@ -2726,7 +2726,7 @@ fn add_native_libs_from_crate( cmd.link_framework_by_name(name, verbatim, as_needed.unwrap_or(true)) } } - NativeLibKind::RawDylib => { + NativeLibKind::RawDylib { as_needed: _ } => { // Handled separately in `linker_with_args`. } NativeLibKind::WasmImportModule => {} diff --git a/compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs b/compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs index 9f42991d4c06..7321bc1da391 100644 --- a/compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs +++ b/compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs @@ -31,7 +31,7 @@ fn collate_raw_dylibs_windows<'a>( let mut dylib_table = FxIndexMap::>::default(); for lib in used_libraries { - if lib.kind == NativeLibKind::RawDylib { + if let NativeLibKind::RawDylib { .. } = lib.kind { let ext = if lib.verbatim { "" } else { ".dll" }; let name = format!("{}{}", lib.name, ext); let imports = dylib_table.entry(name.clone()).or_default(); @@ -128,12 +128,12 @@ pub(super) fn create_raw_dylib_dll_import_libs<'a>( fn collate_raw_dylibs_elf<'a>( sess: &Session, used_libraries: impl IntoIterator, -) -> Vec<(String, Vec)> { +) -> Vec<(String, Vec, bool)> { // Use index maps to preserve original order of imports and libraries. - let mut dylib_table = FxIndexMap::>::default(); + let mut dylib_table = FxIndexMap::, bool)>::default(); for lib in used_libraries { - if lib.kind == NativeLibKind::RawDylib { + if let NativeLibKind::RawDylib { as_needed } = lib.kind { let filename = if lib.verbatim { lib.name.as_str().to_owned() } else { @@ -142,17 +142,19 @@ fn collate_raw_dylibs_elf<'a>( format!("{prefix}{}{ext}", lib.name) }; - let imports = dylib_table.entry(filename.clone()).or_default(); + let (stub_imports, stub_as_needed) = + dylib_table.entry(filename.clone()).or_insert((Default::default(), true)); for import in &lib.dll_imports { - imports.insert(import.name, import); + stub_imports.insert(import.name, import); } + *stub_as_needed = *stub_as_needed && as_needed.unwrap_or(true); } } sess.dcx().abort_if_errors(); dylib_table .into_iter() - .map(|(name, imports)| { - (name, imports.into_iter().map(|(_, import)| import.clone()).collect()) + .map(|(name, (imports, as_needed))| { + (name, imports.into_iter().map(|(_, import)| import.clone()).collect(), as_needed) }) .collect() } @@ -161,10 +163,10 @@ pub(super) fn create_raw_dylib_elf_stub_shared_objects<'a>( sess: &Session, used_libraries: impl IntoIterator, raw_dylib_so_dir: &Path, -) -> Vec { +) -> Vec<(String, bool)> { collate_raw_dylibs_elf(sess, used_libraries) .into_iter() - .map(|(load_filename, raw_dylib_imports)| { + .map(|(load_filename, raw_dylib_imports, as_needed)| { use std::hash::Hash; // `load_filename` is the *target/loader* filename that will end up in NEEDED. @@ -205,7 +207,7 @@ pub(super) fn create_raw_dylib_elf_stub_shared_objects<'a>( }); }; - temporary_lib_name + (temporary_lib_name, as_needed) }) .collect() } diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index beeca7332cb1..2435363ef0eb 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -309,7 +309,10 @@ pub enum NativeLibKind { }, /// Dynamic library (e.g. `foo.dll` on Windows) without a corresponding import library. /// On Linux, it refers to a generated shared library stub. - RawDylib, + RawDylib { + /// Whether the dynamic library will be linked only if it satisfies some undefined symbols + as_needed: Option, + }, /// A macOS-specific kind of dynamic libraries. Framework { /// Whether the framework will be linked only if it satisfies some undefined symbols @@ -332,11 +335,10 @@ impl NativeLibKind { NativeLibKind::Static { bundle, whole_archive } => { bundle.is_some() || whole_archive.is_some() } - NativeLibKind::Dylib { as_needed } | NativeLibKind::Framework { as_needed } => { - as_needed.is_some() - } - NativeLibKind::RawDylib - | NativeLibKind::Unspecified + NativeLibKind::Dylib { as_needed } + | NativeLibKind::Framework { as_needed } + | NativeLibKind::RawDylib { as_needed } => as_needed.is_some(), + NativeLibKind::Unspecified | NativeLibKind::LinkArg | NativeLibKind::WasmImportModule => false, } @@ -349,7 +351,9 @@ impl NativeLibKind { pub fn is_dllimport(&self) -> bool { matches!( self, - NativeLibKind::Dylib { .. } | NativeLibKind::RawDylib | NativeLibKind::Unspecified + NativeLibKind::Dylib { .. } + | NativeLibKind::RawDylib { .. } + | NativeLibKind::Unspecified ) } } diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index 82738c68c592..15572063d45a 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -218,7 +218,7 @@ impl<'tcx> Collector<'tcx> { .flatten() { let dll_imports = match attr.kind { - NativeLibKind::RawDylib => foreign_items + NativeLibKind::RawDylib { .. } => foreign_items .iter() .map(|&child_item| { self.build_dll_import( diff --git a/compiler/rustc_session/src/config/native_libs.rs b/compiler/rustc_session/src/config/native_libs.rs index 50a0593f8871..71d3e222c8a1 100644 --- a/compiler/rustc_session/src/config/native_libs.rs +++ b/compiler/rustc_session/src/config/native_libs.rs @@ -135,7 +135,8 @@ fn parse_and_apply_modifier(cx: &ParseNativeLibCx<'_>, modifier: &str, native_li ), ("as-needed", NativeLibKind::Dylib { as_needed }) - | ("as-needed", NativeLibKind::Framework { as_needed }) => { + | ("as-needed", NativeLibKind::Framework { as_needed }) + | ("as-needed", NativeLibKind::RawDylib { as_needed }) => { cx.on_unstable_value( "linking modifier `as-needed` is unstable", ", the `-Z unstable-options` flag must also be passed to use it", diff --git a/tests/ui/linkage-attr/raw-dylib/elf/as_needed.rs b/tests/ui/linkage-attr/raw-dylib/elf/as_needed.rs new file mode 100644 index 000000000000..48ca39300f41 --- /dev/null +++ b/tests/ui/linkage-attr/raw-dylib/elf/as_needed.rs @@ -0,0 +1,43 @@ +//@ only-elf +//@ needs-dynamic-linking + +//@ only-gnu +//@ only-x86_64 +//@ revisions: as_needed no_as_needed no_modifier merge_1 merge_2 merge_3 merge_4 + +//@ [as_needed] run-pass +//@ [no_as_needed] run-fail +//@ [no_modifier] run-pass +//@ [merge_1] run-fail +//@ [merge_2] run-fail +//@ [merge_3] run-fail +//@ [merge_4] run-pass + +#![allow(incomplete_features)] +#![feature(raw_dylib_elf)] +#![feature(native_link_modifiers_as_needed)] + +#[cfg_attr( + as_needed, + link(name = "taiqannf1y28z2rw", kind = "raw-dylib", modifiers = "+as-needed") +)] +#[cfg_attr( + no_as_needed, + link(name = "taiqannf1y28z2rw", kind = "raw-dylib", modifiers = "-as-needed") +)] +#[cfg_attr(no_modifier, link(name = "taiqannf1y28z2rw", kind = "raw-dylib"))] +unsafe extern "C" {} + +#[cfg_attr(merge_1, link(name = "k9nm7qxoa79bg7e6", kind = "raw-dylib", modifiers = "+as-needed"))] +#[cfg_attr(merge_2, link(name = "k9nm7qxoa79bg7e6", kind = "raw-dylib", modifiers = "-as-needed"))] +#[cfg_attr(merge_3, link(name = "k9nm7qxoa79bg7e6", kind = "raw-dylib", modifiers = "-as-needed"))] +#[cfg_attr(merge_4, link(name = "k9nm7qxoa79bg7e6", kind = "raw-dylib", modifiers = "+as-needed"))] +unsafe extern "C" {} + +#[cfg_attr(merge_1, link(name = "k9nm7qxoa79bg7e6", kind = "raw-dylib", modifiers = "-as-needed"))] +#[cfg_attr(merge_2, link(name = "k9nm7qxoa79bg7e6", kind = "raw-dylib", modifiers = "+as-needed"))] +#[cfg_attr(merge_3, link(name = "k9nm7qxoa79bg7e6", kind = "raw-dylib", modifiers = "-as-needed"))] +#[cfg_attr(merge_4, link(name = "k9nm7qxoa79bg7e6", kind = "raw-dylib", modifiers = "+as-needed"))] +unsafe extern "C" {} + +fn main() {} From 69a975faa9804a0b693c3b57c4d5b36073f9ac16 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sat, 4 Oct 2025 16:51:58 +1000 Subject: [PATCH 189/226] Consistently import `llvm::Type` and `llvm::Value` --- compiler/rustc_codegen_llvm/src/abi.rs | 4 +--- compiler/rustc_codegen_llvm/src/asm.rs | 6 ++---- compiler/rustc_codegen_llvm/src/attributes.rs | 5 +++-- compiler/rustc_codegen_llvm/src/base.rs | 4 ++-- compiler/rustc_codegen_llvm/src/builder.rs | 4 +--- compiler/rustc_codegen_llvm/src/builder/autodiff.rs | 4 +--- compiler/rustc_codegen_llvm/src/callee.rs | 3 +-- compiler/rustc_codegen_llvm/src/common.rs | 4 +--- compiler/rustc_codegen_llvm/src/consts.rs | 5 ++--- compiler/rustc_codegen_llvm/src/context.rs | 6 ++---- compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs | 3 +-- compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs | 3 +-- compiler/rustc_codegen_llvm/src/debuginfo/mod.rs | 3 +-- compiler/rustc_codegen_llvm/src/declare.rs | 6 ++---- compiler/rustc_codegen_llvm/src/intrinsic.rs | 4 +--- compiler/rustc_codegen_llvm/src/llvm/diagnostic.rs | 2 +- compiler/rustc_codegen_llvm/src/type_.rs | 6 ++---- compiler/rustc_codegen_llvm/src/type_of.rs | 2 +- compiler/rustc_codegen_llvm/src/va_arg.rs | 3 +-- compiler/rustc_codegen_llvm/src/value.rs | 3 +-- 20 files changed, 28 insertions(+), 52 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index 15b4d5247a97..680ad98593e7 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -22,11 +22,9 @@ use smallvec::SmallVec; use crate::attributes::{self, llfn_attrs_from_instance}; use crate::builder::Builder; use crate::context::CodegenCx; -use crate::llvm::{self, Attribute, AttributePlace}; +use crate::llvm::{self, Attribute, AttributePlace, Type, Value}; use crate::llvm_util; -use crate::type_::Type; use crate::type_of::LayoutLlvmExt; -use crate::value::Value; trait ArgAttributesExt { fn apply_attrs_to_llfn(&self, idx: AttributePlace, cx: &CodegenCx<'_, '_>, llfn: &Value); diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index 93b1cf272ab7..2b6a0ac9757b 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -13,14 +13,12 @@ use rustc_target::asm::*; use smallvec::SmallVec; use tracing::debug; +use crate::attributes; use crate::builder::Builder; use crate::common::Funclet; use crate::context::CodegenCx; -use crate::llvm::ToLlvmBool; -use crate::type_::Type; +use crate::llvm::{self, ToLlvmBool, Type, Value}; use crate::type_of::LayoutLlvmExt; -use crate::value::Value; -use crate::{attributes, llvm}; impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { fn codegen_inline_asm( diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 8070ea0b3e92..209b8efa2c3b 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -13,8 +13,9 @@ use smallvec::SmallVec; use crate::context::SimpleCx; use crate::errors::SanitizerMemtagRequiresMte; use crate::llvm::AttributePlace::Function; -use crate::llvm::{self, AllocKindFlags, Attribute, AttributeKind, AttributePlace, MemoryEffects}; -use crate::value::Value; +use crate::llvm::{ + self, AllocKindFlags, Attribute, AttributeKind, AttributePlace, MemoryEffects, Value, +}; use crate::{Session, attributes, llvm_util}; pub(crate) fn apply_to_llfn(llfn: &Value, idx: AttributePlace, attrs: &[&Attribute]) { diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs index 6d12b511e9c8..4523d629b1ef 100644 --- a/compiler/rustc_codegen_llvm/src/base.rs +++ b/compiler/rustc_codegen_llvm/src/base.rs @@ -28,10 +28,10 @@ use rustc_span::Symbol; use rustc_target::spec::SanitizerSet; use super::ModuleLlvm; +use crate::attributes; use crate::builder::Builder; use crate::context::CodegenCx; -use crate::value::Value; -use crate::{attributes, llvm}; +use crate::llvm::{self, Value}; pub(crate) struct ValueIter<'ll> { cur: Option<&'ll Value>, diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 08da90d535da..e65f3d292dbb 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -37,11 +37,9 @@ use crate::common::Funclet; use crate::context::{CodegenCx, FullCx, GenericCx, SCx}; use crate::llvm::{ self, AtomicOrdering, AtomicRmwBinOp, BasicBlock, FromGeneric, GEPNoWrapFlags, Metadata, TRUE, - ToLlvmBool, + ToLlvmBool, Type, Value, }; -use crate::type_::Type; use crate::type_of::LayoutLlvmExt; -use crate::value::Value; #[must_use] pub(crate) struct GenericBuilder<'a, 'll, CX: Borrow>> { diff --git a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs index 4a749642265d..bf70a3f4a071 100644 --- a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs +++ b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs @@ -12,9 +12,7 @@ use tracing::debug; use crate::builder::{Builder, PlaceRef, UNNAMED}; use crate::context::SimpleCx; use crate::declare::declare_simple_fn; -use crate::llvm; -use crate::llvm::{Metadata, TRUE, Type}; -use crate::value::Value; +use crate::llvm::{self, Metadata, TRUE, Type, Value}; pub(crate) fn adjust_activity_to_abi<'tcx>( tcx: TyCtxt<'tcx>, diff --git a/compiler/rustc_codegen_llvm/src/callee.rs b/compiler/rustc_codegen_llvm/src/callee.rs index 791a71d73ae5..b86b32b92df0 100644 --- a/compiler/rustc_codegen_llvm/src/callee.rs +++ b/compiler/rustc_codegen_llvm/src/callee.rs @@ -10,8 +10,7 @@ use rustc_middle::ty::{self, Instance, TypeVisitableExt}; use tracing::debug; use crate::context::CodegenCx; -use crate::llvm; -use crate::value::Value; +use crate::llvm::{self, Value}; /// Codegens a reference to a fn/method item, monomorphizing and /// inlining as it goes. diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index aa2df46329f2..175fc8535ac3 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -20,9 +20,7 @@ use tracing::debug; use crate::consts::const_alloc_to_llvm; pub(crate) use crate::context::CodegenCx; use crate::context::{GenericCx, SCx}; -use crate::llvm::{self, BasicBlock, ConstantInt, FALSE, Metadata, TRUE, ToLlvmBool}; -use crate::type_::Type; -use crate::value::Value; +use crate::llvm::{self, BasicBlock, ConstantInt, FALSE, Metadata, TRUE, ToLlvmBool, Type, Value}; /* * A note on nomenclature of linking: "extern", "foreign", and "upcall". diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index b4ca85a26c8b..e14f0098dd9b 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -21,10 +21,9 @@ use tracing::{debug, instrument, trace}; use crate::common::CodegenCx; use crate::errors::SymbolAlreadyDefined; -use crate::type_::Type; +use crate::llvm::{self, Type, Value}; use crate::type_of::LayoutLlvmExt; -use crate::value::Value; -use crate::{base, debuginfo, llvm}; +use crate::{base, debuginfo}; pub(crate) fn const_alloc_to_llvm<'ll>( cx: &CodegenCx<'ll, '_>, diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 3ddbd81ef758..808aaceab4d2 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -35,10 +35,8 @@ use crate::abi::to_llvm_calling_convention; use crate::back::write::to_llvm_code_model; use crate::callee::get_fn; use crate::debuginfo::metadata::apply_vcall_visibility_metadata; -use crate::llvm::{Metadata, MetadataKindId, Module}; -use crate::type_::Type; -use crate::value::Value; -use crate::{attributes, common, coverageinfo, debuginfo, llvm, llvm_util}; +use crate::llvm::{self, Metadata, MetadataKindId, Module, Type, Value}; +use crate::{attributes, common, coverageinfo, debuginfo, llvm_util}; /// `TyCtxt` (and related cache datastructures) can't be move between threads. /// However, there are various cx related functions which we want to be available to the builder and diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs index 3081badb821f..3a750b6b53eb 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs @@ -9,8 +9,7 @@ use rustc_session::config::{CrateType, DebugInfo}; use crate::builder::Builder; use crate::common::CodegenCx; -use crate::llvm; -use crate::value::Value; +use crate::llvm::{self, Value}; /// Inserts a side-effect free instruction sequence that makes sure that the /// .debug_gdb_scripts global is referenced, so it isn't removed by the linker. diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index b8b5f3d30d7c..014715dd4fda 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -41,8 +41,7 @@ use crate::llvm::debuginfo::{ DIBasicType, DIBuilder, DICompositeType, DIDescriptor, DIFile, DIFlags, DILexicalBlock, DIScope, DIType, DebugEmissionKind, DebugNameTableKind, }; -use crate::llvm::{self, FromGeneric}; -use crate::value::Value; +use crate::llvm::{self, FromGeneric, Value}; impl PartialEq for llvm::Metadata { fn eq(&self, other: &Self) -> bool { diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index c6ad1c2e18ec..f61c25bccfad 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -35,12 +35,11 @@ use self::namespace::mangled_name_of_instance; use self::utils::{DIB, create_DIArray, is_node_local_to_unit}; use crate::builder::Builder; use crate::common::{AsCCharPtr, CodegenCx}; -use crate::llvm; use crate::llvm::debuginfo::{ DIArray, DIBuilderBox, DIFile, DIFlags, DILexicalBlock, DILocation, DISPFlags, DIScope, DITemplateTypeParameter, DIType, DIVariable, }; -use crate::value::Value; +use crate::llvm::{self, Value}; mod create_scope_map; mod dwarf_const; diff --git a/compiler/rustc_codegen_llvm/src/declare.rs b/compiler/rustc_codegen_llvm/src/declare.rs index f598763efcf2..48b2d09afb67 100644 --- a/compiler/rustc_codegen_llvm/src/declare.rs +++ b/compiler/rustc_codegen_llvm/src/declare.rs @@ -23,13 +23,11 @@ use smallvec::SmallVec; use tracing::debug; use crate::abi::FnAbiLlvmExt; +use crate::attributes; use crate::common::AsCCharPtr; use crate::context::{CodegenCx, GenericCx, SCx, SimpleCx}; use crate::llvm::AttributePlace::Function; -use crate::llvm::{FromGeneric, Visibility}; -use crate::type_::Type; -use crate::value::Value; -use crate::{attributes, llvm}; +use crate::llvm::{self, FromGeneric, Type, Value, Visibility}; /// Declare a function with a SimpleCx. /// diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 467655b0bfcd..c12383f19312 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -25,11 +25,9 @@ use crate::builder::Builder; use crate::builder::autodiff::{adjust_activity_to_abi, generate_enzyme_call}; use crate::context::CodegenCx; use crate::errors::AutoDiffWithoutEnable; -use crate::llvm::{self, Metadata}; -use crate::type_::Type; +use crate::llvm::{self, Metadata, Type, Value}; use crate::type_of::LayoutLlvmExt; use crate::va_arg::emit_va_arg; -use crate::value::Value; fn call_simple_intrinsic<'ll, 'tcx>( bx: &mut Builder<'_, 'll, 'tcx>, diff --git a/compiler/rustc_codegen_llvm/src/llvm/diagnostic.rs b/compiler/rustc_codegen_llvm/src/llvm/diagnostic.rs index 0e0f2b0eab01..59b2cd329ae7 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/diagnostic.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/diagnostic.rs @@ -6,7 +6,7 @@ use rustc_span::InnerSpan; pub(crate) use self::Diagnostic::*; use self::OptimizationDiagnosticKind::*; use super::{DiagnosticInfo, SMDiagnostic}; -use crate::value::Value; +use crate::llvm::Value; #[derive(Copy, Clone, Debug)] pub(crate) enum OptimizationDiagnosticKind { diff --git a/compiler/rustc_codegen_llvm/src/type_.rs b/compiler/rustc_codegen_llvm/src/type_.rs index 9de9e0ec44c6..81bb70c95879 100644 --- a/compiler/rustc_codegen_llvm/src/type_.rs +++ b/compiler/rustc_codegen_llvm/src/type_.rs @@ -13,12 +13,10 @@ use rustc_middle::ty::{self, Ty}; use rustc_target::callconv::{CastTarget, FnAbi}; use crate::abi::{FnAbiLlvmExt, LlvmType}; +use crate::common; use crate::context::{CodegenCx, GenericCx, SCx}; -pub(crate) use crate::llvm::Type; -use crate::llvm::{FALSE, Metadata, TRUE, ToLlvmBool}; +use crate::llvm::{self, FALSE, Metadata, TRUE, ToLlvmBool, Type, Value}; use crate::type_of::LayoutLlvmExt; -use crate::value::Value; -use crate::{common, llvm}; impl PartialEq for Type { fn eq(&self, other: &Self) -> bool { diff --git a/compiler/rustc_codegen_llvm/src/type_of.rs b/compiler/rustc_codegen_llvm/src/type_of.rs index 84998b5499bd..2b2ac1c6eb29 100644 --- a/compiler/rustc_codegen_llvm/src/type_of.rs +++ b/compiler/rustc_codegen_llvm/src/type_of.rs @@ -11,7 +11,7 @@ use rustc_span::{DUMMY_SP, Span}; use tracing::debug; use crate::common::*; -use crate::type_::Type; +use crate::llvm::Type; fn uncached_llvm_type<'a, 'tcx>( cx: &CodegenCx<'a, 'tcx>, diff --git a/compiler/rustc_codegen_llvm/src/va_arg.rs b/compiler/rustc_codegen_llvm/src/va_arg.rs index 234366e491c5..2d9abb412d4f 100644 --- a/compiler/rustc_codegen_llvm/src/va_arg.rs +++ b/compiler/rustc_codegen_llvm/src/va_arg.rs @@ -9,9 +9,8 @@ use rustc_middle::ty::Ty; use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf}; use crate::builder::Builder; -use crate::type_::Type; +use crate::llvm::{Type, Value}; use crate::type_of::LayoutLlvmExt; -use crate::value::Value; fn round_up_to_alignment<'ll>( bx: &mut Builder<'_, 'll, '_>, diff --git a/compiler/rustc_codegen_llvm/src/value.rs b/compiler/rustc_codegen_llvm/src/value.rs index 2eabac3be8c5..37f05f10c50c 100644 --- a/compiler/rustc_codegen_llvm/src/value.rs +++ b/compiler/rustc_codegen_llvm/src/value.rs @@ -1,8 +1,7 @@ use std::hash::{Hash, Hasher}; use std::{fmt, ptr}; -use crate::llvm; -pub(crate) use crate::llvm::Value; +use crate::llvm::{self, Value}; impl PartialEq for Value { fn eq(&self, other: &Self) -> bool { From f54139aa310b4cd2cb2bfc144eec865464a8cc09 Mon Sep 17 00:00:00 2001 From: The rustc-josh-sync Cronjob Bot Date: Mon, 6 Oct 2025 04:13:15 +0000 Subject: [PATCH 190/226] Prepare for merging from rust-lang/rust This updates the rust-version file to 4fa824bb78318a3cba8c7339d5754b4909922547. --- 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 0dc9ce843e9c..47552aee08f7 100644 --- a/src/doc/rustc-dev-guide/rust-version +++ b/src/doc/rustc-dev-guide/rust-version @@ -1 +1 @@ -9f32ccf35fb877270bc44a86a126440f04d676d0 +4fa824bb78318a3cba8c7339d5754b4909922547 From eeb7cb1b8a42630458e6c6cdabf0c4264579aec7 Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Mon, 22 Sep 2025 07:39:48 +0200 Subject: [PATCH 191/226] compiler: Hint at multiple crate versions if trait impl is for wrong ADT If a user does e.g. impl From for foo::Foo and get a compilation error about that `From` is not implemented for `Foo`, check if multiple versions of the crate with `Foo` is present in the dependency graph. If so, give a hint about it. I encountered this case in the wild and didn't realize I had multiple versions of a crate in my dependency graph. So I was a bit confused at first. This fix will make life easier for others. --- .../traits/fulfillment_errors.rs | 85 +++++++++++++++++-- .../run-make/duplicate-dependency/main.stderr | 6 ++ tests/run-make/duplicate-dependency/rmake.rs | 4 +- 3 files changed, 88 insertions(+), 7 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 149f5e638b1a..7728f657beda 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 @@ -467,7 +467,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { span, leaf_trait_predicate, ); - self.note_version_mismatch(&mut err, leaf_trait_predicate); + self.note_trait_version_mismatch(&mut err, leaf_trait_predicate); + self.note_adt_version_mismatch(&mut err, leaf_trait_predicate); self.suggest_remove_await(&obligation, &mut err); self.suggest_derive(&obligation, &mut err, leaf_trait_predicate); @@ -2406,7 +2407,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { /// If the `Self` type of the unsatisfied trait `trait_ref` implements a trait /// with the same path as `trait_ref`, a help message about /// a probable version mismatch is added to `err` - fn note_version_mismatch( + fn note_trait_version_mismatch( &self, err: &mut Diag<'_>, trait_pred: ty::PolyTraitPredicate<'tcx>, @@ -2446,15 +2447,87 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { impl_spans, format!("trait impl{} with same name found", pluralize!(trait_impls.len())), ); - let trait_crate = self.tcx.crate_name(trait_with_same_path.krate); - let crate_msg = - format!("perhaps two different versions of crate `{trait_crate}` are being used?"); - err.note(crate_msg); + self.note_two_crate_versions(trait_with_same_path, err); suggested = true; } suggested } + fn note_two_crate_versions(&self, did: DefId, err: &mut Diag<'_>) { + let crate_name = self.tcx.crate_name(did.krate); + let crate_msg = + format!("perhaps two different versions of crate `{crate_name}` are being used?"); + err.note(crate_msg); + } + + fn note_adt_version_mismatch( + &self, + err: &mut Diag<'_>, + trait_pred: ty::PolyTraitPredicate<'tcx>, + ) { + let ty::Adt(impl_self_def, _) = trait_pred.self_ty().skip_binder().peel_refs().kind() + else { + return; + }; + + let impl_self_did = impl_self_def.did(); + + // We only want to warn about different versions of a dependency. + // If no dependency is involved, bail. + if impl_self_did.krate == LOCAL_CRATE { + return; + } + + let impl_self_path = self.comparable_path(impl_self_did); + let impl_self_crate_name = self.tcx.crate_name(impl_self_did.krate); + let similar_items: UnordSet<_> = self + .tcx + .visible_parent_map(()) + .items() + .filter_map(|(&item, _)| { + // If we found ourselves, ignore. + if impl_self_did == item { + return None; + } + // We only want to warn about different versions of a dependency. + // Ignore items from our own crate. + if item.krate == LOCAL_CRATE { + return None; + } + // We want to warn about different versions of a dependency. + // So make sure the crate names are the same. + if impl_self_crate_name != self.tcx.crate_name(item.krate) { + return None; + } + // Filter out e.g. constructors that often have the same path + // str as the relevant ADT. + if !self.tcx.def_kind(item).is_adt() { + return None; + } + let path = self.comparable_path(item); + // We don't know if our item or the one we found is the re-exported one. + // Check both cases. + let is_similar = path.ends_with(&impl_self_path) || impl_self_path.ends_with(&path); + is_similar.then_some((item, path)) + }) + .collect(); + + let mut similar_items = + similar_items.into_items().into_sorted_stable_ord_by_key(|(_, path)| path); + similar_items.dedup(); + + for (similar_item, _) in similar_items { + err.span_help(self.tcx.def_span(similar_item), "item with same name found"); + self.note_two_crate_versions(similar_item, err); + } + } + + /// Add a `::` prefix when comparing paths so that paths with just one item + /// like "Foo" does not equal the end of "OtherFoo". + fn comparable_path(&self, did: DefId) -> String { + format!("::{}", self.tcx.def_path_str(did)) + } + /// Creates a `PredicateObligation` with `new_self_ty` replacing the existing type in the /// `trait_ref`. /// diff --git a/tests/run-make/duplicate-dependency/main.stderr b/tests/run-make/duplicate-dependency/main.stderr index 2c912f872fe3..36d54988788f 100644 --- a/tests/run-make/duplicate-dependency/main.stderr +++ b/tests/run-make/duplicate-dependency/main.stderr @@ -6,6 +6,12 @@ LL | re_export_foo::into_foo(Bar); | | | required by a bound introduced by this call | +help: item with same name found + --> $DIR/foo-v1.rs:1:1 + | +LL | pub struct Foo; + | ^^^^^^^^^^^^^^ + = note: perhaps two different versions of crate `foo` are being used? = note: required for `Bar` to implement `Into` note: required by a bound in `into_foo` --> $DIR/re-export-foo.rs:3:25 diff --git a/tests/run-make/duplicate-dependency/rmake.rs b/tests/run-make/duplicate-dependency/rmake.rs index 13ab4caaba56..762d97e4311f 100644 --- a/tests/run-make/duplicate-dependency/rmake.rs +++ b/tests/run-make/duplicate-dependency/rmake.rs @@ -1,3 +1,5 @@ +//@ needs-target-std + use run_make_support::{Rustc, cwd, diff, rust_lib_name, rustc}; fn rustc_with_common_args() -> Rustc { @@ -39,5 +41,5 @@ fn main() { .run_fail() .stderr_utf8(); - diff().expected_file("main.stderr").actual_text("(rustc)", &stderr).run(); + diff().expected_file("main.stderr").normalize(r"\\", "/").actual_text("(rustc)", &stderr).run(); } From 98afeb86453540ebdea0af21ba5064caed6b089b Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Mon, 6 Oct 2025 07:06:27 +0200 Subject: [PATCH 192/226] fix text describing edition range testing --- src/doc/rustc-dev-guide/src/tests/directives.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/tests/directives.md b/src/doc/rustc-dev-guide/src/tests/directives.md index 3b6c89174645..25c8a4c354ab 100644 --- a/src/doc/rustc-dev-guide/src/tests/directives.md +++ b/src/doc/rustc-dev-guide/src/tests/directives.md @@ -264,11 +264,16 @@ Consider writing the test as a proper incremental test instead. #### The edition directive -The `//@ edition` directive can take an exact edition, a bounded half-open range of editions or a left-bounded half-open range of editions, this affects which edition is used by `./x test` to run the test. For example: +The `//@ edition` directive can take an exact edition, a bounded range of editions, or a left-bounded half-open range of editions. +This affects which edition is used by `./x test` to run the test. + +For example: - A test with the `//@ edition: 2018` directive will only run under the 2018 edition. -- A test with the `//@ edition: 2015..2021` directive can be run under both the 2015 and 2018 editions. However, CI will only run the test with the lowest edition possible (2015 in this case). -- A test with the `//@ edition: 2018..` directive will run under any edition greater or equal than 2018. However, CI will only run the test with the lowest edition possible (2018 in this case). +- A test with the `//@ edition: 2015..2021` directive can be run under the 2015, 2018, and 2021 editions. + However, CI will only run the test with the lowest edition in the range (which is 2015 in this example). +- A test with the `//@ edition: 2018..` directive will run under 2018 edition or greater. + However, CI will only run the test with the lowest edition in the range (which is 2018 in this example). You can also force `./x test` to use a specific edition by passing the `-- --edition=` argument. However, tests with the `//@ edition` directive will clamp the value passed to the argument. For example, if we run `./x test -- --edition=2015`: From 2d6f590af115f16c57796162703cca62a5860ebd Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Mon, 6 Oct 2025 07:13:01 +0200 Subject: [PATCH 193/226] overlong --- src/doc/rustc-dev-guide/src/tests/directives.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/tests/directives.md b/src/doc/rustc-dev-guide/src/tests/directives.md index 25c8a4c354ab..2c6cfeb0ac70 100644 --- a/src/doc/rustc-dev-guide/src/tests/directives.md +++ b/src/doc/rustc-dev-guide/src/tests/directives.md @@ -264,7 +264,8 @@ Consider writing the test as a proper incremental test instead. #### The edition directive -The `//@ edition` directive can take an exact edition, a bounded range of editions, or a left-bounded half-open range of editions. +The `//@ edition` directive can take an exact edition, a bounded range of editions, +or a left-bounded half-open range of editions. This affects which edition is used by `./x test` to run the test. For example: @@ -275,7 +276,9 @@ For example: - A test with the `//@ edition: 2018..` directive will run under 2018 edition or greater. However, CI will only run the test with the lowest edition in the range (which is 2018 in this example). -You can also force `./x test` to use a specific edition by passing the `-- --edition=` argument. However, tests with the `//@ edition` directive will clamp the value passed to the argument. For example, if we run `./x test -- --edition=2015`: +You can also force `./x test` to use a specific edition by passing the `-- --edition=` argument. +However, tests with the `//@ edition` directive will clamp the value passed to the argument. +For example, if we run `./x test -- --edition=2015`: - A test with the `//@ edition: 2018` will run with the 2018 edition. - A test with the `//@ edition: 2015..2021` will be run with the 2015 edition. From d24ee20876dc3438810ddf640f6ceb732bd75ed3 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 6 Oct 2025 12:09:57 +0200 Subject: [PATCH 194/226] use `fluent_syntax` to parse fluent files --- src/tools/tidy/src/fluent_alphabetical.rs | 41 ++++++++++++----------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/src/tools/tidy/src/fluent_alphabetical.rs b/src/tools/tidy/src/fluent_alphabetical.rs index b1d1c16b4549..1fadc523591c 100644 --- a/src/tools/tidy/src/fluent_alphabetical.rs +++ b/src/tools/tidy/src/fluent_alphabetical.rs @@ -5,6 +5,8 @@ use std::fs::OpenOptions; use std::io::Write; use std::path::Path; +use fluent_syntax::ast::Entry; +use fluent_syntax::parser; use regex::Regex; use crate::diagnostics::{CheckId, DiagCtx, RunningCheck}; @@ -24,30 +26,31 @@ fn check_alphabetic( check: &mut RunningCheck, all_defined_msgs: &mut HashMap, ) { - let mut matches = message().captures_iter(fluent).peekable(); - while let Some(m) = matches.next() { - let name = m.get(1).unwrap(); - if let Some(defined_filename) = all_defined_msgs.get(name.as_str()) { - check.error(format!( - "{filename}: message `{}` is already defined in {defined_filename}", - name.as_str(), - )); - } + let Ok(resource) = parser::parse(fluent) else { + panic!("Errors encountered while parsing fluent file `{filename}`"); + }; - all_defined_msgs.insert(name.as_str().to_owned(), filename.to_owned()); + let mut prev: Option<&str> = None; - if let Some(next) = matches.peek() { - let next = next.get(1).unwrap(); - if name.as_str() > next.as_str() { + for entry in &resource.body { + if let Entry::Message(msg) = entry { + let name: &str = msg.id.name; + if let Some(defined_filename) = all_defined_msgs.get(name) { check.error(format!( - "{filename}: message `{}` appears before `{}`, but is alphabetically later than it -run `./x.py test tidy --bless` to sort the file correctly", - name.as_str(), - next.as_str() + "{filename}: message `{name}` is already defined in {defined_filename}", + )); + } else { + all_defined_msgs.insert(name.to_string(), filename.to_owned()); + } + if let Some(prev) = prev + && prev > name + { + check.error(format!( + "{filename}: message `{prev}` appears before `{name}`, but is alphabetically \ +later than it. Run `./x.py test tidy --bless` to sort the file correctly", )); } - } else { - break; + prev = Some(name); } } } From 831cdf3144f23871e9923ddeb9a36d0192e7244a Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 6 Oct 2025 12:11:56 +0200 Subject: [PATCH 195/226] Fail if no fluent messages were found --- src/tools/tidy/src/fluent_alphabetical.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tools/tidy/src/fluent_alphabetical.rs b/src/tools/tidy/src/fluent_alphabetical.rs index 1fadc523591c..769f92d04f0a 100644 --- a/src/tools/tidy/src/fluent_alphabetical.rs +++ b/src/tools/tidy/src/fluent_alphabetical.rs @@ -118,5 +118,7 @@ pub fn check(path: &Path, bless: bool, diag_ctx: DiagCtx) { }, ); + assert!(!all_defined_msgs.is_empty()); + crate::fluent_used::check(path, all_defined_msgs, diag_ctx); } From f4b4fb9b7f1af29c3b9f6e25229a2514d5db35c7 Mon Sep 17 00:00:00 2001 From: Teodoro Freund Date: Mon, 6 Oct 2025 09:35:46 -0300 Subject: [PATCH 196/226] Fixed some lint deprecated versions --- clippy_lints/src/deprecated_lints.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index 80b74f50223a..2147f7288909 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -18,11 +18,11 @@ declare_with_version! { DEPRECATED(DEPRECATED_VERSION) = [ ("clippy::assign_ops", "compound operators are harmless and linting on them is not in scope for clippy"), #[clippy::version = "pre 1.29.0"] ("clippy::extend_from_slice", "`Vec::extend_from_slice` is no longer faster than `Vec::extend` due to specialization"), - #[clippy::version = "1.86.0"] + #[clippy::version = "1.88.0"] ("clippy::match_on_vec_items", "`clippy::indexing_slicing` covers indexing and slicing on `Vec<_>`"), #[clippy::version = "pre 1.29.0"] ("clippy::misaligned_transmute", "split into `clippy::cast_ptr_alignment` and `clippy::transmute_ptr_to_ptr`"), - #[clippy::version = "1.86.0"] + #[clippy::version = "1.87.0"] ("clippy::option_map_or_err_ok", "`clippy::manual_ok_or` covers this case"), #[clippy::version = "1.54.0"] ("clippy::pub_enum_variant_names", "`clippy::enum_variant_names` now covers this case via the `avoid-breaking-exported-api` config"), @@ -34,7 +34,7 @@ declare_with_version! { DEPRECATED(DEPRECATED_VERSION) = [ ("clippy::replace_consts", "`min_value` and `max_value` are now deprecated"), #[clippy::version = "pre 1.29.0"] ("clippy::should_assert_eq", "`assert!(a == b)` can now print the values the same way `assert_eq!(a, b) can"), - #[clippy::version = "1.90.0"] + #[clippy::version = "1.91.0"] ("clippy::string_to_string", "`clippy:implicit_clone` covers those cases"), #[clippy::version = "pre 1.29.0"] ("clippy::unsafe_vector_initialization", "the suggested alternative could be substantially slower"), From 696b6ac14dd9c3aaae12c000cf5fa1bd0fec8c34 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Sat, 16 Aug 2025 22:08:41 +0800 Subject: [PATCH 197/226] use declarative macro for `#[derive(TryFromU32)]` --- .../src/coverageinfo/mapgen.rs | 3 +- compiler/rustc_codegen_llvm/src/lib.rs | 4 ++ compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 3 +- compiler/rustc_codegen_llvm/src/macros.rs | 22 ++++++++ compiler/rustc_macros/src/lib.rs | 9 --- compiler/rustc_macros/src/try_from.rs | 55 ------------------- tests/ui-fulldeps/try-from-u32/errors.rs | 24 -------- tests/ui-fulldeps/try-from-u32/errors.stderr | 32 ----------- tests/ui-fulldeps/try-from-u32/hygiene.rs | 32 ----------- tests/ui-fulldeps/try-from-u32/values.rs | 36 ------------ 10 files changed, 28 insertions(+), 192 deletions(-) create mode 100644 compiler/rustc_codegen_llvm/src/macros.rs delete mode 100644 compiler/rustc_macros/src/try_from.rs delete mode 100644 tests/ui-fulldeps/try-from-u32/errors.rs delete mode 100644 tests/ui-fulldeps/try-from-u32/errors.stderr delete mode 100644 tests/ui-fulldeps/try-from-u32/hygiene.rs delete mode 100644 tests/ui-fulldeps/try-from-u32/values.rs diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs index d1cb95507d91..7e873347c82b 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs @@ -6,7 +6,6 @@ use rustc_abi::Align; use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, ConstCodegenMethods}; use rustc_data_structures::fx::FxIndexMap; use rustc_index::IndexVec; -use rustc_macros::TryFromU32; use rustc_middle::ty::TyCtxt; use rustc_session::RemapFileNameExt; use rustc_session::config::RemapPathScopeComponents; @@ -16,7 +15,7 @@ use tracing::debug; use crate::common::CodegenCx; use crate::coverageinfo::llvm_cov; use crate::coverageinfo::mapgen::covfun::prepare_covfun_record; -use crate::llvm; +use crate::{TryFromU32, llvm}; mod covfun; mod spans; diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 2405a25c7020..807049f08d36 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -14,6 +14,7 @@ #![feature(if_let_guard)] #![feature(impl_trait_in_assoc_type)] #![feature(iter_intersperse)] +#![feature(macro_derive)] #![feature(rustdoc_internals)] #![feature(slice_as_array)] #![feature(try_blocks)] @@ -65,6 +66,7 @@ mod errors; mod intrinsic; mod llvm; mod llvm_util; +mod macros; mod mono_item; mod type_; mod type_of; @@ -74,6 +76,8 @@ mod value; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } +pub(crate) use macros::TryFromU32; + #[derive(Clone)] pub struct LlvmCodegenBackend(()); diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index ccbc999dad1f..c4b5cf413a72 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -19,7 +19,6 @@ use std::ptr; use bitflags::bitflags; use libc::{c_char, c_int, c_uchar, c_uint, c_ulonglong, c_void, size_t}; -use rustc_macros::TryFromU32; use super::RustString; use super::debuginfo::{ @@ -27,8 +26,8 @@ use super::debuginfo::{ DIGlobalVariableExpression, DILocation, DISPFlags, DIScope, DISubprogram, DITemplateTypeParameter, DIType, DebugEmissionKind, DebugNameTableKind, }; -use crate::llvm; use crate::llvm::MetadataKindId; +use crate::{TryFromU32, llvm}; /// In the LLVM-C API, boolean values are passed as `typedef int LLVMBool`, /// which has a different ABI from Rust or C++ `bool`. diff --git a/compiler/rustc_codegen_llvm/src/macros.rs b/compiler/rustc_codegen_llvm/src/macros.rs new file mode 100644 index 000000000000..fddc428ca273 --- /dev/null +++ b/compiler/rustc_codegen_llvm/src/macros.rs @@ -0,0 +1,22 @@ +macro_rules! TryFromU32 { + derive() ( + $(#[$meta:meta])* + $vis:vis enum $Type:ident { + $( + $(#[$varmeta:meta])* + $Variant:ident $(= $discr:expr)? + ),* $(,)? + } + ) => { + impl ::core::convert::TryFrom for $Type { + type Error = u32; + #[allow(deprecated)] // Don't warn about deprecated variants. + fn try_from(value: u32) -> ::core::result::Result<$Type, Self::Error> { + $( if value == const { $Type::$Variant as u32 } { return Ok($Type::$Variant) } )* + Err(value) + } + } + } +} + +pub(crate) use TryFromU32; diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs index 803b3621c887..a6f53d92e100 100644 --- a/compiler/rustc_macros/src/lib.rs +++ b/compiler/rustc_macros/src/lib.rs @@ -18,7 +18,6 @@ mod print_attribute; mod query; mod serialize; mod symbols; -mod try_from; mod type_foldable; mod type_visitable; mod visitable; @@ -176,14 +175,6 @@ decl_derive!( applicability)] => diagnostics::subdiagnostic_derive ); -decl_derive! { - [TryFromU32] => - /// Derives `TryFrom` for the annotated `enum`, which must have no fields. - /// Each variant maps to the value it would produce under an `as u32` cast. - /// - /// The error type is `u32`. - try_from::try_from_u32 -} decl_derive! { [PrintAttribute] => /// Derives `PrintAttribute` for `AttributeKind`. diff --git a/compiler/rustc_macros/src/try_from.rs b/compiler/rustc_macros/src/try_from.rs deleted file mode 100644 index 9338c1c2b336..000000000000 --- a/compiler/rustc_macros/src/try_from.rs +++ /dev/null @@ -1,55 +0,0 @@ -use proc_macro2::TokenStream; -use quote::{quote, quote_spanned}; -use syn::Data; -use syn::spanned::Spanned; -use synstructure::Structure; - -pub(crate) fn try_from_u32(s: Structure<'_>) -> TokenStream { - let span_error = |span, message: &str| { - quote_spanned! { span => const _: () = ::core::compile_error!(#message); } - }; - - // Must be applied to an enum type. - if let Some(span) = match &s.ast().data { - Data::Enum(_) => None, - Data::Struct(s) => Some(s.struct_token.span()), - Data::Union(u) => Some(u.union_token.span()), - } { - return span_error(span, "type is not an enum (TryFromU32)"); - } - - // The enum's variants must not have fields. - let variant_field_errors = s - .variants() - .iter() - .filter_map(|v| v.ast().fields.iter().map(|f| f.span()).next()) - .map(|span| span_error(span, "enum variant cannot have fields (TryFromU32)")) - .collect::(); - if !variant_field_errors.is_empty() { - return variant_field_errors; - } - - let ctor = s - .variants() - .iter() - .map(|v| v.construct(|_, _| -> TokenStream { unreachable!() })) - .collect::>(); - // FIXME(edition_2024): Fix the `keyword_idents_2024` lint to not trigger here? - #[allow(keyword_idents_2024)] - s.gen_impl(quote! { - // The surrounding code might have shadowed these identifiers. - use ::core::convert::TryFrom; - use ::core::primitive::u32; - use ::core::result::Result::{self, Ok, Err}; - - gen impl TryFrom for @Self { - type Error = u32; - - #[allow(deprecated)] // Don't warn about deprecated variants. - fn try_from(value: u32) -> Result { - #( if value == const { #ctor as u32 } { return Ok(#ctor) } )* - Err(value) - } - } - }) -} diff --git a/tests/ui-fulldeps/try-from-u32/errors.rs b/tests/ui-fulldeps/try-from-u32/errors.rs deleted file mode 100644 index a25069c0a53c..000000000000 --- a/tests/ui-fulldeps/try-from-u32/errors.rs +++ /dev/null @@ -1,24 +0,0 @@ -#![feature(rustc_private)] -//@ edition: 2021 - -// Checks the error messages produced by `#[derive(TryFromU32)]`. - -extern crate rustc_macros; - -use rustc_macros::TryFromU32; - -#[derive(TryFromU32)] -struct MyStruct {} //~ ERROR type is not an enum - -#[derive(TryFromU32)] -enum NonTrivial { - A, - B(), - C {}, - D(bool), //~ ERROR enum variant cannot have fields - E(bool, bool), //~ ERROR enum variant cannot have fields - F { x: bool }, //~ ERROR enum variant cannot have fields - G { x: bool, y: bool }, //~ ERROR enum variant cannot have fields -} - -fn main() {} diff --git a/tests/ui-fulldeps/try-from-u32/errors.stderr b/tests/ui-fulldeps/try-from-u32/errors.stderr deleted file mode 100644 index d20567061d7b..000000000000 --- a/tests/ui-fulldeps/try-from-u32/errors.stderr +++ /dev/null @@ -1,32 +0,0 @@ -error: type is not an enum (TryFromU32) - --> $DIR/errors.rs:11:1 - | -LL | struct MyStruct {} - | ^^^^^^ - -error: enum variant cannot have fields (TryFromU32) - --> $DIR/errors.rs:18:7 - | -LL | D(bool), - | ^^^^ - -error: enum variant cannot have fields (TryFromU32) - --> $DIR/errors.rs:19:7 - | -LL | E(bool, bool), - | ^^^^ - -error: enum variant cannot have fields (TryFromU32) - --> $DIR/errors.rs:20:9 - | -LL | F { x: bool }, - | ^ - -error: enum variant cannot have fields (TryFromU32) - --> $DIR/errors.rs:21:9 - | -LL | G { x: bool, y: bool }, - | ^ - -error: aborting due to 5 previous errors - diff --git a/tests/ui-fulldeps/try-from-u32/hygiene.rs b/tests/ui-fulldeps/try-from-u32/hygiene.rs deleted file mode 100644 index e0655a64a64d..000000000000 --- a/tests/ui-fulldeps/try-from-u32/hygiene.rs +++ /dev/null @@ -1,32 +0,0 @@ -#![feature(rustc_private)] -//@ edition: 2021 -//@ check-pass - -// Checks that the derive macro still works even if the surrounding code has -// shadowed the relevant library types. - -extern crate rustc_macros; - -mod submod { - use rustc_macros::TryFromU32; - - struct Result; - trait TryFrom {} - #[allow(non_camel_case_types)] - struct u32; - struct Ok; - struct Err; - mod core {} - mod std {} - - #[derive(TryFromU32)] - pub(crate) enum MyEnum { - Zero, - One, - } -} - -fn main() { - use submod::MyEnum; - let _: Result = MyEnum::try_from(1u32); -} diff --git a/tests/ui-fulldeps/try-from-u32/values.rs b/tests/ui-fulldeps/try-from-u32/values.rs deleted file mode 100644 index 180a8f2beb75..000000000000 --- a/tests/ui-fulldeps/try-from-u32/values.rs +++ /dev/null @@ -1,36 +0,0 @@ -#![feature(assert_matches)] -#![feature(rustc_private)] -//@ edition: 2021 -//@ run-pass - -// Checks the values accepted by the `TryFrom` impl produced by `#[derive(TryFromU32)]`. - -extern crate rustc_macros; - -use core::assert_matches::assert_matches; -use rustc_macros::TryFromU32; - -#[derive(TryFromU32, Debug, PartialEq)] -#[repr(u32)] -enum Repr { - Zero, - One(), - Seven = 7, -} - -#[derive(TryFromU32, Debug)] -enum NoRepr { - Zero, - One, -} - -fn main() { - assert_eq!(Repr::try_from(0u32), Ok(Repr::Zero)); - assert_eq!(Repr::try_from(1u32), Ok(Repr::One())); - assert_eq!(Repr::try_from(2u32), Err(2)); - assert_eq!(Repr::try_from(7u32), Ok(Repr::Seven)); - - assert_matches!(NoRepr::try_from(0u32), Ok(NoRepr::Zero)); - assert_matches!(NoRepr::try_from(1u32), Ok(NoRepr::One)); - assert_matches!(NoRepr::try_from(2u32), Err(2)); -} From 085ddaaa97bf55914a33938462d133dd597c8f0b Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Mon, 6 Oct 2025 17:26:17 +0200 Subject: [PATCH 198/226] Bump nightly version -> 2025-10-06 --- clippy_utils/README.md | 2 +- rust-toolchain.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_utils/README.md b/clippy_utils/README.md index 2c66fdc73f53..1f678a6a29f0 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-09-18 +nightly-2025-10-06 ``` diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 9c102de44820..e936f5dc3b7a 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2025-09-18" +channel = "nightly-2025-10-06" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" From 8c4be66084f62b6de4dece52f517b455b802edca Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Mon, 6 Oct 2025 18:10:44 +0200 Subject: [PATCH 199/226] Update Cargo.lock --- Cargo.lock | 101 ++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 80 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2de9c84e3b03..92a9013ca257 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -216,7 +216,7 @@ dependencies = [ "memchr", "serde", "serde_derive", - "winnow 0.7.12", + "winnow 0.7.13", ] [[package]] @@ -590,7 +590,7 @@ dependencies = [ "serde_json", "tempfile", "termize", - "toml 0.7.8", + "toml 0.9.7", "ui_test", "walkdir", ] @@ -632,7 +632,7 @@ dependencies = [ "regex-syntax 0.8.5", "semver", "serde", - "toml 0.7.8", + "toml 0.9.7", "unicode-normalization", "unicode-script", "url", @@ -1866,13 +1866,14 @@ checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5" [[package]] name = "indexmap" -version = "2.10.0" +version = "2.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" dependencies = [ "equivalent", "hashbrown", "serde", + "serde_core", ] [[package]] @@ -5003,10 +5004,11 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ + "serde_core", "serde_derive", ] @@ -5032,10 +5034,19 @@ dependencies = [ ] [[package]] -name = "serde_derive" -version = "1.0.219" +name = "serde_core" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", @@ -5084,6 +5095,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5417783452c2be558477e104686f7de5dae53dba813c28435e0e70f82d9b04ee" +dependencies = [ + "serde_core", +] + [[package]] name = "sha1" version = "0.10.6" @@ -5545,8 +5565,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" dependencies = [ "serde", - "serde_spanned", - "toml_datetime", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", "toml_edit 0.19.15", ] @@ -5558,11 +5578,26 @@ checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" dependencies = [ "indexmap", "serde", - "serde_spanned", - "toml_datetime", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", "toml_edit 0.22.27", ] +[[package]] +name = "toml" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00e5e5d9bf2475ac9d4f0d9edab68cc573dc2fd644b0dba36b0c30a92dd9eaa0" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned 1.0.2", + "toml_datetime 0.7.2", + "toml_parser", + "toml_writer", + "winnow 0.7.13", +] + [[package]] name = "toml_datetime" version = "0.6.11" @@ -5572,6 +5607,15 @@ dependencies = [ "serde", ] +[[package]] +name = "toml_datetime" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f1085dec27c2b6632b04c80b3bb1b4300d6495d1e129693bdda7d91e72eec1" +dependencies = [ + "serde_core", +] + [[package]] name = "toml_edit" version = "0.19.15" @@ -5580,8 +5624,8 @@ checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ "indexmap", "serde", - "serde_spanned", - "toml_datetime", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", "winnow 0.5.40", ] @@ -5593,10 +5637,19 @@ checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ "indexmap", "serde", - "serde_spanned", - "toml_datetime", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", "toml_write", - "winnow 0.7.12", + "winnow 0.7.13", +] + +[[package]] +name = "toml_parser" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cf893c33be71572e0e9aa6dd15e6677937abd686b066eac3f8cd3531688a627" +dependencies = [ + "winnow 0.7.13", ] [[package]] @@ -5605,6 +5658,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" +[[package]] +name = "toml_writer" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d163a63c116ce562a22cda521fcc4d79152e7aba014456fb5eb442f6d6a10109" + [[package]] name = "tracing" version = "0.1.41" @@ -6569,9 +6628,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.12" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" +checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" dependencies = [ "memchr", ] From e652436f2adbb58f8754a12f8040b5093bd1e039 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Mon, 6 Oct 2025 18:18:33 +0200 Subject: [PATCH 200/226] Allow serde_core as a dependency --- src/bootstrap/src/utils/proc_macro_deps.rs | 1 + src/tools/tidy/src/deps.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/bootstrap/src/utils/proc_macro_deps.rs b/src/bootstrap/src/utils/proc_macro_deps.rs index db2369097d6c..83e5580eaa91 100644 --- a/src/bootstrap/src/utils/proc_macro_deps.rs +++ b/src/bootstrap/src/utils/proc_macro_deps.rs @@ -43,6 +43,7 @@ pub static CRATES: &[&str] = &[ "rustc-hash", "self_cell", "serde", + "serde_core", "serde_derive_internals", "sha2", "smallvec", diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index c76b46ec2bf2..3f40bef821c9 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -458,6 +458,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "scopeguard", "self_cell", "serde", + "serde_core", "serde_derive", "serde_derive_internals", "serde_json", From 490fd3ddd24418188dc6e419ed54ac0fa16f2cf4 Mon Sep 17 00:00:00 2001 From: rustbot <47979223+rustbot@users.noreply.github.com> Date: Mon, 6 Oct 2025 19:01:25 +0200 Subject: [PATCH 201/226] Update books --- src/doc/reference | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/reference b/src/doc/reference index e11adf6016a3..8efb98056867 160000 --- a/src/doc/reference +++ b/src/doc/reference @@ -1 +1 @@ -Subproject commit e11adf6016a362766eea5a3f9832e193994dd0c8 +Subproject commit 8efb9805686722dba511b7b27281bb6b77d32130 From e0c20a0476ef961121aa6c314a0f1c25870a97e3 Mon Sep 17 00:00:00 2001 From: Urgau Date: Mon, 6 Oct 2025 19:25:40 +0200 Subject: [PATCH 202/226] Update `S-waiting-on-team` refs to new `S-waiting-on-{team}` labels --- rust-bors.toml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/rust-bors.toml b/rust-bors.toml index 56f48512b06a..1014e31bae49 100644 --- a/rust-bors.toml +++ b/rust-bors.toml @@ -15,5 +15,12 @@ labels_blocking_approval = [ "S-waiting-on-crater", "S-waiting-on-fcp", "S-waiting-on-MCP", - "S-waiting-on-team" + "S-waiting-on-lang", + "S-waiting-on-compiler", + "S-waiting-on-libs-api", + "S-waiting-on-types", + "S-waiting-on-opsem", + "S-waiting-on-rustdoc", + "S-waiting-on-rustdoc-frontend", + "S-waiting-on-clippy" ] From 02126adc1b8d0b61ddaec2adb9c0d2c9dddcd493 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Mon, 6 Oct 2025 19:03:48 +0900 Subject: [PATCH 203/226] Improve diagnostics: replace 'non-inline module' with 'file module' and update note/help messages --- compiler/rustc_expand/messages.ftl | 5 +++-- compiler/rustc_expand/src/errors.rs | 3 ++- tests/ui/directory_ownership/file-mod-restriction.rs | 5 +++++ .../ui/directory_ownership/file-mod-restriction.stderr | 10 ++++++++++ tests/ui/directory_ownership/macro-expanded-mod.rs | 4 ++-- tests/ui/directory_ownership/macro-expanded-mod.stderr | 3 ++- .../directory_ownership/non-inline-mod-restriction.rs | 5 ----- .../non-inline-mod-restriction.stderr | 8 -------- 8 files changed, 24 insertions(+), 19 deletions(-) create mode 100644 tests/ui/directory_ownership/file-mod-restriction.rs create mode 100644 tests/ui/directory_ownership/file-mod-restriction.stderr delete mode 100644 tests/ui/directory_ownership/non-inline-mod-restriction.rs delete mode 100644 tests/ui/directory_ownership/non-inline-mod-restriction.stderr diff --git a/compiler/rustc_expand/messages.ftl b/compiler/rustc_expand/messages.ftl index 47c00bff5c95..7c2c9002a32c 100644 --- a/compiler/rustc_expand/messages.ftl +++ b/compiler/rustc_expand/messages.ftl @@ -118,8 +118,9 @@ expand_module_file_not_found = .note = if there is a `mod {$name}` elsewhere in the crate already, import it with `use crate::...` instead expand_module_in_block = - cannot declare a non-inline module inside a block unless it has a path attribute - .note = maybe `use` the module `{$name}` instead of redeclaring it + cannot declare a file module inside a block unless it has a path attribute + .help = maybe `use` the module `{$name}` instead of redeclaring it + .note = file modules are usually placed outside of blocks, at the top level of the file expand_module_multiple_candidates = file for module `{$name}` found at both "{$default_path}" and "{$secondary_path}" diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs index c37c2d88d9cd..6bae16b3bba1 100644 --- a/compiler/rustc_expand/src/errors.rs +++ b/compiler/rustc_expand/src/errors.rs @@ -265,6 +265,7 @@ pub(crate) struct ModuleCircular { #[derive(Diagnostic)] #[diag(expand_module_in_block)] +#[note] pub(crate) struct ModuleInBlock { #[primary_span] pub span: Span, @@ -273,7 +274,7 @@ pub(crate) struct ModuleInBlock { } #[derive(Subdiagnostic)] -#[note(expand_note)] +#[help(expand_help)] pub(crate) struct ModuleInBlockName { #[primary_span] pub span: Span, diff --git a/tests/ui/directory_ownership/file-mod-restriction.rs b/tests/ui/directory_ownership/file-mod-restriction.rs new file mode 100644 index 000000000000..cff876941dbc --- /dev/null +++ b/tests/ui/directory_ownership/file-mod-restriction.rs @@ -0,0 +1,5 @@ +// Test that file modules are not allowed inside blocks. + +fn main() { + mod foo; //~ ERROR cannot declare a file module inside a block unless it has a path attribute +} diff --git a/tests/ui/directory_ownership/file-mod-restriction.stderr b/tests/ui/directory_ownership/file-mod-restriction.stderr new file mode 100644 index 000000000000..4c1ff777f37f --- /dev/null +++ b/tests/ui/directory_ownership/file-mod-restriction.stderr @@ -0,0 +1,10 @@ +error: cannot declare a file module inside a block unless it has a path attribute + --> $DIR/file-mod-restriction.rs:4:5 + | +LL | mod foo; + | ^^^^^^^^ + | + = note: file modules are usually placed outside of blocks, at the top level of the file + +error: aborting due to 1 previous error + diff --git a/tests/ui/directory_ownership/macro-expanded-mod.rs b/tests/ui/directory_ownership/macro-expanded-mod.rs index fa81769e5a80..1495764a9d8c 100644 --- a/tests/ui/directory_ownership/macro-expanded-mod.rs +++ b/tests/ui/directory_ownership/macro-expanded-mod.rs @@ -1,8 +1,8 @@ -// Test that macro-expanded non-inline modules behave correctly +// Test that macro-expanded file modules behave correctly macro_rules! mod_decl { ($i:ident) => { - mod $i; //~ ERROR cannot declare a non-inline module inside a block + mod $i; //~ ERROR cannot declare a file module inside a block unless it has a path attribute }; } diff --git a/tests/ui/directory_ownership/macro-expanded-mod.stderr b/tests/ui/directory_ownership/macro-expanded-mod.stderr index 2cacd52b94ef..cb00a3fe6082 100644 --- a/tests/ui/directory_ownership/macro-expanded-mod.stderr +++ b/tests/ui/directory_ownership/macro-expanded-mod.stderr @@ -1,4 +1,4 @@ -error: cannot declare a non-inline module inside a block unless it has a path attribute +error: cannot declare a file module inside a block unless it has a path attribute --> $DIR/macro-expanded-mod.rs:5:9 | LL | mod $i; @@ -7,6 +7,7 @@ LL | mod $i; LL | mod_decl!(foo); | -------------- in this macro invocation | + = note: file modules are usually placed outside of blocks, at the top level of the file = note: this error originates in the macro `mod_decl` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/directory_ownership/non-inline-mod-restriction.rs b/tests/ui/directory_ownership/non-inline-mod-restriction.rs deleted file mode 100644 index de4f816656cc..000000000000 --- a/tests/ui/directory_ownership/non-inline-mod-restriction.rs +++ /dev/null @@ -1,5 +0,0 @@ -// Test that non-inline modules are not allowed inside blocks. - -fn main() { - mod foo; //~ ERROR cannot declare a non-inline module inside a block -} diff --git a/tests/ui/directory_ownership/non-inline-mod-restriction.stderr b/tests/ui/directory_ownership/non-inline-mod-restriction.stderr deleted file mode 100644 index 882c8652520b..000000000000 --- a/tests/ui/directory_ownership/non-inline-mod-restriction.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: cannot declare a non-inline module inside a block unless it has a path attribute - --> $DIR/non-inline-mod-restriction.rs:4:5 - | -LL | mod foo; - | ^^^^^^^^ - -error: aborting due to 1 previous error - From 5407eb8ca2254db44b806ada123e389302eae04d Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Sun, 7 Sep 2025 20:51:16 +0000 Subject: [PATCH 204/226] GVN: Support unions. --- compiler/rustc_mir_transform/src/gvn.rs | 38 ++++++++++++++----- .../const_prop/invalid_constant.main.GVN.diff | 30 ++++++++++++--- tests/mir-opt/const_prop/transmute.rs | 4 +- ...mute.undef_union_as_integer.GVN.32bit.diff | 9 ++++- ...mute.undef_union_as_integer.GVN.64bit.diff | 9 ++++- ...in.DestinationPropagation.panic-abort.diff | 10 +++-- ...n.DestinationPropagation.panic-unwind.diff | 10 +++-- tests/mir-opt/dest-prop/union.rs | 5 ++- 8 files changed, 85 insertions(+), 30 deletions(-) diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 03a40f83732a..bd8ea7dddad8 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -213,6 +213,8 @@ enum Value<'a, 'tcx> { /// An aggregate value, either tuple/closure/struct/enum. /// This does not contain unions, as we cannot reason with the value. Aggregate(VariantIdx, &'a [VnIndex]), + /// A union aggregate value. + Union(FieldIdx, VnIndex), /// A raw pointer aggregate built from a thin pointer and metadata. RawPtr { /// Thin pointer component. This is field 0 in MIR. @@ -600,6 +602,21 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { return None; } } + Union(active_field, field) => { + let field = self.evaluated[field].as_ref()?; + if matches!(ty.backend_repr, BackendRepr::Scalar(..) | BackendRepr::ScalarPair(..)) + { + let dest = self.ecx.allocate(ty, MemoryKind::Stack).discard_err()?; + let field_dest = self.ecx.project_field(&dest, active_field).discard_err()?; + self.ecx.copy_op(field, &field_dest).discard_err()?; + self.ecx + .alloc_mark_immutable(dest.ptr().provenance.unwrap().alloc_id()) + .discard_err()?; + dest.into() + } else { + return None; + } + } RawPtr { pointer, metadata } => { let pointer = self.evaluated[pointer].as_ref()?; let metadata = self.evaluated[metadata].as_ref()?; @@ -802,11 +819,11 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { } } ProjectionElem::Downcast(name, index) => ProjectionElem::Downcast(name, index), - ProjectionElem::Field(f, _) => { - if let Value::Aggregate(_, fields) = self.get(value) { - return Some((projection_ty, fields[f.as_usize()])); - } else if let Value::Projection(outer_value, ProjectionElem::Downcast(_, read_variant)) = self.get(value) - && let Value::Aggregate(written_variant, fields) = self.get(outer_value) + ProjectionElem::Field(f, _) => match self.get(value) { + Value::Aggregate(_, fields) => return Some((projection_ty, fields[f.as_usize()])), + Value::Union(active, field) if active == f => return Some((projection_ty, field)), + Value::Projection(outer_value, ProjectionElem::Downcast(_, read_variant)) + if let Value::Aggregate(written_variant, fields) = self.get(outer_value) // This pass is not aware of control-flow, so we do not know whether the // replacement we are doing is actually reachable. We could be in any arm of // ``` @@ -822,12 +839,12 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { // accessing the wrong variant is not UB if the enum has repr. // So it's not impossible for a series of MIR opts to generate // a downcast to an inactive variant. - && written_variant == read_variant + && written_variant == read_variant => { return Some((projection_ty, fields[f.as_usize()])); } - ProjectionElem::Field(f, ()) - } + _ => ProjectionElem::Field(f, ()), + }, ProjectionElem::Index(idx) => { if let Value::Repeat(inner, _) = self.get(value) { return Some((projection_ty, inner)); @@ -1167,7 +1184,10 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { | AggregateKind::Coroutine(..) => FIRST_VARIANT, AggregateKind::Adt(_, variant_index, _, _, None) => variant_index, // Do not track unions. - AggregateKind::Adt(_, _, _, _, Some(_)) => return None, + AggregateKind::Adt(_, _, _, _, Some(active_field)) => { + let field = *fields.first()?; + return Some(self.insert(ty, Value::Union(active_field, field))); + } AggregateKind::RawPtr(..) => { assert_eq!(field_ops.len(), 2); let [mut pointer, metadata] = fields.try_into().unwrap(); diff --git a/tests/mir-opt/const_prop/invalid_constant.main.GVN.diff b/tests/mir-opt/const_prop/invalid_constant.main.GVN.diff index 5e843da86792..a4900a1ac72e 100644 --- a/tests/mir-opt/const_prop/invalid_constant.main.GVN.diff +++ b/tests/mir-opt/const_prop/invalid_constant.main.GVN.diff @@ -28,21 +28,27 @@ bb0: { StorageLive(_1); StorageLive(_2); - _2 = InvalidChar { int: const 1114113_u32 }; - _1 = copy (_2.1: char); +- _2 = InvalidChar { int: const 1114113_u32 }; +- _1 = copy (_2.1: char); ++ _2 = const InvalidChar {{ int: 1114113_u32, chr: {transmute(0x00110001): char} }}; ++ _1 = const {transmute(0x00110001): char}; StorageDead(_2); StorageLive(_3); StorageLive(_4); StorageLive(_5); - _5 = InvalidTag { int: const 4_u32 }; - _4 = copy (_5.1: E); - _3 = [move _4]; +- _5 = InvalidTag { int: const 4_u32 }; +- _4 = copy (_5.1: E); +- _3 = [move _4]; ++ _5 = const InvalidTag {{ int: 4_u32, e: Scalar(0x00000004): E }}; ++ _4 = const Scalar(0x00000004): E; ++ _3 = [const Scalar(0x00000004): E]; StorageDead(_4); StorageDead(_5); nop; nop; StorageLive(_8); - _8 = NoVariants { int: const 0_u32 }; +- _8 = NoVariants { int: const 0_u32 }; ++ _8 = const NoVariants {{ int: 0_u32, empty: ZeroSized: Empty }}; nop; nop; nop; @@ -55,5 +61,17 @@ StorageDead(_1); return; } ++ } ++ ++ ALLOC0 (size: 4, align: 4) { ++ 00 00 00 00 │ .... ++ } ++ ++ ALLOC1 (size: 4, align: 4) { ++ 04 00 00 00 │ .... ++ } ++ ++ ALLOC2 (size: 4, align: 4) { ++ 01 00 11 00 │ .... } diff --git a/tests/mir-opt/const_prop/transmute.rs b/tests/mir-opt/const_prop/transmute.rs index 33cbefbf053f..ece6331ade27 100644 --- a/tests/mir-opt/const_prop/transmute.rs +++ b/tests/mir-opt/const_prop/transmute.rs @@ -43,8 +43,8 @@ pub unsafe fn invalid_bool() -> bool { // EMIT_MIR transmute.undef_union_as_integer.GVN.diff pub unsafe fn undef_union_as_integer() -> u32 { // CHECK-LABEL: fn undef_union_as_integer( - // CHECK: _1 = Union32 { - // CHECK: _0 = move _1 as u32 (Transmute); + // CHECK: _1 = const Union32 + // CHECK: _0 = const {{.*}}: u32; union Union32 { value: u32, unit: (), diff --git a/tests/mir-opt/const_prop/transmute.undef_union_as_integer.GVN.32bit.diff b/tests/mir-opt/const_prop/transmute.undef_union_as_integer.GVN.32bit.diff index 2ac9769a0e77..be0450114b1c 100644 --- a/tests/mir-opt/const_prop/transmute.undef_union_as_integer.GVN.32bit.diff +++ b/tests/mir-opt/const_prop/transmute.undef_union_as_integer.GVN.32bit.diff @@ -12,11 +12,16 @@ - _2 = (); - _1 = Union32 { value: move _2 }; + _2 = const (); -+ _1 = Union32 { value: const () }; ++ _1 = const Union32 {{ value: Indirect { alloc_id: ALLOC0, offset: Size(0 bytes) }: u32, unit: () }}; StorageDead(_2); - _0 = move _1 as u32 (Transmute); +- _0 = move _1 as u32 (Transmute); ++ _0 = const Indirect { alloc_id: ALLOC0, offset: Size(0 bytes) }: u32; StorageDead(_1); return; } ++ } ++ ++ ALLOC0 (size: 4, align: 4) { ++ __ __ __ __ │ ░░░░ } diff --git a/tests/mir-opt/const_prop/transmute.undef_union_as_integer.GVN.64bit.diff b/tests/mir-opt/const_prop/transmute.undef_union_as_integer.GVN.64bit.diff index 2ac9769a0e77..be0450114b1c 100644 --- a/tests/mir-opt/const_prop/transmute.undef_union_as_integer.GVN.64bit.diff +++ b/tests/mir-opt/const_prop/transmute.undef_union_as_integer.GVN.64bit.diff @@ -12,11 +12,16 @@ - _2 = (); - _1 = Union32 { value: move _2 }; + _2 = const (); -+ _1 = Union32 { value: const () }; ++ _1 = const Union32 {{ value: Indirect { alloc_id: ALLOC0, offset: Size(0 bytes) }: u32, unit: () }}; StorageDead(_2); - _0 = move _1 as u32 (Transmute); +- _0 = move _1 as u32 (Transmute); ++ _0 = const Indirect { alloc_id: ALLOC0, offset: Size(0 bytes) }: u32; StorageDead(_1); return; } ++ } ++ ++ ALLOC0 (size: 4, align: 4) { ++ __ __ __ __ │ ░░░░ } diff --git a/tests/mir-opt/dest-prop/union.main.DestinationPropagation.panic-abort.diff b/tests/mir-opt/dest-prop/union.main.DestinationPropagation.panic-abort.diff index ef418798faaf..013f1ea4e497 100644 --- a/tests/mir-opt/dest-prop/union.main.DestinationPropagation.panic-abort.diff +++ b/tests/mir-opt/dest-prop/union.main.DestinationPropagation.panic-abort.diff @@ -6,9 +6,9 @@ let _1: main::Un; let mut _2: u32; scope 1 { - debug un => _1; + debug un => const Un {{ us: 1_u32 }}; scope 3 (inlined std::mem::drop::) { - debug _x => _2; + debug _x => const 1_u32; } } scope 2 (inlined val) { @@ -16,12 +16,14 @@ bb0: { StorageLive(_1); - _1 = Un { us: const 1_u32 }; StorageLive(_2); - _2 = copy (_1.0: u32); StorageDead(_2); StorageDead(_1); return; } } + ALLOC0 (size: 4, align: 4) { + 01 00 00 00 │ .... + } + diff --git a/tests/mir-opt/dest-prop/union.main.DestinationPropagation.panic-unwind.diff b/tests/mir-opt/dest-prop/union.main.DestinationPropagation.panic-unwind.diff index ef418798faaf..013f1ea4e497 100644 --- a/tests/mir-opt/dest-prop/union.main.DestinationPropagation.panic-unwind.diff +++ b/tests/mir-opt/dest-prop/union.main.DestinationPropagation.panic-unwind.diff @@ -6,9 +6,9 @@ let _1: main::Un; let mut _2: u32; scope 1 { - debug un => _1; + debug un => const Un {{ us: 1_u32 }}; scope 3 (inlined std::mem::drop::) { - debug _x => _2; + debug _x => const 1_u32; } } scope 2 (inlined val) { @@ -16,12 +16,14 @@ bb0: { StorageLive(_1); - _1 = Un { us: const 1_u32 }; StorageLive(_2); - _2 = copy (_1.0: u32); StorageDead(_2); StorageDead(_1); return; } } + ALLOC0 (size: 4, align: 4) { + 01 00 00 00 │ .... + } + diff --git a/tests/mir-opt/dest-prop/union.rs b/tests/mir-opt/dest-prop/union.rs index 85eded099803..977ab9c57ad7 100644 --- a/tests/mir-opt/dest-prop/union.rs +++ b/tests/mir-opt/dest-prop/union.rs @@ -8,7 +8,10 @@ fn val() -> u32 { // EMIT_MIR union.main.DestinationPropagation.diff fn main() { // CHECK-LABEL: fn main( - // CHECK: {{_.*}} = Un { us: const 1_u32 }; + // CHECK: debug un => const Un + // CHECK: debug _x => const 1_u32; + // CHECK: bb0: { + // CHECK-NEXT: return; union Un { us: u32, } From 96c3978eaa5d116a9ffff6b33c61e25d972a9aa2 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Fri, 26 Sep 2025 21:06:22 +0000 Subject: [PATCH 205/226] Repurpose defunct test. --- tests/mir-opt/const_prop/union.main.GVN.diff | 42 +++++++++++++++++++ .../{dest-prop => const_prop}/union.rs | 12 +++--- ...in.DestinationPropagation.panic-abort.diff | 29 ------------- ...n.DestinationPropagation.panic-unwind.diff | 29 ------------- 4 files changed, 48 insertions(+), 64 deletions(-) create mode 100644 tests/mir-opt/const_prop/union.main.GVN.diff rename tests/mir-opt/{dest-prop => const_prop}/union.rs (52%) delete mode 100644 tests/mir-opt/dest-prop/union.main.DestinationPropagation.panic-abort.diff delete mode 100644 tests/mir-opt/dest-prop/union.main.DestinationPropagation.panic-unwind.diff diff --git a/tests/mir-opt/const_prop/union.main.GVN.diff b/tests/mir-opt/const_prop/union.main.GVN.diff new file mode 100644 index 000000000000..16a0432ab901 --- /dev/null +++ b/tests/mir-opt/const_prop/union.main.GVN.diff @@ -0,0 +1,42 @@ +- // MIR for `main` before GVN ++ // MIR for `main` after GVN + + fn main() -> () { + let mut _0: (); + let _1: main::Un; + let mut _2: u32; + let _3: (); + let mut _4: u32; + scope 1 { + debug un => _1; + scope 3 (inlined std::mem::drop::) { + } + } + scope 2 (inlined val) { + } + + bb0: { + StorageLive(_1); +- StorageLive(_2); ++ nop; + _2 = const 1_u32; +- _1 = Un { us: move _2 }; +- StorageDead(_2); ++ _1 = const Un {{ us: 1_u32 }}; ++ nop; + StorageLive(_3); + StorageLive(_4); +- _4 = copy (_1.0: u32); ++ _4 = const 1_u32; + StorageDead(_4); + StorageDead(_3); + _0 = const (); + StorageDead(_1); + return; + } ++ } ++ ++ ALLOC0 (size: 4, align: 4) { ++ 01 00 00 00 │ .... + } + diff --git a/tests/mir-opt/dest-prop/union.rs b/tests/mir-opt/const_prop/union.rs similarity index 52% rename from tests/mir-opt/dest-prop/union.rs rename to tests/mir-opt/const_prop/union.rs index 977ab9c57ad7..9f197a1a5833 100644 --- a/tests/mir-opt/dest-prop/union.rs +++ b/tests/mir-opt/const_prop/union.rs @@ -1,17 +1,17 @@ -// EMIT_MIR_FOR_EACH_PANIC_STRATEGY //! Tests that we can propagate into places that are projections into unions -//@ compile-flags: -Zunsound-mir-opts -C debuginfo=full +//@ test-mir-pass: GVN +//@ compile-flags: -Zinline-mir + fn val() -> u32 { 1 } -// EMIT_MIR union.main.DestinationPropagation.diff +// EMIT_MIR union.main.GVN.diff fn main() { // CHECK-LABEL: fn main( - // CHECK: debug un => const Un - // CHECK: debug _x => const 1_u32; + // CHECK: debug un => [[un:_.*]]; // CHECK: bb0: { - // CHECK-NEXT: return; + // CHECK: [[un]] = const Un {{{{ us: 1_u32 }}}}; union Un { us: u32, } diff --git a/tests/mir-opt/dest-prop/union.main.DestinationPropagation.panic-abort.diff b/tests/mir-opt/dest-prop/union.main.DestinationPropagation.panic-abort.diff deleted file mode 100644 index 013f1ea4e497..000000000000 --- a/tests/mir-opt/dest-prop/union.main.DestinationPropagation.panic-abort.diff +++ /dev/null @@ -1,29 +0,0 @@ -- // MIR for `main` before DestinationPropagation -+ // MIR for `main` after DestinationPropagation - - fn main() -> () { - let mut _0: (); - let _1: main::Un; - let mut _2: u32; - scope 1 { - debug un => const Un {{ us: 1_u32 }}; - scope 3 (inlined std::mem::drop::) { - debug _x => const 1_u32; - } - } - scope 2 (inlined val) { - } - - bb0: { - StorageLive(_1); - StorageLive(_2); - StorageDead(_2); - StorageDead(_1); - return; - } - } - - ALLOC0 (size: 4, align: 4) { - 01 00 00 00 │ .... - } - diff --git a/tests/mir-opt/dest-prop/union.main.DestinationPropagation.panic-unwind.diff b/tests/mir-opt/dest-prop/union.main.DestinationPropagation.panic-unwind.diff deleted file mode 100644 index 013f1ea4e497..000000000000 --- a/tests/mir-opt/dest-prop/union.main.DestinationPropagation.panic-unwind.diff +++ /dev/null @@ -1,29 +0,0 @@ -- // MIR for `main` before DestinationPropagation -+ // MIR for `main` after DestinationPropagation - - fn main() -> () { - let mut _0: (); - let _1: main::Un; - let mut _2: u32; - scope 1 { - debug un => const Un {{ us: 1_u32 }}; - scope 3 (inlined std::mem::drop::) { - debug _x => const 1_u32; - } - } - scope 2 (inlined val) { - } - - bb0: { - StorageLive(_1); - StorageLive(_2); - StorageDead(_2); - StorageDead(_1); - return; - } - } - - ALLOC0 (size: 4, align: 4) { - 01 00 00 00 │ .... - } - From cb06d91cd0ec0cd0393cee3a943f312ed849b530 Mon Sep 17 00:00:00 2001 From: Karol Zwolak Date: Mon, 6 Oct 2025 19:51:37 +0200 Subject: [PATCH 206/226] don't panic on extern with just multiple quotes in the name --- compiler/rustc_span/src/symbol.rs | 6 ++-- ....rs => extern-only-quotes-issue-147365.rs} | 6 +++- .../extern-only-quotes-issue-147365.stderr | 32 +++++++++++++++++++ .../extern-single-quote-issue-147365.stderr | 16 ---------- 4 files changed, 41 insertions(+), 19 deletions(-) rename tests/ui/extern/{extern-single-quote-issue-147365.rs => extern-only-quotes-issue-147365.rs} (63%) create mode 100644 tests/ui/extern/extern-only-quotes-issue-147365.stderr delete mode 100644 tests/ui/extern/extern-single-quote-issue-147365.stderr diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 92dd56f3d589..36214dd1c3ed 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -2511,9 +2511,11 @@ impl Ident { } /// Creates a new ident with the same span and name with leading quote removed, if any. - /// If called on an empty ident, or with name just a single quote, returns an empty ident which is invalid. + /// Calling it on a `'` ident will return an empty ident, which triggers debug assertions. pub fn without_first_quote(self) -> Ident { - Ident::new(Symbol::intern(self.as_str().trim_start_matches('\'')), self.span) + self.as_str() + .strip_prefix('\'') + .map_or(self, |name| Ident::new(Symbol::intern(name), self.span)) } /// "Normalize" ident for use in comparisons using "item hygiene". diff --git a/tests/ui/extern/extern-single-quote-issue-147365.rs b/tests/ui/extern/extern-only-quotes-issue-147365.rs similarity index 63% rename from tests/ui/extern/extern-single-quote-issue-147365.rs rename to tests/ui/extern/extern-only-quotes-issue-147365.rs index b50016bd65f3..3a492b4fb4f8 100644 --- a/tests/ui/extern/extern-single-quote-issue-147365.rs +++ b/tests/ui/extern/extern-only-quotes-issue-147365.rs @@ -2,8 +2,12 @@ // https://github.com/rust-lang/rust/issues/147365 // Ensures we don't trigger debug assert by creating an empty Ident when determining whether -// the single quote is a raw lifetime. +// the quotes are a raw lifetime. extern "'" {} //~ ERROR invalid ABI: found `'` +extern "''" {} //~ ERROR invalid ABI: found `''` + +extern "'''" {} //~ ERROR invalid ABI: found `'''` + fn main() {} diff --git a/tests/ui/extern/extern-only-quotes-issue-147365.stderr b/tests/ui/extern/extern-only-quotes-issue-147365.stderr new file mode 100644 index 000000000000..fabc0fd4916b --- /dev/null +++ b/tests/ui/extern/extern-only-quotes-issue-147365.stderr @@ -0,0 +1,32 @@ +error[E0703]: invalid ABI: found `'` + --> $DIR/extern-only-quotes-issue-147365.rs:7:8 + | +LL | extern "'" {} + | ^^^ invalid ABI + | + = note: invoke `rustc --print=calling-conventions` for a full list of supported calling conventions +help: there's a similarly named valid ABI `C` + | +LL - extern "'" {} +LL + extern "C" {} + | + +error[E0703]: invalid ABI: found `''` + --> $DIR/extern-only-quotes-issue-147365.rs:9:8 + | +LL | extern "''" {} + | ^^^^ invalid ABI + | + = note: invoke `rustc --print=calling-conventions` for a full list of supported calling conventions + +error[E0703]: invalid ABI: found `'''` + --> $DIR/extern-only-quotes-issue-147365.rs:11:8 + | +LL | extern "'''" {} + | ^^^^^ invalid ABI + | + = note: invoke `rustc --print=calling-conventions` for a full list of supported calling conventions + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0703`. diff --git a/tests/ui/extern/extern-single-quote-issue-147365.stderr b/tests/ui/extern/extern-single-quote-issue-147365.stderr deleted file mode 100644 index d761bc3ebf32..000000000000 --- a/tests/ui/extern/extern-single-quote-issue-147365.stderr +++ /dev/null @@ -1,16 +0,0 @@ -error[E0703]: invalid ABI: found `'` - --> $DIR/extern-single-quote-issue-147365.rs:7:8 - | -LL | extern "'" {} - | ^^^ invalid ABI - | - = note: invoke `rustc --print=calling-conventions` for a full list of supported calling conventions -help: there's a similarly named valid ABI `C` - | -LL - extern "'" {} -LL + extern "C" {} - | - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0703`. From 4787834edaa66a596be6b78459b85e20363adb0c Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Mon, 6 Oct 2025 22:31:38 +0200 Subject: [PATCH 207/226] Fix target list of `link_section` Signed-off-by: Jonathan Brouwer --- .../rustc_attr_parsing/src/attributes/link_attrs.rs | 10 ++++++++-- tests/ui/attributes/attr-on-mac-call.stderr | 2 +- .../issue-43106-gating-of-builtin-attrs.stderr | 12 ++++++------ 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index 09f7bfa24e82..40ecd91e5cfc 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -467,8 +467,14 @@ impl SingleAttributeParser for LinkSectionParser { const PATH: &[Symbol] = &[sym::link_section]; const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError; - const ALLOWED_TARGETS: AllowedTargets = - AllowedTargets::AllowListWarnRest(&[Allow(Target::Static), Allow(Target::Fn)]); + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[ + Allow(Target::Static), + Allow(Target::Fn), + Allow(Target::Method(MethodKind::Inherent)), + Allow(Target::Method(MethodKind::Trait { body: false })), + Allow(Target::Method(MethodKind::Trait { body: true })), + Allow(Target::Method(MethodKind::TraitImpl)), + ]); const TEMPLATE: AttributeTemplate = template!( NameValueStr: "name", "https://doc.rust-lang.org/reference/abi.html#the-link_section-attribute" diff --git a/tests/ui/attributes/attr-on-mac-call.stderr b/tests/ui/attributes/attr-on-mac-call.stderr index a08d30591685..02c0ec8ea1d6 100644 --- a/tests/ui/attributes/attr-on-mac-call.stderr +++ b/tests/ui/attributes/attr-on-mac-call.stderr @@ -82,7 +82,7 @@ LL | #[link_section = "x"] | ^^^^^^^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[link_section]` can be applied to statics and functions + = help: `#[link_section]` can be applied to functions and statics warning: `#[link_ordinal]` attribute cannot be used on macro calls --> $DIR/attr-on-mac-call.rs:33:5 diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr index 5e2029c45165..2e2eea65b817 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr @@ -1236,7 +1236,7 @@ LL | #[link_section = "1800"] | ^^^^^^^^^^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[link_section]` can be applied to statics and functions + = help: `#[link_section]` can be applied to functions and statics warning: `#[link_section]` attribute cannot be used on modules --> $DIR/issue-43106-gating-of-builtin-attrs.rs:683:17 @@ -1245,7 +1245,7 @@ LL | mod inner { #![link_section="1800"] } | ^^^^^^^^^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[link_section]` can be applied to statics and functions + = help: `#[link_section]` can be applied to functions and statics warning: `#[link_section]` attribute cannot be used on structs --> $DIR/issue-43106-gating-of-builtin-attrs.rs:691:5 @@ -1254,7 +1254,7 @@ LL | #[link_section = "1800"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[link_section]` can be applied to statics and functions + = help: `#[link_section]` can be applied to functions and statics warning: `#[link_section]` attribute cannot be used on type aliases --> $DIR/issue-43106-gating-of-builtin-attrs.rs:697:5 @@ -1263,7 +1263,7 @@ LL | #[link_section = "1800"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[link_section]` can be applied to statics and functions + = help: `#[link_section]` can be applied to functions and statics warning: `#[link_section]` attribute cannot be used on inherent impl blocks --> $DIR/issue-43106-gating-of-builtin-attrs.rs:703:5 @@ -1272,7 +1272,7 @@ LL | #[link_section = "1800"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[link_section]` can be applied to statics and functions + = help: `#[link_section]` can be applied to functions and statics warning: `#[must_use]` attribute cannot be used on modules --> $DIR/issue-43106-gating-of-builtin-attrs.rs:764:1 @@ -1572,7 +1572,7 @@ LL | #![link_section = "1800"] | ^^^^^^^^^^^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[link_section]` can be applied to statics and functions + = help: `#[link_section]` can be applied to functions and statics warning: `#[must_use]` attribute cannot be used on crates --> $DIR/issue-43106-gating-of-builtin-attrs.rs:84:1 From bc930cd2d101d1b0b858e9fcf981c916721e1c2d Mon Sep 17 00:00:00 2001 From: Jynn Nelson Date: Mon, 6 Oct 2025 11:56:08 -0400 Subject: [PATCH 208/226] collect-license-metadata: Print a diff of the expected output Previously, `x test collect-license-metadata` gave the following message on errors: ``` gathering license information from REUSE (this might take a minute...) finished gathering the license information from REUSE in 78.69s loading existing license information The existing /home/runner/work/ferrocene/ferrocene/license-metadata.json file is out of date. Run ./x run collect-license-metadata to update it. Error: The existing /home/runner/work/ferrocene/ferrocene/license-metadata.json file doesn't match what REUSE reports. Bootstrap failed while executing `test collect-license-metadata` ``` Notable, this doesn't actually say what went wrong. Print a diff in addition so it's more clear what broke: ``` ... "license": { "copyright": [ + "2010 The Rust Project Developers", "2016, 2017, 2018, 2019, 2020, 2021 AXE Consultants. All Rights", + "License. Subject to the terms and conditions of this", "Notice", - "The Ferrocene Developers" + "The Ferrocene Developers", + "[yyyy] [name of copyright owner]" ], ... ``` Currently, this prints the entire text of the JSON file as context. That's not ideal, but it's rare for this to fail, so I think it's ok for now. I considered using `assert_json_diff` instead of `similar`, but its errors are a lot harder to read IMO, even though they are better at omitting unnecessary context: ``` Diff: json atoms at path ".files.children[0].children[10].license.copyright[0]" are not equal: lhs: "2016 The Fuchsia Authors" rhs: "2019 The Crossbeam Project Developers" json atoms at path ".files.children[0].children[10].license.spdx" are not equal: lhs: "BSD-2-Clause AND (Apache-2.0 OR MIT)" rhs: "Apache-2.0 OR MIT" json atom at path ".files.children[0].children[10].children" is missing from lhs json atoms at path ".files.children[0].children[10].name" are not equal: lhs: "library/std/src/sys/sync/mutex/fuchsia.rs" rhs: "library/std/src/sync/mpmc" ... ``` --- Cargo.lock | 1 + src/tools/collect-license-metadata/Cargo.toml | 1 + src/tools/collect-license-metadata/src/main.rs | 14 ++++++++++++++ 3 files changed, 16 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 2de9c84e3b03..68fd8a8385ec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -686,6 +686,7 @@ dependencies = [ "anyhow", "serde", "serde_json", + "similar", "spdx-rs", ] diff --git a/src/tools/collect-license-metadata/Cargo.toml b/src/tools/collect-license-metadata/Cargo.toml index edf9e5c5393e..f84da2442815 100644 --- a/src/tools/collect-license-metadata/Cargo.toml +++ b/src/tools/collect-license-metadata/Cargo.toml @@ -9,4 +9,5 @@ license = "MIT OR Apache-2.0" anyhow = "1.0.65" serde = { version = "1.0.147", features = ["derive"] } serde_json = "1.0.85" +similar = "2.7.0" spdx-rs = "0.5.1" diff --git a/src/tools/collect-license-metadata/src/main.rs b/src/tools/collect-license-metadata/src/main.rs index 08a30d0b8994..4e218ea59fda 100644 --- a/src/tools/collect-license-metadata/src/main.rs +++ b/src/tools/collect-license-metadata/src/main.rs @@ -5,9 +5,21 @@ mod reuse; use std::path::PathBuf; use anyhow::{Context, Error}; +use similar::{ChangeTag, TextDiff}; use crate::licenses::LicensesInterner; +fn diff_text(expected: &str, actual: &str) { + for change in TextDiff::from_lines(expected, actual).iter_all_changes() { + let sign = match change.tag() { + ChangeTag::Delete => "-", + ChangeTag::Insert => "+", + ChangeTag::Equal => " ", + }; + print!("{}{}", sign, change); + } +} + /// The entry point to the binary. /// /// You should probably let `bootstrap` execute this program instead of running it directly. @@ -41,6 +53,8 @@ fn main() -> Result<(), Error> { if existing_json != output { eprintln!("The existing {} file is out of date.", dest.display()); eprintln!("Run ./x run collect-license-metadata to update it."); + eprintln!("Diff:"); + diff_text(&existing, &serde_json::to_string_pretty(&output).unwrap()); anyhow::bail!("The existing {} file doesn't match what REUSE reports.", dest.display()); } println!("license information matches"); From 231c39545ee4610d946ad202545869d8f4e2efc4 Mon Sep 17 00:00:00 2001 From: yukang Date: Tue, 7 Oct 2025 10:43:07 +0800 Subject: [PATCH 209/226] Fix wrong span for hightlight for duplicated diff lines --- compiler/rustc_errors/src/emitter.rs | 2 +- ...g-highlight-span-extra-arguments-147070.rs | 30 ++++++++ ...-highlight-span-extra-arguments-147070.svg | 72 +++++++++++++++++++ 3 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 tests/ui/argument-suggestions/wrong-highlight-span-extra-arguments-147070.rs create mode 100644 tests/ui/argument-suggestions/wrong-highlight-span-extra-arguments-147070.svg diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 93b1e6b76152..aa3671383481 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -2412,7 +2412,7 @@ impl HumanEmitter { // too bad to begin with, so we side-step that issue here. for (i, line) in snippet.lines().enumerate() { let line = normalize_whitespace(line); - let row = row_num - 2 - (newlines - i - 1); + let row = (row_num - 2 - (newlines - i - 1)).max(2); // On the first line, we highlight between the start of the part // span, and the end of that line. // On the last line, we highlight between the start of the line, and diff --git a/tests/ui/argument-suggestions/wrong-highlight-span-extra-arguments-147070.rs b/tests/ui/argument-suggestions/wrong-highlight-span-extra-arguments-147070.rs new file mode 100644 index 000000000000..65408fd02860 --- /dev/null +++ b/tests/ui/argument-suggestions/wrong-highlight-span-extra-arguments-147070.rs @@ -0,0 +1,30 @@ +//@ only-linux +//@ compile-flags: --error-format=human --color=always + +// The hightlight span should be correct. See #147070 +struct Thingie; + +impl Thingie { + pub(crate) fn new( + _a: String, + _b: String, + _c: String, + _d: String, + _e: String, + _f: String, + ) -> Self { + unimplemented!() + } +} + +fn main() { + let foo = Thingie::new( + String::from(""), + String::from(""), + String::from(""), + String::from(""), + String::from(""), + String::from(""), + String::from(""), + ); +} diff --git a/tests/ui/argument-suggestions/wrong-highlight-span-extra-arguments-147070.svg b/tests/ui/argument-suggestions/wrong-highlight-span-extra-arguments-147070.svg new file mode 100644 index 000000000000..67f9436b6a52 --- /dev/null +++ b/tests/ui/argument-suggestions/wrong-highlight-span-extra-arguments-147070.svg @@ -0,0 +1,72 @@ + + + + + + + error[E0061]: this function takes 6 arguments but 7 arguments were supplied + + --> $DIR/wrong-highlight-span-extra-arguments-147070.rs:21:15 + + | + + LL | let foo = Thingie::new( + + | ^^^^^^^^^^^^ + + ... + + LL | String::from(""), + + | ---------------- unexpected argument #7 of type `String` + + | + + note: associated function defined here + + --> $DIR/wrong-highlight-span-extra-arguments-147070.rs:8:19 + + | + + LL | pub(crate) fn new( + + | ^^^ + + help: remove the extra argument + + | + + LL - String::from(""), + + | + + + + error: aborting due to 1 previous error + + + + For more information about this error, try `rustc --explain E0061`. + + + + + + From 525ed4cc5cfc9a8230deccf92edd4a41fbebc8ee Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 7 Oct 2025 17:26:01 +1100 Subject: [PATCH 210/226] Read the whole test file before parsing directives Few tests are larger than a handful of kilobytes, and nowadays we scan the whole file for directives anyway, so there's little reason not to just read the whole thing up-front. This avoids having to deal with I/O within `iter_directives`, which should make it easier to overhaul directive processing. --- src/tools/compiletest/src/directives.rs | 37 +++++++------------ src/tools/compiletest/src/directives/tests.rs | 36 ++++++++---------- src/tools/compiletest/src/lib.rs | 5 ++- 3 files changed, 32 insertions(+), 46 deletions(-) diff --git a/src/tools/compiletest/src/directives.rs b/src/tools/compiletest/src/directives.rs index dab6850e0d62..34ecbc2d59f1 100644 --- a/src/tools/compiletest/src/directives.rs +++ b/src/tools/compiletest/src/directives.rs @@ -1,9 +1,6 @@ use std::collections::HashSet; -use std::env; -use std::fs::File; -use std::io::BufReader; -use std::io::prelude::*; use std::process::Command; +use std::{env, fs}; use camino::{Utf8Path, Utf8PathBuf}; use semver::Version; @@ -54,18 +51,19 @@ pub struct EarlyProps { impl EarlyProps { pub fn from_file(config: &Config, testfile: &Utf8Path) -> Self { - let file = File::open(testfile.as_std_path()).expect("open test file to parse earlyprops"); - Self::from_reader(config, testfile, file) + let file_contents = + fs::read_to_string(testfile).expect("read test file to parse earlyprops"); + Self::from_file_contents(config, testfile, &file_contents) } - pub fn from_reader(config: &Config, testfile: &Utf8Path, rdr: R) -> Self { + pub fn from_file_contents(config: &Config, testfile: &Utf8Path, file_contents: &str) -> Self { let mut props = EarlyProps::default(); let mut poisoned = false; iter_directives( config.mode, &mut poisoned, testfile, - rdr, + file_contents, // (dummy comment to force args into vertical layout) &mut |ref ln: DirectiveLine<'_>| { parse_and_update_aux(config, ln, testfile, &mut props.aux); @@ -362,7 +360,7 @@ impl TestProps { fn load_from(&mut self, testfile: &Utf8Path, test_revision: Option<&str>, config: &Config) { let mut has_edition = false; if !testfile.is_dir() { - let file = File::open(testfile.as_std_path()).unwrap(); + let file_contents = fs::read_to_string(testfile).unwrap(); let mut poisoned = false; @@ -370,7 +368,7 @@ impl TestProps { config.mode, &mut poisoned, testfile, - file, + &file_contents, &mut |ref ln: DirectiveLine<'_>| { if !ln.applies_to_test_revision(test_revision) { return; @@ -859,7 +857,7 @@ fn iter_directives( mode: TestMode, poisoned: &mut bool, testfile: &Utf8Path, - rdr: impl Read, + file_contents: &str, it: &mut dyn FnMut(DirectiveLine<'_>), ) { if testfile.is_dir() { @@ -886,16 +884,7 @@ fn iter_directives( } } - let mut rdr = BufReader::with_capacity(1024, rdr); - let mut ln = String::new(); - let mut line_number = 0; - - loop { - line_number += 1; - ln.clear(); - if rdr.read_line(&mut ln).unwrap() == 0 { - break; - } + for (line_number, ln) in (1..).zip(file_contents.lines()) { let ln = ln.trim(); let Some(directive_line) = line_directive(line_number, ln) else { @@ -1359,13 +1348,13 @@ where Some((min, max)) } -pub(crate) fn make_test_description( +pub(crate) fn make_test_description( config: &Config, cache: &DirectivesCache, name: String, path: &Utf8Path, filterable_path: &Utf8Path, - src: R, + file_contents: &str, test_revision: Option<&str>, poisoned: &mut bool, ) -> CollectedTestDesc { @@ -1380,7 +1369,7 @@ pub(crate) fn make_test_description( config.mode, &mut local_poisoned, path, - src, + file_contents, &mut |ref ln @ DirectiveLine { line_number, .. }| { if !ln.applies_to_test_revision(test_revision) { return; diff --git a/src/tools/compiletest/src/directives/tests.rs b/src/tools/compiletest/src/directives/tests.rs index 95dd46532ba8..77080c746937 100644 --- a/src/tools/compiletest/src/directives/tests.rs +++ b/src/tools/compiletest/src/directives/tests.rs @@ -1,5 +1,3 @@ -use std::io::Read; - use camino::Utf8Path; use semver::Version; @@ -10,12 +8,12 @@ use crate::directives::{ }; use crate::executor::{CollectedTestDesc, ShouldPanic}; -fn make_test_description( +fn make_test_description( config: &Config, name: String, path: &Utf8Path, filterable_path: &Utf8Path, - src: R, + file_contents: &str, revision: Option<&str>, ) -> CollectedTestDesc { let cache = DirectivesCache::load(config); @@ -26,7 +24,7 @@ fn make_test_description( name, path, filterable_path, - src, + file_contents, revision, &mut poisoned, ); @@ -226,14 +224,13 @@ fn cfg() -> ConfigBuilder { } fn parse_rs(config: &Config, contents: &str) -> EarlyProps { - let bytes = contents.as_bytes(); - EarlyProps::from_reader(config, Utf8Path::new("a.rs"), bytes) + EarlyProps::from_file_contents(config, Utf8Path::new("a.rs"), contents) } fn check_ignore(config: &Config, contents: &str) -> bool { let tn = String::new(); let p = Utf8Path::new("a.rs"); - let d = make_test_description(&config, tn, p, p, std::io::Cursor::new(contents), None); + let d = make_test_description(&config, tn, p, p, contents, None); d.ignore } @@ -243,9 +240,9 @@ fn should_fail() { let tn = String::new(); let p = Utf8Path::new("a.rs"); - let d = make_test_description(&config, tn.clone(), p, p, std::io::Cursor::new(""), None); + let d = make_test_description(&config, tn.clone(), p, p, "", None); assert_eq!(d.should_panic, ShouldPanic::No); - let d = make_test_description(&config, tn, p, p, std::io::Cursor::new("//@ should-fail"), None); + let d = make_test_description(&config, tn, p, p, "//@ should-fail", None); assert_eq!(d.should_panic, ShouldPanic::Yes); } @@ -778,9 +775,8 @@ fn threads_support() { } } -fn run_path(poisoned: &mut bool, path: &Utf8Path, buf: &[u8]) { - let rdr = std::io::Cursor::new(&buf); - iter_directives(TestMode::Ui, poisoned, path, rdr, &mut |_| {}); +fn run_path(poisoned: &mut bool, path: &Utf8Path, file_contents: &str) { + iter_directives(TestMode::Ui, poisoned, path, file_contents, &mut |_| {}); } #[test] @@ -789,7 +785,7 @@ fn test_unknown_directive_check() { run_path( &mut poisoned, Utf8Path::new("a.rs"), - include_bytes!("./test-auxillary/unknown_directive.rs"), + include_str!("./test-auxillary/unknown_directive.rs"), ); assert!(poisoned); } @@ -800,7 +796,7 @@ fn test_known_directive_check_no_error() { run_path( &mut poisoned, Utf8Path::new("a.rs"), - include_bytes!("./test-auxillary/known_directive.rs"), + include_str!("./test-auxillary/known_directive.rs"), ); assert!(!poisoned); } @@ -811,7 +807,7 @@ fn test_error_annotation_no_error() { run_path( &mut poisoned, Utf8Path::new("a.rs"), - include_bytes!("./test-auxillary/error_annotation.rs"), + include_str!("./test-auxillary/error_annotation.rs"), ); assert!(!poisoned); } @@ -822,7 +818,7 @@ fn test_non_rs_unknown_directive_not_checked() { run_path( &mut poisoned, Utf8Path::new("a.Makefile"), - include_bytes!("./test-auxillary/not_rs.Makefile"), + include_str!("./test-auxillary/not_rs.Makefile"), ); assert!(!poisoned); } @@ -830,21 +826,21 @@ fn test_non_rs_unknown_directive_not_checked() { #[test] fn test_trailing_directive() { let mut poisoned = false; - run_path(&mut poisoned, Utf8Path::new("a.rs"), b"//@ only-x86 only-arm"); + run_path(&mut poisoned, Utf8Path::new("a.rs"), "//@ only-x86 only-arm"); assert!(poisoned); } #[test] fn test_trailing_directive_with_comment() { let mut poisoned = false; - run_path(&mut poisoned, Utf8Path::new("a.rs"), b"//@ only-x86 only-arm with comment"); + run_path(&mut poisoned, Utf8Path::new("a.rs"), "//@ only-x86 only-arm with comment"); assert!(poisoned); } #[test] fn test_not_trailing_directive() { let mut poisoned = false; - run_path(&mut poisoned, Utf8Path::new("a.rs"), b"//@ revisions: incremental"); + run_path(&mut poisoned, Utf8Path::new("a.rs"), "//@ revisions: incremental"); assert!(!poisoned); } diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index 2d759279f34b..ac1a8226112a 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -892,7 +892,8 @@ fn make_test(cx: &TestCollectorCx, collector: &mut TestCollector, testpaths: &Te // `CollectedTest` that can be handed over to the test executor. collector.tests.extend(revisions.into_iter().map(|revision| { // Create a test name and description to hand over to the executor. - let src_file = fs::File::open(&test_path).expect("open test file to parse ignores"); + let file_contents = + fs::read_to_string(&test_path).expect("read test file to parse ignores"); let (test_name, filterable_path) = make_test_name_and_filterable_path(&cx.config, testpaths, revision); // Create a description struct for the test/revision. @@ -904,7 +905,7 @@ fn make_test(cx: &TestCollectorCx, collector: &mut TestCollector, testpaths: &Te test_name, &test_path, &filterable_path, - src_file, + &file_contents, revision, &mut collector.poisoned, ); From 1589c6c12d0f984343c6090c817e1dfbb9cf6d10 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Tue, 7 Oct 2025 09:40:27 +0200 Subject: [PATCH 211/226] Add regression test for `link_section` targets --- .../issue-43106-gating-of-builtin-attrs.rs | 26 +++ ...issue-43106-gating-of-builtin-attrs.stderr | 159 +++++++++--------- 2 files changed, 110 insertions(+), 75 deletions(-) diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs index aa5aab41e725..a0d3ed97142c 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs @@ -705,6 +705,32 @@ mod link_section { //~| WARN previously accepted //~| HELP can be applied to //~| HELP remove the attribute + + #[link_section = "1800"] + //~^ WARN attribute cannot be used on + //~| WARN previously accepted + //~| HELP can be applied to + //~| HELP remove the attribute + trait Tr { + #[link_section = "1800"] + fn inside_tr_no_default(&self); + + #[link_section = "1800"] + fn inside_tr_default(&self) { } + } + + impl S { + #[link_section = "1800"] + fn inside_abc_123(&self) { } + } + + impl Tr for S { + #[link_section = "1800"] + fn inside_tr_no_default(&self) { } + } + + #[link_section = "1800"] + fn should_always_link() { } } diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr index 2e2eea65b817..30b6d1c9180f 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr @@ -203,7 +203,7 @@ LL | #![reexport_test_harness_main = "2900"] | + warning: attribute should be applied to an `extern` block with non-Rust ABI - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:713:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:739:1 | LL | #[link(name = "x")] | ^^^^^^^^^^^^^^^^^^^ @@ -219,7 +219,7 @@ LL | | } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:789:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:815:1 | LL | #[windows_subsystem = "windows"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -230,7 +230,7 @@ LL | #![windows_subsystem = "windows"] | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:839:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:865:1 | LL | #[crate_type = "0800"] | ^^^^^^^^^^^^^^^^^^^^^^ @@ -241,7 +241,7 @@ LL | #![crate_type = "0800"] | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:863:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:889:1 | LL | #[feature(x0600)] | ^^^^^^^^^^^^^^^^^ @@ -252,7 +252,7 @@ LL | #![feature(x0600)] | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:888:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:914:1 | LL | #[no_main] | ^^^^^^^^^^ @@ -263,7 +263,7 @@ LL | #![no_main] | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:912:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:938:1 | LL | #[no_builtins] | ^^^^^^^^^^^^^^ @@ -340,7 +340,7 @@ LL | #![reexport_test_harness_main = "2900"] impl S { } | + warning: attribute should be applied to an `extern` block with non-Rust ABI - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:719:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:745:17 | LL | mod inner { #![link(name = "x")] } | ------------^^^^^^^^^^^^^^^^^^^^-- not an `extern` block @@ -348,7 +348,7 @@ LL | mod inner { #![link(name = "x")] } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to an `extern` block with non-Rust ABI - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:724:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:750:5 | LL | #[link(name = "x")] fn f() { } | ^^^^^^^^^^^^^^^^^^^ ---------- not an `extern` block @@ -356,7 +356,7 @@ LL | #[link(name = "x")] fn f() { } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to an `extern` block with non-Rust ABI - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:729:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:755:5 | LL | #[link(name = "x")] struct S; | ^^^^^^^^^^^^^^^^^^^ --------- not an `extern` block @@ -364,7 +364,7 @@ LL | #[link(name = "x")] struct S; = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to an `extern` block with non-Rust ABI - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:734:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:760:5 | LL | #[link(name = "x")] type T = S; | ^^^^^^^^^^^^^^^^^^^ ----------- not an `extern` block @@ -372,7 +372,7 @@ LL | #[link(name = "x")] type T = S; = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to an `extern` block with non-Rust ABI - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:739:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:765:5 | LL | #[link(name = "x")] impl S { } | ^^^^^^^^^^^^^^^^^^^ ---------- not an `extern` block @@ -380,7 +380,7 @@ LL | #[link(name = "x")] impl S { } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to an `extern` block with non-Rust ABI - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:744:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:770:5 | LL | #[link(name = "x")] extern "Rust" {} | ^^^^^^^^^^^^^^^^^^^ @@ -388,13 +388,13 @@ LL | #[link(name = "x")] extern "Rust" {} = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:793:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:819:17 | LL | mod inner { #![windows_subsystem="windows"] } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:796:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:822:5 | LL | #[windows_subsystem = "windows"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -405,7 +405,7 @@ LL | #![windows_subsystem = "windows"] fn f() { } | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:800:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:826:5 | LL | #[windows_subsystem = "windows"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -416,7 +416,7 @@ LL | #![windows_subsystem = "windows"] struct S; | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:804:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:830:5 | LL | #[windows_subsystem = "windows"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -427,7 +427,7 @@ LL | #![windows_subsystem = "windows"] type T = S; | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:808:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:834:5 | LL | #[windows_subsystem = "windows"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -438,13 +438,13 @@ LL | #![windows_subsystem = "windows"] impl S { } | + warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:843:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:869:17 | LL | mod inner { #![crate_type="0800"] } | ^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:846:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:872:5 | LL | #[crate_type = "0800"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^ @@ -455,7 +455,7 @@ LL | #![crate_type = "0800"] fn f() { } | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:850:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:876:5 | LL | #[crate_type = "0800"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^ @@ -466,7 +466,7 @@ LL | #![crate_type = "0800"] struct S; | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:854:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:880:5 | LL | #[crate_type = "0800"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^ @@ -477,7 +477,7 @@ LL | #![crate_type = "0800"] type T = S; | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:858:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:884:5 | LL | #[crate_type = "0800"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^ @@ -488,13 +488,13 @@ LL | #![crate_type = "0800"] impl S { } | + warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:867:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:893:17 | LL | mod inner { #![feature(x0600)] } | ^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:870:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:896:5 | LL | #[feature(x0600)] fn f() { } | ^^^^^^^^^^^^^^^^^ @@ -505,7 +505,7 @@ LL | #![feature(x0600)] fn f() { } | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:874:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:900:5 | LL | #[feature(x0600)] struct S; | ^^^^^^^^^^^^^^^^^ @@ -516,7 +516,7 @@ LL | #![feature(x0600)] struct S; | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:878:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:904:5 | LL | #[feature(x0600)] type T = S; | ^^^^^^^^^^^^^^^^^ @@ -527,7 +527,7 @@ LL | #![feature(x0600)] type T = S; | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:882:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:908:5 | LL | #[feature(x0600)] impl S { } | ^^^^^^^^^^^^^^^^^ @@ -538,13 +538,13 @@ LL | #![feature(x0600)] impl S { } | + warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:892:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:918:17 | LL | mod inner { #![no_main] } | ^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:895:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:921:5 | LL | #[no_main] fn f() { } | ^^^^^^^^^^ @@ -555,7 +555,7 @@ LL | #![no_main] fn f() { } | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:899:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:925:5 | LL | #[no_main] struct S; | ^^^^^^^^^^ @@ -566,7 +566,7 @@ LL | #![no_main] struct S; | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:903:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:929:5 | LL | #[no_main] type T = S; | ^^^^^^^^^^ @@ -577,7 +577,7 @@ LL | #![no_main] type T = S; | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:907:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:933:5 | LL | #[no_main] impl S { } | ^^^^^^^^^^ @@ -588,13 +588,13 @@ LL | #![no_main] impl S { } | + warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:916:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:942:17 | LL | mod inner { #![no_builtins] } | ^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:919:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:945:5 | LL | #[no_builtins] fn f() { } | ^^^^^^^^^^^^^^ @@ -605,7 +605,7 @@ LL | #![no_builtins] fn f() { } | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:923:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:949:5 | LL | #[no_builtins] struct S; | ^^^^^^^^^^^^^^ @@ -616,7 +616,7 @@ LL | #![no_builtins] struct S; | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:927:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:953:5 | LL | #[no_builtins] type T = S; | ^^^^^^^^^^^^^^ @@ -627,7 +627,7 @@ LL | #![no_builtins] type T = S; | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:931:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:957:5 | LL | #[no_builtins] impl S { } | ^^^^^^^^^^^^^^ @@ -1274,8 +1274,17 @@ LL | #[link_section = "1800"] impl S { } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = help: `#[link_section]` can be applied to functions and statics +warning: `#[link_section]` attribute cannot be used on traits + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:709:5 + | +LL | #[link_section = "1800"] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = help: `#[link_section]` can be applied to functions and statics + warning: `#[must_use]` attribute cannot be used on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:764:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:790:1 | LL | #[must_use] | ^^^^^^^^^^^ @@ -1284,7 +1293,7 @@ LL | #[must_use] = help: `#[must_use]` can be applied to functions, data types, unions, and traits warning: `#[must_use]` attribute cannot be used on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:769:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:795:17 | LL | mod inner { #![must_use] } | ^^^^^^^^^^^^ @@ -1293,7 +1302,7 @@ LL | mod inner { #![must_use] } = help: `#[must_use]` can be applied to functions, data types, unions, and traits warning: `#[must_use]` attribute cannot be used on type aliases - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:778:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:804:5 | LL | #[must_use] type T = S; | ^^^^^^^^^^^ @@ -1302,7 +1311,7 @@ LL | #[must_use] type T = S; = help: `#[must_use]` can be applied to functions, data types, unions, and traits warning: `#[must_use]` attribute cannot be used on inherent impl blocks - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:783:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:809:5 | LL | #[must_use] impl S { } | ^^^^^^^^^^^ @@ -1311,13 +1320,13 @@ LL | #[must_use] impl S { } = help: `#[must_use]` can be applied to functions, data types, unions, and traits warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![crate_name]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:815:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:841:1 | LL | #[crate_name = "0900"] | ^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:817:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:843:1 | LL | / mod crate_name { LL | | @@ -1327,67 +1336,67 @@ LL | | } | |_^ warning: the `#![crate_name]` attribute can only be used at the crate root - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:819:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:845:17 | LL | mod inner { #![crate_name="0900"] } | ^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![crate_name]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:822:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:848:5 | LL | #[crate_name = "0900"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this function - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:822:28 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:848:28 | LL | #[crate_name = "0900"] fn f() { } | ^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![crate_name]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:826:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:852:5 | LL | #[crate_name = "0900"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this struct - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:826:28 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:852:28 | LL | #[crate_name = "0900"] struct S; | ^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![crate_name]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:830:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:856:5 | LL | #[crate_name = "0900"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this type alias - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:830:28 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:856:28 | LL | #[crate_name = "0900"] type T = S; | ^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![crate_name]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:834:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:860:5 | LL | #[crate_name = "0900"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this implementation block - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:834:28 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:860:28 | LL | #[crate_name = "0900"] impl S { } | ^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![recursion_limit]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:936:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:962:1 | LL | #[recursion_limit="0200"] | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:938:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:964:1 | LL | / mod recursion_limit { LL | | @@ -1397,67 +1406,67 @@ LL | | } | |_^ warning: the `#![recursion_limit]` attribute can only be used at the crate root - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:940:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:966:17 | LL | mod inner { #![recursion_limit="0200"] } | ^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![recursion_limit]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:943:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:969:5 | LL | #[recursion_limit="0200"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this function - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:943:31 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:969:31 | LL | #[recursion_limit="0200"] fn f() { } | ^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![recursion_limit]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:947:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:973:5 | LL | #[recursion_limit="0200"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this struct - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:947:31 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:973:31 | LL | #[recursion_limit="0200"] struct S; | ^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![recursion_limit]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:951:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:977:5 | LL | #[recursion_limit="0200"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this type alias - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:951:31 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:977:31 | LL | #[recursion_limit="0200"] type T = S; | ^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![recursion_limit]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:955:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:981:5 | LL | #[recursion_limit="0200"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this implementation block - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:955:31 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:981:31 | LL | #[recursion_limit="0200"] impl S { } | ^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![type_length_limit]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:960:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:986:1 | LL | #[type_length_limit="0100"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:962:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:988:1 | LL | / mod type_length_limit { LL | | @@ -1467,55 +1476,55 @@ LL | | } | |_^ warning: the `#![type_length_limit]` attribute can only be used at the crate root - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:964:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:990:17 | LL | mod inner { #![type_length_limit="0100"] } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![type_length_limit]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:967:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:993:5 | LL | #[type_length_limit="0100"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this function - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:967:33 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:993:33 | LL | #[type_length_limit="0100"] fn f() { } | ^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![type_length_limit]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:971:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:997:5 | LL | #[type_length_limit="0100"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this struct - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:971:33 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:997:33 | LL | #[type_length_limit="0100"] struct S; | ^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![type_length_limit]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:975:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:1001:5 | LL | #[type_length_limit="0100"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this type alias - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:975:33 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:1001:33 | LL | #[type_length_limit="0100"] type T = S; | ^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![type_length_limit]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:979:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:1005:5 | LL | #[type_length_limit="0100"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this implementation block - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:979:33 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:1005:33 | LL | #[type_length_limit="0100"] impl S { } | ^^^^^^^^^^ @@ -1583,5 +1592,5 @@ LL | #![must_use] = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = help: `#[must_use]` can be applied to functions, data types, unions, and traits -warning: 173 warnings emitted +warning: 174 warnings emitted From 0fe466cc25975a5d531a546ec7cce2f38f3e0d6a Mon Sep 17 00:00:00 2001 From: "Tim (Theemathas) Chirananthavat" Date: Tue, 7 Oct 2025 14:47:39 +0700 Subject: [PATCH 212/226] Fix doc comment --- compiler/rustc_middle/src/thir.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index f1d19800a789..eed27345b747 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -839,7 +839,7 @@ pub enum PatKind<'tcx> { /// exhaustiveness to cover exactly its own value, similar to `&str`, but these values are /// much simpler. /// * raw pointers derived from integers, other raw pointers will have already resulted in an - // error. + /// error. /// * `String`, if `string_deref_patterns` is enabled. Constant { value: ty::Value<'tcx>, From 43f7eaa0ba4cbd2537260fe223ce1e7883c405fc Mon Sep 17 00:00:00 2001 From: James Barford-Evans Date: Mon, 6 Oct 2025 11:38:09 +0100 Subject: [PATCH 213/226] Fix; correct placement of type inference error for method calls --- compiler/rustc_hir_typeck/src/method/probe.rs | 21 ++++++++++++++----- ...eref-ambiguity-becomes-nonambiguous.stderr | 2 +- ...tion-with-leaking-placeholders.next.stderr | 2 +- .../hidden-type-is-opaque-2.default.stderr | 4 ++-- .../hidden-type-is-opaque-2.next.stderr | 4 ++-- ...ncompat-call-after-qualified-path-0.stderr | 2 +- ...ncompat-call-after-qualified-path-1.stderr | 2 +- tests/ui/issues/issue-20261.stderr | 4 ++-- tests/ui/issues/issue-2151.stderr | 2 +- .../branches3.stderr | 8 +++---- .../call_method_unknown_pointee.stderr | 18 +++++++--------- .../call_method_unknown_referent.stderr | 8 +++---- .../ui/proc-macro/quote/not-repeatable.stderr | 4 ++-- ...opy-inference-side-effects-are-lazy.stderr | 2 +- .../issue-42234-unknown-receiver-type.stderr | 4 +--- .../closures_in_branches.stderr | 4 ++-- .../regression-issue-81317.stderr | 2 +- tests/ui/typeck/issue-13853.stderr | 4 ++-- 18 files changed, 52 insertions(+), 45 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 12f80a197b1b..45b87cee957c 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -7,9 +7,8 @@ use rustc_attr_parsing::is_doc_alias_attrs_contain_symbol; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::sso::SsoHashSet; use rustc_errors::Applicability; -use rustc_hir as hir; -use rustc_hir::HirId; use rustc_hir::def::DefKind; +use rustc_hir::{self as hir, ExprKind, HirId, Node}; use rustc_hir_analysis::autoderef::{self, Autoderef}; use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse}; use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk, TyCtxtInferExt}; @@ -486,13 +485,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ty = self.resolve_vars_if_possible(ty.value); let guar = match *ty.kind() { ty::Infer(ty::TyVar(_)) => { + // We want to get the variable name that the method + // is being called on. If it is a method call. + let err_span = match (mode, self.tcx.hir_node(scope_expr_id)) { + ( + Mode::MethodCall, + Node::Expr(hir::Expr { + kind: ExprKind::MethodCall(_, recv, ..), + .. + }), + ) => recv.span, + _ => span, + }; + let raw_ptr_call = bad_ty.reached_raw_pointer && !self.tcx.features().arbitrary_self_types(); - // FIXME: Ideally we'd use the span of the self-expr here, - // not of the method path. + let mut err = self.err_ctxt().emit_inference_failure_err( self.body_id, - span, + err_span, ty.into(), TypeAnnotationNeeded::E0282, !raw_ptr_call, diff --git a/tests/ui/autoref-autoderef/deref-ambiguity-becomes-nonambiguous.stderr b/tests/ui/autoref-autoderef/deref-ambiguity-becomes-nonambiguous.stderr index 19c3c6418198..bad799b2550b 100644 --- a/tests/ui/autoref-autoderef/deref-ambiguity-becomes-nonambiguous.stderr +++ b/tests/ui/autoref-autoderef/deref-ambiguity-becomes-nonambiguous.stderr @@ -5,7 +5,7 @@ LL | let var_fn = Value::wrap(); | ^^^^^^ ... LL | let _ = var_fn.clone(); - | ----- type must be known at this point + | ------ type must be known at this point | help: consider giving `var_fn` an explicit type, where the placeholders `_` are specified | diff --git a/tests/ui/closures/deduce-signature/obligation-with-leaking-placeholders.next.stderr b/tests/ui/closures/deduce-signature/obligation-with-leaking-placeholders.next.stderr index 4bb9047b3035..3d667f12371a 100644 --- a/tests/ui/closures/deduce-signature/obligation-with-leaking-placeholders.next.stderr +++ b/tests/ui/closures/deduce-signature/obligation-with-leaking-placeholders.next.stderr @@ -5,7 +5,7 @@ LL | needs_foo(|x| { | ^ ... LL | x.to_string(); - | --------- type must be known at this point + | - type must be known at this point | help: consider giving this closure parameter an explicit type | diff --git a/tests/ui/impl-trait/hidden-type-is-opaque-2.default.stderr b/tests/ui/impl-trait/hidden-type-is-opaque-2.default.stderr index cb383b2db389..dca0a7b0a1a9 100644 --- a/tests/ui/impl-trait/hidden-type-is-opaque-2.default.stderr +++ b/tests/ui/impl-trait/hidden-type-is-opaque-2.default.stderr @@ -5,7 +5,7 @@ LL | Thunk::new(|mut cont| { | ^^^^^^^^ LL | LL | cont.reify_as(); - | -------- type must be known at this point + | ---- type must be known at this point | help: consider giving this closure parameter an explicit type | @@ -19,7 +19,7 @@ LL | Thunk::new(|mut cont| { | ^^^^^^^^ LL | LL | cont.reify_as(); - | -------- type must be known at this point + | ---- type must be known at this point | help: consider giving this closure parameter an explicit type | diff --git a/tests/ui/impl-trait/hidden-type-is-opaque-2.next.stderr b/tests/ui/impl-trait/hidden-type-is-opaque-2.next.stderr index cb383b2db389..dca0a7b0a1a9 100644 --- a/tests/ui/impl-trait/hidden-type-is-opaque-2.next.stderr +++ b/tests/ui/impl-trait/hidden-type-is-opaque-2.next.stderr @@ -5,7 +5,7 @@ LL | Thunk::new(|mut cont| { | ^^^^^^^^ LL | LL | cont.reify_as(); - | -------- type must be known at this point + | ---- type must be known at this point | help: consider giving this closure parameter an explicit type | @@ -19,7 +19,7 @@ LL | Thunk::new(|mut cont| { | ^^^^^^^^ LL | LL | cont.reify_as(); - | -------- type must be known at this point + | ---- type must be known at this point | help: consider giving this closure parameter an explicit type | diff --git a/tests/ui/inference/need_type_info/incompat-call-after-qualified-path-0.stderr b/tests/ui/inference/need_type_info/incompat-call-after-qualified-path-0.stderr index ba1c81c4518a..10056bdf3d4f 100644 --- a/tests/ui/inference/need_type_info/incompat-call-after-qualified-path-0.stderr +++ b/tests/ui/inference/need_type_info/incompat-call-after-qualified-path-0.stderr @@ -2,7 +2,7 @@ error[E0282]: type annotations needed --> $DIR/incompat-call-after-qualified-path-0.rs:21:6 | LL | f(|a, b| a.cmp(b)); - | ^ --- type must be known at this point + | ^ - type must be known at this point | help: consider giving this closure parameter an explicit type | diff --git a/tests/ui/inference/need_type_info/incompat-call-after-qualified-path-1.stderr b/tests/ui/inference/need_type_info/incompat-call-after-qualified-path-1.stderr index 93bba3625b54..632a9b99f84e 100644 --- a/tests/ui/inference/need_type_info/incompat-call-after-qualified-path-1.stderr +++ b/tests/ui/inference/need_type_info/incompat-call-after-qualified-path-1.stderr @@ -2,7 +2,7 @@ error[E0282]: type annotations needed --> $DIR/incompat-call-after-qualified-path-1.rs:25:6 | LL | f(|a, b| a.cmp(b)); - | ^ --- type must be known at this point + | ^ - type must be known at this point | help: consider giving this closure parameter an explicit type | diff --git a/tests/ui/issues/issue-20261.stderr b/tests/ui/issues/issue-20261.stderr index 6738708ca225..c5348abb3c50 100644 --- a/tests/ui/issues/issue-20261.stderr +++ b/tests/ui/issues/issue-20261.stderr @@ -1,8 +1,8 @@ error[E0282]: type annotations needed - --> $DIR/issue-20261.rs:4:11 + --> $DIR/issue-20261.rs:4:9 | LL | i.clone(); - | ^^^^^ cannot infer type + | ^ cannot infer type error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-2151.stderr b/tests/ui/issues/issue-2151.stderr index 59fef42eb5e8..b130f162414d 100644 --- a/tests/ui/issues/issue-2151.stderr +++ b/tests/ui/issues/issue-2151.stderr @@ -4,7 +4,7 @@ error[E0282]: type annotations needed LL | let x = panic!(); | ^ LL | x.clone(); - | ----- type must be known at this point + | - type must be known at this point | help: consider giving `x` an explicit type | diff --git a/tests/ui/lazy-type-alias-impl-trait/branches3.stderr b/tests/ui/lazy-type-alias-impl-trait/branches3.stderr index 539673bc343c..117d189867bd 100644 --- a/tests/ui/lazy-type-alias-impl-trait/branches3.stderr +++ b/tests/ui/lazy-type-alias-impl-trait/branches3.stderr @@ -2,7 +2,7 @@ error[E0282]: type annotations needed --> $DIR/branches3.rs:9:10 | LL | |s| s.len() - | ^ --- type must be known at this point + | ^ - type must be known at this point | help: consider giving this closure parameter an explicit type | @@ -13,7 +13,7 @@ error[E0282]: type annotations needed --> $DIR/branches3.rs:18:10 | LL | |s| s.len() - | ^ --- type must be known at this point + | ^ - type must be known at this point | help: consider giving this closure parameter an explicit type | @@ -24,7 +24,7 @@ error[E0282]: type annotations needed --> $DIR/branches3.rs:26:10 | LL | |s| s.len() - | ^ --- type must be known at this point + | ^ - type must be known at this point | help: consider giving this closure parameter an explicit type | @@ -35,7 +35,7 @@ error[E0282]: type annotations needed --> $DIR/branches3.rs:33:10 | LL | |s| s.len() - | ^ --- type must be known at this point + | ^ - type must be known at this point | help: consider giving this closure parameter an explicit type | diff --git a/tests/ui/methods/call_method_unknown_pointee.stderr b/tests/ui/methods/call_method_unknown_pointee.stderr index 9d0f38cf6b5c..e20c6f8e8a17 100644 --- a/tests/ui/methods/call_method_unknown_pointee.stderr +++ b/tests/ui/methods/call_method_unknown_pointee.stderr @@ -1,11 +1,10 @@ error[E0282]: type annotations needed - --> $DIR/call_method_unknown_pointee.rs:10:41 + --> $DIR/call_method_unknown_pointee.rs:10:23 | LL | let _a: i32 = (ptr as *const _).read(); - | ^^^^ - | | - | cannot infer type - | cannot call a method on a raw pointer with an unknown pointee type + | ^^^^^^^^^^^^^^^^^ ---- cannot call a method on a raw pointer with an unknown pointee type + | | + | cannot infer type error[E0282]: type annotations needed for `*const _` --> $DIR/call_method_unknown_pointee.rs:12:13 @@ -22,13 +21,12 @@ LL | let b: *const _ = ptr as *const _; | ++++++++++ error[E0282]: type annotations needed - --> $DIR/call_method_unknown_pointee.rs:21:39 + --> $DIR/call_method_unknown_pointee.rs:21:23 | LL | let _a: i32 = (ptr as *mut _).read(); - | ^^^^ - | | - | cannot infer type - | cannot call a method on a raw pointer with an unknown pointee type + | ^^^^^^^^^^^^^^^ ---- cannot call a method on a raw pointer with an unknown pointee type + | | + | cannot infer type error[E0282]: type annotations needed for `*mut _` --> $DIR/call_method_unknown_pointee.rs:23:13 diff --git a/tests/ui/methods/call_method_unknown_referent.stderr b/tests/ui/methods/call_method_unknown_referent.stderr index 5d6974a00c69..35c7d9caf3ef 100644 --- a/tests/ui/methods/call_method_unknown_referent.stderr +++ b/tests/ui/methods/call_method_unknown_referent.stderr @@ -1,14 +1,14 @@ error[E0282]: type annotations needed - --> $DIR/call_method_unknown_referent.rs:20:31 + --> $DIR/call_method_unknown_referent.rs:20:19 | LL | let _a: i32 = (ptr as &_).read(); - | ^^^^ cannot infer type + | ^^^^^^^^^^^ cannot infer type error[E0282]: type annotations needed - --> $DIR/call_method_unknown_referent.rs:26:37 + --> $DIR/call_method_unknown_referent.rs:26:14 | LL | let _b = (rc as std::rc::Rc<_>).read(); - | ^^^^ cannot infer type + | ^^^^^^^^^^^^^^^^^^^^^^ cannot infer type error[E0599]: no method named `read` found for struct `SmartPtr` in the current scope --> $DIR/call_method_unknown_referent.rs:46:35 diff --git a/tests/ui/proc-macro/quote/not-repeatable.stderr b/tests/ui/proc-macro/quote/not-repeatable.stderr index ff31799abb00..5943111efd58 100644 --- a/tests/ui/proc-macro/quote/not-repeatable.stderr +++ b/tests/ui/proc-macro/quote/not-repeatable.stderr @@ -21,10 +21,10 @@ note: the traits `Iterator` and `ToTokens` must be implemented --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL error[E0282]: type annotations needed - --> $DIR/not-repeatable.rs:11:13 + --> $DIR/not-repeatable.rs:11:25 | LL | let _ = quote! { $($ip)* }; - | ^^^^^^^^^^^^^^^^^^ cannot infer type + | ^^ cannot infer type error: aborting due to 2 previous errors diff --git a/tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.stderr b/tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.stderr index b8a8f927542f..c98b9bb38fdb 100644 --- a/tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.stderr +++ b/tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.stderr @@ -5,7 +5,7 @@ LL | let x = [Foo(PhantomData); 2]; | ^ LL | LL | extract(x).max(2); - | --- type must be known at this point + | ---------- type must be known at this point | help: consider giving `x` an explicit type, where the placeholders `_` are specified | diff --git a/tests/ui/span/issue-42234-unknown-receiver-type.stderr b/tests/ui/span/issue-42234-unknown-receiver-type.stderr index 71ac4f53b3f4..10308ec07da5 100644 --- a/tests/ui/span/issue-42234-unknown-receiver-type.stderr +++ b/tests/ui/span/issue-42234-unknown-receiver-type.stderr @@ -4,7 +4,7 @@ error[E0282]: type annotations needed LL | let x: Option<_> = None; | ^^^^ cannot infer type of the type parameter `T` declared on the enum `Option` LL | x.unwrap().method_that_could_exist_on_some_type(); - | ------------------------------------ type must be known at this point + | ---------- type must be known at this point | help: consider specifying the generic argument | @@ -16,8 +16,6 @@ error[E0282]: type annotations needed | LL | .sum::<_>() | ^^^ cannot infer type of the type parameter `S` declared on the method `sum` -LL | .to_string() - | --------- type must be known at this point | error: aborting due to 2 previous errors diff --git a/tests/ui/type-alias-impl-trait/closures_in_branches.stderr b/tests/ui/type-alias-impl-trait/closures_in_branches.stderr index 559bc57d9063..849ffd214f07 100644 --- a/tests/ui/type-alias-impl-trait/closures_in_branches.stderr +++ b/tests/ui/type-alias-impl-trait/closures_in_branches.stderr @@ -2,7 +2,7 @@ error[E0282]: type annotations needed --> $DIR/closures_in_branches.rs:8:10 | LL | |x| x.len() - | ^ --- type must be known at this point + | ^ - type must be known at this point | help: consider giving this closure parameter an explicit type | @@ -13,7 +13,7 @@ error[E0282]: type annotations needed --> $DIR/closures_in_branches.rs:22:10 | LL | |x| x.len() - | ^ --- type must be known at this point + | ^ - type must be known at this point | help: consider giving this closure parameter an explicit type | diff --git a/tests/ui/type-inference/regression-issue-81317.stderr b/tests/ui/type-inference/regression-issue-81317.stderr index a070b50e3117..fcd3fca06e18 100644 --- a/tests/ui/type-inference/regression-issue-81317.stderr +++ b/tests/ui/type-inference/regression-issue-81317.stderr @@ -5,7 +5,7 @@ LL | let iv = S ^ index.into(); | ^^ LL | LL | &iv.to_bytes_be(); - | ----------- type must be known at this point + | -- type must be known at this point | help: consider giving `iv` an explicit type | diff --git a/tests/ui/typeck/issue-13853.stderr b/tests/ui/typeck/issue-13853.stderr index 9b8698d6ed2c..4a39b404770d 100644 --- a/tests/ui/typeck/issue-13853.stderr +++ b/tests/ui/typeck/issue-13853.stderr @@ -18,10 +18,10 @@ LL | for node in graph.iter() { | ^^^^ method not found in `&G` error[E0282]: type annotations needed - --> $DIR/issue-13853.rs:28:14 + --> $DIR/issue-13853.rs:28:9 | LL | node.zomg(); - | ^^^^ cannot infer type + | ^^^^ cannot infer type error[E0308]: mismatched types --> $DIR/issue-13853.rs:37:13 From eb8d87d1d84c27876a6f780356be102ac4337ffa Mon Sep 17 00:00:00 2001 From: Yotam Ofek Date: Mon, 6 Oct 2025 16:55:14 +0300 Subject: [PATCH 214/226] avoid in-process serializing&deserializing --- src/librustdoc/html/render/mod.rs | 75 +++++- src/librustdoc/html/render/search_index.rs | 268 ++++++++------------- 2 files changed, 168 insertions(+), 175 deletions(-) diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 84d684e0c95f..52617b6c4320 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -61,6 +61,8 @@ use rustc_middle::ty::print::PrintTraitRefExt; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::symbol::{Symbol, sym}; use rustc_span::{BytePos, DUMMY_SP, FileName, RealFileName}; +use serde::ser::SerializeSeq as _; +use serde::{Deserialize, Serialize}; use tracing::{debug, info}; pub(crate) use self::context::*; @@ -144,7 +146,7 @@ pub(crate) struct IndexItem { } /// A type used for the search index. -#[derive(Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq)] struct RenderType { id: Option, generics: Option>, @@ -301,7 +303,7 @@ impl RenderTypeId { } /// Full type of functions/methods in the search index. -#[derive(Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq)] pub(crate) struct IndexItemFunctionType { inputs: Vec, output: Vec, @@ -423,6 +425,75 @@ impl IndexItemFunctionType { } } +impl Serialize for IndexItemFunctionType { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut seq = serializer.serialize_seq(Some(2))?; + let mut fn_type = String::new(); + self.write_to_string_without_param_names(&mut fn_type); + seq.serialize_element(&fn_type)?; + + struct ParamNames<'a>(&'a [Option]); + + impl<'a> Serialize for ParamNames<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.collect_seq( + self.0 + .iter() + .map(|symbol| symbol.as_ref().map(ToString::to_string).unwrap_or_default()), + ) + } + } + + seq.serialize_element(&ParamNames(&self.param_names))?; + seq.end() + } +} + +impl<'de> Deserialize<'de> for IndexItemFunctionType { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + use serde::de::{self, Error as _}; + + struct FunctionDataVisitor; + impl<'de> de::Visitor<'de> for FunctionDataVisitor { + type Value = IndexItemFunctionType; + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> fmt::Result { + write!(formatter, "fn data") + } + fn visit_seq>(self, mut v: A) -> Result { + let (mut function_signature, _) = v + .next_element()? + .map(|fn_: String| { + IndexItemFunctionType::read_from_string_without_param_names(fn_.as_bytes()) + }) + .ok_or_else(|| A::Error::missing_field("function_signature"))?; + let param_names: Vec> = v + .next_element()? + .map(|param_names: Vec| { + param_names + .into_iter() + .map(|symbol| { + if symbol.is_empty() { None } else { Some(Symbol::intern(&symbol)) } + }) + .collect() + }) + .ok_or_else(|| A::Error::missing_field("param_names"))?; + function_signature.param_names = param_names; + Ok(function_signature) + } + } + deserializer.deserialize_any(FunctionDataVisitor) + } +} + #[derive(Debug, Clone)] pub(crate) struct StylePath { /// The path to the theme diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index 253d90294687..080faff3a6de 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -34,7 +34,7 @@ pub(crate) struct SerializedSearchIndex { path_data: Vec>, entry_data: Vec>, descs: Vec, - function_data: Vec>, + function_data: Vec>, alias_pointers: Vec>, // inverted index for concrete types and generics type_data: Vec>, @@ -61,7 +61,7 @@ impl SerializedSearchIndex { let mut path_data: Vec> = Vec::new(); let mut entry_data: Vec> = Vec::new(); let mut descs: Vec = Vec::new(); - let mut function_data: Vec> = Vec::new(); + let mut function_data: Vec> = Vec::new(); let mut type_data: Vec> = Vec::new(); let mut alias_pointers: Vec> = Vec::new(); @@ -207,7 +207,7 @@ impl SerializedSearchIndex { path_data: Option, entry_data: Option, desc: String, - function_data: Option, + function_data: Option, type_data: Option, alias_pointer: Option, ) -> usize { @@ -446,73 +446,62 @@ impl SerializedSearchIndex { ..other_entry_data.clone() }), other.descs[other_entryid].clone(), - other.function_data[other_entryid].as_ref().map(|function_data| FunctionData { - function_signature: { - let (mut func, _offset) = - IndexItemFunctionType::read_from_string_without_param_names( - function_data.function_signature.as_bytes(), - ); - fn map_fn_sig_item( - map_other_pathid_to_self_pathid: &mut Vec, - ty: &mut RenderType, - ) { - match ty.id { - None => {} - Some(RenderTypeId::Index(generic)) if generic < 0 => {} - Some(RenderTypeId::Index(id)) => { - let id = usize::try_from(id).unwrap(); - let id = map_other_pathid_to_self_pathid[id]; - assert!(id != !0); - ty.id = - Some(RenderTypeId::Index(isize::try_from(id).unwrap())); - } - _ => unreachable!(), + other.function_data[other_entryid].clone().map(|mut func| { + fn map_fn_sig_item( + map_other_pathid_to_self_pathid: &mut Vec, + ty: &mut RenderType, + ) { + match ty.id { + None => {} + Some(RenderTypeId::Index(generic)) if generic < 0 => {} + Some(RenderTypeId::Index(id)) => { + let id = usize::try_from(id).unwrap(); + let id = map_other_pathid_to_self_pathid[id]; + assert!(id != !0); + ty.id = Some(RenderTypeId::Index(isize::try_from(id).unwrap())); } - if let Some(generics) = &mut ty.generics { - for generic in generics { - map_fn_sig_item(map_other_pathid_to_self_pathid, generic); - } + _ => unreachable!(), + } + if let Some(generics) = &mut ty.generics { + for generic in generics { + map_fn_sig_item(map_other_pathid_to_self_pathid, generic); } - if let Some(bindings) = &mut ty.bindings { - for (param, constraints) in bindings { - *param = match *param { - param @ RenderTypeId::Index(generic) if generic < 0 => { - param - } - RenderTypeId::Index(id) => { - let id = usize::try_from(id).unwrap(); - let id = map_other_pathid_to_self_pathid[id]; - assert!(id != !0); - RenderTypeId::Index(isize::try_from(id).unwrap()) - } - _ => unreachable!(), - }; - for constraint in constraints { - map_fn_sig_item( - map_other_pathid_to_self_pathid, - constraint, - ); + } + if let Some(bindings) = &mut ty.bindings { + for (param, constraints) in bindings { + *param = match *param { + param @ RenderTypeId::Index(generic) if generic < 0 => { + param } + RenderTypeId::Index(id) => { + let id = usize::try_from(id).unwrap(); + let id = map_other_pathid_to_self_pathid[id]; + assert!(id != !0); + RenderTypeId::Index(isize::try_from(id).unwrap()) + } + _ => unreachable!(), + }; + for constraint in constraints { + map_fn_sig_item( + map_other_pathid_to_self_pathid, + constraint, + ); } } } - for input in &mut func.inputs { - map_fn_sig_item(&mut map_other_pathid_to_self_pathid, input); + } + for input in &mut func.inputs { + map_fn_sig_item(&mut map_other_pathid_to_self_pathid, input); + } + for output in &mut func.output { + map_fn_sig_item(&mut map_other_pathid_to_self_pathid, output); + } + for clause in &mut func.where_clause { + for entry in clause { + map_fn_sig_item(&mut map_other_pathid_to_self_pathid, entry); } - for output in &mut func.output { - map_fn_sig_item(&mut map_other_pathid_to_self_pathid, output); - } - for clause in &mut func.where_clause { - for entry in clause { - map_fn_sig_item(&mut map_other_pathid_to_self_pathid, entry); - } - } - let mut result = - String::with_capacity(function_data.function_signature.len()); - func.write_to_string_without_param_names(&mut result); - result - }, - param_names: function_data.param_names.clone(), + } + func }), other.type_data[other_entryid].as_ref().map(|type_data| TypeData { inverted_function_inputs_index: type_data @@ -626,69 +615,55 @@ impl SerializedSearchIndex { }, ), self.descs[id].clone(), - self.function_data[id].as_ref().map( - |FunctionData { function_signature, param_names }| FunctionData { - function_signature: { - let (mut func, _offset) = - IndexItemFunctionType::read_from_string_without_param_names( - function_signature.as_bytes(), - ); - fn map_fn_sig_item(map: &FxHashMap, ty: &mut RenderType) { - match ty.id { - None => {} - Some(RenderTypeId::Index(generic)) if generic < 0 => {} - Some(RenderTypeId::Index(id)) => { + self.function_data[id].clone().map(|mut func| { + fn map_fn_sig_item(map: &FxHashMap, ty: &mut RenderType) { + match ty.id { + None => {} + Some(RenderTypeId::Index(generic)) if generic < 0 => {} + Some(RenderTypeId::Index(id)) => { + let id = usize::try_from(id).unwrap(); + let id = *map.get(&id).unwrap(); + assert!(id != !0); + ty.id = Some(RenderTypeId::Index(isize::try_from(id).unwrap())); + } + _ => unreachable!(), + } + if let Some(generics) = &mut ty.generics { + for generic in generics { + map_fn_sig_item(map, generic); + } + } + if let Some(bindings) = &mut ty.bindings { + for (param, constraints) in bindings { + *param = match *param { + param @ RenderTypeId::Index(generic) if generic < 0 => param, + RenderTypeId::Index(id) => { let id = usize::try_from(id).unwrap(); let id = *map.get(&id).unwrap(); assert!(id != !0); - ty.id = - Some(RenderTypeId::Index(isize::try_from(id).unwrap())); + RenderTypeId::Index(isize::try_from(id).unwrap()) } _ => unreachable!(), - } - if let Some(generics) = &mut ty.generics { - for generic in generics { - map_fn_sig_item(map, generic); - } - } - if let Some(bindings) = &mut ty.bindings { - for (param, constraints) in bindings { - *param = match *param { - param @ RenderTypeId::Index(generic) if generic < 0 => { - param - } - RenderTypeId::Index(id) => { - let id = usize::try_from(id).unwrap(); - let id = *map.get(&id).unwrap(); - assert!(id != !0); - RenderTypeId::Index(isize::try_from(id).unwrap()) - } - _ => unreachable!(), - }; - for constraint in constraints { - map_fn_sig_item(map, constraint); - } - } + }; + for constraint in constraints { + map_fn_sig_item(map, constraint); } } - for input in &mut func.inputs { - map_fn_sig_item(&map, input); - } - for output in &mut func.output { - map_fn_sig_item(&map, output); - } - for clause in &mut func.where_clause { - for entry in clause { - map_fn_sig_item(&map, entry); - } - } - let mut result = String::with_capacity(function_signature.len()); - func.write_to_string_without_param_names(&mut result); - result - }, - param_names: param_names.clone(), - }, - ), + } + } + for input in &mut func.inputs { + map_fn_sig_item(&map, input); + } + for output in &mut func.output { + map_fn_sig_item(&map, output); + } + for clause in &mut func.where_clause { + for entry in clause { + map_fn_sig_item(&map, entry); + } + } + func + }), self.type_data[id].as_ref().map( |TypeData { search_unbox, @@ -1259,48 +1234,6 @@ impl<'de> Deserialize<'de> for SerializedOptional32 { } } -#[derive(Clone, Debug)] -pub struct FunctionData { - function_signature: String, - param_names: Vec, -} - -impl Serialize for FunctionData { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut seq = serializer.serialize_seq(None)?; - seq.serialize_element(&self.function_signature)?; - seq.serialize_element(&self.param_names)?; - seq.end() - } -} - -impl<'de> Deserialize<'de> for FunctionData { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct FunctionDataVisitor; - impl<'de> de::Visitor<'de> for FunctionDataVisitor { - type Value = FunctionData; - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(formatter, "fn data") - } - fn visit_seq>(self, mut v: A) -> Result { - let function_signature: String = v - .next_element()? - .ok_or_else(|| A::Error::missing_field("function_signature"))?; - let param_names: Vec = - v.next_element()?.ok_or_else(|| A::Error::missing_field("param_names"))?; - Ok(FunctionData { function_signature, param_names }) - } - } - deserializer.deserialize_any(FunctionDataVisitor) - } -} - /// Builds the search index from the collected metadata pub(crate) fn build_index( krate: &clean::Crate, @@ -1927,18 +1860,7 @@ pub(crate) fn build_index( // because the postings list has to fill in an empty array for each // unoccupied size. if item.ty.is_fn_like() { 0 } else { 16 }; - serialized_index.function_data[new_entry_id] = Some(FunctionData { - function_signature: { - let mut function_signature = String::new(); - search_type.write_to_string_without_param_names(&mut function_signature); - function_signature - }, - param_names: search_type - .param_names - .iter() - .map(|sym| sym.map(|sym| sym.to_string()).unwrap_or(String::new())) - .collect::>(), - }); + serialized_index.function_data[new_entry_id] = Some(search_type.clone()); for index in used_in_function_inputs { let postings = if index >= 0 { assert!(serialized_index.path_data[index as usize].is_some()); From 978ab7c54229359f62593d4afd8249a09bf7607c Mon Sep 17 00:00:00 2001 From: Yotam Ofek Date: Tue, 7 Oct 2025 12:57:59 +0300 Subject: [PATCH 215/226] nicer serde impls --- src/librustdoc/html/render/mod.rs | 105 +++++++++++++++++++----------- 1 file changed, 66 insertions(+), 39 deletions(-) diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 52617b6c4320..d604526aa4a1 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -62,7 +62,7 @@ use rustc_middle::ty::{self, TyCtxt}; use rustc_span::symbol::{Symbol, sym}; use rustc_span::{BytePos, DUMMY_SP, FileName, RealFileName}; use serde::ser::SerializeSeq as _; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; use tracing::{debug, info}; pub(crate) use self::context::*; @@ -428,29 +428,31 @@ impl IndexItemFunctionType { impl Serialize for IndexItemFunctionType { fn serialize(&self, serializer: S) -> Result where - S: serde::Serializer, + S: Serializer, { - let mut seq = serializer.serialize_seq(Some(2))?; - let mut fn_type = String::new(); - self.write_to_string_without_param_names(&mut fn_type); - seq.serialize_element(&fn_type)?; - struct ParamNames<'a>(&'a [Option]); impl<'a> Serialize for ParamNames<'a> { fn serialize(&self, serializer: S) -> Result where - S: serde::Serializer, + S: Serializer, { serializer.collect_seq( self.0 .iter() - .map(|symbol| symbol.as_ref().map(ToString::to_string).unwrap_or_default()), + .map(|symbol| symbol.as_ref().map(Symbol::as_str).unwrap_or_default()), ) } } + let mut seq = serializer.serialize_seq(Some(2))?; + + let mut fn_type = String::new(); + self.write_to_string_without_param_names(&mut fn_type); + seq.serialize_element(&fn_type)?; + seq.serialize_element(&ParamNames(&self.param_names))?; + seq.end() } } @@ -458,39 +460,64 @@ impl Serialize for IndexItemFunctionType { impl<'de> Deserialize<'de> for IndexItemFunctionType { fn deserialize(deserializer: D) -> Result where - D: serde::Deserializer<'de>, + D: Deserializer<'de>, { - use serde::de::{self, Error as _}; + use serde::de::{self, SeqAccess}; - struct FunctionDataVisitor; - impl<'de> de::Visitor<'de> for FunctionDataVisitor { - type Value = IndexItemFunctionType; - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> fmt::Result { - write!(formatter, "fn data") - } - fn visit_seq>(self, mut v: A) -> Result { - let (mut function_signature, _) = v - .next_element()? - .map(|fn_: String| { - IndexItemFunctionType::read_from_string_without_param_names(fn_.as_bytes()) - }) - .ok_or_else(|| A::Error::missing_field("function_signature"))?; - let param_names: Vec> = v - .next_element()? - .map(|param_names: Vec| { - param_names - .into_iter() - .map(|symbol| { - if symbol.is_empty() { None } else { Some(Symbol::intern(&symbol)) } - }) - .collect() - }) - .ok_or_else(|| A::Error::missing_field("param_names"))?; - function_signature.param_names = param_names; - Ok(function_signature) - } + #[derive(Deserialize)] + struct Deserialized { + #[serde(deserialize_with = "function_signature")] + function_signature: IndexItemFunctionType, + #[serde(deserialize_with = "param_names")] + param_names: Vec>, } - deserializer.deserialize_any(FunctionDataVisitor) + + fn function_signature<'de, D: Deserializer<'de>>( + deserializer: D, + ) -> Result { + String::deserialize(deserializer).map(|sig| { + IndexItemFunctionType::read_from_string_without_param_names(sig.as_bytes()).0 + }) + } + + fn param_names<'de, D: Deserializer<'de>>( + deserializer: D, + ) -> Result>, D::Error> { + struct Visitor; + + impl<'de> de::Visitor<'de> for Visitor { + type Value = Vec>; + + fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("seq of param names") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: SeqAccess<'de>, + { + let mut param_names = Vec::with_capacity(seq.size_hint().unwrap_or_default()); + + while let Some(symbol) = seq.next_element::()? { + param_names.push(if symbol.is_empty() { + None + } else { + Some(Symbol::intern(&symbol)) + }); + } + + Ok(param_names) + } + } + + deserializer.deserialize_seq(Visitor) + } + + let Deserialized { mut function_signature, param_names } = + Deserialized::deserialize(deserializer)?; + function_signature.param_names = param_names; + + Ok(function_signature) } } From f8ee9f018c53c7555c07a035ff2efaad9723adc9 Mon Sep 17 00:00:00 2001 From: yukang Date: Tue, 7 Oct 2025 18:26:36 +0800 Subject: [PATCH 216/226] Print tip for human error format when check fail --- src/tools/compiletest/src/runtest.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index bd32bec383fa..e9dea95cafbd 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -878,6 +878,21 @@ impl<'test> TestCx<'test> { prefix = self.error_prefix(), n = not_found.len(), ); + + // FIXME: Ideally, we should check this at the place where we actually parse error annotations. + // it's better to use (negated) heuristic inside normalize_output if possible + if let Some(human_format) = self.props.compile_flags.iter().find(|flag| { + // `human`, `human-unicode`, `short` will not generate JSON output + flag.contains("error-format") + && (flag.contains("short") || flag.contains("human")) + }) { + let msg = format!( + "tests with compile flag `{}` should not have error annotations such as `//~ ERROR`", + human_format + ).color(Color::Red); + writeln!(self.stdout, "{}", msg); + } + for error in ¬_found { print_error(error); let mut suggestions = Vec::new(); From c5ab5307497269eae873750c7890b579bac03fb0 Mon Sep 17 00:00:00 2001 From: Yotam Ofek Date: Tue, 7 Oct 2025 13:37:49 +0300 Subject: [PATCH 217/226] move serde impls to separate module --- src/librustdoc/html/render/mod.rs | 98 ----------------- src/librustdoc/html/render/search_index.rs | 7 +- .../html/render/search_index/serde.rs | 102 ++++++++++++++++++ 3 files changed, 106 insertions(+), 101 deletions(-) create mode 100644 src/librustdoc/html/render/search_index/serde.rs diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index d604526aa4a1..c3b1e3eb6c08 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -61,8 +61,6 @@ use rustc_middle::ty::print::PrintTraitRefExt; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::symbol::{Symbol, sym}; use rustc_span::{BytePos, DUMMY_SP, FileName, RealFileName}; -use serde::ser::SerializeSeq as _; -use serde::{Deserialize, Deserializer, Serialize, Serializer}; use tracing::{debug, info}; pub(crate) use self::context::*; @@ -425,102 +423,6 @@ impl IndexItemFunctionType { } } -impl Serialize for IndexItemFunctionType { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - struct ParamNames<'a>(&'a [Option]); - - impl<'a> Serialize for ParamNames<'a> { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.collect_seq( - self.0 - .iter() - .map(|symbol| symbol.as_ref().map(Symbol::as_str).unwrap_or_default()), - ) - } - } - - let mut seq = serializer.serialize_seq(Some(2))?; - - let mut fn_type = String::new(); - self.write_to_string_without_param_names(&mut fn_type); - seq.serialize_element(&fn_type)?; - - seq.serialize_element(&ParamNames(&self.param_names))?; - - seq.end() - } -} - -impl<'de> Deserialize<'de> for IndexItemFunctionType { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - use serde::de::{self, SeqAccess}; - - #[derive(Deserialize)] - struct Deserialized { - #[serde(deserialize_with = "function_signature")] - function_signature: IndexItemFunctionType, - #[serde(deserialize_with = "param_names")] - param_names: Vec>, - } - - fn function_signature<'de, D: Deserializer<'de>>( - deserializer: D, - ) -> Result { - String::deserialize(deserializer).map(|sig| { - IndexItemFunctionType::read_from_string_without_param_names(sig.as_bytes()).0 - }) - } - - fn param_names<'de, D: Deserializer<'de>>( - deserializer: D, - ) -> Result>, D::Error> { - struct Visitor; - - impl<'de> de::Visitor<'de> for Visitor { - type Value = Vec>; - - fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("seq of param names") - } - - fn visit_seq(self, mut seq: A) -> Result - where - A: SeqAccess<'de>, - { - let mut param_names = Vec::with_capacity(seq.size_hint().unwrap_or_default()); - - while let Some(symbol) = seq.next_element::()? { - param_names.push(if symbol.is_empty() { - None - } else { - Some(Symbol::intern(&symbol)) - }); - } - - Ok(param_names) - } - } - - deserializer.deserialize_seq(Visitor) - } - - let Deserialized { mut function_signature, param_names } = - Deserialized::deserialize(deserializer)?; - function_signature.param_names = param_names; - - Ok(function_signature) - } -} - #[derive(Debug, Clone)] pub(crate) struct StylePath { /// The path to the theme diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index 080faff3a6de..67a88b1d2c57 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -1,9 +1,13 @@ pub(crate) mod encode; +mod serde; use std::collections::BTreeSet; use std::collections::hash_map::Entry; use std::path::Path; +use ::serde::de::{self, Deserializer, Error as _}; +use ::serde::ser::{SerializeSeq, Serializer}; +use ::serde::{Deserialize, Serialize}; use rustc_ast::join_path_syms; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_hir::attrs::AttributeKind; @@ -12,9 +16,6 @@ use rustc_middle::ty::TyCtxt; use rustc_span::def_id::DefId; use rustc_span::sym; use rustc_span::symbol::{Symbol, kw}; -use serde::de::{self, Deserializer, Error as _}; -use serde::ser::{SerializeSeq, Serializer}; -use serde::{Deserialize, Serialize}; use stringdex::internals as stringdex_internals; use thin_vec::ThinVec; use tracing::instrument; diff --git a/src/librustdoc/html/render/search_index/serde.rs b/src/librustdoc/html/render/search_index/serde.rs new file mode 100644 index 000000000000..2ba91a56031d --- /dev/null +++ b/src/librustdoc/html/render/search_index/serde.rs @@ -0,0 +1,102 @@ +use std::fmt::{self, Formatter}; + +use rustc_span::Symbol; +use serde::de::{self, SeqAccess}; +use serde::ser::SerializeSeq as _; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +use crate::html::render::IndexItemFunctionType; + +impl Serialize for IndexItemFunctionType { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + struct ParamNames<'a>(&'a [Option]); + + impl<'a> Serialize for ParamNames<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.collect_seq( + self.0 + .iter() + .map(|symbol| symbol.as_ref().map(Symbol::as_str).unwrap_or_default()), + ) + } + } + + let mut seq = serializer.serialize_seq(Some(2))?; + + let mut fn_type = String::new(); + self.write_to_string_without_param_names(&mut fn_type); + seq.serialize_element(&fn_type)?; + + seq.serialize_element(&ParamNames(&self.param_names))?; + + seq.end() + } +} + +impl<'de> Deserialize<'de> for IndexItemFunctionType { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + #[derive(Deserialize)] + struct Deserialized { + #[serde(deserialize_with = "function_signature")] + function_signature: IndexItemFunctionType, + #[serde(deserialize_with = "param_names")] + param_names: Vec>, + } + + fn function_signature<'de, D: Deserializer<'de>>( + deserializer: D, + ) -> Result { + String::deserialize(deserializer).map(|sig| { + IndexItemFunctionType::read_from_string_without_param_names(sig.as_bytes()).0 + }) + } + + fn param_names<'de, D: Deserializer<'de>>( + deserializer: D, + ) -> Result>, D::Error> { + struct Visitor; + + impl<'de> de::Visitor<'de> for Visitor { + type Value = Vec>; + + fn expecting(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str("seq of param names") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: SeqAccess<'de>, + { + let mut param_names = Vec::with_capacity(seq.size_hint().unwrap_or_default()); + + while let Some(symbol) = seq.next_element::()? { + param_names.push(if symbol.is_empty() { + None + } else { + Some(Symbol::intern(&symbol)) + }); + } + + Ok(param_names) + } + } + + deserializer.deserialize_seq(Visitor) + } + + let Deserialized { mut function_signature, param_names } = + Deserialized::deserialize(deserializer)?; + function_signature.param_names = param_names; + + Ok(function_signature) + } +} From 713e0dddfd22ef37765fd5907b4639f7a690f8ed Mon Sep 17 00:00:00 2001 From: yukang Date: Tue, 7 Oct 2025 21:05:45 +0800 Subject: [PATCH 218/226] Fix comments error for CtfeProvenance --- compiler/rustc_middle/src/mir/interpret/pointer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_middle/src/mir/interpret/pointer.rs b/compiler/rustc_middle/src/mir/interpret/pointer.rs index c581b2ebf092..2aac8852b7e8 100644 --- a/compiler/rustc_middle/src/mir/interpret/pointer.rs +++ b/compiler/rustc_middle/src/mir/interpret/pointer.rs @@ -167,7 +167,7 @@ impl CtfeProvenance { } impl Provenance for CtfeProvenance { - // With the `AllocId` as provenance, the `offset` is interpreted *relative to the allocation*, + // With the `CtfeProvenance` as provenance, the `offset` is interpreted *relative to the allocation*, // so ptr-to-int casts are not possible (since we do not know the global physical offset). const OFFSET_IS_ADDR: bool = false; From 0a3f1d5b9843c5e911a90ed9eb3010c29a407c66 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Tue, 7 Oct 2025 15:58:20 +0200 Subject: [PATCH 219/226] c-variadic: fix thir-print for `...` without a pattern --- compiler/rustc_mir_build/src/thir/print.rs | 4 +- tests/ui/thir-print/c-variadic.rs | 6 +++ tests/ui/thir-print/c-variadic.stdout | 61 ++++++++++++++++++++++ 3 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 tests/ui/thir-print/c-variadic.rs create mode 100644 tests/ui/thir-print/c-variadic.stdout diff --git a/compiler/rustc_mir_build/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs index b7e8d6fea40b..188f179669cb 100644 --- a/compiler/rustc_mir_build/src/thir/print.rs +++ b/compiler/rustc_mir_build/src/thir/print.rs @@ -689,7 +689,9 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { print_indented!(self, "kind: PatKind {", depth_lvl); match pat_kind { - PatKind::Missing => unreachable!(), + PatKind::Missing => { + print_indented!(self, "Missing", depth_lvl + 1); + } PatKind::Wild => { print_indented!(self, "Wild", depth_lvl + 1); } diff --git a/tests/ui/thir-print/c-variadic.rs b/tests/ui/thir-print/c-variadic.rs new file mode 100644 index 000000000000..e03c4b083858 --- /dev/null +++ b/tests/ui/thir-print/c-variadic.rs @@ -0,0 +1,6 @@ +//@ compile-flags: -Zunpretty=thir-tree --crate-type=lib +//@ check-pass +#![feature(c_variadic)] + +// The `...` argument uses `PatKind::Missing`. +unsafe extern "C" fn foo(_: i32, ...) {} diff --git a/tests/ui/thir-print/c-variadic.stdout b/tests/ui/thir-print/c-variadic.stdout new file mode 100644 index 000000000000..8363f06ebe88 --- /dev/null +++ b/tests/ui/thir-print/c-variadic.stdout @@ -0,0 +1,61 @@ +DefId(0:3 ~ c_variadic[a5de]::foo): +params: [ + Param { + ty: i32 + ty_span: Some($DIR/c-variadic.rs:6:29: 6:32 (#0)) + self_kind: None + hir_id: Some(HirId(DefId(0:3 ~ c_variadic[a5de]::foo).1)) + param: Some( + Pat: { + ty: i32 + span: $DIR/c-variadic.rs:6:26: 6:27 (#0) + kind: PatKind { + Wild + } + } + ) + } + Param { + ty: std::ffi::VaListImpl<'{erased}> + ty_span: None + self_kind: None + hir_id: Some(HirId(DefId(0:3 ~ c_variadic[a5de]::foo).3)) + param: Some( + Pat: { + ty: std::ffi::VaListImpl<'{erased}> + span: $DIR/c-variadic.rs:6:34: 6:37 (#0) + kind: PatKind { + Missing + } + } + ) + } +] +body: + Expr { + ty: () + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(6)), backwards_incompatible: None } + span: $DIR/c-variadic.rs:6:39: 6:41 (#0) + kind: + Scope { + region_scope: Node(6) + lint_level: Explicit(HirId(DefId(0:3 ~ c_variadic[a5de]::foo).6)) + value: + Expr { + ty: () + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(6)), backwards_incompatible: None } + span: $DIR/c-variadic.rs:6:39: 6:41 (#0) + kind: + Block { + targeted_by_break: false + span: $DIR/c-variadic.rs:6:39: 6:41 (#0) + region_scope: Node(5) + safety_mode: Safe + stmts: [] + expr: [] + } + } + } + } + + From fbdc685ed09f8315f583f9ba846c056b3ed810a4 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 1 Oct 2025 21:14:27 +0200 Subject: [PATCH 220/226] cmse: disallow `impl Trait` in `cmse-nonsecure-entry` return types --- .../src/hir_ty_lowering/cmse.rs | 18 +++++++++-- .../cmse-nonsecure-call/generics.rs | 3 +- .../cmse-nonsecure-call/generics.stderr | 24 ++++++++++----- .../cmse-nonsecure-entry/generics.rs | 12 ++++++++ .../cmse-nonsecure-entry/generics.stderr | 30 ++++++++++++++++++- 5 files changed, 75 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs index 378771c05ee3..91d1f89b7447 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs @@ -3,7 +3,7 @@ use rustc_errors::{DiagCtxtHandle, E0781, struct_span_code_err}; use rustc_hir::{self as hir, HirId}; use rustc_middle::bug; use rustc_middle::ty::layout::{LayoutError, TyAndLayout}; -use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; use crate::errors; @@ -165,9 +165,23 @@ fn is_valid_cmse_output<'tcx>( // this type is only used for layout computation, which does not rely on regions let fn_sig = tcx.instantiate_bound_regions_with_erased(fn_sig); let fn_sig = tcx.erase_and_anonymize_regions(fn_sig); + let return_type = fn_sig.output(); + + // `impl Trait` is already disallowed with `cmse-nonsecure-call`, because that ABI is only + // allowed on function pointers, and function pointers cannot contain `impl Trait` in their + // signature. + // + // Here we explicitly disallow `impl Trait` in the `cmse-nonsecure-entry` return type too, to + // prevent query cycles when calculating the layout. This ABI is meant to be used with + // `#[no_mangle]` or similar, so generics in the type really don't make sense. + // + // see also https://github.com/rust-lang/rust/issues/147242. + if return_type.has_opaque_types() { + return Err(tcx.arena.alloc(LayoutError::TooGeneric(return_type))); + } let typing_env = ty::TypingEnv::fully_monomorphized(); - let layout = tcx.layout_of(typing_env.as_query_input(fn_sig.output()))?; + let layout = tcx.layout_of(typing_env.as_query_input(return_type))?; Ok(is_valid_cmse_output_layout(layout)) } diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.rs index 4ce5890a2da3..2dc540675e2e 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.rs +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.rs @@ -14,8 +14,9 @@ struct Test { f1: extern "cmse-nonsecure-call" fn(U, u32, u32, u32) -> u64, //~^ ERROR cannot find type `U` in this scope //~| ERROR function pointer types may not have generic parameters - f2: extern "cmse-nonsecure-call" fn(impl Copy, u32, u32, u32) -> u64, + f2: extern "cmse-nonsecure-call" fn(impl Copy, u32, u32, u32) -> impl Copy, //~^ ERROR `impl Trait` is not allowed in `fn` pointer parameters + //~| ERROR `impl Trait` is not allowed in `fn` pointer return types f3: extern "cmse-nonsecure-call" fn(T, u32, u32, u32) -> u64, //~ ERROR [E0798] f4: extern "cmse-nonsecure-call" fn(Wrapper, u32, u32, u32) -> u64, //~ ERROR [E0798] } diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr index 156568535763..750dbf8d5707 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr @@ -25,25 +25,33 @@ LL | struct Test { error[E0562]: `impl Trait` is not allowed in `fn` pointer parameters --> $DIR/generics.rs:17:41 | -LL | f2: extern "cmse-nonsecure-call" fn(impl Copy, u32, u32, u32) -> u64, +LL | f2: extern "cmse-nonsecure-call" fn(impl Copy, u32, u32, u32) -> impl Copy, | ^^^^^^^^^ | = note: `impl Trait` is only allowed in arguments and return types of functions and methods +error[E0562]: `impl Trait` is not allowed in `fn` pointer return types + --> $DIR/generics.rs:17:70 + | +LL | f2: extern "cmse-nonsecure-call" fn(impl Copy, u32, u32, u32) -> impl Copy, + | ^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods + error[E0798]: function pointers with the `"cmse-nonsecure-call"` ABI cannot contain generics in their type - --> $DIR/generics.rs:19:9 + --> $DIR/generics.rs:20:9 | LL | f3: extern "cmse-nonsecure-call" fn(T, u32, u32, u32) -> u64, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0798]: function pointers with the `"cmse-nonsecure-call"` ABI cannot contain generics in their type - --> $DIR/generics.rs:20:9 + --> $DIR/generics.rs:21:9 | LL | f4: extern "cmse-nonsecure-call" fn(Wrapper, u32, u32, u32) -> u64, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0798]: return value of `"cmse-nonsecure-call"` function too large to pass via registers - --> $DIR/generics.rs:26:71 + --> $DIR/generics.rs:27:71 | LL | type WithTraitObject = extern "cmse-nonsecure-call" fn(&dyn Trait) -> &dyn Trait; | ^^^^^^^^^^ this type doesn't fit in the available registers @@ -52,7 +60,7 @@ LL | type WithTraitObject = extern "cmse-nonsecure-call" fn(&dyn Trait) -> &dyn = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size error[E0798]: return value of `"cmse-nonsecure-call"` function too large to pass via registers - --> $DIR/generics.rs:30:60 + --> $DIR/generics.rs:31:60 | LL | extern "cmse-nonsecure-call" fn(&'static dyn Trait) -> &'static dyn Trait; | ^^^^^^^^^^^^^^^^^^ this type doesn't fit in the available registers @@ -61,7 +69,7 @@ LL | extern "cmse-nonsecure-call" fn(&'static dyn Trait) -> &'static dyn Tra = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size error[E0798]: return value of `"cmse-nonsecure-call"` function too large to pass via registers - --> $DIR/generics.rs:37:60 + --> $DIR/generics.rs:38:60 | LL | extern "cmse-nonsecure-call" fn(WrapperTransparent) -> WrapperTransparent; | ^^^^^^^^^^^^^^^^^^ this type doesn't fit in the available registers @@ -70,12 +78,12 @@ LL | extern "cmse-nonsecure-call" fn(WrapperTransparent) -> WrapperTranspare = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size error[E0045]: C-variadic functions with the "cmse-nonsecure-call" calling convention are not supported - --> $DIR/generics.rs:40:20 + --> $DIR/generics.rs:41:20 | LL | type WithVarArgs = extern "cmse-nonsecure-call" fn(u32, ...); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention -error: aborting due to 9 previous errors +error: aborting due to 10 previous errors Some errors have detailed explanations: E0045, E0412, E0562, E0798. For more information about an error, try `rustc --explain E0045`. diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs index aee01e25ddc4..116f0fdc972f 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs @@ -65,3 +65,15 @@ extern "cmse-nonsecure-entry" fn wrapped_trait_object(x: WrapperTransparent) -> //~^ ERROR return value of `"cmse-nonsecure-entry"` function too large to pass via registers [E0798] x } + +extern "cmse-nonsecure-entry" fn return_impl_trait(_: impl Copy) -> impl Copy { + //~^ ERROR functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type + //~| ERROR functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type + 0u128 +} + +extern "cmse-nonsecure-entry" fn return_impl_trait_nested(v: (impl Copy, i32)) -> (impl Copy, i32) { + //~^ ERROR functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type + //~| ERROR functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type + v +} diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr index df1a91070437..4158fef4553a 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr @@ -16,6 +16,34 @@ error[E0798]: functions with the `"cmse-nonsecure-entry"` ABI cannot contain gen LL | extern "cmse-nonsecure-entry" fn impl_trait(_: impl Copy, _: u32, _: u32, _: u32) -> u64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error[E0798]: functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type + --> $DIR/generics.rs:69:1 + | +LL | extern "cmse-nonsecure-entry" fn return_impl_trait(_: impl Copy) -> impl Copy { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0798]: functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type + --> $DIR/generics.rs:69:1 + | +LL | extern "cmse-nonsecure-entry" fn return_impl_trait(_: impl Copy) -> impl Copy { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0798]: functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type + --> $DIR/generics.rs:75:1 + | +LL | extern "cmse-nonsecure-entry" fn return_impl_trait_nested(v: (impl Copy, i32)) -> (impl Copy, i32) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0798]: functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type + --> $DIR/generics.rs:75:1 + | +LL | extern "cmse-nonsecure-entry" fn return_impl_trait_nested(v: (impl Copy, i32)) -> (impl Copy, i32) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + error[E0798]: functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type --> $DIR/generics.rs:14:5 | @@ -61,6 +89,6 @@ LL | extern "cmse-nonsecure-entry" fn wrapped_trait_object(x: WrapperTransparent = note: functions with the `"cmse-nonsecure-entry"` ABI must pass their result via the available return registers = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size -error: aborting due to 7 previous errors +error: aborting due to 11 previous errors For more information about this error, try `rustc --explain E0798`. From 659575bfbe295d1ca6c86e6fe72325631ec4cf27 Mon Sep 17 00:00:00 2001 From: Urgau Date: Tue, 7 Oct 2025 18:15:02 +0200 Subject: [PATCH 221/226] Add `t-` prefix to `S-waiting-on-{team}` labels --- rust-bors.toml | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/rust-bors.toml b/rust-bors.toml index 1014e31bae49..e813c6c4b116 100644 --- a/rust-bors.toml +++ b/rust-bors.toml @@ -15,12 +15,13 @@ labels_blocking_approval = [ "S-waiting-on-crater", "S-waiting-on-fcp", "S-waiting-on-MCP", - "S-waiting-on-lang", - "S-waiting-on-compiler", - "S-waiting-on-libs-api", - "S-waiting-on-types", - "S-waiting-on-opsem", - "S-waiting-on-rustdoc", - "S-waiting-on-rustdoc-frontend", - "S-waiting-on-clippy" + "S-waiting-on-t-lang", + "S-waiting-on-t-compiler", + "S-waiting-on-t-libs-api", + "S-waiting-on-t-libs", + "S-waiting-on-t-types", + "S-waiting-on-t-opsem", + "S-waiting-on-t-rustdoc", + "S-waiting-on-t-rustdoc-frontend", + "S-waiting-on-t-clippy" ] From 5446a0ae0afa8678afff93fb2fdc7db860fbc102 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Wed, 8 Oct 2025 04:52:55 +0000 Subject: [PATCH 222/226] Prepare for merging from rust-lang/rust This updates the rust-version file to 4fd31815524baba0bf368f151f757101f432e3de. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 77436ae2f825..692cbc0a56bc 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -3b8665c5ab3aeced9b01672404c3764583e722ca +4fd31815524baba0bf368f151f757101f432e3de From c33b667e8bc6623437c17e85519583796e38a1ae Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 6 Oct 2025 17:19:07 +1100 Subject: [PATCH 223/226] Hoist stranded `use` declarations --- compiler/rustc_middle/src/query/mod.rs | 4 ++-- compiler/rustc_middle/src/query/plumbing.rs | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 895c8c0295a0..30c4b4369830 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -103,6 +103,8 @@ use rustc_span::{DUMMY_SP, Span, Symbol}; use rustc_target::spec::{PanicStrategy, SanitizerSet}; use {rustc_abi as abi, rustc_ast as ast, rustc_hir as hir}; +pub use self::keys::{AsLocalKey, Key, LocalCrate}; +pub use self::plumbing::{IntoQueryParam, TyCtxtAt, TyCtxtEnsureDone, TyCtxtEnsureOk}; use crate::infer::canonical::{self, Canonical}; use crate::lint::LintExpectation; use crate::metadata::ModChild; @@ -146,11 +148,9 @@ use crate::{dep_graph, mir, thir}; mod arena_cached; pub mod erase; mod keys; -pub use keys::{AsLocalKey, Key, LocalCrate}; pub mod on_disk_cache; #[macro_use] pub mod plumbing; -pub use plumbing::{IntoQueryParam, TyCtxtAt, TyCtxtEnsureDone, TyCtxtEnsureOk}; // Each of these queries corresponds to a function pointer field in the // `Providers` struct for requesting a value of that type, and a method diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index 769df1ffd6f9..16cc81dacad4 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -9,7 +9,9 @@ use rustc_query_system::dep_graph::{DepNodeIndex, SerializedDepNodeIndex}; pub(crate) use rustc_query_system::query::QueryJobId; use rustc_query_system::query::*; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; +pub use sealed::IntoQueryParam; +use super::erase::EraseType; use crate::dep_graph; use crate::dep_graph::DepKind; use crate::query::on_disk_cache::{CacheEncoder, EncodedDepNodeIndex, OnDiskCache}; @@ -694,10 +696,6 @@ mod sealed { } } -pub use sealed::IntoQueryParam; - -use super::erase::EraseType; - #[derive(Copy, Clone, Debug, HashStable)] pub struct CyclePlaceholder(pub ErrorGuaranteed); From b97bdcb0a03abe95db5cdb4ea685ee320ac8ef29 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Wed, 8 Oct 2025 17:39:21 +1100 Subject: [PATCH 224/226] Extract query-method inner functions to `query::inner` --- compiler/rustc_middle/src/query/inner.rs | 78 ++++++++++++++++++++ compiler/rustc_middle/src/query/mod.rs | 5 +- compiler/rustc_middle/src/query/plumbing.rs | 82 +++------------------ 3 files changed, 90 insertions(+), 75 deletions(-) create mode 100644 compiler/rustc_middle/src/query/inner.rs diff --git a/compiler/rustc_middle/src/query/inner.rs b/compiler/rustc_middle/src/query/inner.rs new file mode 100644 index 000000000000..6efe21bc8997 --- /dev/null +++ b/compiler/rustc_middle/src/query/inner.rs @@ -0,0 +1,78 @@ +//! Helper functions that serve as the immediate implementation of +//! `tcx.$query(..)` and its variations. + +use rustc_query_system::query::{QueryCache, QueryMode, try_get_cached}; +use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; + +use crate::query::IntoQueryParam; +use crate::query::erase::{self, Erase, EraseType}; +use crate::ty::TyCtxt; + +/// Shared implementation of `tcx.$query(..)` and `tcx.at(span).$query(..)` +/// for all queries. +#[inline(always)] +pub(crate) fn query_get_at<'tcx, Cache>( + tcx: TyCtxt<'tcx>, + execute_query: fn(TyCtxt<'tcx>, Span, Cache::Key, QueryMode) -> Option, + query_cache: &Cache, + span: Span, + key: Cache::Key, +) -> Cache::Value +where + Cache: QueryCache, +{ + let key = key.into_query_param(); + match try_get_cached(tcx, query_cache, &key) { + Some(value) => value, + None => execute_query(tcx, span, key, QueryMode::Get).unwrap(), + } +} + +/// Shared implementation of `tcx.ensure_ok().$query(..)` for most queries, +/// and `tcx.ensure_done().$query(..)` for all queries. +#[inline] +pub(crate) fn query_ensure<'tcx, Cache>( + tcx: TyCtxt<'tcx>, + execute_query: fn(TyCtxt<'tcx>, Span, Cache::Key, QueryMode) -> Option, + query_cache: &Cache, + key: Cache::Key, + check_cache: bool, +) where + Cache: QueryCache, +{ + let key = key.into_query_param(); + if try_get_cached(tcx, query_cache, &key).is_none() { + execute_query(tcx, DUMMY_SP, key, QueryMode::Ensure { check_cache }); + } +} + +/// Shared implementation of `tcx.ensure_ok().$query(..)` for queries that +/// have the `return_result_from_ensure_ok` modifier. +#[inline] +pub(crate) fn query_ensure_error_guaranteed<'tcx, Cache, T>( + tcx: TyCtxt<'tcx>, + execute_query: fn(TyCtxt<'tcx>, Span, Cache::Key, QueryMode) -> Option, + query_cache: &Cache, + key: Cache::Key, + check_cache: bool, +) -> Result<(), ErrorGuaranteed> +where + Cache: QueryCache>>, + Result: EraseType, +{ + let key = key.into_query_param(); + if let Some(res) = try_get_cached(tcx, query_cache, &key) { + erase::restore(res).map(drop) + } else { + execute_query(tcx, DUMMY_SP, key, QueryMode::Ensure { check_cache }) + .map(erase::restore) + .map(|res| res.map(drop)) + // Either we actually executed the query, which means we got a full `Result`, + // or we can just assume the query succeeded, because it was green in the + // incremental cache. If it is green, that means that the previous compilation + // that wrote to the incremental cache compiles successfully. That is only + // possible if the cache entry was `Ok(())`, so we emit that here, without + // actually encoding the `Result` in the cache or loading it from there. + .unwrap_or(Ok(())) + } +} diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 30c4b4369830..4bac1e8406c1 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -121,9 +121,7 @@ use crate::mir::interpret::{ }; use crate::mir::mono::{CodegenUnit, CollectionMode, MonoItem, MonoItemPartitions}; use crate::query::erase::{Erase, erase, restore}; -use crate::query::plumbing::{ - CyclePlaceholder, DynamicQuery, query_ensure, query_ensure_error_guaranteed, query_get_at, -}; +use crate::query::plumbing::{CyclePlaceholder, DynamicQuery}; use crate::traits::query::{ CanonicalAliasGoal, CanonicalDropckOutlivesGoal, CanonicalImpliedOutlivesBoundsGoal, CanonicalMethodAutoderefStepsGoal, CanonicalPredicateGoal, CanonicalTypeOpAscribeUserTypeGoal, @@ -147,6 +145,7 @@ use crate::{dep_graph, mir, thir}; mod arena_cached; pub mod erase; +pub(crate) mod inner; mod keys; pub mod on_disk_cache; #[macro_use] diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index 16cc81dacad4..c2fc43b444b8 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -8,10 +8,9 @@ use rustc_query_system::HandleCycleError; use rustc_query_system::dep_graph::{DepNodeIndex, SerializedDepNodeIndex}; pub(crate) use rustc_query_system::query::QueryJobId; use rustc_query_system::query::*; -use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; +use rustc_span::{ErrorGuaranteed, Span}; pub use sealed::IntoQueryParam; -use super::erase::EraseType; use crate::dep_graph; use crate::dep_graph::DepKind; use crate::query::on_disk_cache::{CacheEncoder, EncodedDepNodeIndex, OnDiskCache}; @@ -167,78 +166,17 @@ impl<'tcx> TyCtxt<'tcx> { } } -#[inline(always)] -pub fn query_get_at<'tcx, Cache>( - tcx: TyCtxt<'tcx>, - execute_query: fn(TyCtxt<'tcx>, Span, Cache::Key, QueryMode) -> Option, - query_cache: &Cache, - span: Span, - key: Cache::Key, -) -> Cache::Value -where - Cache: QueryCache, -{ - let key = key.into_query_param(); - match try_get_cached(tcx, query_cache, &key) { - Some(value) => value, - None => execute_query(tcx, span, key, QueryMode::Get).unwrap(), - } -} - -#[inline] -pub fn query_ensure<'tcx, Cache>( - tcx: TyCtxt<'tcx>, - execute_query: fn(TyCtxt<'tcx>, Span, Cache::Key, QueryMode) -> Option, - query_cache: &Cache, - key: Cache::Key, - check_cache: bool, -) where - Cache: QueryCache, -{ - let key = key.into_query_param(); - if try_get_cached(tcx, query_cache, &key).is_none() { - execute_query(tcx, DUMMY_SP, key, QueryMode::Ensure { check_cache }); - } -} - -#[inline] -pub fn query_ensure_error_guaranteed<'tcx, Cache, T>( - tcx: TyCtxt<'tcx>, - execute_query: fn(TyCtxt<'tcx>, Span, Cache::Key, QueryMode) -> Option, - query_cache: &Cache, - key: Cache::Key, - check_cache: bool, -) -> Result<(), ErrorGuaranteed> -where - Cache: QueryCache>>, - Result: EraseType, -{ - let key = key.into_query_param(); - if let Some(res) = try_get_cached(tcx, query_cache, &key) { - super::erase::restore(res).map(drop) - } else { - execute_query(tcx, DUMMY_SP, key, QueryMode::Ensure { check_cache }) - .map(super::erase::restore) - .map(|res| res.map(drop)) - // Either we actually executed the query, which means we got a full `Result`, - // or we can just assume the query succeeded, because it was green in the - // incremental cache. If it is green, that means that the previous compilation - // that wrote to the incremental cache compiles successfully. That is only - // possible if the cache entry was `Ok(())`, so we emit that here, without - // actually encoding the `Result` in the cache or loading it from there. - .unwrap_or(Ok(())) - } -} - -macro_rules! query_ensure { +/// Calls either `query_ensure` or `query_ensure_error_guaranteed`, depending +/// on whether the list of modifiers contains `return_result_from_ensure_ok`. +macro_rules! query_ensure_select { ([]$($args:tt)*) => { - query_ensure($($args)*) + crate::query::inner::query_ensure($($args)*) }; ([(return_result_from_ensure_ok) $($rest:tt)*]$($args:tt)*) => { - query_ensure_error_guaranteed($($args)*).map(|_| ()) + crate::query::inner::query_ensure_error_guaranteed($($args)*) }; ([$other:tt $($modifiers:tt)*]$($args:tt)*) => { - query_ensure!([$($modifiers)*]$($args)*) + query_ensure_select!([$($modifiers)*]$($args)*) }; } @@ -434,7 +372,7 @@ macro_rules! define_callbacks { self, key: query_helper_param_ty!($($K)*), ) -> ensure_ok_result!([$($modifiers)*]) { - query_ensure!( + query_ensure_select!( [$($modifiers)*] self.tcx, self.tcx.query_system.fns.engine.$name, @@ -449,7 +387,7 @@ macro_rules! define_callbacks { $($(#[$attr])* #[inline(always)] pub fn $name(self, key: query_helper_param_ty!($($K)*)) { - query_ensure( + crate::query::inner::query_ensure( self.tcx, self.tcx.query_system.fns.engine.$name, &self.tcx.query_system.caches.$name, @@ -474,7 +412,7 @@ macro_rules! define_callbacks { #[inline(always)] pub fn $name(self, key: query_helper_param_ty!($($K)*)) -> $V { - restore::<$V>(query_get_at( + restore::<$V>(crate::query::inner::query_get_at( self.tcx, self.tcx.query_system.fns.engine.$name, &self.tcx.query_system.caches.$name, From cb0c092c786acebe56b915344b82e53d06b0e1d4 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 6 Oct 2025 17:22:39 +1100 Subject: [PATCH 225/226] Prepare `define_feedable!` for code extraction --- compiler/rustc_middle/src/query/mod.rs | 5 +---- compiler/rustc_middle/src/query/plumbing.rs | 20 ++++++++++++-------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 4bac1e8406c1..a81c2530a392 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -70,7 +70,6 @@ use std::sync::Arc; use rustc_abi::Align; use rustc_arena::TypedArena; use rustc_ast::expand::allocator::AllocatorKind; -use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::steal::Steal; @@ -88,9 +87,7 @@ use rustc_index::IndexVec; use rustc_lint_defs::LintId; use rustc_macros::rustc_queries; use rustc_query_system::ich::StableHashingContext; -use rustc_query_system::query::{ - QueryCache, QueryMode, QueryStackDeferred, QueryState, try_get_cached, -}; +use rustc_query_system::query::{QueryMode, QueryStackDeferred, QueryState}; use rustc_session::Limits; use rustc_session::config::{EntryFnType, OptLevel, OutputFilenames, SymbolManglingVersion}; use rustc_session::cstore::{ diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index c2fc43b444b8..cb8a15c029f4 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -501,17 +501,22 @@ macro_rules! define_feedable { $(#[$attr])* #[inline(always)] pub fn $name(self, value: queries::$name::ProvidedValue<'tcx>) { + #![allow(unused_imports)] // Removed later in this PR. + use rustc_data_structures::fingerprint::Fingerprint; + use rustc_query_system::query::{QueryCache, try_get_cached}; + let key = self.key().into_query_param(); let tcx = self.tcx; let erased = queries::$name::provided_to_erased(tcx, value); - let value = restore::<$V>(erased); + let value = erase::restore::<$V>(erased); let cache = &tcx.query_system.caches.$name; + let dep_kind: dep_graph::DepKind = dep_graph::dep_kinds::$name; let hasher: Option, &_) -> _> = hash_result!([$($modifiers)*]); match try_get_cached(tcx, cache, &key) { Some(old) => { - let old = restore::<$V>(old); + let old = erase::restore::<$V>(old); if let Some(hasher) = hasher { let (value_hash, old_hash): (Fingerprint, Fingerprint) = tcx.with_stable_hashing_context(|mut hcx| (hasher(&mut hcx, &value), hasher(&mut hcx, &old)) @@ -521,9 +526,8 @@ macro_rules! define_feedable { // results is tainted by errors. In this case, delay a bug to // ensure compilation is doomed, and keep the `old` value. tcx.dcx().delayed_bug(format!( - "Trying to feed an already recorded value for query {} key={key:?}:\n\ + "Trying to feed an already recorded value for query {dep_kind:?} key={key:?}:\n\ old value: {old:?}\nnew value: {value:?}", - stringify!($name), )); } } else { @@ -531,18 +535,18 @@ macro_rules! define_feedable { // If feeding the same value multiple times needs to be supported, // the query should not be marked `no_hash`. bug!( - "Trying to feed an already recorded value for query {} key={key:?}:\nold value: {old:?}\nnew value: {value:?}", - stringify!($name), + "Trying to feed an already recorded value for query {dep_kind:?} key={key:?}:\n\ + old value: {old:?}\nnew value: {value:?}", ) } } None => { - let dep_node = dep_graph::DepNode::construct(tcx, dep_graph::dep_kinds::$name, &key); + let dep_node = dep_graph::DepNode::construct(tcx, dep_kind, &key); let dep_node_index = tcx.dep_graph.with_feed_task( dep_node, tcx, &value, - hash_result!([$($modifiers)*]), + hasher, ); cache.complete(key, erased, dep_node_index); } From a282336c5ca01d1be21ee21f7fb872341a36a65b Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 6 Oct 2025 16:07:09 +1100 Subject: [PATCH 226/226] Extract most code from `define_feedable!` --- compiler/rustc_middle/src/query/inner.rs | 56 +++++++++++++++++++++ compiler/rustc_middle/src/query/plumbing.rs | 51 ++++--------------- 2 files changed, 65 insertions(+), 42 deletions(-) diff --git a/compiler/rustc_middle/src/query/inner.rs b/compiler/rustc_middle/src/query/inner.rs index 6efe21bc8997..ee828ae55f7a 100644 --- a/compiler/rustc_middle/src/query/inner.rs +++ b/compiler/rustc_middle/src/query/inner.rs @@ -1,9 +1,15 @@ //! Helper functions that serve as the immediate implementation of //! `tcx.$query(..)` and its variations. +use std::fmt::Debug; + +use rustc_data_structures::fingerprint::Fingerprint; +use rustc_query_system::dep_graph::{DepKind, DepNodeParams}; +use rustc_query_system::ich::StableHashingContext; use rustc_query_system::query::{QueryCache, QueryMode, try_get_cached}; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; +use crate::dep_graph; use crate::query::IntoQueryParam; use crate::query::erase::{self, Erase, EraseType}; use crate::ty::TyCtxt; @@ -76,3 +82,53 @@ where .unwrap_or(Ok(())) } } + +/// Common implementation of query feeding, used by `define_feedable!`. +pub(crate) fn query_feed<'tcx, Cache, Value>( + tcx: TyCtxt<'tcx>, + dep_kind: DepKind, + hasher: Option, &Value) -> Fingerprint>, + cache: &Cache, + key: Cache::Key, + erased: Erase, +) where + Cache: QueryCache>, + Cache::Key: DepNodeParams>, + Value: EraseType + Debug, +{ + let value = erase::restore::(erased); + + match try_get_cached(tcx, cache, &key) { + Some(old) => { + let old = erase::restore::(old); + if let Some(hasher) = hasher { + let (value_hash, old_hash): (Fingerprint, Fingerprint) = tcx + .with_stable_hashing_context(|mut hcx| { + (hasher(&mut hcx, &value), hasher(&mut hcx, &old)) + }); + if old_hash != value_hash { + // We have an inconsistency. This can happen if one of the two + // results is tainted by errors. In this case, delay a bug to + // ensure compilation is doomed, and keep the `old` value. + tcx.dcx().delayed_bug(format!( + "Trying to feed an already recorded value for query {dep_kind:?} key={key:?}:\n\ + old value: {old:?}\nnew value: {value:?}", + )); + } + } else { + // The query is `no_hash`, so we have no way to perform a sanity check. + // If feeding the same value multiple times needs to be supported, + // the query should not be marked `no_hash`. + bug!( + "Trying to feed an already recorded value for query {dep_kind:?} key={key:?}:\n\ + old value: {old:?}\nnew value: {value:?}", + ) + } + } + None => { + let dep_node = dep_graph::DepNode::construct(tcx, dep_kind, &key); + let dep_node_index = tcx.dep_graph.with_feed_task(dep_node, tcx, &value, hasher); + cache.complete(key, erased, dep_node_index); + } + } +} diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index cb8a15c029f4..8d01d9482ed4 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -501,56 +501,23 @@ macro_rules! define_feedable { $(#[$attr])* #[inline(always)] pub fn $name(self, value: queries::$name::ProvidedValue<'tcx>) { - #![allow(unused_imports)] // Removed later in this PR. - use rustc_data_structures::fingerprint::Fingerprint; - use rustc_query_system::query::{QueryCache, try_get_cached}; - let key = self.key().into_query_param(); let tcx = self.tcx; let erased = queries::$name::provided_to_erased(tcx, value); - let value = erase::restore::<$V>(erased); let cache = &tcx.query_system.caches.$name; let dep_kind: dep_graph::DepKind = dep_graph::dep_kinds::$name; let hasher: Option, &_) -> _> = hash_result!([$($modifiers)*]); - match try_get_cached(tcx, cache, &key) { - Some(old) => { - let old = erase::restore::<$V>(old); - if let Some(hasher) = hasher { - let (value_hash, old_hash): (Fingerprint, Fingerprint) = tcx.with_stable_hashing_context(|mut hcx| - (hasher(&mut hcx, &value), hasher(&mut hcx, &old)) - ); - if old_hash != value_hash { - // We have an inconsistency. This can happen if one of the two - // results is tainted by errors. In this case, delay a bug to - // ensure compilation is doomed, and keep the `old` value. - tcx.dcx().delayed_bug(format!( - "Trying to feed an already recorded value for query {dep_kind:?} key={key:?}:\n\ - old value: {old:?}\nnew value: {value:?}", - )); - } - } else { - // The query is `no_hash`, so we have no way to perform a sanity check. - // If feeding the same value multiple times needs to be supported, - // the query should not be marked `no_hash`. - bug!( - "Trying to feed an already recorded value for query {dep_kind:?} key={key:?}:\n\ - old value: {old:?}\nnew value: {value:?}", - ) - } - } - None => { - let dep_node = dep_graph::DepNode::construct(tcx, dep_kind, &key); - let dep_node_index = tcx.dep_graph.with_feed_task( - dep_node, - tcx, - &value, - hasher, - ); - cache.complete(key, erased, dep_node_index); - } - } + + $crate::query::inner::query_feed( + tcx, + dep_kind, + hasher, + cache, + key, + erased, + ); } })* }