diff --git a/src/bootstrap/src/core/build_steps/perf.rs b/src/bootstrap/src/core/build_steps/perf.rs index 108b7f90c149e..7bdcb483eb923 100644 --- a/src/bootstrap/src/core/build_steps/perf.rs +++ b/src/bootstrap/src/core/build_steps/perf.rs @@ -196,7 +196,7 @@ Consider setting `rust.debuginfo-level = 1` in `bootstrap.toml`."#); apply_shared_opts(&mut cmd, opts); cmd.run(builder); - println!("You can find the results at `{}`", &results_dir.display()); + println!("You can find the results at `{}`", results_dir.display()); } PerfCommand::Benchmark { id, opts } => { cmd.arg("bench_local"); diff --git a/src/bootstrap/src/core/build_steps/setup.rs b/src/bootstrap/src/core/build_steps/setup.rs index 85c23ac18e763..8e65cb18d58fb 100644 --- a/src/bootstrap/src/core/build_steps/setup.rs +++ b/src/bootstrap/src/core/build_steps/setup.rs @@ -312,7 +312,7 @@ fn attempt_toolchain_link(builder: &Builder<'_>, stage_path: &str) { eprintln!( "To manually link stage 1 build to `stage1` toolchain, run:\n `rustup toolchain link stage1 {}`", - &stage_path + stage_path ); } } diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 991592ec522d0..8dd617f522209 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -1349,9 +1349,9 @@ impl Step for Tidy { /// for the `dev` or `nightly` channels. fn run(self, builder: &Builder<'_>) { let mut cmd = builder.tool_cmd(Tool::Tidy); - cmd.arg(format!("--root-path={}", &builder.src.display())); - cmd.arg(format!("--cargo-path={}", &builder.initial_cargo.display())); - cmd.arg(format!("--output-dir={}", &builder.out.display())); + cmd.arg(format!("--root-path={}", builder.src.display())); + cmd.arg(format!("--cargo-path={}", builder.initial_cargo.display())); + cmd.arg(format!("--output-dir={}", builder.out.display())); // Tidy is heavily IO constrained. Still respect `-j`, but use a higher limit if `jobs` hasn't been configured. let jobs = builder.config.jobs.unwrap_or_else(|| { 8 * std::thread::available_parallelism().map_or(1, std::num::NonZeroUsize::get) as u32 @@ -2576,7 +2576,7 @@ Please disable assertions with `rust.debug-assertions = false`. builder.info(&format!( "Check compiletest suite={} mode={} compare_mode={} ({} -> {})", - suite, mode, compare_mode, &test_compiler.host, target + suite, mode, compare_mode, test_compiler.host, target )); let _time = helpers::timeit(builder); try_run_tests(builder, &mut cmd, false); diff --git a/src/tools/clippy/.github/workflows/clippy_mq.yml b/src/tools/clippy/.github/workflows/clippy_mq.yml index c49241bdff1bc..b612ea4611a9f 100644 --- a/src/tools/clippy/.github/workflows/clippy_mq.yml +++ b/src/tools/clippy/.github/workflows/clippy_mq.yml @@ -135,7 +135,7 @@ jobs: find $DIR ! -executable -o -type d ! -path $DIR | xargs rm -rf - name: Upload Binaries - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: binaries path: target/debug @@ -179,7 +179,7 @@ jobs: # Download - name: Download target dir - uses: actions/download-artifact@v6 + uses: actions/download-artifact@v8 with: name: binaries path: target/debug diff --git a/src/tools/clippy/.github/workflows/lintcheck.yml b/src/tools/clippy/.github/workflows/lintcheck.yml index 9ce0b7f5fc46b..981fecd17c988 100644 --- a/src/tools/clippy/.github/workflows/lintcheck.yml +++ b/src/tools/clippy/.github/workflows/lintcheck.yml @@ -44,7 +44,7 @@ jobs: - name: Cache lintcheck bin id: cache-lintcheck-bin - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: target/debug/lintcheck key: lintcheck-bin-${{ hashfiles('lintcheck/**') }} @@ -59,7 +59,7 @@ jobs: - name: Cache results JSON id: cache-json - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: lintcheck-logs/ci_crates_logs.json key: ${{ steps.key.outputs.key }} @@ -69,7 +69,7 @@ jobs: run: env CLIPPY_CONF_DIR="$PWD/lintcheck/ci-config" ./target/debug/lintcheck --format json --all-lints --crates-toml ./lintcheck/ci_crates.toml - name: Upload base JSON - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: base path: lintcheck-logs/ci_crates_logs.json @@ -87,7 +87,7 @@ jobs: - name: Cache lintcheck bin id: cache-lintcheck-bin - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: target/debug/lintcheck key: lintcheck-bin-${{ hashfiles('lintcheck/**') }} @@ -100,7 +100,7 @@ jobs: run: env CLIPPY_CONF_DIR="$PWD/lintcheck/ci-config" ./target/debug/lintcheck --format json --all-lints --crates-toml ./lintcheck/ci_crates.toml - name: Upload head JSON - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: head path: lintcheck-logs/ci_crates_logs.json @@ -119,14 +119,14 @@ jobs: persist-credentials: false - name: Restore lintcheck bin - uses: actions/cache/restore@v4 + uses: actions/cache/restore@v5 with: path: target/debug/lintcheck key: lintcheck-bin-${{ hashfiles('lintcheck/**') }} fail-on-cache-miss: true - name: Download JSON - uses: actions/download-artifact@v5 + uses: actions/download-artifact@v8 - name: Store PR number run: echo ${{ github.event.pull_request.number }} > pr.txt @@ -140,13 +140,13 @@ jobs: ./target/debug/lintcheck diff {base,head}/ci_crates_logs.json --write-summary summary.json > full_diff.md - name: Upload full diff - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: full_diff path: full_diff.md - name: Upload summary - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: summary path: | diff --git a/src/tools/clippy/.github/workflows/lintcheck_summary.yml b/src/tools/clippy/.github/workflows/lintcheck_summary.yml index 6768cd65701ac..da6a7474cdb91 100644 --- a/src/tools/clippy/.github/workflows/lintcheck_summary.yml +++ b/src/tools/clippy/.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@v5 + uses: actions/download-artifact@v8 with: name: summary path: untrusted @@ -35,7 +35,7 @@ jobs: github-token: ${{ github.token }} - name: Format comment - uses: actions/github-script@v8 + uses: actions/github-script@v9 with: script: | const fs = require("fs"); diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index 1276ab3d4bd37..24b91932567a5 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -6804,6 +6804,7 @@ Released 2018-09-13 [`inline_asm_x86_att_syntax`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_asm_x86_att_syntax [`inline_asm_x86_intel_syntax`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_asm_x86_intel_syntax [`inline_fn_without_body`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_fn_without_body +[`inline_modules`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_modules [`inspect_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#inspect_for_each [`int_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#int_plus_one [`integer_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_arithmetic @@ -6878,6 +6879,7 @@ Released 2018-09-13 [`main_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#main_recursion [`manual_abs_diff`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_abs_diff [`manual_assert`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_assert +[`manual_assert_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_assert_eq [`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn [`manual_bits`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits [`manual_c_str_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_c_str_literals @@ -7393,6 +7395,7 @@ Released 2018-09-13 [`used_underscore_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#used_underscore_items [`useless_asref`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_asref [`useless_attribute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_attribute +[`useless_borrows_in_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_borrows_in_formatting [`useless_concat`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_concat [`useless_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion [`useless_format`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_format diff --git a/src/tools/clippy/book/src/configuration.md b/src/tools/clippy/book/src/configuration.md index b270c11ab397c..45faf039dd670 100644 --- a/src/tools/clippy/book/src/configuration.md +++ b/src/tools/clippy/book/src/configuration.md @@ -79,7 +79,7 @@ This also works with lint groups. For example, you can run Clippy with warnings cargo clippy -- -W clippy::pedantic ``` -If you care only about a certain lints, you can allow all others and then explicitly warn on the lints you are +If you care only about certain lints, you can allow all others and then explicitly warn on the lints you are interested in: ```terminal diff --git a/src/tools/clippy/clippy_dev/src/new_lint.rs b/src/tools/clippy/clippy_dev/src/new_lint.rs index dc2c6d8aa520e..a5e2050a3865e 100644 --- a/src/tools/clippy/clippy_dev/src/new_lint.rs +++ b/src/tools/clippy/clippy_dev/src/new_lint.rs @@ -502,7 +502,7 @@ fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str> file_contents.replace_range(arr_start + 1..arr_end, &new_arr_content); // Just add the mod declaration at the top, it'll be fixed by rustfmt - file_contents.insert_str(0, &format!("mod {};\n", &lint.name)); + file_contents.insert_str(0, &format!("mod {};\n", lint.name)); let mut file = OpenOptions::new() .write(true) diff --git a/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs b/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs index 21cb3c5d0443c..6afc1f9ef0928 100644 --- a/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs +++ b/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs @@ -307,15 +307,15 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering { } }, ItemKind::Trait { - impl_restriction:_, - constness:_, + impl_restriction: _, + constness: _, is_auto, - safety:_, - ident:_, + safety: _, + ident: _, generics: _, bounds: _, - items: item_ref} - if self.enable_ordering_for_trait && *is_auto == IsAuto::No => { + items: item_ref, + } if self.enable_ordering_for_trait && *is_auto == IsAuto::No => { let mut cur_t: Option<(TraitItemId, Ident)> = None; for &item in *item_ref { diff --git a/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs b/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs index 8e85ce6171842..cdcb66566d951 100644 --- a/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs +++ b/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs @@ -4,12 +4,11 @@ use clippy_utils::source::walk_span_to_context; use clippy_utils::sugg::Sugg; use clippy_utils::sym; use clippy_utils::ty::{implements_trait, is_copy}; -use rustc_middle::ty::Unnormalized; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Lit}; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, Ty, Unnormalized}; use rustc_session::declare_lint_pass; use rustc_span::symbol::Ident; @@ -65,7 +64,9 @@ fn is_impl_not_trait_with_bool_out<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) - }) .is_some_and(|assoc_item| { let proj = Ty::new_projection(cx.tcx, assoc_item.def_id, cx.tcx.mk_args_trait(ty, [])); - let nty = cx.tcx.normalize_erasing_regions(cx.typing_env(), Unnormalized::new_wip(proj)); + let nty = cx + .tcx + .normalize_erasing_regions(cx.typing_env(), Unnormalized::new_wip(proj)); nty.is_bool() }) diff --git a/src/tools/clippy/clippy_lints/src/booleans.rs b/src/tools/clippy/clippy_lints/src/booleans.rs index 986e75577412a..ed963dc3e90e7 100644 --- a/src/tools/clippy/clippy_lints/src/booleans.rs +++ b/src/tools/clippy/clippy_lints/src/booleans.rs @@ -360,7 +360,7 @@ impl SuggestContext<'_, '_, '_> { if app != Applicability::MachineApplicable { return None; } - let _cannot_fail = write!(&mut self.output, "{}", &(!snip)); + let _cannot_fail = write!(&mut self.output, "{}", !snip); } }, True | False | Not(_) => { diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs b/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs index c54dab7e4aec0..69490fb23646d 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs @@ -59,7 +59,12 @@ fn is_used_as_unaligned(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { if matches!(name.ident.name, sym::read_unaligned | sym::write_unaligned) && let Some(def_id) = cx.typeck_results().type_dependent_def_id(parent.hir_id) && let Some(def_id) = cx.tcx.impl_of_assoc(def_id) - && cx.tcx.type_of(def_id).instantiate_identity().skip_norm_wip().is_raw_ptr() + && cx + .tcx + .type_of(def_id) + .instantiate_identity() + .skip_norm_wip() + .is_raw_ptr() { true } else { diff --git a/src/tools/clippy/clippy_lints/src/cloned_ref_to_slice_refs.rs b/src/tools/clippy/clippy_lints/src/cloned_ref_to_slice_refs.rs index c5eabe4c2b88d..4f663f4aa9098 100644 --- a/src/tools/clippy/clippy_lints/src/cloned_ref_to_slice_refs.rs +++ b/src/tools/clippy/clippy_lints/src/cloned_ref_to_slice_refs.rs @@ -1,15 +1,22 @@ +use std::ops::ControlFlow; + use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::sugg::Sugg; use clippy_utils::visitors::is_const_evaluatable; -use clippy_utils::{is_in_const_context, is_mutable}; +use clippy_utils::{is_in_const_context, is_mutable, sym}; +use rustc_ast::Mutability; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind}; +use rustc_hir::{Expr, ExprKind, HirId, LangItem}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_middle::ty::adjustment::{Adjust, DerefAdjustKind, OverloadedDeref}; use rustc_session::impl_lint_pass; -use rustc_span::sym; +use rustc_span::Symbol; + +use crate::methods::is_clone_like; declare_clippy_lint! { /// ### What it does @@ -73,29 +80,116 @@ impl<'tcx> LateLintPass<'tcx> for ClonedRefToSliceRefs<'_> { && let ExprKind::Array([item]) = &arr.kind // check for clones - && let ExprKind::MethodCall(_, val, _, _) = item.kind - && cx.ty_based_def(item).opt_parent(cx).is_diag_item(cx, sym::Clone) + && let ExprKind::MethodCall(path, recv, _, _) = item.kind + && let Some(adjustment) = is_needless_clone_or_equivalent(cx, recv, path.ident.name, item.hir_id) // check for immutability or purity - && (!is_mutable(cx, val) || is_const_evaluatable(cx, val)) + && (!is_mutable(cx, recv) || is_const_evaluatable(cx, recv)) // get appropriate crate for `slice::from_ref` && let Some(builtin_crate) = clippy_utils::std_or_core(cx) { - let mut sugg = Sugg::hir(cx, val, "_"); - if !cx.typeck_results().expr_ty(val).is_ref() { - sugg = sugg.addr(); - } + let mut applicability = Applicability::MachineApplicable; + let sugg = Sugg::hir_with_context(cx, recv, expr.span.ctxt(), "_", &mut applicability); span_lint_and_sugg( cx, CLONED_REF_TO_SLICE_REFS, expr.span, - format!("this call to `clone` can be replaced with `{builtin_crate}::slice::from_ref`"), + format!( + "unnecessary use of `{}` to create a slice from a reference", + path.ident.name + ), "try", - format!("{builtin_crate}::slice::from_ref({sugg})"), - Applicability::MaybeIncorrect, + format!("{builtin_crate}::slice::from_ref({adjustment}{sugg})"), + applicability, ); } } } + +/// Checks if a method call is a needless clone or equivalent. If so, returns the necessary +/// adjustments to use the method receiver directly without cloning. +/// For example, in the code below: +/// ```rust,no_run +/// use std::path::PathBuf; +/// +/// let w = &PathBuf::new(); +/// let b = &[w.to_path_buf()]; +/// ``` +/// We would replace `&[w.to_path_buf()]` with `std::slice::from_ref(&*w)`, +/// hence we return `Some("&*")` as the adjustment. +fn is_needless_clone_or_equivalent<'tcx>( + cx: &LateContext<'tcx>, + method_recv: &'tcx Expr<'tcx>, + method_name: Symbol, + hir_id: HirId, +) -> Option { + let method_def = cx.ty_based_def(hir_id).opt_parent(cx)?; + if !method_def.is_lang_item(cx, LangItem::Clone) && !is_clone_like(cx, method_name, method_def) { + return None; + } + + let method_ret_ty = cx.typeck_results().node_type(hir_id); + let method_recv_ty = cx.typeck_results().expr_ty_adjusted(method_recv); + let ty::Ref(_, method_recv_ty_inner, Mutability::Not) = method_recv_ty.kind() else { + return None; + }; + + let method_recv_adjustments = cx.typeck_results().expr_adjustments(method_recv); + + // The return type of the clone-like method should be the same as the inner type of the reference + // being cloned, except for the following special cases: + // 1. `OsString`, which is first dereferenced to `OsStr` and the borrowed as `&OsStr`. + // 2. `PathBuf`, which is first dereferenced to `Path` and then borrowed as `&Path`. + let adjust_target_ty = if method_ret_ty == *method_recv_ty_inner { + method_ret_ty + } else if let Some(after_special_case_ty_name @ (sym::OsStr | sym::Path)) = method_recv_ty_inner.opt_diag_name(cx) + // Looking for the `OSString -> OSStr` or `PathBuf -> Path` adjustment in the abovementioned special cases + && let [preceeding_derefs @ .., special_case, last_borrow] = method_recv_adjustments + && matches!( + special_case.kind, + Adjust::Deref(DerefAdjustKind::Overloaded(OverloadedDeref { + mutbl: Mutability::Not, + .. + })) + ) + && matches!(last_borrow.kind, Adjust::Borrow(_)) + && special_case.target.is_diag_item(cx, after_special_case_ty_name) + && let before_special_case_ty = preceeding_derefs + .last().map_or_else(|| cx.typeck_results().expr_ty(method_recv), |a| a.target) + && matches!( + (before_special_case_ty.opt_diag_name(cx)?, after_special_case_ty_name), + (sym::OsString, sym::OsStr) | (sym::PathBuf, sym::Path)) + { + before_special_case_ty + } else { + return None; + }; + + // Find the number of adjustments required until `method_recv_ty_source` becomes `adjust_target_ty` + let method_recv_ty_source = cx.typeck_results().expr_ty(method_recv); + let adjust_count = method_recv_adjustments + .iter() + .enumerate() + .try_fold(method_recv_ty_source, |ty, (i, a)| { + if ty == adjust_target_ty { + ControlFlow::Break(i) + } else { + ControlFlow::Continue(a.target) + } + }) + .break_value()?; + + let (needs_borrow, deref_count) = if adjust_count == 0 || !method_recv_ty_source.is_ref() { + (true, adjust_count) + } else { + (false, adjust_count - 1) + }; + + Some(if needs_borrow { + format!("&{}", "*".repeat(deref_count)) + } else { + "*".repeat(deref_count) + }) +} diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs index c164241673a31..79ed199147f1d 100644 --- a/src/tools/clippy/clippy_lints/src/declared_lints.rs +++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs @@ -176,6 +176,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::format_args::UNNECESSARY_DEBUG_FORMATTING_INFO, crate::format_args::UNNECESSARY_TRAILING_COMMA_INFO, crate::format_args::UNUSED_FORMAT_SPECS_INFO, + crate::format_args::USELESS_BORROWS_IN_FORMATTING_INFO, crate::format_impl::PRINT_IN_FORMAT_IMPL_INFO, crate::format_impl::RECURSIVE_FORMAT_IMPL_INFO, crate::format_push_string::FORMAT_PUSH_STRING_INFO, @@ -297,6 +298,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::main_recursion::MAIN_RECURSION_INFO, crate::manual_abs_diff::MANUAL_ABS_DIFF_INFO, crate::manual_assert::MANUAL_ASSERT_INFO, + crate::manual_assert_eq::MANUAL_ASSERT_EQ_INFO, crate::manual_async_fn::MANUAL_ASYNC_FN_INFO, crate::manual_bits::MANUAL_BITS_INFO, crate::manual_checked_ops::MANUAL_CHECKED_OPS_INFO, @@ -536,6 +538,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::missing_trait_methods::MISSING_TRAIT_METHODS_INFO, crate::mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION_INFO, crate::mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION_INFO, + crate::module_style::INLINE_MODULES_INFO, crate::module_style::MOD_MODULE_FILES_INFO, crate::module_style::SELF_NAMED_MODULE_FILES_INFO, crate::multi_assignments::MULTI_ASSIGNMENTS_INFO, diff --git a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs index 4324a8465be6f..f93f682bc9298 100644 --- a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs +++ b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs @@ -166,7 +166,13 @@ impl<'tcx> Visitor<'tcx> for NumericFallbackVisitor<'_, 'tcx> { ExprKind::MethodCall(_, receiver, args, _) => { if let Some(def_id) = self.cx.typeck_results().type_dependent_def_id(expr.hir_id) { - let fn_sig = self.cx.tcx.fn_sig(def_id).instantiate_identity().skip_norm_wip().skip_binder(); + let fn_sig = self + .cx + .tcx + .fn_sig(def_id) + .instantiate_identity() + .skip_norm_wip() + .skip_binder(); for (expr, bound) in iter::zip(iter::once(*receiver).chain(args.iter()), fn_sig.inputs()) { self.ty_bounds.push((*bound).into()); self.visit_expr(expr); diff --git a/src/tools/clippy/clippy_lints/src/default_union_representation.rs b/src/tools/clippy/clippy_lints/src/default_union_representation.rs index 49d550f1cbd4b..76242d7756881 100644 --- a/src/tools/clippy/clippy_lints/src/default_union_representation.rs +++ b/src/tools/clippy/clippy_lints/src/default_union_representation.rs @@ -80,7 +80,12 @@ impl<'tcx> LateLintPass<'tcx> for DefaultUnionRepresentation { /// of that field does not matter either.) fn is_union_with_two_non_zst_fields<'tcx>(cx: &LateContext<'tcx>, item: &Item<'tcx>) -> bool { if let ItemKind::Union(..) = &item.kind - && let ty::Adt(adt_def, args) = cx.tcx.type_of(item.owner_id).instantiate_identity().skip_norm_wip().kind() + && let ty::Adt(adt_def, args) = cx + .tcx + .type_of(item.owner_id) + .instantiate_identity() + .skip_norm_wip() + .kind() { adt_def.all_fields().filter(|f| !is_zst(cx, f, args)).count() >= 2 } else { diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs index c1ebb675c58ee..7a5150da6593a 100644 --- a/src/tools/clippy/clippy_lints/src/dereference.rs +++ b/src/tools/clippy/clippy_lints/src/dereference.rs @@ -6,7 +6,6 @@ use clippy_utils::ty::{adjust_derefs_manually_drop, implements_trait, is_manuall use clippy_utils::{ DefinedTy, ExprUseNode, get_expr_use_site, get_parent_expr, is_block_like, is_from_proc_macro, is_lint_allowed, sym, }; -use rustc_middle::ty::Unnormalized; use rustc_ast::util::parser::ExprPrecedence; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::Applicability; @@ -18,7 +17,7 @@ use rustc_hir::{ }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, TypeckResults}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, TypeckResults, Unnormalized}; use rustc_session::impl_lint_pass; use rustc_span::{Span, Symbol, SyntaxContext}; use std::borrow::Cow; @@ -381,16 +380,21 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { && let ty::Ref(_, sub_ty, _) = *arg_ty.kind() && let args = typeck.node_args_opt(hir_id).map(|args| &args[1..]).unwrap_or_default() - && let impl_ty = - if cx.tcx.fn_sig(fn_id).instantiate_identity().skip_norm_wip().skip_binder().inputs()[0] - .is_ref() - { - // Trait methods taking `&self` - sub_ty - } else { - // Trait methods taking `self` - arg_ty - } + && let impl_ty = if cx + .tcx + .fn_sig(fn_id) + .instantiate_identity() + .skip_norm_wip() + .skip_binder() + .inputs()[0] + .is_ref() + { + // Trait methods taking `&self` + sub_ty + } else { + // Trait methods taking `self` + arg_ty + } && impl_ty.is_ref() && implements_trait( cx, @@ -877,7 +881,9 @@ impl TyCoercionStability { if let Some(def_id) = def_site_def_id { let typing_env = ty::TypingEnv::non_body_analysis(tcx, def_id); - ty = tcx.try_normalize_erasing_regions(typing_env, Unnormalized::new_wip(ty)).unwrap_or(ty); + ty = tcx + .try_normalize_erasing_regions(typing_env, Unnormalized::new_wip(ty)) + .unwrap_or(ty); } loop { break match *ty.kind() { diff --git a/src/tools/clippy/clippy_lints/src/derivable_impls.rs b/src/tools/clippy/clippy_lints/src/derivable_impls.rs index 5e1144254fdb2..811c360e533dd 100644 --- a/src/tools/clippy/clippy_lints/src/derivable_impls.rs +++ b/src/tools/clippy/clippy_lints/src/derivable_impls.rs @@ -243,7 +243,12 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls { && let Node::ImplItem(impl_item) = cx.tcx.hir_node(impl_item_hir) && let ImplItemKind::Fn(_, b) = &impl_item.kind && let Body { value: func_expr, .. } = cx.tcx.hir_body(*b) - && let &ty::Adt(adt_def, args) = cx.tcx.type_of(item.owner_id).instantiate_identity().skip_norm_wip().kind() + && let &ty::Adt(adt_def, args) = cx + .tcx + .type_of(item.owner_id) + .instantiate_identity() + .skip_norm_wip() + .kind() && let attrs = cx.tcx.hir_attrs(item.hir_id()) && !attrs.iter().any(|attr| attr.doc_str().is_some()) && cx.tcx.hir_attrs(impl_item_hir).is_empty() diff --git a/src/tools/clippy/clippy_lints/src/enum_clike.rs b/src/tools/clippy/clippy_lints/src/enum_clike.rs index 2a8b6778fd0ac..bd92e7e850870 100644 --- a/src/tools/clippy/clippy_lints/src/enum_clike.rs +++ b/src/tools/clippy/clippy_lints/src/enum_clike.rs @@ -42,7 +42,11 @@ impl<'tcx> LateLintPass<'tcx> for UnportableVariant { for var in def.variants { 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().skip_norm_wip(); + let mut ty = cx + .tcx + .type_of(def_id.to_def_id()) + .instantiate_identity() + .skip_norm_wip(); 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() diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs index 229af104799d1..6248ba6e44da5 100644 --- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs +++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs @@ -372,7 +372,7 @@ fn has_late_bound_to_non_late_bound_regions(from_sig: FnSig<'_>, to_sig: FnSig<' } } - assert!(from_sig.inputs_and_output.len() == to_sig.inputs_and_output.len()); + assert_eq!(from_sig.inputs_and_output.len(), to_sig.inputs_and_output.len()); from_sig .inputs_and_output .iter() diff --git a/src/tools/clippy/clippy_lints/src/format_args.rs b/src/tools/clippy/clippy_lints/src/format_args.rs index fd2b9826bb20f..ae92284f81f81 100644 --- a/src/tools/clippy/clippy_lints/src/format_args.rs +++ b/src/tools/clippy/clippy_lints/src/format_args.rs @@ -2,7 +2,7 @@ use std::collections::hash_map::Entry; use arrayvec::ArrayVec; use clippy_config::Conf; -use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::macros::{ FormatArgsStorage, FormatParamUsage, MacroCall, find_format_arg_expr, format_arg_removal_span, format_placeholder_format_span, is_assert_macro, is_format_macro, is_panic, matching_root_macro_call, @@ -10,14 +10,14 @@ use clippy_utils::macros::{ }; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::MaybeDef; -use clippy_utils::source::{SpanRangeExt, snippet}; +use clippy_utils::source::{SpanRangeExt, snippet, snippet_opt}; use clippy_utils::ty::implements_trait; -use clippy_utils::{is_from_proc_macro, is_in_test, sym, trait_ref_of_method}; +use clippy_utils::{is_from_proc_macro, is_in_test, peel_hir_expr_while, sym, trait_ref_of_method}; use itertools::Itertools; -use rustc_middle::ty::Unnormalized; +use rustc_ast::FormatTrait::{Binary, Debug, Display, LowerExp, LowerHex, Octal, Pointer, UpperExp, UpperHex}; use rustc_ast::{ - FormatArgPosition, FormatArgPositionKind, FormatArgsPiece, FormatArgumentKind, FormatCount, FormatOptions, - FormatPlaceholder, FormatTrait, + BorrowKind, FormatArgPosition, FormatArgPositionKind, FormatArgsPiece, FormatArgumentKind, FormatCount, + FormatOptions, FormatPlaceholder, }; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; @@ -25,7 +25,7 @@ use rustc_errors::SuggestionStyle::{CompletelyHidden, ShowCode}; use rustc_hir::{Expr, ExprKind, LangItem, RustcVersion, find_attr}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, DerefAdjustKind}; -use rustc_middle::ty::{self, GenericArg, List, TraitRef, Ty, TyCtxt, Upcast}; +use rustc_middle::ty::{self, GenericArg, List, TraitRef, Ty, TyCtxt, Unnormalized, Upcast}; use rustc_session::impl_lint_pass; use rustc_span::edition::Edition::Edition2021; use rustc_span::{BytePos, Pos, Span, Symbol}; @@ -172,6 +172,11 @@ declare_clippy_lint! { /// ### What it does /// Checks for `Debug` formatting (`{:?}`) applied to an `OsStr` or `Path`. /// + /// This includes: + /// - Format specifiers on `format_args!()` (width, precision have no effect) + /// - Format width too small for the format trait (e.g. `{:#02x}` outputs "0x1" + /// so width 2 has no effect; minimum is 4 for alternate hex/octal/binary) + /// /// ### Why is this bad? /// Rust doesn't guarantee what `Debug` formatting looks like, and it could /// change in the future. `OsStr`s and `Path`s can be `Display` formatted @@ -232,6 +237,11 @@ declare_clippy_lint! { /// Detects [formatting parameters] that have no effect on the output of /// `format!()`, `println!()` or similar macros. /// + /// This includes: + /// - Format specifiers on `format_args!()` (width, precision have no effect) + /// - Format width too small for the format trait (e.g. `{:#02x}` outputs "0x1" + /// so width 2 has no effect; minimum is 4 for alternate hex/octal/binary) + /// /// ### Why is this bad? /// Shorter format specifiers are easier to read, it may also indicate that /// an expected formatting operation such as adding padding isn't happening. @@ -241,6 +251,9 @@ declare_clippy_lint! { /// println!("{:.}", 1.0); /// /// println!("not padded: {:5}", format_args!("...")); + /// + /// // width 2 has no effect for alternate hex (outputs "0x1") + /// format!("{:#02x}", 1_u8); /// ``` /// Use instead: /// ```no_run @@ -249,6 +262,8 @@ declare_clippy_lint! { /// println!("not padded: {}", format_args!("...")); /// // OR /// println!("padded: {:5}", format!("...")); + /// + /// format!("{:#04x}", 1_u8); // width 4 for two-digit zero-padded hex /// ``` /// /// [formatting parameters]: https://doc.rust-lang.org/std/fmt/index.html#formatting-parameters @@ -258,6 +273,34 @@ declare_clippy_lint! { "use of a format specifier that has no effect" } +declare_clippy_lint! { + /// ### What it does + /// Detects format!-style macros (e.g. `format!`, `println!`, `write!`) where an argument + /// is passed with an explicit `&` but the value is already a reference, resulting in a + /// double reference (e.g. `&&T`). + /// + /// ### Why is this bad? + /// The extra `&` is redundant and can make the code less clear. Format macros take + /// references to the arguments internally, so passing `&x` when `x` is already a + /// reference produces a double reference. The compiler is currently unable to + /// optimize double references, which results in about 6% degradation per call. + /// + /// ### Example + /// ```no_run + /// let s: &str = "hello"; + /// println!("{}", &s); + /// ``` + /// Use instead: + /// ```no_run + /// let s: &str = "hello"; + /// println!("{}", s); + /// ``` + #[clippy::version = "1.97.0"] + pub USELESS_BORROWS_IN_FORMATTING, + perf, + "redundant reference in format args causes double reference" +} + impl_lint_pass!(FormatArgs<'_> => [ FORMAT_IN_FORMAT_ARGS, POINTER_FORMAT, @@ -266,6 +309,7 @@ impl_lint_pass!(FormatArgs<'_> => [ UNNECESSARY_DEBUG_FORMATTING, UNNECESSARY_TRAILING_COMMA, UNUSED_FORMAT_SPECS, + USELESS_BORROWS_IN_FORMATTING, ]); #[expect(clippy::struct_field_names)] @@ -364,8 +408,21 @@ impl<'tcx> FormatArgsExpr<'_, 'tcx> { && let Some(arg_expr) = find_format_arg_expr(self.expr, arg) { self.check_unused_format_specifier(placeholder, arg_expr); + self.check_useless_format_width(placeholder); + self.check_useless_borrows_in_formatting(placeholder, arg_expr); + + // Check width and precision arguments the same way as the value + for opt in [&placeholder.format_options.width, &placeholder.format_options.precision] { + if let Some(FormatCount::Argument(position)) = opt.as_ref() + && let Ok(pos_index) = position.index + && let Some(pos_arg) = self.format_args.arguments.all_args().get(pos_index) + && let Some(pos_arg_expr) = find_format_arg_expr(self.expr, pos_arg) + { + self.check_useless_borrows_in_formatting(placeholder, pos_arg_expr); + } + } - if placeholder.format_trait == FormatTrait::Display + if placeholder.format_trait == Display && placeholder.format_options == FormatOptions::default() && !self.is_aliased(index) { @@ -374,7 +431,7 @@ impl<'tcx> FormatArgsExpr<'_, 'tcx> { self.check_to_string_in_format_args(name, arg_expr); } - if placeholder.format_trait == FormatTrait::Debug { + if placeholder.format_trait == Debug { let name = self.cx.tcx.item_name(self.macro_call.def_id); self.check_unnecessary_debug_formatting(name, arg_expr); if let Some(span) = placeholder.span @@ -384,7 +441,7 @@ impl<'tcx> FormatArgsExpr<'_, 'tcx> { } } - if placeholder.format_trait == FormatTrait::Pointer + if placeholder.format_trait == Pointer && let Some(span) = placeholder.span { span_lint(self.cx, POINTER_FORMAT, span, "pointer formatting detected"); @@ -393,6 +450,43 @@ impl<'tcx> FormatArgsExpr<'_, 'tcx> { } } + fn check_useless_borrows_in_formatting(&self, placeholder: &FormatPlaceholder, arg_expr: &Expr<'tcx>) { + if !arg_expr.span.from_expansion() + && !is_from_proc_macro(self.cx, arg_expr) + && let Some(fmt_trait) = match placeholder.format_trait { + Display => self.cx.tcx.get_diagnostic_item(sym::Display), + Debug => self.cx.tcx.get_diagnostic_item(sym::Debug), + _ => None, + } + && let Some(sized_trait) = self.cx.tcx.lang_items().sized_trait() + && let peeled_expr = peel_hir_expr_while(arg_expr, |e| { + // Need to handle `&&&T` to `&T` when a single ref is still required + if let ExprKind::AddrOf(BorrowKind::Ref, _, e) = e.kind + && let ty = self.cx.typeck_results().expr_ty(e) + && implements_trait(self.cx, ty, sized_trait, &[]) + && implements_trait(self.cx, ty, fmt_trait, &[]) + { + Some(e) + } else { + None + } + }) + && !std::ptr::eq(arg_expr, peeled_expr) + && let Some(peeled_snippet) = snippet_opt(self.cx, peeled_expr.span) + { + let name = self.cx.tcx.item_name(self.macro_call.def_id); + span_lint_and_sugg( + self.cx, + USELESS_BORROWS_IN_FORMATTING, + arg_expr.span, + format!("redundant reference in `{name}!` argument"), + "remove the redundant `&`", + peeled_snippet, + Applicability::MachineApplicable, + ); + } + } + fn check_unused_format_specifier(&self, placeholder: &FormatPlaceholder, arg: &Expr<'_>) { let options = &placeholder.format_options; @@ -443,6 +537,30 @@ impl<'tcx> FormatArgsExpr<'_, 'tcx> { } } + /// Lint when format width has no effect on the output because the format trait's + /// minimum output is larger (e.g. `{:#02X}` outputs "0x1" so width 2 has no effect). + fn check_useless_format_width(&self, placeholder: &FormatPlaceholder) { + let min_width = match placeholder.format_trait { + // 0x prefix, e.g. 0x1, 0o1, 0b1 + LowerHex | UpperHex | Octal | Binary if placeholder.format_options.alternate => 4, + LowerExp | UpperExp | Pointer => 4, // e.g. 1e0 with exponent, 0x1 for pointer + _ => return, + }; + if let Some(FormatCount::Literal(width_value)) = placeholder.format_options.width + && width_value < min_width + && let Some(placeholder_span) = placeholder.span + { + span_lint_and_help( + self.cx, + UNUSED_FORMAT_SPECS, + placeholder_span, + "format width has no effect on the output for this format trait", + None, + format!("consider removing the width or increasing it to at least {min_width}"), + ); + } + } + fn check_uninlined_args(&self) { if self.format_args.span.from_expansion() { return; @@ -726,10 +844,7 @@ impl<'tcx> FormatArgsExpr<'_, 'tcx> { let pointer_debug = derived_debug && adt.all_fields().any(|f| { self.has_pointer_debug( - tcx.normalize_erasing_regions( - typing_env, - Unnormalized::new_wip(f.ty(tcx, args)) - ), + tcx.normalize_erasing_regions(typing_env, Unnormalized::new_wip(f.ty(tcx, args))), depth, ) }); diff --git a/src/tools/clippy/clippy_lints/src/from_over_into.rs b/src/tools/clippy/clippy_lints/src/from_over_into.rs index 944c4eee90257..433d591418798 100644 --- a/src/tools/clippy/clippy_lints/src/from_over_into.rs +++ b/src/tools/clippy/clippy_lints/src/from_over_into.rs @@ -10,11 +10,11 @@ use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_path}; use rustc_hir::{ FnRetTy, GenericArg, GenericArgs, HirId, Impl, ImplItemId, ImplItemKind, Item, ItemKind, PatKind, Path, - PathSegment, Ty, TyKind, + PathSegment, Ty as HirTy, TyKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter::OnlyBodies; -use rustc_middle::ty; +use rustc_middle::ty::{self, Ty}; use rustc_session::impl_lint_pass; use rustc_span::symbol::{kw, sym}; use rustc_span::{Span, Symbol}; @@ -80,6 +80,8 @@ impl<'tcx> LateLintPass<'tcx> for FromOverInto { && cx.tcx.is_diagnostic_item(sym::Into, middle_trait_ref.def_id) && !matches!(middle_trait_ref.args.type_at(1).kind(), ty::Alias(ty::AliasTy { kind: ty::Opaque{..} , .. })) && self.msrv.meets(cx, msrvs::RE_REBALANCING_COHERENCE) + // skip if there's a blanket From impl, the suggested impl would conflict + && !has_blanket_from_impl(cx, middle_trait_ref.self_ty()) { span_lint_and_then( cx, @@ -163,11 +165,21 @@ impl<'tcx> Visitor<'tcx> for SelfFinder<'_, 'tcx> { } } +fn has_blanket_from_impl<'tcx>(cx: &LateContext<'tcx>, self_ty: Ty<'tcx>) -> bool { + let Some(from_def_id) = cx.tcx.get_diagnostic_item(sym::From) else { + return false; + }; + cx.tcx.non_blanket_impls_for_ty(from_def_id, self_ty).any(|impl_id| { + let impl_trait_ref = cx.tcx.impl_trait_ref(impl_id).instantiate_identity().skip_norm_wip(); + matches!(impl_trait_ref.args.type_at(1).kind(), ty::Param(_)) + }) +} + fn convert_to_from( cx: &LateContext<'_>, into_trait_seg: &PathSegment<'_>, - target_ty: &Ty<'_>, - self_ty: &Ty<'_>, + target_ty: &HirTy<'_>, + self_ty: &HirTy<'_>, impl_item_ref: ImplItemId, ) -> Option> { if !target_ty.find_self_aliases().is_empty() { diff --git a/src/tools/clippy/clippy_lints/src/functions/ref_option.rs b/src/tools/clippy/clippy_lints/src/functions/ref_option.rs index c5c0cc0b5ab61..1727f50b521a6 100644 --- a/src/tools/clippy/clippy_lints/src/functions/ref_option.rs +++ b/src/tools/clippy/clippy_lints/src/functions/ref_option.rs @@ -107,7 +107,12 @@ pub(crate) fn check_fn<'a>( 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_norm_wip().skip_binder(); + let sig = cx + .tcx + .fn_sig(def_id) + .instantiate_identity() + .skip_norm_wip() + .skip_binder(); if is_from_proc_macro(cx, &(&kind, body, hir_id, span)) { return; @@ -128,7 +133,12 @@ pub(super) fn check_trait_item<'a>( && !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_norm_wip().skip_binder(); + let ty_sig = cx + .tcx + .fn_sig(def_id) + .instantiate_identity() + .skip_norm_wip() + .skip_binder(); check_fn_sig(cx, sig.decl, sig.span, ty_sig); } } diff --git a/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs b/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs index e25611d48817a..2d330835a037a 100644 --- a/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs +++ b/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs @@ -57,7 +57,7 @@ impl RenamedFnArgs { { let mut renamed: Vec<(Span, String)> = vec![]; - debug_assert!(default_idents.size_hint() == current_idents.size_hint()); + debug_assert_eq!(default_idents.size_hint(), current_idents.size_hint()); for (default_ident, current_ident) in iter::zip(default_idents, current_idents) { let has_name_to_check = |ident: Option| { ident diff --git a/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs index f2e85a717dca4..45adcfbb030f5 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs @@ -360,7 +360,12 @@ fn check_with_condition<'tcx>( if name.ident.name == sym::MIN && let Some(const_id) = cx.typeck_results().type_dependent_def_id(cond_num_val.hir_id) && let Some(impl_id) = cx.tcx.inherent_impl_of_assoc(const_id) - && cx.tcx.type_of(impl_id).instantiate_identity().skip_norm_wip().is_integral() + && cx + .tcx + .type_of(impl_id) + .instantiate_identity() + .skip_norm_wip() + .is_integral() { print_lint_and_sugg(cx, var_name, expr); } @@ -370,7 +375,12 @@ fn check_with_condition<'tcx>( && name.ident.name == sym::min_value && let Some(func_id) = cx.typeck_results().type_dependent_def_id(func.hir_id) && let Some(impl_id) = cx.tcx.inherent_impl_of_assoc(func_id) - && cx.tcx.type_of(impl_id).instantiate_identity().skip_norm_wip().is_integral() + && cx + .tcx + .type_of(impl_id) + .instantiate_identity() + .skip_norm_wip() + .is_integral() { print_lint_and_sugg(cx, var_name, expr); } diff --git a/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs b/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs index 5ded0efacb815..9569b50a32070 100644 --- a/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs +++ b/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs @@ -528,7 +528,7 @@ impl LateLintPass<'_> for ItemNameRepetitions { | ItemKind::Fn { ident, .. } | ItemKind::Macro(ident, ..) | ItemKind::Static(_, ident, ..) - | ItemKind::Trait { ident, ..} + | ItemKind::Trait { ident, .. } | ItemKind::TraitAlias(_, ident, ..) | ItemKind::TyAlias(ident, ..) | ItemKind::Union(ident, ..) diff --git a/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs b/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs index 11de287c0ea57..06ad45316811d 100644 --- a/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs +++ b/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::implements_trait; -use rustc_middle::ty::Unnormalized; use rustc_hir::def_id::LocalDefId; use rustc_hir::{FnSig, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::Unnormalized; use rustc_session::declare_lint_pass; use rustc_span::{Symbol, sym}; @@ -66,13 +66,12 @@ impl<'tcx> LateLintPass<'tcx> for IterNotReturningIterator { fn check_sig(cx: &LateContext<'_>, name: Symbol, sig: &FnSig<'_>, fn_id: LocalDefId) { if sig.decl.implicit_self().has_implicit_self() { + let ret_ty = cx.tcx.instantiate_bound_regions_with_erased( + cx.tcx.fn_sig(fn_id).instantiate_identity().skip_norm_wip().output(), + ); let ret_ty = cx .tcx - .instantiate_bound_regions_with_erased(cx.tcx - .fn_sig(fn_id).instantiate_identity().skip_norm_wip().output() - ); - let ret_ty = cx - .tcx.try_normalize_erasing_regions(cx.typing_env(), Unnormalized::new_wip(ret_ty)) + .try_normalize_erasing_regions(cx.typing_env(), Unnormalized::new_wip(ret_ty)) .unwrap_or(ret_ty); if cx .tcx diff --git a/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs b/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs index 3746c9384957d..fc0a725e06335 100644 --- a/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs +++ b/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs @@ -134,7 +134,12 @@ impl LateLintPass<'_> for IterWithoutIntoIter { .trait_def_id() .is_some_and(|did| cx.tcx.is_diagnostic_item(sym::IntoIterator, did)) && !item.span.in_external_macro(cx.sess().source_map()) - && let &ty::Ref(_, ty, mtbl) = cx.tcx.type_of(item.owner_id).instantiate_identity().skip_norm_wip().kind() + && let &ty::Ref(_, ty, mtbl) = cx + .tcx + .type_of(item.owner_id) + .instantiate_identity() + .skip_norm_wip() + .kind() && let expected_method_name = match mtbl { Mutability::Mut => sym::iter_mut, Mutability::Not => sym::iter, diff --git a/src/tools/clippy/clippy_lints/src/large_const_arrays.rs b/src/tools/clippy/clippy_lints/src/large_const_arrays.rs index 11b5a339c18b4..80b49ecc7a40b 100644 --- a/src/tools/clippy/clippy_lints/src/large_const_arrays.rs +++ b/src/tools/clippy/clippy_lints/src/large_const_arrays.rs @@ -1,10 +1,10 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; -use rustc_middle::ty::Unnormalized; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; +use rustc_middle::ty::Unnormalized; use rustc_middle::ty::layout::LayoutOf; use rustc_session::impl_lint_pass; use rustc_span::{BytePos, Pos, Span}; diff --git a/src/tools/clippy/clippy_lints/src/len_without_is_empty.rs b/src/tools/clippy/clippy_lints/src/len_without_is_empty.rs index 9bf06dbf452d6..8ae91bbcd2a91 100644 --- a/src/tools/clippy/clippy_lints/src/len_without_is_empty.rs +++ b/src/tools/clippy/clippy_lints/src/len_without_is_empty.rs @@ -44,7 +44,11 @@ declare_lint_pass!(LenWithoutIsEmpty => [LEN_WITHOUT_IS_EMPTY]); impl<'tcx> LateLintPass<'tcx> for LenWithoutIsEmpty { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - if let ItemKind::Trait { ident, items: trait_items, .. } = item.kind + if let ItemKind::Trait { + ident, + items: trait_items, + .. + } = item.kind && !item.span.from_expansion() { check_trait_items(cx, item, ident, trait_items); @@ -64,7 +68,14 @@ impl<'tcx> LateLintPass<'tcx> for LenWithoutIsEmpty { && let Some(ty_id) = cx.qpath_res(ty_path, imp.self_ty.hir_id).opt_def_id() && let Some(local_id) = ty_id.as_local() && let ty_hir_id = cx.tcx.local_def_id_to_hir_id(local_id) - && let Some(output) = LenOutput::new(cx, cx.tcx.fn_sig(item.owner_id).instantiate_identity().skip_norm_wip().skip_binder()) + && let Some(output) = LenOutput::new( + cx, + cx.tcx + .fn_sig(item.owner_id) + .instantiate_identity() + .skip_norm_wip() + .skip_binder(), + ) { let (name, kind) = match cx.tcx.hir_node(ty_hir_id) { Node::ForeignItem(x) => (x.ident.name, "extern type"), @@ -313,7 +324,11 @@ fn check_for_is_empty( if !(is_empty.is_method() && check_is_empty_sig( cx, - cx.tcx.fn_sig(is_empty.def_id).instantiate_identity().skip_norm_wip().skip_binder(), + cx.tcx + .fn_sig(is_empty.def_id) + .instantiate_identity() + .skip_norm_wip() + .skip_binder(), len_self_kind, len_output, )) => diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index 72ee5cca03970..0875982f3bbf1 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -196,6 +196,7 @@ mod macro_use; mod main_recursion; mod manual_abs_diff; mod manual_assert; +mod manual_assert_eq; mod manual_async_fn; mod manual_bits; mod manual_checked_ops; @@ -867,6 +868,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co Box::new(move |tcx| Box::new(manual_pop_if::ManualPopIf::new(tcx, conf))), Box::new(move |_| Box::new(manual_noop_waker::ManualNoopWaker::new(conf))), Box::new(|_| Box::new(byte_char_slices::ByteCharSlice)), + Box::new(|_| Box::new(manual_assert_eq::ManualAssertEq)), // add late passes here, used by `cargo dev new_lint` ]; store.late_passes.extend(late_lints); diff --git a/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs b/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs index 6ee93fa759a98..2cb98fdfe4484 100644 --- a/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs @@ -139,7 +139,9 @@ fn is_ref_iterable<'tcx>( } let res_ty = cx.tcx.erase_and_anonymize_regions( - EarlyBinder::bind(req_res_ty).instantiate(cx.tcx, typeck.node_args(call_expr.hir_id)).skip_norm_wip(), + EarlyBinder::bind(req_res_ty) + .instantiate(cx.tcx, typeck.node_args(call_expr.hir_id)) + .skip_norm_wip(), ); let mutbl = if let ty::Ref(_, _, mutbl) = *req_self_ty.kind() { Some(mutbl) diff --git a/src/tools/clippy/clippy_lints/src/loops/for_kv_map.rs b/src/tools/clippy/clippy_lints/src/loops/for_kv_map.rs index 7fb8e51377a20..d28029790732b 100644 --- a/src/tools/clippy/clippy_lints/src/loops/for_kv_map.rs +++ b/src/tools/clippy/clippy_lints/src/loops/for_kv_map.rs @@ -23,7 +23,20 @@ pub(super) fn check<'tcx>( && pat.len() == 2 { let arg_span = arg.span; - let (new_pat_span, kind, ty, mutbl) = match *cx.typeck_results().expr_ty(arg).kind() { + let (arg, arg_ty) = match arg.kind { + // `for x in &expr` or `for x in &mut expr` + ExprKind::AddrOf(BorrowKind::Ref, _, expr) => (expr, cx.typeck_results().expr_ty(arg)), + // `for x in receiver.iter()` or `for x in receiver.iter_mut()` + ExprKind::MethodCall(path, receiver, [], ..) + if path.ident.name == sym::iter || path.ident.name == sym::iter_mut => + { + // Use `expr_ty_adjusted` because `.iter()` / `.iter_mut()` may introduce auto deferences + (receiver, cx.typeck_results().expr_ty_adjusted(receiver)) + }, + _ => (arg, cx.typeck_results().expr_ty(arg)), + }; + + let (new_pat_span, kind, ty, mutbl) = match *arg_ty.kind() { ty::Ref(_, ty, mutbl) => match (&pat[0].kind, &pat[1].kind) { (key, _) if pat_is_wild(cx, key, body) => (pat[1].span, "value", ty, mutbl), (_, value) if pat_is_wild(cx, value, body) => (pat[0].span, "key", ty, Mutability::Not), @@ -35,10 +48,6 @@ pub(super) fn check<'tcx>( Mutability::Not => "", Mutability::Mut => "_mut", }; - let arg = match arg.kind { - ExprKind::AddrOf(BorrowKind::Ref, _, expr) => expr, - _ => arg, - }; if matches!(ty.opt_diag_name(cx), Some(sym::HashMap | sym::BTreeMap)) && let Some(arg_span) = walk_span_to_context(arg_span, span.ctxt()) diff --git a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs index 307b8fcec1506..3979e668c2cc6 100644 --- a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs @@ -397,7 +397,13 @@ impl<'tcx> Visitor<'tcx> for VarVisitor<'_, 'tcx> { ExprKind::MethodCall(_, receiver, args, _) => { let def_id = self.cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap(); for (ty, expr) in iter::zip( - self.cx.tcx.fn_sig(def_id).instantiate_identity().skip_norm_wip().inputs().skip_binder(), + self.cx + .tcx + .fn_sig(def_id) + .instantiate_identity() + .skip_norm_wip() + .inputs() + .skip_binder(), iter::once(receiver).chain(args.iter()), ) { self.prefer_mutable = false; diff --git a/src/tools/clippy/clippy_lints/src/manual_assert_eq.rs b/src/tools/clippy/clippy_lints/src/manual_assert_eq.rs new file mode 100644 index 0000000000000..d1770d2e95a66 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/manual_assert_eq.rs @@ -0,0 +1,122 @@ +use clippy_utils::consts::ConstEvalCtxt; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::macros::{PanicCall, find_assert_args, root_macro_call_first_node}; +use clippy_utils::source::walk_span_to_context; +use clippy_utils::ty::implements_trait; +use clippy_utils::{is_in_const_context, sym}; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_session::declare_lint_pass; + +declare_clippy_lint! { + /// ### What it does + /// Checks for `assert!` and `debug_assert!` that consist of only an (in)equality check + /// + /// ### Why is this bad? + /// `assert_{eq,ne}!` and `debug_assert_{eq,ne}!` achieves the same goal, and provides some + /// additional debug information + /// + /// ### Example + /// ```no_run + /// assert!(2 * 2 == 4); + /// assert!(2 * 2 != 5); + /// debug_assert!(2 * 2 == 4); + /// debug_assert!(2 * 2 != 5); + /// ``` + /// Use instead: + /// ```no_run + /// assert_eq!(2 * 2, 4); + /// assert_ne!(2 * 2, 5); + /// debug_assert_eq!(2 * 2, 4); + /// debug_assert_ne!(2 * 2, 5); + /// ``` + #[clippy::version = "1.97.0"] + pub MANUAL_ASSERT_EQ, + pedantic, + "checks for assertions consisting of an (in)equality check" +} + +declare_lint_pass!(ManualAssertEq => [MANUAL_ASSERT_EQ]); + +#[derive(Clone, Copy, PartialEq, Eq)] +enum EqKind { + Eq, + Ne, +} + +impl EqKind { + fn postfix(self) -> &'static str { + match self { + Self::Eq => "_eq", + Self::Ne => "_ne", + } + } +} + +impl LateLintPass<'_> for ManualAssertEq { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + if let Some(macro_call) = root_macro_call_first_node(cx, expr) + && let macro_name = match cx.tcx.get_diagnostic_name(macro_call.def_id) { + Some(sym::assert_macro) => "assert", + Some(sym::debug_assert_macro) => "debug_assert", + _ => return, + } + && !is_in_const_context(cx) + && let Some((cond, panic_expn)) = find_assert_args(cx, expr, macro_call.expn) + // Don't lint if the user has a painstakingly written assertion message + && !matches!(panic_expn, PanicCall::Display(_) | PanicCall::Format(_)) + && let ExprKind::Binary(op, lhs, rhs) = cond.kind + && let eq_kind = match op.node { + BinOpKind::Eq => EqKind::Eq, + BinOpKind::Ne => EqKind::Ne, + _ => return, + } + && !cond.span.from_expansion() + && let Some(debug_trait) = cx.tcx.get_diagnostic_item(sym::Debug) + && let lhs_ty = cx.typeck_results().expr_ty(lhs) + && let rhs_ty = cx.typeck_results().expr_ty(rhs) + // Can't print the values unless the types implement `Debug` + && implements_trait(cx, lhs_ty, debug_trait, &[]) + && implements_trait(cx, rhs_ty, debug_trait, &[]) + // Printing raw pointers isn't very useful + && !lhs_ty.is_raw_ptr() + && !rhs_ty.is_raw_ptr() + // The output of `(debug_)assert_eq` isn't very useful when one of the sides is a constant value + && if eq_kind == EqKind::Ne { + let ecx = ConstEvalCtxt::new(cx); + ecx.eval(lhs).is_none() && ecx.eval(rhs).is_none() + } else { + true + } + { + span_lint_and_then( + cx, + MANUAL_ASSERT_EQ, + macro_call.span, + format!("used `{macro_name}!` with an equality comparison"), + |diag| { + let postfix = eq_kind.postfix(); + let new_name = format_args!("{macro_name}{postfix}"); + let msg = format!("replace it with `{new_name}!(..)`"); + + let ctxt = cond.span.ctxt(); + if let Some(lhs_span) = walk_span_to_context(lhs.span, ctxt) + && let Some(rhs_span) = walk_span_to_context(rhs.span, ctxt) + { + let macro_name_span = cx.sess().source_map().span_until_char(macro_call.span, '!'); + let eq_span = cond.span.with_lo(lhs_span.hi()).with_hi(rhs_span.lo()); + let suggestions = vec![ + (macro_name_span.shrink_to_hi(), postfix.to_string()), + (eq_span, ", ".to_string()), + ]; + + diag.multipart_suggestion(msg, suggestions, Applicability::MachineApplicable); + } else { + diag.span_help(expr.span, msg); + } + }, + ); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs b/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs index cb784d1ff6600..d86b05e5c8824 100644 --- a/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs @@ -23,8 +23,20 @@ use super::{COLLAPSIBLE_MATCH, pat_contains_disallowed_or}; pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>], msrv: Msrv) { if let Some(els_arm) = arms.iter().rfind(|arm| arm_is_wild_like(cx, arm)) { - for arm in arms { - check_arm(cx, true, arm.pat, expr, arm.body, arm.guard, Some(els_arm.body), msrv); + let last_non_wildcard = arms.iter().rposition(|arm| !arm_is_wild_like(cx, arm)); + for (idx, arm) in arms.iter().enumerate() { + let only_wildcards_after = last_non_wildcard.is_none_or(|lnw| idx >= lnw); + check_arm( + cx, + true, + arm.pat, + expr, + arm.body, + arm.guard, + Some(els_arm.body), + msrv, + only_wildcards_after, + ); } } } @@ -37,7 +49,7 @@ pub(super) fn check_if_let<'tcx>( let_expr: &'tcx Expr<'_>, msrv: Msrv, ) { - check_arm(cx, false, pat, let_expr, body, None, else_expr, msrv); + check_arm(cx, false, pat, let_expr, body, None, else_expr, msrv, false); } #[expect(clippy::too_many_arguments, clippy::too_many_lines)] @@ -50,6 +62,7 @@ fn check_arm<'tcx>( outer_guard: Option<&'tcx Expr<'tcx>>, outer_else_body: Option<&'tcx Expr<'tcx>>, msrv: Msrv, + only_wildcards_after: bool, ) { let inner_expr = peel_blocks_with_stmt(outer_then_body); if let Some(inner) = IfLetOrMatch::parse(cx, inner_expr) @@ -126,6 +139,7 @@ fn check_arm<'tcx>( ); }); } else if outer_is_match // Leave if-let to the `collapsible_if` lint + && only_wildcards_after // adding a guard allows fall-through; unsafe if other arms follow && let Some(inner) = If::hir(inner_expr) && outer_pat.span.eq_ctxt(inner.cond.span) && match (outer_else_body, inner.r#else) { diff --git a/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs b/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs index f8520c23ea503..6390680c067cb 100644 --- a/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs @@ -128,6 +128,7 @@ impl BindInsteadOfMap { } }); let (span, msg) = if can_sugg + && !suggs.is_empty() && let hir::ExprKind::MethodCall(segment, ..) = expr.kind && let Some(msg) = self.lint_msg(cx) { diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs b/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs index 8f18de41e8a37..48f2c10f97cc6 100644 --- a/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs +++ b/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs @@ -20,7 +20,9 @@ pub(super) fn check<'tcx>( && let Some(impl_id) = cx.tcx.impl_of_assoc(method_id) && cx .tcx - .type_of(impl_id).instantiate_identity().skip_norm_wip() + .type_of(impl_id) + .instantiate_identity() + .skip_norm_wip() .is_diag_item(cx, sym::Option) && let ExprKind::Call(err_path, [err_arg]) = or_expr.kind && err_path.res(cx).ctor_parent(cx).is_lang_item(cx, ResultErr) diff --git a/src/tools/clippy/clippy_lints/src/methods/map_err_ignore.rs b/src/tools/clippy/clippy_lints/src/methods/map_err_ignore.rs index 3e59e0a642cd2..96b8b9dc03227 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_err_ignore.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_err_ignore.rs @@ -11,7 +11,9 @@ pub(super) fn check(cx: &LateContext<'_>, e: &Expr<'_>, arg: &Expr<'_>) { && let Some(impl_id) = cx.tcx.impl_of_assoc(method_id) && cx .tcx - .type_of(impl_id).instantiate_identity().skip_norm_wip() + .type_of(impl_id) + .instantiate_identity() + .skip_norm_wip() .is_diag_item(cx, sym::Result) && let ExprKind::Closure(&Closure { capture_clause: CaptureBy::Ref, diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index cbcc99d264ff9..4fdde52c327e7 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -155,7 +155,6 @@ use clippy_utils::macros::FormatArgsStorage; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::{contains_return, 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}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -165,6 +164,9 @@ use rustc_span::{Span, Symbol}; use crate::matches::manual_filter; +pub use implicit_clone::is_clone_like; +pub use path_ends_with_ext::DEFAULT_ALLOWED_DOTFILES; + declare_clippy_lint! { /// ### What it does /// Checks for usage of `_.and_then(|x| Some(y))`, `_.and_then(|x| Ok(y))` @@ -5222,7 +5224,6 @@ impl Methods { format_collect::check(cx, expr, m_arg, m_ident_span); }, Some((sym::take, take_self_arg, [take_arg], _, _)) => { - #[expect(clippy::collapsible_match)] if self.msrv.meets(cx, msrvs::STR_REPEAT) { manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg); } @@ -5547,9 +5548,7 @@ impl Methods { (sym::open, [_]) => { open_options::check(cx, expr, recv); }, - (sym::or_else, [arg]) => - { - #[expect(clippy::collapsible_match)] + (sym::or_else, [arg]) => { if !bind_instead_of_map::check_or_else_err(cx, expr, recv, arg) { unnecessary_lazy_eval::check(cx, expr, recv, arg, "or"); } @@ -5654,9 +5653,7 @@ impl Methods { (sym::try_into, []) if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::TryInto) => { unnecessary_fallible_conversions::check_method(cx, expr); }, - (sym::to_owned, []) => - { - #[expect(clippy::collapsible_match)] + (sym::to_owned, []) => { if !suspicious_to_owned::check(cx, expr, span) { implicit_clone::check(cx, name, expr, recv); } diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs index b5809d3040241..4f281d745a94e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs @@ -8,7 +8,6 @@ use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{has_non_owning_mutable_access, make_normalized_projection, make_projection}; use clippy_utils::{CaptureKind, can_move_expr_to_closure, fn_def_id, get_enclosing_block, higher, sym}; -use rustc_middle::ty::Unnormalized; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, MultiSpan}; use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_stmt}; @@ -17,7 +16,7 @@ use rustc_hir::{ }; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; -use rustc_middle::ty::{self, AssocTag, ClauseKind, EarlyBinder, GenericArg, GenericArgKind, Ty}; +use rustc_middle::ty::{self, AssocTag, ClauseKind, EarlyBinder, GenericArg, GenericArgKind, Ty, Unnormalized}; use rustc_span::symbol::Ident; use rustc_span::{Span, Symbol}; @@ -248,9 +247,11 @@ fn iterates_same_ty<'tcx>(cx: &LateContext<'tcx>, iter_ty: Ty<'tcx>, collect_ty: && let Some(into_iter_item_proj) = make_projection(cx.tcx, into_iter_trait, sym::Item, [collect_ty]) && let Ok(into_iter_item_ty) = cx.tcx.try_normalize_erasing_regions( cx.typing_env(), - Unnormalized::new_wip( - Ty::new_projection_from_args(cx.tcx, into_iter_item_proj.kind.def_id(), into_iter_item_proj.args) - ), + Unnormalized::new_wip(Ty::new_projection_from_args( + cx.tcx, + into_iter_item_proj.kind.def_id(), + into_iter_item_proj.args, + )), ) { iter_item_ty == into_iter_item_ty @@ -280,9 +281,14 @@ fn is_contains_sig(cx: &LateContext<'_>, call_id: HirId, iter_expr: &Expr<'_>) - ) && let args = cx.tcx.mk_args(&[GenericArg::from(typeck.expr_ty_adjusted(iter_expr))]) && let proj_ty = Ty::new_projection_from_args(cx.tcx, iter_item.def_id, args) - && let Ok(item_ty) = cx.tcx.try_normalize_erasing_regions(cx.typing_env(), Unnormalized::new_wip(proj_ty)) + && let Ok(item_ty) = cx + .tcx + .try_normalize_erasing_regions(cx.typing_env(), Unnormalized::new_wip(proj_ty)) { - item_ty == EarlyBinder::bind(search_ty).instantiate(cx.tcx, cx.typeck_results().node_args(call_id)).skip_norm_wip() + item_ty + == EarlyBinder::bind(search_ty) + .instantiate(cx.tcx, cx.typeck_results().node_args(call_id)) + .skip_norm_wip() } else { false } diff --git a/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs b/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs index a5d88a49ab341..a07cd5a8925ae 100644 --- a/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs +++ b/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs @@ -135,7 +135,13 @@ fn check_unwrap_or_default( 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_norm_wip().skip_binder().output(); + let output_ty = cx + .tcx + .fn_sig(def_id) + .instantiate(cx.tcx, args) + .skip_norm_wip() + .skip_binder() + .output(); cx.tcx .get_diagnostic_item(sym::Default) .is_some_and(|default_trait_id| implements_trait(cx, output_ty, default_trait_id, &[])) diff --git a/src/tools/clippy/clippy_lints/src/methods/stable_sort_primitive.rs b/src/tools/clippy/clippy_lints/src/methods/stable_sort_primitive.rs index 23a5ae2a866b5..75b67f1bdd7d8 100644 --- a/src/tools/clippy/clippy_lints/src/methods/stable_sort_primitive.rs +++ b/src/tools/clippy/clippy_lints/src/methods/stable_sort_primitive.rs @@ -10,7 +10,12 @@ use super::STABLE_SORT_PRIMITIVE; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id) && let Some(impl_id) = cx.tcx.impl_of_assoc(method_id) - && cx.tcx.type_of(impl_id).instantiate_identity().skip_norm_wip().is_slice() + && cx + .tcx + .type_of(impl_id) + .instantiate_identity() + .skip_norm_wip() + .is_slice() && let Some(slice_type) = is_slice_of_primitives(cx, recv) { span_lint_and_then( diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs index 281d9e5f712f7..391209d8c3657 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs @@ -243,7 +243,12 @@ fn mapping_of_mirrored_pats(a_pat: &Pat<'_>, b_pat: &Pat<'_>) -> Option, expr: &Expr<'_>, arg: &Expr<'_>) -> Option { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) && let Some(impl_id) = cx.tcx.impl_of_assoc(method_id) - && cx.tcx.type_of(impl_id).instantiate_identity().skip_norm_wip().is_slice() + && cx + .tcx + .type_of(impl_id) + .instantiate_identity() + .skip_norm_wip() + .is_slice() && let ExprKind::Closure(&Closure { body, .. }) = arg.kind && let closure_body = cx.tcx.hir_body(body) && let &[Param { pat: l_pat, .. }, Param { pat: r_pat, .. }] = closure_body.params diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs index d696bdfa6bd43..a56dcd894b6aa 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -569,7 +569,10 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty< })); if trait_predicates.any(|predicate| { - let predicate = bound_fn_sig.rebind(predicate).instantiate(cx.tcx, new_subst).skip_norm_wip(); + let predicate = bound_fn_sig + .rebind(predicate) + .instantiate(cx.tcx, new_subst) + .skip_norm_wip(); let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), cx.param_env, predicate); !cx.tcx .infer_ctxt() diff --git a/src/tools/clippy/clippy_lints/src/methods/utils.rs b/src/tools/clippy/clippy_lints/src/methods/utils.rs index 012a148bb2a29..33346d867ffee 100644 --- a/src/tools/clippy/clippy_lints/src/methods/utils.rs +++ b/src/tools/clippy/clippy_lints/src/methods/utils.rs @@ -111,7 +111,12 @@ impl<'tcx> Visitor<'tcx> for CloneOrCopyVisitor<'_, 'tcx> { ExprKind::MethodCall(.., args, _) => { if args.iter().all(|arg| !self.is_binding(arg)) && let Some(method_def_id) = self.cx.typeck_results().type_dependent_def_id(parent.hir_id) - && let method_ty = self.cx.tcx.type_of(method_def_id).instantiate_identity().skip_norm_wip() + && let method_ty = self + .cx + .tcx + .type_of(method_def_id) + .instantiate_identity() + .skip_norm_wip() && let self_ty = method_ty.fn_sig(self.cx.tcx).input(0).skip_binder() && matches!(self_ty.kind(), ty::Ref(_, _, Mutability::Not)) { diff --git a/src/tools/clippy/clippy_lints/src/methods/wrong_self_convention.rs b/src/tools/clippy/clippy_lints/src/methods/wrong_self_convention.rs index 12a6f345168fe..439a1af93cadc 100644 --- a/src/tools/clippy/clippy_lints/src/methods/wrong_self_convention.rs +++ b/src/tools/clippy/clippy_lints/src/methods/wrong_self_convention.rs @@ -121,7 +121,7 @@ pub(super) fn check<'tcx>( format!("methods with the following characteristics: ({s})") } else { - format!("methods called {}", &conventions[0]) + format!("methods called {}", conventions[0]) } }; diff --git a/src/tools/clippy/clippy_lints/src/methods/zst_offset.rs b/src/tools/clippy/clippy_lints/src/methods/zst_offset.rs index 102fa7bc8953e..941afb9e2a988 100644 --- a/src/tools/clippy/clippy_lints/src/methods/zst_offset.rs +++ b/src/tools/clippy/clippy_lints/src/methods/zst_offset.rs @@ -1,13 +1,20 @@ use clippy_utils::diagnostics::span_lint; +use clippy_utils::res::MaybeDef; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty; +use rustc_span::sym; use super::ZST_OFFSET; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { - if let ty::RawPtr(ty, _) = cx.typeck_results().expr_ty(recv).kind() - && let Ok(layout) = cx.tcx.layout_of(cx.typing_env().as_query_input(*ty)) + let recv_ty = cx.typeck_results().expr_ty(recv); + let pointee_ty = match recv_ty.kind() { + ty::RawPtr(ty, _) => *ty, + ty::Adt(_, args) if recv_ty.is_diag_item(cx, sym::NonNull) => args.type_at(0), + _ => return, + }; + if let Ok(layout) = cx.tcx.layout_of(cx.typing_env().as_query_input(pointee_ty)) && layout.is_zst() { span_lint(cx, ZST_OFFSET, expr.span, "offset calculation on zero-sized value"); diff --git a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs index b9378d2cdd8cc..4d4dc5678d7b2 100644 --- a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs +++ b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs @@ -203,7 +203,13 @@ fn could_be_const_with_abi(cx: &LateContext<'_>, msrv: Msrv, abi: ExternAbi) -> /// Return `true` when the given `def_id` is a function that has `impl Trait` ty as one of /// its parameter types. fn fn_inputs_has_impl_trait_ty(cx: &LateContext<'_>, def_id: LocalDefId) -> bool { - let inputs = cx.tcx.fn_sig(def_id).instantiate_identity().skip_norm_wip().inputs().skip_binder(); + let inputs = cx + .tcx + .fn_sig(def_id) + .instantiate_identity() + .skip_norm_wip() + .inputs() + .skip_binder(); inputs.iter().any(|input| { matches!( input.kind(), diff --git a/src/tools/clippy/clippy_lints/src/module_style.rs b/src/tools/clippy/clippy_lints/src/module_style.rs index befd50a5c85fa..c1c5a01427197 100644 --- a/src/tools/clippy/clippy_lints/src/module_style.rs +++ b/src/tools/clippy/clippy_lints/src/module_style.rs @@ -1,12 +1,46 @@ +use clippy_utils::ast_utils::is_cfg_test; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet; 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, sym}; +use rustc_span::{FileName, Ident, SourceFile, Span, SyntaxContext, sym}; use std::path::{Component, Path, PathBuf}; use std::sync::Arc; +declare_clippy_lint! { + /// ### What it does + /// Checks that module layout does not use inline modules. + /// Inline test modules (anything annotated with `#[cfg(test)]`) are not linted. + /// + /// ### Why restrict this? + /// Having multiple module layout styles in a project can be confusing. + /// + /// ### Known problems + /// The lint currently doesn't lint inline modules whose parent module is annotated + /// with the `#[path]` attribute. + /// + /// ### Example + /// ```ignore + /// // in `src/lib.rs` + /// mod foo { + /// /* module contents */ + /// } + /// ``` + /// Use instead: + /// ```ignore + /// // in `src/lib.rs` + /// mod foo; + /// // in `src/foo.rs` (or `src/foo/mod.rs`) + /// /* module contents */ + /// ``` + #[clippy::version = "1.97.0"] + pub INLINE_MODULES, + restriction, + "checks that module layout does not use inline modules" +} + declare_clippy_lint! { /// ### What it does /// Checks that module layout uses only self named module files; bans `mod.rs` files. @@ -65,18 +99,36 @@ declare_clippy_lint! { "checks that module layout is consistent" } -impl_lint_pass!(ModStyle => [MOD_MODULE_FILES, SELF_NAMED_MODULE_FILES]); +impl_lint_pass!(ModStyle => [ + INLINE_MODULES, + MOD_MODULE_FILES, + SELF_NAMED_MODULE_FILES, +]); pub struct ModState { + mod_file: Arc, + mod_ident: Ident, + path_from_working_dir: Option, contains_external: bool, has_path_attr: bool, - mod_file: Arc, + is_cfg_test: bool, } #[derive(Default)] pub struct ModStyle { working_dir: Option, - module_stack: Vec, + regular_mod_stack: Vec, + inline_mod_stack: Vec, +} + +impl ModStyle { + fn inside_cfg_test_inline_mod(&self) -> bool { + self.inline_mod_stack.last().is_some_and(|last| last.is_cfg_test) + } + + fn get_relative_path_from_working_dir(&self, file: &SourceFile) -> Option { + try_trim_file_path_prefix(file, self.working_dir.as_ref()?).map(Path::to_path_buf) + } } impl EarlyLintPass for ModStyle { @@ -87,45 +139,83 @@ impl EarlyLintPass for ModStyle { 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 + && cx.builder.lint_level(INLINE_MODULES).level == Level::Allow { return; } - if let ItemKind::Mod(.., ModKind::Loaded(_, Inline::No { .. }, mod_spans, ..)) = &item.kind { + if let ItemKind::Mod(_, mod_ident, ModKind::Loaded(_, inline_or_not, 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 { + let path_from_working_dir = self.get_relative_path_from_working_dir(&mod_file); + let current = ModState { + mod_file, + mod_ident: *mod_ident, + path_from_working_dir, contains_external: false, has_path_attr, - mod_file, - }); + is_cfg_test: self.inside_cfg_test_inline_mod() || is_cfg_test(item), + }; + match inline_or_not { + Inline::Yes => { + if !current.is_cfg_test + && !item.span.from_expansion() + && self.regular_mod_stack.last().is_none_or(|last| !last.has_path_attr) + && let Some(path) = ¤t.path_from_working_dir + { + let opt_extra_mod_dir = self.regular_mod_stack.last().and_then(|last| { + if last.path_from_working_dir.as_ref()?.ends_with("mod.rs") { + None + } else { + Some(&last.mod_ident) + } + }); + check_inline_module( + cx, + path, + *mod_ident, + item.span, + opt_extra_mod_dir + .into_iter() + .chain(self.inline_mod_stack.iter().map(|state| &state.mod_ident)), + ); + } + self.inline_mod_stack.push(current); + }, + Inline::No { .. } => { + if !has_path_attr && let Some(last) = self.regular_mod_stack.last_mut() { + last.contains_external = true; + } + self.regular_mod_stack.push(current); + }, + } } } 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 + && cx.builder.lint_level(INLINE_MODULES).level == Level::Allow { return; } - 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); + if let ItemKind::Mod(.., ModKind::Loaded(_, inline_or_not, ..)) = &item.kind { + match inline_or_not { + Inline::Yes => { + self.inline_mod_stack.pop(); + }, + Inline::No { .. } => { + if let Some(current) = self.regular_mod_stack.pop() + && let Some(path) = ¤t.path_from_working_dir + && !current.has_path_attr + { + if current.contains_external { + check_self_named_module(cx, path, ¤t.mod_file); + } + check_mod_module(cx, path, ¤t.mod_file); + } + }, } - check_mod_module(cx, path, ¤t.mod_file); } } } @@ -173,6 +263,31 @@ fn check_mod_module(cx: &EarlyContext<'_>, path: &Path, file: &SourceFile) { } } +fn check_inline_module<'a>( + cx: &EarlyContext<'_>, + path: &Path, + mod_ident: Ident, + mod_span: Span, + ancestor_mods: impl Iterator, +) { + let Some(parent) = path.parent() else { return }; + + span_lint_and_then(cx, INLINE_MODULES, mod_span, "inline module found", |diag| { + let mut mod_folder = parent.to_path_buf(); + mod_folder.extend(ancestor_mods.map(Ident::as_str)); + let mod_name = mod_ident.as_str(); + + let mod_file = mod_folder.join(mod_name).join("mod.rs"); + let self_named_mod_file = mod_folder.join(format!("{mod_name}.rs")); + let outlined_mod = snippet(cx, mod_span.with_hi(mod_ident.span.hi()), ""); + diag.help(format!( + "move the contents of the module to `{}` or `{}`, and replace this with `{outlined_mod};`", + self_named_mod_file.display(), + mod_file.display(), + )); + }); +} + 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() diff --git a/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs b/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs index 5c0ed6e4aec9b..40db810c12840 100644 --- a/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs +++ b/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs @@ -5,7 +5,6 @@ use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{implements_trait, is_copy}; use clippy_utils::{DefinedTy, ExprUseNode, get_expr_use_site, peel_n_hir_expr_refs, sym}; -use rustc_middle::ty::Unnormalized; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -15,7 +14,7 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::{Rvalue, StatementKind}; use rustc_middle::ty::{ - self, ClauseKind, EarlyBinder, FnSig, GenericArg, GenericArgKind, ParamTy, ProjectionPredicate, Ty, + self, ClauseKind, EarlyBinder, FnSig, GenericArg, GenericArgKind, ParamTy, ProjectionPredicate, Ty, Unnormalized, }; use rustc_session::impl_lint_pass; use rustc_span::SyntaxContext; @@ -180,7 +179,12 @@ fn needless_borrow_count<'tcx>( let meta_sized_trait_def_id = cx.tcx.lang_items().meta_sized_trait(); let drop_trait_def_id = cx.tcx.lang_items().drop_trait(); - let fn_sig = cx.tcx.fn_sig(fn_id).instantiate_identity().skip_norm_wip().skip_binder(); + let fn_sig = cx + .tcx + .fn_sig(fn_id) + .instantiate_identity() + .skip_norm_wip() + .skip_binder(); let predicates = cx.tcx.param_env(fn_id).caller_bounds(); let projection_predicates = predicates .iter() @@ -281,7 +285,9 @@ fn needless_borrow_count<'tcx>( return false; } - let predicate = EarlyBinder::bind(predicate).instantiate(cx.tcx, &args_with_referent_ty[..]).skip_norm_wip(); + let predicate = EarlyBinder::bind(predicate) + .instantiate(cx.tcx, &args_with_referent_ty[..]) + .skip_norm_wip(); let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), cx.param_env, predicate); let infcx = cx.tcx.infer_ctxt().build(cx.typing_mode()); infcx.predicate_must_hold_modulo_regions(&obligation) @@ -430,10 +436,10 @@ fn replace_types<'tcx>( .expect_ty(cx.tcx) .to_ty(cx.tcx); - if let Ok(projected_ty) = cx.tcx.try_normalize_erasing_regions( - cx.typing_env(), - Unnormalized::new_wip(projection), - ) && args[term_param_ty.index as usize] != GenericArg::from(projected_ty) + if let Ok(projected_ty) = cx + .tcx + .try_normalize_erasing_regions(cx.typing_env(), Unnormalized::new_wip(projection)) + && args[term_param_ty.index as usize] != GenericArg::from(projected_ty) { deque.push_back((*term_param_ty, projected_ty)); } diff --git a/src/tools/clippy/clippy_lints/src/needless_ifs.rs b/src/tools/clippy/clippy_lints/src/needless_ifs.rs index ad1da9441dacb..cdf9bd91339c6 100644 --- a/src/tools/clippy/clippy_lints/src/needless_ifs.rs +++ b/src/tools/clippy/clippy_lints/src/needless_ifs.rs @@ -55,7 +55,7 @@ impl LateLintPass<'_> for NeedlessIfs { // - comments // - #[cfg]'d out code src.bytes() - .all(|ch| matches!(ch, b'{' | b'}') || ch.is_ascii_whitespace()) + .all(|ch| matches!(ch, b'{' | b'}') || ch.is_ascii_whitespace() || ch == b'\x0b') }) && let Some(cond_span) = walk_span_to_context(cond.span, expr.span.ctxt()) && let Some(cond_snippet) = cond_span.get_source_text(cx) diff --git a/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs b/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs index ac3cf0250d0fc..600e4beba7d2f 100644 --- a/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs +++ b/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs @@ -92,7 +92,9 @@ fn path_to_sized_bound(cx: &LateContext<'_>, trait_bound: &PolyTraitRef<'_>) -> return true; } - for (predicate, _) in cx.tcx.explicit_super_predicates_of(trait_def_id) + for (predicate, _) in cx + .tcx + .explicit_super_predicates_of(trait_def_id) .iter_identity_copied() .map(Unnormalized::skip_norm_wip) { diff --git a/src/tools/clippy/clippy_lints/src/non_copy_const.rs b/src/tools/clippy/clippy_lints/src/non_copy_const.rs index a06497e140ba4..82095e361e0e0 100644 --- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs +++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs @@ -24,7 +24,6 @@ use clippy_utils::macros::macro_backtrace; use clippy_utils::paths::{PathNS, lookup_path_str}; use clippy_utils::ty::{get_field_idx_by_name, implements_trait}; use clippy_utils::{is_in_const_context, sym}; -use rustc_middle::ty::Unnormalized; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, DefIdSet}; @@ -37,7 +36,7 @@ use rustc_middle::mir::{ConstValue, UnevaluatedConst}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, DerefAdjustKind}; use rustc_middle::ty::{ self, EarlyBinder, GenericArgs, GenericArgsRef, Instance, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, TypeckResults, - TypingEnv, + TypingEnv, Unnormalized, }; use rustc_session::impl_lint_pass; use rustc_span::DUMMY_SP; @@ -278,7 +277,9 @@ impl<'tcx> NonCopyConst<'tcx> { /// Checks if a value of the given type is `Freeze`, or may be depending on the value. fn is_ty_freeze(&mut self, tcx: TyCtxt<'tcx>, typing_env: TypingEnv<'tcx>, ty: Ty<'tcx>) -> IsFreeze { // FIXME: this should probably be using the trait solver - let ty = tcx.try_normalize_erasing_regions(typing_env, Unnormalized::new_wip(ty)).unwrap_or(ty); + let ty = tcx + .try_normalize_erasing_regions(typing_env, Unnormalized::new_wip(ty)) + .unwrap_or(ty); match self.freeze_tys.entry(ty) { Entry::Occupied(e) => *e.get(), Entry::Vacant(e) => { @@ -346,7 +347,9 @@ impl<'tcx> NonCopyConst<'tcx> { ty: Ty<'tcx>, val: ConstValue, ) -> Result { - let ty = tcx.try_normalize_erasing_regions(typing_env, Unnormalized::new_wip(ty)).unwrap_or(ty); + let ty = tcx + .try_normalize_erasing_regions(typing_env, Unnormalized::new_wip(ty)) + .unwrap_or(ty); match self.is_ty_freeze(tcx, typing_env, ty) { IsFreeze::Yes => Ok(true), IsFreeze::Maybe if matches!(ty.kind(), ty::Adt(..) | ty::Array(..) | ty::Tuple(..)) => { @@ -382,7 +385,9 @@ impl<'tcx> NonCopyConst<'tcx> { ) -> bool { // Make sure to instantiate all types coming from `typeck` with `gen_args`. let ty = EarlyBinder::bind(typeck.expr_ty(e)).instantiate(tcx, gen_args); - let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty.skip_norm_wip()); + let ty = tcx + .try_normalize_erasing_regions(typing_env, ty) + .unwrap_or(ty.skip_norm_wip()); match self.is_ty_freeze(tcx, typing_env, ty) { IsFreeze::Yes => true, IsFreeze::No => false, @@ -396,7 +401,9 @@ impl<'tcx> NonCopyConst<'tcx> { }, ExprKind::Path(ref p) => { let res = typeck.qpath_res(p, e.hir_id); - let gen_args = EarlyBinder::bind(typeck.node_args(e.hir_id)).instantiate(tcx, gen_args).skip_norm_wip(); + let gen_args = EarlyBinder::bind(typeck.node_args(e.hir_id)) + .instantiate(tcx, gen_args) + .skip_norm_wip(); match res { Res::Def(DefKind::Const { .. } | DefKind::AssocConst { .. }, did) if let Ok(val) = @@ -448,7 +455,9 @@ impl<'tcx> NonCopyConst<'tcx> { loop { let ty = typeck.expr_ty(src_expr); // Normalized as we need to check if this is an array later. - let ty = tcx.try_normalize_erasing_regions(typing_env, Unnormalized::new_wip(ty)).unwrap_or(ty); + let ty = tcx + .try_normalize_erasing_regions(typing_env, Unnormalized::new_wip(ty)) + .unwrap_or(ty); let is_freeze = self.is_ty_freeze(tcx, typing_env, ty); if is_freeze.is_freeze() { return None; @@ -489,7 +498,9 @@ impl<'tcx> NonCopyConst<'tcx> { let mut ty = typeck.expr_ty(src_expr); loop { // Normalized as we need to check if this is an array later. - ty = tcx.try_normalize_erasing_regions(typing_env, Unnormalized::new_wip(ty)).unwrap_or(ty); + ty = tcx + .try_normalize_erasing_regions(typing_env, Unnormalized::new_wip(ty)) + .unwrap_or(ty); if let [adjust, ..] = typeck.expr_adjustments(src_expr) { let res = if let Some(cause) = does_adjust_borrow(adjust) && !self.is_value_freeze(tcx, typing_env, ty, val)? @@ -588,10 +599,9 @@ impl<'tcx> NonCopyConst<'tcx> { init_expr = next_init; }, ExprKind::Path(ref init_path) => { - let next_init_args = - EarlyBinder::bind(init_typeck.node_args(init_expr.hir_id)) - .instantiate(tcx, init_args) - .skip_norm_wip(); + let next_init_args = EarlyBinder::bind(init_typeck.node_args(init_expr.hir_id)) + .instantiate(tcx, init_args) + .skip_norm_wip(); match init_typeck.qpath_res(init_path, init_expr.hir_id) { Res::Def(DefKind::Ctor(..), _) => return None, Res::Def(DefKind::Const { .. } | DefKind::AssocConst { .. }, did) @@ -627,7 +637,9 @@ impl<'tcx> NonCopyConst<'tcx> { // gets cached. let ty = typeck.expr_ty(src_expr); // Normalized as we need to check if this is an array later. - let ty = tcx.try_normalize_erasing_regions(typing_env, Unnormalized::new_wip(ty)).unwrap_or(ty); + let ty = tcx + .try_normalize_erasing_regions(typing_env, Unnormalized::new_wip(ty)) + .unwrap_or(ty); if self.is_ty_freeze(tcx, typing_env, ty).is_freeze() { return None; } @@ -798,7 +810,11 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> { let ty = (ReplaceAssocFolder { tcx: cx.tcx, trait_id, - self_ty: cx.tcx.type_of(parent_item.owner_id).instantiate_identity().skip_norm_wip(), + self_ty: cx + .tcx + .type_of(parent_item.owner_id) + .instantiate_identity() + .skip_norm_wip(), }) .fold_ty(cx.tcx.type_of(item.owner_id).instantiate_identity().skip_norm_wip()); // `ty` may not be normalizable, but that should be fine. diff --git a/src/tools/clippy/clippy_lints/src/operators/bit_mask.rs b/src/tools/clippy/clippy_lints/src/operators/bit_mask.rs index 104f786ead165..1607d23e6a335 100644 --- a/src/tools/clippy/clippy_lints/src/operators/bit_mask.rs +++ b/src/tools/clippy/clippy_lints/src/operators/bit_mask.rs @@ -43,7 +43,8 @@ fn check_compare<'a>(cx: &LateContext<'a>, bit_op: &Expr<'a>, cmp_op: BinOpKind, } if let Some(mask) = fetch_int_literal(cx, right).or_else(|| fetch_int_literal(cx, left)) { let ty = cx.typeck_results().expr_ty(bit_op); - if !ty.is_ptr_sized_integral() + if ty.is_primitive() + && !ty.is_ptr_sized_integral() && let bits = ty.primitive_size(cx.tcx) { // Strip high bits that don't fit into the result type as they won't be used in the comparison diff --git a/src/tools/clippy/clippy_lints/src/operators/identity_op.rs b/src/tools/clippy/clippy_lints/src/operators/identity_op.rs index 832b12712f83f..2e0beb2d4e821 100644 --- a/src/tools/clippy/clippy_lints/src/operators/identity_op.rs +++ b/src/tools/clippy/clippy_lints/src/operators/identity_op.rs @@ -283,7 +283,13 @@ fn is_assoc_fn_without_type_instance<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<' .. }, )) = func.kind - && let output_ty = cx.tcx.fn_sig(*def_id).instantiate_identity().skip_norm_wip().skip_binder().output() + && let output_ty = cx + .tcx + .fn_sig(*def_id) + .instantiate_identity() + .skip_norm_wip() + .skip_binder() + .output() && let ty::Param(ty::ParamTy { name: kw::SelfUpper, .. }) = output_ty.kind() diff --git a/src/tools/clippy/clippy_lints/src/operators/manual_div_ceil.rs b/src/tools/clippy/clippy_lints/src/operators/manual_div_ceil.rs index 5a4823ddfcf63..304c51ba2627d 100644 --- a/src/tools/clippy/clippy_lints/src/operators/manual_div_ceil.rs +++ b/src/tools/clippy/clippy_lints/src/operators/manual_div_ceil.rs @@ -177,11 +177,7 @@ fn build_suggestion( // suggestion message, we want to make a suggestion string before `div_ceil` like // `(-2048_{type_suffix})`. let suggestion_before_div_ceil = if has_enclosing_paren(÷nd_sugg_str) { - format!( - "{}{})", - ÷nd_sugg_str[..dividend_sugg_str.len() - 1].to_string(), - type_suffix - ) + format!("{}{type_suffix})", ÷nd_sugg_str[..dividend_sugg_str.len() - 1]) } else { format!("{dividend_sugg_str}{type_suffix}") }; diff --git a/src/tools/clippy/clippy_lints/src/ptr/ptr_arg.rs b/src/tools/clippy/clippy_lints/src/ptr/ptr_arg.rs index ea1be8899b292..40bc42dcdcb36 100644 --- a/src/tools/clippy/clippy_lints/src/ptr/ptr_arg.rs +++ b/src/tools/clippy/clippy_lints/src/ptr/ptr_arg.rs @@ -36,7 +36,12 @@ pub(super) fn check_body<'tcx>( } let decl = sig.decl; - let sig = cx.tcx.fn_sig(item_id).instantiate_identity().skip_norm_wip().skip_binder(); + let sig = cx + .tcx + .fn_sig(item_id) + .instantiate_identity() + .skip_norm_wip() + .skip_binder(); let lint_args: Vec<_> = check_fn_args(cx, sig, decl.inputs, body.params) .filter(|arg| !is_trait_item || arg.mutability() == Mutability::Not) .collect(); @@ -68,7 +73,11 @@ pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item_id: OwnerId, s for arg in check_fn_args( cx, - cx.tcx.fn_sig(item_id).instantiate_identity().skip_norm_wip().skip_binder(), + cx.tcx + .fn_sig(item_id) + .instantiate_identity() + .skip_norm_wip() + .skip_binder(), sig.decl.inputs, &[], ) diff --git a/src/tools/clippy/clippy_lints/src/question_mark.rs b/src/tools/clippy/clippy_lints/src/question_mark.rs index 4bd6b1696b354..f07cc10cbced5 100644 --- a/src/tools/clippy/clippy_lints/src/question_mark.rs +++ b/src/tools/clippy/clippy_lints/src/question_mark.rs @@ -495,7 +495,7 @@ fn check_if_try_match<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { let mut sugg = reindent_multiline(&arm_body_snippet, true, Some(indent)); let binding_snippet = snippet_with_applicability(cx, binding_span, "..", &mut applicability); let inner_indent = " ".repeat(indent + 4); - if matches!(arm_body.kind, ExprKind::Block(..)) { + if matches!(arm_body.kind, ExprKind::Block(..)) && sugg.starts_with('{') { sugg.insert_str( 1, &format!("\n{inner_indent}let {binding_snippet} = {scrutinee_snippet}?;"), diff --git a/src/tools/clippy/clippy_lints/src/redundant_slicing.rs b/src/tools/clippy/clippy_lints/src/redundant_slicing.rs index 3c0f04a8e87ce..be5e4190e1284 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_slicing.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_slicing.rs @@ -3,13 +3,12 @@ use clippy_utils::get_parent_expr; use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::peel_and_count_ty_refs; -use rustc_middle::ty::Unnormalized; use rustc_ast::util::parser::ExprPrecedence; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability}; use rustc_lint::{LateContext, LateLintPass, Lint}; use rustc_middle::ty::adjustment::{Adjust, AutoBorrow, AutoBorrowMutability}; -use rustc_middle::ty::{GenericArg, Ty}; +use rustc_middle::ty::{GenericArg, Ty, Unnormalized}; use rustc_session::declare_lint_pass; declare_clippy_lint! { @@ -146,8 +145,8 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing { Unnormalized::new_wip(Ty::new_projection_from_args( cx.tcx, target_id, - cx.tcx.mk_args(&[GenericArg::from(indexed_ty)]) - )) + cx.tcx.mk_args(&[GenericArg::from(indexed_ty)]), + )), ) && deref_ty == expr_ty { diff --git a/src/tools/clippy/clippy_lints/src/ref_option_ref.rs b/src/tools/clippy/clippy_lints/src/ref_option_ref.rs index 074345e753212..f1cf505482e47 100644 --- a/src/tools/clippy/clippy_lints/src/ref_option_ref.rs +++ b/src/tools/clippy/clippy_lints/src/ref_option_ref.rs @@ -58,7 +58,7 @@ impl<'tcx> LateLintPass<'tcx> for RefOptionRef { ty.span, "since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>`", "try", - format!("Option<{}>", &snippet(cx, inner_ty.span, "..")), + format!("Option<{}>", snippet(cx, inner_ty.span, "..")), Applicability::MaybeIncorrect, ); } diff --git a/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs b/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs index 12ce318969686..53cac75b802ff 100644 --- a/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs +++ b/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::MaybeResPath; use clippy_utils::source::{indent_of, snippet}; use clippy_utils::{expr_or_init, get_builtin_attr, peel_hir_expr_unary, sym}; -use rustc_middle::ty::Unnormalized; use rustc_ast::BindingMode; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_errors::Applicability; @@ -10,7 +9,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{self as hir, HirId}; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty::{GenericArgKind, Ty}; +use rustc_middle::ty::{GenericArgKind, Ty, Unnormalized}; use rustc_session::impl_lint_pass; use rustc_span::symbol::Ident; use rustc_span::{DUMMY_SP, Span}; @@ -154,7 +153,8 @@ impl<'cx, 'others, 'tcx> AttrChecker<'cx, 'others, 'tcx> { } let ty = self .cx - .tcx.try_normalize_erasing_regions(self.cx.typing_env(), Unnormalized::new_wip(ty)) + .tcx + .try_normalize_erasing_regions(self.cx.typing_env(), Unnormalized::new_wip(ty)) .unwrap_or(ty); match self.type_cache.entry(ty) { Entry::Occupied(e) => return *e.get(), diff --git a/src/tools/clippy/clippy_lints/src/trait_bounds.rs b/src/tools/clippy/clippy_lints/src/trait_bounds.rs index e4faf8e82a8e6..13ff3914f8e0a 100644 --- a/src/tools/clippy/clippy_lints/src/trait_bounds.rs +++ b/src/tools/clippy/clippy_lints/src/trait_bounds.rs @@ -136,7 +136,9 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds { .. }) = segments.first() && let Some(Node::Item(Item { - kind: ItemKind::Trait {bounds: self_bounds,..}, + kind: ItemKind::Trait { + bounds: self_bounds, .. + }, .. })) = cx.tcx.hir_get_if_local(*def_id) { diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs index d5a9006095da1..05f4071406477 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs @@ -1,10 +1,9 @@ use super::TRANSMUTE_UNDEFINED_REPR; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::is_c_void; -use rustc_middle::ty::Unnormalized; use rustc_hir::Expr; use rustc_lint::LateContext; -use rustc_middle::ty::{self, GenericArgsRef, IntTy, Ty, UintTy}; +use rustc_middle::ty::{self, GenericArgsRef, IntTy, Ty, UintTy, Unnormalized}; #[expect(clippy::too_many_lines)] pub(super) fn check<'tcx>( @@ -241,7 +240,10 @@ enum ReducedTy<'tcx> { /// Reduce structs containing a single non-zero sized field to it's contained type. fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx> { loop { - ty = cx.tcx.try_normalize_erasing_regions(cx.typing_env(), Unnormalized::new_wip(ty)).unwrap_or(ty); + ty = cx + .tcx + .try_normalize_erasing_regions(cx.typing_env(), Unnormalized::new_wip(ty)) + .unwrap_or(ty); return match *ty.kind() { ty::Pat(base, _) => { ty = base; @@ -298,7 +300,9 @@ fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx> } fn is_zero_sized_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { - if let Ok(ty) = cx.tcx.try_normalize_erasing_regions(cx.typing_env(), Unnormalized::new_wip(ty)) + if let Ok(ty) = cx + .tcx + .try_normalize_erasing_regions(cx.typing_env(), Unnormalized::new_wip(ty)) && let Ok(layout) = cx.tcx.layout_of(cx.typing_env().as_query_input(ty)) { layout.layout.size().bytes() == 0 diff --git a/src/tools/clippy/clippy_lints/src/transmute/utils.rs b/src/tools/clippy/clippy_lints/src/transmute/utils.rs index 74494aff0cb62..0306e6c2015bb 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/utils.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/utils.rs @@ -1,13 +1,16 @@ -use rustc_middle::ty::Unnormalized; use rustc_lint::LateContext; -use rustc_middle::ty::Ty; +use rustc_middle::ty::{Ty, Unnormalized}; // check if the component types of the transmuted collection and the result have different ABI, // size or alignment pub(super) fn is_layout_incompatible<'tcx>(cx: &LateContext<'tcx>, from: Ty<'tcx>, to: Ty<'tcx>) -> bool { let typing_env = cx.typing_env(); - if let Ok(from) = cx.tcx.try_normalize_erasing_regions(typing_env, Unnormalized::new_wip(from)) - && let Ok(to) = cx.tcx.try_normalize_erasing_regions(typing_env, Unnormalized::new_wip(to)) + if let Ok(from) = cx + .tcx + .try_normalize_erasing_regions(typing_env, Unnormalized::new_wip(from)) + && let Ok(to) = cx + .tcx + .try_normalize_erasing_regions(typing_env, Unnormalized::new_wip(to)) && let Ok(from_layout) = cx.tcx.layout_of(typing_env.as_query_input(from)) && let Ok(to_layout) = cx.tcx.layout_of(typing_env.as_query_input(to)) { diff --git a/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs b/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs index 973b2e95b474f..c48ca652ffd11 100644 --- a/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs +++ b/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs @@ -231,9 +231,8 @@ fn fmt_stmts_and_call( let block_indent = call_expr_indent + 4; stmts_and_call_snippet = reindent_multiline(&stmts_and_call_snippet, true, Some(block_indent)); stmts_and_call_snippet = format!( - "{{\n{}{}\n{}}}", + "{{\n{}{stmts_and_call_snippet}\n{}}}", " ".repeat(block_indent), - &stmts_and_call_snippet, " ".repeat(call_expr_indent) ); } diff --git a/src/tools/clippy/clippy_lints/src/upper_case_acronyms.rs b/src/tools/clippy/clippy_lints/src/upper_case_acronyms.rs index 0b95468436127..15dc40af947ef 100644 --- a/src/tools/clippy/clippy_lints/src/upper_case_acronyms.rs +++ b/src/tools/clippy/clippy_lints/src/upper_case_acronyms.rs @@ -131,7 +131,7 @@ impl LateLintPass<'_> for UpperCaseAcronyms { return; } match it.kind { - ItemKind::TyAlias(ident, ..) | ItemKind::Struct(ident, ..) | ItemKind::Trait { ident, .. }=> { + ItemKind::TyAlias(ident, ..) | ItemKind::Struct(ident, ..) | ItemKind::Trait { ident, .. } => { check_ident(cx, &ident, it.hir_id(), self.upper_case_acronyms_aggressive); }, ItemKind::Enum(ident, _, ref enumdef) => { diff --git a/src/tools/clippy/clippy_lints/src/useless_conversion.rs b/src/tools/clippy/clippy_lints/src/useless_conversion.rs index 0174b90064b53..d6db088ba76c0 100644 --- a/src/tools/clippy/clippy_lints/src/useless_conversion.rs +++ b/src/tools/clippy/clippy_lints/src/useless_conversion.rs @@ -6,7 +6,7 @@ use clippy_utils::ty::{is_copy, same_type_modulo_regions}; use clippy_utils::{get_parent_expr, is_ty_alias, sym}; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; -use rustc_hir::{BindingMode, Expr, ExprKind, HirId, MatchSource, Mutability, Node, PatKind}; +use rustc_hir::{BindingMode, Expr, ExprKind, HirId, LangItem, MatchSource, Mutability, Node, PatKind}; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::Obligation; use rustc_lint::{LateContext, LateLintPass}; @@ -19,7 +19,7 @@ use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; declare_clippy_lint! { /// ### What it does - /// Checks for `Into`, `TryInto`, `From`, `TryFrom`, or `IntoIter` calls + /// Checks for `Into`, `TryInto`, `From`, `TryFrom`, or `IntoIterator` calls /// which uselessly convert to the same type. /// /// ### Why is this bad? @@ -38,7 +38,7 @@ declare_clippy_lint! { #[clippy::version = "1.45.0"] pub USELESS_CONVERSION, complexity, - "calls to `Into`, `TryInto`, `From`, `TryFrom`, or `IntoIter` which perform useless conversions to the same type" + "calls to `Into`, `TryInto`, `From`, `TryFrom`, or `IntoIterator` which perform useless conversions to the same type" } impl_lint_pass!(UselessConversion => [USELESS_CONVERSION]); @@ -322,13 +322,13 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { return; } - let a = cx.typeck_results().expr_ty(e); - let b = cx.typeck_results().expr_ty(recv); + let iter_ty = cx.typeck_results().expr_ty(e); + let into_iter_ty = cx.typeck_results().expr_ty(recv); // 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_modulo_regions(a, b) && !is_copy(cx, b) { + if same_type_modulo_regions(iter_ty, into_iter_ty) && !is_copy(cx, into_iter_ty) { // 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) @@ -356,6 +356,28 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { return; } + // In a future edition of Rust (edition 2027, hopefully), or with the unstable + // `feature(new_range)`, the syntax `a..b` will change from producing type `core::ops::Range`, + // which implements `Iterator`, to producing type `core::range::Range`, which implements + // `IntoIterator` only. + // + // Therefore, an `(a..b).into_iter()` call that is technically useless today will be useful for + // edition migration or unstable feature testing; do not remove it. + // + // In the future, after most code has either migrated to the new range types or declined to + // do so, this special case will be much less useful and could be removed. + if let Some(parent) = get_parent_expr(cx, e) + // Is a method call, not, say, a for loop where the conversion *is* useless. + && let ExprKind::MethodCall(_, _, _, _) = parent.kind + // These lang items are the 3 core::ops range types that implement Iterator. + // All other range types do not implement Iterator, so this lint does not apply to them. + && (into_iter_ty.is_lang_item(cx, LangItem::Range) + || into_iter_ty.is_lang_item(cx, LangItem::RangeFrom) + || into_iter_ty.is_lang_item(cx, LangItem::RangeInclusiveStruct)) + { + return; + } + let mut applicability = Applicability::MachineApplicable; let sugg = snippet_with_context(cx, recv.span, e.span.ctxt(), "", &mut applicability) .0 @@ -364,7 +386,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - format!("useless conversion to the same type: `{b}`"), + format!("useless conversion to the same type: `{into_iter_ty}`"), "consider removing `.into_iter()`", sugg, applicability, diff --git a/src/tools/clippy/clippy_lints_internal/src/collapsible_span_lint_calls.rs b/src/tools/clippy/clippy_lints_internal/src/collapsible_span_lint_calls.rs index b048a1004b0d5..bbcef0856ba4d 100644 --- a/src/tools/clippy/clippy_lints_internal/src/collapsible_span_lint_calls.rs +++ b/src/tools/clippy/clippy_lints_internal/src/collapsible_span_lint_calls.rs @@ -232,8 +232,8 @@ fn suggest_help( "this call is collapsible", "collapse into", format!( - "span_lint_and_help({}, {}, {}, {}, {}, {help})", - and_then_snippets.cx, and_then_snippets.lint, and_then_snippets.span, and_then_snippets.msg, &option_span, + "span_lint_and_help({}, {}, {}, {}, {option_span}, {help})", + and_then_snippets.cx, and_then_snippets.lint, and_then_snippets.span, and_then_snippets.msg ), app, ); diff --git a/src/tools/clippy/clippy_lints_internal/src/msrv_attr_impl.rs b/src/tools/clippy/clippy_lints_internal/src/msrv_attr_impl.rs index a968629ac2dc1..1225f42d56022 100644 --- a/src/tools/clippy/clippy_lints_internal/src/msrv_attr_impl.rs +++ b/src/tools/clippy/clippy_lints_internal/src/msrv_attr_impl.rs @@ -26,13 +26,19 @@ impl LateLintPass<'_> for MsrvAttrImpl { items, .. }) = &item.kind - && let trait_ref = cx.tcx.impl_trait_ref(item.owner_id).instantiate_identity().skip_norm_wip() + && let trait_ref = cx + .tcx + .impl_trait_ref(item.owner_id) + .instantiate_identity() + .skip_norm_wip() && internal_paths::EARLY_LINT_PASS.matches(cx, trait_ref.def_id) && let ty::Adt(self_ty_def, _) = trait_ref.self_ty().kind() && self_ty_def.is_struct() && self_ty_def.all_fields().any(|f| { cx.tcx - .type_of(f.did).instantiate_identity().skip_norm_wip() + .type_of(f.did) + .instantiate_identity() + .skip_norm_wip() .walk() .filter(|t| matches!(t.kind(), GenericArgKind::Type(_))) .any(|t| internal_paths::MSRV_STACK.matches_ty(cx, t.expect_ty())) diff --git a/src/tools/clippy/clippy_lints_internal/src/unusual_names.rs b/src/tools/clippy/clippy_lints_internal/src/unusual_names.rs index b10f4be7497b9..b68ebed713fcb 100644 --- a/src/tools/clippy/clippy_lints_internal/src/unusual_names.rs +++ b/src/tools/clippy/clippy_lints_internal/src/unusual_names.rs @@ -65,11 +65,14 @@ impl<'tcx> LateLintPass<'tcx> for UnusualNames { if matches!(kind, FnKind::Closure) { return; } - for (param, ty) in body - .params - .iter() - .zip(cx.tcx.fn_sig(def_id).instantiate_identity().skip_norm_wip().skip_binder().inputs()) - { + for (param, ty) in body.params.iter().zip( + cx.tcx + .fn_sig(def_id) + .instantiate_identity() + .skip_norm_wip() + .skip_binder() + .inputs(), + ) { check_pat_name_for_ty(cx, param.pat, *ty, "parameter"); } } diff --git a/src/tools/clippy/clippy_utils/README.md b/src/tools/clippy/clippy_utils/README.md index 99489cb11e734..de7afb7cf13ac 100644 --- a/src/tools/clippy/clippy_utils/README.md +++ b/src/tools/clippy/clippy_utils/README.md @@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain: ``` -nightly-2026-04-16 +nightly-2026-04-30 ``` diff --git a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs index 82142d55e21d8..7b408471574f9 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs @@ -5,7 +5,8 @@ #![allow(clippy::wildcard_imports, clippy::enum_glob_use)] use crate::{both, over}; -use rustc_ast::{self as ast, *}; +use rustc_ast::{self as ast, HasAttrs, *}; +use rustc_span::sym; use rustc_span::symbol::Ident; use std::mem; @@ -1044,3 +1045,17 @@ pub fn eq_delim_args(l: &DelimArgs, r: &DelimArgs) -> bool { && l.tokens.len() == r.tokens.len() && l.tokens.iter().zip(r.tokens.iter()).all(|(a, b)| a.eq_unspanned(b)) } + +/// Checks whether `#[cfg(test)]` is directly applied to `item`. +pub fn is_cfg_test(item: &impl HasAttrs) -> bool { + item.attrs().iter().any(|attr| { + if attr.has_name(sym::cfg) + && let Some(item_list) = attr.meta_item_list() + && item_list.iter().any(|item| item.has_name(sym::test)) + { + true + } else { + false + } + }) +} diff --git a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs index e1382f5b706c0..55c0372259c0f 100644 --- a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs +++ b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs @@ -265,14 +265,18 @@ fn item_search_pat(item: &Item<'_>) -> (Pat, Pat) { ItemKind::Struct(_, _, VariantData::Struct { .. }) => (Pat::Str("struct"), Pat::Str("}")), ItemKind::Struct(..) => (Pat::Str("struct"), Pat::Str(";")), ItemKind::Union(..) => (Pat::Str("union"), Pat::Str("}")), - ItemKind::Trait { safety: Safety::Unsafe, .. } + ItemKind::Trait { + safety: Safety::Unsafe, .. + } | ItemKind::Impl(Impl { of_trait: Some(TraitImplHeader { safety: Safety::Unsafe, .. }), .. }) => (Pat::Str("unsafe"), Pat::Str("}")), - ItemKind::Trait { is_auto: IsAuto::Yes, .. } => (Pat::Str("auto"), Pat::Str("}")), + ItemKind::Trait { + is_auto: IsAuto::Yes, .. + } => (Pat::Str("auto"), Pat::Str("}")), ItemKind::Trait { .. } => (Pat::Str("trait"), Pat::Str("}")), ItemKind::Impl(_) => (Pat::Str("impl"), Pat::Str("}")), ItemKind::Mod(..) => (Pat::Str("mod"), Pat::Str("")), diff --git a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs index 0e28cdf3b9ca3..cd609980b149b 100644 --- a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs +++ b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs @@ -71,7 +71,12 @@ fn fn_eagerness(cx: &LateContext<'_>, fn_id: DefId, name: Symbol, have_one_arg: // Due to the limited operations on these types functions should be fairly cheap. if def.variants().iter().flat_map(|v| v.fields.iter()).any(|x| { matches!( - cx.tcx.type_of(x.did).instantiate_identity().skip_norm_wip().peel_refs().kind(), + cx.tcx + .type_of(x.did) + .instantiate_identity() + .skip_norm_wip() + .peel_refs() + .kind(), ty::Param(_) ) }) && all_predicates_of(cx.tcx, fn_id).all(|(pred, _)| match pred.kind().skip_binder() { @@ -82,7 +87,9 @@ fn fn_eagerness(cx: &LateContext<'_>, fn_id: DefId, name: Symbol, have_one_arg: // Limit the function to either `(self) -> bool` or `(&self) -> bool` match &**cx .tcx - .fn_sig(fn_id).instantiate_identity().skip_norm_wip() + .fn_sig(fn_id) + .instantiate_identity() + .skip_norm_wip() .skip_binder() .inputs_and_output { diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index e5420f9f9d166..4e912bcaa9b73 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -548,7 +548,12 @@ fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath< if let QPath::TypeRelative(_, method) = path && method.ident.name == sym::new && let Some(impl_did) = cx.tcx.impl_of_assoc(def_id) - && let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().skip_norm_wip().ty_adt_def() + && let Some(adt) = cx + .tcx + .type_of(impl_did) + .instantiate_identity() + .skip_norm_wip() + .ty_adt_def() { return Some(adt.did()) == cx.tcx.lang_items().string() || (cx.tcx.get_diagnostic_name(adt.did())).is_some_and(|adt_name| std_types_symbols.contains(&adt_name)); @@ -1489,7 +1494,12 @@ pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_def_id: OwnerId) -> Ty<'tcx> { /// Convenience function to get the nth argument type of a function. pub fn nth_arg<'tcx>(cx: &LateContext<'tcx>, fn_def_id: OwnerId, nth: usize) -> Ty<'tcx> { - let arg = cx.tcx.fn_sig(fn_def_id).instantiate_identity().skip_norm_wip().input(nth); + let arg = cx + .tcx + .fn_sig(fn_def_id) + .instantiate_identity() + .skip_norm_wip() + .input(nth); cx.tcx.instantiate_bound_regions_with_erased(arg) } diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index 8f7a140e91a86..41cac00e70049 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -50,7 +50,12 @@ pub fn is_min_const_fn<'tcx>(cx: &LateContext<'tcx>, body: &Body<'tcx>, msrv: Ms // impl trait is gone in MIR, so check the return type manually check_ty( cx, - cx.tcx.fn_sig(def_id).instantiate_identity().skip_norm_wip().output().skip_binder(), + cx.tcx + .fn_sig(def_id) + .instantiate_identity() + .skip_norm_wip() + .output() + .skip_binder(), body.local_decls.iter().next().unwrap().source_info.span, msrv, )?; diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs index a5d17b76aa5e9..92f08b604ca58 100644 --- a/src/tools/clippy/clippy_utils/src/sugg.rs +++ b/src/tools/clippy/clippy_utils/src/sugg.rs @@ -896,7 +896,14 @@ impl<'tcx> DerefDelegate<'_, 'tcx> { .cx .typeck_results() .type_dependent_def_id(parent_expr.hir_id) - .map(|did| self.cx.tcx.fn_sig(did).instantiate_identity().skip_norm_wip().skip_binder()) + .map(|did| { + self.cx + .tcx + .fn_sig(did) + .instantiate_identity() + .skip_norm_wip() + .skip_binder() + }) { std::iter::once(receiver) .chain(call_args.iter()) diff --git a/src/tools/clippy/clippy_utils/src/sym.rs b/src/tools/clippy/clippy_utils/src/sym.rs index 87aac25f5bd12..d04257067be37 100644 --- a/src/tools/clippy/clippy_utils/src/sym.rs +++ b/src/tools/clippy/clippy_utils/src/sym.rs @@ -91,9 +91,7 @@ generate! { MIN, MaybeDef, MpmcReceiver, - MpmcSender, MpscReceiver, - MpscSender, MsrvStack, Octal, OpenOptions, diff --git a/src/tools/clippy/clippy_utils/src/ty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/mod.rs index 66d85b58eaae4..4ab21f34de60d 100644 --- a/src/tools/clippy/clippy_utils/src/ty/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ty/mod.rs @@ -21,8 +21,7 @@ use rustc_middle::ty::layout::ValidityRequirement; use rustc_middle::ty::{ self, AdtDef, AliasTy, AssocItem, AssocTag, Binder, BoundRegion, BoundVarIndexKind, FnSig, GenericArg, GenericArgKind, GenericArgsRef, IntTy, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, - TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr, - Unnormalized, + TypeVisitableExt, TypeVisitor, UintTy, Unnormalized, Upcast, VariantDef, VariantDiscr, }; use rustc_span::symbol::Ident; use rustc_span::{DUMMY_SP, Span, Symbol}; @@ -111,7 +110,9 @@ pub fn contains_ty_adt_constructor_opaque<'tcx>(cx: &LateContext<'tcx>, ty: Ty<' return false; } - for (predicate, _span) in cx.tcx.explicit_item_self_bounds(def_id) + for (predicate, _span) in cx + .tcx + .explicit_item_self_bounds(def_id) .iter_identity_copied() .map(Unnormalized::skip_norm_wip) { @@ -610,7 +611,10 @@ impl<'tcx> ExprFnSig<'tcx> { /// If the expression is function like, get the signature for it. pub fn expr_sig<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option> { if let Res::Def(DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn, id) = expr.res(cx) { - Some(ExprFnSig::Sig(cx.tcx.fn_sig(id).instantiate_identity().skip_norm_wip(), Some(id))) + Some(ExprFnSig::Sig( + cx.tcx.fn_sig(id).instantiate_identity().skip_norm_wip(), + Some(id), + )) } else { ty_sig(cx, cx.typeck_results().expr_ty_adjusted(expr).peel_refs()) } @@ -628,7 +632,10 @@ pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option Some(ExprFnSig::Sig(cx.tcx.fn_sig(id).instantiate(cx.tcx, subs).skip_norm_wip(), Some(id))), + ty::FnDef(id, subs) => Some(ExprFnSig::Sig( + cx.tcx.fn_sig(id).instantiate(cx.tcx, subs).skip_norm_wip(), + Some(id), + )), ty::Alias(AliasTy { kind: ty::Opaque { def_id }, args, @@ -636,7 +643,10 @@ pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option sig_from_bounds( cx, ty, - cx.tcx.item_self_bounds(def_id).iter_instantiated(cx.tcx, args).map(Unnormalized::skip_norm_wip), + cx.tcx + .item_self_bounds(def_id) + .iter_instantiated(cx.tcx, args) + .map(Unnormalized::skip_norm_wip), cx.tcx.opt_parent(def_id), ), ty::FnPtr(sig_tys, hdr) => Some(ExprFnSig::Sig(sig_tys.with(hdr), None)), @@ -662,7 +672,10 @@ pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option match cx.tcx.try_normalize_erasing_regions(cx.typing_env(), Unnormalized::new_wip(ty)) { + ) => match cx + .tcx + .try_normalize_erasing_regions(cx.typing_env(), Unnormalized::new_wip(ty)) + { Ok(normalized_ty) if normalized_ty != ty => ty_sig(cx, normalized_ty), _ => sig_for_projection(cx, proj).or_else(|| sig_from_bounds(cx, ty, cx.param_env.caller_bounds(), None)), }, @@ -892,7 +905,13 @@ pub fn adt_and_variant_of_res<'tcx>(cx: &LateContext<'tcx>, res: Res) -> Option< Some((adt, adt.variant_with_id(var_id))) }, Res::SelfCtor(id) => { - let adt = cx.tcx.type_of(id).instantiate_identity().skip_norm_wip().ty_adt_def().unwrap(); + let adt = cx + .tcx + .type_of(id) + .instantiate_identity() + .skip_norm_wip() + .ty_adt_def() + .unwrap(); Some((adt, adt.non_enum_variant())) }, _ => None, @@ -1074,7 +1093,7 @@ pub fn make_normalized_projection<'tcx>( } match tcx.try_normalize_erasing_regions( typing_env, - Unnormalized::new_wip(Ty::new_projection_from_args(tcx, ty.kind.def_id(), ty.args)) + Unnormalized::new_wip(Ty::new_projection_from_args(tcx, ty.kind.def_id(), ty.args)), ) { Ok(ty) => Some(ty), Err(e) => { @@ -1180,7 +1199,10 @@ impl<'tcx> InteriorMut<'tcx> { ty::Alias(AliasTy { kind: ty::Projection { .. }, .. - }) => match cx.tcx.try_normalize_erasing_regions(cx.typing_env(), Unnormalized::new_wip(ty)) { + }) => match cx + .tcx + .try_normalize_erasing_regions(cx.typing_env(), Unnormalized::new_wip(ty)) + { Ok(normalized_ty) if ty != normalized_ty => self.interior_mut_ty_chain_inner(cx, normalized_ty, depth), _ => None, }, @@ -1323,7 +1345,9 @@ pub fn option_arg_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option(cx: &LateContext<'tcx>, iter_ty: Ty<'tcx>) -> bool { fn normalize_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { - cx.tcx.try_normalize_erasing_regions(cx.typing_env(), Unnormalized::new_wip(ty)).unwrap_or(ty) + cx.tcx + .try_normalize_erasing_regions(cx.typing_env(), Unnormalized::new_wip(ty)) + .unwrap_or(ty) } /// Check if `ty` contains mutable references or equivalent, which includes: diff --git a/src/tools/clippy/lintcheck/src/main.rs b/src/tools/clippy/lintcheck/src/main.rs index b30df79023784..6a9e6cab8957b 100644 --- a/src/tools/clippy/lintcheck/src/main.rs +++ b/src/tools/clippy/lintcheck/src/main.rs @@ -85,12 +85,12 @@ impl Crate { if config.max_jobs == 1 { println!( "{index}/{total_crates_to_lint} {perc}% Linting {} {}", - &self.name, &self.version + self.name, self.version ); } else { println!( "{index}/{total_crates_to_lint} {perc}% Linting {} {} in target dir {thread_index:?}", - &self.name, &self.version + self.name, self.version ); } diff --git a/src/tools/clippy/lintcheck/src/output.rs b/src/tools/clippy/lintcheck/src/output.rs index 1ecc3f7c24943..dfba1804642eb 100644 --- a/src/tools/clippy/lintcheck/src/output.rs +++ b/src/tools/clippy/lintcheck/src/output.rs @@ -228,8 +228,8 @@ fn print_stats(old_stats: HashMap, new_stats: HashMap<&String, us // remove duplicates from both hashmaps for (k, v) in &same_in_both_hashmaps { - assert!(old_stats_deduped.remove(k) == Some(*v)); - assert!(new_stats_deduped.remove(k) == Some(*v)); + assert_eq!(old_stats_deduped.remove(k), Some(*v)); + assert_eq!(new_stats_deduped.remove(k), Some(*v)); } println!("\nStats:"); diff --git a/src/tools/clippy/rust-toolchain.toml b/src/tools/clippy/rust-toolchain.toml index 97c8cf260cad5..9992299153e21 100644 --- a/src/tools/clippy/rust-toolchain.toml +++ b/src/tools/clippy/rust-toolchain.toml @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2026-04-16" +channel = "nightly-2026-04-30" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" diff --git a/src/tools/clippy/tests/ui-cargo/module_style/inline_mod/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/module_style/inline_mod/Cargo.stderr new file mode 100644 index 0000000000000..e3f18e37db9a2 --- /dev/null +++ b/src/tools/clippy/tests/ui-cargo/module_style/inline_mod/Cargo.stderr @@ -0,0 +1,71 @@ +error: inline module found + --> src/other.rs:1:1 + | +1 | mod foo {} + | ^^^^^^^^^^ + | + = help: move the contents of the module to `src/other/foo.rs` or `src/other/foo/mod.rs`, and replace this with `mod foo;` + = note: `-D clippy::inline-modules` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::inline_modules)]` + +error: inline module found + --> src/qux/foo.rs:1:1 + | +1 | mod bar {} + | ^^^^^^^^^^ + | + = help: move the contents of the module to `src/qux/foo/bar.rs` or `src/qux/foo/bar/mod.rs`, and replace this with `mod bar;` + +error: inline module found + --> src/lib.rs:9:1 + | + 9 | / pub mod test_nested_inline_mods { +10 | | mod bar { +11 | | mod baz {} +12 | | } +13 | | } + | |_^ + | + = help: move the contents of the module to `src/test_nested_inline_mods.rs` or `src/test_nested_inline_mods/mod.rs`, and replace this with `pub mod test_nested_inline_mods;` + +error: inline module found + --> src/lib.rs:10:5 + | +10 | / mod bar { +11 | | mod baz {} +12 | | } + | |_____^ + | + = help: move the contents of the module to `src/test_nested_inline_mods/bar.rs` or `src/test_nested_inline_mods/bar/mod.rs`, and replace this with `mod bar;` + +error: inline module found + --> src/lib.rs:11:9 + | +11 | mod baz {} + | ^^^^^^^^^^ + | + = help: move the contents of the module to `src/test_nested_inline_mods/bar/baz.rs` or `src/test_nested_inline_mods/bar/baz/mod.rs`, and replace this with `mod baz;` + +error: inline module found + --> src/lib.rs:20:1 + | +20 | / mod partially_escaped_test_mod { +21 | | #[cfg(test)] +22 | | mod tests { +23 | | mod bar {} +24 | | } +25 | | mod baz {} +26 | | } + | |_^ + | + = help: move the contents of the module to `src/partially_escaped_test_mod.rs` or `src/partially_escaped_test_mod/mod.rs`, and replace this with `mod partially_escaped_test_mod;` + +error: inline module found + --> src/lib.rs:25:5 + | +25 | mod baz {} + | ^^^^^^^^^^ + | + = help: move the contents of the module to `src/partially_escaped_test_mod/baz.rs` or `src/partially_escaped_test_mod/baz/mod.rs`, and replace this with `mod baz;` + +error: could not compile `inline-mod` (lib) due to 7 previous errors diff --git a/src/tools/clippy/tests/ui-cargo/module_style/inline_mod/Cargo.toml b/src/tools/clippy/tests/ui-cargo/module_style/inline_mod/Cargo.toml new file mode 100644 index 0000000000000..4108d8aaa5c0d --- /dev/null +++ b/src/tools/clippy/tests/ui-cargo/module_style/inline_mod/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "inline-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/src/tools/clippy/tests/ui-cargo/module_style/inline_mod/src/foo.rs b/src/tools/clippy/tests/ui-cargo/module_style/inline_mod/src/foo.rs new file mode 100644 index 0000000000000..9e19e6e44fddc --- /dev/null +++ b/src/tools/clippy/tests/ui-cargo/module_style/inline_mod/src/foo.rs @@ -0,0 +1,2 @@ +/// Lint shouldn't fire because parent mod has a path attribute. +mod foo {} diff --git a/src/tools/clippy/tests/ui-cargo/module_style/inline_mod/src/lib.rs b/src/tools/clippy/tests/ui-cargo/module_style/inline_mod/src/lib.rs new file mode 100644 index 0000000000000..df5816e251dc1 --- /dev/null +++ b/src/tools/clippy/tests/ui-cargo/module_style/inline_mod/src/lib.rs @@ -0,0 +1,34 @@ +#![warn(clippy::inline_modules)] + +pub mod other; +#[path = "qux/mod.rs"] +pub mod something; +#[path = "foo.rs"] +pub mod stuff; + +pub mod test_nested_inline_mods { + mod bar { + mod baz {} + } +} + +#[cfg(test)] +mod escaped_test_mod { + mod bar {} +} + +mod partially_escaped_test_mod { + #[cfg(test)] + mod tests { + mod bar {} + } + mod baz {} +} + +macro_rules! inline_mod_from_expansion { + () => { + mod _foo {} + }; +} + +inline_mod_from_expansion!(); diff --git a/src/tools/clippy/tests/ui-cargo/module_style/inline_mod/src/other.rs b/src/tools/clippy/tests/ui-cargo/module_style/inline_mod/src/other.rs new file mode 100644 index 0000000000000..e5736aedfdc7c --- /dev/null +++ b/src/tools/clippy/tests/ui-cargo/module_style/inline_mod/src/other.rs @@ -0,0 +1 @@ +mod foo {} diff --git a/src/tools/clippy/tests/ui-cargo/module_style/inline_mod/src/qux/foo.rs b/src/tools/clippy/tests/ui-cargo/module_style/inline_mod/src/qux/foo.rs new file mode 100644 index 0000000000000..e4cdd207c6635 --- /dev/null +++ b/src/tools/clippy/tests/ui-cargo/module_style/inline_mod/src/qux/foo.rs @@ -0,0 +1 @@ +mod bar {} diff --git a/src/tools/clippy/tests/ui-cargo/module_style/inline_mod/src/qux/mod.rs b/src/tools/clippy/tests/ui-cargo/module_style/inline_mod/src/qux/mod.rs new file mode 100644 index 0000000000000..f4ad3bff5c973 --- /dev/null +++ b/src/tools/clippy/tests/ui-cargo/module_style/inline_mod/src/qux/mod.rs @@ -0,0 +1 @@ +mod foo; diff --git a/src/tools/clippy/tests/ui/assertions_on_constants.rs b/src/tools/clippy/tests/ui/assertions_on_constants.rs index 1c49b6e6b7b11..44bcc5c724e97 100644 --- a/src/tools/clippy/tests/ui/assertions_on_constants.rs +++ b/src/tools/clippy/tests/ui/assertions_on_constants.rs @@ -1,4 +1,5 @@ #![allow(non_fmt_panics, clippy::needless_bool, clippy::eq_op)] +#![expect(clippy::manual_assert_eq)] macro_rules! assert_const { ($len:expr) => { diff --git a/src/tools/clippy/tests/ui/assertions_on_constants.stderr b/src/tools/clippy/tests/ui/assertions_on_constants.stderr index a996c41b69420..abace40735edd 100644 --- a/src/tools/clippy/tests/ui/assertions_on_constants.stderr +++ b/src/tools/clippy/tests/ui/assertions_on_constants.stderr @@ -1,5 +1,5 @@ error: this assertion is always `true` - --> tests/ui/assertions_on_constants.rs:10:5 + --> tests/ui/assertions_on_constants.rs:11:5 | LL | assert!(true); | ^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | assert!(true); = help: to override `-D warnings` add `#[allow(clippy::assertions_on_constants)]` error: this assertion is always `false` - --> tests/ui/assertions_on_constants.rs:13:5 + --> tests/ui/assertions_on_constants.rs:14:5 | LL | assert!(false); | ^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | assert!(false); = help: replace this with `panic!()` or `unreachable!()` error: this assertion is always `true` - --> tests/ui/assertions_on_constants.rs:16:5 + --> tests/ui/assertions_on_constants.rs:17:5 | LL | assert!(true, "true message"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL | assert!(true, "true message"); = help: remove the assertion error: this assertion is always `false` - --> tests/ui/assertions_on_constants.rs:19:5 + --> tests/ui/assertions_on_constants.rs:20:5 | LL | assert!(false, "false message"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -33,7 +33,7 @@ LL | assert!(false, "false message"); = help: replace this with `panic!()` or `unreachable!()` error: this assertion is always `false` - --> tests/ui/assertions_on_constants.rs:23:5 + --> tests/ui/assertions_on_constants.rs:24:5 | LL | assert!(false, "{}", msg.to_uppercase()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -41,7 +41,7 @@ LL | assert!(false, "{}", msg.to_uppercase()); = help: replace this with `panic!()` or `unreachable!()` error: this assertion has a constant value - --> tests/ui/assertions_on_constants.rs:27:5 + --> tests/ui/assertions_on_constants.rs:28:5 | LL | assert!(B); | ^^^^^^^^^^ @@ -49,7 +49,7 @@ LL | assert!(B); = help: consider moving this into a const block: `const { assert!(..) }` error: this assertion has a constant value - --> tests/ui/assertions_on_constants.rs:31:5 + --> tests/ui/assertions_on_constants.rs:32:5 | LL | assert!(C); | ^^^^^^^^^^ @@ -57,7 +57,7 @@ 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:34:5 + --> tests/ui/assertions_on_constants.rs:35:5 | LL | assert!(C, "C message"); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -65,7 +65,7 @@ LL | assert!(C, "C message"); = help: consider moving this into a const block: `const { assert!(..) }` error: this assertion is always `true` - --> tests/ui/assertions_on_constants.rs:37:5 + --> tests/ui/assertions_on_constants.rs:38:5 | LL | debug_assert!(true); | ^^^^^^^^^^^^^^^^^^^ @@ -73,7 +73,7 @@ LL | debug_assert!(true); = help: remove the assertion error: this assertion has a constant value - --> tests/ui/assertions_on_constants.rs:45:5 + --> tests/ui/assertions_on_constants.rs:46:5 | LL | assert!(cfg!(feature = "hey") || cfg!(not(feature = "asdf"))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -81,7 +81,7 @@ 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:54:19 + --> tests/ui/assertions_on_constants.rs:55:19 | LL | const _: () = assert!(true); | ^^^^^^^^^^^^^ @@ -89,7 +89,7 @@ LL | const _: () = assert!(true); = help: remove the assertion error: this assertion is always `true` - --> tests/ui/assertions_on_constants.rs:57:5 + --> tests/ui/assertions_on_constants.rs:58:5 | LL | assert!(8 == (7 + 1)); | ^^^^^^^^^^^^^^^^^^^^^ @@ -97,7 +97,7 @@ LL | assert!(8 == (7 + 1)); = help: remove the assertion error: this assertion is always `true` - --> tests/ui/assertions_on_constants.rs:68:5 + --> tests/ui/assertions_on_constants.rs:69:5 | LL | assert!(true); | ^^^^^^^^^^^^^ @@ -105,7 +105,7 @@ LL | assert!(true); = help: remove the assertion error: this assertion is always `true` - --> tests/ui/assertions_on_constants.rs:71:5 + --> tests/ui/assertions_on_constants.rs:72:5 | LL | assert!(8 == (7 + 1)); | ^^^^^^^^^^^^^^^^^^^^^ @@ -113,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:79:5 + --> tests/ui/assertions_on_constants.rs:80:5 | LL | assert!(C); | ^^^^^^^^^^ @@ -121,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:90:5 + --> tests/ui/assertions_on_constants.rs:91:5 | LL | assert!(C); | ^^^^^^^^^^ @@ -129,7 +129,7 @@ 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:96:5 + --> tests/ui/assertions_on_constants.rs:97:5 | LL | assert!(C); | ^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/bind_instead_of_map.fixed b/src/tools/clippy/tests/ui/bind_instead_of_map.fixed index a1c4cb5a4823a..b1c83970ae9fe 100644 --- a/src/tools/clippy/tests/ui/bind_instead_of_map.fixed +++ b/src/tools/clippy/tests/ui/bind_instead_of_map.fixed @@ -26,3 +26,9 @@ pub fn foo() -> Option { pub fn example2(x: bool) -> Option<&'static str> { Some("a").and_then(|s| Some(if x { s } else { return None })) } + +fn issue16861(b: bool, res: Result) { + let _: Result = res.or_else(|_| panic!("should not happen")); + let _: Result = res.map_err(|_| if b { panic!("should not happen") } else { 42 }); + //~^ bind_instead_of_map +} diff --git a/src/tools/clippy/tests/ui/bind_instead_of_map.rs b/src/tools/clippy/tests/ui/bind_instead_of_map.rs index 1308fa9f416be..65e7e490a8901 100644 --- a/src/tools/clippy/tests/ui/bind_instead_of_map.rs +++ b/src/tools/clippy/tests/ui/bind_instead_of_map.rs @@ -26,3 +26,9 @@ pub fn foo() -> Option { pub fn example2(x: bool) -> Option<&'static str> { Some("a").and_then(|s| Some(if x { s } else { return None })) } + +fn issue16861(b: bool, res: Result) { + let _: Result = res.or_else(|_| panic!("should not happen")); + let _: Result = res.or_else(|_| if b { panic!("should not happen") } else { Err(42) }); + //~^ bind_instead_of_map +} diff --git a/src/tools/clippy/tests/ui/bind_instead_of_map.stderr b/src/tools/clippy/tests/ui/bind_instead_of_map.stderr index 08f85fb58549c..1e2b93adffade 100644 --- a/src/tools/clippy/tests/ui/bind_instead_of_map.stderr +++ b/src/tools/clippy/tests/ui/bind_instead_of_map.stderr @@ -22,5 +22,17 @@ error: using `Result.and_then(Ok)`, which is a no-op LL | let _ = x.and_then(Ok); | ^^^^^^^^^^^^^^ help: use the expression directly: `x` -error: aborting due to 3 previous errors +error: using `Result.or_else(|x| Err(y))`, which is more succinctly expressed as `map_err(|x| y)` + --> tests/ui/bind_instead_of_map.rs:32:31 + | +LL | let _: Result = res.or_else(|_| if b { panic!("should not happen") } else { Err(42) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `map_err` instead + | +LL - let _: Result = res.or_else(|_| if b { panic!("should not happen") } else { Err(42) }); +LL + let _: Result = res.map_err(|_| if b { panic!("should not happen") } else { 42 }); + | + +error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui/bit_masks.rs b/src/tools/clippy/tests/ui/bit_masks.rs index bca5b2ec34e12..30dd8f912acfd 100644 --- a/src/tools/clippy/tests/ui/bit_masks.rs +++ b/src/tools/clippy/tests/ui/bit_masks.rs @@ -99,3 +99,25 @@ mod issue16781 { x & 0x70 == 0x11 << 4 } } + +mod issue16935 { + struct Wrapper(usize); + + impl std::ops::BitAnd for Wrapper { + type Output = Self; + + fn bitand(self, rhs: usize) -> Self::Output { + Self(self.0 & rhs) + } + } + + impl PartialEq for Wrapper { + fn eq(&self, other: &usize) -> bool { + self.0.eq(other) + } + } + + fn check(value: Wrapper) -> bool { + value & 0x1 != 0 + } +} diff --git a/src/tools/clippy/tests/ui/cloned_ref_to_slice_refs.fixed b/src/tools/clippy/tests/ui/cloned_ref_to_slice_refs.fixed index 818c6e23259e3..5b39a31538096 100644 --- a/src/tools/clippy/tests/ui/cloned_ref_to_slice_refs.fixed +++ b/src/tools/clippy/tests/ui/cloned_ref_to_slice_refs.fixed @@ -1,3 +1,4 @@ +#![allow(clippy::borrow_deref_ref)] #![warn(clippy::cloned_ref_to_slice_refs)] #[derive(Clone)] @@ -7,18 +8,18 @@ fn main() { { let data = Data; let data_ref = &data; - let _ = std::slice::from_ref(data_ref); //~ ERROR: this call to `clone` can be replaced with `std::slice::from_ref` + let _ = std::slice::from_ref(data_ref); //~ cloned_ref_to_slice_refs } { - let _ = std::slice::from_ref(&Data); //~ ERROR: this call to `clone` can be replaced with `std::slice::from_ref` + let _ = std::slice::from_ref(&Data); //~ cloned_ref_to_slice_refs } { #[derive(Clone)] struct Point(i32, i32); - let _ = std::slice::from_ref(&Point(0, 0)); //~ ERROR: this call to `clone` can be replaced with `std::slice::from_ref` + let _ = std::slice::from_ref(&Point(0, 0)); //~ cloned_ref_to_slice_refs } // the string was cloned with the intention to not mutate @@ -62,3 +63,76 @@ fn main() { let _ = &[data_1.clone(), data_2.clone()]; } } + +fn issue16320(items: &[String]) { + use std::ffi::OsString; + use std::ops::Deref; + use std::path::PathBuf; + + let _a = String::new(); + let _b = std::slice::from_ref(&_a); + //~^ cloned_ref_to_slice_refs + let _c = std::slice::from_ref(&_a); + //~^ cloned_ref_to_slice_refs + + let _a = OsString::new(); + let _b = std::slice::from_ref(&_a); + //~^ cloned_ref_to_slice_refs + + let _a = PathBuf::new(); + let _b = std::slice::from_ref(&_a); + //~^ cloned_ref_to_slice_refs + + let _a = &PathBuf::new(); + let _b = std::slice::from_ref(_a); + //~^ cloned_ref_to_slice_refs + + #[derive(Clone)] + struct A(i32); + + impl std::fmt::Display for A { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } + } + + let a = A(42); + _ = &[a.to_string()]; + + struct Wrapper(T); + impl Deref for Wrapper { + type Target = T; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + let w = Wrapper(String::from("hello")); + let w = Wrapper(w); + let _b = std::slice::from_ref(&**w); + //~^ cloned_ref_to_slice_refs + + let w = Wrapper(&PathBuf::new()); + let w = Wrapper(w); + let _b = std::slice::from_ref(&***w); + //~^ cloned_ref_to_slice_refs +} + +fn wrongly_unmangled_macros(items: &[String]) { + use std::path::PathBuf; + + struct Wrapper { + inner: PathBuf, + } + + let _a = Wrapper { inner: PathBuf::new() }; + + macro_rules! accessor { + ($e:expr) => { + $e.inner + }; + } + + let _d = std::slice::from_ref(&accessor!(_a)); + //~^ cloned_ref_to_slice_refs +} diff --git a/src/tools/clippy/tests/ui/cloned_ref_to_slice_refs.rs b/src/tools/clippy/tests/ui/cloned_ref_to_slice_refs.rs index 9517dbfd1569c..ce9f9a1151177 100644 --- a/src/tools/clippy/tests/ui/cloned_ref_to_slice_refs.rs +++ b/src/tools/clippy/tests/ui/cloned_ref_to_slice_refs.rs @@ -1,3 +1,4 @@ +#![allow(clippy::borrow_deref_ref)] #![warn(clippy::cloned_ref_to_slice_refs)] #[derive(Clone)] @@ -7,18 +8,18 @@ fn main() { { let data = Data; let data_ref = &data; - let _ = &[data_ref.clone()]; //~ ERROR: this call to `clone` can be replaced with `std::slice::from_ref` + let _ = &[data_ref.clone()]; //~ cloned_ref_to_slice_refs } { - let _ = &[Data.clone()]; //~ ERROR: this call to `clone` can be replaced with `std::slice::from_ref` + let _ = &[Data.clone()]; //~ cloned_ref_to_slice_refs } { #[derive(Clone)] struct Point(i32, i32); - let _ = &[Point(0, 0).clone()]; //~ ERROR: this call to `clone` can be replaced with `std::slice::from_ref` + let _ = &[Point(0, 0).clone()]; //~ cloned_ref_to_slice_refs } // the string was cloned with the intention to not mutate @@ -62,3 +63,76 @@ fn main() { let _ = &[data_1.clone(), data_2.clone()]; } } + +fn issue16320(items: &[String]) { + use std::ffi::OsString; + use std::ops::Deref; + use std::path::PathBuf; + + let _a = String::new(); + let _b = &[_a.to_owned()]; + //~^ cloned_ref_to_slice_refs + let _c = &[_a.to_string()]; + //~^ cloned_ref_to_slice_refs + + let _a = OsString::new(); + let _b = &[_a.to_os_string()]; + //~^ cloned_ref_to_slice_refs + + let _a = PathBuf::new(); + let _b = &[_a.to_path_buf()]; + //~^ cloned_ref_to_slice_refs + + let _a = &PathBuf::new(); + let _b = &[_a.to_path_buf()]; + //~^ cloned_ref_to_slice_refs + + #[derive(Clone)] + struct A(i32); + + impl std::fmt::Display for A { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } + } + + let a = A(42); + _ = &[a.to_string()]; + + struct Wrapper(T); + impl Deref for Wrapper { + type Target = T; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + let w = Wrapper(String::from("hello")); + let w = Wrapper(w); + let _b = &[w.to_string()]; + //~^ cloned_ref_to_slice_refs + + let w = Wrapper(&PathBuf::new()); + let w = Wrapper(w); + let _b = &[w.to_path_buf()]; + //~^ cloned_ref_to_slice_refs +} + +fn wrongly_unmangled_macros(items: &[String]) { + use std::path::PathBuf; + + struct Wrapper { + inner: PathBuf, + } + + let _a = Wrapper { inner: PathBuf::new() }; + + macro_rules! accessor { + ($e:expr) => { + $e.inner + }; + } + + let _d = &[accessor!(_a).to_path_buf()]; + //~^ cloned_ref_to_slice_refs +} diff --git a/src/tools/clippy/tests/ui/cloned_ref_to_slice_refs.stderr b/src/tools/clippy/tests/ui/cloned_ref_to_slice_refs.stderr index 6a31d87823955..a37438147b524 100644 --- a/src/tools/clippy/tests/ui/cloned_ref_to_slice_refs.stderr +++ b/src/tools/clippy/tests/ui/cloned_ref_to_slice_refs.stderr @@ -1,5 +1,5 @@ -error: this call to `clone` can be replaced with `std::slice::from_ref` - --> tests/ui/cloned_ref_to_slice_refs.rs:10:17 +error: unnecessary use of `clone` to create a slice from a reference + --> tests/ui/cloned_ref_to_slice_refs.rs:11:17 | LL | let _ = &[data_ref.clone()]; | ^^^^^^^^^^^^^^^^^^^ help: try: `std::slice::from_ref(data_ref)` @@ -7,17 +7,65 @@ LL | let _ = &[data_ref.clone()]; = note: `-D clippy::cloned-ref-to-slice-refs` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::cloned_ref_to_slice_refs)]` -error: this call to `clone` can be replaced with `std::slice::from_ref` - --> tests/ui/cloned_ref_to_slice_refs.rs:14:17 +error: unnecessary use of `clone` to create a slice from a reference + --> tests/ui/cloned_ref_to_slice_refs.rs:15:17 | LL | let _ = &[Data.clone()]; | ^^^^^^^^^^^^^^^ help: try: `std::slice::from_ref(&Data)` -error: this call to `clone` can be replaced with `std::slice::from_ref` - --> tests/ui/cloned_ref_to_slice_refs.rs:21:17 +error: unnecessary use of `clone` to create a slice from a reference + --> tests/ui/cloned_ref_to_slice_refs.rs:22:17 | LL | let _ = &[Point(0, 0).clone()]; | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::slice::from_ref(&Point(0, 0))` -error: aborting due to 3 previous errors +error: unnecessary use of `to_owned` to create a slice from a reference + --> tests/ui/cloned_ref_to_slice_refs.rs:73:14 + | +LL | let _b = &[_a.to_owned()]; + | ^^^^^^^^^^^^^^^^ help: try: `std::slice::from_ref(&_a)` + +error: unnecessary use of `to_string` to create a slice from a reference + --> tests/ui/cloned_ref_to_slice_refs.rs:75:14 + | +LL | let _c = &[_a.to_string()]; + | ^^^^^^^^^^^^^^^^^ help: try: `std::slice::from_ref(&_a)` + +error: unnecessary use of `to_os_string` to create a slice from a reference + --> tests/ui/cloned_ref_to_slice_refs.rs:79:14 + | +LL | let _b = &[_a.to_os_string()]; + | ^^^^^^^^^^^^^^^^^^^^ help: try: `std::slice::from_ref(&_a)` + +error: unnecessary use of `to_path_buf` to create a slice from a reference + --> tests/ui/cloned_ref_to_slice_refs.rs:83:14 + | +LL | let _b = &[_a.to_path_buf()]; + | ^^^^^^^^^^^^^^^^^^^ help: try: `std::slice::from_ref(&_a)` + +error: unnecessary use of `to_path_buf` to create a slice from a reference + --> tests/ui/cloned_ref_to_slice_refs.rs:87:14 + | +LL | let _b = &[_a.to_path_buf()]; + | ^^^^^^^^^^^^^^^^^^^ help: try: `std::slice::from_ref(_a)` + +error: unnecessary use of `to_string` to create a slice from a reference + --> tests/ui/cloned_ref_to_slice_refs.rs:112:14 + | +LL | let _b = &[w.to_string()]; + | ^^^^^^^^^^^^^^^^ help: try: `std::slice::from_ref(&**w)` + +error: unnecessary use of `to_path_buf` to create a slice from a reference + --> tests/ui/cloned_ref_to_slice_refs.rs:117:14 + | +LL | let _b = &[w.to_path_buf()]; + | ^^^^^^^^^^^^^^^^^^ help: try: `std::slice::from_ref(&***w)` + +error: unnecessary use of `to_path_buf` to create a slice from a reference + --> tests/ui/cloned_ref_to_slice_refs.rs:136:14 + | +LL | let _d = &[accessor!(_a).to_path_buf()]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::slice::from_ref(&accessor!(_a))` + +error: aborting due to 11 previous errors diff --git a/src/tools/clippy/tests/ui/cmp_null.fixed b/src/tools/clippy/tests/ui/cmp_null.fixed index 4a0ee439e94aa..1b49549e9712c 100644 --- a/src/tools/clippy/tests/ui/cmp_null.fixed +++ b/src/tools/clippy/tests/ui/cmp_null.fixed @@ -1,4 +1,5 @@ #![warn(clippy::cmp_null)] +#![allow(clippy::manual_assert_eq)] use std::ptr; diff --git a/src/tools/clippy/tests/ui/cmp_null.rs b/src/tools/clippy/tests/ui/cmp_null.rs index 26ea8960e5fb6..35de3a14afc6f 100644 --- a/src/tools/clippy/tests/ui/cmp_null.rs +++ b/src/tools/clippy/tests/ui/cmp_null.rs @@ -1,4 +1,5 @@ #![warn(clippy::cmp_null)] +#![allow(clippy::manual_assert_eq)] use std::ptr; diff --git a/src/tools/clippy/tests/ui/cmp_null.stderr b/src/tools/clippy/tests/ui/cmp_null.stderr index 51b98d2a2320d..9831b4f6eb32b 100644 --- a/src/tools/clippy/tests/ui/cmp_null.stderr +++ b/src/tools/clippy/tests/ui/cmp_null.stderr @@ -1,5 +1,5 @@ error: comparing with null is better expressed by the `.is_null()` method - --> tests/ui/cmp_null.rs:8:8 + --> tests/ui/cmp_null.rs:9:8 | LL | if p == ptr::null() { | ^^^^^^^^^^^^^^^^ help: try: `p.is_null()` @@ -8,37 +8,37 @@ LL | if p == ptr::null() { = help: to override `-D warnings` add `#[allow(clippy::cmp_null)]` error: comparing with null is better expressed by the `.is_null()` method - --> tests/ui/cmp_null.rs:13:8 + --> tests/ui/cmp_null.rs:14:8 | LL | if ptr::null() == p { | ^^^^^^^^^^^^^^^^ help: try: `p.is_null()` error: comparing with null is better expressed by the `.is_null()` method - --> tests/ui/cmp_null.rs:21:8 + --> tests/ui/cmp_null.rs:22:8 | LL | if m == ptr::null_mut() { | ^^^^^^^^^^^^^^^^^^^^ help: try: `m.is_null()` error: comparing with null is better expressed by the `.is_null()` method - --> tests/ui/cmp_null.rs:26:8 + --> tests/ui/cmp_null.rs:27:8 | LL | if ptr::null_mut() == m { | ^^^^^^^^^^^^^^^^^^^^ help: try: `m.is_null()` error: comparing with null is better expressed by the `.is_null()` method - --> tests/ui/cmp_null.rs:32:13 + --> tests/ui/cmp_null.rs:33:13 | LL | let _ = x as *const () == ptr::null(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(x as *const ()).is_null()` error: comparing with null is better expressed by the `.is_null()` method - --> tests/ui/cmp_null.rs:38:19 + --> tests/ui/cmp_null.rs:39:19 | LL | debug_assert!(f != std::ptr::null_mut()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `!f.is_null()` error: comparing with null is better expressed by the `.is_null()` method - --> tests/ui/cmp_null.rs:56:8 + --> tests/ui/cmp_null.rs:57:8 | LL | if dot_value!(x) == ptr::null() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `dot_value!(x).is_null()` diff --git a/src/tools/clippy/tests/ui/collapsible_match.rs b/src/tools/clippy/tests/ui/collapsible_match.rs index 98f2fcfdf479f..51cb0168c735b 100644 --- a/src/tools/clippy/tests/ui/collapsible_match.rs +++ b/src/tools/clippy/tests/ui/collapsible_match.rs @@ -390,6 +390,26 @@ fn take(t: T) {} fn main() {} +// https://github.com/rust-lang/rust-clippy/issues/16875 +// Adding a match guard allows fall-through to subsequent arms, which changes semantics +// when non-wildcard arms follow the arm being collapsed. +fn issue16875(a: Option<&str>, b: i32) -> i32 { + let mut res = 0; + // should NOT lint: `_ if b == 1` is not wild-like (has a guard), so collapsing + // `Some(_)` into `Some(_) if b == 0` would let `_ if b == 1` match Some values + // that previously fell through to the no-op arm body. + match a { + Some(_) => { + if b == 0 { + res = 1; + } + }, + _ if b == 1 => res = 2, + _ => {}, + } + res +} + fn issue16705(x: Option) { fn takes_ownership(s: String) -> bool { true diff --git a/src/tools/clippy/tests/ui/collapsible_match_fixable.fixed b/src/tools/clippy/tests/ui/collapsible_match_fixable.fixed index db76530aee144..8e5934d3693a3 100644 --- a/src/tools/clippy/tests/ui/collapsible_match_fixable.fixed +++ b/src/tools/clippy/tests/ui/collapsible_match_fixable.fixed @@ -28,3 +28,18 @@ fn issue16558() { _ => 1, }; } + +// https://github.com/rust-lang/rust-clippy/issues/16875 +// lint still fires when only wildcard-like arms follow (fall-through is harmless) +fn issue16875(a: Option<&str>, b: i32) -> i32 { + let mut res = 0; + match a { + Some(_) + if b == 0 => { + //~^ collapsible_match + res = 1; + }, + _ => {}, + } + res +} diff --git a/src/tools/clippy/tests/ui/collapsible_match_fixable.rs b/src/tools/clippy/tests/ui/collapsible_match_fixable.rs index 94bf1d6bfdfab..e2ccae4559612 100644 --- a/src/tools/clippy/tests/ui/collapsible_match_fixable.rs +++ b/src/tools/clippy/tests/ui/collapsible_match_fixable.rs @@ -29,3 +29,19 @@ fn issue16558() { _ => 1, }; } + +// https://github.com/rust-lang/rust-clippy/issues/16875 +// lint still fires when only wildcard-like arms follow (fall-through is harmless) +fn issue16875(a: Option<&str>, b: i32) -> i32 { + let mut res = 0; + match a { + Some(_) => { + if b == 0 { + //~^ collapsible_match + res = 1; + } + }, + _ => {}, + } + res +} diff --git a/src/tools/clippy/tests/ui/collapsible_match_fixable.stderr b/src/tools/clippy/tests/ui/collapsible_match_fixable.stderr index 4d501cbd0993d..0be47076fa472 100644 --- a/src/tools/clippy/tests/ui/collapsible_match_fixable.stderr +++ b/src/tools/clippy/tests/ui/collapsible_match_fixable.stderr @@ -46,5 +46,23 @@ LL | LL ~ , | -error: aborting due to 3 previous errors +error: this `if` can be collapsed into the outer `match` + --> tests/ui/collapsible_match_fixable.rs:39:13 + | +LL | / if b == 0 { +LL | | +LL | | res = 1; +LL | | } + | |_____________^ + | +help: collapse nested if block + | +LL ~ Some(_) +LL ~ if b == 0 => { +LL | +LL | res = 1; +LL ~ }, + | + +error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui/explicit_deref_methods.fixed b/src/tools/clippy/tests/ui/explicit_deref_methods.fixed index 6c29630dc3a57..35149755a34be 100644 --- a/src/tools/clippy/tests/ui/explicit_deref_methods.fixed +++ b/src/tools/clippy/tests/ui/explicit_deref_methods.fixed @@ -3,14 +3,15 @@ #![allow(unused_variables, unused_must_use)] #![allow( clippy::borrow_deref_ref, - suspicious_double_ref_op, - noop_method_call, + clippy::deref_addrof, clippy::explicit_auto_deref, clippy::needless_borrow, clippy::no_effect, + clippy::useless_borrows_in_formatting, clippy::uninlined_format_args, clippy::unnecessary_literal_unwrap, - clippy::deref_addrof + noop_method_call, + suspicious_double_ref_op )] use std::ops::{Deref, DerefMut}; diff --git a/src/tools/clippy/tests/ui/explicit_deref_methods.rs b/src/tools/clippy/tests/ui/explicit_deref_methods.rs index f6309cd404b85..215c5450597f0 100644 --- a/src/tools/clippy/tests/ui/explicit_deref_methods.rs +++ b/src/tools/clippy/tests/ui/explicit_deref_methods.rs @@ -3,14 +3,15 @@ #![allow(unused_variables, unused_must_use)] #![allow( clippy::borrow_deref_ref, - suspicious_double_ref_op, - noop_method_call, + clippy::deref_addrof, clippy::explicit_auto_deref, clippy::needless_borrow, clippy::no_effect, + clippy::useless_borrows_in_formatting, clippy::uninlined_format_args, clippy::unnecessary_literal_unwrap, - clippy::deref_addrof + noop_method_call, + suspicious_double_ref_op )] use std::ops::{Deref, DerefMut}; diff --git a/src/tools/clippy/tests/ui/explicit_deref_methods.stderr b/src/tools/clippy/tests/ui/explicit_deref_methods.stderr index e2f2e68720b17..d7e41e4409faa 100644 --- a/src/tools/clippy/tests/ui/explicit_deref_methods.stderr +++ b/src/tools/clippy/tests/ui/explicit_deref_methods.stderr @@ -1,5 +1,5 @@ error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:58:19 + --> tests/ui/explicit_deref_methods.rs:59: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:61:23 + --> tests/ui/explicit_deref_methods.rs:62:23 | LL | let b: &mut str = a.deref_mut(); | ^^^^^^^^^^^^^ help: try: `&mut **a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:65:39 + --> tests/ui/explicit_deref_methods.rs:66:39 | LL | let b: String = format!("{}, {}", a.deref(), a.deref()); | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:65:50 + --> tests/ui/explicit_deref_methods.rs:66:50 | LL | let b: String = format!("{}, {}", a.deref(), a.deref()); | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:69:20 + --> tests/ui/explicit_deref_methods.rs:70:20 | LL | println!("{}", a.deref()); | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:73:11 + --> tests/ui/explicit_deref_methods.rs:74:11 | LL | match a.deref() { | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:78:28 + --> tests/ui/explicit_deref_methods.rs:79:28 | LL | let b: String = concat(a.deref()); | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:81:13 + --> tests/ui/explicit_deref_methods.rs:82:13 | LL | let b = just_return(a).deref(); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `just_return(a)` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:84:28 + --> tests/ui/explicit_deref_methods.rs:85: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:124:31 + --> tests/ui/explicit_deref_methods.rs:125:31 | LL | let b: &str = expr_deref!(a.deref()); | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:154:14 + --> tests/ui/explicit_deref_methods.rs:155:14 | LL | let _ = &Deref::deref(&"foo"); | ^^^^^^^^^^^^^^^^^^^^ help: try: `*&"foo"` error: explicit `deref_mut` method call - --> tests/ui/explicit_deref_methods.rs:156:14 + --> tests/ui/explicit_deref_methods.rs:157: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:157:14 + --> tests/ui/explicit_deref_methods.rs:158:14 | LL | let _ = &DerefMut::deref_mut((&mut &mut x).deref_mut()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut ***(&mut &mut x)` diff --git a/src/tools/clippy/tests/ui/for_kv_map.fixed b/src/tools/clippy/tests/ui/for_kv_map.fixed index 6ec4cb01ffd1d..2fd6d9ca499dd 100644 --- a/src/tools/clippy/tests/ui/for_kv_map.fixed +++ b/src/tools/clippy/tests/ui/for_kv_map.fixed @@ -86,3 +86,25 @@ fn wrongly_unmangled_macros() { let _v = v; } } + +fn issue16822(mut x: HashMap) { + for v in x.values() { + //~^ for_kv_map + println!("{}", v); + } + + for v in x.values_mut() { + //~^ for_kv_map + *v += 1; + } + + for k in x.keys() { + //~^ for_kv_map + println!("{}", k); + } + + for k in x.keys() { + //~^ for_kv_map + println!("{}", k); + } +} diff --git a/src/tools/clippy/tests/ui/for_kv_map.rs b/src/tools/clippy/tests/ui/for_kv_map.rs index 19e907ff10a62..12e16d1dbfd61 100644 --- a/src/tools/clippy/tests/ui/for_kv_map.rs +++ b/src/tools/clippy/tests/ui/for_kv_map.rs @@ -86,3 +86,25 @@ fn wrongly_unmangled_macros() { let _v = v; } } + +fn issue16822(mut x: HashMap) { + for (_, v) in x.iter() { + //~^ for_kv_map + println!("{}", v); + } + + for (_, v) in x.iter_mut() { + //~^ for_kv_map + *v += 1; + } + + for (k, _) in x.iter() { + //~^ for_kv_map + println!("{}", k); + } + + for (k, _) in x.iter_mut() { + //~^ for_kv_map + println!("{}", k); + } +} diff --git a/src/tools/clippy/tests/ui/for_kv_map.stderr b/src/tools/clippy/tests/ui/for_kv_map.stderr index 5436592f2ab6d..fac0407e10032 100644 --- a/src/tools/clippy/tests/ui/for_kv_map.stderr +++ b/src/tools/clippy/tests/ui/for_kv_map.stderr @@ -84,5 +84,53 @@ LL - for (_, v) in test_map!(wrapped) { LL + for v in test_map!(wrapped).values() { | -error: aborting due to 7 previous errors +error: you seem to want to iterate on a map's values + --> tests/ui/for_kv_map.rs:91:19 + | +LL | for (_, v) in x.iter() { + | ^^^^^^^^ + | +help: use the corresponding method + | +LL - for (_, v) in x.iter() { +LL + for v in x.values() { + | + +error: you seem to want to iterate on a map's values + --> tests/ui/for_kv_map.rs:96:19 + | +LL | for (_, v) in x.iter_mut() { + | ^^^^^^^^^^^^ + | +help: use the corresponding method + | +LL - for (_, v) in x.iter_mut() { +LL + for v in x.values_mut() { + | + +error: you seem to want to iterate on a map's keys + --> tests/ui/for_kv_map.rs:101:19 + | +LL | for (k, _) in x.iter() { + | ^^^^^^^^ + | +help: use the corresponding method + | +LL - for (k, _) in x.iter() { +LL + for k in x.keys() { + | + +error: you seem to want to iterate on a map's keys + --> tests/ui/for_kv_map.rs:106:19 + | +LL | for (k, _) in x.iter_mut() { + | ^^^^^^^^^^^^ + | +help: use the corresponding method + | +LL - for (k, _) in x.iter_mut() { +LL + for k in x.keys() { + | + +error: aborting due to 11 previous errors diff --git a/src/tools/clippy/tests/ui/from_over_into.fixed b/src/tools/clippy/tests/ui/from_over_into.fixed index 7229e5a2d3589..3eed0ad52e9a7 100644 --- a/src/tools/clippy/tests/ui/from_over_into.fixed +++ b/src/tools/clippy/tests/ui/from_over_into.fixed @@ -116,4 +116,24 @@ fn issue_112502() { } } +fn issue_16823() { + pub struct Foo(pub String); + + impl From for Foo + where + String: From, + { + fn from(val: T) -> Self { + Self(String::from(val)) + } + } + + // no lint, From for String would conflict with the blanket impl above + impl Into for Foo { + fn into(self) -> String { + self.0 + } + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/from_over_into.rs b/src/tools/clippy/tests/ui/from_over_into.rs index 9c75969c5c134..b4f58df0a4372 100644 --- a/src/tools/clippy/tests/ui/from_over_into.rs +++ b/src/tools/clippy/tests/ui/from_over_into.rs @@ -116,4 +116,24 @@ fn issue_112502() { } } +fn issue_16823() { + pub struct Foo(pub String); + + impl From for Foo + where + String: From, + { + fn from(val: T) -> Self { + Self(String::from(val)) + } + } + + // no lint, From for String would conflict with the blanket impl above + impl Into for Foo { + fn into(self) -> String { + self.0 + } + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/infinite_loops.rs b/src/tools/clippy/tests/ui/infinite_loops.rs index 88e3328d662ec..c0f0087b374c5 100644 --- a/src/tools/clippy/tests/ui/infinite_loops.rs +++ b/src/tools/clippy/tests/ui/infinite_loops.rs @@ -295,7 +295,7 @@ fn panic_like_macros_1() { } fn panic_like_macros_2() { - let mut x = 0; + let mut x: i32 = 0; loop { do_something(); @@ -310,7 +310,7 @@ fn panic_like_macros_2() { } loop { do_something(); - assert!(x % 2 == 0); + assert!(x.is_positive()); } loop { do_something(); diff --git a/src/tools/clippy/tests/ui/manual_assert_eq.fixed b/src/tools/clippy/tests/ui/manual_assert_eq.fixed new file mode 100644 index 0000000000000..175c33e271924 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_assert_eq.fixed @@ -0,0 +1,114 @@ +//@aux-build:proc_macros.rs +#![warn(clippy::manual_assert_eq)] +#![allow(clippy::manual_ignore_case_cmp)] // only raised before the fix +#![expect(clippy::eq_op, clippy::assertions_on_constants)] + +fn main() { + let a = "a"; + assert_eq!(a, "a".to_ascii_lowercase()); + //~^ manual_assert_eq + assert_ne!(a, "a".to_ascii_uppercase()); + //~^ manual_assert_eq + debug_assert_eq!(a, "a".to_ascii_lowercase()); + //~^ manual_assert_eq + debug_assert_ne!(a, "a".to_ascii_uppercase()); + //~^ manual_assert_eq + + // macros + let v = vec![]; + assert_eq!(v, vec![1, 2, 3]); + //~^ manual_assert_eq + assert_eq!(vec![1, 2, 3], v); + //~^ manual_assert_eq + assert_eq!(vec![1], vec![1, 2, 3]); + //~^ manual_assert_eq + + // Don't lint: has assert message + assert!(a == "a".to_ascii_lowercase(), "{a}"); + assert!(a == "a".to_ascii_lowercase(), "a==a"); + assert!(a == "a".to_ascii_lowercase(), "{a}==a"); + assert!(a != "a".to_ascii_uppercase(), "a!=A"); + debug_assert!(a == "a".to_ascii_lowercase(), "a==a"); + debug_assert!(a != "a".to_ascii_uppercase(), "a!=A"); + + // Don't lint: `!=`, and at least one of the sides is a constant value + assert!(a != "A"); + assert!("A" != a); + assert!("A" != "A"); + + // Don't lint: comparison of ptrs + fn cmp_ptrs(a: *const u8, b: *const u8) { + assert!(a == b); + } + + // Don't lint: one of the sides isn't `Debug` + { + #[derive(PartialEq)] + struct NotDebug; + + #[derive(PartialEq)] + struct NotDebug2; + + impl PartialEq for NotDebug { + fn eq(&self, other: &NotDebug2) -> bool { + unimplemented!() + } + } + impl PartialEq for NotDebug2 { + fn eq(&self, other: &NotDebug) -> bool { + unimplemented!() + } + } + + #[derive(Debug)] + struct IsDebug; + + impl PartialEq for NotDebug { + fn eq(&self, other: &IsDebug) -> bool { + unimplemented!() + } + } + impl PartialEq for IsDebug { + fn eq(&self, other: &NotDebug) -> bool { + unimplemented!() + } + } + + let nd = NotDebug; + assert!(nd == nd); + + let nd2 = NotDebug2; + assert!(nd == nd2); + assert!(nd2 == nd); + + let id = IsDebug; + assert!(id == nd); + assert!(nd == id); + } + + // Don't lint: in const context + const { + assert!(5 == 2 + 3); + } + + // Don't lint: in external macro + { + // NOTE: this only works because `root_macro_call_first_node` returns `external!`, + // which then gets rejected by the macro name check + proc_macros::external!(assert!('a' == 'b')); + proc_macros::external!({ + let some_padding_before = 'a'; + assert!('a' == 'b'); + let some_padding_after = 'b'; + }); + + // .. which also means that the following is _technically_ a FN -- but surely no one would write + // code like this (diverging/unit expression as a child expression of a macro call) + vec![(), assert!('a' == 'b'), ()]; + } +} + +// Don't lint: in const context +const _: () = { + assert!(8 == (7 + 1)); +}; diff --git a/src/tools/clippy/tests/ui/manual_assert_eq.rs b/src/tools/clippy/tests/ui/manual_assert_eq.rs new file mode 100644 index 0000000000000..0df5518bd3545 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_assert_eq.rs @@ -0,0 +1,114 @@ +//@aux-build:proc_macros.rs +#![warn(clippy::manual_assert_eq)] +#![allow(clippy::manual_ignore_case_cmp)] // only raised before the fix +#![expect(clippy::eq_op, clippy::assertions_on_constants)] + +fn main() { + let a = "a"; + assert!(a == "a".to_ascii_lowercase()); + //~^ manual_assert_eq + assert!(a != "a".to_ascii_uppercase()); + //~^ manual_assert_eq + debug_assert!(a == "a".to_ascii_lowercase()); + //~^ manual_assert_eq + debug_assert!(a != "a".to_ascii_uppercase()); + //~^ manual_assert_eq + + // macros + let v = vec![]; + assert!(v == vec![1, 2, 3]); + //~^ manual_assert_eq + assert!(vec![1, 2, 3] == v); + //~^ manual_assert_eq + assert!(vec![1] == vec![1, 2, 3]); + //~^ manual_assert_eq + + // Don't lint: has assert message + assert!(a == "a".to_ascii_lowercase(), "{a}"); + assert!(a == "a".to_ascii_lowercase(), "a==a"); + assert!(a == "a".to_ascii_lowercase(), "{a}==a"); + assert!(a != "a".to_ascii_uppercase(), "a!=A"); + debug_assert!(a == "a".to_ascii_lowercase(), "a==a"); + debug_assert!(a != "a".to_ascii_uppercase(), "a!=A"); + + // Don't lint: `!=`, and at least one of the sides is a constant value + assert!(a != "A"); + assert!("A" != a); + assert!("A" != "A"); + + // Don't lint: comparison of ptrs + fn cmp_ptrs(a: *const u8, b: *const u8) { + assert!(a == b); + } + + // Don't lint: one of the sides isn't `Debug` + { + #[derive(PartialEq)] + struct NotDebug; + + #[derive(PartialEq)] + struct NotDebug2; + + impl PartialEq for NotDebug { + fn eq(&self, other: &NotDebug2) -> bool { + unimplemented!() + } + } + impl PartialEq for NotDebug2 { + fn eq(&self, other: &NotDebug) -> bool { + unimplemented!() + } + } + + #[derive(Debug)] + struct IsDebug; + + impl PartialEq for NotDebug { + fn eq(&self, other: &IsDebug) -> bool { + unimplemented!() + } + } + impl PartialEq for IsDebug { + fn eq(&self, other: &NotDebug) -> bool { + unimplemented!() + } + } + + let nd = NotDebug; + assert!(nd == nd); + + let nd2 = NotDebug2; + assert!(nd == nd2); + assert!(nd2 == nd); + + let id = IsDebug; + assert!(id == nd); + assert!(nd == id); + } + + // Don't lint: in const context + const { + assert!(5 == 2 + 3); + } + + // Don't lint: in external macro + { + // NOTE: this only works because `root_macro_call_first_node` returns `external!`, + // which then gets rejected by the macro name check + proc_macros::external!(assert!('a' == 'b')); + proc_macros::external!({ + let some_padding_before = 'a'; + assert!('a' == 'b'); + let some_padding_after = 'b'; + }); + + // .. which also means that the following is _technically_ a FN -- but surely no one would write + // code like this (diverging/unit expression as a child expression of a macro call) + vec![(), assert!('a' == 'b'), ()]; + } +} + +// Don't lint: in const context +const _: () = { + assert!(8 == (7 + 1)); +}; diff --git a/src/tools/clippy/tests/ui/manual_assert_eq.stderr b/src/tools/clippy/tests/ui/manual_assert_eq.stderr new file mode 100644 index 0000000000000..0694df01a8f81 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_assert_eq.stderr @@ -0,0 +1,88 @@ +error: used `assert!` with an equality comparison + --> tests/ui/manual_assert_eq.rs:8:5 + | +LL | assert!(a == "a".to_ascii_lowercase()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::manual-assert-eq` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::manual_assert_eq)]` +help: replace it with `assert_eq!(..)` + | +LL - assert!(a == "a".to_ascii_lowercase()); +LL + assert_eq!(a, "a".to_ascii_lowercase()); + | + +error: used `assert!` with an equality comparison + --> tests/ui/manual_assert_eq.rs:10:5 + | +LL | assert!(a != "a".to_ascii_uppercase()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `assert_ne!(..)` + | +LL - assert!(a != "a".to_ascii_uppercase()); +LL + assert_ne!(a, "a".to_ascii_uppercase()); + | + +error: used `debug_assert!` with an equality comparison + --> tests/ui/manual_assert_eq.rs:12:5 + | +LL | debug_assert!(a == "a".to_ascii_lowercase()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `debug_assert_eq!(..)` + | +LL - debug_assert!(a == "a".to_ascii_lowercase()); +LL + debug_assert_eq!(a, "a".to_ascii_lowercase()); + | + +error: used `debug_assert!` with an equality comparison + --> tests/ui/manual_assert_eq.rs:14:5 + | +LL | debug_assert!(a != "a".to_ascii_uppercase()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `debug_assert_ne!(..)` + | +LL - debug_assert!(a != "a".to_ascii_uppercase()); +LL + debug_assert_ne!(a, "a".to_ascii_uppercase()); + | + +error: used `assert!` with an equality comparison + --> tests/ui/manual_assert_eq.rs:19:5 + | +LL | assert!(v == vec![1, 2, 3]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `assert_eq!(..)` + | +LL - assert!(v == vec![1, 2, 3]); +LL + assert_eq!(v, vec![1, 2, 3]); + | + +error: used `assert!` with an equality comparison + --> tests/ui/manual_assert_eq.rs:21:5 + | +LL | assert!(vec![1, 2, 3] == v); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `assert_eq!(..)` + | +LL - assert!(vec![1, 2, 3] == v); +LL + assert_eq!(vec![1, 2, 3], v); + | + +error: used `assert!` with an equality comparison + --> tests/ui/manual_assert_eq.rs:23:5 + | +LL | assert!(vec![1] == vec![1, 2, 3]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `assert_eq!(..)` + | +LL - assert!(vec![1] == vec![1, 2, 3]); +LL + assert_eq!(vec![1], vec![1, 2, 3]); + | + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/missing_asserts_for_indexing.fixed b/src/tools/clippy/tests/ui/missing_asserts_for_indexing.fixed index 50bc576dd1e23..f877cbc1f1145 100644 --- a/src/tools/clippy/tests/ui/missing_asserts_for_indexing.fixed +++ b/src/tools/clippy/tests/ui/missing_asserts_for_indexing.fixed @@ -1,4 +1,4 @@ -#![allow(unused)] +#![expect(clippy::manual_assert_eq)] #![warn(clippy::missing_asserts_for_indexing)] // ok diff --git a/src/tools/clippy/tests/ui/missing_asserts_for_indexing.rs b/src/tools/clippy/tests/ui/missing_asserts_for_indexing.rs index 9e219a2af0732..8084e0b71be98 100644 --- a/src/tools/clippy/tests/ui/missing_asserts_for_indexing.rs +++ b/src/tools/clippy/tests/ui/missing_asserts_for_indexing.rs @@ -1,4 +1,4 @@ -#![allow(unused)] +#![expect(clippy::manual_assert_eq)] #![warn(clippy::missing_asserts_for_indexing)] // ok diff --git a/src/tools/clippy/tests/ui/needless_ifs.fixed b/src/tools/clippy/tests/ui/needless_ifs.fixed index 0e0b0fa39c9b7..89d5b1da7b3f0 100644 --- a/src/tools/clippy/tests/ui/needless_ifs.fixed +++ b/src/tools/clippy/tests/ui/needless_ifs.fixed @@ -13,7 +13,6 @@ unused )] #![warn(clippy::needless_ifs)] - extern crate proc_macros; use proc_macros::{external, with_span}; @@ -113,3 +112,12 @@ fn issue15960() -> i32 { 1 // put something here so that `if` is a statement not an expression } + +#[rustfmt::skip] +fn issue_16845() { + + // Vertical tab (U+000B) should be treated as whitespace, + + //~^ needless_ifs + let () = if maybe_side_effect() {}; +} diff --git a/src/tools/clippy/tests/ui/needless_ifs.rs b/src/tools/clippy/tests/ui/needless_ifs.rs index fb0ee5c9cc832..837eaaf9647b0 100644 --- a/src/tools/clippy/tests/ui/needless_ifs.rs +++ b/src/tools/clippy/tests/ui/needless_ifs.rs @@ -13,7 +13,6 @@ unused )] #![warn(clippy::needless_ifs)] - extern crate proc_macros; use proc_macros::{external, with_span}; @@ -114,3 +113,12 @@ fn issue15960() -> i32 { 1 // put something here so that `if` is a statement not an expression } + +#[rustfmt::skip] +fn issue_16845() { + + // Vertical tab (U+000B) should be treated as whitespace, + if true { } + //~^ needless_ifs + let () = if maybe_side_effect() {}; +} diff --git a/src/tools/clippy/tests/ui/needless_ifs.stderr b/src/tools/clippy/tests/ui/needless_ifs.stderr index 8684ec217a0a4..7c7fcdc183668 100644 --- a/src/tools/clippy/tests/ui/needless_ifs.stderr +++ b/src/tools/clippy/tests/ui/needless_ifs.stderr @@ -1,5 +1,5 @@ error: this `if` branch is empty - --> tests/ui/needless_ifs.rs:26:5 + --> tests/ui/needless_ifs.rs:25:5 | LL | if (true) {} | ^^^^^^^^^^^^ help: you can remove it @@ -8,13 +8,13 @@ LL | if (true) {} = help: to override `-D warnings` add `#[allow(clippy::needless_ifs)]` error: this `if` branch is empty - --> tests/ui/needless_ifs.rs:29:5 + --> tests/ui/needless_ifs.rs:28:5 | LL | if maybe_side_effect() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can remove it: `maybe_side_effect();` error: this `if` branch is empty - --> tests/ui/needless_ifs.rs:35:5 + --> tests/ui/needless_ifs.rs:34:5 | LL | / if { LL | | @@ -31,7 +31,7 @@ LL + }); | error: this `if` branch is empty - --> tests/ui/needless_ifs.rs:50:5 + --> tests/ui/needless_ifs.rs:49:5 | LL | / if { LL | | @@ -57,34 +57,40 @@ LL + } && true); | error: this `if` branch is empty - --> tests/ui/needless_ifs.rs:95:5 + --> tests/ui/needless_ifs.rs:94:5 | LL | if { maybe_side_effect() } {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can remove it: `({ maybe_side_effect() });` error: this `if` branch is empty - --> tests/ui/needless_ifs.rs:98:5 + --> tests/ui/needless_ifs.rs:97:5 | LL | if { maybe_side_effect() } && true {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can remove it: `({ maybe_side_effect() } && true);` error: this `if` branch is empty - --> tests/ui/needless_ifs.rs:103:5 + --> tests/ui/needless_ifs.rs:102:5 | LL | if true {} | ^^^^^^^^^^ help: you can remove it: `true;` error: this `if` branch is empty - --> tests/ui/needless_ifs.rs:110:5 + --> tests/ui/needless_ifs.rs:109:5 | LL | if matches!(2, 3) {} | ^^^^^^^^^^^^^^^^^^^^ help: you can remove it: `matches!(2, 3);` error: this `if` branch is empty - --> tests/ui/needless_ifs.rs:112:5 + --> tests/ui/needless_ifs.rs:111:5 | LL | if matches!(2, 3) == (2 * 2 == 5) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can remove it: `matches!(2, 3) == (2 * 2 == 5);` -error: aborting due to 9 previous errors +error: this `if` branch is empty + --> tests/ui/needless_ifs.rs:121:5 + | +LL | if true {␋} + | ^^^^^^^^^^^ help: you can remove it + +error: aborting due to 10 previous errors diff --git a/src/tools/clippy/tests/ui/panic_in_result_fn_assertions.rs b/src/tools/clippy/tests/ui/panic_in_result_fn_assertions.rs index 4e70282415794..17b221044b1f2 100644 --- a/src/tools/clippy/tests/ui/panic_in_result_fn_assertions.rs +++ b/src/tools/clippy/tests/ui/panic_in_result_fn_assertions.rs @@ -7,7 +7,7 @@ impl A { fn result_with_assert_with_message(x: i32) -> Result // should emit lint //~^ panic_in_result_fn { - assert!(x == 5, "wrong argument"); + assert!(x.is_positive(), "wrong argument"); Ok(true) } @@ -27,7 +27,7 @@ impl A { fn other_with_assert_with_message(x: i32) // should not emit lint { - assert!(x == 5, "wrong argument"); + assert!(x.is_positive(), "wrong argument"); } fn other_with_assert_eq(x: i32) // should not emit lint diff --git a/src/tools/clippy/tests/ui/panic_in_result_fn_assertions.stderr b/src/tools/clippy/tests/ui/panic_in_result_fn_assertions.stderr index cdb7762510d9c..db881da06ba83 100644 --- a/src/tools/clippy/tests/ui/panic_in_result_fn_assertions.stderr +++ b/src/tools/clippy/tests/ui/panic_in_result_fn_assertions.stderr @@ -4,7 +4,7 @@ error: used `panic!()` or assertion in a function that returns `Result` LL | / fn result_with_assert_with_message(x: i32) -> Result // should emit lint LL | | LL | | { -LL | | assert!(x == 5, "wrong argument"); +LL | | assert!(x.is_positive(), "wrong argument"); LL | | Ok(true) LL | | } | |_____^ @@ -13,8 +13,8 @@ LL | | } note: return Err() instead of panicking --> tests/ui/panic_in_result_fn_assertions.rs:10:9 | -LL | assert!(x == 5, "wrong argument"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | assert!(x.is_positive(), "wrong argument"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: `-D clippy::panic-in-result-fn` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::panic_in_result_fn)]` diff --git a/src/tools/clippy/tests/ui/panic_in_result_fn_debug_assertions.rs b/src/tools/clippy/tests/ui/panic_in_result_fn_debug_assertions.rs index c4549c6b84123..9cce339f4d60d 100644 --- a/src/tools/clippy/tests/ui/panic_in_result_fn_debug_assertions.rs +++ b/src/tools/clippy/tests/ui/panic_in_result_fn_debug_assertions.rs @@ -9,7 +9,7 @@ struct A; impl A { fn result_with_debug_assert_with_message(x: i32) -> Result { - debug_assert!(x == 5, "wrong argument"); + debug_assert!(x.is_positive(), "wrong argument"); Ok(true) } @@ -24,7 +24,7 @@ impl A { } fn other_with_debug_assert_with_message(x: i32) { - debug_assert!(x == 5, "wrong argument"); + debug_assert!(x.is_positive(), "wrong argument"); } fn other_with_debug_assert_eq(x: i32) { diff --git a/src/tools/clippy/tests/ui/question_mark.fixed b/src/tools/clippy/tests/ui/question_mark.fixed index bf4b4ff0a21ed..786431bc1f539 100644 --- a/src/tools/clippy/tests/ui/question_mark.fixed +++ b/src/tools/clippy/tests/ui/question_mark.fixed @@ -564,3 +564,14 @@ fn issue16751(mut v: Option) -> Option { if n > 10 { Some(42) } else { None } } } + +fn issue_destructuring_assignment() -> Option<(i32, i32)> { + let mut a = 0i32; + let mut b = 0i32; + let opt: Option<(i32, i32)> = Some((1, 2)); + { + let x = opt?; + (a, b) = x + } + Some((a, b)) +} diff --git a/src/tools/clippy/tests/ui/question_mark.rs b/src/tools/clippy/tests/ui/question_mark.rs index 93f76f16576c6..7cbcc604eb591 100644 --- a/src/tools/clippy/tests/ui/question_mark.rs +++ b/src/tools/clippy/tests/ui/question_mark.rs @@ -710,3 +710,15 @@ fn issue16751(mut v: Option) -> Option { None => return None, } } + +fn issue_destructuring_assignment() -> Option<(i32, i32)> { + let mut a = 0i32; + let mut b = 0i32; + let opt: Option<(i32, i32)> = Some((1, 2)); + match opt { + //~^ question_mark + Some(x) => (a, b) = x, + None => return None, + } + Some((a, b)) +} diff --git a/src/tools/clippy/tests/ui/question_mark.stderr b/src/tools/clippy/tests/ui/question_mark.stderr index 9d7cfc2057641..74fb2c45a2549 100644 --- a/src/tools/clippy/tests/ui/question_mark.stderr +++ b/src/tools/clippy/tests/ui/question_mark.stderr @@ -498,5 +498,23 @@ LL + if n > 10 { Some(42) } else { None } LL + } | -error: aborting due to 46 previous errors +error: this `match` expression can be replaced with `?` + --> tests/ui/question_mark.rs:718:5 + | +LL | / match opt { +LL | | +LL | | Some(x) => (a, b) = x, +LL | | None => return None, +LL | | } + | |_____^ + | +help: try instead + | +LL ~ { +LL + let x = opt?; +LL + (a, b) = x +LL + } + | + +error: aborting due to 47 previous errors diff --git a/src/tools/clippy/tests/ui/recursive_format_impl.rs b/src/tools/clippy/tests/ui/recursive_format_impl.rs index 9f46fef62354a..936d56877055e 100644 --- a/src/tools/clippy/tests/ui/recursive_format_impl.rs +++ b/src/tools/clippy/tests/ui/recursive_format_impl.rs @@ -3,6 +3,7 @@ clippy::borrow_deref_ref, clippy::deref_addrof, clippy::inherent_to_string_shadow_display, + clippy::useless_borrows_in_formatting, clippy::to_string_in_format_args, clippy::uninlined_format_args )] diff --git a/src/tools/clippy/tests/ui/recursive_format_impl.stderr b/src/tools/clippy/tests/ui/recursive_format_impl.stderr index 4361d612bf2a5..c813bb349b86a 100644 --- a/src/tools/clippy/tests/ui/recursive_format_impl.stderr +++ b/src/tools/clippy/tests/ui/recursive_format_impl.stderr @@ -1,5 +1,5 @@ error: using `self.to_string` in `fmt::Display` implementation will cause infinite recursion - --> tests/ui/recursive_format_impl.rs:31:25 + --> tests/ui/recursive_format_impl.rs:32:25 | LL | write!(f, "{}", self.to_string()) | ^^^^^^^^^^^^^^^^ @@ -8,55 +8,55 @@ LL | write!(f, "{}", self.to_string()) = help: to override `-D warnings` add `#[allow(clippy::recursive_format_impl)]` error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> tests/ui/recursive_format_impl.rs:76:9 + --> tests/ui/recursive_format_impl.rs:77:9 | LL | write!(f, "{}", self) | ^^^^^^^^^^^^^^^^^^^^^ error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> tests/ui/recursive_format_impl.rs:86:9 + --> tests/ui/recursive_format_impl.rs:87:9 | LL | write!(f, "{}", &self) | ^^^^^^^^^^^^^^^^^^^^^^ error: using `self` as `Debug` in `impl Debug` will cause infinite recursion - --> tests/ui/recursive_format_impl.rs:93:9 + --> tests/ui/recursive_format_impl.rs:94:9 | LL | write!(f, "{:?}", &self) | ^^^^^^^^^^^^^^^^^^^^^^^^ error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> tests/ui/recursive_format_impl.rs:103:9 + --> tests/ui/recursive_format_impl.rs:104:9 | LL | write!(f, "{}", &&&self) | ^^^^^^^^^^^^^^^^^^^^^^^^ error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> tests/ui/recursive_format_impl.rs:178:9 + --> tests/ui/recursive_format_impl.rs:179:9 | LL | write!(f, "{}", &*self) | ^^^^^^^^^^^^^^^^^^^^^^^ error: using `self` as `Debug` in `impl Debug` will cause infinite recursion - --> tests/ui/recursive_format_impl.rs:185:9 + --> tests/ui/recursive_format_impl.rs:186:9 | LL | write!(f, "{:?}", &*self) | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> tests/ui/recursive_format_impl.rs:202:9 + --> tests/ui/recursive_format_impl.rs:203:9 | LL | write!(f, "{}", *self) | ^^^^^^^^^^^^^^^^^^^^^^ error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> tests/ui/recursive_format_impl.rs:219:9 + --> tests/ui/recursive_format_impl.rs:220:9 | LL | write!(f, "{}", **&&*self) | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> tests/ui/recursive_format_impl.rs:236:9 + --> tests/ui/recursive_format_impl.rs:237:9 | LL | write!(f, "{}", &&**&&*self) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/uninit_vec.rs b/src/tools/clippy/tests/ui/uninit_vec.rs index eeb281322da92..a0bac28e87292 100644 --- a/src/tools/clippy/tests/ui/uninit_vec.rs +++ b/src/tools/clippy/tests/ui/uninit_vec.rs @@ -87,7 +87,7 @@ fn main() { unsafe { // test the case where there are other statements in the following unsafe block vec.set_len(200); - assert!(vec.len() == 200); + assert_eq!(vec.len(), 200); } // handle vec stored in the field of a struct diff --git a/src/tools/clippy/tests/ui/unnecessary_map_or.fixed b/src/tools/clippy/tests/ui/unnecessary_map_or.fixed index 52c1143392925..b1f991b9b26c0 100644 --- a/src/tools/clippy/tests/ui/unnecessary_map_or.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_map_or.fixed @@ -1,9 +1,12 @@ //@aux-build:proc_macros.rs #![warn(clippy::unnecessary_map_or)] -#![allow(clippy::no_effect)] -#![allow(clippy::eq_op)] -#![allow(clippy::unnecessary_lazy_evaluations)] -#![allow(clippy::nonminimal_bool)] +#![allow( + clippy::no_effect, + clippy::eq_op, + clippy::unnecessary_lazy_evaluations, + clippy::nonminimal_bool, + clippy::manual_assert_eq +)] #[clippy::msrv = "1.70.0"] #[macro_use] extern crate proc_macros; diff --git a/src/tools/clippy/tests/ui/unnecessary_map_or.rs b/src/tools/clippy/tests/ui/unnecessary_map_or.rs index dd2e1a569469c..edd5ea9d878fe 100644 --- a/src/tools/clippy/tests/ui/unnecessary_map_or.rs +++ b/src/tools/clippy/tests/ui/unnecessary_map_or.rs @@ -1,9 +1,12 @@ //@aux-build:proc_macros.rs #![warn(clippy::unnecessary_map_or)] -#![allow(clippy::no_effect)] -#![allow(clippy::eq_op)] -#![allow(clippy::unnecessary_lazy_evaluations)] -#![allow(clippy::nonminimal_bool)] +#![allow( + clippy::no_effect, + clippy::eq_op, + clippy::unnecessary_lazy_evaluations, + clippy::nonminimal_bool, + clippy::manual_assert_eq +)] #[clippy::msrv = "1.70.0"] #[macro_use] extern crate proc_macros; diff --git a/src/tools/clippy/tests/ui/unnecessary_map_or.stderr b/src/tools/clippy/tests/ui/unnecessary_map_or.stderr index d11e7179f9216..12a973c84213d 100644 --- a/src/tools/clippy/tests/ui/unnecessary_map_or.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_map_or.stderr @@ -1,5 +1,5 @@ error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:13:13 + --> tests/ui/unnecessary_map_or.rs:16:13 | LL | let _ = Some(5).map_or(false, |n| n == 5); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -13,7 +13,7 @@ LL + let _ = Some(5) == Some(5); | error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:15:13 + --> tests/ui/unnecessary_map_or.rs:18:13 | LL | let _ = Some(5).map_or(true, |n| n != 5); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL + let _ = Some(5) != Some(5); | error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:17:13 + --> tests/ui/unnecessary_map_or.rs:20:13 | LL | let _ = Some(5).map_or(false, |n| { | _____________^ @@ -46,7 +46,7 @@ LL + let _ = Some(5) == Some(5); | error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:22:13 + --> tests/ui/unnecessary_map_or.rs:25:13 | LL | let _ = Some(5).map_or(false, |n| { | _____________^ @@ -63,7 +63,7 @@ LL + let _ = Some(5).is_some_and(|n| { | error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:27:13 + --> tests/ui/unnecessary_map_or.rs:30:13 | LL | let _ = Some(vec![5]).map_or(false, |n| n == [5]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -75,7 +75,7 @@ LL + let _ = Some(vec![5]).is_some_and(|n| n == [5]); | error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:29:13 + --> tests/ui/unnecessary_map_or.rs:32:13 | LL | let _ = Some(vec![1]).map_or(false, |n| vec![2] == n); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -87,7 +87,7 @@ LL + let _ = Some(vec![1]).is_some_and(|n| vec![2] == n); | error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:31:13 + --> tests/ui/unnecessary_map_or.rs:34:13 | LL | let _ = Some(5).map_or(false, |n| n == n); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -99,7 +99,7 @@ LL + let _ = Some(5).is_some_and(|n| n == n); | error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:33:13 + --> tests/ui/unnecessary_map_or.rs:36:13 | LL | let _ = Some(5).map_or(false, |n| n == if 2 > 1 { n } else { 0 }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -111,7 +111,7 @@ LL + let _ = Some(5).is_some_and(|n| n == if 2 > 1 { n } else { 0 }); | error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:35:13 + --> tests/ui/unnecessary_map_or.rs:38:13 | LL | let _ = Ok::, i32>(vec![5]).map_or(false, |n| n == [5]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -123,7 +123,7 @@ LL + let _ = Ok::, i32>(vec![5]).is_ok_and(|n| n == [5]); | error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:37:13 + --> tests/ui/unnecessary_map_or.rs:40:13 | LL | let _ = Ok::(5).map_or(false, |n| n == 5); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -135,7 +135,7 @@ LL + let _ = Ok::(5) == Ok(5); | error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:39:13 + --> tests/ui/unnecessary_map_or.rs:42:13 | LL | let _ = Some(5).map_or(false, |n| n == 5).then(|| 1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -147,7 +147,7 @@ LL + let _ = (Some(5) == Some(5)).then(|| 1); | error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:41:13 + --> tests/ui/unnecessary_map_or.rs:44:13 | LL | let _ = Some(5).map_or(true, |n| n == 5); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -159,7 +159,7 @@ LL + let _ = Some(5).is_none_or(|n| n == 5); | error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:43:13 + --> tests/ui/unnecessary_map_or.rs:46:13 | LL | let _ = Some(5).map_or(true, |n| 5 == n); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -171,7 +171,7 @@ LL + let _ = Some(5).is_none_or(|n| 5 == n); | error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:45:14 + --> tests/ui/unnecessary_map_or.rs:48:14 | LL | let _ = !Some(5).map_or(false, |n| n == 5); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -183,7 +183,7 @@ LL + let _ = !(Some(5) == Some(5)); | error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:47:13 + --> tests/ui/unnecessary_map_or.rs:50:13 | LL | let _ = Some(5).map_or(false, |n| n == 5) || false; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -195,7 +195,7 @@ LL + let _ = (Some(5) == Some(5)) || false; | error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:49:13 + --> tests/ui/unnecessary_map_or.rs:52:13 | LL | let _ = Some(5).map_or(false, |n| n == 5) as usize; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -207,7 +207,7 @@ LL + let _ = (Some(5) == Some(5)) as usize; | error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:74:13 + --> tests/ui/unnecessary_map_or.rs:77:13 | LL | let _ = r.map_or(false, |x| x == 7); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -219,7 +219,7 @@ LL + let _ = r.is_ok_and(|x| x == 7); | error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:80:13 + --> tests/ui/unnecessary_map_or.rs:83:13 | LL | let _ = r.map_or(false, func); | ^^^^^^^^^^^^^^^^^^^^^ @@ -231,7 +231,7 @@ LL + let _ = r.is_ok_and(func); | error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:82:13 + --> tests/ui/unnecessary_map_or.rs:85:13 | LL | let _ = Some(5).map_or(false, func); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -243,7 +243,7 @@ LL + let _ = Some(5).is_some_and(func); | error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:84:13 + --> tests/ui/unnecessary_map_or.rs:87:13 | LL | let _ = Some(5).map_or(true, func); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -255,7 +255,7 @@ LL + let _ = Some(5).is_none_or(func); | error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:90:13 + --> tests/ui/unnecessary_map_or.rs:93:13 | LL | let _ = r.map_or(false, |x| x == 8); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -267,7 +267,7 @@ LL + let _ = r == Ok(8); | error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:111:5 + --> tests/ui/unnecessary_map_or.rs:114:5 | LL | o.map_or(true, |n| n > 5) || (o as &Option).map_or(true, |n| n < 5) | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -279,7 +279,7 @@ LL + o.is_none_or(|n| n > 5) || (o as &Option).map_or(true, |n| n < 5) | error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:111:34 + --> tests/ui/unnecessary_map_or.rs:114:34 | LL | o.map_or(true, |n| n > 5) || (o as &Option).map_or(true, |n| n < 5) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -291,7 +291,7 @@ LL + o.map_or(true, |n| n > 5) || (o as &Option).is_none_or(|n| n < 5) | error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:126:5 + --> tests/ui/unnecessary_map_or.rs:129:5 | LL | o.map_or(true, |n| n > 5) | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -303,7 +303,7 @@ LL + o.is_none_or(|n| n > 5) | error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:131:13 + --> tests/ui/unnecessary_map_or.rs:134:13 | LL | let x = a.map_or(false, |a| a == *s); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -315,7 +315,7 @@ LL + let x = a.is_some_and(|a| a == *s); | error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:133:13 + --> tests/ui/unnecessary_map_or.rs:136:13 | LL | let y = b.map_or(true, |b| b == *s); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -327,7 +327,7 @@ LL + let y = b.is_none_or(|b| b == *s); | error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:139:13 + --> tests/ui/unnecessary_map_or.rs:142:13 | LL | assert!(Some("test").map_or(false, |x| x == "test")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -339,7 +339,7 @@ LL + assert!(Some("test") == Some("test")); | error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:143:13 + --> tests/ui/unnecessary_map_or.rs:146:13 | LL | assert!(Some("test").map_or(false, |x| x == "test").then(|| 1).is_some()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -351,7 +351,7 @@ LL + assert!((Some("test") == Some("test")).then(|| 1).is_some()); | error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:160:9 + --> tests/ui/unnecessary_map_or.rs:163:9 | LL | _ = s.lock().unwrap().map_or(false, |s| s == "foo"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -363,7 +363,7 @@ LL + _ = s.lock().unwrap().is_some_and(|s| s == "foo"); | error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:164:9 + --> tests/ui/unnecessary_map_or.rs:167:9 | LL | _ = s.map_or(false, |s| s == "foo"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/unused_format_specs_width.rs b/src/tools/clippy/tests/ui/unused_format_specs_width.rs new file mode 100644 index 0000000000000..e894ea76fc186 --- /dev/null +++ b/src/tools/clippy/tests/ui/unused_format_specs_width.rs @@ -0,0 +1,41 @@ +//@no-rustfix +// Format width has no effect for certain traits (issue #15039) + +#![warn(clippy::unused_format_specs)] +#![allow(clippy::zero_ptr, clippy::manual_dangling_ptr)] + +fn main() { + // Integer formats with # (alternate): 0x/0o/0b prefix makes min width 4 + println!("{:#02X}", 1u8); //~ ERROR: format width has no effect on the output + println!("{:#2X}", 1u8); //~ ERROR: format width has no effect on the output + println!("{:#02x}", 1u8); //~ ERROR: format width has no effect on the output + println!("{:#02o}", 1u8); //~ ERROR: format width has no effect on the output + println!("{:#02b}", 1u8); //~ ERROR: format width has no effect on the output + + // Exponent formats: min width 4 (e.g. 1e0) + println!("{:02e}", 1u8); //~ ERROR: format width has no effect on the output + println!("{:02E}", 1u8); //~ ERROR: format width has no effect on the output + println!("{:2e}", 1.0); //~ ERROR: format width has no effect on the output + println!("{:2E}", 1.0); //~ ERROR: format width has no effect on the output + println!("{:2e}", 0.1); //~ ERROR: format width has no effect on the output + println!("{:2E}", 0.1); //~ ERROR: format width has no effect on the output + + // Pointer: min width 4 (0x1) + println!("{:2p}", 0 as *const usize); //~ ERROR: format width has no effect on the output + println!("{:02p}", 1 as *const usize); //~ ERROR: format width has no effect on the output + + // Width 2 still too small for exponent; precision+width + println!("{:2.2e}", 1.0); //~ ERROR: format width has no effect on the output + println!("{:2.2E}", 1.0); //~ ERROR: format width has no effect on the output + println!("{:2.2e}", 0.1); //~ ERROR: format width has no effect on the output + println!("{:2.2E}", 0.1); //~ ERROR: format width has no effect on the output + + // Width 3 is exactly the minimum for alternate hex, still warn + println!("{:#03X}", 1u8); //~ ERROR: format width has no effect on the output + + // Not linted: width more than 3, or no # for x/o/b + println!("{:#04X}", 1u8); + println!("{:2X}", 1u8); // no #, so no prefix + println!("{:2o}", 1u8); + println!("{}", 1); +} diff --git a/src/tools/clippy/tests/ui/unused_format_specs_width.stderr b/src/tools/clippy/tests/ui/unused_format_specs_width.stderr new file mode 100644 index 0000000000000..defb96ac08bf8 --- /dev/null +++ b/src/tools/clippy/tests/ui/unused_format_specs_width.stderr @@ -0,0 +1,148 @@ +error: format width has no effect on the output for this format trait + --> tests/ui/unused_format_specs_width.rs:9:15 + | +LL | println!("{:#02X}", 1u8); + | ^^^^^^^ + | + = help: consider removing the width or increasing it to at least 4 + = note: `-D clippy::unused-format-specs` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unused_format_specs)]` + +error: format width has no effect on the output for this format trait + --> tests/ui/unused_format_specs_width.rs:10:15 + | +LL | println!("{:#2X}", 1u8); + | ^^^^^^ + | + = help: consider removing the width or increasing it to at least 4 + +error: format width has no effect on the output for this format trait + --> tests/ui/unused_format_specs_width.rs:11:15 + | +LL | println!("{:#02x}", 1u8); + | ^^^^^^^ + | + = help: consider removing the width or increasing it to at least 4 + +error: format width has no effect on the output for this format trait + --> tests/ui/unused_format_specs_width.rs:12:15 + | +LL | println!("{:#02o}", 1u8); + | ^^^^^^^ + | + = help: consider removing the width or increasing it to at least 4 + +error: format width has no effect on the output for this format trait + --> tests/ui/unused_format_specs_width.rs:13:15 + | +LL | println!("{:#02b}", 1u8); + | ^^^^^^^ + | + = help: consider removing the width or increasing it to at least 4 + +error: format width has no effect on the output for this format trait + --> tests/ui/unused_format_specs_width.rs:16:15 + | +LL | println!("{:02e}", 1u8); + | ^^^^^^ + | + = help: consider removing the width or increasing it to at least 4 + +error: format width has no effect on the output for this format trait + --> tests/ui/unused_format_specs_width.rs:17:15 + | +LL | println!("{:02E}", 1u8); + | ^^^^^^ + | + = help: consider removing the width or increasing it to at least 4 + +error: format width has no effect on the output for this format trait + --> tests/ui/unused_format_specs_width.rs:18:15 + | +LL | println!("{:2e}", 1.0); + | ^^^^^ + | + = help: consider removing the width or increasing it to at least 4 + +error: format width has no effect on the output for this format trait + --> tests/ui/unused_format_specs_width.rs:19:15 + | +LL | println!("{:2E}", 1.0); + | ^^^^^ + | + = help: consider removing the width or increasing it to at least 4 + +error: format width has no effect on the output for this format trait + --> tests/ui/unused_format_specs_width.rs:20:15 + | +LL | println!("{:2e}", 0.1); + | ^^^^^ + | + = help: consider removing the width or increasing it to at least 4 + +error: format width has no effect on the output for this format trait + --> tests/ui/unused_format_specs_width.rs:21:15 + | +LL | println!("{:2E}", 0.1); + | ^^^^^ + | + = help: consider removing the width or increasing it to at least 4 + +error: format width has no effect on the output for this format trait + --> tests/ui/unused_format_specs_width.rs:24:15 + | +LL | println!("{:2p}", 0 as *const usize); + | ^^^^^ + | + = help: consider removing the width or increasing it to at least 4 + +error: format width has no effect on the output for this format trait + --> tests/ui/unused_format_specs_width.rs:25:15 + | +LL | println!("{:02p}", 1 as *const usize); + | ^^^^^^ + | + = help: consider removing the width or increasing it to at least 4 + +error: format width has no effect on the output for this format trait + --> tests/ui/unused_format_specs_width.rs:28:15 + | +LL | println!("{:2.2e}", 1.0); + | ^^^^^^^ + | + = help: consider removing the width or increasing it to at least 4 + +error: format width has no effect on the output for this format trait + --> tests/ui/unused_format_specs_width.rs:29:15 + | +LL | println!("{:2.2E}", 1.0); + | ^^^^^^^ + | + = help: consider removing the width or increasing it to at least 4 + +error: format width has no effect on the output for this format trait + --> tests/ui/unused_format_specs_width.rs:30:15 + | +LL | println!("{:2.2e}", 0.1); + | ^^^^^^^ + | + = help: consider removing the width or increasing it to at least 4 + +error: format width has no effect on the output for this format trait + --> tests/ui/unused_format_specs_width.rs:31:15 + | +LL | println!("{:2.2E}", 0.1); + | ^^^^^^^ + | + = help: consider removing the width or increasing it to at least 4 + +error: format width has no effect on the output for this format trait + --> tests/ui/unused_format_specs_width.rs:34:15 + | +LL | println!("{:#03X}", 1u8); + | ^^^^^^^ + | + = help: consider removing the width or increasing it to at least 4 + +error: aborting due to 18 previous errors + diff --git a/src/tools/clippy/tests/ui/useless_borrows_in_formatting.fixed b/src/tools/clippy/tests/ui/useless_borrows_in_formatting.fixed new file mode 100644 index 0000000000000..82319252b4688 --- /dev/null +++ b/src/tools/clippy/tests/ui/useless_borrows_in_formatting.fixed @@ -0,0 +1,118 @@ +// When testing or blessing this lint, set TESTNAME so only this test runs: +// TESTNAME=useless_borrows_in_formatting cargo uitest +// TESTNAME=useless_borrows_in_formatting cargo uibless +#![warn(clippy::useless_borrows_in_formatting)] +#![allow(unused, clippy::useless_format)] + +fn main() { + let s: &str = "hello"; + println!("{}", s); //~ useless_borrows_in_formatting + println!("{:?}", s); //~ useless_borrows_in_formatting + println!("{}", s); //~ useless_borrows_in_formatting + + let string = String::from("world"); + println!("{}", string); //~ useless_borrows_in_formatting + println!("{:?}", string); //~ useless_borrows_in_formatting + println!("{}", string); //~ useless_borrows_in_formatting + println!("{}", &string[..2]); //~ useless_borrows_in_formatting + println!("{:?}", &string[..2]); //~ useless_borrows_in_formatting + // these are ok + println!("{}", &string[..2]); + println!("{:?}", &string[..2]); + + let n: i32 = 42; + println!("{}", n); //~ useless_borrows_in_formatting + println!("{:?}", n); //~ useless_borrows_in_formatting + println!("{}", n); //~ useless_borrows_in_formatting + + // Reference to slice element + let slice: [i32; 3] = [1, 2, 3]; + println!("{}", slice[0]); //~ useless_borrows_in_formatting + println!("{:?}", slice[0]); //~ useless_borrows_in_formatting + println!("{}", slice[0]); //~ useless_borrows_in_formatting + + // big array: should not suggest removing & because of the size of the output + println!( + "{:?}", + [ + //~^ useless_borrows_in_formatting + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, + 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, + 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + ] + ); + + println!("{:?}", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0]); + //~^ useless_borrows_in_formatting + + let a: [i32; 2] = [1, 2]; + println!("{:016x?}", [a[0], a[1], a[0], a[1]]); //~ useless_borrows_in_formatting + + // &slice[0..1] with {:?}: inner type [i32] is unsized, so we don't suggest removing & + println!("{:?}", &slice[0..1]); // don't change + println!("{:?}", &slice[0..1]); //~ useless_borrows_in_formatting + + // Pointer formatting ({:p}): never suggest any changes to it + let x: i32 = 0; + println!("{:p}", &x); // don't change + println!("{:p}", &&x); // should change, but out of scope + + struct Wrap(i32); + let w: Wrap = Wrap(42); + println!("{}", w.0); //~ useless_borrows_in_formatting + println!("{:?}", w.0); //~ useless_borrows_in_formatting + println!("{}", w.0); //~ useless_borrows_in_formatting + + struct WrapRef<'a>(&'a i32); + let n: i32 = 42; + let w: WrapRef<'_> = WrapRef(&n); + println!("{}", w.0); //~ useless_borrows_in_formatting + println!("{:?}", w.0); //~ useless_borrows_in_formatting + println!("{}", w.0); //~ useless_borrows_in_formatting + + let a: &mut String = &mut String::from("foo"); + println!("{}", *a); //~ useless_borrows_in_formatting + println!("{:?}", *a); //~ useless_borrows_in_formatting + + // Parenthesized expressions: &(expr) + let n: i32 = 42; + println!("{}", (n)); //~ useless_borrows_in_formatting + println!("{:?}", (n + 1)); //~ useless_borrows_in_formatting + println!("{}", (String::from("paren"))); //~ useless_borrows_in_formatting + + // Block expressions: &{ expr } + println!("{}", { n }); //~ useless_borrows_in_formatting + println!("{:?}", { n + 1 }); //~ useless_borrows_in_formatting + println!("{}", { String::from("block") }); //~ useless_borrows_in_formatting + + let v1 = 42.12345; + let v2 = 20; + let v3 = 10; + println!("{0:1$.2$}", v1, v2, v3); + //~^ useless_borrows_in_formatting + //~| useless_borrows_in_formatting + //~| useless_borrows_in_formatting + println!("{0:1$.2$?}", v1, v2, v3); + //~^ useless_borrows_in_formatting + //~| useless_borrows_in_formatting + //~| useless_borrows_in_formatting + println!("{0:1$.2$}", v1, v2, v3); //~ useless_borrows_in_formatting + println!("{0:1$.2$}", v1, v2, v3); //~ useless_borrows_in_formatting + println!("{0:1$.2$}", v1, v2, v3); //~ useless_borrows_in_formatting + + // Macro wrapping println! - should not lint (println! call is inside macro expansion) + macro_rules! my_println { + ($($args:tt)*) => { + println!($($args)*); + }; + } + my_println!("{}", &n); + + // Arguments coming from a macro - should not lint (& comes from expansion) + macro_rules! make_ref { + ($e:expr) => { + &$e + }; + } + println!("{}", make_ref!(n)); +} diff --git a/src/tools/clippy/tests/ui/useless_borrows_in_formatting.rs b/src/tools/clippy/tests/ui/useless_borrows_in_formatting.rs new file mode 100644 index 0000000000000..6d23c9c8e7bbc --- /dev/null +++ b/src/tools/clippy/tests/ui/useless_borrows_in_formatting.rs @@ -0,0 +1,118 @@ +// When testing or blessing this lint, set TESTNAME so only this test runs: +// TESTNAME=useless_borrows_in_formatting cargo uitest +// TESTNAME=useless_borrows_in_formatting cargo uibless +#![warn(clippy::useless_borrows_in_formatting)] +#![allow(unused, clippy::useless_format)] + +fn main() { + let s: &str = "hello"; + println!("{}", &s); //~ useless_borrows_in_formatting + println!("{:?}", &s); //~ useless_borrows_in_formatting + println!("{}", &&s); //~ useless_borrows_in_formatting + + let string = String::from("world"); + println!("{}", &string); //~ useless_borrows_in_formatting + println!("{:?}", &string); //~ useless_borrows_in_formatting + println!("{}", &&string); //~ useless_borrows_in_formatting + println!("{}", &&string[..2]); //~ useless_borrows_in_formatting + println!("{:?}", &&string[..2]); //~ useless_borrows_in_formatting + // these are ok + println!("{}", &string[..2]); + println!("{:?}", &string[..2]); + + let n: i32 = 42; + println!("{}", &n); //~ useless_borrows_in_formatting + println!("{:?}", &n); //~ useless_borrows_in_formatting + println!("{}", &&n); //~ useless_borrows_in_formatting + + // Reference to slice element + let slice: [i32; 3] = [1, 2, 3]; + println!("{}", &slice[0]); //~ useless_borrows_in_formatting + println!("{:?}", &slice[0]); //~ useless_borrows_in_formatting + println!("{}", &&slice[0]); //~ useless_borrows_in_formatting + + // big array: should not suggest removing & because of the size of the output + println!( + "{:?}", + &[ + //~^ useless_borrows_in_formatting + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, + 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, + 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + ] + ); + + println!("{:?}", &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0]); + //~^ useless_borrows_in_formatting + + let a: [i32; 2] = [1, 2]; + println!("{:016x?}", &[a[0], a[1], a[0], a[1]]); //~ useless_borrows_in_formatting + + // &slice[0..1] with {:?}: inner type [i32] is unsized, so we don't suggest removing & + println!("{:?}", &slice[0..1]); // don't change + println!("{:?}", &&slice[0..1]); //~ useless_borrows_in_formatting + + // Pointer formatting ({:p}): never suggest any changes to it + let x: i32 = 0; + println!("{:p}", &x); // don't change + println!("{:p}", &&x); // should change, but out of scope + + struct Wrap(i32); + let w: Wrap = Wrap(42); + println!("{}", &w.0); //~ useless_borrows_in_formatting + println!("{:?}", &w.0); //~ useless_borrows_in_formatting + println!("{}", &&w.0); //~ useless_borrows_in_formatting + + struct WrapRef<'a>(&'a i32); + let n: i32 = 42; + let w: WrapRef<'_> = WrapRef(&n); + println!("{}", &w.0); //~ useless_borrows_in_formatting + println!("{:?}", &w.0); //~ useless_borrows_in_formatting + println!("{}", &&w.0); //~ useless_borrows_in_formatting + + let a: &mut String = &mut String::from("foo"); + println!("{}", &*a); //~ useless_borrows_in_formatting + println!("{:?}", &*a); //~ useless_borrows_in_formatting + + // Parenthesized expressions: &(expr) + let n: i32 = 42; + println!("{}", &(n)); //~ useless_borrows_in_formatting + println!("{:?}", &(n + 1)); //~ useless_borrows_in_formatting + println!("{}", &(String::from("paren"))); //~ useless_borrows_in_formatting + + // Block expressions: &{ expr } + println!("{}", &{ n }); //~ useless_borrows_in_formatting + println!("{:?}", &{ n + 1 }); //~ useless_borrows_in_formatting + println!("{}", &{ String::from("block") }); //~ useless_borrows_in_formatting + + let v1 = 42.12345; + let v2 = 20; + let v3 = 10; + println!("{0:1$.2$}", &v1, &v2, &v3); + //~^ useless_borrows_in_formatting + //~| useless_borrows_in_formatting + //~| useless_borrows_in_formatting + println!("{0:1$.2$?}", &v1, &v2, &v3); + //~^ useless_borrows_in_formatting + //~| useless_borrows_in_formatting + //~| useless_borrows_in_formatting + println!("{0:1$.2$}", &v1, v2, v3); //~ useless_borrows_in_formatting + println!("{0:1$.2$}", v1, &v2, v3); //~ useless_borrows_in_formatting + println!("{0:1$.2$}", v1, v2, &v3); //~ useless_borrows_in_formatting + + // Macro wrapping println! - should not lint (println! call is inside macro expansion) + macro_rules! my_println { + ($($args:tt)*) => { + println!($($args)*); + }; + } + my_println!("{}", &n); + + // Arguments coming from a macro - should not lint (& comes from expansion) + macro_rules! make_ref { + ($e:expr) => { + &$e + }; + } + println!("{}", make_ref!(n)); +} diff --git a/src/tools/clippy/tests/ui/useless_borrows_in_formatting.stderr b/src/tools/clippy/tests/ui/useless_borrows_in_formatting.stderr new file mode 100644 index 0000000000000..e9e028c81ac43 --- /dev/null +++ b/src/tools/clippy/tests/ui/useless_borrows_in_formatting.stderr @@ -0,0 +1,266 @@ +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:9:20 + | +LL | println!("{}", &s); + | ^^ help: remove the redundant `&`: `s` + | + = note: `-D clippy::useless-borrows-in-formatting` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::useless_borrows_in_formatting)]` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:10:22 + | +LL | println!("{:?}", &s); + | ^^ help: remove the redundant `&`: `s` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:11:20 + | +LL | println!("{}", &&s); + | ^^^ help: remove the redundant `&`: `s` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:14:20 + | +LL | println!("{}", &string); + | ^^^^^^^ help: remove the redundant `&`: `string` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:15:22 + | +LL | println!("{:?}", &string); + | ^^^^^^^ help: remove the redundant `&`: `string` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:16:20 + | +LL | println!("{}", &&string); + | ^^^^^^^^ help: remove the redundant `&`: `string` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:17:20 + | +LL | println!("{}", &&string[..2]); + | ^^^^^^^^^^^^^ help: remove the redundant `&`: `&string[..2]` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:18:22 + | +LL | println!("{:?}", &&string[..2]); + | ^^^^^^^^^^^^^ help: remove the redundant `&`: `&string[..2]` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:24:20 + | +LL | println!("{}", &n); + | ^^ help: remove the redundant `&`: `n` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:25:22 + | +LL | println!("{:?}", &n); + | ^^ help: remove the redundant `&`: `n` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:26:20 + | +LL | println!("{}", &&n); + | ^^^ help: remove the redundant `&`: `n` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:30:20 + | +LL | println!("{}", &slice[0]); + | ^^^^^^^^^ help: remove the redundant `&`: `slice[0]` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:31:22 + | +LL | println!("{:?}", &slice[0]); + | ^^^^^^^^^ help: remove the redundant `&`: `slice[0]` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:32:20 + | +LL | println!("{}", &&slice[0]); + | ^^^^^^^^^^ help: remove the redundant `&`: `slice[0]` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:37:9 + | +LL | / &[ +LL | | +LL | | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, +LL | | 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, +LL | | 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, +LL | | ] + | |_________^ + | +help: remove the redundant `&` + | +LL ~ [ +LL + +LL + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, +LL + 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, +LL + 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, +LL + ] + | + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:45:22 + | +LL | println!("{:?}", &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the redundant `&`: `[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0]` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:49:26 + | +LL | println!("{:016x?}", &[a[0], a[1], a[0], a[1]]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the redundant `&`: `[a[0], a[1], a[0], a[1]]` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:53:22 + | +LL | println!("{:?}", &&slice[0..1]); + | ^^^^^^^^^^^^^ help: remove the redundant `&`: `&slice[0..1]` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:62:20 + | +LL | println!("{}", &w.0); + | ^^^^ help: remove the redundant `&`: `w.0` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:63:22 + | +LL | println!("{:?}", &w.0); + | ^^^^ help: remove the redundant `&`: `w.0` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:64:20 + | +LL | println!("{}", &&w.0); + | ^^^^^ help: remove the redundant `&`: `w.0` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:69:20 + | +LL | println!("{}", &w.0); + | ^^^^ help: remove the redundant `&`: `w.0` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:70:22 + | +LL | println!("{:?}", &w.0); + | ^^^^ help: remove the redundant `&`: `w.0` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:71:20 + | +LL | println!("{}", &&w.0); + | ^^^^^ help: remove the redundant `&`: `w.0` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:74:20 + | +LL | println!("{}", &*a); + | ^^^ help: remove the redundant `&`: `*a` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:75:22 + | +LL | println!("{:?}", &*a); + | ^^^ help: remove the redundant `&`: `*a` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:79:20 + | +LL | println!("{}", &(n)); + | ^^^^ help: remove the redundant `&`: `(n)` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:80:22 + | +LL | println!("{:?}", &(n + 1)); + | ^^^^^^^^ help: remove the redundant `&`: `(n + 1)` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:81:20 + | +LL | println!("{}", &(String::from("paren"))); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the redundant `&`: `(String::from("paren"))` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:84:20 + | +LL | println!("{}", &{ n }); + | ^^^^^^ help: remove the redundant `&`: `{ n }` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:85:22 + | +LL | println!("{:?}", &{ n + 1 }); + | ^^^^^^^^^^ help: remove the redundant `&`: `{ n + 1 }` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:86:20 + | +LL | println!("{}", &{ String::from("block") }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the redundant `&`: `{ String::from("block") }` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:91:27 + | +LL | println!("{0:1$.2$}", &v1, &v2, &v3); + | ^^^ help: remove the redundant `&`: `v1` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:91:32 + | +LL | println!("{0:1$.2$}", &v1, &v2, &v3); + | ^^^ help: remove the redundant `&`: `v2` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:91:37 + | +LL | println!("{0:1$.2$}", &v1, &v2, &v3); + | ^^^ help: remove the redundant `&`: `v3` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:95:28 + | +LL | println!("{0:1$.2$?}", &v1, &v2, &v3); + | ^^^ help: remove the redundant `&`: `v1` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:95:33 + | +LL | println!("{0:1$.2$?}", &v1, &v2, &v3); + | ^^^ help: remove the redundant `&`: `v2` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:95:38 + | +LL | println!("{0:1$.2$?}", &v1, &v2, &v3); + | ^^^ help: remove the redundant `&`: `v3` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:99:27 + | +LL | println!("{0:1$.2$}", &v1, v2, v3); + | ^^^ help: remove the redundant `&`: `v1` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:100:31 + | +LL | println!("{0:1$.2$}", v1, &v2, v3); + | ^^^ help: remove the redundant `&`: `v2` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:101:35 + | +LL | println!("{0:1$.2$}", v1, v2, &v3); + | ^^^ help: remove the redundant `&`: `v3` + +error: aborting due to 41 previous errors + diff --git a/src/tools/clippy/tests/ui/useless_conversion.fixed b/src/tools/clippy/tests/ui/useless_conversion.fixed index d0297ef6bcdc0..a22df7013f988 100644 --- a/src/tools/clippy/tests/ui/useless_conversion.fixed +++ b/src/tools/clippy/tests/ui/useless_conversion.fixed @@ -72,13 +72,13 @@ fn lint_into_iter_on_expr_implementing_iterator_2() { #[allow(const_item_mutation)] fn lint_into_iter_on_const_implementing_iterator() { - const NUMBERS: std::ops::Range = 0..10; + const NUMBERS: std::iter::Empty = std::iter::empty(); let _ = NUMBERS.next(); //~^ useless_conversion } fn lint_into_iter_on_const_implementing_iterator_2() { - const NUMBERS: std::ops::Range = 0..10; + const NUMBERS: std::iter::Empty = std::iter::empty(); let mut n = NUMBERS; //~^ useless_conversion n.next(); @@ -423,10 +423,8 @@ mod issue11819 { } } -fn issue14739() { - use std::ops::Range; - - const R: Range = 2..7; +fn issue14800() { + const R: std::iter::Empty = std::iter::empty(); R.into_iter().all(|_x| true); // no lint @@ -438,6 +436,33 @@ fn issue14739() { //~^ useless_conversion } +// In a future edition of Rust or with the unstable `feature(new_range)`, the syntax `a..b` +// will change from producing type `core::ops::Range`, which implements `Iterator`, to +// producing type `core::range::Range`, which implements `IntoIterator`. +// +// Therefore, an `.into_iter()` call that is technically useless today will be useful for +// edition migration or unstable feature testing; do not remove it. +// +// This test case tests that the ranges produced *by range syntax* aren’t linted on, which +// should be true both before and after the expected 2027 edition migration (but after such +// migration, this test will not really be testing anything). +fn do_not_lint_on_ops_range_into_iter_before_method() { + #![allow(clippy::never_loop)] + + // No lint on these + (0..10).into_iter().for_each(drop); + (0..=10).into_iter().for_each(drop); + (0..).into_iter().take(10).for_each(drop); + + // But do still lint on for loops + for _ in (0..10) {} //~ useless_conversion + for _ in (0..=10) {} //~ useless_conversion + for _ in (0..) { + //~^ useless_conversion + break; + } +} + fn issue16165() { macro_rules! mac { (iter $e:expr) => { diff --git a/src/tools/clippy/tests/ui/useless_conversion.rs b/src/tools/clippy/tests/ui/useless_conversion.rs index 20a0f6d72f9a8..1f170cf87ac58 100644 --- a/src/tools/clippy/tests/ui/useless_conversion.rs +++ b/src/tools/clippy/tests/ui/useless_conversion.rs @@ -72,13 +72,13 @@ fn lint_into_iter_on_expr_implementing_iterator_2() { #[allow(const_item_mutation)] fn lint_into_iter_on_const_implementing_iterator() { - const NUMBERS: std::ops::Range = 0..10; + const NUMBERS: std::iter::Empty = std::iter::empty(); let _ = NUMBERS.into_iter().next(); //~^ useless_conversion } fn lint_into_iter_on_const_implementing_iterator_2() { - const NUMBERS: std::ops::Range = 0..10; + const NUMBERS: std::iter::Empty = std::iter::empty(); let mut n = NUMBERS.into_iter(); //~^ useless_conversion n.next(); @@ -423,10 +423,8 @@ mod issue11819 { } } -fn issue14739() { - use std::ops::Range; - - const R: Range = 2..7; +fn issue14800() { + const R: std::iter::Empty = std::iter::empty(); R.into_iter().all(|_x| true); // no lint @@ -438,6 +436,33 @@ fn issue14739() { //~^ useless_conversion } +// In a future edition of Rust or with the unstable `feature(new_range)`, the syntax `a..b` +// will change from producing type `core::ops::Range`, which implements `Iterator`, to +// producing type `core::range::Range`, which implements `IntoIterator`. +// +// Therefore, an `.into_iter()` call that is technically useless today will be useful for +// edition migration or unstable feature testing; do not remove it. +// +// This test case tests that the ranges produced *by range syntax* aren’t linted on, which +// should be true both before and after the expected 2027 edition migration (but after such +// migration, this test will not really be testing anything). +fn do_not_lint_on_ops_range_into_iter_before_method() { + #![allow(clippy::never_loop)] + + // No lint on these + (0..10).into_iter().for_each(drop); + (0..=10).into_iter().for_each(drop); + (0..).into_iter().take(10).for_each(drop); + + // But do still lint on for loops + for _ in (0..10).into_iter() {} //~ useless_conversion + for _ in (0..=10).into_iter() {} //~ useless_conversion + for _ in (0..).into_iter() { + //~^ useless_conversion + break; + } +} + fn issue16165() { macro_rules! mac { (iter $e:expr) => { diff --git a/src/tools/clippy/tests/ui/useless_conversion.stderr b/src/tools/clippy/tests/ui/useless_conversion.stderr index 18d5c9d28c0dc..7042e18a2cad8 100644 --- a/src/tools/clippy/tests/ui/useless_conversion.stderr +++ b/src/tools/clippy/tests/ui/useless_conversion.stderr @@ -40,13 +40,13 @@ error: useless conversion to the same type: `std::str::Lines<'_>` LL | if Some("ok") == text.lines().into_iter().next() {} | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `text.lines()` -error: useless conversion to the same type: `std::ops::Range` +error: useless conversion to the same type: `std::iter::Empty` --> tests/ui/useless_conversion.rs:76:13 | LL | let _ = NUMBERS.into_iter().next(); | ^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `NUMBERS` -error: useless conversion to the same type: `std::ops::Range` +error: useless conversion to the same type: `std::iter::Empty` --> tests/ui/useless_conversion.rs:82:17 | LL | let mut n = NUMBERS.into_iter(); @@ -421,32 +421,50 @@ LL - takes_into_iter(self.my_field.into_iter()); LL + takes_into_iter(&mut *self.my_field); | -error: useless conversion to the same type: `std::ops::Range` - --> tests/ui/useless_conversion.rs:435:5 +error: useless conversion to the same type: `std::iter::Empty` + --> tests/ui/useless_conversion.rs:433:5 | LL | R.into_iter().for_each(|_x| {}); | ^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `R` -error: useless conversion to the same type: `std::ops::Range` - --> tests/ui/useless_conversion.rs:437:13 +error: useless conversion to the same type: `std::iter::Empty` + --> tests/ui/useless_conversion.rs:435:13 | LL | let _ = R.into_iter().map(|_x| 0); | ^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `R` +error: useless conversion to the same type: `std::ops::Range` + --> tests/ui/useless_conversion.rs:458:14 + | +LL | for _ in (0..10).into_iter() {} + | ^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `(0..10)` + +error: useless conversion to the same type: `std::ops::RangeInclusive` + --> tests/ui/useless_conversion.rs:459:14 + | +LL | for _ in (0..=10).into_iter() {} + | ^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `(0..=10)` + +error: useless conversion to the same type: `std::ops::RangeFrom` + --> tests/ui/useless_conversion.rs:460:14 + | +LL | for _ in (0..).into_iter() { + | ^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `(0..)` + error: useless conversion to the same type: `std::slice::Iter<'_, i32>` - --> tests/ui/useless_conversion.rs:448:14 + --> tests/ui/useless_conversion.rs:473:14 | LL | for _ in mac!(iter [1, 2]).into_iter() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `mac!(iter [1, 2])` error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:461:27 + --> tests/ui/useless_conversion.rs:486:27 | LL | takes_into_iter_usize(b.into_iter()); | ^^^^^^^^^^^^^ | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:452:34 + --> tests/ui/useless_conversion.rs:477:34 | LL | fn takes_into_iter_usize(_: impl IntoIterator) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -457,13 +475,13 @@ LL + takes_into_iter_usize(b); | error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:470:31 + --> tests/ui/useless_conversion.rs:495:31 | LL | takes_into_iter_usize(b.clone().into_iter()); | ^^^^^^^^^^^^^^^^^^^^^ | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:452:34 + --> tests/ui/useless_conversion.rs:477:34 | LL | fn takes_into_iter_usize(_: impl IntoIterator) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -474,13 +492,13 @@ LL + takes_into_iter_usize(b.clone()); | error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:478:34 + --> tests/ui/useless_conversion.rs:503:34 | LL | takes_into_iter_usize_result(b.clone().into_iter())?; | ^^^^^^^^^^^^^^^^^^^^^ | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:453:41 + --> tests/ui/useless_conversion.rs:478:41 | LL | fn takes_into_iter_usize_result(_: impl IntoIterator) -> Result<(), ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -490,5 +508,5 @@ LL - takes_into_iter_usize_result(b.clone().into_iter())?; LL + takes_into_iter_usize_result(b.clone())?; | -error: aborting due to 48 previous errors +error: aborting due to 51 previous errors diff --git a/src/tools/clippy/tests/ui/zero_offset.rs b/src/tools/clippy/tests/ui/zero_offset.rs index 5a9c3ac9248fa..93ed50fde488f 100644 --- a/src/tools/clippy/tests/ui/zero_offset.rs +++ b/src/tools/clippy/tests/ui/zero_offset.rs @@ -29,5 +29,18 @@ fn main() { let sized = &1 as *const i32; sized.offset(0); + + let nn = core::ptr::NonNull::<()>::dangling(); + nn.add(0); + //~^ zst_offset + + nn.offset(0); + //~^ zst_offset + + nn.sub(0); + //~^ zst_offset + + let nn_sized = core::ptr::NonNull::::dangling(); + nn_sized.add(0); } } diff --git a/src/tools/clippy/tests/ui/zero_offset.stderr b/src/tools/clippy/tests/ui/zero_offset.stderr index b69c7b92d56a6..e78620ddcba4c 100644 --- a/src/tools/clippy/tests/ui/zero_offset.stderr +++ b/src/tools/clippy/tests/ui/zero_offset.stderr @@ -48,5 +48,23 @@ error: offset calculation on zero-sized value LL | c.wrapping_sub(0); | ^^^^^^^^^^^^^^^^^ -error: aborting due to 8 previous errors +error: offset calculation on zero-sized value + --> tests/ui/zero_offset.rs:34:9 + | +LL | nn.add(0); + | ^^^^^^^^^ + +error: offset calculation on zero-sized value + --> tests/ui/zero_offset.rs:37:9 + | +LL | nn.offset(0); + | ^^^^^^^^^^^^ + +error: offset calculation on zero-sized value + --> tests/ui/zero_offset.rs:40:9 + | +LL | nn.sub(0); + | ^^^^^^^^^ + +error: aborting due to 11 previous errors diff --git a/src/tools/clippy/triagebot.toml b/src/tools/clippy/triagebot.toml index 8e7c36dac8412..869b8d730507a 100644 --- a/src/tools/clippy/triagebot.toml +++ b/src/tools/clippy/triagebot.toml @@ -93,6 +93,7 @@ users_on_vacation = [ "Alexendoo", "y21", "blyxyas", + "samueltardieu", ] [assign.owners]