From 1c77b0b2677f04f535137cdf582c98a281309a1b Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Sun, 27 Aug 2023 11:43:45 +0000 Subject: [PATCH 01/48] Move needless_raw_string_hashes to pedantic --- clippy_lints/src/raw_strings.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/raw_strings.rs b/clippy_lints/src/raw_strings.rs index ccabb577cb722..edd4d961b0c7a 100644 --- a/clippy_lints/src/raw_strings.rs +++ b/clippy_lints/src/raw_strings.rs @@ -49,7 +49,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.72.0"] pub NEEDLESS_RAW_STRING_HASHES, - style, + pedantic, "suggests reducing the number of hashes around a raw string literal" } impl_lint_pass!(RawStrings => [NEEDLESS_RAW_STRINGS, NEEDLESS_RAW_STRING_HASHES]); From b5941a2fd5f01de1888c2e781883b6c3cbdaeef5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Sun, 3 Sep 2023 06:31:56 +0200 Subject: [PATCH 02/48] Partially outline code inside the panic! macro --- .../src/mixed_read_write_in_expression.rs | 23 +++++++++++++++---- clippy_utils/src/macros.rs | 16 ++++++++++--- tests/ui/match_same_arms_non_exhaustive.rs | 20 +++++++++------- .../ui/match_same_arms_non_exhaustive.stderr | 16 ++++++------- 4 files changed, 52 insertions(+), 23 deletions(-) diff --git a/clippy_lints/src/mixed_read_write_in_expression.rs b/clippy_lints/src/mixed_read_write_in_expression.rs index 367cd6bd41337..ea6a9bc5657da 100644 --- a/clippy_lints/src/mixed_read_write_in_expression.rs +++ b/clippy_lints/src/mixed_read_write_in_expression.rs @@ -134,15 +134,30 @@ impl<'a, 'tcx> DivergenceVisitor<'a, 'tcx> { } } +fn stmt_might_diverge(stmt: &Stmt<'_>) -> bool { + match stmt.kind { + StmtKind::Item(..) => false, + _ => true, + } +} + impl<'a, 'tcx> Visitor<'tcx> for DivergenceVisitor<'a, 'tcx> { fn visit_expr(&mut self, e: &'tcx Expr<'_>) { match e.kind { // fix #10776 ExprKind::Block(block, ..) => match (block.stmts, block.expr) { - ([], Some(e)) => self.visit_expr(e), - ([stmt], None) => match stmt.kind { - StmtKind::Expr(e) | StmtKind::Semi(e) => self.visit_expr(e), - _ => {}, + (stmts, Some(e)) => { + if stmts.iter().all(|stmt| !stmt_might_diverge(stmt)) { + self.visit_expr(e) + } + }, + ([first @ .., stmt], None) => { + if first.iter().all(|stmt| !stmt_might_diverge(stmt)) { + match stmt.kind { + StmtKind::Expr(e) | StmtKind::Semi(e) => self.visit_expr(e), + _ => {}, + } + } }, _ => {}, }, diff --git a/clippy_utils/src/macros.rs b/clippy_utils/src/macros.rs index 173f9841d4469..94334f55109b2 100644 --- a/clippy_utils/src/macros.rs +++ b/clippy_utils/src/macros.rs @@ -227,16 +227,26 @@ pub enum PanicExpn<'a> { impl<'a> PanicExpn<'a> { pub fn parse(expr: &'a Expr<'a>) -> Option { - let ExprKind::Call(callee, [arg, rest @ ..]) = &expr.kind else { + let ExprKind::Call(callee, args) = &expr.kind else { return None; }; let ExprKind::Path(QPath::Resolved(_, path)) = &callee.kind else { return None; }; - let result = match path.segments.last().unwrap().ident.as_str() { + let name = path.segments.last().unwrap().ident.as_str(); + + // This has no argument + if name == "panic_cold_explicit" { + return Some(Self::Empty); + }; + + let [arg, rest @ ..] = args else { + return None; + }; + let result = match name { "panic" if arg.span.ctxt() == expr.span.ctxt() => Self::Empty, "panic" | "panic_str" => Self::Str(arg), - "panic_display" => { + "panic_display" | "panic_cold_display" => { let ExprKind::AddrOf(_, _, e) = &arg.kind else { return None; }; diff --git a/tests/ui/match_same_arms_non_exhaustive.rs b/tests/ui/match_same_arms_non_exhaustive.rs index 1ee048bf7f6c3..e1b95aa577604 100644 --- a/tests/ui/match_same_arms_non_exhaustive.rs +++ b/tests/ui/match_same_arms_non_exhaustive.rs @@ -4,14 +4,18 @@ //@no-rustfix use std::sync::atomic::Ordering; // #[non_exhaustive] enum +fn repeat() -> ! { + panic!() +} + pub fn f(x: Ordering) { match x { Ordering::Relaxed => println!("relaxed"), Ordering::Release => println!("release"), Ordering::Acquire => println!("acquire"), - Ordering::AcqRel | Ordering::SeqCst => panic!(), + Ordering::AcqRel | Ordering::SeqCst => repeat(), #[deny(non_exhaustive_omitted_patterns)] - _ => panic!(), + _ => repeat(), } } @@ -25,8 +29,8 @@ mod f { Ordering::Relaxed => println!("relaxed"), Ordering::Release => println!("release"), Ordering::Acquire => println!("acquire"), - Ordering::AcqRel | Ordering::SeqCst => panic!(), - _ => panic!(), + Ordering::AcqRel | Ordering::SeqCst => repeat(), + _ => repeat(), } } } @@ -38,9 +42,9 @@ pub fn g(x: Ordering) { Ordering::Relaxed => println!("relaxed"), Ordering::Release => println!("release"), Ordering::Acquire => println!("acquire"), - Ordering::AcqRel | Ordering::SeqCst => panic!(), + Ordering::AcqRel | Ordering::SeqCst => repeat(), //~^ ERROR: this match arm has an identical body to the `_` wildcard arm - _ => panic!(), + _ => repeat(), } } @@ -52,9 +56,9 @@ mod g { Ordering::Relaxed => println!("relaxed"), Ordering::Release => println!("release"), Ordering::Acquire => println!("acquire"), - Ordering::AcqRel | Ordering::SeqCst => panic!(), + Ordering::AcqRel | Ordering::SeqCst => repeat(), //~^ ERROR: this match arm has an identical body to the `_` wildcard arm - _ => panic!(), + _ => repeat(), } } } diff --git a/tests/ui/match_same_arms_non_exhaustive.stderr b/tests/ui/match_same_arms_non_exhaustive.stderr index a039536338bcb..ae6b02ab1b507 100644 --- a/tests/ui/match_same_arms_non_exhaustive.stderr +++ b/tests/ui/match_same_arms_non_exhaustive.stderr @@ -1,29 +1,29 @@ error: this match arm has an identical body to the `_` wildcard arm - --> $DIR/match_same_arms_non_exhaustive.rs:41:9 + --> $DIR/match_same_arms_non_exhaustive.rs:45:9 | -LL | Ordering::AcqRel | Ordering::SeqCst => panic!(), +LL | Ordering::AcqRel | Ordering::SeqCst => repeat(), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try removing the arm | = help: or try changing either arm body note: `_` wildcard arm here - --> $DIR/match_same_arms_non_exhaustive.rs:43:9 + --> $DIR/match_same_arms_non_exhaustive.rs:47:9 | -LL | _ => panic!(), +LL | _ => repeat(), | ^^^^^^^^^^^^^ = note: `-D clippy::match-same-arms` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::match_same_arms)]` error: this match arm has an identical body to the `_` wildcard arm - --> $DIR/match_same_arms_non_exhaustive.rs:55:13 + --> $DIR/match_same_arms_non_exhaustive.rs:59:13 | -LL | Ordering::AcqRel | Ordering::SeqCst => panic!(), +LL | Ordering::AcqRel | Ordering::SeqCst => repeat(), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try removing the arm | = help: or try changing either arm body note: `_` wildcard arm here - --> $DIR/match_same_arms_non_exhaustive.rs:57:13 + --> $DIR/match_same_arms_non_exhaustive.rs:61:13 | -LL | _ => panic!(), +LL | _ => repeat(), | ^^^^^^^^^^^^^ error: aborting due to 2 previous errors From 39f7f695dab8287c06adf04dc0a8d46f6ce1e2be Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Sat, 23 Sep 2023 18:11:56 +0000 Subject: [PATCH 03/48] Add msrv test template for `cargo dev new_lint --msrv` --- clippy_dev/src/new_lint.rs | 63 ++++++++++++++++++++++++++++++-------- 1 file changed, 51 insertions(+), 12 deletions(-) diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index e64cf2c874968..be2386bb1d24b 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -58,7 +58,7 @@ pub fn create( }; create_lint(&lint, msrv).context("Unable to create lint implementation")?; - create_test(&lint).context("Unable to create a test for the new lint")?; + create_test(&lint, msrv).context("Unable to create a test for the new lint")?; if lint.ty.is_none() { add_lint(&lint, msrv).context("Unable to add lint to clippy_lints/src/lib.rs")?; @@ -88,15 +88,21 @@ fn create_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> { } } -fn create_test(lint: &LintData<'_>) -> io::Result<()> { - fn create_project_layout>(lint_name: &str, location: P, case: &str, hint: &str) -> io::Result<()> { +fn create_test(lint: &LintData<'_>, msrv: bool) -> io::Result<()> { + fn create_project_layout>( + lint_name: &str, + location: P, + case: &str, + hint: &str, + msrv: bool, + ) -> io::Result<()> { let mut path = location.into().join(case); fs::create_dir(&path)?; write_file(path.join("Cargo.toml"), get_manifest_contents(lint_name, hint))?; path.push("src"); fs::create_dir(&path)?; - write_file(path.join("main.rs"), get_test_file_contents(lint_name))?; + write_file(path.join("main.rs"), get_test_file_contents(lint_name, msrv))?; Ok(()) } @@ -106,13 +112,25 @@ fn create_test(lint: &LintData<'_>) -> io::Result<()> { let test_dir = lint.project_root.join(&relative_test_dir); fs::create_dir(&test_dir)?; - create_project_layout(lint.name, &test_dir, "fail", "Content that triggers the lint goes here")?; - create_project_layout(lint.name, &test_dir, "pass", "This file should not trigger the lint")?; + create_project_layout( + lint.name, + &test_dir, + "fail", + "Content that triggers the lint goes here", + msrv, + )?; + create_project_layout( + lint.name, + &test_dir, + "pass", + "This file should not trigger the lint", + false, + )?; println!("Generated test directories: `{relative_test_dir}/pass`, `{relative_test_dir}/fail`"); } else { let test_path = format!("tests/ui/{}.rs", lint.name); - let test_contents = get_test_file_contents(lint.name); + let test_contents = get_test_file_contents(lint.name, msrv); write_file(lint.project_root.join(&test_path), test_contents)?; println!("Generated test file: `{test_path}`"); @@ -194,8 +212,8 @@ pub(crate) fn get_stabilization_version() -> String { parse_manifest(&contents).expect("Unable to find package version in `Cargo.toml`") } -fn get_test_file_contents(lint_name: &str) -> String { - formatdoc!( +fn get_test_file_contents(lint_name: &str, msrv: bool) -> String { + let mut test = formatdoc!( r#" #![warn(clippy::{lint_name})] @@ -203,7 +221,29 @@ fn get_test_file_contents(lint_name: &str) -> String { // test code goes here }} "# - ) + ); + + if msrv { + let _ = writedoc!( + test, + r#" + + // TODO: set xx to the version one below the MSRV used by the lint, and yy to + // the version used by the lint + #[clippy::msrv = "1.xx"] + fn msrv_1_xx() {{ + // a simple example that would trigger the lint if the MSRV were met + }} + + #[clippy::msrv = "1.yy"] + fn msrv_1_yy() {{ + // the same example as above + }} + "# + ); + } + + test } fn get_manifest_contents(lint_name: &str, hint: &str) -> String { @@ -258,7 +298,7 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { ) }); - let _: fmt::Result = write!(result, "{}", get_lint_declaration(&name_upper, category)); + let _: fmt::Result = writeln!(result, "{}", get_lint_declaration(&name_upper, category)); result.push_str(&if enable_msrv { formatdoc!( @@ -281,7 +321,6 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { }} // TODO: Add MSRV level to `clippy_utils/src/msrvs.rs` if needed. - // TODO: Add MSRV test to `tests/ui/min_rust_version_attr.rs`. // TODO: Update msrv config comment in `clippy_lints/src/utils/conf.rs` "# ) From 772296c50e28920ac2611587d21b543df73e0e83 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Mon, 25 Sep 2023 11:28:58 +0200 Subject: [PATCH 04/48] Merge commit '7671c283a50b5d1168841f3014b14000f01dd204' into clippyup --- .github/workflows/remark.yml | 8 +- CHANGELOG.md | 5 + Cargo.toml | 1 - book/src/lint_configuration.md | 14 +- clippy_lints/src/attrs.rs | 2 +- clippy_lints/src/casts/cast_lossless.rs | 2 +- .../src/casts/cast_possible_truncation.rs | 2 +- clippy_lints/src/casts/mod.rs | 26 + clippy_lints/src/casts/zero_ptr.rs | 39 ++ clippy_lints/src/declared_lints.rs | 6 +- .../src/default_union_representation.rs | 22 +- clippy_lints/src/dereference.rs | 374 +----------- clippy_lints/src/doc.rs | 3 +- clippy_lints/src/enum_variants.rs | 5 +- clippy_lints/src/error_impl_error.rs | 8 +- clippy_lints/src/explicit_write.rs | 90 ++- .../src/extra_unused_type_parameters.rs | 7 +- clippy_lints/src/format.rs | 14 +- clippy_lints/src/format_args.rs | 20 +- clippy_lints/src/format_impl.rs | 41 +- clippy_lints/src/large_const_arrays.rs | 5 +- clippy_lints/src/large_futures.rs | 18 +- clippy_lints/src/len_zero.rs | 8 + clippy_lints/src/lib.rs | 21 +- clippy_lints/src/loops/utils.rs | 3 +- clippy_lints/src/matches/needless_match.rs | 17 +- clippy_lints/src/matches/redundant_guards.rs | 101 +++- clippy_lints/src/methods/expect_fun_call.rs | 13 +- .../src/methods/filter_map_bool_then.rs | 11 +- clippy_lints/src/methods/mod.rs | 89 ++- .../src/methods/path_ends_with_ext.rs | 53 ++ clippy_lints/src/methods/redundant_as_str.rs | 34 ++ clippy_lints/src/misc.rs | 176 ++---- clippy_lints/src/missing_const_for_fn.rs | 12 +- .../src/needless_borrows_for_generic_args.rs | 410 ++++++++++++++ clippy_lints/src/needless_pass_by_ref_mut.rs | 71 ++- clippy_lints/src/no_effect.rs | 11 +- clippy_lints/src/non_canonical_impls.rs | 18 +- clippy_lints/src/non_copy_const.rs | 15 +- clippy_lints/src/ptr.rs | 21 +- clippy_lints/src/raw_strings.rs | 7 +- .../src/transmute/transmute_null_to_fn.rs | 30 +- clippy_lints/src/types/mod.rs | 11 +- clippy_lints/src/unit_types/let_unit_value.rs | 3 +- .../src/unnecessary_map_on_constructor.rs | 93 +++ clippy_lints/src/useless_conversion.rs | 110 +++- clippy_lints/src/utils/conf.rs | 9 +- .../src/utils/format_args_collector.rs | 28 +- .../interning_defined_symbol.rs | 2 +- .../src/utils/internal_lints/invalid_paths.rs | 18 +- .../internal_lints/metadata_collector.rs | 37 +- .../utils/internal_lints/msrv_attr_impl.rs | 15 +- .../internal_lints/unnecessary_def_path.rs | 6 +- clippy_lints/src/write.rs | 10 +- clippy_utils/src/consts.rs | 5 +- clippy_utils/src/lib.rs | 29 +- clippy_utils/src/macros.rs | 35 +- clippy_utils/src/mir/mod.rs | 40 +- clippy_utils/src/ty.rs | 3 +- clippy_utils/src/ty/type_certainty/mod.rs | 14 +- rust-toolchain | 2 +- tests/compile-test.rs | 2 - .../clippy.toml | 1 + .../decimal_literal_representation.fixed | 6 + .../decimal_literal_representation.rs | 6 + .../decimal_literal_representation.stderr | 11 + .../disallowed_script_idents/clippy.toml | 1 + .../disallowed_script_idents.rs | 6 + .../disallowed_script_idents.stderr | 11 + tests/ui-toml/enum_variant_names/clippy.toml | 1 + .../enum_variant_names/enum_variant_names.rs | 16 + .../enum_variant_names.stderr | 18 + tests/ui-toml/enum_variant_size/clippy.toml | 1 + .../enum_variant_size/enum_variant_size.fixed | 11 + .../enum_variant_size/enum_variant_size.rs | 11 + .../enum_variant_size.stderr | 21 + .../enum_variants_threshold0/clippy.toml | 1 + .../enum_variants_name_threshold.rs | 3 + tests/ui-toml/explicit_iter_loop/clippy.toml | 1 + .../explicit_iter_loop.fixed | 10 + .../explicit_iter_loop/explicit_iter_loop.rs | 10 + .../explicit_iter_loop.stderr | 17 + tests/ui-toml/large_stack_frames/clippy.toml | 1 + .../large_stack_frames/large_stack_frames.rs | 17 + .../large_stack_frames.stderr | 15 + .../large_types_passed_by_value/clippy.toml | 1 + .../large_types_passed_by_value.fixed | 7 + .../large_types_passed_by_value.rs | 7 + .../large_types_passed_by_value.stderr | 11 + tests/ui-toml/manual_let_else/clippy.toml | 1 + .../manual_let_else/manual_let_else.fixed | 10 + .../manual_let_else/manual_let_else.rs | 14 + .../manual_let_else/manual_let_else.stderr | 15 + tests/ui-toml/path_ends_with_ext/clippy.toml | 1 + .../path_ends_with_ext/path_ends_with_ext.rs | 9 + tests/ui-toml/result_large_err/clippy.toml | 1 + .../result_large_err/result_large_err.rs | 10 + .../result_large_err/result_large_err.stderr | 12 + .../toml_unknown_key/conf_unknown_key.stderr | 2 + .../too_large_for_stack/boxed_local.rs | 5 + .../too_large_for_stack/boxed_local.stderr | 11 + tests/ui-toml/too_large_for_stack/clippy.toml | 1 + .../too_large_for_stack/useless_vec.fixed | 9 + .../too_large_for_stack/useless_vec.rs | 9 + .../too_large_for_stack/useless_vec.stderr | 11 + tests/ui-toml/too_many_arguments/clippy.toml | 1 + .../too_many_arguments/too_many_arguments.rs | 7 + .../too_many_arguments.stderr | 11 + tests/ui-toml/type_complexity/clippy.toml | 1 + .../type_complexity/type_complexity.rs | 7 + .../type_complexity/type_complexity.stderr | 11 + .../type_repetition_in_bounds/clippy.toml | 1 + .../ui-toml/type_repetition_in_bounds/main.rs | 18 + .../type_repetition_in_bounds/main.stderr | 12 + .../undocumented_unsafe_blocks/clippy.toml | 2 - .../default/clippy.toml | 2 + .../disabled/clippy.toml | 3 + ...undocumented_unsafe_blocks.default.stderr} | 74 +-- ...ndocumented_unsafe_blocks.disabled.stderr} | 132 +++-- .../undocumented_unsafe_blocks.rs | 30 +- .../unnecessary_box_returns/clippy.toml | 1 + .../unnecessary_box_returns.fixed | 11 + .../unnecessary_box_returns.rs | 11 + .../unnecessary_box_returns.stderr | 12 + tests/ui-toml/verbose_bit_mask/clippy.toml | 1 + .../verbose_bit_mask/verbose_bit_mask.fixed | 7 + .../verbose_bit_mask/verbose_bit_mask.rs | 7 + .../verbose_bit_mask/verbose_bit_mask.stderr | 11 + tests/ui-toml/wildcard_imports/clippy.toml | 1 + .../wildcard_imports/wildcard_imports.fixed | 11 + .../wildcard_imports/wildcard_imports.rs | 11 + .../wildcard_imports/wildcard_imports.stderr | 11 + tests/ui/cast.rs | 4 + tests/ui/cast_lossless_integer.fixed | 11 + tests/ui/cast_lossless_integer.rs | 11 + tests/ui/cast_lossless_integer.stderr | 14 +- tests/ui/eta.fixed | 3 +- tests/ui/eta.rs | 3 +- tests/ui/eta.stderr | 54 +- tests/ui/extra_unused_type_parameters.fixed | 15 + tests/ui/extra_unused_type_parameters.rs | 15 + tests/ui/filter_map_bool_then.fixed | 24 + tests/ui/filter_map_bool_then.rs | 24 + tests/ui/filter_map_bool_then.stderr | 26 +- tests/ui/len_without_is_empty.rs | 23 + tests/ui/len_without_is_empty.stderr | 8 +- tests/ui/let_unit.fixed | 2 + tests/ui/let_unit.rs | 2 + tests/ui/manual_map_option.fixed | 1 + tests/ui/manual_map_option.rs | 1 + tests/ui/manual_map_option.stderr | 42 +- tests/ui/needless_borrow.fixed | 290 ---------- tests/ui/needless_borrow.rs | 290 ---------- tests/ui/needless_borrow.stderr | 90 +-- .../needless_borrows_for_generic_args.fixed | 287 ++++++++++ tests/ui/needless_borrows_for_generic_args.rs | 287 ++++++++++ .../needless_borrows_for_generic_args.stderr | 77 +++ tests/ui/needless_pass_by_ref_mut.rs | 40 +- tests/ui/needless_pass_by_ref_mut.stderr | 34 +- tests/ui/needless_raw_string_hashes.fixed | 3 + tests/ui/needless_raw_string_hashes.rs | 3 + tests/ui/needless_raw_string_hashes.stderr | 26 +- tests/ui/no_effect_return.rs | 1 + tests/ui/no_effect_return.stderr | 8 +- tests/ui/option_filter_map.fixed | 2 +- tests/ui/option_filter_map.rs | 2 +- tests/ui/path_ends_with_ext.fixed | 36 ++ tests/ui/path_ends_with_ext.rs | 36 ++ tests/ui/path_ends_with_ext.stderr | 17 + tests/ui/redundant_allocation.rs | 5 + tests/ui/redundant_as_str.fixed | 24 + tests/ui/redundant_as_str.rs | 24 + tests/ui/redundant_as_str.stderr | 17 + tests/ui/redundant_field_names.fixed | 11 +- tests/ui/redundant_field_names.rs | 11 +- tests/ui/redundant_field_names.stderr | 2 +- tests/ui/redundant_guards.fixed | 50 ++ tests/ui/redundant_guards.rs | 50 ++ tests/ui/redundant_guards.stderr | 116 +++- tests/ui/regex.rs | 3 +- tests/ui/regex.stderr | 48 +- tests/ui/result_map_unit_fn_unfixable.rs | 2 +- tests/ui/transmute_null_to_fn.rs | 11 + tests/ui/transmute_null_to_fn.stderr | 26 +- tests/ui/undocumented_unsafe_blocks.rs | 534 ------------------ tests/ui/unnecessary_map_on_constructor.fixed | 56 ++ tests/ui/unnecessary_map_on_constructor.rs | 56 ++ .../ui/unnecessary_map_on_constructor.stderr | 53 ++ tests/ui/unnecessary_to_owned.fixed | 2 +- tests/ui/unnecessary_to_owned.rs | 2 +- tests/ui/used_underscore_binding.rs | 28 +- tests/ui/used_underscore_binding.stderr | 47 +- tests/ui/useless_conversion.fixed | 91 +++ tests/ui/useless_conversion.rs | 91 +++ tests/ui/useless_conversion.stderr | 70 ++- tests/ui/vec_box_sized.fixed | 5 + tests/ui/vec_box_sized.rs | 5 + 197 files changed, 3995 insertions(+), 2312 deletions(-) create mode 100644 clippy_lints/src/casts/zero_ptr.rs create mode 100644 clippy_lints/src/methods/path_ends_with_ext.rs create mode 100644 clippy_lints/src/methods/redundant_as_str.rs create mode 100644 clippy_lints/src/needless_borrows_for_generic_args.rs create mode 100644 clippy_lints/src/unnecessary_map_on_constructor.rs create mode 100644 tests/ui-toml/decimal_literal_representation/clippy.toml create mode 100644 tests/ui-toml/decimal_literal_representation/decimal_literal_representation.fixed create mode 100644 tests/ui-toml/decimal_literal_representation/decimal_literal_representation.rs create mode 100644 tests/ui-toml/decimal_literal_representation/decimal_literal_representation.stderr create mode 100644 tests/ui-toml/disallowed_script_idents/clippy.toml create mode 100644 tests/ui-toml/disallowed_script_idents/disallowed_script_idents.rs create mode 100644 tests/ui-toml/disallowed_script_idents/disallowed_script_idents.stderr create mode 100644 tests/ui-toml/enum_variant_names/clippy.toml create mode 100644 tests/ui-toml/enum_variant_names/enum_variant_names.rs create mode 100644 tests/ui-toml/enum_variant_names/enum_variant_names.stderr create mode 100644 tests/ui-toml/enum_variant_size/clippy.toml create mode 100644 tests/ui-toml/enum_variant_size/enum_variant_size.fixed create mode 100644 tests/ui-toml/enum_variant_size/enum_variant_size.rs create mode 100644 tests/ui-toml/enum_variant_size/enum_variant_size.stderr create mode 100644 tests/ui-toml/enum_variants_threshold0/clippy.toml create mode 100644 tests/ui-toml/enum_variants_threshold0/enum_variants_name_threshold.rs create mode 100644 tests/ui-toml/explicit_iter_loop/clippy.toml create mode 100644 tests/ui-toml/explicit_iter_loop/explicit_iter_loop.fixed create mode 100644 tests/ui-toml/explicit_iter_loop/explicit_iter_loop.rs create mode 100644 tests/ui-toml/explicit_iter_loop/explicit_iter_loop.stderr create mode 100644 tests/ui-toml/large_stack_frames/clippy.toml create mode 100644 tests/ui-toml/large_stack_frames/large_stack_frames.rs create mode 100644 tests/ui-toml/large_stack_frames/large_stack_frames.stderr create mode 100644 tests/ui-toml/large_types_passed_by_value/clippy.toml create mode 100644 tests/ui-toml/large_types_passed_by_value/large_types_passed_by_value.fixed create mode 100644 tests/ui-toml/large_types_passed_by_value/large_types_passed_by_value.rs create mode 100644 tests/ui-toml/large_types_passed_by_value/large_types_passed_by_value.stderr create mode 100644 tests/ui-toml/manual_let_else/clippy.toml create mode 100644 tests/ui-toml/manual_let_else/manual_let_else.fixed create mode 100644 tests/ui-toml/manual_let_else/manual_let_else.rs create mode 100644 tests/ui-toml/manual_let_else/manual_let_else.stderr create mode 100644 tests/ui-toml/path_ends_with_ext/clippy.toml create mode 100644 tests/ui-toml/path_ends_with_ext/path_ends_with_ext.rs create mode 100644 tests/ui-toml/result_large_err/clippy.toml create mode 100644 tests/ui-toml/result_large_err/result_large_err.rs create mode 100644 tests/ui-toml/result_large_err/result_large_err.stderr create mode 100644 tests/ui-toml/too_large_for_stack/boxed_local.rs create mode 100644 tests/ui-toml/too_large_for_stack/boxed_local.stderr create mode 100644 tests/ui-toml/too_large_for_stack/clippy.toml create mode 100644 tests/ui-toml/too_large_for_stack/useless_vec.fixed create mode 100644 tests/ui-toml/too_large_for_stack/useless_vec.rs create mode 100644 tests/ui-toml/too_large_for_stack/useless_vec.stderr create mode 100644 tests/ui-toml/too_many_arguments/clippy.toml create mode 100644 tests/ui-toml/too_many_arguments/too_many_arguments.rs create mode 100644 tests/ui-toml/too_many_arguments/too_many_arguments.stderr create mode 100644 tests/ui-toml/type_complexity/clippy.toml create mode 100644 tests/ui-toml/type_complexity/type_complexity.rs create mode 100644 tests/ui-toml/type_complexity/type_complexity.stderr create mode 100644 tests/ui-toml/type_repetition_in_bounds/clippy.toml create mode 100644 tests/ui-toml/type_repetition_in_bounds/main.rs create mode 100644 tests/ui-toml/type_repetition_in_bounds/main.stderr delete mode 100644 tests/ui-toml/undocumented_unsafe_blocks/clippy.toml create mode 100644 tests/ui-toml/undocumented_unsafe_blocks/default/clippy.toml create mode 100644 tests/ui-toml/undocumented_unsafe_blocks/disabled/clippy.toml rename tests/ui-toml/undocumented_unsafe_blocks/{undocumented_unsafe_blocks.stderr => undocumented_unsafe_blocks.default.stderr} (83%) rename tests/{ui/undocumented_unsafe_blocks.stderr => ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr} (74%) create mode 100644 tests/ui-toml/unnecessary_box_returns/clippy.toml create mode 100644 tests/ui-toml/unnecessary_box_returns/unnecessary_box_returns.fixed create mode 100644 tests/ui-toml/unnecessary_box_returns/unnecessary_box_returns.rs create mode 100644 tests/ui-toml/unnecessary_box_returns/unnecessary_box_returns.stderr create mode 100644 tests/ui-toml/verbose_bit_mask/clippy.toml create mode 100644 tests/ui-toml/verbose_bit_mask/verbose_bit_mask.fixed create mode 100644 tests/ui-toml/verbose_bit_mask/verbose_bit_mask.rs create mode 100644 tests/ui-toml/verbose_bit_mask/verbose_bit_mask.stderr create mode 100644 tests/ui-toml/wildcard_imports/clippy.toml create mode 100644 tests/ui-toml/wildcard_imports/wildcard_imports.fixed create mode 100644 tests/ui-toml/wildcard_imports/wildcard_imports.rs create mode 100644 tests/ui-toml/wildcard_imports/wildcard_imports.stderr create mode 100644 tests/ui/needless_borrows_for_generic_args.fixed create mode 100644 tests/ui/needless_borrows_for_generic_args.rs create mode 100644 tests/ui/needless_borrows_for_generic_args.stderr create mode 100644 tests/ui/path_ends_with_ext.fixed create mode 100644 tests/ui/path_ends_with_ext.rs create mode 100644 tests/ui/path_ends_with_ext.stderr create mode 100644 tests/ui/redundant_as_str.fixed create mode 100644 tests/ui/redundant_as_str.rs create mode 100644 tests/ui/redundant_as_str.stderr delete mode 100644 tests/ui/undocumented_unsafe_blocks.rs create mode 100644 tests/ui/unnecessary_map_on_constructor.fixed create mode 100644 tests/ui/unnecessary_map_on_constructor.rs create mode 100644 tests/ui/unnecessary_map_on_constructor.stderr diff --git a/.github/workflows/remark.yml b/.github/workflows/remark.yml index 7d25b6a2b79e7..30bd476332f75 100644 --- a/.github/workflows/remark.yml +++ b/.github/workflows/remark.yml @@ -21,7 +21,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v3 with: - node-version: '14.x' + node-version: '18.x' - name: Install remark run: npm install remark-cli remark-lint remark-lint-maximum-line-length remark-preset-lint-recommended remark-gfm @@ -29,19 +29,19 @@ jobs: - name: Install mdbook run: | mkdir mdbook - curl -Lf https://github.com/rust-lang/mdBook/releases/download/v0.4.28/mdbook-v0.4.28-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook + curl -Lf https://github.com/rust-lang/mdBook/releases/download/v0.4.34/mdbook-v0.4.34-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook echo `pwd`/mdbook >> $GITHUB_PATH # Run - name: Check *.md files - run: git ls-files -z '*.md' | xargs -0 -n 1 -I {} ./node_modules/.bin/remark {} -u lint -f > /dev/null + run: ./node_modules/.bin/remark -u lint -f . - name: Linkcheck book run: | rustup toolchain install nightly --component rust-docs curl https://raw.githubusercontent.com/rust-lang/rust/master/src/tools/linkchecker/linkcheck.sh -o linkcheck.sh sh linkcheck.sh clippy --path ./book - + - name: Build mdbook run: mdbook build book diff --git a/CHANGELOG.md b/CHANGELOG.md index a7ae4a0ee2c09..8c9ab1e2402ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5171,6 +5171,7 @@ Released 2018-09-13 [`needless_bool_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bool_assign [`needless_borrow`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow [`needless_borrowed_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrowed_reference +[`needless_borrows_for_generic_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrows_for_generic_args [`needless_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_collect [`needless_continue`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_continue [`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main @@ -5245,6 +5246,7 @@ Released 2018-09-13 [`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl [`partialeq_to_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_to_none [`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite +[`path_ends_with_ext`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_ends_with_ext [`pattern_type_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#pattern_type_mismatch [`permissions_set_readonly_false`]: https://rust-lang.github.io/rust-clippy/master/index.html#permissions_set_readonly_false [`positional_named_format_parameters`]: https://rust-lang.github.io/rust-clippy/master/index.html#positional_named_format_parameters @@ -5279,6 +5281,7 @@ Released 2018-09-13 [`readonly_write_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#readonly_write_lock [`recursive_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#recursive_format_impl [`redundant_allocation`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation +[`redundant_as_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_as_str [`redundant_async_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_async_block [`redundant_at_rest_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_at_rest_pattern [`redundant_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_clone @@ -5437,6 +5440,7 @@ Released 2018-09-13 [`unnecessary_join`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_join [`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations [`unnecessary_literal_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_literal_unwrap +[`unnecessary_map_on_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_map_on_constructor [`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed [`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation [`unnecessary_owned_empty_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_owned_empty_strings @@ -5574,5 +5578,6 @@ Released 2018-09-13 [`allow-one-hash-in-raw-strings`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-one-hash-in-raw-strings [`absolute-paths-max-segments`]: https://doc.rust-lang.org/clippy/lint_configuration.html#absolute-paths-max-segments [`absolute-paths-allowed-crates`]: https://doc.rust-lang.org/clippy/lint_configuration.html#absolute-paths-allowed-crates +[`allowed-dotfiles`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-dotfiles [`enforce-iter-loop-reborrow`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enforce-iter-loop-reborrow diff --git a/Cargo.toml b/Cargo.toml index 2d8b590dbe311..66786004f6e72 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,6 @@ itertools = "0.10.1" # UI test dependencies clippy_utils = { path = "clippy_utils" } -derive-new = "0.5" if_chain = "1.0" quote = "1.0" serde = { version = "1.0.125", features = ["derive"] } diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 52c795e04fe95..b980083f1f52a 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -703,7 +703,7 @@ Minimum chars an ident can have, anything below or equal to this will be linted. ## `accept-comment-above-statement` Whether to accept a safety comment to be placed above the statement containing the `unsafe` block -**Default Value:** `false` (`bool`) +**Default Value:** `true` (`bool`) --- **Affected lints:** @@ -713,7 +713,7 @@ Whether to accept a safety comment to be placed above the statement containing t ## `accept-comment-above-attributes` Whether to accept a safety comment to be placed above the attributes for the `unsafe` block -**Default Value:** `false` (`bool`) +**Default Value:** `true` (`bool`) --- **Affected lints:** @@ -751,6 +751,16 @@ Which crates to allow absolute paths from * [`absolute_paths`](https://rust-lang.github.io/rust-clippy/master/index.html#absolute_paths) +## `allowed-dotfiles` +Additional dotfiles (files or directories starting with a dot) to allow + +**Default Value:** `{}` (`rustc_data_structures::fx::FxHashSet`) + +--- +**Affected lints:** +* [`path_ends_with_ext`](https://rust-lang.github.io/rust-clippy/master/index.html#path_ends_with_ext) + + ## `enforce-iter-loop-reborrow` #### Example ``` diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index a88f2b51c827e..0546807bac4fb 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -616,7 +616,7 @@ fn check_should_panic_reason(cx: &LateContext<'_>, attr: &Attribute) { attr.span, "#[should_panic] attribute without a reason", "consider specifying the expected panic", - r#"#[should_panic(expected = /* panic message */)]"#.into(), + "#[should_panic(expected = /* panic message */)]".into(), Applicability::HasPlaceholders, ); } diff --git a/clippy_lints/src/casts/cast_lossless.rs b/clippy_lints/src/casts/cast_lossless.rs index cf07e050ccce9..c586b572be9a1 100644 --- a/clippy_lints/src/casts/cast_lossless.rs +++ b/clippy_lints/src/casts/cast_lossless.rs @@ -25,7 +25,7 @@ pub(super) fn check( // The suggestion is to use a function call, so if the original expression // has parens on the outside, they are no longer needed. let mut applicability = Applicability::MachineApplicable; - let opt = snippet_opt(cx, cast_op.span); + let opt = snippet_opt(cx, cast_op.span.source_callsite()); let sugg = opt.as_ref().map_or_else( || { applicability = Applicability::HasPlaceholders; diff --git a/clippy_lints/src/casts/cast_possible_truncation.rs b/clippy_lints/src/casts/cast_possible_truncation.rs index 84b99ad5c243d..f99a51e2b88c5 100644 --- a/clippy_lints/src/casts/cast_possible_truncation.rs +++ b/clippy_lints/src/casts/cast_possible_truncation.rs @@ -44,7 +44,7 @@ fn apply_reductions(cx: &LateContext<'_>, nbits: u64, expr: &Expr<'_>, signed: b .unwrap_or(u64::max_value()) .min(apply_reductions(cx, nbits, left, signed)), BinOpKind::Shr => apply_reductions(cx, nbits, left, signed) - .saturating_sub(constant_int(cx, right).map_or(0, |s| u64::try_from(s).expect("shift too high"))), + .saturating_sub(constant_int(cx, right).map_or(0, |s| u64::try_from(s).unwrap_or_default())), _ => nbits, }, ExprKind::MethodCall(method, left, [right], _) => { diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index 88ffbb55486bd..b00130ffd76db 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -20,6 +20,7 @@ mod ptr_as_ptr; mod ptr_cast_constness; mod unnecessary_cast; mod utils; +mod zero_ptr; use clippy_utils::is_hir_ty_cfg_dependant; use clippy_utils::msrvs::{self, Msrv}; @@ -665,6 +666,29 @@ declare_clippy_lint! { "casting a known floating-point NaN into an integer" } +declare_clippy_lint! { + /// ### What it does + /// Catch casts from `0` to some pointer type + /// + /// ### Why is this bad? + /// This generally means `null` and is better expressed as + /// {`std`, `core`}`::ptr::`{`null`, `null_mut`}. + /// + /// ### Example + /// ```rust + /// let a = 0 as *const u32; + /// ``` + /// + /// Use instead: + /// ```rust + /// let a = std::ptr::null::(); + /// ``` + #[clippy::version = "pre 1.29.0"] + pub ZERO_PTR, + style, + "using `0 as *{const, mut} T`" +} + pub struct Casts { msrv: Msrv, } @@ -699,6 +723,7 @@ impl_lint_pass!(Casts => [ CAST_SLICE_FROM_RAW_PARTS, AS_PTR_CAST_MUT, CAST_NAN_TO_INT, + ZERO_PTR, ]); impl<'tcx> LateLintPass<'tcx> for Casts { @@ -729,6 +754,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts { fn_to_numeric_cast_any::check(cx, expr, cast_expr, cast_from, cast_to); fn_to_numeric_cast::check(cx, expr, cast_expr, cast_from, cast_to); fn_to_numeric_cast_with_truncation::check(cx, expr, cast_expr, cast_from, cast_to); + zero_ptr::check(cx, expr, cast_expr, cast_to_hir); if cast_to.is_numeric() && !in_external_macro(cx.sess(), expr.span) { cast_possible_truncation::check(cx, expr, cast_expr, cast_from, cast_to, cast_to_hir.span); diff --git a/clippy_lints/src/casts/zero_ptr.rs b/clippy_lints/src/casts/zero_ptr.rs new file mode 100644 index 0000000000000..5071af5ecb986 --- /dev/null +++ b/clippy_lints/src/casts/zero_ptr.rs @@ -0,0 +1,39 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_opt; +use clippy_utils::{in_constant, is_integer_literal, std_or_core}; +use rustc_errors::Applicability; +use rustc_hir::{Expr, Mutability, Ty, TyKind}; +use rustc_lint::LateContext; + +use super::ZERO_PTR; + +pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, from: &Expr<'_>, to: &Ty<'_>) { + if let TyKind::Ptr(ref mut_ty) = to.kind + && is_integer_literal(from, 0) + && !in_constant(cx, from.hir_id) + && let Some(std_or_core) = std_or_core(cx) + { + let (msg, sugg_fn) = match mut_ty.mutbl { + Mutability::Mut => ("`0 as *mut _` detected", "ptr::null_mut"), + Mutability::Not => ("`0 as *const _` detected", "ptr::null"), + }; + + let sugg = if let TyKind::Infer = mut_ty.ty.kind { + format!("{std_or_core}::{sugg_fn}()") + } else if let Some(mut_ty_snip) = snippet_opt(cx, mut_ty.ty.span) { + format!("{std_or_core}::{sugg_fn}::<{mut_ty_snip}>()") + } else { + return; + }; + + span_lint_and_sugg( + cx, + ZERO_PTR, + expr.span, + msg, + "try", + sugg, + Applicability::MachineApplicable, + ); + } +} diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index a4d40df52e7bc..4d1281ec1e7c7 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -97,6 +97,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::casts::PTR_AS_PTR_INFO, crate::casts::PTR_CAST_CONSTNESS_INFO, crate::casts::UNNECESSARY_CAST_INFO, + crate::casts::ZERO_PTR_INFO, crate::checked_conversions::CHECKED_CONVERSIONS_INFO, crate::cognitive_complexity::COGNITIVE_COMPLEXITY_INFO, crate::collapsible_if::COLLAPSIBLE_ELSE_IF_INFO, @@ -399,9 +400,11 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::methods::OR_FUN_CALL_INFO, crate::methods::OR_THEN_UNWRAP_INFO, crate::methods::PATH_BUF_PUSH_OVERWRITE_INFO, + crate::methods::PATH_ENDS_WITH_EXT_INFO, crate::methods::RANGE_ZIP_WITH_LEN_INFO, crate::methods::READONLY_WRITE_LOCK_INFO, crate::methods::READ_LINE_WITHOUT_TRIM_INFO, + crate::methods::REDUNDANT_AS_STR_INFO, crate::methods::REPEAT_ONCE_INFO, crate::methods::RESULT_MAP_OR_INTO_OPTION_INFO, crate::methods::SEARCH_IS_SOME_INFO, @@ -441,7 +444,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::misc::SHORT_CIRCUIT_STATEMENT_INFO, crate::misc::TOPLEVEL_REF_ARG_INFO, crate::misc::USED_UNDERSCORE_BINDING_INFO, - crate::misc::ZERO_PTR_INFO, crate::misc_early::BUILTIN_TYPE_SHADOW_INFO, crate::misc_early::DOUBLE_NEG_INFO, crate::misc_early::DUPLICATE_UNDERSCORE_ARGUMENT_INFO, @@ -479,6 +481,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::needless_bool::NEEDLESS_BOOL_INFO, crate::needless_bool::NEEDLESS_BOOL_ASSIGN_INFO, crate::needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE_INFO, + crate::needless_borrows_for_generic_args::NEEDLESS_BORROWS_FOR_GENERIC_ARGS_INFO, crate::needless_continue::NEEDLESS_CONTINUE_INFO, crate::needless_else::NEEDLESS_ELSE_INFO, crate::needless_for_each::NEEDLESS_FOR_EACH_INFO, @@ -671,6 +674,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::unnamed_address::FN_ADDRESS_COMPARISONS_INFO, crate::unnamed_address::VTABLE_ADDRESS_COMPARISONS_INFO, crate::unnecessary_box_returns::UNNECESSARY_BOX_RETURNS_INFO, + crate::unnecessary_map_on_constructor::UNNECESSARY_MAP_ON_CONSTRUCTOR_INFO, crate::unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS_INFO, crate::unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS_INFO, crate::unnecessary_struct_initialization::UNNECESSARY_STRUCT_INITIALIZATION_INFO, diff --git a/clippy_lints/src/default_union_representation.rs b/clippy_lints/src/default_union_representation.rs index bbce6e1dd8f2b..63ec819502088 100644 --- a/clippy_lints/src/default_union_representation.rs +++ b/clippy_lints/src/default_union_representation.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_help; -use rustc_hir::{self as hir, HirId, Item, ItemKind}; -use rustc_hir_analysis::hir_ty_to_ty; +use rustc_hir::{HirId, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::layout::LayoutOf; +use rustc_middle::ty::{self, FieldDef, GenericArg, List}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -52,7 +52,10 @@ declare_lint_pass!(DefaultUnionRepresentation => [DEFAULT_UNION_REPRESENTATION]) impl<'tcx> LateLintPass<'tcx> for DefaultUnionRepresentation { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { - if is_union_with_two_non_zst_fields(cx, item) && !has_c_repr_attr(cx, item.hir_id()) { + if !item.span.from_expansion() + && is_union_with_two_non_zst_fields(cx, item) + && !has_c_repr_attr(cx, item.hir_id()) + { span_lint_and_help( cx, DEFAULT_UNION_REPRESENTATION, @@ -73,18 +76,17 @@ impl<'tcx> LateLintPass<'tcx> for DefaultUnionRepresentation { /// if there is only one field left after ignoring ZST fields then the offset /// of that field does not matter either.) fn is_union_with_two_non_zst_fields(cx: &LateContext<'_>, item: &Item<'_>) -> bool { - if let ItemKind::Union(data, _) = &item.kind { - data.fields().iter().filter(|f| !is_zst(cx, f.ty)).count() >= 2 + if let ItemKind::Union(..) = &item.kind + && let ty::Adt(adt_def, args) = cx.tcx.type_of(item.owner_id).instantiate_identity().kind() + { + adt_def.all_fields().filter(|f| !is_zst(cx, f, args)).count() >= 2 } else { false } } -fn is_zst(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) -> bool { - if hir_ty.span.from_expansion() { - return false; - } - let ty = hir_ty_to_ty(cx.tcx, hir_ty); +fn is_zst<'tcx>(cx: &LateContext<'tcx>, field: &FieldDef, args: &'tcx List>) -> bool { + let ty = field.ty(cx.tcx, args); if let Ok(layout) = cx.layout_of(ty) { layout.is_zst() } else { diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index fe37fd4a0c192..14877385646a0 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1,41 +1,24 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; -use clippy_utils::mir::{enclosing_mir, expr_local, local_assignments, used_exactly_once, PossibleBorrowerMap}; -use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; -use clippy_utils::ty::{implements_trait, is_copy, peel_mid_ty_refs}; +use clippy_utils::ty::{implements_trait, peel_mid_ty_refs}; use clippy_utils::{ expr_use_ctxt, get_parent_expr, get_parent_node, is_lint_allowed, path_to_local, DefinedTy, ExprUseNode, }; - -use hir::def::DefKind; -use hir::MatchSource; use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX}; use rustc_data_structures::fx::FxIndexMap; -use rustc_data_structures::graph::iterate::{CycleDetector, TriColorDepthFirstSearch}; use rustc_errors::Applicability; -use rustc_hir::def::Res; -use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{walk_ty, Visitor}; use rustc_hir::{ - self as hir, BindingAnnotation, Body, BodyId, BorrowKind, Expr, ExprKind, HirId, Mutability, Node, Pat, PatKind, - Path, QPath, TyKind, UnOp, + self as hir, BindingAnnotation, Body, BodyId, BorrowKind, Expr, ExprKind, HirId, MatchSource, Mutability, Node, + Pat, PatKind, Path, QPath, TyKind, UnOp, }; -use rustc_index::bit_set::BitSet; -use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::mir::{Rvalue, StatementKind}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; -use rustc_middle::ty::{ - self, ClauseKind, EarlyBinder, FnSig, GenericArg, GenericArgKind, List, ParamEnv, ParamTy, ProjectionPredicate, Ty, - TyCtxt, TypeVisitableExt, TypeckResults, -}; +use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt, TypeVisitableExt, TypeckResults}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::sym; use rustc_span::{Span, Symbol}; -use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; -use rustc_trait_selection::traits::{Obligation, ObligationCause}; -use std::collections::VecDeque; declare_clippy_lint! { /// ### What it does @@ -183,24 +166,6 @@ pub struct Dereferencing<'tcx> { /// /// e.g. `m!(x) | Foo::Bar(ref x)` ref_locals: FxIndexMap>, - - /// Stack of (body owner, `PossibleBorrowerMap`) pairs. Used by - /// `needless_borrow_impl_arg_position` to determine when a borrowed expression can instead - /// be moved. - possible_borrowers: Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, - - // `IntoIterator` for arrays requires Rust 1.53. - msrv: Msrv, -} - -impl<'tcx> Dereferencing<'tcx> { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { - msrv, - ..Dereferencing::default() - } - } } #[derive(Debug)] @@ -355,52 +320,6 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { )); }, (Some(use_cx), RefOp::AddrOf(mutability)) => { - let defined_ty = use_cx.node.defined_ty(cx); - - // Check needless_borrow for generic arguments. - if !use_cx.is_ty_unified - && let Some(DefinedTy::Mir(ty)) = defined_ty - && let ty::Param(ty) = *ty.value.skip_binder().kind() - && let Some((hir_id, fn_id, i)) = match use_cx.node { - ExprUseNode::MethodArg(_, _, 0) => None, - ExprUseNode::MethodArg(hir_id, None, i) => { - typeck.type_dependent_def_id(hir_id).map(|id| (hir_id, id, i)) - }, - ExprUseNode::FnArg(&Expr { kind: ExprKind::Path(ref p), hir_id, .. }, i) - if !path_has_args(p) => match typeck.qpath_res(p, hir_id) { - Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) => { - Some((hir_id, id, i)) - }, - _ => None, - }, - _ => None, - } && let count = needless_borrow_generic_arg_count( - cx, - &mut self.possible_borrowers, - fn_id, - typeck.node_args(hir_id), - i, - ty, - expr, - &self.msrv, - ) && count != 0 - { - self.state = Some(( - State::DerefedBorrow(DerefedBorrow { - count: count - 1, - msg: "the borrowed expression implements the required traits", - stability: TyCoercionStability::None, - for_field_access: None, - }), - StateData { - span: expr.span, - hir_id: expr.hir_id, - adjusted_ty: use_cx.adjustments.last().map_or(expr_ty, |a| a.target), - }, - )); - return; - } - // Find the number of times the borrow is auto-derefed. let mut iter = use_cx.adjustments.iter(); let mut deref_count = 0usize; @@ -419,7 +338,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { }; }; - let stability = defined_ty.map_or(TyCoercionStability::None, |ty| { + let stability = use_cx.node.defined_ty(cx).map_or(TyCoercionStability::None, |ty| { TyCoercionStability::for_defined_ty(cx, ty, use_cx.node.is_return()) }); let can_auto_borrow = match use_cx.node { @@ -700,12 +619,6 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { } fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) { - if self.possible_borrowers.last().map_or(false, |&(local_def_id, _)| { - local_def_id == cx.tcx.hir().body_owner_def_id(body.id()) - }) { - self.possible_borrowers.pop(); - } - if Some(body.id()) == self.current_body { for pat in self.ref_locals.drain(..).filter_map(|(_, x)| x) { let replacements = pat.replacements; @@ -729,8 +642,6 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { self.current_body = None; } } - - extract_msrv_attr!(LateContext); } fn try_parse_ref_op<'tcx>( @@ -788,13 +699,6 @@ fn deref_method_same_type<'tcx>(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool { } } -fn path_has_args(p: &QPath<'_>) -> bool { - match *p { - QPath::Resolved(_, Path { segments: [.., s], .. }) | QPath::TypeRelative(_, s) => s.args.is_some(), - _ => false, - } -} - fn in_postfix_position<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool { if let Some(parent) = get_parent_expr(cx, e) && parent.span.ctxt() == e.span.ctxt() @@ -980,274 +884,6 @@ fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool { v.0 } -/// Checks for the number of borrow expressions which can be removed from the given expression -/// where the expression is used as an argument to a function expecting a generic type. -/// -/// The following constraints will be checked: -/// * The borrowed expression meets all the generic type's constraints. -/// * The generic type appears only once in the functions signature. -/// * The borrowed value will not be moved if it is used later in the function. -#[expect(clippy::too_many_arguments)] -fn needless_borrow_generic_arg_count<'tcx>( - cx: &LateContext<'tcx>, - possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, - fn_id: DefId, - callee_args: &'tcx List>, - arg_index: usize, - param_ty: ParamTy, - mut expr: &Expr<'tcx>, - msrv: &Msrv, -) -> usize { - let destruct_trait_def_id = cx.tcx.lang_items().destruct_trait(); - let sized_trait_def_id = cx.tcx.lang_items().sized_trait(); - - let fn_sig = cx.tcx.fn_sig(fn_id).instantiate_identity().skip_binder(); - let predicates = cx.tcx.param_env(fn_id).caller_bounds(); - let projection_predicates = predicates - .iter() - .filter_map(|predicate| { - if let ClauseKind::Projection(projection_predicate) = predicate.kind().skip_binder() { - Some(projection_predicate) - } else { - None - } - }) - .collect::>(); - - let mut trait_with_ref_mut_self_method = false; - - // If no traits were found, or only the `Destruct`, `Sized`, or `Any` traits were found, return. - if predicates - .iter() - .filter_map(|predicate| { - if let ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder() - && trait_predicate.trait_ref.self_ty() == param_ty.to_ty(cx.tcx) - { - Some(trait_predicate.trait_ref.def_id) - } else { - None - } - }) - .inspect(|trait_def_id| { - trait_with_ref_mut_self_method |= has_ref_mut_self_method(cx, *trait_def_id); - }) - .all(|trait_def_id| { - Some(trait_def_id) == destruct_trait_def_id - || Some(trait_def_id) == sized_trait_def_id - || cx.tcx.is_diagnostic_item(sym::Any, trait_def_id) - }) - { - return 0; - } - - // See: - // - https://github.com/rust-lang/rust-clippy/pull/9674#issuecomment-1289294201 - // - https://github.com/rust-lang/rust-clippy/pull/9674#issuecomment-1292225232 - if projection_predicates - .iter() - .any(|projection_predicate| is_mixed_projection_predicate(cx, fn_id, projection_predicate)) - { - return 0; - } - - // `args_with_referent_ty` can be constructed outside of `check_referent` because the same - // elements are modified each time `check_referent` is called. - let mut args_with_referent_ty = callee_args.to_vec(); - - let mut check_reference_and_referent = |reference, referent| { - let referent_ty = cx.typeck_results().expr_ty(referent); - - if !is_copy(cx, referent_ty) - && (referent_ty.has_significant_drop(cx.tcx, cx.param_env) - || !referent_used_exactly_once(cx, possible_borrowers, reference)) - { - return false; - } - - // https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321 - if trait_with_ref_mut_self_method && !matches!(referent_ty.kind(), ty::Ref(_, _, Mutability::Mut)) { - return false; - } - - if !replace_types( - cx, - param_ty, - referent_ty, - fn_sig, - arg_index, - &projection_predicates, - &mut args_with_referent_ty, - ) { - return false; - } - - predicates.iter().all(|predicate| { - if let ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder() - && cx.tcx.is_diagnostic_item(sym::IntoIterator, trait_predicate.trait_ref.def_id) - && let ty::Param(param_ty) = trait_predicate.self_ty().kind() - && let GenericArgKind::Type(ty) = args_with_referent_ty[param_ty.index as usize].unpack() - && ty.is_array() - && !msrv.meets(msrvs::ARRAY_INTO_ITERATOR) - { - return false; - } - - let predicate = EarlyBinder::bind(predicate).instantiate(cx.tcx, &args_with_referent_ty); - let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), cx.param_env, predicate); - let infcx = cx.tcx.infer_ctxt().build(); - infcx.predicate_must_hold_modulo_regions(&obligation) - }) - }; - - let mut count = 0; - while let ExprKind::AddrOf(_, _, referent) = expr.kind { - if !check_reference_and_referent(expr, referent) { - break; - } - expr = referent; - count += 1; - } - count -} - -fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool { - cx.tcx - .associated_items(trait_def_id) - .in_definition_order() - .any(|assoc_item| { - if assoc_item.fn_has_self_parameter { - let self_ty = cx - .tcx - .fn_sig(assoc_item.def_id) - .instantiate_identity() - .skip_binder() - .inputs()[0]; - matches!(self_ty.kind(), ty::Ref(_, _, Mutability::Mut)) - } else { - false - } - }) -} - -fn is_mixed_projection_predicate<'tcx>( - cx: &LateContext<'tcx>, - callee_def_id: DefId, - projection_predicate: &ProjectionPredicate<'tcx>, -) -> bool { - let generics = cx.tcx.generics_of(callee_def_id); - // The predicate requires the projected type to equal a type parameter from the parent context. - if let Some(term_ty) = projection_predicate.term.ty() - && let ty::Param(term_param_ty) = term_ty.kind() - && (term_param_ty.index as usize) < generics.parent_count - { - // The inner-most self type is a type parameter from the current function. - let mut projection_ty = projection_predicate.projection_ty; - loop { - match projection_ty.self_ty().kind() { - ty::Alias(ty::Projection, inner_projection_ty) => { - projection_ty = *inner_projection_ty; - } - ty::Param(param_ty) => { - return (param_ty.index as usize) >= generics.parent_count; - } - _ => { - return false; - } - } - } - } else { - false - } -} - -fn referent_used_exactly_once<'tcx>( - cx: &LateContext<'tcx>, - possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, - reference: &Expr<'tcx>, -) -> bool { - if let Some(mir) = enclosing_mir(cx.tcx, reference.hir_id) - && let Some(local) = expr_local(cx.tcx, reference) - && let [location] = *local_assignments(mir, local).as_slice() - && let Some(statement) = mir.basic_blocks[location.block].statements.get(location.statement_index) - && let StatementKind::Assign(box (_, Rvalue::Ref(_, _, place))) = statement.kind - && !place.is_indirect_first_projection() - // Ensure not in a loop (https://github.com/rust-lang/rust-clippy/issues/9710) - && TriColorDepthFirstSearch::new(&mir.basic_blocks).run_from(location.block, &mut CycleDetector).is_none() - { - let body_owner_local_def_id = cx.tcx.hir().enclosing_body_owner(reference.hir_id); - if possible_borrowers - .last() - .map_or(true, |&(local_def_id, _)| local_def_id != body_owner_local_def_id) - { - possible_borrowers.push((body_owner_local_def_id, PossibleBorrowerMap::new(cx, mir))); - } - let possible_borrower = &mut possible_borrowers.last_mut().unwrap().1; - // If `only_borrowers` were used here, the `copyable_iterator::warn` test would fail. The reason is - // that `PossibleBorrowerVisitor::visit_terminator` considers `place.local` a possible borrower of - // itself. See the comment in that method for an explanation as to why. - possible_borrower.bounded_borrowers(&[local], &[local, place.local], place.local, location) - && used_exactly_once(mir, place.local).unwrap_or(false) - } else { - false - } -} - -// Iteratively replaces `param_ty` with `new_ty` in `args`, and similarly for each resulting -// projected type that is a type parameter. Returns `false` if replacing the types would have an -// effect on the function signature beyond substituting `new_ty` for `param_ty`. -// See: https://github.com/rust-lang/rust-clippy/pull/9136#discussion_r927212757 -fn replace_types<'tcx>( - cx: &LateContext<'tcx>, - param_ty: ParamTy, - new_ty: Ty<'tcx>, - fn_sig: FnSig<'tcx>, - arg_index: usize, - projection_predicates: &[ProjectionPredicate<'tcx>], - args: &mut [ty::GenericArg<'tcx>], -) -> bool { - let mut replaced = BitSet::new_empty(args.len()); - - let mut deque = VecDeque::with_capacity(args.len()); - deque.push_back((param_ty, new_ty)); - - while let Some((param_ty, new_ty)) = deque.pop_front() { - // If `replaced.is_empty()`, then `param_ty` and `new_ty` are those initially passed in. - if !fn_sig - .inputs_and_output - .iter() - .enumerate() - .all(|(i, ty)| (replaced.is_empty() && i == arg_index) || !ty.contains(param_ty.to_ty(cx.tcx))) - { - return false; - } - - args[param_ty.index as usize] = ty::GenericArg::from(new_ty); - - // The `replaced.insert(...)` check provides some protection against infinite loops. - if replaced.insert(param_ty.index) { - for projection_predicate in projection_predicates { - if projection_predicate.projection_ty.self_ty() == param_ty.to_ty(cx.tcx) - && let Some(term_ty) = projection_predicate.term.ty() - && let ty::Param(term_param_ty) = term_ty.kind() - { - let projection = cx.tcx.mk_ty_from_kind(ty::Alias( - ty::Projection, - projection_predicate.projection_ty.with_self_ty(cx.tcx, new_ty), - )); - - if let Ok(projected_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, projection) - && args[term_param_ty.index as usize] != ty::GenericArg::from(projected_ty) - { - deque.push_back((*term_param_ty, projected_ty)); - } - } - } - } - } - - true -} - fn ty_contains_field(ty: Ty<'_>, name: Symbol) -> bool { if let ty::Adt(adt, _) = *ty.kind() { adt.is_struct() && adt.all_fields().any(|f| f.name == name) diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index bf2add6aa64f7..e789e0da6797a 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -459,7 +459,7 @@ struct Fragments<'a> { impl Fragments<'_> { fn span(self, cx: &LateContext<'_>, range: Range) -> Option { - source_span_for_markdown_range(cx.tcx, &self.doc, &range, &self.fragments) + source_span_for_markdown_range(cx.tcx, self.doc, &range, self.fragments) } } @@ -513,6 +513,7 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &[ const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail"]; +#[allow(clippy::too_many_lines)] // Only a big match statement fn check_doc<'a, Events: Iterator, Range)>>( cx: &LateContext<'_>, valid_idents: &FxHashSet, diff --git a/clippy_lints/src/enum_variants.rs b/clippy_lints/src/enum_variants.rs index d4df6f7aa2d09..e332f681b6dbd 100644 --- a/clippy_lints/src/enum_variants.rs +++ b/clippy_lints/src/enum_variants.rs @@ -167,7 +167,10 @@ fn check_variant(cx: &LateContext<'_>, threshold: u64, def: &EnumDef<'_>, item_n return; } - let first = &def.variants[0].ident.name.as_str(); + let first = match def.variants.first() { + Some(variant) => variant.ident.name.as_str(), + None => return, + }; let mut pre = camel_case_split(first); let mut post = pre.clone(); post.reverse(); diff --git a/clippy_lints/src/error_impl_error.rs b/clippy_lints/src/error_impl_error.rs index 379af9b2234c2..f24577c738229 100644 --- a/clippy_lints/src/error_impl_error.rs +++ b/clippy_lints/src/error_impl_error.rs @@ -3,7 +3,6 @@ use clippy_utils::path_res; use clippy_utils::ty::implements_trait; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{Item, ItemKind}; -use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::Visibility; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -42,9 +41,10 @@ impl<'tcx> LateLintPass<'tcx> for ErrorImplError { }; match item.kind { - ItemKind::TyAlias(ty, _) if implements_trait(cx, hir_ty_to_ty(cx.tcx, ty), error_def_id, &[]) - && item.ident.name == sym::Error - && is_visible_outside_module(cx, item.owner_id.def_id) => + ItemKind::TyAlias(..) if item.ident.name == sym::Error + && is_visible_outside_module(cx, item.owner_id.def_id) + && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity() + && implements_trait(cx, ty, error_def_id, &[]) => { span_lint( cx, diff --git a/clippy_lints/src/explicit_write.rs b/clippy_lints/src/explicit_write.rs index 4b9ca8c917e52..b612cc00bf97e 100644 --- a/clippy_lints/src/explicit_write.rs +++ b/clippy_lints/src/explicit_write.rs @@ -57,54 +57,52 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { } else { None } + && let Some(format_args) = find_format_args(cx, write_arg, ExpnId::root()) { - find_format_args(cx, write_arg, ExpnId::root(), |format_args| { - let calling_macro = - // ordering is important here, since `writeln!` uses `write!` internally - if is_expn_of(write_call.span, "writeln").is_some() { - Some("writeln") - } else if is_expn_of(write_call.span, "write").is_some() { - Some("write") - } else { - None - }; - let prefix = if dest_name == "stderr" { - "e" - } else { - "" - }; + // ordering is important here, since `writeln!` uses `write!` internally + let calling_macro = if is_expn_of(write_call.span, "writeln").is_some() { + Some("writeln") + } else if is_expn_of(write_call.span, "write").is_some() { + Some("write") + } else { + None + }; + let prefix = if dest_name == "stderr" { + "e" + } else { + "" + }; - // We need to remove the last trailing newline from the string because the - // underlying `fmt::write` function doesn't know whether `println!` or `print!` was - // used. - let (used, sugg_mac) = if let Some(macro_name) = calling_macro { - ( - format!("{macro_name}!({dest_name}(), ...)"), - macro_name.replace("write", "print"), - ) - } else { - ( - format!("{dest_name}().write_fmt(...)"), - "print".into(), - ) - }; - let mut applicability = Applicability::MachineApplicable; - let inputs_snippet = snippet_with_applicability( - cx, - format_args_inputs_span(format_args), - "..", - &mut applicability, - ); - span_lint_and_sugg( - cx, - EXPLICIT_WRITE, - expr.span, - &format!("use of `{used}.unwrap()`"), - "try", - format!("{prefix}{sugg_mac}!({inputs_snippet})"), - applicability, - ); - }); + // We need to remove the last trailing newline from the string because the + // underlying `fmt::write` function doesn't know whether `println!` or `print!` was + // used. + let (used, sugg_mac) = if let Some(macro_name) = calling_macro { + ( + format!("{macro_name}!({dest_name}(), ...)"), + macro_name.replace("write", "print"), + ) + } else { + ( + format!("{dest_name}().write_fmt(...)"), + "print".into(), + ) + }; + let mut applicability = Applicability::MachineApplicable; + let inputs_snippet = snippet_with_applicability( + cx, + format_args_inputs_span(&format_args), + "..", + &mut applicability, + ); + span_lint_and_sugg( + cx, + EXPLICIT_WRITE, + expr.span, + &format!("use of `{used}.unwrap()`"), + "try", + format!("{prefix}{sugg_mac}!({inputs_snippet})"), + applicability, + ); } } } diff --git a/clippy_lints/src/extra_unused_type_parameters.rs b/clippy_lints/src/extra_unused_type_parameters.rs index c18006a71c26e..0a885984abbf2 100644 --- a/clippy_lints/src/extra_unused_type_parameters.rs +++ b/clippy_lints/src/extra_unused_type_parameters.rs @@ -246,8 +246,13 @@ impl<'cx, 'tcx> Visitor<'tcx> for TypeWalker<'cx, 'tcx> { { self.ty_params.remove(&def_id); } + } else { + // If the bounded type isn't a generic param, but is instead a concrete generic + // type, any params we find nested inside of it are being used as concrete types, + // and can therefore can be considered used. So, we're fine to walk the left-hand + // side of the where bound. + walk_ty(self, predicate.bounded_ty); } - // Only walk the right-hand side of where bounds for bound in predicate.bounds { walk_param_bound(self, bound); } diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index f4f8bdc2c44e5..b748d32936792 100644 --- a/clippy_lints/src/format.rs +++ b/clippy_lints/src/format.rs @@ -43,14 +43,10 @@ declare_lint_pass!(UselessFormat => [USELESS_FORMAT]); impl<'tcx> LateLintPass<'tcx> for UselessFormat { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - let Some(macro_call) = root_macro_call_first_node(cx, expr) else { - return; - }; - if !cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id) { - return; - } - - find_format_args(cx, expr, macro_call.expn, |format_args| { + if let Some(macro_call) = root_macro_call_first_node(cx, expr) + && cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id) + && let Some(format_args) = find_format_args(cx, expr, macro_call.expn) + { let mut applicability = Applicability::MachineApplicable; let call_site = macro_call.span; @@ -91,7 +87,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat { }, _ => {}, } - }); + } } } diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index 01c714c414b5a..39abf5c2def56 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -186,15 +186,10 @@ impl FormatArgs { impl<'tcx> LateLintPass<'tcx> for FormatArgs { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - let Some(macro_call) = root_macro_call_first_node(cx, expr) else { - return; - }; - if !is_format_macro(cx, macro_call.def_id) { - return; - } - let name = cx.tcx.item_name(macro_call.def_id); - - find_format_args(cx, expr, macro_call.expn, |format_args| { + if let Some(macro_call) = root_macro_call_first_node(cx, expr) + && is_format_macro(cx, macro_call.def_id) + && let Some(format_args) = find_format_args(cx, expr, macro_call.expn) + { for piece in &format_args.template { if let FormatArgsPiece::Placeholder(placeholder) = piece && let Ok(index) = placeholder.argument.index @@ -206,12 +201,13 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs { if placeholder.format_trait != FormatTrait::Display || placeholder.format_options != FormatOptions::default() - || is_aliased(format_args, index) + || is_aliased(&format_args, index) { continue; } if let Ok(arg_hir_expr) = arg_expr { + let name = cx.tcx.item_name(macro_call.def_id); check_format_in_format_args(cx, macro_call.span, name, arg_hir_expr); check_to_string_in_format_args(cx, name, arg_hir_expr); } @@ -219,9 +215,9 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs { } if self.msrv.meets(msrvs::FORMAT_ARGS_CAPTURE) { - check_uninlined_args(cx, format_args, macro_call.span, macro_call.def_id, self.ignore_mixed); + check_uninlined_args(cx, &format_args, macro_call.span, macro_call.def_id, self.ignore_mixed); } - }); + } } extract_msrv_attr!(LateContext); diff --git a/clippy_lints/src/format_impl.rs b/clippy_lints/src/format_impl.rs index 76369bccf9e3b..1d2f7cb71303b 100644 --- a/clippy_lints/src/format_impl.rs +++ b/clippy_lints/src/format_impl.rs @@ -170,30 +170,29 @@ fn check_self_in_format_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, if let Some(outer_macro) = root_macro_call_first_node(cx, expr) && let macro_def_id = outer_macro.def_id && is_format_macro(cx, macro_def_id) + && let Some(format_args) = find_format_args(cx, expr, outer_macro.expn) { - find_format_args(cx, expr, outer_macro.expn, |format_args| { - for piece in &format_args.template { - if let FormatArgsPiece::Placeholder(placeholder) = piece - && let trait_name = match placeholder.format_trait { - FormatTrait::Display => sym::Display, - FormatTrait::Debug => sym::Debug, - FormatTrait::LowerExp => sym!(LowerExp), - FormatTrait::UpperExp => sym!(UpperExp), - FormatTrait::Octal => sym!(Octal), - FormatTrait::Pointer => sym::Pointer, - FormatTrait::Binary => sym!(Binary), - FormatTrait::LowerHex => sym!(LowerHex), - FormatTrait::UpperHex => sym!(UpperHex), - } - && trait_name == impl_trait.name - && let Ok(index) = placeholder.argument.index - && let Some(arg) = format_args.arguments.all_args().get(index) - && let Ok(arg_expr) = find_format_arg_expr(expr, arg) - { - check_format_arg_self(cx, expr.span, arg_expr, impl_trait); + for piece in &format_args.template { + if let FormatArgsPiece::Placeholder(placeholder) = piece + && let trait_name = match placeholder.format_trait { + FormatTrait::Display => sym::Display, + FormatTrait::Debug => sym::Debug, + FormatTrait::LowerExp => sym!(LowerExp), + FormatTrait::UpperExp => sym!(UpperExp), + FormatTrait::Octal => sym!(Octal), + FormatTrait::Pointer => sym::Pointer, + FormatTrait::Binary => sym!(Binary), + FormatTrait::LowerHex => sym!(LowerHex), + FormatTrait::UpperHex => sym!(UpperHex), } + && trait_name == impl_trait.name + && let Ok(index) = placeholder.argument.index + && let Some(arg) = format_args.arguments.all_args().get(index) + && let Ok(arg_expr) = find_format_arg_expr(expr, arg) + { + check_format_arg_self(cx, expr.span, arg_expr, impl_trait); } - }); + } } } diff --git a/clippy_lints/src/large_const_arrays.rs b/clippy_lints/src/large_const_arrays.rs index 9b26c3573e18f..a4f3d49834531 100644 --- a/clippy_lints/src/large_const_arrays.rs +++ b/clippy_lints/src/large_const_arrays.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; -use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, ConstKind}; @@ -50,12 +49,12 @@ impl<'tcx> LateLintPass<'tcx> for LargeConstArrays { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if_chain! { if !item.span.from_expansion(); - if let ItemKind::Const(hir_ty, generics, _) = &item.kind; + if let ItemKind::Const(_, generics, _) = &item.kind; // Since static items may not have generics, skip generic const items. // FIXME(generic_const_items): I don't think checking `generics.hwcp` suffices as it // doesn't account for empty where-clauses that only consist of keyword `where` IINM. if generics.params.is_empty() && !generics.has_where_clause_predicates; - let ty = hir_ty_to_ty(cx.tcx, hir_ty); + let ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); if let ty::Array(element_type, cst) = ty.kind(); if let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind(); if let Ok(element_count) = element_count.try_to_target_usize(cx.tcx); diff --git a/clippy_lints/src/large_futures.rs b/clippy_lints/src/large_futures.rs index d67d5899350a8..19f1e08b57ad1 100644 --- a/clippy_lints/src/large_futures.rs +++ b/clippy_lints/src/large_futures.rs @@ -17,26 +17,20 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// async fn wait(f: impl std::future::Future) {} + /// async fn large_future(_x: [u8; 16 * 1024]) {} /// - /// async fn big_fut(arg: [u8; 1024]) {} - /// - /// pub async fn test() { - /// let fut = big_fut([0u8; 1024]); - /// wait(fut).await; + /// pub async fn trigger() { + /// large_future([0u8; 16 * 1024]).await; /// } /// ``` /// /// `Box::pin` the big future instead. /// /// ```rust - /// async fn wait(f: impl std::future::Future) {} - /// - /// async fn big_fut(arg: [u8; 1024]) {} + /// async fn large_future(_x: [u8; 16 * 1024]) {} /// - /// pub async fn test() { - /// let fut = Box::pin(big_fut([0u8; 1024])); - /// wait(fut).await; + /// pub async fn trigger() { + /// Box::pin(large_future([0u8; 16 * 1024])).await; /// } /// ``` #[clippy::version = "1.70.0"] diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index deba232bdd23e..c06b35ca0dabf 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -424,6 +424,14 @@ fn check_for_is_empty( item_name: Symbol, item_kind: &str, ) { + // Implementor may be a type alias, in which case we need to get the `DefId` of the aliased type to + // find the correct inherent impls. + let impl_ty = if let Some(adt) = cx.tcx.type_of(impl_ty).skip_binder().ty_adt_def() { + adt.did() + } else { + return; + }; + let is_empty = Symbol::intern("is_empty"); let is_empty = cx .tcx diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f52614b62088f..1271be2fd9368 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -230,6 +230,7 @@ mod mutex_atomic; mod needless_arbitrary_self_type; mod needless_bool; mod needless_borrowed_ref; +mod needless_borrows_for_generic_args; mod needless_continue; mod needless_else; mod needless_for_each; @@ -331,6 +332,7 @@ mod unit_return_expecting_ord; mod unit_types; mod unnamed_address; mod unnecessary_box_returns; +mod unnecessary_map_on_constructor; mod unnecessary_owned_empty_strings; mod unnecessary_self_imports; mod unnecessary_struct_initialization; @@ -610,7 +612,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: .collect(), )) }); - store.register_early_pass(|| Box::new(utils::format_args_collector::FormatArgsCollector)); + store.register_early_pass(|| Box::::default()); store.register_late_pass(|_| Box::new(utils::dump_hir::DumpHir)); store.register_late_pass(|_| Box::new(utils::author::Author)); let await_holding_invalid_types = conf.await_holding_invalid_types.clone(); @@ -637,7 +639,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(needless_bool::NeedlessBool)); store.register_late_pass(|_| Box::new(needless_bool::BoolComparison)); store.register_late_pass(|_| Box::new(needless_for_each::NeedlessForEach)); - store.register_late_pass(|_| Box::::default()); + store.register_late_pass(|_| Box::new(misc::LintPass)); store.register_late_pass(|_| Box::new(eta_reduction::EtaReduction)); store.register_late_pass(|_| Box::new(mut_mut::MutMut)); store.register_late_pass(|_| Box::new(mut_reference::UnnecessaryMutPassed)); @@ -663,12 +665,19 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: let allow_unwrap_in_tests = conf.allow_unwrap_in_tests; let suppress_restriction_lint_in_const = conf.suppress_restriction_lint_in_const; store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(msrv()))); + let allowed_dotfiles = conf + .allowed_dotfiles + .iter() + .cloned() + .chain(methods::DEFAULT_ALLOWED_DOTFILES.iter().copied().map(ToOwned::to_owned)) + .collect::>(); store.register_late_pass(move |_| { Box::new(methods::Methods::new( avoid_breaking_exported_api, msrv(), allow_expect_in_tests, allow_unwrap_in_tests, + allowed_dotfiles.clone(), )) }); store.register_late_pass(move |_| Box::new(matches::Matches::new(msrv()))); @@ -881,7 +890,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move |_| Box::new(wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports))); store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(unnamed_address::UnnamedAddress)); - store.register_late_pass(move |_| Box::new(dereference::Dereferencing::new(msrv()))); + store.register_late_pass(|_| Box::>::default()); store.register_late_pass(|_| Box::new(option_if_let_else::OptionIfLetElse)); store.register_late_pass(|_| Box::new(future_not_send::FutureNotSend)); let future_size_threshold = conf.future_size_threshold; @@ -1104,6 +1113,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(implied_bounds_in_impls::ImpliedBoundsInImpls)); store.register_late_pass(|_| Box::new(missing_asserts_for_indexing::MissingAssertsForIndexing)); + store.register_late_pass(|_| Box::new(unnecessary_map_on_constructor::UnnecessaryMapOnConstructor)); + store.register_late_pass(move |_| { + Box::new(needless_borrows_for_generic_args::NeedlessBorrowsForGenericArgs::new( + msrv(), + )) + }); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/loops/utils.rs b/clippy_lints/src/loops/utils.rs index 6edca2d55f649..0a2bd89eb3cd3 100644 --- a/clippy_lints/src/loops/utils.rs +++ b/clippy_lints/src/loops/utils.rs @@ -5,7 +5,6 @@ use rustc_ast::ast::{LitIntType, LitKind}; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, walk_local, walk_pat, walk_stmt, Visitor}; use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, HirId, HirIdMap, Local, Mutability, Pat, PatKind, Stmt}; -use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, Ty}; @@ -150,7 +149,7 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { if l.pat.hir_id == self.var_id; if let PatKind::Binding(.., ident, _) = l.pat.kind; then { - let ty = l.ty.map(|ty| hir_ty_to_ty(self.cx.tcx, ty)); + let ty = l.ty.map(|_| self.cx.typeck_results().pat_ty(l.pat)); self.state = l.init.map_or(InitializeVisitorState::Declared(ident.name, ty), |init| { InitializeVisitorState::Initialized { diff --git a/clippy_lints/src/matches/needless_match.rs b/clippy_lints/src/matches/needless_match.rs index c4f6852aedc3d..44dc29c36a6ba 100644 --- a/clippy_lints/src/matches/needless_match.rs +++ b/clippy_lints/src/matches/needless_match.rs @@ -8,8 +8,7 @@ use clippy_utils::{ }; use rustc_errors::Applicability; use rustc_hir::LangItem::OptionNone; -use rustc_hir::{Arm, BindingAnnotation, ByRef, Expr, ExprKind, FnRetTy, Guard, Node, Pat, PatKind, Path, QPath}; -use rustc_hir_analysis::hir_ty_to_ty; +use rustc_hir::{Arm, BindingAnnotation, ByRef, Expr, ExprKind, Guard, ItemKind, Node, Pat, PatKind, Path, QPath}; use rustc_lint::LateContext; use rustc_span::sym; @@ -141,11 +140,15 @@ fn expr_ty_matches_p_ty(cx: &LateContext<'_>, expr: &Expr<'_>, p_expr: &Expr<'_> return same_type_and_consts(results.node_type(local.hir_id), results.expr_ty(expr)); }, // compare match_expr ty with RetTy in `fn foo() -> RetTy` - Node::Item(..) => { - if let Some(fn_decl) = p_node.fn_decl() { - if let FnRetTy::Return(ret_ty) = fn_decl.output { - return same_type_and_consts(hir_ty_to_ty(cx.tcx, ret_ty), cx.typeck_results().expr_ty(expr)); - } + Node::Item(item) => { + if let ItemKind::Fn(..) = item.kind { + let output = cx + .tcx + .fn_sig(item.owner_id) + .instantiate_identity() + .output() + .skip_binder(); + return same_type_and_consts(output, cx.typeck_results().expr_ty(expr)); } }, // check the parent expr for this whole block `{ match match_expr {..} }` diff --git a/clippy_lints/src/matches/redundant_guards.rs b/clippy_lints/src/matches/redundant_guards.rs index 29af4812351e3..0efeeacc9d97c 100644 --- a/clippy_lints/src/matches/redundant_guards.rs +++ b/clippy_lints/src/matches/redundant_guards.rs @@ -2,11 +2,12 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::path_to_local; use clippy_utils::source::snippet_with_applicability; use clippy_utils::visitors::{for_each_expr, is_local_used}; -use rustc_ast::LitKind; +use rustc_ast::{BorrowKind, LitKind}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Arm, BinOpKind, Expr, ExprKind, Guard, MatchSource, Node, Pat, PatKind}; use rustc_lint::LateContext; +use rustc_span::symbol::Ident; use rustc_span::Span; use std::ops::ControlFlow; @@ -34,32 +35,45 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>]) { ], MatchSource::Normal, ) = if_expr.kind + && let Some(binding) = get_pat_binding(cx, scrutinee, outer_arm) { + let pat_span = match (arm.pat.kind, binding.byref_ident) { + (PatKind::Ref(pat, _), Some(_)) => pat.span, + (PatKind::Ref(..), None) | (_, Some(_)) => continue, + _ => arm.pat.span, + }; emit_redundant_guards( cx, outer_arm, if_expr.span, - scrutinee, - arm.pat.span, + pat_span, + &binding, arm.guard, ); } // `Some(x) if let Some(2) = x` - else if let Guard::IfLet(let_expr) = guard { + else if let Guard::IfLet(let_expr) = guard + && let Some(binding) = get_pat_binding(cx, let_expr.init, outer_arm) + { + let pat_span = match (let_expr.pat.kind, binding.byref_ident) { + (PatKind::Ref(pat, _), Some(_)) => pat.span, + (PatKind::Ref(..), None) | (_, Some(_)) => continue, + _ => let_expr.pat.span, + }; emit_redundant_guards( cx, outer_arm, let_expr.span, - let_expr.init, - let_expr.pat.span, + pat_span, + &binding, None, ); } // `Some(x) if x == Some(2)` + // `Some(x) if Some(2) == x` else if let Guard::If(if_expr) = guard && let ExprKind::Binary(bin_op, local, pat) = if_expr.kind && matches!(bin_op.node, BinOpKind::Eq) - && expr_can_be_pat(cx, pat) // Ensure they have the same type. If they don't, we'd need deref coercion which isn't // possible (currently) in a pattern. In some cases, you can use something like // `as_deref` or similar but in general, we shouldn't lint this as it'd create an @@ -67,43 +81,68 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>]) { // // This isn't necessary in the other two checks, as they must be a pattern already. && cx.typeck_results().expr_ty(local) == cx.typeck_results().expr_ty(pat) + // Since we want to lint on both `x == Some(2)` and `Some(2) == x`, we might have to "swap" + // `local` and `pat`, depending on which side they are. + && let Some((binding, pat)) = get_pat_binding(cx, local, outer_arm) + .map(|binding| (binding, pat)) + .or_else(|| get_pat_binding(cx, pat, outer_arm).map(|binding| (binding, local))) + && expr_can_be_pat(cx, pat) { + let pat_span = match (pat.kind, binding.byref_ident) { + (ExprKind::AddrOf(BorrowKind::Ref, _, expr), Some(_)) => expr.span, + (ExprKind::AddrOf(..), None) | (_, Some(_)) => continue, + _ => pat.span, + }; emit_redundant_guards( cx, outer_arm, if_expr.span, - local, - pat.span, + pat_span, + &binding, None, ); } } } -fn get_pat_binding<'tcx>(cx: &LateContext<'tcx>, guard_expr: &Expr<'_>, outer_arm: &Arm<'tcx>) -> Option<(Span, bool)> { +struct PatBindingInfo { + span: Span, + byref_ident: Option, + is_field: bool, +} + +fn get_pat_binding<'tcx>( + cx: &LateContext<'tcx>, + guard_expr: &Expr<'_>, + outer_arm: &Arm<'tcx>, +) -> Option { if let Some(local) = path_to_local(guard_expr) && !is_local_used(cx, outer_arm.body, local) { let mut span = None; + let mut byref_ident = None; let mut multiple_bindings = false; // `each_binding` gives the `HirId` of the `Pat` itself, not the binding outer_arm.pat.walk(|pat| { - if let PatKind::Binding(_, hir_id, _, _) = pat.kind + if let PatKind::Binding(bind_annot, hir_id, ident, _) = pat.kind && hir_id == local - && span.replace(pat.span).is_some() { - multiple_bindings = true; - return false; + if matches!(bind_annot.0, rustc_ast::ByRef::Yes) { + let _ = byref_ident.insert(ident); + } + // the second call of `replace()` returns a `Some(span)`, meaning a multi-binding pattern + if span.replace(pat.span).is_some() { + multiple_bindings = true; + return false; + } } - true }); // Ignore bindings from or patterns, like `First(x) | Second(x, _) | Third(x, _, _)` if !multiple_bindings { - return span.map(|span| { - ( - span, - !matches!(cx.tcx.hir().get_parent(local), Node::PatField(_)), - ) + return span.map(|span| PatBindingInfo { + span, + byref_ident, + is_field: matches!(cx.tcx.hir().get_parent(local), Node::PatField(_)), }); } } @@ -115,14 +154,11 @@ fn emit_redundant_guards<'tcx>( cx: &LateContext<'tcx>, outer_arm: &Arm<'tcx>, guard_span: Span, - local: &Expr<'_>, pat_span: Span, + pat_binding: &PatBindingInfo, inner_guard: Option>, ) { let mut app = Applicability::MaybeIncorrect; - let Some((pat_binding, can_use_shorthand)) = get_pat_binding(cx, local, outer_arm) else { - return; - }; span_lint_and_then( cx, @@ -131,14 +167,21 @@ fn emit_redundant_guards<'tcx>( "redundant guard", |diag| { let binding_replacement = snippet_with_applicability(cx, pat_span, "", &mut app); + let suggestion_span = match *pat_binding { + PatBindingInfo { + span, + byref_ident: Some(ident), + is_field: true, + } => (span, format!("{ident}: {binding_replacement}")), + PatBindingInfo { + span, is_field: true, .. + } => (span.shrink_to_hi(), format!(": {binding_replacement}")), + PatBindingInfo { span, .. } => (span, binding_replacement.into_owned()), + }; diag.multipart_suggestion_verbose( "try", vec![ - if can_use_shorthand { - (pat_binding, binding_replacement.into_owned()) - } else { - (pat_binding.shrink_to_hi(), format!(": {binding_replacement}")) - }, + suggestion_span, ( guard_span.source_callsite().with_lo(outer_arm.pat.span.hi()), inner_guard.map_or_else(String::new, |guard| { diff --git a/clippy_lints/src/methods/expect_fun_call.rs b/clippy_lints/src/methods/expect_fun_call.rs index d3e90e4bba392..40e487bf65058 100644 --- a/clippy_lints/src/methods/expect_fun_call.rs +++ b/clippy_lints/src/methods/expect_fun_call.rs @@ -131,13 +131,12 @@ pub(super) fn check<'tcx>( let mut applicability = Applicability::MachineApplicable; - //Special handling for `format!` as arg_root + // Special handling for `format!` as arg_root if let Some(macro_call) = root_macro_call_first_node(cx, arg_root) { - if !cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id) { - return; - } - find_format_args(cx, arg_root, macro_call.expn, |format_args| { - let span = format_args_inputs_span(format_args); + if cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id) + && let Some(format_args) = find_format_args(cx, arg_root, macro_call.expn) + { + let span = format_args_inputs_span(&format_args); let sugg = snippet_with_applicability(cx, span, "..", &mut applicability); span_lint_and_sugg( cx, @@ -148,7 +147,7 @@ pub(super) fn check<'tcx>( format!("unwrap_or_else({closure_args} panic!({sugg}))"), applicability, ); - }); + } return; } diff --git a/clippy_lints/src/methods/filter_map_bool_then.rs b/clippy_lints/src/methods/filter_map_bool_then.rs index fafc970977009..33657254965c1 100644 --- a/clippy_lints/src/methods/filter_map_bool_then.rs +++ b/clippy_lints/src/methods/filter_map_bool_then.rs @@ -8,6 +8,7 @@ use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::adjustment::Adjust; use rustc_middle::ty::Binder; use rustc_span::{sym, Span}; @@ -36,6 +37,11 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: & && let Some(def_id) = cx.typeck_results().type_dependent_def_id(value.hir_id) && match_def_path(cx, def_id, &BOOL_THEN) && !is_from_proc_macro(cx, expr) + // Count the number of derefs needed to get to the bool because we need those in the suggestion + && let needed_derefs = cx.typeck_results().expr_adjustments(recv) + .iter() + .filter(|adj| matches!(adj.kind, Adjust::Deref(_))) + .count() && let Some(param_snippet) = snippet_opt(cx, param.span) && let Some(filter) = snippet_opt(cx, recv.span) && let Some(map) = snippet_opt(cx, then_body.span) @@ -46,7 +52,10 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: & call_span, "usage of `bool::then` in `filter_map`", "use `filter` then `map` instead", - format!("filter(|&{param_snippet}| {filter}).map(|{param_snippet}| {map})"), + format!( + "filter(|&{param_snippet}| {derefs}{filter}).map(|{param_snippet}| {map})", + derefs="*".repeat(needed_derefs) + ), Applicability::MachineApplicable, ); } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 81223fa8d954f..e7fcef9e9de27 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -74,9 +74,11 @@ mod option_map_unwrap_or; mod or_fun_call; mod or_then_unwrap; mod path_buf_push_overwrite; +mod path_ends_with_ext; mod range_zip_with_len; mod read_line_without_trim; mod readonly_write_lock; +mod redundant_as_str; mod repeat_once; mod search_is_some; mod seek_from_current; @@ -120,9 +122,10 @@ use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item}; use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, peel_blocks, return_ty}; use if_chain::if_chain; +pub use path_ends_with_ext::DEFAULT_ALLOWED_DOTFILES; +use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::{Expr, ExprKind, Node, Stmt, StmtKind, TraitItem, TraitItemKind}; -use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, TraitRef, Ty}; @@ -3563,11 +3566,77 @@ declare_clippy_lint! { "calls to `.take()` or `.skip()` that are out of bounds" } +declare_clippy_lint! { + /// ### What it does + /// Looks for calls to `Path::ends_with` calls where the argument looks like a file extension. + /// + /// By default, Clippy has a short list of known filenames that start with a dot + /// but aren't necessarily file extensions (e.g. the `.git` folder), which are allowed by default. + /// The `allowed-dotfiles` configuration can be used to allow additional + /// file extensions that Clippy should not lint. + /// + /// ### Why is this bad? + /// This doesn't actually compare file extensions. Rather, `ends_with` compares the given argument + /// to the last **component** of the path and checks if it matches exactly. + /// + /// ### Known issues + /// File extensions are often at most three characters long, so this only lints in those cases + /// in an attempt to avoid false positives. + /// Any extension names longer than that are assumed to likely be real path components and are + /// therefore ignored. + /// + /// ### Example + /// ```rust + /// # use std::path::Path; + /// fn is_markdown(path: &Path) -> bool { + /// path.ends_with(".md") + /// } + /// ``` + /// Use instead: + /// ```rust + /// # use std::path::Path; + /// fn is_markdown(path: &Path) -> bool { + /// path.extension().is_some_and(|ext| ext == "md") + /// } + /// ``` + #[clippy::version = "1.74.0"] + pub PATH_ENDS_WITH_EXT, + suspicious, + "attempting to compare file extensions using `Path::ends_with`" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for usage of `as_str()` on a `String`` chained with a method available on the `String` itself. + /// + /// ### Why is this bad? + /// The `as_str()` conversion is pointless and can be removed for simplicity and cleanliness. + /// + /// ### Example + /// ```rust + /// # #![allow(unused)] + /// let owned_string = "This is a string".to_owned(); + /// owned_string.as_str().as_bytes(); + /// ``` + /// + /// Use instead: + /// ```rust + /// # #![allow(unused)] + /// let owned_string = "This is a string".to_owned(); + /// owned_string.as_bytes(); + /// ``` + #[clippy::version = "1.74.0"] + pub REDUNDANT_AS_STR, + complexity, + "`as_str` used to call a method on `str` that is also available on `String`" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Msrv, allow_expect_in_tests: bool, allow_unwrap_in_tests: bool, + allowed_dotfiles: FxHashSet, } impl Methods { @@ -3577,12 +3646,14 @@ impl Methods { msrv: Msrv, allow_expect_in_tests: bool, allow_unwrap_in_tests: bool, + allowed_dotfiles: FxHashSet, ) -> Self { Self { avoid_breaking_exported_api, msrv, allow_expect_in_tests, allow_unwrap_in_tests, + allowed_dotfiles, } } } @@ -3703,6 +3774,8 @@ impl_lint_pass!(Methods => [ FILTER_MAP_BOOL_THEN, READONLY_WRITE_LOCK, ITER_OUT_OF_BOUNDS, + PATH_ENDS_WITH_EXT, + REDUNDANT_AS_STR, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -3852,18 +3925,20 @@ impl<'tcx> LateLintPass<'tcx> for Methods { if_chain! { if let TraitItemKind::Fn(ref sig, _) = item.kind; if sig.decl.implicit_self.has_implicit_self(); - if let Some(first_arg_ty) = sig.decl.inputs.iter().next(); - + if let Some(first_arg_hir_ty) = sig.decl.inputs.first(); + if let Some(&first_arg_ty) = cx.tcx.fn_sig(item.owner_id) + .instantiate_identity() + .inputs() + .skip_binder() + .first(); then { - let first_arg_span = first_arg_ty.span; - let first_arg_ty = hir_ty_to_ty(cx.tcx, first_arg_ty); let self_ty = TraitRef::identity(cx.tcx, item.owner_id.to_def_id()).self_ty(); wrong_self_convention::check( cx, item.ident.name.as_str(), self_ty, first_arg_ty, - first_arg_span, + first_arg_hir_ty.span, false, true, ); @@ -3929,6 +4004,7 @@ impl Methods { ("as_deref" | "as_deref_mut", []) => { needless_option_as_deref::check(cx, expr, recv, name); }, + ("as_bytes" | "is_empty", []) => if let Some(("as_str", recv, [], as_str_span, _)) = method_call(recv) { redundant_as_str::check(cx, expr, recv, as_str_span, span); }, ("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv), ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv), ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv), @@ -3978,6 +4054,7 @@ impl Methods { if let ExprKind::MethodCall(.., span) = expr.kind { case_sensitive_file_extension_comparisons::check(cx, expr, span, recv, arg); } + path_ends_with_ext::check(cx, recv, arg, expr, &self.msrv, &self.allowed_dotfiles); }, ("expect", [_]) => { match method_call(recv) { diff --git a/clippy_lints/src/methods/path_ends_with_ext.rs b/clippy_lints/src/methods/path_ends_with_ext.rs new file mode 100644 index 0000000000000..3347c8c162015 --- /dev/null +++ b/clippy_lints/src/methods/path_ends_with_ext.rs @@ -0,0 +1,53 @@ +use super::PATH_ENDS_WITH_EXT; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs; +use clippy_utils::msrvs::Msrv; +use clippy_utils::source::snippet; +use clippy_utils::ty::is_type_diagnostic_item; +use rustc_ast::{LitKind, StrStyle}; +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_span::sym; +use std::fmt::Write; + +pub const DEFAULT_ALLOWED_DOTFILES: &[&str] = &[ + "git", "svn", "gem", "npm", "vim", "env", "rnd", "ssh", "vnc", "smb", "nvm", "bin", +]; + +pub(super) fn check( + cx: &LateContext<'_>, + recv: &Expr<'_>, + path: &Expr<'_>, + expr: &Expr<'_>, + msrv: &Msrv, + allowed_dotfiles: &FxHashSet, +) { + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv).peel_refs(), sym::Path) + && !path.span.from_expansion() + && let ExprKind::Lit(lit) = path.kind + && let LitKind::Str(path, StrStyle::Cooked) = lit.node + && let Some(path) = path.as_str().strip_prefix('.') + && (1..=3).contains(&path.len()) + && !allowed_dotfiles.contains(path) + && path.chars().all(char::is_alphanumeric) + { + let mut sugg = snippet(cx, recv.span, "..").into_owned(); + if msrv.meets(msrvs::OPTION_IS_SOME_AND) { + let _ = write!(sugg, r#".extension().is_some_and(|ext| ext == "{path}")"#); + } else { + let _ = write!(sugg, r#".extension().map_or(false, |ext| ext == "{path}")"#); + }; + + span_lint_and_sugg( + cx, + PATH_ENDS_WITH_EXT, + expr.span, + "this looks like a failed attempt at checking for the file extension", + "try", + sugg, + Applicability::MaybeIncorrect + ); + } +} diff --git a/clippy_lints/src/methods/redundant_as_str.rs b/clippy_lints/src/methods/redundant_as_str.rs new file mode 100644 index 0000000000000..98cd6afc2b79f --- /dev/null +++ b/clippy_lints/src/methods/redundant_as_str.rs @@ -0,0 +1,34 @@ +use super::REDUNDANT_AS_STR; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use rustc_errors::Applicability; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_middle::query::Key; +use rustc_span::Span; + +pub(super) fn check( + cx: &LateContext<'_>, + _expr: &Expr<'_>, + recv: &Expr<'_>, + as_str_span: Span, + other_method_span: Span, +) { + if cx + .tcx + .lang_items() + .string() + .is_some_and(|id| Some(id) == cx.typeck_results().expr_ty(recv).ty_adt_id()) + { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + REDUNDANT_AS_STR, + as_str_span.to(other_method_span), + "this `as_str` is redundant and can be removed as the method immediately following exists on `String` too", + "try", + snippet_with_applicability(cx, other_method_span, "..", &mut applicability).into_owned(), + applicability, + ); + } +} diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 303f012569087..9c8b47fb30327 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -1,24 +1,22 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_hir_and_then}; -use clippy_utils::source::{snippet, snippet_opt, snippet_with_context}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_then, span_lint_hir_and_then}; +use clippy_utils::source::{snippet, snippet_with_context}; +use clippy_utils::sugg::Sugg; +use clippy_utils::{ + any_parent_is_automatically_derived, fulfill_or_allowed, get_parent_expr, is_lint_allowed, iter_input_pats, + last_path_segment, SpanlessEq, +}; use if_chain::if_chain; use rustc_errors::Applicability; +use rustc_hir::def::Res; use rustc_hir::intravisit::FnKind; use rustc_hir::{ - self as hir, def, BinOpKind, BindingAnnotation, Body, ByRef, Expr, ExprKind, FnDecl, Mutability, PatKind, Stmt, - StmtKind, TyKind, + BinOpKind, BindingAnnotation, Body, ByRef, Expr, ExprKind, FnDecl, Mutability, PatKind, QPath, Stmt, StmtKind, }; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::def_id::LocalDefId; -use rustc_span::hygiene::DesugaringKind; -use rustc_span::source_map::{ExpnKind, Span}; - -use clippy_utils::sugg::Sugg; -use clippy_utils::{ - get_parent_expr, in_constant, is_integer_literal, is_lint_allowed, is_no_std_crate, iter_input_pats, - last_path_segment, SpanlessEq, -}; +use rustc_span::source_map::Span; use crate::ref_patterns::REF_PATTERNS; @@ -56,6 +54,7 @@ declare_clippy_lint! { style, "an entire binding declared as `ref`, in a function argument or a `let` statement" } + declare_clippy_lint! { /// ### What it does /// Checks for the use of bindings with a single leading @@ -103,51 +102,13 @@ declare_clippy_lint! { "using a short circuit boolean condition as a statement" } -declare_clippy_lint! { - /// ### What it does - /// Catch casts from `0` to some pointer type - /// - /// ### Why is this bad? - /// This generally means `null` and is better expressed as - /// {`std`, `core`}`::ptr::`{`null`, `null_mut`}. - /// - /// ### Example - /// ```rust - /// let a = 0 as *const u32; - /// ``` - /// - /// Use instead: - /// ```rust - /// let a = std::ptr::null::(); - /// ``` - #[clippy::version = "pre 1.29.0"] - pub ZERO_PTR, - style, - "using `0 as *{const, mut} T`" -} - -pub struct LintPass { - std_or_core: &'static str, -} -impl Default for LintPass { - fn default() -> Self { - Self { std_or_core: "std" } - } -} -impl_lint_pass!(LintPass => [ +declare_lint_pass!(LintPass => [ TOPLEVEL_REF_ARG, USED_UNDERSCORE_BINDING, SHORT_CIRCUIT_STATEMENT, - ZERO_PTR, ]); impl<'tcx> LateLintPass<'tcx> for LintPass { - fn check_crate(&mut self, cx: &LateContext<'_>) { - if is_no_std_crate(cx) { - self.std_or_core = "core"; - } - } - fn check_fn( &mut self, cx: &LateContext<'tcx>, @@ -253,50 +214,56 @@ impl<'tcx> LateLintPass<'tcx> for LintPass { } fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Cast(e, ty) = expr.kind { - self.check_cast(cx, expr.span, e, ty); - return; - } - if in_attributes_expansion(expr) || expr.span.is_desugaring(DesugaringKind::Await) { - // Don't lint things expanded by #[derive(...)], etc or `await` desugaring + if in_external_macro(cx.sess(), expr.span) + || expr.span.desugaring_kind().is_some() + || any_parent_is_automatically_derived(cx.tcx, expr.hir_id) + { return; } - let sym; - let binding = match expr.kind { - ExprKind::Path(ref qpath) if !matches!(qpath, hir::QPath::LangItem(..)) => { - let binding = last_path_segment(qpath).ident.as_str(); - if binding.starts_with('_') && - !binding.starts_with("__") && - binding != "_result" && // FIXME: #944 - is_used(cx, expr) && - // don't lint if the declaration is in a macro - non_macro_local(cx, cx.qpath_res(qpath, expr.hir_id)) + let (definition_hir_id, ident) = match expr.kind { + ExprKind::Path(ref qpath) => { + if let QPath::Resolved(None, path) = qpath + && let Res::Local(id) = path.res + && is_used(cx, expr) { - Some(binding) + (id, last_path_segment(qpath).ident) } else { - None + return; } }, - ExprKind::Field(_, ident) => { - sym = ident.name; - let name = sym.as_str(); - if name.starts_with('_') && !name.starts_with("__") { - Some(name) + ExprKind::Field(recv, ident) => { + if let Some(adt_def) = cx.typeck_results().expr_ty_adjusted(recv).ty_adt_def() + && let Some(field) = adt_def.all_fields().find(|field| field.name == ident.name) + && let Some(local_did) = field.did.as_local() + && let Some(hir_id) = cx.tcx.opt_local_def_id_to_hir_id(local_did) + && !cx.tcx.type_of(field.did).skip_binder().is_phantom_data() + { + (hir_id, ident) } else { - None + return; } }, - _ => None, + _ => return, }; - if let Some(binding) = binding { - span_lint( + + let name = ident.name.as_str(); + if name.starts_with('_') + && !name.starts_with("__") + && let definition_span = cx.tcx.hir().span(definition_hir_id) + && !definition_span.from_expansion() + && !fulfill_or_allowed(cx, USED_UNDERSCORE_BINDING, [expr.hir_id, definition_hir_id]) + { + span_lint_and_then( cx, USED_UNDERSCORE_BINDING, expr.span, &format!( - "used binding `{binding}` which is prefixed with an underscore. A leading \ + "used binding `{name}` which is prefixed with an underscore. A leading \ underscore signals that a binding will not be used" ), + |diag| { + diag.span_note(definition_span, format!("`{name}` is defined here")); + } ); } } @@ -311,50 +278,3 @@ fn is_used(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { _ => is_used(cx, parent), }) } - -/// Tests whether an expression is in a macro expansion (e.g., something -/// generated by `#[derive(...)]` or the like). -fn in_attributes_expansion(expr: &Expr<'_>) -> bool { - use rustc_span::hygiene::MacroKind; - if expr.span.from_expansion() { - let data = expr.span.ctxt().outer_expn_data(); - matches!(data.kind, ExpnKind::Macro(MacroKind::Attr | MacroKind::Derive, _)) - } else { - false - } -} - -/// Tests whether `res` is a variable defined outside a macro. -fn non_macro_local(cx: &LateContext<'_>, res: def::Res) -> bool { - if let def::Res::Local(id) = res { - !cx.tcx.hir().span(id).from_expansion() - } else { - false - } -} - -impl LintPass { - fn check_cast(&self, cx: &LateContext<'_>, span: Span, e: &Expr<'_>, ty: &hir::Ty<'_>) { - if_chain! { - if let TyKind::Ptr(ref mut_ty) = ty.kind; - if is_integer_literal(e, 0); - if !in_constant(cx, e.hir_id); - then { - let (msg, sugg_fn) = match mut_ty.mutbl { - Mutability::Mut => ("`0 as *mut _` detected", "ptr::null_mut"), - Mutability::Not => ("`0 as *const _` detected", "ptr::null"), - }; - - let (sugg, appl) = if let TyKind::Infer = mut_ty.ty.kind { - (format!("{}::{sugg_fn}()", self.std_or_core), Applicability::MachineApplicable) - } else if let Some(mut_ty_snip) = snippet_opt(cx, mut_ty.ty.span) { - (format!("{}::{sugg_fn}::<{mut_ty_snip}>()", self.std_or_core), Applicability::MachineApplicable) - } else { - // `MaybeIncorrect` as type inference may not work with the suggested code - (format!("{}::{sugg_fn}()", self.std_or_core), Applicability::MaybeIncorrect) - }; - span_lint_and_sugg(cx, ZERO_PTR, span, msg, "try", sugg, appl); - } - } - } -} diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index 3b7eccad79df8..f598a65d2e488 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -7,7 +7,6 @@ use rustc_hir as hir; use rustc_hir::def_id::CRATE_DEF_ID; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, Constness, FnDecl, GenericParamKind}; -use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -124,7 +123,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { FnKind::Method(_, sig, ..) => { if trait_ref_of_method(cx, def_id).is_some() || already_const(sig.header) - || method_accepts_droppable(cx, sig.decl.inputs) + || method_accepts_droppable(cx, def_id) { return; } @@ -165,12 +164,11 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { /// Returns true if any of the method parameters is a type that implements `Drop`. The method /// can't be made const then, because `drop` can't be const-evaluated. -fn method_accepts_droppable(cx: &LateContext<'_>, param_tys: &[hir::Ty<'_>]) -> bool { +fn method_accepts_droppable(cx: &LateContext<'_>, def_id: LocalDefId) -> bool { + let sig = cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder(); + // If any of the params are droppable, return true - param_tys.iter().any(|hir_ty| { - let ty_ty = hir_ty_to_ty(cx.tcx, hir_ty); - has_drop(cx, ty_ty) - }) + sig.inputs().iter().any(|&ty| has_drop(cx, ty)) } // We don't have to lint on something that's already `const` diff --git a/clippy_lints/src/needless_borrows_for_generic_args.rs b/clippy_lints/src/needless_borrows_for_generic_args.rs new file mode 100644 index 0000000000000..d55c77a92b158 --- /dev/null +++ b/clippy_lints/src/needless_borrows_for_generic_args.rs @@ -0,0 +1,410 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::mir::{enclosing_mir, expr_local, local_assignments, used_exactly_once, PossibleBorrowerMap}; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::source::snippet_with_context; +use clippy_utils::ty::is_copy; +use clippy_utils::{expr_use_ctxt, peel_n_hir_expr_refs, DefinedTy, ExprUseNode}; +use rustc_errors::Applicability; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::{Body, Expr, ExprKind, Mutability, Path, QPath}; +use rustc_index::bit_set::BitSet; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::mir::{Rvalue, StatementKind}; +use rustc_middle::ty::{ + self, ClauseKind, EarlyBinder, FnSig, GenericArg, GenericArgKind, List, ParamTy, ProjectionPredicate, Ty, +}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::symbol::sym; +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; +use rustc_trait_selection::traits::{Obligation, ObligationCause}; +use std::collections::VecDeque; + +declare_clippy_lint! { + /// ### What it does + /// Checks for borrow operations (`&`) that used as a generic argument to a + /// function when the borrowed value could be used. + /// + /// ### Why is this bad? + /// Suggests that the receiver of the expression borrows + /// the expression. + /// + /// ### Known problems + /// The lint cannot tell when the implementation of a trait + /// for `&T` and `T` do different things. Removing a borrow + /// in such a case can change the semantics of the code. + /// + /// ### Example + /// ```rust + /// fn f(_: impl AsRef) {} + /// + /// let x = "foo"; + /// f(&x); + /// ``` + /// + /// Use instead: + /// ```rust + /// fn f(_: impl AsRef) {} + /// + /// let x = "foo"; + /// f(x); + /// ``` + #[clippy::version = "pre 1.29.0"] + pub NEEDLESS_BORROWS_FOR_GENERIC_ARGS, + style, + "taking a reference that is going to be automatically dereferenced" +} + +pub struct NeedlessBorrowsForGenericArgs<'tcx> { + /// Stack of (body owner, `PossibleBorrowerMap`) pairs. Used by + /// `needless_borrow_impl_arg_position` to determine when a borrowed expression can instead + /// be moved. + possible_borrowers: Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, + + // `IntoIterator` for arrays requires Rust 1.53. + msrv: Msrv, +} +impl_lint_pass!(NeedlessBorrowsForGenericArgs<'_> => [NEEDLESS_BORROWS_FOR_GENERIC_ARGS]); + +impl NeedlessBorrowsForGenericArgs<'_> { + #[must_use] + pub fn new(msrv: Msrv) -> Self { + Self { + possible_borrowers: Vec::new(), + msrv, + } + } +} + +impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowsForGenericArgs<'tcx> { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if matches!(expr.kind, ExprKind::AddrOf(..)) + && !expr.span.from_expansion() + && let Some(use_cx) = expr_use_ctxt(cx, expr) + && !use_cx.is_ty_unified + && let Some(DefinedTy::Mir(ty)) = use_cx.node.defined_ty(cx) + && let ty::Param(ty) = *ty.value.skip_binder().kind() + && let Some((hir_id, fn_id, i)) = match use_cx.node { + ExprUseNode::MethodArg(_, _, 0) => None, + ExprUseNode::MethodArg(hir_id, None, i) => { + cx.typeck_results().type_dependent_def_id(hir_id).map(|id| (hir_id, id, i)) + }, + ExprUseNode::FnArg(&Expr { kind: ExprKind::Path(ref p), hir_id, .. }, i) + if !path_has_args(p) => match cx.typeck_results().qpath_res(p, hir_id) { + Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) => { + Some((hir_id, id, i)) + }, + _ => None, + }, + _ => None, + } && let count = needless_borrow_count( + cx, + &mut self.possible_borrowers, + fn_id, + cx.typeck_results().node_args(hir_id), + i, + ty, + expr, + &self.msrv, + ) && count != 0 + { + span_lint_and_then( + cx, + NEEDLESS_BORROWS_FOR_GENERIC_ARGS, + expr.span, + "the borrowed expression implements the required traits", + |diag| { + let mut app = Applicability::MachineApplicable; + let snip_span = peel_n_hir_expr_refs(expr, count).0.span; + let snip = snippet_with_context(cx, snip_span, expr.span.ctxt(), "..", &mut app).0; + diag.span_suggestion(expr.span, "change this to", snip.into_owned(), app); + } + ); + } + } + + fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) { + if self.possible_borrowers.last().map_or(false, |&(local_def_id, _)| { + local_def_id == cx.tcx.hir().body_owner_def_id(body.id()) + }) { + self.possible_borrowers.pop(); + } + } + + extract_msrv_attr!(LateContext); +} + +fn path_has_args(p: &QPath<'_>) -> bool { + match *p { + QPath::Resolved(_, Path { segments: [.., s], .. }) | QPath::TypeRelative(_, s) => s.args.is_some(), + _ => false, + } +} + +/// Checks for the number of borrow expressions which can be removed from the given expression +/// where the expression is used as an argument to a function expecting a generic type. +/// +/// The following constraints will be checked: +/// * The borrowed expression meets all the generic type's constraints. +/// * The generic type appears only once in the functions signature. +/// * The borrowed value will not be moved if it is used later in the function. +#[expect(clippy::too_many_arguments)] +fn needless_borrow_count<'tcx>( + cx: &LateContext<'tcx>, + possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, + fn_id: DefId, + callee_args: &'tcx List>, + arg_index: usize, + param_ty: ParamTy, + mut expr: &Expr<'tcx>, + msrv: &Msrv, +) -> usize { + let destruct_trait_def_id = cx.tcx.lang_items().destruct_trait(); + let sized_trait_def_id = cx.tcx.lang_items().sized_trait(); + + let fn_sig = cx.tcx.fn_sig(fn_id).instantiate_identity().skip_binder(); + let predicates = cx.tcx.param_env(fn_id).caller_bounds(); + let projection_predicates = predicates + .iter() + .filter_map(|predicate| { + if let ClauseKind::Projection(projection_predicate) = predicate.kind().skip_binder() { + Some(projection_predicate) + } else { + None + } + }) + .collect::>(); + + let mut trait_with_ref_mut_self_method = false; + + // If no traits were found, or only the `Destruct`, `Sized`, or `Any` traits were found, return. + if predicates + .iter() + .filter_map(|predicate| { + if let ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder() + && trait_predicate.trait_ref.self_ty() == param_ty.to_ty(cx.tcx) + { + Some(trait_predicate.trait_ref.def_id) + } else { + None + } + }) + .inspect(|trait_def_id| { + trait_with_ref_mut_self_method |= has_ref_mut_self_method(cx, *trait_def_id); + }) + .all(|trait_def_id| { + Some(trait_def_id) == destruct_trait_def_id + || Some(trait_def_id) == sized_trait_def_id + || cx.tcx.is_diagnostic_item(sym::Any, trait_def_id) + }) + { + return 0; + } + + // See: + // - https://github.com/rust-lang/rust-clippy/pull/9674#issuecomment-1289294201 + // - https://github.com/rust-lang/rust-clippy/pull/9674#issuecomment-1292225232 + if projection_predicates + .iter() + .any(|projection_predicate| is_mixed_projection_predicate(cx, fn_id, projection_predicate)) + { + return 0; + } + + // `args_with_referent_ty` can be constructed outside of `check_referent` because the same + // elements are modified each time `check_referent` is called. + let mut args_with_referent_ty = callee_args.to_vec(); + + let mut check_reference_and_referent = |reference, referent| { + let referent_ty = cx.typeck_results().expr_ty(referent); + + if !is_copy(cx, referent_ty) + && (referent_ty.has_significant_drop(cx.tcx, cx.param_env) + || !referent_used_exactly_once(cx, possible_borrowers, reference)) + { + return false; + } + + // https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321 + if trait_with_ref_mut_self_method && !matches!(referent_ty.kind(), ty::Ref(_, _, Mutability::Mut)) { + return false; + } + + if !replace_types( + cx, + param_ty, + referent_ty, + fn_sig, + arg_index, + &projection_predicates, + &mut args_with_referent_ty, + ) { + return false; + } + + predicates.iter().all(|predicate| { + if let ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder() + && cx.tcx.is_diagnostic_item(sym::IntoIterator, trait_predicate.trait_ref.def_id) + && let ty::Param(param_ty) = trait_predicate.self_ty().kind() + && let GenericArgKind::Type(ty) = args_with_referent_ty[param_ty.index as usize].unpack() + && ty.is_array() + && !msrv.meets(msrvs::ARRAY_INTO_ITERATOR) + { + return false; + } + + let predicate = EarlyBinder::bind(predicate).instantiate(cx.tcx, &args_with_referent_ty); + let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), cx.param_env, predicate); + let infcx = cx.tcx.infer_ctxt().build(); + infcx.predicate_must_hold_modulo_regions(&obligation) + }) + }; + + let mut count = 0; + while let ExprKind::AddrOf(_, _, referent) = expr.kind { + if !check_reference_and_referent(expr, referent) { + break; + } + expr = referent; + count += 1; + } + count +} + +fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool { + cx.tcx + .associated_items(trait_def_id) + .in_definition_order() + .any(|assoc_item| { + if assoc_item.fn_has_self_parameter { + let self_ty = cx + .tcx + .fn_sig(assoc_item.def_id) + .instantiate_identity() + .skip_binder() + .inputs()[0]; + matches!(self_ty.kind(), ty::Ref(_, _, Mutability::Mut)) + } else { + false + } + }) +} + +fn is_mixed_projection_predicate<'tcx>( + cx: &LateContext<'tcx>, + callee_def_id: DefId, + projection_predicate: &ProjectionPredicate<'tcx>, +) -> bool { + let generics = cx.tcx.generics_of(callee_def_id); + // The predicate requires the projected type to equal a type parameter from the parent context. + if let Some(term_ty) = projection_predicate.term.ty() + && let ty::Param(term_param_ty) = term_ty.kind() + && (term_param_ty.index as usize) < generics.parent_count + { + // The inner-most self type is a type parameter from the current function. + let mut projection_ty = projection_predicate.projection_ty; + loop { + match projection_ty.self_ty().kind() { + ty::Alias(ty::Projection, inner_projection_ty) => { + projection_ty = *inner_projection_ty; + } + ty::Param(param_ty) => { + return (param_ty.index as usize) >= generics.parent_count; + } + _ => { + return false; + } + } + } + } else { + false + } +} + +fn referent_used_exactly_once<'tcx>( + cx: &LateContext<'tcx>, + possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, + reference: &Expr<'tcx>, +) -> bool { + if let Some(mir) = enclosing_mir(cx.tcx, reference.hir_id) + && let Some(local) = expr_local(cx.tcx, reference) + && let [location] = *local_assignments(mir, local).as_slice() + && let block_data = &mir.basic_blocks[location.block] + && let Some(statement) = block_data.statements.get(location.statement_index) + && let StatementKind::Assign(box (_, Rvalue::Ref(_, _, place))) = statement.kind + && !place.is_indirect_first_projection() + { + let body_owner_local_def_id = cx.tcx.hir().enclosing_body_owner(reference.hir_id); + if possible_borrowers + .last() + .map_or(true, |&(local_def_id, _)| local_def_id != body_owner_local_def_id) + { + possible_borrowers.push((body_owner_local_def_id, PossibleBorrowerMap::new(cx, mir))); + } + let possible_borrower = &mut possible_borrowers.last_mut().unwrap().1; + // If `only_borrowers` were used here, the `copyable_iterator::warn` test would fail. The reason is + // that `PossibleBorrowerVisitor::visit_terminator` considers `place.local` a possible borrower of + // itself. See the comment in that method for an explanation as to why. + possible_borrower.bounded_borrowers(&[local], &[local, place.local], place.local, location) + && used_exactly_once(mir, place.local).unwrap_or(false) + } else { + false + } +} + +// Iteratively replaces `param_ty` with `new_ty` in `args`, and similarly for each resulting +// projected type that is a type parameter. Returns `false` if replacing the types would have an +// effect on the function signature beyond substituting `new_ty` for `param_ty`. +// See: https://github.com/rust-lang/rust-clippy/pull/9136#discussion_r927212757 +fn replace_types<'tcx>( + cx: &LateContext<'tcx>, + param_ty: ParamTy, + new_ty: Ty<'tcx>, + fn_sig: FnSig<'tcx>, + arg_index: usize, + projection_predicates: &[ProjectionPredicate<'tcx>], + args: &mut [ty::GenericArg<'tcx>], +) -> bool { + let mut replaced = BitSet::new_empty(args.len()); + + let mut deque = VecDeque::with_capacity(args.len()); + deque.push_back((param_ty, new_ty)); + + while let Some((param_ty, new_ty)) = deque.pop_front() { + // If `replaced.is_empty()`, then `param_ty` and `new_ty` are those initially passed in. + if !fn_sig + .inputs_and_output + .iter() + .enumerate() + .all(|(i, ty)| (replaced.is_empty() && i == arg_index) || !ty.contains(param_ty.to_ty(cx.tcx))) + { + return false; + } + + args[param_ty.index as usize] = ty::GenericArg::from(new_ty); + + // The `replaced.insert(...)` check provides some protection against infinite loops. + if replaced.insert(param_ty.index) { + for projection_predicate in projection_predicates { + if projection_predicate.projection_ty.self_ty() == param_ty.to_ty(cx.tcx) + && let Some(term_ty) = projection_predicate.term.ty() + && let ty::Param(term_param_ty) = term_ty.kind() + { + let projection = cx.tcx.mk_ty_from_kind(ty::Alias( + ty::Projection, + projection_predicate.projection_ty.with_self_ty(cx.tcx, new_ty), + )); + + if let Ok(projected_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, projection) + && args[term_param_ty.index as usize] != ty::GenericArg::from(projected_ty) + { + deque.push_back((*term_param_ty, projected_ty)); + } + } + } + } + } + + true +} diff --git a/clippy_lints/src/needless_pass_by_ref_mut.rs b/clippy_lints/src/needless_pass_by_ref_mut.rs index 7b00eabf97b41..57652e5ff546b 100644 --- a/clippy_lints/src/needless_pass_by_ref_mut.rs +++ b/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -1,6 +1,7 @@ use super::needless_pass_by_value::requires_exact_signature; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::source::snippet; +use clippy_utils::visitors::for_each_expr_with_closures; use clippy_utils::{get_parent_node, inherits_cfg, is_from_proc_macro, is_self}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_errors::Applicability; @@ -9,7 +10,7 @@ use rustc_hir::{ Body, Closure, Expr, ExprKind, FnDecl, HirId, HirIdMap, HirIdSet, Impl, ItemKind, Mutability, Node, PatKind, QPath, }; use rustc_hir_typeck::expr_use_visitor as euv; -use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::associated_body; use rustc_middle::hir::nested_filter::OnlyBodies; @@ -21,6 +22,8 @@ use rustc_span::symbol::kw; use rustc_span::Span; use rustc_target::spec::abi::Abi; +use core::ops::ControlFlow; + declare_clippy_lint! { /// ### What it does /// Check if a `&mut` function argument is actually used mutably. @@ -95,6 +98,30 @@ fn should_skip<'tcx>( is_from_proc_macro(cx, &input) } +fn check_closures<'tcx>( + ctx: &mut MutablyUsedVariablesCtxt<'tcx>, + cx: &LateContext<'tcx>, + infcx: &InferCtxt<'tcx>, + checked_closures: &mut FxHashSet, + closures: FxHashSet, +) { + let hir = cx.tcx.hir(); + for closure in closures { + if !checked_closures.insert(closure) { + continue; + } + ctx.prev_bind = None; + ctx.prev_move_to_closure.clear(); + if let Some(body) = hir + .find_by_def_id(closure) + .and_then(associated_body) + .map(|(_, body_id)| hir.body(body_id)) + { + euv::ExprUseVisitor::new(ctx, infcx, closure, cx.param_env, cx.typeck_results()).consume_body(body); + } + } +} + impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { fn check_fn( &mut self, @@ -161,25 +188,22 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { euv::ExprUseVisitor::new(&mut ctx, &infcx, fn_def_id, cx.param_env, cx.typeck_results()).consume_body(body); if is_async { let mut checked_closures = FxHashSet::default(); + + // We retrieve all the closures declared in the async function because they will + // not be found by `euv::Delegate`. + let mut closures: FxHashSet = FxHashSet::default(); + for_each_expr_with_closures(cx, body, |expr| { + if let ExprKind::Closure(closure) = expr.kind { + closures.insert(closure.def_id); + } + ControlFlow::<()>::Continue(()) + }); + check_closures(&mut ctx, cx, &infcx, &mut checked_closures, closures); + while !ctx.async_closures.is_empty() { - let closures = ctx.async_closures.clone(); + let async_closures = ctx.async_closures.clone(); ctx.async_closures.clear(); - let hir = cx.tcx.hir(); - for closure in closures { - if !checked_closures.insert(closure) { - continue; - } - ctx.prev_bind = None; - ctx.prev_move_to_closure.clear(); - if let Some(body) = hir - .find_by_def_id(closure) - .and_then(associated_body) - .map(|(_, body_id)| hir.body(body_id)) - { - euv::ExprUseVisitor::new(&mut ctx, &infcx, closure, cx.param_env, cx.typeck_results()) - .consume_body(body); - } - } + check_closures(&mut ctx, cx, &infcx, &mut checked_closures, async_closures); } } ctx @@ -244,6 +268,10 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { struct MutablyUsedVariablesCtxt<'tcx> { mutably_used_vars: HirIdSet, prev_bind: Option, + /// In async functions, the inner AST is composed of multiple layers until we reach the code + /// defined by the user. Because of that, some variables are marked as mutably borrowed even + /// though they're not. This field lists the `HirId` that should not be considered as mutable + /// use of a variable. prev_move_to_closure: HirIdSet, aliases: HirIdMap, async_closures: FxHashSet, @@ -308,7 +336,12 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> { fn borrow(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, _id: HirId, borrow: ty::BorrowKind) { self.prev_bind = None; if let euv::Place { - base: euv::PlaceBase::Local(vid), + base: + euv::PlaceBase::Local(vid) + | euv::PlaceBase::Upvar(UpvarId { + var_path: UpvarPath { hir_id: vid }, + .. + }), base_ty, .. } = &cmt.place diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index 5f2a324b05fb9..aee184252fbfd 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs @@ -5,10 +5,8 @@ use clippy_utils::{get_parent_node, is_lint_allowed, peel_blocks}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{ - is_range_literal, BinOpKind, BlockCheckMode, Expr, ExprKind, FnRetTy, ItemKind, Node, PatKind, Stmt, StmtKind, - UnsafeSource, + is_range_literal, BinOpKind, BlockCheckMode, Expr, ExprKind, ItemKind, Node, PatKind, Stmt, StmtKind, UnsafeSource, }; -use rustc_hir_analysis::hir_ty_to_ty; use rustc_infer::infer::TyCtxtInferExt as _; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -99,14 +97,13 @@ fn check_no_effect(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool { |diag| { for parent in cx.tcx.hir().parent_iter(stmt.hir_id) { if let Node::Item(item) = parent.1 - && let ItemKind::Fn(sig, ..) = item.kind - && let FnRetTy::Return(ret_ty) = sig.decl.output + && let ItemKind::Fn(..) = item.kind && let Some(Node::Block(block)) = get_parent_node(cx.tcx, stmt.hir_id) && let [.., final_stmt] = block.stmts && final_stmt.hir_id == stmt.hir_id { let expr_ty = cx.typeck_results().expr_ty(expr); - let mut ret_ty = hir_ty_to_ty(cx.tcx, ret_ty); + let mut ret_ty = cx.tcx.fn_sig(item.owner_id).instantiate_identity().output().skip_binder(); // Remove `impl Future` to get `T` if cx.tcx.ty_is_opaque_future(ret_ty) && @@ -115,7 +112,7 @@ fn check_no_effect(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool { ret_ty = true_ret_ty; } - if ret_ty == expr_ty { + if !ret_ty.is_unit() && ret_ty == expr_ty { diag.span_suggestion( stmt.span.shrink_to_lo(), "did you mean to return it?", diff --git a/clippy_lints/src/non_canonical_impls.rs b/clippy_lints/src/non_canonical_impls.rs index 4b24f059afdf5..20b4b4f03ed47 100644 --- a/clippy_lints/src/non_canonical_impls.rs +++ b/clippy_lints/src/non_canonical_impls.rs @@ -4,7 +4,7 @@ use clippy_utils::ty::implements_trait; use clippy_utils::{get_parent_node, is_res_lang_ctor, last_path_segment, match_def_path, path_res, std_or_core}; use rustc_errors::Applicability; use rustc_hir::def_id::LocalDefId; -use rustc_hir::{Expr, ExprKind, ImplItem, ImplItemKind, ItemKind, LangItem, Node, UnOp}; +use rustc_hir::{Expr, ExprKind, ImplItem, ImplItemKind, LangItem, Node, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::EarlyBinder; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -122,9 +122,6 @@ impl LateLintPass<'_> for NonCanonicalImpls { if cx.tcx.is_automatically_derived(item.owner_id.to_def_id()) { return; } - let ItemKind::Impl(_) = item.kind else { - return; - }; let ImplItemKind::Fn(_, impl_item_id) = cx.tcx.hir().impl_item(impl_item.impl_item_id()).kind else { return; }; @@ -180,17 +177,8 @@ impl LateLintPass<'_> for NonCanonicalImpls { if cx.tcx.is_diagnostic_item(sym::PartialOrd, trait_impl.def_id) && impl_item.ident.name == sym::partial_cmp - && let Some(ord_def_id) = cx - .tcx - .diagnostic_items(trait_impl.def_id.krate) - .name_to_id - .get(&sym::Ord) - && implements_trait( - cx, - trait_impl.self_ty(), - *ord_def_id, - &[], - ) + && let Some(ord_def_id) = cx.tcx.get_diagnostic_item(sym::Ord) + && implements_trait(cx, trait_impl.self_ty(), ord_def_id, &[]) { // If the `cmp` call likely needs to be fully qualified in the suggestion // (like `std::cmp::Ord::cmp`). It's unfortunate we must put this here but we can't diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index 8846633378798..2b4e3260c56b1 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -13,7 +13,6 @@ use rustc_hir::def_id::DefId; use rustc_hir::{ BodyId, Expr, ExprKind, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind, UnOp, }; -use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass, Lint}; use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult, GlobalId}; use rustc_middle::ty::adjustment::Adjust; @@ -297,8 +296,8 @@ declare_lint_pass!(NonCopyConst => [DECLARE_INTERIOR_MUTABLE_CONST, BORROW_INTER impl<'tcx> LateLintPass<'tcx> for NonCopyConst { fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx Item<'_>) { - if let ItemKind::Const(hir_ty, _generics, body_id) = it.kind { - let ty = hir_ty_to_ty(cx.tcx, hir_ty); + if let ItemKind::Const(.., body_id) = it.kind { + let ty = cx.tcx.type_of(it.owner_id).instantiate_identity(); if !ignored_macro(cx, it) && is_unfrozen(cx, ty) && is_value_unfrozen_poly(cx, body_id, ty) { lint(cx, Source::Item { item: it.span }); } @@ -306,8 +305,8 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { } fn check_trait_item(&mut self, cx: &LateContext<'tcx>, trait_item: &'tcx TraitItem<'_>) { - if let TraitItemKind::Const(hir_ty, body_id_opt) = &trait_item.kind { - let ty = hir_ty_to_ty(cx.tcx, hir_ty); + if let TraitItemKind::Const(_, body_id_opt) = &trait_item.kind { + let ty = cx.tcx.type_of(trait_item.owner_id).instantiate_identity(); // Normalize assoc types because ones originated from generic params // bounded other traits could have their bound. @@ -333,7 +332,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) { - if let ImplItemKind::Const(hir_ty, body_id) = &impl_item.kind { + if let ImplItemKind::Const(_, body_id) = &impl_item.kind { let item_def_id = cx.tcx.hir().get_parent_item(impl_item.hir_id()).def_id; let item = cx.tcx.hir().expect_item(item_def_id); @@ -366,7 +365,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { // we should use here as a frozen variant is a potential to be frozen // similar to unknown layouts. // e.g. `layout_of(...).is_err() || has_frozen_variant(...);` - let ty = hir_ty_to_ty(cx.tcx, hir_ty); + let ty = cx.tcx.type_of(impl_item.owner_id).instantiate_identity(); let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty); if is_unfrozen(cx, normalized); if is_value_unfrozen_poly(cx, *body_id, normalized); @@ -381,7 +380,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { } }, ItemKind::Impl(Impl { of_trait: None, .. }) => { - let ty = hir_ty_to_ty(cx.tcx, hir_ty); + let ty = cx.tcx.type_of(impl_item.owner_id).instantiate_identity(); // Normalize assoc types originated from generic params. let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty); diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index bf031ac84549d..7dabdcd58ec2c 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -16,7 +16,6 @@ use rustc_hir::{ ImplItemKind, ItemKind, Lifetime, Mutability, Node, Param, PatKind, QPath, TraitFn, TraitItem, TraitItemKind, TyKind, Unsafety, }; -use rustc_hir_analysis::hir_ty_to_ty; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::{Obligation, ObligationCause}; use rustc_lint::{LateContext, LateLintPass}; @@ -172,13 +171,8 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { for arg in check_fn_args( cx, - cx.tcx - .fn_sig(item.owner_id) - .instantiate_identity() - .skip_binder() - .inputs(), + cx.tcx.fn_sig(item.owner_id).instantiate_identity().skip_binder(), sig.decl.inputs, - &sig.decl.output, &[], ) .filter(|arg| arg.mutability() == Mutability::Not) @@ -237,7 +231,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { let decl = sig.decl; let sig = cx.tcx.fn_sig(item_id).instantiate_identity().skip_binder(); - let lint_args: Vec<_> = check_fn_args(cx, sig.inputs(), decl.inputs, &decl.output, body.params) + let lint_args: Vec<_> = check_fn_args(cx, sig, decl.inputs, body.params) .filter(|arg| !is_trait_item || arg.mutability() == Mutability::Not) .collect(); let results = check_ptr_arg_usage(cx, body, &lint_args); @@ -443,12 +437,13 @@ impl<'tcx> DerefTy<'tcx> { #[expect(clippy::too_many_lines)] fn check_fn_args<'cx, 'tcx: 'cx>( cx: &'cx LateContext<'tcx>, - tys: &'tcx [Ty<'tcx>], + fn_sig: ty::FnSig<'tcx>, hir_tys: &'tcx [hir::Ty<'tcx>], - ret_ty: &'tcx FnRetTy<'tcx>, params: &'tcx [Param<'tcx>], ) -> impl Iterator> + 'cx { - tys.iter() + fn_sig + .inputs() + .iter() .zip(hir_tys.iter()) .enumerate() .filter_map(move |(i, (ty, hir_ty))| { @@ -494,9 +489,7 @@ fn check_fn_args<'cx, 'tcx: 'cx>( }) { if !lifetime.is_anonymous() - && let FnRetTy::Return(ret_ty) = ret_ty - && let ret_ty = hir_ty_to_ty(cx.tcx, ret_ty) - && ret_ty + && fn_sig.output() .walk() .filter_map(|arg| { arg.as_region().and_then(|lifetime| { diff --git a/clippy_lints/src/raw_strings.rs b/clippy_lints/src/raw_strings.rs index e8018462d75f6..2895595e03908 100644 --- a/clippy_lints/src/raw_strings.rs +++ b/clippy_lints/src/raw_strings.rs @@ -93,7 +93,7 @@ impl EarlyLintPass for RawStrings { diag.span_suggestion( start, "use a string literal instead", - format!("\"{}\"", str), + format!("\"{str}\""), Applicability::MachineApplicable, ); } else { @@ -105,8 +105,9 @@ impl EarlyLintPass for RawStrings { } }, ); - - return; + if !matches!(cx.get_lint_level(NEEDLESS_RAW_STRINGS), rustc_lint::Allow) { + return; + } } let req = { diff --git a/clippy_lints/src/transmute/transmute_null_to_fn.rs b/clippy_lints/src/transmute/transmute_null_to_fn.rs index 4944381da24d5..b26365e34ab94 100644 --- a/clippy_lints/src/transmute/transmute_null_to_fn.rs +++ b/clippy_lints/src/transmute/transmute_null_to_fn.rs @@ -28,35 +28,43 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t return false; } - match arg.kind { + let casts_peeled = peel_casts(arg); + match casts_peeled.kind { // Catching: // transmute over constants that resolve to `null`. - ExprKind::Path(ref _qpath) if matches!(constant(cx, cx.typeck_results(), arg), Some(Constant::RawPtr(0))) => { + ExprKind::Path(ref _qpath) + if matches!( + constant(cx, cx.typeck_results(), casts_peeled), + Some(Constant::RawPtr(0)) + ) => + { lint_expr(cx, expr); true }, - - // Catching: - // `std::mem::transmute(0 as *const i32)` - ExprKind::Cast(inner_expr, _cast_ty) if is_integer_literal(inner_expr, 0) => { - lint_expr(cx, expr); - true - }, - // Catching: // `std::mem::transmute(std::ptr::null::())` ExprKind::Call(func1, []) if is_path_diagnostic_item(cx, func1, sym::ptr_null) => { lint_expr(cx, expr); true }, - _ => { // FIXME: // Also catch transmutations of variables which are known nulls. // To do this, MIR const propagation seems to be the better tool. // Whenever MIR const prop routines are more developed, this will // become available. As of this writing (25/03/19) it is not yet. + if is_integer_literal(casts_peeled, 0) { + lint_expr(cx, expr); + return true; + } false }, } } + +fn peel_casts<'tcx>(expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> { + match &expr.kind { + ExprKind::Cast(inner_expr, _) => peel_casts(inner_expr), + _ => expr, + } +} diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index 79f9d45d597ef..71a4b3fba1b59 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -315,7 +315,7 @@ impl<'tcx> LateLintPass<'tcx> for Types { fn check_fn( &mut self, cx: &LateContext<'_>, - _: FnKind<'_>, + fn_kind: FnKind<'_>, decl: &FnDecl<'_>, _: &Body<'_>, _: Span, @@ -340,6 +340,7 @@ impl<'tcx> LateLintPass<'tcx> for Types { CheckTyContext { is_in_trait_impl, is_exported, + in_body: matches!(fn_kind, FnKind::Closure), ..CheckTyContext::default() }, ); @@ -427,7 +428,7 @@ impl<'tcx> LateLintPass<'tcx> for Types { cx, ty, CheckTyContext { - is_local: true, + in_body: true, ..CheckTyContext::default() }, ); @@ -481,7 +482,7 @@ impl Types { } match hir_ty.kind { - TyKind::Path(ref qpath) if !context.is_local => { + TyKind::Path(ref qpath) if !context.in_body => { let hir_id = hir_ty.hir_id; let res = cx.qpath_res(qpath, hir_id); if let Some(def_id) = res.opt_def_id() { @@ -581,8 +582,8 @@ impl Types { #[derive(Clone, Copy, Default)] struct CheckTyContext { is_in_trait_impl: bool, - /// `true` for types on local variables. - is_local: bool, + /// `true` for types on local variables and in closure signatures. + in_body: bool, /// `true` for types that are part of the public API. is_exported: bool, is_nested_call: bool, diff --git a/clippy_lints/src/unit_types/let_unit_value.rs b/clippy_lints/src/unit_types/let_unit_value.rs index 704d7abd7e55a..e7915953d85b3 100644 --- a/clippy_lints/src/unit_types/let_unit_value.rs +++ b/clippy_lints/src/unit_types/let_unit_value.rs @@ -7,7 +7,7 @@ use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, ExprKind, HirId, HirIdSet, Local, MatchSource, Node, PatKind, QPath, TyKind}; use rustc_lint::{LateContext, LintContext}; -use rustc_middle::lint::in_external_macro; +use rustc_middle::lint::{in_external_macro, is_from_async_await}; use rustc_middle::ty; use super::LET_UNIT_VALUE; @@ -16,6 +16,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>) { if let Some(init) = local.init && !local.pat.span.from_expansion() && !in_external_macro(cx.sess(), local.span) + && !is_from_async_await(local.span) && cx.typeck_results().pat_ty(local.pat).is_unit() { if (local.ty.map_or(false, |ty| !matches!(ty.kind, TyKind::Infer)) diff --git a/clippy_lints/src/unnecessary_map_on_constructor.rs b/clippy_lints/src/unnecessary_map_on_constructor.rs new file mode 100644 index 0000000000000..5aa057580e9d9 --- /dev/null +++ b/clippy_lints/src/unnecessary_map_on_constructor.rs @@ -0,0 +1,93 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::get_type_diagnostic_name; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// Suggest removing the use of a may (or map_err) method when an Option or Result is being construted. + /// + /// ### Why is this bad? + /// It introduces unnecessary complexity. In this case the function can be used directly and + /// construct the Option or Result from the output. + /// + /// ### Example + /// ```rust + /// Some(4).map(i32::swap_bytes); + /// ``` + /// Use instead: + /// ```rust + /// Some(i32::swap_bytes(4)); + /// ``` + #[clippy::version = "1.73.0"] + pub UNNECESSARY_MAP_ON_CONSTRUCTOR, + complexity, + "using `map`/`map_err` on `Option` or `Result` constructors" +} +declare_lint_pass!(UnnecessaryMapOnConstructor => [UNNECESSARY_MAP_ON_CONSTRUCTOR]); + +impl<'tcx> LateLintPass<'tcx> for UnnecessaryMapOnConstructor { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) { + if expr.span.from_expansion() { + return; + } + if let hir::ExprKind::MethodCall(path, recv, args, ..) = expr.kind + && let Some(sym::Option | sym::Result) = get_type_diagnostic_name(cx, cx.typeck_results().expr_ty(recv)){ + let (constructor_path, constructor_item) = + if let hir::ExprKind::Call(constructor, constructor_args) = recv.kind + && let hir::ExprKind::Path(constructor_path) = constructor.kind + && let Some(arg) = constructor_args.get(0) + { + if constructor.span.from_expansion() || arg.span.from_expansion() { + return; + } + (constructor_path, arg) + } else { + return; + }; + let constructor_symbol = match constructor_path { + hir::QPath::Resolved(_, path) => { + if let Some(path_segment) = path.segments.last() { + path_segment.ident.name + } else { + return; + } + }, + hir::QPath::TypeRelative(_, path) => path.ident.name, + hir::QPath::LangItem(_, _, _) => return, + }; + match constructor_symbol { + sym::Some | sym::Ok if path.ident.name == rustc_span::sym::map => (), + sym::Err if path.ident.name == sym!(map_err) => (), + _ => return, + } + + if let Some(map_arg) = args.get(0) + && let hir::ExprKind::Path(fun) = map_arg.kind + { + if map_arg.span.from_expansion() { + return; + } + let mut applicability = Applicability::MachineApplicable; + let fun_snippet = snippet_with_applicability(cx, fun.span(), "_", &mut applicability); + let constructor_snippet = + snippet_with_applicability(cx, constructor_path.span(), "_", &mut applicability); + let constructor_arg_snippet = + snippet_with_applicability(cx, constructor_item.span, "_", &mut applicability); + span_lint_and_sugg( + cx, + UNNECESSARY_MAP_ON_CONSTRUCTOR, + expr.span, + &format!("unnecessary {} on constructor {constructor_snippet}(_)", path.ident.name), + "try", + format!("{constructor_snippet}({fun_snippet}({constructor_arg_snippet}))"), + applicability, + ); + } + } + } +} diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 5ac4f0aa46c10..f32e7edad6cb6 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -8,10 +8,14 @@ use rustc_errors::Applicability; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::{BindingAnnotation, Expr, ExprKind, HirId, MatchSource, Node, PatKind}; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::traits::Obligation; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty; +use rustc_middle::traits::ObligationCause; +use rustc_middle::ty::{self, EarlyBinder, GenericArg, GenericArgsRef, Ty, TypeVisitableExt}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, Span}; +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; declare_clippy_lint! { /// ### What it does @@ -61,22 +65,69 @@ impl MethodOrFunction { } } -/// Returns the span of the `IntoIterator` trait bound in the function pointed to by `fn_did` -fn into_iter_bound(cx: &LateContext<'_>, fn_did: DefId, into_iter_did: DefId, param_index: u32) -> Option { - cx.tcx - .predicates_of(fn_did) - .predicates - .iter() - .find_map(|&(ref pred, span)| { - if let ty::ClauseKind::Trait(tr) = pred.kind().skip_binder() - && tr.def_id() == into_iter_did - && tr.self_ty().is_param(param_index) - { - Some(span) - } else { - None +/// Returns the span of the `IntoIterator` trait bound in the function pointed to by `fn_did`, +/// iff all of the bounds also hold for the type of the `.into_iter()` receiver. +/// ```ignore +/// pub fn foo(i: I) +/// where I: IntoIterator + ExactSizeIterator +/// ^^^^^^^^^^^^^^^^^ this extra bound stops us from suggesting to remove `.into_iter()` ... +/// { +/// assert_eq!(i.len(), 3); +/// } +/// +/// pub fn bar() { +/// foo([1, 2, 3].into_iter()); +/// ^^^^^^^^^^^^ ... here, because `[i32; 3]` is not `ExactSizeIterator` +/// } +/// ``` +fn into_iter_bound<'tcx>( + cx: &LateContext<'tcx>, + fn_did: DefId, + into_iter_did: DefId, + into_iter_receiver: Ty<'tcx>, + param_index: u32, + node_args: GenericArgsRef<'tcx>, +) -> Option { + let param_env = cx.tcx.param_env(fn_did); + let mut into_iter_span = None; + + for (pred, span) in cx.tcx.explicit_predicates_of(fn_did).predicates { + if let ty::ClauseKind::Trait(tr) = pred.kind().skip_binder() { + if tr.self_ty().is_param(param_index) { + if tr.def_id() == into_iter_did { + into_iter_span = Some(*span); + } else { + let tr = cx.tcx.erase_regions(tr); + if tr.has_escaping_bound_vars() { + return None; + } + + // Substitute generics in the predicate and replace the IntoIterator type parameter with the + // `.into_iter()` receiver to see if the bound also holds for that type. + let args = cx.tcx.mk_args_from_iter(node_args.iter().enumerate().map(|(i, arg)| { + if i == param_index as usize { + GenericArg::from(into_iter_receiver) + } else { + arg + } + })); + + let predicate = EarlyBinder::bind(tr).instantiate(cx.tcx, args); + let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), param_env, predicate); + if !cx + .tcx + .infer_ctxt() + .build() + .predicate_must_hold_modulo_regions(&obligation) + { + return None; + } + } } - }) + } + } + + into_iter_span } /// Extracts the receiver of a `.into_iter()` method call. @@ -160,22 +211,41 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { // `fn_sig` does not ICE. (see #11065) && cx.tcx.opt_def_kind(did).is_some_and(DefKind::is_fn_like) => { - Some((did, args, MethodOrFunction::Function)) + Some(( + did, + args, + cx.typeck_results().node_args(recv.hir_id), + MethodOrFunction::Function + )) } ExprKind::MethodCall(.., args, _) => { cx.typeck_results().type_dependent_def_id(parent.hir_id) - .map(|did| (did, args, MethodOrFunction::Method)) + .map(|did| { + return ( + did, + args, + cx.typeck_results().node_args(parent.hir_id), + MethodOrFunction::Method + ); + }) } _ => None, }; - if let Some((parent_fn_did, args, kind)) = parent_fn + if let Some((parent_fn_did, args, node_args, kind)) = parent_fn && let Some(into_iter_did) = cx.tcx.get_diagnostic_item(sym::IntoIterator) && let sig = cx.tcx.fn_sig(parent_fn_did).skip_binder().skip_binder() && let Some(arg_pos) = args.iter().position(|x| x.hir_id == e.hir_id) && let Some(&into_iter_param) = sig.inputs().get(kind.param_pos(arg_pos)) && let ty::Param(param) = into_iter_param.kind() - && let Some(span) = into_iter_bound(cx, parent_fn_did, into_iter_did, param.index) + && let Some(span) = into_iter_bound( + cx, + parent_fn_did, + into_iter_did, + cx.typeck_results().expr_ty(into_iter_recv), + param.index, + node_args + ) && self.expn_depth == 0 { // Get the "innermost" `.into_iter()` call, e.g. given this expression: diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 2688947552253..75c3c7a958a21 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -542,11 +542,11 @@ define_Conf! { /// Lint: UNDOCUMENTED_UNSAFE_BLOCKS. /// /// Whether to accept a safety comment to be placed above the statement containing the `unsafe` block - (accept_comment_above_statement: bool = false), + (accept_comment_above_statement: bool = true), /// Lint: UNDOCUMENTED_UNSAFE_BLOCKS. /// /// Whether to accept a safety comment to be placed above the attributes for the `unsafe` block - (accept_comment_above_attributes: bool = false), + (accept_comment_above_attributes: bool = true), /// Lint: UNNECESSARY_RAW_STRING_HASHES. /// /// Whether to allow `r#""#` when `r""` can be used @@ -561,6 +561,11 @@ define_Conf! { /// Which crates to allow absolute paths from (absolute_paths_allowed_crates: rustc_data_structures::fx::FxHashSet = rustc_data_structures::fx::FxHashSet::default()), + /// Lint: PATH_ENDS_WITH_EXT. + /// + /// Additional dotfiles (files or directories starting with a dot) to allow + (allowed_dotfiles: rustc_data_structures::fx::FxHashSet = + rustc_data_structures::fx::FxHashSet::default()), /// Lint: EXPLICIT_ITER_LOOP /// /// Whether to recommend using implicit into iter for reborrowed values. diff --git a/clippy_lints/src/utils/format_args_collector.rs b/clippy_lints/src/utils/format_args_collector.rs index 6d3493523e6fc..94a9a7c241bb7 100644 --- a/clippy_lints/src/utils/format_args_collector.rs +++ b/clippy_lints/src/utils/format_args_collector.rs @@ -1,12 +1,15 @@ -use clippy_utils::macros::collect_ast_format_args; +use clippy_utils::macros::AST_FORMAT_ARGS; use clippy_utils::source::snippet_opt; use itertools::Itertools; -use rustc_ast::{Expr, ExprKind, FormatArgs}; +use rustc_ast::{Crate, Expr, ExprKind, FormatArgs}; +use rustc_data_structures::fx::FxHashMap; use rustc_lexer::{tokenize, TokenKind}; use rustc_lint::{EarlyContext, EarlyLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::hygiene; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::{hygiene, Span}; use std::iter::once; +use std::mem; +use std::rc::Rc; declare_clippy_lint! { /// ### What it does @@ -17,7 +20,12 @@ declare_clippy_lint! { "collects `format_args` AST nodes for use in later lints" } -declare_lint_pass!(FormatArgsCollector => [FORMAT_ARGS_COLLECTOR]); +#[derive(Default)] +pub struct FormatArgsCollector { + format_args: FxHashMap>, +} + +impl_lint_pass!(FormatArgsCollector => [FORMAT_ARGS_COLLECTOR]); impl EarlyLintPass for FormatArgsCollector { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { @@ -26,9 +34,17 @@ impl EarlyLintPass for FormatArgsCollector { return; } - collect_ast_format_args(expr.span, args); + self.format_args + .insert(expr.span.with_parent(None), Rc::new((**args).clone())); } } + + fn check_crate_post(&mut self, _: &EarlyContext<'_>, _: &Crate) { + AST_FORMAT_ARGS.with(|ast_format_args| { + let result = ast_format_args.set(mem::take(&mut self.format_args)); + debug_assert!(result.is_ok()); + }); + } } /// Detects if the format string or an argument has its span set by a proc macro to something inside diff --git a/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs b/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs index da8654d9388d3..82f9d4e41e882 100644 --- a/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs +++ b/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs @@ -10,7 +10,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::mir::interpret::ConstValue; +use rustc_middle::mir::ConstValue; use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::Symbol; diff --git a/clippy_lints/src/utils/internal_lints/invalid_paths.rs b/clippy_lints/src/utils/internal_lints/invalid_paths.rs index 4ed985f54d0e2..250772238853b 100644 --- a/clippy_lints/src/utils/internal_lints/invalid_paths.rs +++ b/clippy_lints/src/utils/internal_lints/invalid_paths.rs @@ -5,10 +5,9 @@ use if_chain::if_chain; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::Item; -use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::fast_reject::SimplifiedType; -use rustc_middle::ty::{self, FloatTy}; +use rustc_middle::ty::FloatTy; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::Symbol; @@ -34,25 +33,20 @@ impl<'tcx> LateLintPass<'tcx> for InvalidPaths { let mod_name = &cx.tcx.item_name(local_def_id.to_def_id()); if_chain! { if mod_name.as_str() == "paths"; - if let hir::ItemKind::Const(ty, _, body_id) = item.kind; - let ty = hir_ty_to_ty(cx.tcx, ty); - if let ty::Array(el_ty, _) = &ty.kind(); - if let ty::Ref(_, el_ty, _) = &el_ty.kind(); - if el_ty.is_str(); + if let hir::ItemKind::Const(.., body_id) = item.kind; let body = cx.tcx.hir().body(body_id); let typeck_results = cx.tcx.typeck_body(body_id); if let Some(Constant::Vec(path)) = constant_simple(cx, typeck_results, body.value); - let path: Vec<&str> = path + if let Some(path) = path .iter() .map(|x| { if let Constant::Str(s) = x { - s.as_str() + Some(s.as_str()) } else { - // We checked the type of the constant above - unreachable!() + None } }) - .collect(); + .collect::>>(); if !check_path(cx, &path[..]); then { span_lint(cx, INVALID_PATHS, item.span, "invalid path"); diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index f49c3fadb0780..c38a3e81b0f72 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -31,7 +31,7 @@ use serde::{Serialize, Serializer}; use std::collections::{BTreeSet, BinaryHeap}; use std::fmt; use std::fmt::Write as _; -use std::fs::{self, OpenOptions}; +use std::fs::{self, File}; use std::io::prelude::*; use std::path::{Path, PathBuf}; use std::process::Command; @@ -229,25 +229,10 @@ impl Drop for MetadataCollector { collect_renames(&mut lints); // Outputting json - if Path::new(JSON_OUTPUT_FILE).exists() { - fs::remove_file(JSON_OUTPUT_FILE).unwrap(); - } - let mut file = OpenOptions::new() - .write(true) - .create(true) - .open(JSON_OUTPUT_FILE) - .unwrap(); - writeln!(file, "{}", serde_json::to_string_pretty(&lints).unwrap()).unwrap(); + fs::write(JSON_OUTPUT_FILE, serde_json::to_string_pretty(&lints).unwrap()).unwrap(); // Outputting markdown - if Path::new(MARKDOWN_OUTPUT_FILE).exists() { - fs::remove_file(MARKDOWN_OUTPUT_FILE).unwrap(); - } - let mut file = OpenOptions::new() - .write(true) - .create(true) - .open(MARKDOWN_OUTPUT_FILE) - .unwrap(); + let mut file = File::create(MARKDOWN_OUTPUT_FILE).unwrap(); writeln!( file, "") { - // I know this is kinda wasteful, we just don't have regex on `clippy_lints` so... this is the best - // we can do AFAIK. - changelog = changelog[..position].to_string(); - } + let changelog = std::fs::read_to_string(CHANGELOG_PATH).unwrap(); + let mut changelog_file = File::create(CHANGELOG_PATH).unwrap(); + let position = changelog + .find("") + .unwrap(); writeln!( changelog_file, - "{changelog}\n{}\n", + "{}\n{}\n", + &changelog[..position], self.configs_to_markdown(ClippyConfiguration::to_markdown_link) ) .unwrap(); diff --git a/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs b/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs index bf835f89cfc7f..86b77a77f1730 100644 --- a/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs +++ b/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs @@ -5,9 +5,8 @@ use clippy_utils::{match_def_path, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty::{self, GenericArgKind}; +use rustc_middle::ty::{self, EarlyBinder, GenericArgKind}; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { @@ -25,16 +24,14 @@ impl LateLintPass<'_> for MsrvAttrImpl { fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { if_chain! { if let hir::ItemKind::Impl(hir::Impl { - of_trait: Some(lint_pass_trait_ref), - self_ty, + of_trait: Some(_), items, .. }) = &item.kind; - if let Some(lint_pass_trait_def_id) = lint_pass_trait_ref.trait_def_id(); - let is_late_pass = match_def_path(cx, lint_pass_trait_def_id, &paths::LATE_LINT_PASS); - if is_late_pass || match_def_path(cx, lint_pass_trait_def_id, &paths::EARLY_LINT_PASS); - let self_ty = hir_ty_to_ty(cx.tcx, self_ty); - if let ty::Adt(self_ty_def, _) = self_ty.kind(); + if let Some(trait_ref) = cx.tcx.impl_trait_ref(item.owner_id).map(EarlyBinder::instantiate_identity); + let is_late_pass = match_def_path(cx, trait_ref.def_id, &paths::LATE_LINT_PASS); + if is_late_pass || match_def_path(cx, trait_ref.def_id, &paths::EARLY_LINT_PASS); + if let ty::Adt(self_ty_def, _) = trait_ref.self_ty().kind(); if self_ty_def.is_struct(); if self_ty_def.all_fields().any(|f| { cx.tcx diff --git a/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs b/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs index 4a5b6fa5c18d6..a3acb8f1762d8 100644 --- a/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs +++ b/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs @@ -10,7 +10,8 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::{Expr, ExprKind, Local, Mutability, Node}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::mir::interpret::{Allocation, ConstValue, GlobalAlloc}; +use rustc_middle::mir::interpret::{Allocation, GlobalAlloc}; +use rustc_middle::mir::ConstValue; use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::Symbol; @@ -232,7 +233,8 @@ fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option match cx.tcx.const_eval_poly(def_id).ok()? { - ConstValue::Indirect { alloc, offset } if offset.bytes() == 0 => { + ConstValue::Indirect { alloc_id, offset } if offset.bytes() == 0 => { + let alloc = cx.tcx.global_alloc(alloc_id).unwrap_memory(); read_mir_alloc_def_path(cx, alloc.inner(), cx.tcx.type_of(def_id).instantiate_identity()) }, _ => None, diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index a9957b18a53b3..da083fb14aae1 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -304,7 +304,7 @@ impl<'tcx> LateLintPass<'tcx> for Write { _ => return, } - find_format_args(cx, expr, macro_call.expn, |format_args| { + if let Some(format_args) = find_format_args(cx, expr, macro_call.expn) { // ignore `writeln!(w)` and `write!(v, some_macro!())` if format_args.span.from_expansion() { return; @@ -312,15 +312,15 @@ impl<'tcx> LateLintPass<'tcx> for Write { match diag_name { sym::print_macro | sym::eprint_macro | sym::write_macro => { - check_newline(cx, format_args, ¯o_call, name); + check_newline(cx, &format_args, ¯o_call, name); }, sym::println_macro | sym::eprintln_macro | sym::writeln_macro => { - check_empty_string(cx, format_args, ¯o_call, name); + check_empty_string(cx, &format_args, ¯o_call, name); }, _ => {}, } - check_literal(cx, format_args, name); + check_literal(cx, &format_args, name); if !self.in_debug_impl { for piece in &format_args.template { @@ -334,7 +334,7 @@ impl<'tcx> LateLintPass<'tcx> for Write { } } } - }); + } } } diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 6b1a738aaa94c..d596eed4b7c4d 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -671,10 +671,11 @@ pub fn miri_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> ty::RawPtr(_) => Some(Constant::RawPtr(int.assert_bits(int.size()))), _ => None, }, - mir::Const::Val(cv, _) if matches!(result.ty().kind(), ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), ty::Str)) => { + mir::Const::Val(cv, _) if matches!(result.ty().kind(), ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), ty::Str)) => + { let data = cv.try_get_slice_bytes_for_diagnostics(lcx.tcx)?; String::from_utf8(data.to_owned()).ok().map(Constant::Str) - } + }, mir::Const::Val(ConstValue::Indirect { alloc_id, offset: _ }, _) => { let alloc = lcx.tcx.global_alloc(alloc_id).unwrap_memory(); match result.ty().kind() { diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index be1c46319c2b6..0f7bc04ccba15 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1785,6 +1785,33 @@ pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tc None } +/// Returns `true` if the lint is `#[allow]`ed or `#[expect]`ed at any of the `ids`, fulfilling all +/// of the expectations in `ids` +/// +/// This should only be used when the lint would otherwise be emitted, for a way to check if a lint +/// is allowed early to skip work see [`is_lint_allowed`] +/// +/// To emit at a lint at a different context than the one current see +/// [`span_lint_hir`](diagnostics::span_lint_hir) or +/// [`span_lint_hir_and_then`](diagnostics::span_lint_hir_and_then) +pub fn fulfill_or_allowed(cx: &LateContext<'_>, lint: &'static Lint, ids: impl IntoIterator) -> bool { + let mut suppress_lint = false; + + for id in ids { + let (level, _) = cx.tcx.lint_level_at_node(lint, id); + if let Some(expectation) = level.get_expectation_id() { + cx.fulfill_expectation(expectation); + } + + match level { + Level::Allow | Level::Expect(_) => suppress_lint = true, + Level::Warn | Level::ForceWarn(_) | Level::Deny | Level::Forbid => {}, + } + } + + suppress_lint +} + /// Returns `true` if the lint is allowed in the current context. This is useful for /// skipping long running code when it's unnecessary /// @@ -1958,7 +1985,7 @@ pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, /// Checks if the given function kind is an async function. pub fn is_async_fn(kind: FnKind<'_>) -> bool { match kind { - FnKind::ItemFn(_, _, header) => header.asyncness .is_async(), + FnKind::ItemFn(_, _, header) => header.asyncness.is_async(), FnKind::Method(_, sig) => sig.header.asyncness.is_async(), FnKind::Closure => false, } diff --git a/clippy_utils/src/macros.rs b/clippy_utils/src/macros.rs index 173f9841d4469..82508bcdb857a 100644 --- a/clippy_utils/src/macros.rs +++ b/clippy_utils/src/macros.rs @@ -10,8 +10,9 @@ use rustc_lint::LateContext; use rustc_span::def_id::DefId; use rustc_span::hygiene::{self, MacroKind, SyntaxContext}; use rustc_span::{sym, BytePos, ExpnData, ExpnId, ExpnKind, Span, SpanData, Symbol}; -use std::cell::RefCell; +use std::cell::OnceCell; use std::ops::ControlFlow; +use std::rc::Rc; use std::sync::atomic::{AtomicBool, Ordering}; const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[ @@ -374,28 +375,21 @@ thread_local! { /// A thread local is used because [`FormatArgs`] is `!Send` and `!Sync`, we are making an /// assumption that the early pass that populates the map and the later late passes will all be /// running on the same thread. - static AST_FORMAT_ARGS: RefCell> = { + #[doc(hidden)] + pub static AST_FORMAT_ARGS: OnceCell>> = { static CALLED: AtomicBool = AtomicBool::new(false); debug_assert!( !CALLED.swap(true, Ordering::SeqCst), "incorrect assumption: `AST_FORMAT_ARGS` should only be accessed by a single thread", ); - RefCell::default() + OnceCell::new() }; } -/// Record [`rustc_ast::FormatArgs`] for use in late lint passes, this should only be called by -/// `FormatArgsCollector` -pub fn collect_ast_format_args(span: Span, format_args: &FormatArgs) { - AST_FORMAT_ARGS.with(|ast_format_args| { - ast_format_args.borrow_mut().insert(span, format_args.clone()); - }); -} - -/// Calls `callback` with an AST [`FormatArgs`] node if a `format_args` expansion is found as a -/// descendant of `expn_id` -pub fn find_format_args(cx: &LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId, callback: impl FnOnce(&FormatArgs)) { +/// Returns an AST [`FormatArgs`] node if a `format_args` expansion is found as a descendant of +/// `expn_id` +pub fn find_format_args(cx: &LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId) -> Option> { let format_args_expr = for_each_expr(start, |expr| { let ctxt = expr.span.ctxt(); if ctxt.outer_expn().is_descendant_of(expn_id) { @@ -410,13 +404,14 @@ pub fn find_format_args(cx: &LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId, } else { ControlFlow::Continue(Descend::No) } - }); + })?; - if let Some(expr) = format_args_expr { - AST_FORMAT_ARGS.with(|ast_format_args| { - ast_format_args.borrow().get(&expr.span).map(callback); - }); - } + AST_FORMAT_ARGS.with(|ast_format_args| { + ast_format_args + .get()? + .get(&format_args_expr.span.with_parent(None)) + .map(Rc::clone) + }) } /// Attempt to find the [`rustc_hir::Expr`] that corresponds to the [`FormatArgument`]'s value, if diff --git a/clippy_utils/src/mir/mod.rs b/clippy_utils/src/mir/mod.rs index 131f3c0aa3946..f04467dc19d37 100644 --- a/clippy_utils/src/mir/mod.rs +++ b/clippy_utils/src/mir/mod.rs @@ -1,7 +1,8 @@ use rustc_hir::{Expr, HirId}; +use rustc_index::bit_set::BitSet; use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::{ - traversal, Body, InlineAsmOperand, Local, Location, Place, StatementKind, TerminatorKind, START_BLOCK, + traversal, BasicBlock, Body, InlineAsmOperand, Local, Location, Place, StatementKind, TerminatorKind, START_BLOCK, }; use rustc_middle::ty::TyCtxt; @@ -79,8 +80,32 @@ impl<'a, 'tcx> Visitor<'tcx> for V<'a> { } } +/// Checks if the block is part of a cycle +pub fn block_in_cycle(body: &Body<'_>, block: BasicBlock) -> bool { + let mut seen = BitSet::new_empty(body.basic_blocks.len()); + let mut to_visit = Vec::with_capacity(body.basic_blocks.len() / 2); + + seen.insert(block); + let mut next = block; + loop { + for succ in body.basic_blocks[next].terminator().successors() { + if seen.insert(succ) { + to_visit.push(succ); + } else if succ == block { + return true; + } + } + + if let Some(x) = to_visit.pop() { + next = x; + } else { + return false; + } + } +} + /// Convenience wrapper around `visit_local_usage`. -pub fn used_exactly_once(mir: &rustc_middle::mir::Body<'_>, local: rustc_middle::mir::Local) -> Option { +pub fn used_exactly_once(mir: &Body<'_>, local: rustc_middle::mir::Local) -> Option { visit_local_usage( &[local], mir, @@ -91,11 +116,14 @@ pub fn used_exactly_once(mir: &rustc_middle::mir::Body<'_>, local: rustc_middle: ) .map(|mut vec| { let LocalUsage { local_use_locs, .. } = vec.remove(0); - local_use_locs + let mut locations = local_use_locs .into_iter() - .filter(|location| !is_local_assignment(mir, local, *location)) - .count() - == 1 + .filter(|&location| !is_local_assignment(mir, local, location)); + if let Some(location) = locations.next() { + locations.next().is_none() && !block_in_cycle(mir, location.block) + } else { + false + } }) } diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 9e25d97f5a6bc..604dc76912e65 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -13,7 +13,8 @@ use rustc_hir::{Expr, FnDecl, LangItem, TyKind, Unsafety}; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; -use rustc_middle::mir::{ConstValue, interpret::Scalar}; +use rustc_middle::mir::interpret::Scalar; +use rustc_middle::mir::ConstValue; use rustc_middle::traits::EvaluationResult; use rustc_middle::ty::layout::ValidityRequirement; use rustc_middle::ty::{ diff --git a/clippy_utils/src/ty/type_certainty/mod.rs b/clippy_utils/src/ty/type_certainty/mod.rs index 4b06b12fb94d0..d05d9e7640f0b 100644 --- a/clippy_utils/src/ty/type_certainty/mod.rs +++ b/clippy_utils/src/ty/type_certainty/mod.rs @@ -207,9 +207,8 @@ fn path_segment_certainty( // Checking `res_generics_def_id(..)` before calling `generics_of` avoids an ICE. if cx.tcx.res_generics_def_id(path_segment.res).is_some() { let generics = cx.tcx.generics_of(def_id); - let count = generics.params.len() - generics.host_effect_index.is_some() as usize; - let lhs = if (parent_certainty.is_certain() || generics.parent_count == 0) && count == 0 - { + let count = generics.params.len() - usize::from(generics.host_effect_index.is_some()); + let lhs = if (parent_certainty.is_certain() || generics.parent_count == 0) && count == 0 { Certainty::Certain(None) } else { Certainty::Uncertain @@ -300,10 +299,11 @@ fn type_is_inferrable_from_arguments(cx: &LateContext<'_>, expr: &Expr<'_>) -> b // Check that all type parameters appear in the functions input types. (0..(generics.parent_count + generics.params.len()) as u32).all(|index| { - Some(index as usize) == generics.host_effect_index || fn_sig - .inputs() - .iter() - .any(|input_ty| contains_param(*input_ty.skip_binder(), index)) + Some(index as usize) == generics.host_effect_index + || fn_sig + .inputs() + .iter() + .any(|input_ty| contains_param(*input_ty.skip_binder(), index)) }) } diff --git a/rust-toolchain b/rust-toolchain index 9f5116eb73bb2..5ce22b65f0074 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2023-09-07" +channel = "nightly-2023-09-25" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 9fcc269dbf8c2..f340cf5938a7d 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -18,7 +18,6 @@ use test_utils::IS_RUSTC_TEST_SUITE; // in the depinfo file (otherwise cargo thinks they are unused) extern crate clippy_lints; extern crate clippy_utils; -extern crate derive_new; extern crate futures; extern crate if_chain; extern crate itertools; @@ -33,7 +32,6 @@ mod test_utils; static TEST_DEPENDENCIES: &[&str] = &[ "clippy_lints", "clippy_utils", - "derive_new", "futures", "if_chain", "itertools", diff --git a/tests/ui-toml/decimal_literal_representation/clippy.toml b/tests/ui-toml/decimal_literal_representation/clippy.toml new file mode 100644 index 0000000000000..74fc5d249d005 --- /dev/null +++ b/tests/ui-toml/decimal_literal_representation/clippy.toml @@ -0,0 +1 @@ +literal-representation-threshold = 0xFFFFFF diff --git a/tests/ui-toml/decimal_literal_representation/decimal_literal_representation.fixed b/tests/ui-toml/decimal_literal_representation/decimal_literal_representation.fixed new file mode 100644 index 0000000000000..750f3be84c040 --- /dev/null +++ b/tests/ui-toml/decimal_literal_representation/decimal_literal_representation.fixed @@ -0,0 +1,6 @@ +#![warn(clippy::decimal_literal_representation)] +fn main() { + let _ = 8388608; + let _ = 0x00FF_FFFF; + //~^ ERROR: integer literal has a better hexadecimal representation +} diff --git a/tests/ui-toml/decimal_literal_representation/decimal_literal_representation.rs b/tests/ui-toml/decimal_literal_representation/decimal_literal_representation.rs new file mode 100644 index 0000000000000..26b3354d15909 --- /dev/null +++ b/tests/ui-toml/decimal_literal_representation/decimal_literal_representation.rs @@ -0,0 +1,6 @@ +#![warn(clippy::decimal_literal_representation)] +fn main() { + let _ = 8388608; + let _ = 16777215; + //~^ ERROR: integer literal has a better hexadecimal representation +} diff --git a/tests/ui-toml/decimal_literal_representation/decimal_literal_representation.stderr b/tests/ui-toml/decimal_literal_representation/decimal_literal_representation.stderr new file mode 100644 index 0000000000000..6f817a3fdde42 --- /dev/null +++ b/tests/ui-toml/decimal_literal_representation/decimal_literal_representation.stderr @@ -0,0 +1,11 @@ +error: integer literal has a better hexadecimal representation + --> $DIR/decimal_literal_representation.rs:4:13 + | +LL | let _ = 16777215; + | ^^^^^^^^ help: consider: `0x00FF_FFFF` + | + = note: `-D clippy::decimal-literal-representation` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::decimal_literal_representation)]` + +error: aborting due to previous error + diff --git a/tests/ui-toml/disallowed_script_idents/clippy.toml b/tests/ui-toml/disallowed_script_idents/clippy.toml new file mode 100644 index 0000000000000..26cb2d77bfd9f --- /dev/null +++ b/tests/ui-toml/disallowed_script_idents/clippy.toml @@ -0,0 +1 @@ +allowed-scripts = ["Cyrillic"] diff --git a/tests/ui-toml/disallowed_script_idents/disallowed_script_idents.rs b/tests/ui-toml/disallowed_script_idents/disallowed_script_idents.rs new file mode 100644 index 0000000000000..9df1ec6fab0d7 --- /dev/null +++ b/tests/ui-toml/disallowed_script_idents/disallowed_script_idents.rs @@ -0,0 +1,6 @@ +#![warn(clippy::disallowed_script_idents)] +fn main() { + let счётчик = 10; + let カウンタ = 10; + //~^ ERROR: identifier `カウンタ` has a Unicode script that is not allowed by configuration +} diff --git a/tests/ui-toml/disallowed_script_idents/disallowed_script_idents.stderr b/tests/ui-toml/disallowed_script_idents/disallowed_script_idents.stderr new file mode 100644 index 0000000000000..31bb5ee3514a5 --- /dev/null +++ b/tests/ui-toml/disallowed_script_idents/disallowed_script_idents.stderr @@ -0,0 +1,11 @@ +error: identifier `カウンタ` has a Unicode script that is not allowed by configuration: Katakana + --> $DIR/disallowed_script_idents.rs:4:9 + | +LL | let カウンタ = 10; + | ^^^^^^^^ + | + = note: `-D clippy::disallowed-script-idents` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::disallowed_script_idents)]` + +error: aborting due to previous error + diff --git a/tests/ui-toml/enum_variant_names/clippy.toml b/tests/ui-toml/enum_variant_names/clippy.toml new file mode 100644 index 0000000000000..0ad7a97994849 --- /dev/null +++ b/tests/ui-toml/enum_variant_names/clippy.toml @@ -0,0 +1 @@ +enum-variant-name-threshold = 5 diff --git a/tests/ui-toml/enum_variant_names/enum_variant_names.rs b/tests/ui-toml/enum_variant_names/enum_variant_names.rs new file mode 100644 index 0000000000000..8f4e178ccfe17 --- /dev/null +++ b/tests/ui-toml/enum_variant_names/enum_variant_names.rs @@ -0,0 +1,16 @@ +enum Foo { + AFoo, + BFoo, + CFoo, + DFoo, +} +enum Foo2 { + //~^ ERROR: all variants have the same postfix + AFoo, + BFoo, + CFoo, + DFoo, + EFoo, +} + +fn main() {} diff --git a/tests/ui-toml/enum_variant_names/enum_variant_names.stderr b/tests/ui-toml/enum_variant_names/enum_variant_names.stderr new file mode 100644 index 0000000000000..11039b1db4877 --- /dev/null +++ b/tests/ui-toml/enum_variant_names/enum_variant_names.stderr @@ -0,0 +1,18 @@ +error: all variants have the same postfix: `Foo` + --> $DIR/enum_variant_names.rs:7:1 + | +LL | / enum Foo2 { +LL | | +LL | | AFoo, +LL | | BFoo, +... | +LL | | EFoo, +LL | | } + | |_^ + | + = help: remove the postfixes and use full paths to the variants instead of glob imports + = note: `-D clippy::enum-variant-names` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::enum_variant_names)]` + +error: aborting due to previous error + diff --git a/tests/ui-toml/enum_variant_size/clippy.toml b/tests/ui-toml/enum_variant_size/clippy.toml new file mode 100644 index 0000000000000..64a8017fe02e4 --- /dev/null +++ b/tests/ui-toml/enum_variant_size/clippy.toml @@ -0,0 +1 @@ +enum-variant-size-threshold = 500 diff --git a/tests/ui-toml/enum_variant_size/enum_variant_size.fixed b/tests/ui-toml/enum_variant_size/enum_variant_size.fixed new file mode 100644 index 0000000000000..9ae760ae41aea --- /dev/null +++ b/tests/ui-toml/enum_variant_size/enum_variant_size.fixed @@ -0,0 +1,11 @@ +enum Fine { + A(()), + B([u8; 500]), +} +enum Bad { + //~^ ERROR: large size difference between variants + A(()), + B(Box<[u8; 501]>), +} + +fn main() {} diff --git a/tests/ui-toml/enum_variant_size/enum_variant_size.rs b/tests/ui-toml/enum_variant_size/enum_variant_size.rs new file mode 100644 index 0000000000000..cf7f432bf0b14 --- /dev/null +++ b/tests/ui-toml/enum_variant_size/enum_variant_size.rs @@ -0,0 +1,11 @@ +enum Fine { + A(()), + B([u8; 500]), +} +enum Bad { + //~^ ERROR: large size difference between variants + A(()), + B([u8; 501]), +} + +fn main() {} diff --git a/tests/ui-toml/enum_variant_size/enum_variant_size.stderr b/tests/ui-toml/enum_variant_size/enum_variant_size.stderr new file mode 100644 index 0000000000000..4d9bc9d48e45a --- /dev/null +++ b/tests/ui-toml/enum_variant_size/enum_variant_size.stderr @@ -0,0 +1,21 @@ +error: large size difference between variants + --> $DIR/enum_variant_size.rs:5:1 + | +LL | / enum Bad { +LL | | +LL | | A(()), + | | ----- the second-largest variant contains at least 0 bytes +LL | | B([u8; 501]), + | | ------------ the largest variant contains at least 501 bytes +LL | | } + | |_^ the entire enum is at least 502 bytes + | + = note: `-D clippy::large-enum-variant` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::large_enum_variant)]` +help: consider boxing the large fields to reduce the total size of the enum + | +LL | B(Box<[u8; 501]>), + | ~~~~~~~~~~~~~~ + +error: aborting due to previous error + diff --git a/tests/ui-toml/enum_variants_threshold0/clippy.toml b/tests/ui-toml/enum_variants_threshold0/clippy.toml new file mode 100644 index 0000000000000..f85aade6ae87d --- /dev/null +++ b/tests/ui-toml/enum_variants_threshold0/clippy.toml @@ -0,0 +1 @@ +enum-variant-name-threshold = 0 diff --git a/tests/ui-toml/enum_variants_threshold0/enum_variants_name_threshold.rs b/tests/ui-toml/enum_variants_threshold0/enum_variants_name_threshold.rs new file mode 100644 index 0000000000000..6918d7528c160 --- /dev/null +++ b/tests/ui-toml/enum_variants_threshold0/enum_variants_name_threshold.rs @@ -0,0 +1,3 @@ +enum Actions {} + +fn main() {} diff --git a/tests/ui-toml/explicit_iter_loop/clippy.toml b/tests/ui-toml/explicit_iter_loop/clippy.toml new file mode 100644 index 0000000000000..15d175ef14785 --- /dev/null +++ b/tests/ui-toml/explicit_iter_loop/clippy.toml @@ -0,0 +1 @@ +enforce-iter-loop-reborrow = true diff --git a/tests/ui-toml/explicit_iter_loop/explicit_iter_loop.fixed b/tests/ui-toml/explicit_iter_loop/explicit_iter_loop.fixed new file mode 100644 index 0000000000000..468da22a926fe --- /dev/null +++ b/tests/ui-toml/explicit_iter_loop/explicit_iter_loop.fixed @@ -0,0 +1,10 @@ +#![warn(clippy::explicit_iter_loop)] + +fn main() { + let mut vec = vec![1, 2, 3]; + let rmvec = &mut vec; + for _ in &*rmvec {} + //~^ ERROR: it is more concise to loop over references to containers + for _ in &mut *rmvec {} + //~^ ERROR: it is more concise to loop over references to containers +} diff --git a/tests/ui-toml/explicit_iter_loop/explicit_iter_loop.rs b/tests/ui-toml/explicit_iter_loop/explicit_iter_loop.rs new file mode 100644 index 0000000000000..a934648608c21 --- /dev/null +++ b/tests/ui-toml/explicit_iter_loop/explicit_iter_loop.rs @@ -0,0 +1,10 @@ +#![warn(clippy::explicit_iter_loop)] + +fn main() { + let mut vec = vec![1, 2, 3]; + let rmvec = &mut vec; + for _ in rmvec.iter() {} + //~^ ERROR: it is more concise to loop over references to containers + for _ in rmvec.iter_mut() {} + //~^ ERROR: it is more concise to loop over references to containers +} diff --git a/tests/ui-toml/explicit_iter_loop/explicit_iter_loop.stderr b/tests/ui-toml/explicit_iter_loop/explicit_iter_loop.stderr new file mode 100644 index 0000000000000..587d4f9b3f053 --- /dev/null +++ b/tests/ui-toml/explicit_iter_loop/explicit_iter_loop.stderr @@ -0,0 +1,17 @@ +error: it is more concise to loop over references to containers instead of using explicit iteration methods + --> $DIR/explicit_iter_loop.rs:6:14 + | +LL | for _ in rmvec.iter() {} + | ^^^^^^^^^^^^ help: to write this more concisely, try: `&*rmvec` + | + = note: `-D clippy::explicit-iter-loop` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::explicit_iter_loop)]` + +error: it is more concise to loop over references to containers instead of using explicit iteration methods + --> $DIR/explicit_iter_loop.rs:8:14 + | +LL | for _ in rmvec.iter_mut() {} + | ^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `&mut *rmvec` + +error: aborting due to 2 previous errors + diff --git a/tests/ui-toml/large_stack_frames/clippy.toml b/tests/ui-toml/large_stack_frames/clippy.toml new file mode 100644 index 0000000000000..584335dc28fb2 --- /dev/null +++ b/tests/ui-toml/large_stack_frames/clippy.toml @@ -0,0 +1 @@ +stack-size-threshold = 1000 diff --git a/tests/ui-toml/large_stack_frames/large_stack_frames.rs b/tests/ui-toml/large_stack_frames/large_stack_frames.rs new file mode 100644 index 0000000000000..39798ffea4942 --- /dev/null +++ b/tests/ui-toml/large_stack_frames/large_stack_frames.rs @@ -0,0 +1,17 @@ +#![warn(clippy::large_stack_frames)] + +// We use this helper function instead of writing [0; 4294967297] directly to represent a +// case that large_stack_arrays can't catch +fn create_array() -> [u8; N] { + [0; N] +} + +fn f() { + let _x = create_array::<1000>(); +} +fn f2() { + //~^ ERROR: this function allocates a large amount of stack space + let _x = create_array::<1001>(); +} + +fn main() {} diff --git a/tests/ui-toml/large_stack_frames/large_stack_frames.stderr b/tests/ui-toml/large_stack_frames/large_stack_frames.stderr new file mode 100644 index 0000000000000..67ee57ab672d4 --- /dev/null +++ b/tests/ui-toml/large_stack_frames/large_stack_frames.stderr @@ -0,0 +1,15 @@ +error: this function allocates a large amount of stack space + --> $DIR/large_stack_frames.rs:12:1 + | +LL | / fn f2() { +LL | | +LL | | let _x = create_array::<1001>(); +LL | | } + | |_^ + | + = note: allocating large amounts of stack space can overflow the stack + = note: `-D clippy::large-stack-frames` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::large_stack_frames)]` + +error: aborting due to previous error + diff --git a/tests/ui-toml/large_types_passed_by_value/clippy.toml b/tests/ui-toml/large_types_passed_by_value/clippy.toml new file mode 100644 index 0000000000000..45bcbce1e3c53 --- /dev/null +++ b/tests/ui-toml/large_types_passed_by_value/clippy.toml @@ -0,0 +1 @@ +pass-by-value-size-limit = 512 diff --git a/tests/ui-toml/large_types_passed_by_value/large_types_passed_by_value.fixed b/tests/ui-toml/large_types_passed_by_value/large_types_passed_by_value.fixed new file mode 100644 index 0000000000000..3c87c79cf2fcc --- /dev/null +++ b/tests/ui-toml/large_types_passed_by_value/large_types_passed_by_value.fixed @@ -0,0 +1,7 @@ +#![warn(clippy::large_types_passed_by_value)] + +fn f(_v: [u8; 512]) {} +fn f2(_v: &[u8; 513]) {} +//~^ ERROR: this argument (513 byte) is passed by value + +fn main() {} diff --git a/tests/ui-toml/large_types_passed_by_value/large_types_passed_by_value.rs b/tests/ui-toml/large_types_passed_by_value/large_types_passed_by_value.rs new file mode 100644 index 0000000000000..0572373a61188 --- /dev/null +++ b/tests/ui-toml/large_types_passed_by_value/large_types_passed_by_value.rs @@ -0,0 +1,7 @@ +#![warn(clippy::large_types_passed_by_value)] + +fn f(_v: [u8; 512]) {} +fn f2(_v: [u8; 513]) {} +//~^ ERROR: this argument (513 byte) is passed by value + +fn main() {} diff --git a/tests/ui-toml/large_types_passed_by_value/large_types_passed_by_value.stderr b/tests/ui-toml/large_types_passed_by_value/large_types_passed_by_value.stderr new file mode 100644 index 0000000000000..6678a2b47214f --- /dev/null +++ b/tests/ui-toml/large_types_passed_by_value/large_types_passed_by_value.stderr @@ -0,0 +1,11 @@ +error: this argument (513 byte) is passed by value, but might be more efficient if passed by reference (limit: 512 byte) + --> $DIR/large_types_passed_by_value.rs:4:11 + | +LL | fn f2(_v: [u8; 513]) {} + | ^^^^^^^^^ help: consider passing by reference instead: `&[u8; 513]` + | + = note: `-D clippy::large-types-passed-by-value` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::large_types_passed_by_value)]` + +error: aborting due to previous error + diff --git a/tests/ui-toml/manual_let_else/clippy.toml b/tests/ui-toml/manual_let_else/clippy.toml new file mode 100644 index 0000000000000..cdae1da011b0a --- /dev/null +++ b/tests/ui-toml/manual_let_else/clippy.toml @@ -0,0 +1 @@ +matches-for-let-else = "AllTypes" diff --git a/tests/ui-toml/manual_let_else/manual_let_else.fixed b/tests/ui-toml/manual_let_else/manual_let_else.fixed new file mode 100644 index 0000000000000..972f6aa40303d --- /dev/null +++ b/tests/ui-toml/manual_let_else/manual_let_else.fixed @@ -0,0 +1,10 @@ +#![warn(clippy::manual_let_else)] + +enum Foo { + A(u8), + B, +} + +fn main() { + let Foo::A(x) = Foo::A(1) else { return }; +} diff --git a/tests/ui-toml/manual_let_else/manual_let_else.rs b/tests/ui-toml/manual_let_else/manual_let_else.rs new file mode 100644 index 0000000000000..fdaba4ad2a602 --- /dev/null +++ b/tests/ui-toml/manual_let_else/manual_let_else.rs @@ -0,0 +1,14 @@ +#![warn(clippy::manual_let_else)] + +enum Foo { + A(u8), + B, +} + +fn main() { + let x = match Foo::A(1) { + //~^ ERROR: this could be rewritten as `let...else` + Foo::A(x) => x, + Foo::B => return, + }; +} diff --git a/tests/ui-toml/manual_let_else/manual_let_else.stderr b/tests/ui-toml/manual_let_else/manual_let_else.stderr new file mode 100644 index 0000000000000..5c2c86c373189 --- /dev/null +++ b/tests/ui-toml/manual_let_else/manual_let_else.stderr @@ -0,0 +1,15 @@ +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:9:5 + | +LL | / let x = match Foo::A(1) { +LL | | +LL | | Foo::A(x) => x, +LL | | Foo::B => return, +LL | | }; + | |______^ help: consider writing: `let Foo::A(x) = Foo::A(1) else { return };` + | + = note: `-D clippy::manual-let-else` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::manual_let_else)]` + +error: aborting due to previous error + diff --git a/tests/ui-toml/path_ends_with_ext/clippy.toml b/tests/ui-toml/path_ends_with_ext/clippy.toml new file mode 100644 index 0000000000000..40d7dfd938ce0 --- /dev/null +++ b/tests/ui-toml/path_ends_with_ext/clippy.toml @@ -0,0 +1 @@ +allowed-dotfiles = ["dot"] diff --git a/tests/ui-toml/path_ends_with_ext/path_ends_with_ext.rs b/tests/ui-toml/path_ends_with_ext/path_ends_with_ext.rs new file mode 100644 index 0000000000000..a34b15f4ac9ab --- /dev/null +++ b/tests/ui-toml/path_ends_with_ext/path_ends_with_ext.rs @@ -0,0 +1,9 @@ +#![warn(clippy::path_ends_with_ext)] + +use std::path::Path; + +fn f(p: &Path) { + p.ends_with(".dot"); +} + +fn main() {} diff --git a/tests/ui-toml/result_large_err/clippy.toml b/tests/ui-toml/result_large_err/clippy.toml new file mode 100644 index 0000000000000..df505ed9672a6 --- /dev/null +++ b/tests/ui-toml/result_large_err/clippy.toml @@ -0,0 +1 @@ +large-error-threshold = 512 diff --git a/tests/ui-toml/result_large_err/result_large_err.rs b/tests/ui-toml/result_large_err/result_large_err.rs new file mode 100644 index 0000000000000..dea4d61a96bfb --- /dev/null +++ b/tests/ui-toml/result_large_err/result_large_err.rs @@ -0,0 +1,10 @@ +#![warn(clippy::result_large_err)] + +fn f() -> Result<(), [u8; 511]> { + todo!() +} +fn f2() -> Result<(), [u8; 512]> { + //~^ ERROR: the `Err`-variant returned from this function is very large + todo!() +} +fn main() {} diff --git a/tests/ui-toml/result_large_err/result_large_err.stderr b/tests/ui-toml/result_large_err/result_large_err.stderr new file mode 100644 index 0000000000000..b0936319d1b9d --- /dev/null +++ b/tests/ui-toml/result_large_err/result_large_err.stderr @@ -0,0 +1,12 @@ +error: the `Err`-variant returned from this function is very large + --> $DIR/result_large_err.rs:6:12 + | +LL | fn f2() -> Result<(), [u8; 512]> { + | ^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes + | + = help: try reducing the size of `[u8; 512]`, for example by boxing large elements or replacing it with `Box<[u8; 512]>` + = note: `-D clippy::result-large-err` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::result_large_err)]` + +error: aborting due to previous error + diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index b97bb144468b0..4bed5c149f517 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -10,6 +10,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect allow-print-in-tests allow-private-module-inception allow-unwrap-in-tests + allowed-dotfiles allowed-idents-below-min-chars allowed-scripts arithmetic-side-effects-allowed @@ -82,6 +83,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect allow-print-in-tests allow-private-module-inception allow-unwrap-in-tests + allowed-dotfiles allowed-idents-below-min-chars allowed-scripts arithmetic-side-effects-allowed diff --git a/tests/ui-toml/too_large_for_stack/boxed_local.rs b/tests/ui-toml/too_large_for_stack/boxed_local.rs new file mode 100644 index 0000000000000..2f02361220650 --- /dev/null +++ b/tests/ui-toml/too_large_for_stack/boxed_local.rs @@ -0,0 +1,5 @@ +fn f(x: Box<[u8; 500]>) {} +//~^ ERROR: local variable doesn't need to be boxed here +fn f2(x: Box<[u8; 501]>) {} + +fn main() {} diff --git a/tests/ui-toml/too_large_for_stack/boxed_local.stderr b/tests/ui-toml/too_large_for_stack/boxed_local.stderr new file mode 100644 index 0000000000000..2859a29f1b2a2 --- /dev/null +++ b/tests/ui-toml/too_large_for_stack/boxed_local.stderr @@ -0,0 +1,11 @@ +error: local variable doesn't need to be boxed here + --> $DIR/boxed_local.rs:1:6 + | +LL | fn f(x: Box<[u8; 500]>) {} + | ^ + | + = note: `-D clippy::boxed-local` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::boxed_local)]` + +error: aborting due to previous error + diff --git a/tests/ui-toml/too_large_for_stack/clippy.toml b/tests/ui-toml/too_large_for_stack/clippy.toml new file mode 100644 index 0000000000000..a9c42fca46807 --- /dev/null +++ b/tests/ui-toml/too_large_for_stack/clippy.toml @@ -0,0 +1 @@ +too-large-for-stack = 500 diff --git a/tests/ui-toml/too_large_for_stack/useless_vec.fixed b/tests/ui-toml/too_large_for_stack/useless_vec.fixed new file mode 100644 index 0000000000000..ebe92d9b59946 --- /dev/null +++ b/tests/ui-toml/too_large_for_stack/useless_vec.fixed @@ -0,0 +1,9 @@ +#![warn(clippy::useless_vec)] + +fn main() { + let x = [0u8; 500]; + //~^ ERROR: useless use of `vec!` + x.contains(&1); + let y = vec![0u8; 501]; + y.contains(&1); +} diff --git a/tests/ui-toml/too_large_for_stack/useless_vec.rs b/tests/ui-toml/too_large_for_stack/useless_vec.rs new file mode 100644 index 0000000000000..e2886a8ccd126 --- /dev/null +++ b/tests/ui-toml/too_large_for_stack/useless_vec.rs @@ -0,0 +1,9 @@ +#![warn(clippy::useless_vec)] + +fn main() { + let x = vec![0u8; 500]; + //~^ ERROR: useless use of `vec!` + x.contains(&1); + let y = vec![0u8; 501]; + y.contains(&1); +} diff --git a/tests/ui-toml/too_large_for_stack/useless_vec.stderr b/tests/ui-toml/too_large_for_stack/useless_vec.stderr new file mode 100644 index 0000000000000..923cded5eef18 --- /dev/null +++ b/tests/ui-toml/too_large_for_stack/useless_vec.stderr @@ -0,0 +1,11 @@ +error: useless use of `vec!` + --> $DIR/useless_vec.rs:4:13 + | +LL | let x = vec![0u8; 500]; + | ^^^^^^^^^^^^^^ help: you can use an array directly: `[0u8; 500]` + | + = note: `-D clippy::useless-vec` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::useless_vec)]` + +error: aborting due to previous error + diff --git a/tests/ui-toml/too_many_arguments/clippy.toml b/tests/ui-toml/too_many_arguments/clippy.toml new file mode 100644 index 0000000000000..15906305c891b --- /dev/null +++ b/tests/ui-toml/too_many_arguments/clippy.toml @@ -0,0 +1 @@ +too-many-arguments-threshold = 10 diff --git a/tests/ui-toml/too_many_arguments/too_many_arguments.rs b/tests/ui-toml/too_many_arguments/too_many_arguments.rs new file mode 100644 index 0000000000000..7b2d6897d3c2c --- /dev/null +++ b/tests/ui-toml/too_many_arguments/too_many_arguments.rs @@ -0,0 +1,7 @@ +#![warn(clippy::too_many_arguments)] + +fn not_too_many(p1: u8, p2: u8, p3: u8, p4: u8, p5: u8, p6: u8, p7: u8, p8: u8, p9: u8, p10: u8) {} +fn too_many(p1: u8, p2: u8, p3: u8, p4: u8, p5: u8, p6: u8, p7: u8, p8: u8, p9: u8, p10: u8, p11: u8) {} +//~^ ERROR: this function has too many arguments + +fn main() {} diff --git a/tests/ui-toml/too_many_arguments/too_many_arguments.stderr b/tests/ui-toml/too_many_arguments/too_many_arguments.stderr new file mode 100644 index 0000000000000..a52e1fcb9e3a8 --- /dev/null +++ b/tests/ui-toml/too_many_arguments/too_many_arguments.stderr @@ -0,0 +1,11 @@ +error: this function has too many arguments (11/10) + --> $DIR/too_many_arguments.rs:4:1 + | +LL | fn too_many(p1: u8, p2: u8, p3: u8, p4: u8, p5: u8, p6: u8, p7: u8, p8: u8, p9: u8, p10: u8, p11: u8) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::too-many-arguments` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::too_many_arguments)]` + +error: aborting due to previous error + diff --git a/tests/ui-toml/type_complexity/clippy.toml b/tests/ui-toml/type_complexity/clippy.toml new file mode 100644 index 0000000000000..bf2ffdd0e3071 --- /dev/null +++ b/tests/ui-toml/type_complexity/clippy.toml @@ -0,0 +1 @@ +type-complexity-threshold = 500 diff --git a/tests/ui-toml/type_complexity/type_complexity.rs b/tests/ui-toml/type_complexity/type_complexity.rs new file mode 100644 index 0000000000000..b95f5134347dc --- /dev/null +++ b/tests/ui-toml/type_complexity/type_complexity.rs @@ -0,0 +1,7 @@ +// 480 +fn f(_: (u8, (u8, (u8, (u8, (u8, (u8,))))))) {} +// 550 +fn f2(_: (u8, (u8, (u8, (u8, (u8, (u8, u8))))))) {} +//~^ ERROR: very complex type used + +fn main() {} diff --git a/tests/ui-toml/type_complexity/type_complexity.stderr b/tests/ui-toml/type_complexity/type_complexity.stderr new file mode 100644 index 0000000000000..8ca637f722251 --- /dev/null +++ b/tests/ui-toml/type_complexity/type_complexity.stderr @@ -0,0 +1,11 @@ +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/type_complexity.rs:4:10 + | +LL | fn f2(_: (u8, (u8, (u8, (u8, (u8, (u8, u8))))))) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::type-complexity` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::type_complexity)]` + +error: aborting due to previous error + diff --git a/tests/ui-toml/type_repetition_in_bounds/clippy.toml b/tests/ui-toml/type_repetition_in_bounds/clippy.toml new file mode 100644 index 0000000000000..2f91866aa9367 --- /dev/null +++ b/tests/ui-toml/type_repetition_in_bounds/clippy.toml @@ -0,0 +1 @@ +max-trait-bounds = 5 diff --git a/tests/ui-toml/type_repetition_in_bounds/main.rs b/tests/ui-toml/type_repetition_in_bounds/main.rs new file mode 100644 index 0000000000000..2454c10382df7 --- /dev/null +++ b/tests/ui-toml/type_repetition_in_bounds/main.rs @@ -0,0 +1,18 @@ +#![warn(clippy::type_repetition_in_bounds)] + +fn f() +where + T: Copy + Clone + Sync + Send + ?Sized + Unpin, + T: PartialEq, +{ +} + +fn f2() +where + T: Copy + Clone + Sync + Send + ?Sized, + T: Unpin + PartialEq, + //~^ ERROR: this type has already been used as a bound predicate +{ +} + +fn main() {} diff --git a/tests/ui-toml/type_repetition_in_bounds/main.stderr b/tests/ui-toml/type_repetition_in_bounds/main.stderr new file mode 100644 index 0000000000000..2ae2984975f4b --- /dev/null +++ b/tests/ui-toml/type_repetition_in_bounds/main.stderr @@ -0,0 +1,12 @@ +error: this type has already been used as a bound predicate + --> $DIR/main.rs:13:5 + | +LL | T: Unpin + PartialEq, + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: consider combining the bounds: `T: Copy + Clone + Sync + Send + ?Sized + Unpin + PartialEq` + = note: `-D clippy::type-repetition-in-bounds` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::type_repetition_in_bounds)]` + +error: aborting due to previous error + diff --git a/tests/ui-toml/undocumented_unsafe_blocks/clippy.toml b/tests/ui-toml/undocumented_unsafe_blocks/clippy.toml deleted file mode 100644 index e6dbb3d37841b..0000000000000 --- a/tests/ui-toml/undocumented_unsafe_blocks/clippy.toml +++ /dev/null @@ -1,2 +0,0 @@ -accept-comment-above-statement = true -accept-comment-above-attributes = true diff --git a/tests/ui-toml/undocumented_unsafe_blocks/default/clippy.toml b/tests/ui-toml/undocumented_unsafe_blocks/default/clippy.toml new file mode 100644 index 0000000000000..3b205d536f2ed --- /dev/null +++ b/tests/ui-toml/undocumented_unsafe_blocks/default/clippy.toml @@ -0,0 +1,2 @@ +# default configuration has `accept-comment-above-statement` and +# `accept-comment-above-attributes` true diff --git a/tests/ui-toml/undocumented_unsafe_blocks/disabled/clippy.toml b/tests/ui-toml/undocumented_unsafe_blocks/disabled/clippy.toml new file mode 100644 index 0000000000000..57ecb902d6545 --- /dev/null +++ b/tests/ui-toml/undocumented_unsafe_blocks/disabled/clippy.toml @@ -0,0 +1,3 @@ +# test with these options disabled +accept-comment-above-statement = false +accept-comment-above-attributes = false diff --git a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.stderr b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.default.stderr similarity index 83% rename from tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.stderr rename to tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.default.stderr index 183c07fe786ce..15edf2a7dae47 100644 --- a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.stderr +++ b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.default.stderr @@ -1,5 +1,5 @@ error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:263:19 + --> $DIR/undocumented_unsafe_blocks.rs:266:19 | LL | /* Safety: */ unsafe {} | ^^^^^^^^^ @@ -9,7 +9,7 @@ LL | /* Safety: */ unsafe {} = help: to override `-D warnings` add `#[allow(clippy::undocumented_unsafe_blocks)]` error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:267:5 + --> $DIR/undocumented_unsafe_blocks.rs:270:5 | LL | unsafe {} | ^^^^^^^^^ @@ -17,7 +17,7 @@ LL | unsafe {} = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:271:14 + --> $DIR/undocumented_unsafe_blocks.rs:274:14 | LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; | ^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:271:29 + --> $DIR/undocumented_unsafe_blocks.rs:274:29 | LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; | ^^^^^^^^^^^^^ @@ -33,7 +33,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:271:48 + --> $DIR/undocumented_unsafe_blocks.rs:274:48 | LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; | ^^^^^^^^^^^^^ @@ -41,7 +41,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:275:18 + --> $DIR/undocumented_unsafe_blocks.rs:278:18 | LL | let _ = (42, unsafe {}, "test", unsafe {}); | ^^^^^^^^^ @@ -49,7 +49,7 @@ LL | let _ = (42, unsafe {}, "test", unsafe {}); = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:275:37 + --> $DIR/undocumented_unsafe_blocks.rs:278:37 | LL | let _ = (42, unsafe {}, "test", unsafe {}); | ^^^^^^^^^ @@ -57,7 +57,7 @@ LL | let _ = (42, unsafe {}, "test", unsafe {}); = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:279:14 + --> $DIR/undocumented_unsafe_blocks.rs:282:14 | LL | let _ = *unsafe { &42 }; | ^^^^^^^^^^^^^^ @@ -65,7 +65,7 @@ LL | let _ = *unsafe { &42 }; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:284:19 + --> $DIR/undocumented_unsafe_blocks.rs:287:19 | LL | let _ = match unsafe {} { | ^^^^^^^^^ @@ -73,7 +73,7 @@ LL | let _ = match unsafe {} { = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:290:14 + --> $DIR/undocumented_unsafe_blocks.rs:293:14 | LL | let _ = &unsafe {}; | ^^^^^^^^^ @@ -81,7 +81,7 @@ LL | let _ = &unsafe {}; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:294:14 + --> $DIR/undocumented_unsafe_blocks.rs:297:14 | LL | let _ = [unsafe {}; 5]; | ^^^^^^^^^ @@ -89,7 +89,7 @@ LL | let _ = [unsafe {}; 5]; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:298:13 + --> $DIR/undocumented_unsafe_blocks.rs:301:13 | LL | let _ = unsafe {}; | ^^^^^^^^^ @@ -97,7 +97,7 @@ LL | let _ = unsafe {}; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:308:8 + --> $DIR/undocumented_unsafe_blocks.rs:311:8 | LL | t!(unsafe {}); | ^^^^^^^^^ @@ -105,7 +105,7 @@ LL | t!(unsafe {}); = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:314:13 + --> $DIR/undocumented_unsafe_blocks.rs:317:13 | LL | unsafe {} | ^^^^^^^^^ @@ -117,7 +117,7 @@ LL | t!(); = note: this error originates in the macro `t` (in Nightly builds, run with -Z macro-backtrace for more info) error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:322:5 + --> $DIR/undocumented_unsafe_blocks.rs:325:5 | LL | unsafe {} // SAFETY: | ^^^^^^^^^ @@ -125,7 +125,7 @@ LL | unsafe {} // SAFETY: = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:326:5 + --> $DIR/undocumented_unsafe_blocks.rs:329:5 | LL | unsafe { | ^^^^^^^^ @@ -133,7 +133,7 @@ LL | unsafe { = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:336:5 + --> $DIR/undocumented_unsafe_blocks.rs:339:5 | LL | unsafe {}; | ^^^^^^^^^ @@ -141,7 +141,7 @@ LL | unsafe {}; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:340:20 + --> $DIR/undocumented_unsafe_blocks.rs:343:20 | LL | println!("{}", unsafe { String::from_utf8_unchecked(vec![]) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -149,7 +149,7 @@ LL | println!("{}", unsafe { String::from_utf8_unchecked(vec![]) }); = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:347:5 + --> $DIR/undocumented_unsafe_blocks.rs:350:5 | LL | unsafe impl A for () {} | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -157,7 +157,7 @@ LL | unsafe impl A for () {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:354:9 + --> $DIR/undocumented_unsafe_blocks.rs:357:9 | LL | unsafe impl B for (u32) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -165,7 +165,7 @@ LL | unsafe impl B for (u32) {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:375:13 + --> $DIR/undocumented_unsafe_blocks.rs:378:13 | LL | unsafe impl T for $t {} | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -177,7 +177,7 @@ LL | no_safety_comment!(()); = note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info) error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:400:13 + --> $DIR/undocumented_unsafe_blocks.rs:403:13 | LL | unsafe impl T for $t {} | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -189,7 +189,7 @@ LL | no_safety_comment!(()); = note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info) error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:408:5 + --> $DIR/undocumented_unsafe_blocks.rs:411:5 | LL | unsafe impl T for (i32) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -197,7 +197,7 @@ LL | unsafe impl T for (i32) {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:400:13 + --> $DIR/undocumented_unsafe_blocks.rs:403:13 | LL | unsafe impl T for $t {} | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -209,7 +209,7 @@ LL | no_safety_comment!(u32); = note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info) error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:414:5 + --> $DIR/undocumented_unsafe_blocks.rs:417:5 | LL | unsafe impl T for (bool) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -217,7 +217,7 @@ LL | unsafe impl T for (bool) {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:460:5 + --> $DIR/undocumented_unsafe_blocks.rs:463:5 | LL | unsafe impl NoComment for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -225,7 +225,7 @@ LL | unsafe impl NoComment for () {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:464:19 + --> $DIR/undocumented_unsafe_blocks.rs:467:19 | LL | /* SAFETY: */ unsafe impl InlineComment for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -233,7 +233,7 @@ LL | /* SAFETY: */ unsafe impl InlineComment for () {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:468:5 + --> $DIR/undocumented_unsafe_blocks.rs:471:5 | LL | unsafe impl TrailingComment for () {} // SAFETY: | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -241,13 +241,13 @@ LL | unsafe impl TrailingComment for () {} // SAFETY: = help: consider adding a safety comment on the preceding line error: constant item has unnecessary safety comment - --> $DIR/undocumented_unsafe_blocks.rs:472:5 + --> $DIR/undocumented_unsafe_blocks.rs:475:5 | LL | const BIG_NUMBER: i32 = 1000000; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: consider removing the safety comment - --> $DIR/undocumented_unsafe_blocks.rs:471:5 + --> $DIR/undocumented_unsafe_blocks.rs:474:5 | LL | // SAFETY: | ^^^^^^^^^^ @@ -255,7 +255,7 @@ LL | // SAFETY: = help: to override `-D warnings` add `#[allow(clippy::unnecessary_safety_comment)]` error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:473:5 + --> $DIR/undocumented_unsafe_blocks.rs:476:5 | LL | unsafe impl Interference for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -263,7 +263,7 @@ LL | unsafe impl Interference for () {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:480:5 + --> $DIR/undocumented_unsafe_blocks.rs:483:5 | LL | unsafe impl ImplInFn for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -271,7 +271,7 @@ LL | unsafe impl ImplInFn for () {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:489:1 + --> $DIR/undocumented_unsafe_blocks.rs:492:1 | LL | unsafe impl CrateRoot for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -279,7 +279,7 @@ LL | unsafe impl CrateRoot for () {} = help: consider adding a safety comment on the preceding line error: statement has unnecessary safety comment - --> $DIR/undocumented_unsafe_blocks.rs:502:5 + --> $DIR/undocumented_unsafe_blocks.rs:505:5 | LL | / let _ = { LL | | if unsafe { true } { @@ -291,13 +291,13 @@ LL | | }; | |______^ | help: consider removing the safety comment - --> $DIR/undocumented_unsafe_blocks.rs:501:5 + --> $DIR/undocumented_unsafe_blocks.rs:504:5 | LL | // SAFETY: this is more than one level away, so it should warn | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:503:12 + --> $DIR/undocumented_unsafe_blocks.rs:506:12 | LL | if unsafe { true } { | ^^^^^^^^^^^^^^^ @@ -305,7 +305,7 @@ LL | if unsafe { true } { = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:506:23 + --> $DIR/undocumented_unsafe_blocks.rs:509:23 | LL | let bar = unsafe {}; | ^^^^^^^^^ diff --git a/tests/ui/undocumented_unsafe_blocks.stderr b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr similarity index 74% rename from tests/ui/undocumented_unsafe_blocks.stderr rename to tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr index 77f6aea2e0d07..cc9530f79b67c 100644 --- a/tests/ui/undocumented_unsafe_blocks.stderr +++ b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr @@ -1,5 +1,5 @@ error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:262:19 + --> $DIR/undocumented_unsafe_blocks.rs:266:19 | LL | /* Safety: */ unsafe {} | ^^^^^^^^^ @@ -9,7 +9,7 @@ LL | /* Safety: */ unsafe {} = help: to override `-D warnings` add `#[allow(clippy::undocumented_unsafe_blocks)]` error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:266:5 + --> $DIR/undocumented_unsafe_blocks.rs:270:5 | LL | unsafe {} | ^^^^^^^^^ @@ -17,7 +17,7 @@ LL | unsafe {} = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:270:14 + --> $DIR/undocumented_unsafe_blocks.rs:274:14 | LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; | ^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:270:29 + --> $DIR/undocumented_unsafe_blocks.rs:274:29 | LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; | ^^^^^^^^^^^^^ @@ -33,7 +33,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:270:48 + --> $DIR/undocumented_unsafe_blocks.rs:274:48 | LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; | ^^^^^^^^^^^^^ @@ -41,7 +41,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:274:18 + --> $DIR/undocumented_unsafe_blocks.rs:278:18 | LL | let _ = (42, unsafe {}, "test", unsafe {}); | ^^^^^^^^^ @@ -49,7 +49,7 @@ LL | let _ = (42, unsafe {}, "test", unsafe {}); = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:274:37 + --> $DIR/undocumented_unsafe_blocks.rs:278:37 | LL | let _ = (42, unsafe {}, "test", unsafe {}); | ^^^^^^^^^ @@ -57,7 +57,7 @@ LL | let _ = (42, unsafe {}, "test", unsafe {}); = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:278:14 + --> $DIR/undocumented_unsafe_blocks.rs:282:14 | LL | let _ = *unsafe { &42 }; | ^^^^^^^^^^^^^^ @@ -65,7 +65,7 @@ LL | let _ = *unsafe { &42 }; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:283:19 + --> $DIR/undocumented_unsafe_blocks.rs:287:19 | LL | let _ = match unsafe {} { | ^^^^^^^^^ @@ -73,7 +73,7 @@ LL | let _ = match unsafe {} { = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:289:14 + --> $DIR/undocumented_unsafe_blocks.rs:293:14 | LL | let _ = &unsafe {}; | ^^^^^^^^^ @@ -81,7 +81,7 @@ LL | let _ = &unsafe {}; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:293:14 + --> $DIR/undocumented_unsafe_blocks.rs:297:14 | LL | let _ = [unsafe {}; 5]; | ^^^^^^^^^ @@ -89,7 +89,7 @@ LL | let _ = [unsafe {}; 5]; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:297:13 + --> $DIR/undocumented_unsafe_blocks.rs:301:13 | LL | let _ = unsafe {}; | ^^^^^^^^^ @@ -97,7 +97,7 @@ LL | let _ = unsafe {}; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:307:8 + --> $DIR/undocumented_unsafe_blocks.rs:311:8 | LL | t!(unsafe {}); | ^^^^^^^^^ @@ -105,7 +105,7 @@ LL | t!(unsafe {}); = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:313:13 + --> $DIR/undocumented_unsafe_blocks.rs:317:13 | LL | unsafe {} | ^^^^^^^^^ @@ -117,7 +117,7 @@ LL | t!(); = note: this error originates in the macro `t` (in Nightly builds, run with -Z macro-backtrace for more info) error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:321:5 + --> $DIR/undocumented_unsafe_blocks.rs:325:5 | LL | unsafe {} // SAFETY: | ^^^^^^^^^ @@ -125,7 +125,7 @@ LL | unsafe {} // SAFETY: = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:325:5 + --> $DIR/undocumented_unsafe_blocks.rs:329:5 | LL | unsafe { | ^^^^^^^^ @@ -133,7 +133,7 @@ LL | unsafe { = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:335:5 + --> $DIR/undocumented_unsafe_blocks.rs:339:5 | LL | unsafe {}; | ^^^^^^^^^ @@ -141,7 +141,7 @@ LL | unsafe {}; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:339:20 + --> $DIR/undocumented_unsafe_blocks.rs:343:20 | LL | println!("{}", unsafe { String::from_utf8_unchecked(vec![]) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -149,7 +149,7 @@ LL | println!("{}", unsafe { String::from_utf8_unchecked(vec![]) }); = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:346:5 + --> $DIR/undocumented_unsafe_blocks.rs:350:5 | LL | unsafe impl A for () {} | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -157,7 +157,7 @@ LL | unsafe impl A for () {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:353:9 + --> $DIR/undocumented_unsafe_blocks.rs:357:9 | LL | unsafe impl B for (u32) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -165,7 +165,7 @@ LL | unsafe impl B for (u32) {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:374:13 + --> $DIR/undocumented_unsafe_blocks.rs:378:13 | LL | unsafe impl T for $t {} | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -177,7 +177,7 @@ LL | no_safety_comment!(()); = note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info) error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:399:13 + --> $DIR/undocumented_unsafe_blocks.rs:403:13 | LL | unsafe impl T for $t {} | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -189,7 +189,7 @@ LL | no_safety_comment!(()); = note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info) error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:407:5 + --> $DIR/undocumented_unsafe_blocks.rs:411:5 | LL | unsafe impl T for (i32) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -197,7 +197,7 @@ LL | unsafe impl T for (i32) {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:399:13 + --> $DIR/undocumented_unsafe_blocks.rs:403:13 | LL | unsafe impl T for $t {} | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -209,7 +209,7 @@ LL | no_safety_comment!(u32); = note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info) error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:413:5 + --> $DIR/undocumented_unsafe_blocks.rs:417:5 | LL | unsafe impl T for (bool) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -217,7 +217,7 @@ LL | unsafe impl T for (bool) {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:459:5 + --> $DIR/undocumented_unsafe_blocks.rs:463:5 | LL | unsafe impl NoComment for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -225,7 +225,7 @@ LL | unsafe impl NoComment for () {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:463:19 + --> $DIR/undocumented_unsafe_blocks.rs:467:19 | LL | /* SAFETY: */ unsafe impl InlineComment for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -233,7 +233,7 @@ LL | /* SAFETY: */ unsafe impl InlineComment for () {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:467:5 + --> $DIR/undocumented_unsafe_blocks.rs:471:5 | LL | unsafe impl TrailingComment for () {} // SAFETY: | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -241,13 +241,13 @@ LL | unsafe impl TrailingComment for () {} // SAFETY: = help: consider adding a safety comment on the preceding line error: constant item has unnecessary safety comment - --> $DIR/undocumented_unsafe_blocks.rs:471:5 + --> $DIR/undocumented_unsafe_blocks.rs:475:5 | LL | const BIG_NUMBER: i32 = 1000000; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: consider removing the safety comment - --> $DIR/undocumented_unsafe_blocks.rs:470:5 + --> $DIR/undocumented_unsafe_blocks.rs:474:5 | LL | // SAFETY: | ^^^^^^^^^^ @@ -255,7 +255,7 @@ LL | // SAFETY: = help: to override `-D warnings` add `#[allow(clippy::unnecessary_safety_comment)]` error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:472:5 + --> $DIR/undocumented_unsafe_blocks.rs:476:5 | LL | unsafe impl Interference for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -263,7 +263,7 @@ LL | unsafe impl Interference for () {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:479:5 + --> $DIR/undocumented_unsafe_blocks.rs:483:5 | LL | unsafe impl ImplInFn for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -271,7 +271,7 @@ LL | unsafe impl ImplInFn for () {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:488:1 + --> $DIR/undocumented_unsafe_blocks.rs:492:1 | LL | unsafe impl CrateRoot for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -279,7 +279,7 @@ LL | unsafe impl CrateRoot for () {} = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:498:9 + --> $DIR/undocumented_unsafe_blocks.rs:502:9 | LL | unsafe {}; | ^^^^^^^^^ @@ -287,7 +287,7 @@ LL | unsafe {}; = help: consider adding a safety comment on the preceding line error: statement has unnecessary safety comment - --> $DIR/undocumented_unsafe_blocks.rs:501:5 + --> $DIR/undocumented_unsafe_blocks.rs:505:5 | LL | / let _ = { LL | | if unsafe { true } { @@ -299,13 +299,13 @@ LL | | }; | |______^ | help: consider removing the safety comment - --> $DIR/undocumented_unsafe_blocks.rs:500:5 + --> $DIR/undocumented_unsafe_blocks.rs:504:5 | LL | // SAFETY: this is more than one level away, so it should warn | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:502:12 + --> $DIR/undocumented_unsafe_blocks.rs:506:12 | LL | if unsafe { true } { | ^^^^^^^^^^^^^^^ @@ -313,7 +313,7 @@ LL | if unsafe { true } { = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:505:23 + --> $DIR/undocumented_unsafe_blocks.rs:509:23 | LL | let bar = unsafe {}; | ^^^^^^^^^ @@ -321,7 +321,7 @@ LL | let bar = unsafe {}; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:523:9 + --> $DIR/undocumented_unsafe_blocks.rs:527:9 | LL | unsafe { a_function_with_a_very_long_name_to_break_the_line() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -329,7 +329,7 @@ LL | unsafe { a_function_with_a_very_long_name_to_break_the_line() }; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:527:9 + --> $DIR/undocumented_unsafe_blocks.rs:531:9 | LL | unsafe { a_const_function_with_a_very_long_name_to_break_the_line() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -337,12 +337,60 @@ LL | unsafe { a_const_function_with_a_very_long_name_to_break_the_line() = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:531:9 + --> $DIR/undocumented_unsafe_blocks.rs:535:9 + | +LL | unsafe { a_const_function_with_a_very_long_name_to_break_the_line() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:541:5 + | +LL | unsafe {} + | ^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:545:5 + | +LL | unsafe { + | ^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:552:9 + | +LL | unsafe { a_function_with_a_very_long_name_to_break_the_line() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:557:9 + | +LL | unsafe { a_const_function_with_a_very_long_name_to_break_the_line() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:563:9 | LL | unsafe { a_const_function_with_a_very_long_name_to_break_the_line() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider adding a safety comment on the preceding line -error: aborting due to 39 previous errors +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:568:5 + | +LL | unsafe {} + | ^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: aborting due to 45 previous errors diff --git a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs index b28e1b7d1802d..a278139876064 100644 --- a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs +++ b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs @@ -1,4 +1,7 @@ //@aux-build:../../ui/auxiliary/proc_macro_unsafe.rs +//@revisions: default disabled +//@[default] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/undocumented_unsafe_blocks/default +//@[disabled] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/undocumented_unsafe_blocks/disabled #![warn(clippy::undocumented_unsafe_blocks, clippy::unnecessary_safety_comment)] #![allow(deref_nullptr, clippy::let_unit_value, clippy::missing_safety_doc)] @@ -491,7 +494,7 @@ unsafe impl CrateRoot for () {} // SAFETY: ok unsafe impl CrateRoot for (i32) {} -fn issue_9142() { +fn nested_block_separation_issue_9142() { // SAFETY: ok let _ = // we need this comment to avoid rustfmt putting @@ -518,49 +521,50 @@ pub const unsafe fn a_const_function_with_a_very_long_name_to_break_the_line() - 2 } -fn issue_10832() { - // Safety: A safety comment +fn separate_line_from_let_issue_10832() { + // SAFETY: fail ONLY if `accept-comment-above-statement = false` let _some_variable_with_a_very_long_name_to_break_the_line = unsafe { a_function_with_a_very_long_name_to_break_the_line() }; - // Safety: Another safety comment + // SAFETY: fail ONLY if `accept-comment-above-statement = false` const _SOME_CONST_WITH_A_VERY_LONG_NAME_TO_BREAK_THE_LINE: u32 = unsafe { a_const_function_with_a_very_long_name_to_break_the_line() }; - // Safety: Yet another safety comment + // SAFETY: fail ONLY if `accept-comment-above-statement = false` static _SOME_STATIC_WITH_A_VERY_LONG_NAME_TO_BREAK_THE_LINE: u32 = unsafe { a_const_function_with_a_very_long_name_to_break_the_line() }; } -fn issue_8679() { - // SAFETY: +fn above_expr_attribute_issue_8679() { + // SAFETY: fail ONLY if `accept-comment-above-attribute = false` #[allow(unsafe_code)] unsafe {} - // SAFETY: + // SAFETY: fail ONLY if `accept-comment-above-attribute = false` #[expect(unsafe_code, reason = "totally safe")] unsafe { *std::ptr::null::() }; - // Safety: A safety comment + // SAFETY: fail ONLY if `accept-comment-above-attribute = false` #[allow(unsafe_code)] let _some_variable_with_a_very_long_name_to_break_the_line = unsafe { a_function_with_a_very_long_name_to_break_the_line() }; - // Safety: Another safety comment + // SAFETY: fail ONLY if `accept-comment-above-attribute = false` #[allow(unsafe_code)] const _SOME_CONST_WITH_A_VERY_LONG_NAME_TO_BREAK_THE_LINE: u32 = unsafe { a_const_function_with_a_very_long_name_to_break_the_line() }; - // Safety: Yet another safety comment + // SAFETY: fail ONLY if `accept-comment-above-attribute = false` #[allow(unsafe_code)] - static _SOME_STATIC_WITH_A_VERY_LONG_NAME_TO_BREAK_THE_LINE: u32 = + #[allow(non_upper_case_globals)] + static _some_static_with_a_very_long_name_to_break_the_line: u32 = unsafe { a_const_function_with_a_very_long_name_to_break_the_line() }; // SAFETY: #[allow(unsafe_code)] - // This also works I guess + // This shouldn't work either unsafe {} } diff --git a/tests/ui-toml/unnecessary_box_returns/clippy.toml b/tests/ui-toml/unnecessary_box_returns/clippy.toml new file mode 100644 index 0000000000000..7c3ffc2908f3d --- /dev/null +++ b/tests/ui-toml/unnecessary_box_returns/clippy.toml @@ -0,0 +1 @@ +unnecessary-box-size = 64 diff --git a/tests/ui-toml/unnecessary_box_returns/unnecessary_box_returns.fixed b/tests/ui-toml/unnecessary_box_returns/unnecessary_box_returns.fixed new file mode 100644 index 0000000000000..413bc0bf1e378 --- /dev/null +++ b/tests/ui-toml/unnecessary_box_returns/unnecessary_box_returns.fixed @@ -0,0 +1,11 @@ +#![warn(clippy::unnecessary_box_returns)] + +fn f() -> [u8; 64] { + //~^ ERROR: boxed return of the sized type `[u8; 64]` + todo!() +} +fn f2() -> Box<[u8; 65]> { + todo!() +} + +fn main() {} diff --git a/tests/ui-toml/unnecessary_box_returns/unnecessary_box_returns.rs b/tests/ui-toml/unnecessary_box_returns/unnecessary_box_returns.rs new file mode 100644 index 0000000000000..b44fbb5544856 --- /dev/null +++ b/tests/ui-toml/unnecessary_box_returns/unnecessary_box_returns.rs @@ -0,0 +1,11 @@ +#![warn(clippy::unnecessary_box_returns)] + +fn f() -> Box<[u8; 64]> { + //~^ ERROR: boxed return of the sized type `[u8; 64]` + todo!() +} +fn f2() -> Box<[u8; 65]> { + todo!() +} + +fn main() {} diff --git a/tests/ui-toml/unnecessary_box_returns/unnecessary_box_returns.stderr b/tests/ui-toml/unnecessary_box_returns/unnecessary_box_returns.stderr new file mode 100644 index 0000000000000..df9aa37ac10f4 --- /dev/null +++ b/tests/ui-toml/unnecessary_box_returns/unnecessary_box_returns.stderr @@ -0,0 +1,12 @@ +error: boxed return of the sized type `[u8; 64]` + --> $DIR/unnecessary_box_returns.rs:3:11 + | +LL | fn f() -> Box<[u8; 64]> { + | ^^^^^^^^^^^^^ help: try: `[u8; 64]` + | + = help: changing this also requires a change to the return expressions in this function + = note: `-D clippy::unnecessary-box-returns` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_box_returns)]` + +error: aborting due to previous error + diff --git a/tests/ui-toml/verbose_bit_mask/clippy.toml b/tests/ui-toml/verbose_bit_mask/clippy.toml new file mode 100644 index 0000000000000..55a202eefb964 --- /dev/null +++ b/tests/ui-toml/verbose_bit_mask/clippy.toml @@ -0,0 +1 @@ +verbose-bit-mask-threshold = 31 diff --git a/tests/ui-toml/verbose_bit_mask/verbose_bit_mask.fixed b/tests/ui-toml/verbose_bit_mask/verbose_bit_mask.fixed new file mode 100644 index 0000000000000..437692a4d78c4 --- /dev/null +++ b/tests/ui-toml/verbose_bit_mask/verbose_bit_mask.fixed @@ -0,0 +1,7 @@ +#![warn(clippy::verbose_bit_mask)] +fn main() { + let v: i32 = 0; + let _ = v & 0b11111 == 0; + let _ = v.trailing_zeros() >= 6; + //~^ ERROR: bit mask could be simplified +} diff --git a/tests/ui-toml/verbose_bit_mask/verbose_bit_mask.rs b/tests/ui-toml/verbose_bit_mask/verbose_bit_mask.rs new file mode 100644 index 0000000000000..ce102708055a5 --- /dev/null +++ b/tests/ui-toml/verbose_bit_mask/verbose_bit_mask.rs @@ -0,0 +1,7 @@ +#![warn(clippy::verbose_bit_mask)] +fn main() { + let v: i32 = 0; + let _ = v & 0b11111 == 0; + let _ = v & 0b111111 == 0; + //~^ ERROR: bit mask could be simplified +} diff --git a/tests/ui-toml/verbose_bit_mask/verbose_bit_mask.stderr b/tests/ui-toml/verbose_bit_mask/verbose_bit_mask.stderr new file mode 100644 index 0000000000000..7377921b42ab1 --- /dev/null +++ b/tests/ui-toml/verbose_bit_mask/verbose_bit_mask.stderr @@ -0,0 +1,11 @@ +error: bit mask could be simplified with a call to `trailing_zeros` + --> $DIR/verbose_bit_mask.rs:5:13 + | +LL | let _ = v & 0b111111 == 0; + | ^^^^^^^^^^^^^^^^^ help: try: `v.trailing_zeros() >= 6` + | + = note: `-D clippy::verbose-bit-mask` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::verbose_bit_mask)]` + +error: aborting due to previous error + diff --git a/tests/ui-toml/wildcard_imports/clippy.toml b/tests/ui-toml/wildcard_imports/clippy.toml new file mode 100644 index 0000000000000..875aaeef6c935 --- /dev/null +++ b/tests/ui-toml/wildcard_imports/clippy.toml @@ -0,0 +1 @@ +warn-on-all-wildcard-imports = true diff --git a/tests/ui-toml/wildcard_imports/wildcard_imports.fixed b/tests/ui-toml/wildcard_imports/wildcard_imports.fixed new file mode 100644 index 0000000000000..1752f48856c2b --- /dev/null +++ b/tests/ui-toml/wildcard_imports/wildcard_imports.fixed @@ -0,0 +1,11 @@ +#![warn(clippy::wildcard_imports)] + +mod prelude { + pub const FOO: u8 = 1; +} +use prelude::FOO; +//~^ ERROR: usage of wildcard import + +fn main() { + let _ = FOO; +} diff --git a/tests/ui-toml/wildcard_imports/wildcard_imports.rs b/tests/ui-toml/wildcard_imports/wildcard_imports.rs new file mode 100644 index 0000000000000..331c2c59c222f --- /dev/null +++ b/tests/ui-toml/wildcard_imports/wildcard_imports.rs @@ -0,0 +1,11 @@ +#![warn(clippy::wildcard_imports)] + +mod prelude { + pub const FOO: u8 = 1; +} +use prelude::*; +//~^ ERROR: usage of wildcard import + +fn main() { + let _ = FOO; +} diff --git a/tests/ui-toml/wildcard_imports/wildcard_imports.stderr b/tests/ui-toml/wildcard_imports/wildcard_imports.stderr new file mode 100644 index 0000000000000..13ec3a229ce91 --- /dev/null +++ b/tests/ui-toml/wildcard_imports/wildcard_imports.stderr @@ -0,0 +1,11 @@ +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:6:5 + | +LL | use prelude::*; + | ^^^^^^^^^^ help: try: `prelude::FOO` + | + = note: `-D clippy::wildcard-imports` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::wildcard_imports)]` + +error: aborting due to previous error + diff --git a/tests/ui/cast.rs b/tests/ui/cast.rs index d0a092093f3e0..1ca18170f8a0e 100644 --- a/tests/ui/cast.rs +++ b/tests/ui/cast.rs @@ -361,3 +361,7 @@ fn avoid_subtract_overflow(q: u32) { //~^ ERROR: casting `u32` to `u8` may truncate the value c as usize; } + +fn issue11426() { + (&42u8 >> 0xa9008fb6c9d81e42_0e25730562a601c8_u128) as usize; +} diff --git a/tests/ui/cast_lossless_integer.fixed b/tests/ui/cast_lossless_integer.fixed index 6547107f50060..5e7e545e764a5 100644 --- a/tests/ui/cast_lossless_integer.fixed +++ b/tests/ui/cast_lossless_integer.fixed @@ -49,3 +49,14 @@ mod cast_lossless_in_impl { enum Test { A = u32::MAX as i64 + 1, } + +fn issue11458() { + macro_rules! sign_cast { + ($var: ident, $src: ty, $dest: ty) => { + <$dest>::from_ne_bytes(($var as $src).to_ne_bytes()) + }; + } + let x = 10_u128; + let _ = i32::from(sign_cast!(x, u8, i8)); + let _ = i32::from(sign_cast!(x, u8, i8) + 1); +} diff --git a/tests/ui/cast_lossless_integer.rs b/tests/ui/cast_lossless_integer.rs index 79af9a83ca286..0d69ddbd586a5 100644 --- a/tests/ui/cast_lossless_integer.rs +++ b/tests/ui/cast_lossless_integer.rs @@ -49,3 +49,14 @@ mod cast_lossless_in_impl { enum Test { A = u32::MAX as i64 + 1, } + +fn issue11458() { + macro_rules! sign_cast { + ($var: ident, $src: ty, $dest: ty) => { + <$dest>::from_ne_bytes(($var as $src).to_ne_bytes()) + }; + } + let x = 10_u128; + let _ = sign_cast!(x, u8, i8) as i32; + let _ = (sign_cast!(x, u8, i8) + 1) as i32; +} diff --git a/tests/ui/cast_lossless_integer.stderr b/tests/ui/cast_lossless_integer.stderr index da75cb195ebae..f9f111a7c20f1 100644 --- a/tests/ui/cast_lossless_integer.stderr +++ b/tests/ui/cast_lossless_integer.stderr @@ -115,5 +115,17 @@ error: casting `u8` to `u16` may become silently lossy if you later change the t LL | let _ = (1u8 + 1u8) as u16; | ^^^^^^^^^^^^^^^^^^ help: try: `u16::from(1u8 + 1u8)` -error: aborting due to 19 previous errors +error: casting `i8` to `i32` may become silently lossy if you later change the type + --> $DIR/cast_lossless_integer.rs:60:13 + | +LL | let _ = sign_cast!(x, u8, i8) as i32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::from(sign_cast!(x, u8, i8))` + +error: casting `i8` to `i32` may become silently lossy if you later change the type + --> $DIR/cast_lossless_integer.rs:61:13 + | +LL | let _ = (sign_cast!(x, u8, i8) + 1) as i32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::from(sign_cast!(x, u8, i8) + 1)` + +error: aborting due to 21 previous errors diff --git a/tests/ui/eta.fixed b/tests/ui/eta.fixed index f1cac8c5fbc63..32c7499bf73f6 100644 --- a/tests/ui/eta.fixed +++ b/tests/ui/eta.fixed @@ -7,7 +7,8 @@ clippy::option_map_unit_fn, clippy::redundant_closure_call, clippy::uninlined_format_args, - clippy::useless_vec + clippy::useless_vec, + clippy::unnecessary_map_on_constructor )] use std::path::{Path, PathBuf}; diff --git a/tests/ui/eta.rs b/tests/ui/eta.rs index c7a470b5be6df..25b7431ba8cd7 100644 --- a/tests/ui/eta.rs +++ b/tests/ui/eta.rs @@ -7,7 +7,8 @@ clippy::option_map_unit_fn, clippy::redundant_closure_call, clippy::uninlined_format_args, - clippy::useless_vec + clippy::useless_vec, + clippy::unnecessary_map_on_constructor )] use std::path::{Path, PathBuf}; diff --git a/tests/ui/eta.stderr b/tests/ui/eta.stderr index 7c7f179746205..951e4ac749c25 100644 --- a/tests/ui/eta.stderr +++ b/tests/ui/eta.stderr @@ -1,5 +1,5 @@ error: redundant closure - --> $DIR/eta.rs:28:27 + --> $DIR/eta.rs:29:27 | LL | let a = Some(1u8).map(|a| foo(a)); | ^^^^^^^^^^ help: replace the closure with the function itself: `foo` @@ -8,31 +8,31 @@ LL | let a = Some(1u8).map(|a| foo(a)); = help: to override `-D warnings` add `#[allow(clippy::redundant_closure)]` error: redundant closure - --> $DIR/eta.rs:32:40 + --> $DIR/eta.rs:33:40 | LL | let _: Option> = true.then(|| vec![]); // special case vec! | ^^^^^^^^^ help: replace the closure with `Vec::new`: `std::vec::Vec::new` error: redundant closure - --> $DIR/eta.rs:33:35 + --> $DIR/eta.rs:34:35 | LL | let d = Some(1u8).map(|a| foo((|b| foo2(b))(a))); //is adjusted? | ^^^^^^^^^^^^^ help: replace the closure with the function itself: `foo2` error: redundant closure - --> $DIR/eta.rs:34:26 + --> $DIR/eta.rs:35:26 | LL | all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted | ^^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `below` error: redundant closure - --> $DIR/eta.rs:41:27 + --> $DIR/eta.rs:42:27 | LL | let e = Some(1u8).map(|a| generic(a)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `generic` error: redundant closure - --> $DIR/eta.rs:93:51 + --> $DIR/eta.rs:94:51 | LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo()); | ^^^^^^^^^^^ help: replace the closure with the method itself: `TestStruct::foo` @@ -41,127 +41,127 @@ LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo()); = help: to override `-D warnings` add `#[allow(clippy::redundant_closure_for_method_calls)]` error: redundant closure - --> $DIR/eta.rs:94:51 + --> $DIR/eta.rs:95:51 | LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo()); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `TestTrait::trait_foo` error: redundant closure - --> $DIR/eta.rs:96:42 + --> $DIR/eta.rs:97:42 | LL | let e = Some(&mut vec![1, 2, 3]).map(|v| v.clear()); | ^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::vec::Vec::clear` error: redundant closure - --> $DIR/eta.rs:100:29 + --> $DIR/eta.rs:101:29 | LL | let e = Some("str").map(|s| s.to_string()); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::string::ToString::to_string` error: redundant closure - --> $DIR/eta.rs:101:27 + --> $DIR/eta.rs:102:27 | LL | let e = Some('a').map(|s| s.to_uppercase()); | ^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_uppercase` error: redundant closure - --> $DIR/eta.rs:103:65 + --> $DIR/eta.rs:104:65 | LL | let e: std::vec::Vec = vec!['a', 'b', 'c'].iter().map(|c| c.to_ascii_uppercase()).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_ascii_uppercase` error: redundant closure - --> $DIR/eta.rs:166:22 + --> $DIR/eta.rs:167:22 | LL | requires_fn_once(|| x()); | ^^^^^^ help: replace the closure with the function itself: `x` error: redundant closure - --> $DIR/eta.rs:173:27 + --> $DIR/eta.rs:174:27 | LL | let a = Some(1u8).map(|a| foo_ptr(a)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `foo_ptr` error: redundant closure - --> $DIR/eta.rs:178:27 + --> $DIR/eta.rs:179:27 | LL | let a = Some(1u8).map(|a| closure(a)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `closure` error: redundant closure - --> $DIR/eta.rs:210:28 + --> $DIR/eta.rs:211:28 | LL | x.into_iter().for_each(|x| add_to_res(x)); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res` error: redundant closure - --> $DIR/eta.rs:211:28 + --> $DIR/eta.rs:212:28 | LL | y.into_iter().for_each(|x| add_to_res(x)); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res` error: redundant closure - --> $DIR/eta.rs:212:28 + --> $DIR/eta.rs:213:28 | LL | z.into_iter().for_each(|x| add_to_res(x)); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `add_to_res` error: redundant closure - --> $DIR/eta.rs:219:21 + --> $DIR/eta.rs:220:21 | LL | Some(1).map(|n| closure(n)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut closure` error: redundant closure - --> $DIR/eta.rs:223:21 + --> $DIR/eta.rs:224:21 | LL | Some(1).map(|n| in_loop(n)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `in_loop` error: redundant closure - --> $DIR/eta.rs:316:18 + --> $DIR/eta.rs:317:18 | LL | takes_fn_mut(|| f()); | ^^^^^^ help: replace the closure with the function itself: `&mut f` error: redundant closure - --> $DIR/eta.rs:319:19 + --> $DIR/eta.rs:320:19 | LL | takes_fn_once(|| f()); | ^^^^^^ help: replace the closure with the function itself: `&mut f` error: redundant closure - --> $DIR/eta.rs:323:26 + --> $DIR/eta.rs:324:26 | LL | move || takes_fn_mut(|| f_used_once()) | ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut f_used_once` error: redundant closure - --> $DIR/eta.rs:335:19 + --> $DIR/eta.rs:336:19 | LL | array_opt.map(|a| a.as_slice()); | ^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `<[u8; 3]>::as_slice` error: redundant closure - --> $DIR/eta.rs:338:19 + --> $DIR/eta.rs:339:19 | LL | slice_opt.map(|s| s.len()); | ^^^^^^^^^^^ help: replace the closure with the method itself: `<[u8]>::len` error: redundant closure - --> $DIR/eta.rs:341:17 + --> $DIR/eta.rs:342:17 | LL | ptr_opt.map(|p| p.is_null()); | ^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `<*const usize>::is_null` error: redundant closure - --> $DIR/eta.rs:345:17 + --> $DIR/eta.rs:346:17 | LL | dyn_opt.map(|d| d.method_on_dyn()); | ^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `::method_on_dyn` error: redundant closure - --> $DIR/eta.rs:388:19 + --> $DIR/eta.rs:389:19 | LL | let _ = f(&0, |x, y| f2(x, y)); | ^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `f2` diff --git a/tests/ui/extra_unused_type_parameters.fixed b/tests/ui/extra_unused_type_parameters.fixed index a4943344a11ef..c9bebabdf17ae 100644 --- a/tests/ui/extra_unused_type_parameters.fixed +++ b/tests/ui/extra_unused_type_parameters.fixed @@ -113,4 +113,19 @@ with_span!( } ); +mod issue11302 { + use std::fmt::Debug; + use std::marker::PhantomData; + + #[derive(Debug)] + struct Wrapper(PhantomData); + + fn store(v: &mut Vec>) + where + Wrapper: Debug, + { + v.push(Box::new(Wrapper(PhantomData))); + } +} + fn main() {} diff --git a/tests/ui/extra_unused_type_parameters.rs b/tests/ui/extra_unused_type_parameters.rs index 6d85b1ce9d4f3..1bc0047adf01f 100644 --- a/tests/ui/extra_unused_type_parameters.rs +++ b/tests/ui/extra_unused_type_parameters.rs @@ -113,4 +113,19 @@ with_span!( } ); +mod issue11302 { + use std::fmt::Debug; + use std::marker::PhantomData; + + #[derive(Debug)] + struct Wrapper(PhantomData); + + fn store(v: &mut Vec>) + where + Wrapper: Debug, + { + v.push(Box::new(Wrapper(PhantomData))); + } +} + fn main() {} diff --git a/tests/ui/filter_map_bool_then.fixed b/tests/ui/filter_map_bool_then.fixed index 6de870a928985..6a1b81fdbcbe8 100644 --- a/tests/ui/filter_map_bool_then.fixed +++ b/tests/ui/filter_map_bool_then.fixed @@ -55,3 +55,27 @@ fn main() { fn issue11309<'a>(iter: impl Iterator) -> Vec<&'a str> { iter.filter_map(|(_, s): (&str, _)| Some(s)).collect() } + +fn issue11503() { + let bools: &[bool] = &[true, false, false, true]; + let _: Vec = bools.iter().enumerate().filter(|&(i, b)| *b).map(|(i, b)| i).collect(); + + // Need to insert multiple derefs if there is more than one layer of references + let bools: &[&&bool] = &[&&true, &&false, &&false, &&true]; + let _: Vec = bools.iter().enumerate().filter(|&(i, b)| ***b).map(|(i, b)| i).collect(); + + // Should also suggest derefs when going through a mutable reference + let bools: &[&mut bool] = &[&mut true]; + let _: Vec = bools.iter().enumerate().filter(|&(i, b)| **b).map(|(i, b)| i).collect(); + + // Should also suggest derefs when going through a custom deref + struct DerefToBool; + impl std::ops::Deref for DerefToBool { + type Target = bool; + fn deref(&self) -> &Self::Target { + &true + } + } + let bools: &[&&DerefToBool] = &[&&DerefToBool]; + let _: Vec = bools.iter().enumerate().filter(|&(i, b)| ****b).map(|(i, b)| i).collect(); +} diff --git a/tests/ui/filter_map_bool_then.rs b/tests/ui/filter_map_bool_then.rs index 4108177e3a0e3..a41e88f8805de 100644 --- a/tests/ui/filter_map_bool_then.rs +++ b/tests/ui/filter_map_bool_then.rs @@ -55,3 +55,27 @@ fn main() { fn issue11309<'a>(iter: impl Iterator) -> Vec<&'a str> { iter.filter_map(|(_, s): (&str, _)| Some(s)).collect() } + +fn issue11503() { + let bools: &[bool] = &[true, false, false, true]; + let _: Vec = bools.iter().enumerate().filter_map(|(i, b)| b.then(|| i)).collect(); + + // Need to insert multiple derefs if there is more than one layer of references + let bools: &[&&bool] = &[&&true, &&false, &&false, &&true]; + let _: Vec = bools.iter().enumerate().filter_map(|(i, b)| b.then(|| i)).collect(); + + // Should also suggest derefs when going through a mutable reference + let bools: &[&mut bool] = &[&mut true]; + let _: Vec = bools.iter().enumerate().filter_map(|(i, b)| b.then(|| i)).collect(); + + // Should also suggest derefs when going through a custom deref + struct DerefToBool; + impl std::ops::Deref for DerefToBool { + type Target = bool; + fn deref(&self) -> &Self::Target { + &true + } + } + let bools: &[&&DerefToBool] = &[&&DerefToBool]; + let _: Vec = bools.iter().enumerate().filter_map(|(i, b)| b.then(|| i)).collect(); +} diff --git a/tests/ui/filter_map_bool_then.stderr b/tests/ui/filter_map_bool_then.stderr index 86ef6edf8eeda..fab6987913a10 100644 --- a/tests/ui/filter_map_bool_then.stderr +++ b/tests/ui/filter_map_bool_then.stderr @@ -37,5 +37,29 @@ error: usage of `bool::then` in `filter_map` LL | v.clone().iter().filter_map(|i| (i == &NonCopy).then(|| i)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&i| (i == &NonCopy)).map(|i| i)` -error: aborting due to 6 previous errors +error: usage of `bool::then` in `filter_map` + --> $DIR/filter_map_bool_then.rs:61:50 + | +LL | let _: Vec = bools.iter().enumerate().filter_map(|(i, b)| b.then(|| i)).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&(i, b)| *b).map(|(i, b)| i)` + +error: usage of `bool::then` in `filter_map` + --> $DIR/filter_map_bool_then.rs:65:50 + | +LL | let _: Vec = bools.iter().enumerate().filter_map(|(i, b)| b.then(|| i)).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&(i, b)| ***b).map(|(i, b)| i)` + +error: usage of `bool::then` in `filter_map` + --> $DIR/filter_map_bool_then.rs:69:50 + | +LL | let _: Vec = bools.iter().enumerate().filter_map(|(i, b)| b.then(|| i)).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&(i, b)| **b).map(|(i, b)| i)` + +error: usage of `bool::then` in `filter_map` + --> $DIR/filter_map_bool_then.rs:80:50 + | +LL | let _: Vec = bools.iter().enumerate().filter_map(|(i, b)| b.then(|| i)).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&(i, b)| ****b).map(|(i, b)| i)` + +error: aborting due to 10 previous errors diff --git a/tests/ui/len_without_is_empty.rs b/tests/ui/len_without_is_empty.rs index ac6c3e06365c6..d623601110e27 100644 --- a/tests/ui/len_without_is_empty.rs +++ b/tests/ui/len_without_is_empty.rs @@ -436,4 +436,27 @@ impl DifferingErrors { } } +// Issue #11165 +pub struct Aliased1; +pub type Alias1 = Aliased1; + +impl Alias1 { + pub fn len(&self) -> usize { + todo!() + } + + pub fn is_empty(&self) -> bool { + todo!() + } +} + +pub struct Aliased2; +pub type Alias2 = Aliased2; +impl Alias2 { + pub fn len(&self) -> usize { + //~^ ERROR: type `Alias2` has a public `len` method, but no `is_empty` method + todo!() + } +} + fn main() {} diff --git a/tests/ui/len_without_is_empty.stderr b/tests/ui/len_without_is_empty.stderr index 4815ce6a04b29..8e51c28b33003 100644 --- a/tests/ui/len_without_is_empty.stderr +++ b/tests/ui/len_without_is_empty.stderr @@ -141,5 +141,11 @@ error: struct `AsyncResultLenWithoutIsEmpty` has a public `len` method, but no ` LL | pub async fn len(&self) -> Result { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 15 previous errors +error: type `Alias2` has a public `len` method, but no `is_empty` method + --> $DIR/len_without_is_empty.rs:456:5 + | +LL | pub fn len(&self) -> usize { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 16 previous errors diff --git a/tests/ui/let_unit.fixed b/tests/ui/let_unit.fixed index 57374bd5fcdad..f98ce9d50a994 100644 --- a/tests/ui/let_unit.fixed +++ b/tests/ui/let_unit.fixed @@ -177,3 +177,5 @@ fn attributes() { async fn issue10433() { let _pending: () = std::future::pending().await; } + +pub async fn issue11502(a: ()) {} diff --git a/tests/ui/let_unit.rs b/tests/ui/let_unit.rs index 09077c60d5019..6d942ca8908c4 100644 --- a/tests/ui/let_unit.rs +++ b/tests/ui/let_unit.rs @@ -177,3 +177,5 @@ fn attributes() { async fn issue10433() { let _pending: () = std::future::pending().await; } + +pub async fn issue11502(a: ()) {} diff --git a/tests/ui/manual_map_option.fixed b/tests/ui/manual_map_option.fixed index f6a964da418a2..16cee3fd38236 100644 --- a/tests/ui/manual_map_option.fixed +++ b/tests/ui/manual_map_option.fixed @@ -5,6 +5,7 @@ clippy::unit_arg, clippy::match_ref_pats, clippy::redundant_pattern_matching, + clippy::unnecessary_map_on_constructor, for_loops_over_fallibles, dead_code )] diff --git a/tests/ui/manual_map_option.rs b/tests/ui/manual_map_option.rs index df9dc256d3038..4655acf1406c7 100644 --- a/tests/ui/manual_map_option.rs +++ b/tests/ui/manual_map_option.rs @@ -5,6 +5,7 @@ clippy::unit_arg, clippy::match_ref_pats, clippy::redundant_pattern_matching, + clippy::unnecessary_map_on_constructor, for_loops_over_fallibles, dead_code )] diff --git a/tests/ui/manual_map_option.stderr b/tests/ui/manual_map_option.stderr index ff6ed974d4a12..3754a982cb9bc 100644 --- a/tests/ui/manual_map_option.stderr +++ b/tests/ui/manual_map_option.stderr @@ -1,5 +1,5 @@ error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:13:5 + --> $DIR/manual_map_option.rs:14:5 | LL | / match Some(0) { LL | | Some(_) => Some(2), @@ -11,7 +11,7 @@ LL | | }; = help: to override `-D warnings` add `#[allow(clippy::manual_map)]` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:18:5 + --> $DIR/manual_map_option.rs:19:5 | LL | / match Some(0) { LL | | Some(x) => Some(x + 1), @@ -20,7 +20,7 @@ LL | | }; | |_____^ help: try: `Some(0).map(|x| x + 1)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:23:5 + --> $DIR/manual_map_option.rs:24:5 | LL | / match Some("") { LL | | Some(x) => Some(x.is_empty()), @@ -29,7 +29,7 @@ LL | | }; | |_____^ help: try: `Some("").map(|x| x.is_empty())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:28:5 + --> $DIR/manual_map_option.rs:29:5 | LL | / if let Some(x) = Some(0) { LL | | Some(!x) @@ -39,7 +39,7 @@ LL | | }; | |_____^ help: try: `Some(0).map(|x| !x)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:35:5 + --> $DIR/manual_map_option.rs:36:5 | LL | / match Some(0) { LL | | Some(x) => { Some(std::convert::identity(x)) } @@ -48,7 +48,7 @@ LL | | }; | |_____^ help: try: `Some(0).map(std::convert::identity)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:40:5 + --> $DIR/manual_map_option.rs:41:5 | LL | / match Some(&String::new()) { LL | | Some(x) => Some(str::len(x)), @@ -57,7 +57,7 @@ LL | | }; | |_____^ help: try: `Some(&String::new()).map(|x| str::len(x))` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:50:5 + --> $DIR/manual_map_option.rs:51:5 | LL | / match &Some([0, 1]) { LL | | Some(x) => Some(x[0]), @@ -66,7 +66,7 @@ LL | | }; | |_____^ help: try: `Some([0, 1]).as_ref().map(|x| x[0])` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:55:5 + --> $DIR/manual_map_option.rs:56:5 | LL | / match &Some(0) { LL | | &Some(x) => Some(x * 2), @@ -75,7 +75,7 @@ LL | | }; | |_____^ help: try: `Some(0).map(|x| x * 2)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:60:5 + --> $DIR/manual_map_option.rs:61:5 | LL | / match Some(String::new()) { LL | | Some(ref x) => Some(x.is_empty()), @@ -84,7 +84,7 @@ LL | | }; | |_____^ help: try: `Some(String::new()).as_ref().map(|x| x.is_empty())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:65:5 + --> $DIR/manual_map_option.rs:66:5 | LL | / match &&Some(String::new()) { LL | | Some(x) => Some(x.len()), @@ -93,7 +93,7 @@ LL | | }; | |_____^ help: try: `Some(String::new()).as_ref().map(|x| x.len())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:70:5 + --> $DIR/manual_map_option.rs:71:5 | LL | / match &&Some(0) { LL | | &&Some(x) => Some(x + x), @@ -102,7 +102,7 @@ LL | | }; | |_____^ help: try: `Some(0).map(|x| x + x)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:83:9 + --> $DIR/manual_map_option.rs:84:9 | LL | / match &mut Some(String::new()) { LL | | Some(x) => Some(x.push_str("")), @@ -111,7 +111,7 @@ LL | | }; | |_________^ help: try: `Some(String::new()).as_mut().map(|x| x.push_str(""))` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:89:5 + --> $DIR/manual_map_option.rs:90:5 | LL | / match &mut Some(String::new()) { LL | | Some(ref x) => Some(x.len()), @@ -120,7 +120,7 @@ LL | | }; | |_____^ help: try: `Some(String::new()).as_ref().map(|x| x.len())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:94:5 + --> $DIR/manual_map_option.rs:95:5 | LL | / match &mut &Some(String::new()) { LL | | Some(x) => Some(x.is_empty()), @@ -129,7 +129,7 @@ LL | | }; | |_____^ help: try: `Some(String::new()).as_ref().map(|x| x.is_empty())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:99:5 + --> $DIR/manual_map_option.rs:100:5 | LL | / match Some((0, 1, 2)) { LL | | Some((x, y, z)) => Some(x + y + z), @@ -138,7 +138,7 @@ LL | | }; | |_____^ help: try: `Some((0, 1, 2)).map(|(x, y, z)| x + y + z)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:104:5 + --> $DIR/manual_map_option.rs:105:5 | LL | / match Some([1, 2, 3]) { LL | | Some([first, ..]) => Some(first), @@ -147,7 +147,7 @@ LL | | }; | |_____^ help: try: `Some([1, 2, 3]).map(|[first, ..]| first)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:109:5 + --> $DIR/manual_map_option.rs:110:5 | LL | / match &Some((String::new(), "test")) { LL | | Some((x, y)) => Some((y, x)), @@ -156,7 +156,7 @@ LL | | }; | |_____^ help: try: `Some((String::new(), "test")).as_ref().map(|(x, y)| (y, x))` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:167:5 + --> $DIR/manual_map_option.rs:168:5 | LL | / match Some(0) { LL | | Some(x) => Some(vec![x]), @@ -165,7 +165,7 @@ LL | | }; | |_____^ help: try: `Some(0).map(|x| vec![x])` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:172:5 + --> $DIR/manual_map_option.rs:173:5 | LL | / match option_env!("") { LL | | Some(x) => Some(String::from(x)), @@ -174,7 +174,7 @@ LL | | }; | |_____^ help: try: `option_env!("").map(String::from)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:192:12 + --> $DIR/manual_map_option.rs:193:12 | LL | } else if let Some(x) = Some(0) { | ____________^ @@ -185,7 +185,7 @@ LL | | }; | |_____^ help: try: `{ Some(0).map(|x| x + 1) }` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:200:12 + --> $DIR/manual_map_option.rs:201:12 | LL | } else if let Some(x) = Some(0) { | ____________^ diff --git a/tests/ui/needless_borrow.fixed b/tests/ui/needless_borrow.fixed index 0a52b25229d76..c2c5f765abfff 100644 --- a/tests/ui/needless_borrow.fixed +++ b/tests/ui/needless_borrow.fixed @@ -131,21 +131,6 @@ fn main() { 0 } } - - let _ = std::process::Command::new("ls").args(["-a", "-l"]).status().unwrap(); - let _ = std::path::Path::new(".").join("."); - deref_target_is_x(X); - multiple_constraints([[""]]); - multiple_constraints_normalizes_to_same(X, X); - let _ = Some("").unwrap_or(""); - let _ = std::fs::write("x", "".to_string()); - - only_sized(&""); // Don't lint. `Sized` is only bound - let _ = std::any::Any::type_id(&""); // Don't lint. `Any` is only bound - let _ = Box::new(&""); // Don't lint. Type parameter appears in return type - ref_as_ref_path(&""); // Don't lint. Argument type is not a type parameter - refs_only(&()); // Don't lint. `&T` implements trait, but `T` doesn't - multiple_constraints_normalizes_to_different(&[[""]], &[""]); // Don't lint. Projected type appears in arguments } #[allow(clippy::needless_borrowed_reference)] @@ -201,103 +186,6 @@ mod issue9160 { } } -#[derive(Clone, Copy)] -struct X; - -impl std::ops::Deref for X { - type Target = X; - fn deref(&self) -> &Self::Target { - self - } -} - -fn deref_target_is_x(_: T) -where - T: std::ops::Deref, -{ -} - -fn multiple_constraints(_: T) -where - T: IntoIterator + IntoIterator, - U: IntoIterator, - V: AsRef, - X: IntoIterator, - Y: AsRef, -{ -} - -fn multiple_constraints_normalizes_to_same(_: T, _: V) -where - T: std::ops::Deref, - U: std::ops::Deref, -{ -} - -fn only_sized(_: T) {} - -fn ref_as_ref_path(_: &'static T) -where - &'static T: AsRef, -{ -} - -trait RefsOnly { - type Referent; -} - -impl RefsOnly for &T { - type Referent = T; -} - -fn refs_only(_: T) -where - T: RefsOnly, -{ -} - -fn multiple_constraints_normalizes_to_different(_: T, _: U) -where - T: IntoIterator, - U: IntoIterator, - V: AsRef, -{ -} - -// https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321 -mod copyable_iterator { - #[derive(Clone, Copy)] - struct Iter; - impl Iterator for Iter { - type Item = (); - fn next(&mut self) -> Option { - None - } - } - fn takes_iter(_: impl Iterator) {} - fn dont_warn(mut x: Iter) { - takes_iter(&mut x); - } - #[allow(unused_mut)] - fn warn(mut x: &mut Iter) { - takes_iter(x) - } -} - -#[clippy::msrv = "1.52.0"] -mod under_msrv { - fn foo() { - let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap(); - } -} - -#[clippy::msrv = "1.53.0"] -mod meets_msrv { - fn foo() { - let _ = std::process::Command::new("ls").args(["-a", "-l"]).status().unwrap(); - } -} - fn issue9383() { // Should not lint because unions need explicit deref when accessing field use std::mem::ManuallyDrop; @@ -326,184 +214,6 @@ fn issue9383() { } } -fn closure_test() { - let env = "env".to_owned(); - let arg = "arg".to_owned(); - let f = |arg| { - let loc = "loc".to_owned(); - let _ = std::fs::write("x", &env); // Don't lint. In environment - let _ = std::fs::write("x", arg); - let _ = std::fs::write("x", loc); - }; - let _ = std::fs::write("x", &env); // Don't lint. Borrowed by `f` - f(arg); -} - -mod significant_drop { - #[derive(Debug)] - struct X; - - #[derive(Debug)] - struct Y; - - impl Drop for Y { - fn drop(&mut self) {} - } - - fn foo(x: X, y: Y) { - debug(x); - debug(&y); // Don't lint. Has significant drop - } - - fn debug(_: impl std::fmt::Debug) {} -} - -mod used_exactly_once { - fn foo(x: String) { - use_x(x); - } - fn use_x(_: impl AsRef) {} -} - -mod used_more_than_once { - fn foo(x: String) { - use_x(&x); - use_x_again(&x); - } - fn use_x(_: impl AsRef) {} - fn use_x_again(_: impl AsRef) {} -} - -// https://github.com/rust-lang/rust-clippy/issues/9111#issuecomment-1277114280 -mod issue_9111 { - struct A; - - impl Extend for A { - fn extend>(&mut self, _: T) { - unimplemented!() - } - } - - impl<'a> Extend<&'a u8> for A { - fn extend>(&mut self, _: T) { - unimplemented!() - } - } - - fn main() { - let mut a = A; - a.extend(&[]); // vs a.extend([]); - } -} - -mod issue_9710 { - fn main() { - let string = String::new(); - for _i in 0..10 { - f(&string); - } - } - - fn f>(_: T) {} -} - -mod issue_9739 { - fn foo(_it: impl IntoIterator) {} - - fn main() { - foo(if std::env::var_os("HI").is_some() { - &[0] - } else { - &[] as &[u32] - }); - } -} - -mod issue_9739_method_variant { - struct S; - - impl S { - fn foo(&self, _it: impl IntoIterator) {} - } - - fn main() { - S.foo(if std::env::var_os("HI").is_some() { - &[0] - } else { - &[] as &[u32] - }); - } -} - -mod issue_9782 { - fn foo>(t: T) { - println!("{}", std::mem::size_of::()); - let _t: &[u8] = t.as_ref(); - } - - fn main() { - let a: [u8; 100] = [0u8; 100]; - - // 100 - foo::<[u8; 100]>(a); - foo(a); - - // 16 - foo::<&[u8]>(&a); - foo(a.as_slice()); - - // 8 - foo::<&[u8; 100]>(&a); - foo(a); - } -} - -mod issue_9782_type_relative_variant { - struct S; - - impl S { - fn foo>(t: T) { - println!("{}", std::mem::size_of::()); - let _t: &[u8] = t.as_ref(); - } - } - - fn main() { - let a: [u8; 100] = [0u8; 100]; - - S::foo::<&[u8; 100]>(&a); - } -} - -mod issue_9782_method_variant { - struct S; - - impl S { - fn foo>(&self, t: T) { - println!("{}", std::mem::size_of::()); - let _t: &[u8] = t.as_ref(); - } - } - - fn main() { - let a: [u8; 100] = [0u8; 100]; - - S.foo::<&[u8; 100]>(&a); - } -} - -mod issue_10535 { - static SOME_STATIC: String = String::new(); - - static UNIT: () = compute(&SOME_STATIC); - - pub const fn compute(_: T) - where - T: Copy, - { - } -} - mod issue_10253 { struct S; trait X { diff --git a/tests/ui/needless_borrow.rs b/tests/ui/needless_borrow.rs index 34a95d184635d..0cd6e41b8a47a 100644 --- a/tests/ui/needless_borrow.rs +++ b/tests/ui/needless_borrow.rs @@ -131,21 +131,6 @@ fn main() { 0 } } - - let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap(); - let _ = std::path::Path::new(".").join(&&"."); - deref_target_is_x(&X); - multiple_constraints(&[[""]]); - multiple_constraints_normalizes_to_same(&X, X); - let _ = Some("").unwrap_or(&""); - let _ = std::fs::write("x", &"".to_string()); - - only_sized(&""); // Don't lint. `Sized` is only bound - let _ = std::any::Any::type_id(&""); // Don't lint. `Any` is only bound - let _ = Box::new(&""); // Don't lint. Type parameter appears in return type - ref_as_ref_path(&""); // Don't lint. Argument type is not a type parameter - refs_only(&()); // Don't lint. `&T` implements trait, but `T` doesn't - multiple_constraints_normalizes_to_different(&[[""]], &[""]); // Don't lint. Projected type appears in arguments } #[allow(clippy::needless_borrowed_reference)] @@ -201,103 +186,6 @@ mod issue9160 { } } -#[derive(Clone, Copy)] -struct X; - -impl std::ops::Deref for X { - type Target = X; - fn deref(&self) -> &Self::Target { - self - } -} - -fn deref_target_is_x(_: T) -where - T: std::ops::Deref, -{ -} - -fn multiple_constraints(_: T) -where - T: IntoIterator + IntoIterator, - U: IntoIterator, - V: AsRef, - X: IntoIterator, - Y: AsRef, -{ -} - -fn multiple_constraints_normalizes_to_same(_: T, _: V) -where - T: std::ops::Deref, - U: std::ops::Deref, -{ -} - -fn only_sized(_: T) {} - -fn ref_as_ref_path(_: &'static T) -where - &'static T: AsRef, -{ -} - -trait RefsOnly { - type Referent; -} - -impl RefsOnly for &T { - type Referent = T; -} - -fn refs_only(_: T) -where - T: RefsOnly, -{ -} - -fn multiple_constraints_normalizes_to_different(_: T, _: U) -where - T: IntoIterator, - U: IntoIterator, - V: AsRef, -{ -} - -// https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321 -mod copyable_iterator { - #[derive(Clone, Copy)] - struct Iter; - impl Iterator for Iter { - type Item = (); - fn next(&mut self) -> Option { - None - } - } - fn takes_iter(_: impl Iterator) {} - fn dont_warn(mut x: Iter) { - takes_iter(&mut x); - } - #[allow(unused_mut)] - fn warn(mut x: &mut Iter) { - takes_iter(&mut x) - } -} - -#[clippy::msrv = "1.52.0"] -mod under_msrv { - fn foo() { - let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap(); - } -} - -#[clippy::msrv = "1.53.0"] -mod meets_msrv { - fn foo() { - let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap(); - } -} - fn issue9383() { // Should not lint because unions need explicit deref when accessing field use std::mem::ManuallyDrop; @@ -326,184 +214,6 @@ fn issue9383() { } } -fn closure_test() { - let env = "env".to_owned(); - let arg = "arg".to_owned(); - let f = |arg| { - let loc = "loc".to_owned(); - let _ = std::fs::write("x", &env); // Don't lint. In environment - let _ = std::fs::write("x", &arg); - let _ = std::fs::write("x", &loc); - }; - let _ = std::fs::write("x", &env); // Don't lint. Borrowed by `f` - f(arg); -} - -mod significant_drop { - #[derive(Debug)] - struct X; - - #[derive(Debug)] - struct Y; - - impl Drop for Y { - fn drop(&mut self) {} - } - - fn foo(x: X, y: Y) { - debug(&x); - debug(&y); // Don't lint. Has significant drop - } - - fn debug(_: impl std::fmt::Debug) {} -} - -mod used_exactly_once { - fn foo(x: String) { - use_x(&x); - } - fn use_x(_: impl AsRef) {} -} - -mod used_more_than_once { - fn foo(x: String) { - use_x(&x); - use_x_again(&x); - } - fn use_x(_: impl AsRef) {} - fn use_x_again(_: impl AsRef) {} -} - -// https://github.com/rust-lang/rust-clippy/issues/9111#issuecomment-1277114280 -mod issue_9111 { - struct A; - - impl Extend for A { - fn extend>(&mut self, _: T) { - unimplemented!() - } - } - - impl<'a> Extend<&'a u8> for A { - fn extend>(&mut self, _: T) { - unimplemented!() - } - } - - fn main() { - let mut a = A; - a.extend(&[]); // vs a.extend([]); - } -} - -mod issue_9710 { - fn main() { - let string = String::new(); - for _i in 0..10 { - f(&string); - } - } - - fn f>(_: T) {} -} - -mod issue_9739 { - fn foo(_it: impl IntoIterator) {} - - fn main() { - foo(if std::env::var_os("HI").is_some() { - &[0] - } else { - &[] as &[u32] - }); - } -} - -mod issue_9739_method_variant { - struct S; - - impl S { - fn foo(&self, _it: impl IntoIterator) {} - } - - fn main() { - S.foo(if std::env::var_os("HI").is_some() { - &[0] - } else { - &[] as &[u32] - }); - } -} - -mod issue_9782 { - fn foo>(t: T) { - println!("{}", std::mem::size_of::()); - let _t: &[u8] = t.as_ref(); - } - - fn main() { - let a: [u8; 100] = [0u8; 100]; - - // 100 - foo::<[u8; 100]>(a); - foo(a); - - // 16 - foo::<&[u8]>(&a); - foo(a.as_slice()); - - // 8 - foo::<&[u8; 100]>(&a); - foo(&a); - } -} - -mod issue_9782_type_relative_variant { - struct S; - - impl S { - fn foo>(t: T) { - println!("{}", std::mem::size_of::()); - let _t: &[u8] = t.as_ref(); - } - } - - fn main() { - let a: [u8; 100] = [0u8; 100]; - - S::foo::<&[u8; 100]>(&a); - } -} - -mod issue_9782_method_variant { - struct S; - - impl S { - fn foo>(&self, t: T) { - println!("{}", std::mem::size_of::()); - let _t: &[u8] = t.as_ref(); - } - } - - fn main() { - let a: [u8; 100] = [0u8; 100]; - - S.foo::<&[u8; 100]>(&a); - } -} - -mod issue_10535 { - static SOME_STATIC: String = String::new(); - - static UNIT: () = compute(&SOME_STATIC); - - pub const fn compute(_: T) - where - T: Copy, - { - } -} - mod issue_10253 { struct S; trait X { diff --git a/tests/ui/needless_borrow.stderr b/tests/ui/needless_borrow.stderr index 8e27014d53c3e..e91b78b0a1520 100644 --- a/tests/ui/needless_borrow.stderr +++ b/tests/ui/needless_borrow.stderr @@ -121,101 +121,17 @@ error: this expression creates a reference which is immediately dereferenced by LL | (&&5).foo(); | ^^^^^ help: change this to: `(&5)` -error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:135:51 - | -LL | let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap(); - | ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]` - -error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:136:44 - | -LL | let _ = std::path::Path::new(".").join(&&"."); - | ^^^^^ help: change this to: `"."` - -error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:137:23 - | -LL | deref_target_is_x(&X); - | ^^ help: change this to: `X` - -error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:138:26 - | -LL | multiple_constraints(&[[""]]); - | ^^^^^^^ help: change this to: `[[""]]` - -error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:139:45 - | -LL | multiple_constraints_normalizes_to_same(&X, X); - | ^^ help: change this to: `X` - -error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:140:32 - | -LL | let _ = Some("").unwrap_or(&""); - | ^^^ help: change this to: `""` - -error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:141:33 - | -LL | let _ = std::fs::write("x", &"".to_string()); - | ^^^^^^^^^^^^^^^ help: change this to: `"".to_string()` - error: this expression borrows a value the compiler would automatically borrow - --> $DIR/needless_borrow.rs:190:13 + --> $DIR/needless_borrow.rs:175:13 | LL | (&self.f)() | ^^^^^^^^^ help: change this to: `(self.f)` error: this expression borrows a value the compiler would automatically borrow - --> $DIR/needless_borrow.rs:199:13 + --> $DIR/needless_borrow.rs:184:13 | LL | (&mut self.f)() | ^^^^^^^^^^^^^ help: change this to: `(self.f)` -error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:283:20 - | -LL | takes_iter(&mut x) - | ^^^^^^ help: change this to: `x` - -error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:297:55 - | -LL | let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap(); - | ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]` - -error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:335:37 - | -LL | let _ = std::fs::write("x", &arg); - | ^^^^ help: change this to: `arg` - -error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:336:37 - | -LL | let _ = std::fs::write("x", &loc); - | ^^^^ help: change this to: `loc` - -error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:354:15 - | -LL | debug(&x); - | ^^ help: change this to: `x` - -error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:363:15 - | -LL | use_x(&x); - | ^^ help: change this to: `x` - -error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:457:13 - | -LL | foo(&a); - | ^^ help: change this to: `a` - -error: aborting due to 36 previous errors +error: aborting due to 22 previous errors diff --git a/tests/ui/needless_borrows_for_generic_args.fixed b/tests/ui/needless_borrows_for_generic_args.fixed new file mode 100644 index 0000000000000..2a335516f51c3 --- /dev/null +++ b/tests/ui/needless_borrows_for_generic_args.fixed @@ -0,0 +1,287 @@ +#![warn(clippy::needless_borrows_for_generic_args)] +#![allow( + clippy::unnecessary_to_owned, + clippy::unnecessary_literal_unwrap, + clippy::needless_borrow +)] + +use core::ops::Deref; +use std::any::Any; +use std::ffi::OsStr; +use std::fmt::{Debug, Display}; +use std::path::Path; +use std::process::Command; + +fn main() { + let _ = Command::new("ls").args(["-a", "-l"]).status().unwrap(); + let _ = Path::new(".").join("."); + let _ = Any::type_id(&""); // Don't lint. `Any` is only bound + let _ = Box::new(&""); // Don't lint. Type parameter appears in return type + let _ = Some("").unwrap_or(&""); + let _ = std::fs::write("x", "".to_string()); + + { + #[derive(Clone, Copy)] + struct X; + + impl Deref for X { + type Target = X; + fn deref(&self) -> &Self::Target { + self + } + } + + fn deref_target_is_x>(_: T) {} + + deref_target_is_x(X); + } + { + fn multiple_constraints(_: T) + where + T: IntoIterator + IntoIterator, + U: IntoIterator, + V: AsRef, + X: IntoIterator, + Y: AsRef, + { + } + + multiple_constraints([[""]]); + } + { + #[derive(Clone, Copy)] + struct X; + + impl Deref for X { + type Target = X; + fn deref(&self) -> &Self::Target { + self + } + } + + fn multiple_constraints_normalizes_to_same(_: T, _: V) + where + T: Deref, + U: Deref, + { + } + + multiple_constraints_normalizes_to_same(X, X); + } + { + fn only_sized(_: T) {} + only_sized(&""); // Don't lint. `Sized` is only bound + } + { + fn ref_as_ref_path(_: &'static T) + where + &'static T: AsRef, + { + } + + ref_as_ref_path(&""); // Don't lint. Argument type is not a type parameter + } + { + trait RefsOnly { + type Referent; + } + + impl RefsOnly for &T { + type Referent = T; + } + + fn refs_only(_: T) + where + T: RefsOnly, + { + } + + refs_only(&()); // Don't lint. `&T` implements trait, but `T` doesn't + } + { + fn multiple_constraints_normalizes_to_different(_: T, _: U) + where + T: IntoIterator, + U: IntoIterator, + V: AsRef, + { + } + multiple_constraints_normalizes_to_different(&[[""]], &[""]); // Don't lint. Projected type appears in arguments + } + // https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321 + { + #[derive(Clone, Copy)] + struct Iter; + impl Iterator for Iter { + type Item = (); + fn next(&mut self) -> Option { + None + } + } + fn takes_iter(_: impl Iterator) {} + fn dont_warn(mut x: Iter) { + takes_iter(&mut x); + } + #[allow(unused_mut)] + fn warn(mut x: &mut Iter) { + takes_iter(x) + } + } + #[clippy::msrv = "1.52.0"] + { + let _ = Command::new("ls").args(&["-a", "-l"]).status().unwrap(); + }; + #[clippy::msrv = "1.53.0"] + { + let _ = Command::new("ls").args(["-a", "-l"]).status().unwrap(); + }; + { + let env = "env".to_owned(); + let arg = "arg".to_owned(); + let f = |arg| { + let loc = "loc".to_owned(); + let _ = std::fs::write("x", &env); // Don't lint. In environment + let _ = std::fs::write("x", arg); + let _ = std::fs::write("x", loc); + }; + let _ = std::fs::write("x", &env); // Don't lint. Borrowed by `f` + f(arg); + } + { + #[derive(Debug)] + struct X; + + impl Drop for X { + fn drop(&mut self) {} + } + + fn f(_: impl Debug) {} + + let x = X; + f(&x); // Don't lint. Has significant drop + } + { + fn f(_: impl AsRef) {} + + let x = String::new(); + f(x); + } + { + fn f(_: impl AsRef) {} + fn f2(_: impl AsRef) {} + + let x = String::new(); + f(&x); + f2(&x); + } + // https://github.com/rust-lang/rust-clippy/issues/9111#issuecomment-1277114280 + // issue 9111 + { + struct A; + + impl Extend for A { + fn extend>(&mut self, _: T) { + unimplemented!() + } + } + + impl<'a> Extend<&'a u8> for A { + fn extend>(&mut self, _: T) { + unimplemented!() + } + } + + let mut a = A; + a.extend(&[]); // vs a.extend([]); + } + // issue 9710 + { + fn f(_: impl AsRef) {} + + let x = String::new(); + for _ in 0..10 { + f(&x); + } + } + // issue 9739 + { + fn foo(_it: impl IntoIterator) {} + foo(if std::env::var_os("HI").is_some() { + &[0] + } else { + &[] as &[u32] + }); + } + { + struct S; + + impl S { + fn foo(&self, _it: impl IntoIterator) {} + } + + S.foo(if std::env::var_os("HI").is_some() { + &[0] + } else { + &[] as &[u32] + }); + } + // issue 9782 + { + fn foo>(t: T) { + println!("{}", std::mem::size_of::()); + let _t: &[u8] = t.as_ref(); + } + + let a: [u8; 100] = [0u8; 100]; + + // 100 + foo::<[u8; 100]>(a); + foo(a); + + // 16 + foo::<&[u8]>(&a); + foo(a.as_slice()); + + // 8 + foo::<&[u8; 100]>(&a); + foo(a); + } + { + struct S; + + impl S { + fn foo>(t: T) { + println!("{}", std::mem::size_of::()); + let _t: &[u8] = t.as_ref(); + } + } + + let a: [u8; 100] = [0u8; 100]; + S::foo::<&[u8; 100]>(&a); + } + { + struct S; + + impl S { + fn foo>(&self, t: T) { + println!("{}", std::mem::size_of::()); + let _t: &[u8] = t.as_ref(); + } + } + + let a: [u8; 100] = [0u8; 100]; + S.foo::<&[u8; 100]>(&a); + } + // issue 10535 + { + static SOME_STATIC: String = String::new(); + + static UNIT: () = compute(&SOME_STATIC); + + pub const fn compute(_: T) + where + T: Copy, + { + } + } +} diff --git a/tests/ui/needless_borrows_for_generic_args.rs b/tests/ui/needless_borrows_for_generic_args.rs new file mode 100644 index 0000000000000..f0567f486acca --- /dev/null +++ b/tests/ui/needless_borrows_for_generic_args.rs @@ -0,0 +1,287 @@ +#![warn(clippy::needless_borrows_for_generic_args)] +#![allow( + clippy::unnecessary_to_owned, + clippy::unnecessary_literal_unwrap, + clippy::needless_borrow +)] + +use core::ops::Deref; +use std::any::Any; +use std::ffi::OsStr; +use std::fmt::{Debug, Display}; +use std::path::Path; +use std::process::Command; + +fn main() { + let _ = Command::new("ls").args(&["-a", "-l"]).status().unwrap(); + let _ = Path::new(".").join(&&"."); + let _ = Any::type_id(&""); // Don't lint. `Any` is only bound + let _ = Box::new(&""); // Don't lint. Type parameter appears in return type + let _ = Some("").unwrap_or(&""); + let _ = std::fs::write("x", &"".to_string()); + + { + #[derive(Clone, Copy)] + struct X; + + impl Deref for X { + type Target = X; + fn deref(&self) -> &Self::Target { + self + } + } + + fn deref_target_is_x>(_: T) {} + + deref_target_is_x(&X); + } + { + fn multiple_constraints(_: T) + where + T: IntoIterator + IntoIterator, + U: IntoIterator, + V: AsRef, + X: IntoIterator, + Y: AsRef, + { + } + + multiple_constraints(&[[""]]); + } + { + #[derive(Clone, Copy)] + struct X; + + impl Deref for X { + type Target = X; + fn deref(&self) -> &Self::Target { + self + } + } + + fn multiple_constraints_normalizes_to_same(_: T, _: V) + where + T: Deref, + U: Deref, + { + } + + multiple_constraints_normalizes_to_same(&X, X); + } + { + fn only_sized(_: T) {} + only_sized(&""); // Don't lint. `Sized` is only bound + } + { + fn ref_as_ref_path(_: &'static T) + where + &'static T: AsRef, + { + } + + ref_as_ref_path(&""); // Don't lint. Argument type is not a type parameter + } + { + trait RefsOnly { + type Referent; + } + + impl RefsOnly for &T { + type Referent = T; + } + + fn refs_only(_: T) + where + T: RefsOnly, + { + } + + refs_only(&()); // Don't lint. `&T` implements trait, but `T` doesn't + } + { + fn multiple_constraints_normalizes_to_different(_: T, _: U) + where + T: IntoIterator, + U: IntoIterator, + V: AsRef, + { + } + multiple_constraints_normalizes_to_different(&[[""]], &[""]); // Don't lint. Projected type appears in arguments + } + // https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321 + { + #[derive(Clone, Copy)] + struct Iter; + impl Iterator for Iter { + type Item = (); + fn next(&mut self) -> Option { + None + } + } + fn takes_iter(_: impl Iterator) {} + fn dont_warn(mut x: Iter) { + takes_iter(&mut x); + } + #[allow(unused_mut)] + fn warn(mut x: &mut Iter) { + takes_iter(&mut x) + } + } + #[clippy::msrv = "1.52.0"] + { + let _ = Command::new("ls").args(&["-a", "-l"]).status().unwrap(); + }; + #[clippy::msrv = "1.53.0"] + { + let _ = Command::new("ls").args(&["-a", "-l"]).status().unwrap(); + }; + { + let env = "env".to_owned(); + let arg = "arg".to_owned(); + let f = |arg| { + let loc = "loc".to_owned(); + let _ = std::fs::write("x", &env); // Don't lint. In environment + let _ = std::fs::write("x", &arg); + let _ = std::fs::write("x", &loc); + }; + let _ = std::fs::write("x", &env); // Don't lint. Borrowed by `f` + f(arg); + } + { + #[derive(Debug)] + struct X; + + impl Drop for X { + fn drop(&mut self) {} + } + + fn f(_: impl Debug) {} + + let x = X; + f(&x); // Don't lint. Has significant drop + } + { + fn f(_: impl AsRef) {} + + let x = String::new(); + f(&x); + } + { + fn f(_: impl AsRef) {} + fn f2(_: impl AsRef) {} + + let x = String::new(); + f(&x); + f2(&x); + } + // https://github.com/rust-lang/rust-clippy/issues/9111#issuecomment-1277114280 + // issue 9111 + { + struct A; + + impl Extend for A { + fn extend>(&mut self, _: T) { + unimplemented!() + } + } + + impl<'a> Extend<&'a u8> for A { + fn extend>(&mut self, _: T) { + unimplemented!() + } + } + + let mut a = A; + a.extend(&[]); // vs a.extend([]); + } + // issue 9710 + { + fn f(_: impl AsRef) {} + + let x = String::new(); + for _ in 0..10 { + f(&x); + } + } + // issue 9739 + { + fn foo(_it: impl IntoIterator) {} + foo(if std::env::var_os("HI").is_some() { + &[0] + } else { + &[] as &[u32] + }); + } + { + struct S; + + impl S { + fn foo(&self, _it: impl IntoIterator) {} + } + + S.foo(if std::env::var_os("HI").is_some() { + &[0] + } else { + &[] as &[u32] + }); + } + // issue 9782 + { + fn foo>(t: T) { + println!("{}", std::mem::size_of::()); + let _t: &[u8] = t.as_ref(); + } + + let a: [u8; 100] = [0u8; 100]; + + // 100 + foo::<[u8; 100]>(a); + foo(a); + + // 16 + foo::<&[u8]>(&a); + foo(a.as_slice()); + + // 8 + foo::<&[u8; 100]>(&a); + foo(&a); + } + { + struct S; + + impl S { + fn foo>(t: T) { + println!("{}", std::mem::size_of::()); + let _t: &[u8] = t.as_ref(); + } + } + + let a: [u8; 100] = [0u8; 100]; + S::foo::<&[u8; 100]>(&a); + } + { + struct S; + + impl S { + fn foo>(&self, t: T) { + println!("{}", std::mem::size_of::()); + let _t: &[u8] = t.as_ref(); + } + } + + let a: [u8; 100] = [0u8; 100]; + S.foo::<&[u8; 100]>(&a); + } + // issue 10535 + { + static SOME_STATIC: String = String::new(); + + static UNIT: () = compute(&SOME_STATIC); + + pub const fn compute(_: T) + where + T: Copy, + { + } + } +} diff --git a/tests/ui/needless_borrows_for_generic_args.stderr b/tests/ui/needless_borrows_for_generic_args.stderr new file mode 100644 index 0000000000000..e2cde2c59a6e0 --- /dev/null +++ b/tests/ui/needless_borrows_for_generic_args.stderr @@ -0,0 +1,77 @@ +error: the borrowed expression implements the required traits + --> $DIR/needless_borrows_for_generic_args.rs:16:37 + | +LL | let _ = Command::new("ls").args(&["-a", "-l"]).status().unwrap(); + | ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]` + | + = note: `-D clippy::needless-borrows-for-generic-args` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::needless_borrows_for_generic_args)]` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrows_for_generic_args.rs:17:33 + | +LL | let _ = Path::new(".").join(&&"."); + | ^^^^^ help: change this to: `"."` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrows_for_generic_args.rs:21:33 + | +LL | let _ = std::fs::write("x", &"".to_string()); + | ^^^^^^^^^^^^^^^ help: change this to: `"".to_string()` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrows_for_generic_args.rs:36:27 + | +LL | deref_target_is_x(&X); + | ^^ help: change this to: `X` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrows_for_generic_args.rs:49:30 + | +LL | multiple_constraints(&[[""]]); + | ^^^^^^^ help: change this to: `[[""]]` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrows_for_generic_args.rs:69:49 + | +LL | multiple_constraints_normalizes_to_same(&X, X); + | ^^ help: change this to: `X` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrows_for_generic_args.rs:127:24 + | +LL | takes_iter(&mut x) + | ^^^^^^ help: change this to: `x` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrows_for_generic_args.rs:136:41 + | +LL | let _ = Command::new("ls").args(&["-a", "-l"]).status().unwrap(); + | ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrows_for_generic_args.rs:144:41 + | +LL | let _ = std::fs::write("x", &arg); + | ^^^^ help: change this to: `arg` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrows_for_generic_args.rs:145:41 + | +LL | let _ = std::fs::write("x", &loc); + | ^^^^ help: change this to: `loc` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrows_for_generic_args.rs:167:11 + | +LL | f(&x); + | ^^ help: change this to: `x` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrows_for_generic_args.rs:247:13 + | +LL | foo(&a); + | ^^ help: change this to: `a` + +error: aborting due to 12 previous errors + diff --git a/tests/ui/needless_pass_by_ref_mut.rs b/tests/ui/needless_pass_by_ref_mut.rs index e1e5e8fd220b1..9cddcb3df2378 100644 --- a/tests/ui/needless_pass_by_ref_mut.rs +++ b/tests/ui/needless_pass_by_ref_mut.rs @@ -1,4 +1,4 @@ -#![allow(clippy::if_same_then_else, clippy::no_effect)] +#![allow(clippy::if_same_then_else, clippy::no_effect, clippy::redundant_closure_call)] #![feature(lint_reasons)] //@no-rustfix use std::ptr::NonNull; @@ -230,6 +230,44 @@ async fn async_vec(b: &mut Vec) { async fn async_vec2(b: &mut Vec) { b.push(true); } +fn non_mut(n: &str) {} +//Should warn +pub async fn call_in_closure1(n: &mut str) { + (|| non_mut(n))() +} +fn str_mut(str: &mut String) -> bool { + str.pop().is_some() +} +//Should not warn +pub async fn call_in_closure2(str: &mut String) { + (|| str_mut(str))(); +} + +// Should not warn. +pub async fn closure(n: &mut usize) -> impl '_ + FnMut() { + || { + *n += 1; + } +} + +// Should warn. +pub fn closure2(n: &mut usize) -> impl '_ + FnMut() -> usize { + //~^ ERROR: this argument is a mutable reference, but not used mutably + || *n + 1 +} + +// Should not warn. +pub async fn closure3(n: &mut usize) { + (|| *n += 1)(); +} + +// Should warn. +pub async fn closure4(n: &mut usize) { + //~^ ERROR: this argument is a mutable reference, but not used mutably + (|| { + let _x = *n + 1; + })(); +} fn main() { let mut u = 0; diff --git a/tests/ui/needless_pass_by_ref_mut.stderr b/tests/ui/needless_pass_by_ref_mut.stderr index df3df045776b8..0c7fbd5df6d9d 100644 --- a/tests/ui/needless_pass_by_ref_mut.stderr +++ b/tests/ui/needless_pass_by_ref_mut.stderr @@ -107,5 +107,37 @@ error: this argument is a mutable reference, but not used mutably LL | async fn inner_async3(x: &mut i32, y: &mut u32) { | ^^^^^^^^ help: consider changing to: `&i32` -error: aborting due to 17 previous errors +error: this argument is a mutable reference, but not used mutably + --> $DIR/needless_pass_by_ref_mut.rs:235:34 + | +LL | pub async fn call_in_closure1(n: &mut str) { + | ^^^^^^^^ help: consider changing to: `&str` + | + = warning: changing this function will impact semver compatibility + +error: this argument is a mutable reference, but not used mutably + --> $DIR/needless_pass_by_ref_mut.rs:247:25 + | +LL | pub async fn closure(n: &mut usize) -> impl '_ + FnMut() { + | ^^^^^^^^^^ help: consider changing to: `&usize` + | + = warning: changing this function will impact semver compatibility + +error: this argument is a mutable reference, but not used mutably + --> $DIR/needless_pass_by_ref_mut.rs:254:20 + | +LL | pub fn closure2(n: &mut usize) -> impl '_ + FnMut() -> usize { + | ^^^^^^^^^^ help: consider changing to: `&usize` + | + = warning: changing this function will impact semver compatibility + +error: this argument is a mutable reference, but not used mutably + --> $DIR/needless_pass_by_ref_mut.rs:265:26 + | +LL | pub async fn closure4(n: &mut usize) { + | ^^^^^^^^^^ help: consider changing to: `&usize` + | + = warning: changing this function will impact semver compatibility + +error: aborting due to 21 previous errors diff --git a/tests/ui/needless_raw_string_hashes.fixed b/tests/ui/needless_raw_string_hashes.fixed index e980adeeff4c1..c99c2f46532a3 100644 --- a/tests/ui/needless_raw_string_hashes.fixed +++ b/tests/ui/needless_raw_string_hashes.fixed @@ -21,4 +21,7 @@ fn main() { multiline string "; + + r"rust"; + r"hello world"; } diff --git a/tests/ui/needless_raw_string_hashes.rs b/tests/ui/needless_raw_string_hashes.rs index 6113c5f25ae64..dcc2af69f4e9c 100644 --- a/tests/ui/needless_raw_string_hashes.rs +++ b/tests/ui/needless_raw_string_hashes.rs @@ -21,4 +21,7 @@ fn main() { multiline string "#; + + r###"rust"###; + r#"hello world"#; } diff --git a/tests/ui/needless_raw_string_hashes.stderr b/tests/ui/needless_raw_string_hashes.stderr index 5a8e3d04543af..4399c6555c2c0 100644 --- a/tests/ui/needless_raw_string_hashes.stderr +++ b/tests/ui/needless_raw_string_hashes.stderr @@ -163,5 +163,29 @@ LL | string LL ~ "; | -error: aborting due to 13 previous errors +error: unnecessary hashes around raw string literal + --> $DIR/needless_raw_string_hashes.rs:25:5 + | +LL | r###"rust"###; + | ^^^^^^^^^^^^^ + | +help: remove all the hashes around the literal + | +LL - r###"rust"###; +LL + r"rust"; + | + +error: unnecessary hashes around raw string literal + --> $DIR/needless_raw_string_hashes.rs:26:5 + | +LL | r#"hello world"#; + | ^^^^^^^^^^^^^^^^ + | +help: remove all the hashes around the literal + | +LL - r#"hello world"#; +LL + r"hello world"; + | + +error: aborting due to 15 previous errors diff --git a/tests/ui/no_effect_return.rs b/tests/ui/no_effect_return.rs index f6585aa30a6f0..e46c0d73518b9 100644 --- a/tests/ui/no_effect_return.rs +++ b/tests/ui/no_effect_return.rs @@ -76,6 +76,7 @@ fn h() -> Vec { fn i() -> () { { + // does not suggest on function with explicit unit return type (); //~^ ERROR: statement with no effect } diff --git a/tests/ui/no_effect_return.stderr b/tests/ui/no_effect_return.stderr index b036e63420474..aed079f09b986 100644 --- a/tests/ui/no_effect_return.stderr +++ b/tests/ui/no_effect_return.stderr @@ -54,15 +54,13 @@ LL | ControlFlow::Break::<()>(()); | help: did you mean to return it?: `return` error: statement with no effect - --> $DIR/no_effect_return.rs:79:9 + --> $DIR/no_effect_return.rs:80:9 | LL | (); - | -^^ - | | - | help: did you mean to return it?: `return` + | ^^^ error: statement with no effect - --> $DIR/no_effect_return.rs:88:9 + --> $DIR/no_effect_return.rs:89:9 | LL | (); | ^^^ diff --git a/tests/ui/option_filter_map.fixed b/tests/ui/option_filter_map.fixed index d4c04ff907b2a..ee004c0e194bb 100644 --- a/tests/ui/option_filter_map.fixed +++ b/tests/ui/option_filter_map.fixed @@ -1,5 +1,5 @@ #![warn(clippy::option_filter_map)] -#![allow(clippy::map_flatten)] +#![allow(clippy::map_flatten, clippy::unnecessary_map_on_constructor)] fn main() { let _ = Some(Some(1)).flatten(); diff --git a/tests/ui/option_filter_map.rs b/tests/ui/option_filter_map.rs index 99fb4723cab7a..eae2fa176a890 100644 --- a/tests/ui/option_filter_map.rs +++ b/tests/ui/option_filter_map.rs @@ -1,5 +1,5 @@ #![warn(clippy::option_filter_map)] -#![allow(clippy::map_flatten)] +#![allow(clippy::map_flatten, clippy::unnecessary_map_on_constructor)] fn main() { let _ = Some(Some(1)).filter(Option::is_some).map(Option::unwrap); diff --git a/tests/ui/path_ends_with_ext.fixed b/tests/ui/path_ends_with_ext.fixed new file mode 100644 index 0000000000000..49767e242cee5 --- /dev/null +++ b/tests/ui/path_ends_with_ext.fixed @@ -0,0 +1,36 @@ +#![warn(clippy::path_ends_with_ext)] +use std::path::Path; + +macro_rules! arg { + () => { + ".md" + }; +} + +fn test(path: &Path) { + path.extension().is_some_and(|ext| ext == "md"); + //~^ ERROR: this looks like a failed attempt at checking for the file extension + + // some "extensions" are allowed by default + path.ends_with(".git"); + + // most legitimate "dotfiles" are longer than 3 chars, so we allow them as well + path.ends_with(".bashrc"); + + // argument from expn shouldn't trigger + path.ends_with(arg!()); + + path.ends_with(".."); + path.ends_with("./a"); + path.ends_with("."); + path.ends_with(""); +} + +// is_some_and was stabilized in 1.70, so suggest map_or(false, ..) if under that +#[clippy::msrv = "1.69"] +fn under_msv(path: &Path) -> bool { + path.extension().map_or(false, |ext| ext == "md") + //~^ ERROR: this looks like a failed attempt at checking for the file extension +} + +fn main() {} diff --git a/tests/ui/path_ends_with_ext.rs b/tests/ui/path_ends_with_ext.rs new file mode 100644 index 0000000000000..2dfd046218afa --- /dev/null +++ b/tests/ui/path_ends_with_ext.rs @@ -0,0 +1,36 @@ +#![warn(clippy::path_ends_with_ext)] +use std::path::Path; + +macro_rules! arg { + () => { + ".md" + }; +} + +fn test(path: &Path) { + path.ends_with(".md"); + //~^ ERROR: this looks like a failed attempt at checking for the file extension + + // some "extensions" are allowed by default + path.ends_with(".git"); + + // most legitimate "dotfiles" are longer than 3 chars, so we allow them as well + path.ends_with(".bashrc"); + + // argument from expn shouldn't trigger + path.ends_with(arg!()); + + path.ends_with(".."); + path.ends_with("./a"); + path.ends_with("."); + path.ends_with(""); +} + +// is_some_and was stabilized in 1.70, so suggest map_or(false, ..) if under that +#[clippy::msrv = "1.69"] +fn under_msv(path: &Path) -> bool { + path.ends_with(".md") + //~^ ERROR: this looks like a failed attempt at checking for the file extension +} + +fn main() {} diff --git a/tests/ui/path_ends_with_ext.stderr b/tests/ui/path_ends_with_ext.stderr new file mode 100644 index 0000000000000..a73ab4d08e9c5 --- /dev/null +++ b/tests/ui/path_ends_with_ext.stderr @@ -0,0 +1,17 @@ +error: this looks like a failed attempt at checking for the file extension + --> $DIR/path_ends_with_ext.rs:11:5 + | +LL | path.ends_with(".md"); + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `path.extension().is_some_and(|ext| ext == "md")` + | + = note: `-D clippy::path-ends-with-ext` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::path_ends_with_ext)]` + +error: this looks like a failed attempt at checking for the file extension + --> $DIR/path_ends_with_ext.rs:32:5 + | +LL | path.ends_with(".md") + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `path.extension().map_or(false, |ext| ext == "md")` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/redundant_allocation.rs b/tests/ui/redundant_allocation.rs index b3257c04f8296..e70f8e71fae85 100644 --- a/tests/ui/redundant_allocation.rs +++ b/tests/ui/redundant_allocation.rs @@ -159,4 +159,9 @@ mod box_fat_ptr { //~| NOTE: `Box>` is already on the heap, `Rc>>` makes } +// https://github.com/rust-lang/rust-clippy/issues/11417 +fn type_in_closure() { + let _ = |_: &mut Box>| {}; +} + fn main() {} diff --git a/tests/ui/redundant_as_str.fixed b/tests/ui/redundant_as_str.fixed new file mode 100644 index 0000000000000..a38523a7c79e7 --- /dev/null +++ b/tests/ui/redundant_as_str.fixed @@ -0,0 +1,24 @@ +#![warn(clippy::redundant_as_str)] + +fn main() { + let string = "Hello, world!".to_owned(); + + // These methods are redundant and the `as_str` can be removed + let _redundant = string.as_bytes(); + let _redundant = string.is_empty(); + + // These methods don't use `as_str` when they are redundant + let _no_as_str = string.as_bytes(); + let _no_as_str = string.is_empty(); + + // These methods are not redundant, and are equivelant to + // doing dereferencing the string and applying the method + let _not_redundant = string.as_str().escape_unicode(); + let _not_redundant = string.as_str().trim(); + let _not_redundant = string.as_str().split_whitespace(); + + // These methods don't use `as_str` and are applied on a `str` directly + let borrowed_str = "Hello, world!"; + let _is_str = borrowed_str.as_bytes(); + let _is_str = borrowed_str.is_empty(); +} diff --git a/tests/ui/redundant_as_str.rs b/tests/ui/redundant_as_str.rs new file mode 100644 index 0000000000000..33adb60999641 --- /dev/null +++ b/tests/ui/redundant_as_str.rs @@ -0,0 +1,24 @@ +#![warn(clippy::redundant_as_str)] + +fn main() { + let string = "Hello, world!".to_owned(); + + // These methods are redundant and the `as_str` can be removed + let _redundant = string.as_str().as_bytes(); + let _redundant = string.as_str().is_empty(); + + // These methods don't use `as_str` when they are redundant + let _no_as_str = string.as_bytes(); + let _no_as_str = string.is_empty(); + + // These methods are not redundant, and are equivelant to + // doing dereferencing the string and applying the method + let _not_redundant = string.as_str().escape_unicode(); + let _not_redundant = string.as_str().trim(); + let _not_redundant = string.as_str().split_whitespace(); + + // These methods don't use `as_str` and are applied on a `str` directly + let borrowed_str = "Hello, world!"; + let _is_str = borrowed_str.as_bytes(); + let _is_str = borrowed_str.is_empty(); +} diff --git a/tests/ui/redundant_as_str.stderr b/tests/ui/redundant_as_str.stderr new file mode 100644 index 0000000000000..0ea42a94a81ed --- /dev/null +++ b/tests/ui/redundant_as_str.stderr @@ -0,0 +1,17 @@ +error: this `as_str` is redundant and can be removed as the method immediately following exists on `String` too + --> $DIR/redundant_as_str.rs:7:29 + | +LL | let _redundant = string.as_str().as_bytes(); + | ^^^^^^^^^^^^^^^^^ help: try: `as_bytes` + | + = note: `-D clippy::redundant-as-str` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::redundant_as_str)]` + +error: this `as_str` is redundant and can be removed as the method immediately following exists on `String` too + --> $DIR/redundant_as_str.rs:8:29 + | +LL | let _redundant = string.as_str().is_empty(); + | ^^^^^^^^^^^^^^^^^ help: try: `is_empty` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/redundant_field_names.fixed b/tests/ui/redundant_field_names.fixed index bbe3b38e547bb..c578e786426f8 100644 --- a/tests/ui/redundant_field_names.fixed +++ b/tests/ui/redundant_field_names.fixed @@ -1,8 +1,9 @@ +//@aux-build:proc_macros.rs #![warn(clippy::redundant_field_names)] #![allow(clippy::extra_unused_type_parameters, clippy::no_effect, dead_code, unused_variables)] #[macro_use] -extern crate derive_new; +extern crate proc_macros; use std::ops::{Range, RangeFrom, RangeInclusive, RangeTo, RangeToInclusive}; @@ -18,7 +19,6 @@ struct Person { foo: u8, } -#[derive(new)] pub struct S { v: String, } @@ -57,6 +57,13 @@ fn main() { let _ = Range { start, end }; let _ = RangeInclusive::new(start, end); let _ = RangeToInclusive { end }; + + external! { + let v = String::new(); + let _ = S { + v: v + }; + } } fn issue_3476() { diff --git a/tests/ui/redundant_field_names.rs b/tests/ui/redundant_field_names.rs index 9afa191ce7c70..d8c2286d5ad65 100644 --- a/tests/ui/redundant_field_names.rs +++ b/tests/ui/redundant_field_names.rs @@ -1,8 +1,9 @@ +//@aux-build:proc_macros.rs #![warn(clippy::redundant_field_names)] #![allow(clippy::extra_unused_type_parameters, clippy::no_effect, dead_code, unused_variables)] #[macro_use] -extern crate derive_new; +extern crate proc_macros; use std::ops::{Range, RangeFrom, RangeInclusive, RangeTo, RangeToInclusive}; @@ -18,7 +19,6 @@ struct Person { foo: u8, } -#[derive(new)] pub struct S { v: String, } @@ -57,6 +57,13 @@ fn main() { let _ = Range { start: start, end: end }; let _ = RangeInclusive::new(start, end); let _ = RangeToInclusive { end: end }; + + external! { + let v = String::new(); + let _ = S { + v: v + }; + } } fn issue_3476() { diff --git a/tests/ui/redundant_field_names.stderr b/tests/ui/redundant_field_names.stderr index 5fee60b8ea411..6eb1cc7531928 100644 --- a/tests/ui/redundant_field_names.stderr +++ b/tests/ui/redundant_field_names.stderr @@ -44,7 +44,7 @@ LL | let _ = RangeToInclusive { end: end }; | ^^^^^^^^ help: replace it with: `end` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:81:25 + --> $DIR/redundant_field_names.rs:88:25 | LL | let _ = RangeFrom { start: start }; | ^^^^^^^^^^^^ help: replace it with: `start` diff --git a/tests/ui/redundant_guards.fixed b/tests/ui/redundant_guards.fixed index 9a1ec3a4d36a6..f23116a7e1c5c 100644 --- a/tests/ui/redundant_guards.fixed +++ b/tests/ui/redundant_guards.fixed @@ -43,6 +43,7 @@ fn main() { }, Some(Some(1)) => .., Some(Some(2)) => .., + Some(Some(2)) => .., // Don't lint, since x is used in the body Some(x) if let Some(1) = x => { x; @@ -56,11 +57,13 @@ fn main() { Some(x) if matches!(y, 1 if true) => .., Some(x) if let 1 = y => .., Some(x) if y == 2 => .., + Some(x) if 2 == y => .., _ => todo!(), }; let a = A(1); match a { _ if a.0 == 1 => {}, + _ if 1 == a.0 => {}, _ => todo!(), } let b = B { e: Some(A(0)) }; @@ -119,6 +122,7 @@ fn h(v: Option) { fn f(s: Option) { match s { Some(x) if x == "a" => {}, + Some(x) if "a" == x => {}, _ => {}, } } @@ -140,6 +144,52 @@ static CONST_S: S = S { a: 1 }; fn g(opt_s: Option) { match opt_s { Some(x) if x == CONST_S => {}, + Some(x) if CONST_S == x => {}, _ => {}, } } + +mod issue11465 { + enum A { + Foo([u8; 3]), + } + + struct B { + b: String, + c: i32, + } + + fn issue11465() { + let c = Some(1); + match c { + Some(1) => {}, + Some(1) => {}, + Some(2) => {}, + Some(3) => {}, + _ => {}, + }; + + let enum_a = A::Foo([98, 97, 114]); + match enum_a { + A::Foo(ref arr) if arr == b"foo" => {}, + A::Foo(ref arr) if b"foo" == arr => {}, + A::Foo(ref arr) if let b"bar" = arr => {}, + A::Foo(ref arr) if matches!(arr, b"baz") => {}, + _ => {}, + }; + + let struct_b = B { + b: "bar".to_string(), + c: 42, + }; + match struct_b { + B { ref b, .. } if b == "bar" => {}, + B { ref b, .. } if "bar" == b => {}, + B { c: 1, .. } => {}, + B { c: 1, .. } => {}, + B { c: 1, .. } => {}, + B { c: 1, .. } => {}, + _ => {}, + } + } +} diff --git a/tests/ui/redundant_guards.rs b/tests/ui/redundant_guards.rs index e2e0ee816c51b..c0206b4cec75f 100644 --- a/tests/ui/redundant_guards.rs +++ b/tests/ui/redundant_guards.rs @@ -43,6 +43,7 @@ fn main() { }, Some(x) if let Some(1) = x => .., Some(x) if x == Some(2) => .., + Some(x) if Some(2) == x => .., // Don't lint, since x is used in the body Some(x) if let Some(1) = x => { x; @@ -56,11 +57,13 @@ fn main() { Some(x) if matches!(y, 1 if true) => .., Some(x) if let 1 = y => .., Some(x) if y == 2 => .., + Some(x) if 2 == y => .., _ => todo!(), }; let a = A(1); match a { _ if a.0 == 1 => {}, + _ if 1 == a.0 => {}, _ => todo!(), } let b = B { e: Some(A(0)) }; @@ -119,6 +122,7 @@ fn h(v: Option) { fn f(s: Option) { match s { Some(x) if x == "a" => {}, + Some(x) if "a" == x => {}, _ => {}, } } @@ -140,6 +144,52 @@ static CONST_S: S = S { a: 1 }; fn g(opt_s: Option) { match opt_s { Some(x) if x == CONST_S => {}, + Some(x) if CONST_S == x => {}, _ => {}, } } + +mod issue11465 { + enum A { + Foo([u8; 3]), + } + + struct B { + b: String, + c: i32, + } + + fn issue11465() { + let c = Some(1); + match c { + Some(ref x) if x == &1 => {}, + Some(ref x) if &1 == x => {}, + Some(ref x) if let &2 = x => {}, + Some(ref x) if matches!(x, &3) => {}, + _ => {}, + }; + + let enum_a = A::Foo([98, 97, 114]); + match enum_a { + A::Foo(ref arr) if arr == b"foo" => {}, + A::Foo(ref arr) if b"foo" == arr => {}, + A::Foo(ref arr) if let b"bar" = arr => {}, + A::Foo(ref arr) if matches!(arr, b"baz") => {}, + _ => {}, + }; + + let struct_b = B { + b: "bar".to_string(), + c: 42, + }; + match struct_b { + B { ref b, .. } if b == "bar" => {}, + B { ref b, .. } if "bar" == b => {}, + B { ref c, .. } if c == &1 => {}, + B { ref c, .. } if &1 == c => {}, + B { ref c, .. } if let &1 = c => {}, + B { ref c, .. } if matches!(c, &1) => {}, + _ => {}, + } + } +} diff --git a/tests/ui/redundant_guards.stderr b/tests/ui/redundant_guards.stderr index 0a45a6d7619fe..b8d7834e358ca 100644 --- a/tests/ui/redundant_guards.stderr +++ b/tests/ui/redundant_guards.stderr @@ -60,7 +60,19 @@ LL + Some(Some(2)) => .., | error: redundant guard - --> $DIR/redundant_guards.rs:68:20 + --> $DIR/redundant_guards.rs:46:20 + | +LL | Some(x) if Some(2) == x => .., + | ^^^^^^^^^^^^ + | +help: try + | +LL - Some(x) if Some(2) == x => .., +LL + Some(Some(2)) => .., + | + +error: redundant guard + --> $DIR/redundant_guards.rs:71:20 | LL | B { e } if matches!(e, Some(A(2))) => .., | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -72,7 +84,7 @@ LL + B { e: Some(A(2)) } => .., | error: redundant guard - --> $DIR/redundant_guards.rs:105:20 + --> $DIR/redundant_guards.rs:108:20 | LL | E::A(y) if y == "not from an or pattern" => {}, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -84,7 +96,7 @@ LL + E::A("not from an or pattern") => {}, | error: redundant guard - --> $DIR/redundant_guards.rs:112:14 + --> $DIR/redundant_guards.rs:115:14 | LL | x if matches!(x, Some(0)) => .., | ^^^^^^^^^^^^^^^^^^^^ @@ -95,5 +107,101 @@ LL - x if matches!(x, Some(0)) => .., LL + Some(0) => .., | -error: aborting due to 8 previous errors +error: redundant guard + --> $DIR/redundant_guards.rs:165:28 + | +LL | Some(ref x) if x == &1 => {}, + | ^^^^^^^ + | +help: try + | +LL - Some(ref x) if x == &1 => {}, +LL + Some(1) => {}, + | + +error: redundant guard + --> $DIR/redundant_guards.rs:166:28 + | +LL | Some(ref x) if &1 == x => {}, + | ^^^^^^^ + | +help: try + | +LL - Some(ref x) if &1 == x => {}, +LL + Some(1) => {}, + | + +error: redundant guard + --> $DIR/redundant_guards.rs:167:28 + | +LL | Some(ref x) if let &2 = x => {}, + | ^^^^^^^^^^ + | +help: try + | +LL - Some(ref x) if let &2 = x => {}, +LL + Some(2) => {}, + | + +error: redundant guard + --> $DIR/redundant_guards.rs:168:28 + | +LL | Some(ref x) if matches!(x, &3) => {}, + | ^^^^^^^^^^^^^^^ + | +help: try + | +LL - Some(ref x) if matches!(x, &3) => {}, +LL + Some(3) => {}, + | + +error: redundant guard + --> $DIR/redundant_guards.rs:188:32 + | +LL | B { ref c, .. } if c == &1 => {}, + | ^^^^^^^ + | +help: try + | +LL - B { ref c, .. } if c == &1 => {}, +LL + B { c: 1, .. } => {}, + | + +error: redundant guard + --> $DIR/redundant_guards.rs:189:32 + | +LL | B { ref c, .. } if &1 == c => {}, + | ^^^^^^^ + | +help: try + | +LL - B { ref c, .. } if &1 == c => {}, +LL + B { c: 1, .. } => {}, + | + +error: redundant guard + --> $DIR/redundant_guards.rs:190:32 + | +LL | B { ref c, .. } if let &1 = c => {}, + | ^^^^^^^^^^ + | +help: try + | +LL - B { ref c, .. } if let &1 = c => {}, +LL + B { c: 1, .. } => {}, + | + +error: redundant guard + --> $DIR/redundant_guards.rs:191:32 + | +LL | B { ref c, .. } if matches!(c, &1) => {}, + | ^^^^^^^^^^^^^^^ + | +help: try + | +LL - B { ref c, .. } if matches!(c, &1) => {}, +LL + B { c: 1, .. } => {}, + | + +error: aborting due to 17 previous errors diff --git a/tests/ui/regex.rs b/tests/ui/regex.rs index 5259d9ce04bbf..094d9574ae987 100644 --- a/tests/ui/regex.rs +++ b/tests/ui/regex.rs @@ -2,7 +2,8 @@ unused, clippy::needless_raw_strings, clippy::needless_raw_string_hashes, - clippy::needless_borrow + clippy::needless_borrow, + clippy::needless_borrows_for_generic_args )] #![warn(clippy::invalid_regex, clippy::trivial_regex)] diff --git a/tests/ui/regex.stderr b/tests/ui/regex.stderr index 91f90157e6847..6d98d691d6f0a 100644 --- a/tests/ui/regex.stderr +++ b/tests/ui/regex.stderr @@ -1,5 +1,5 @@ error: trivial regex - --> $DIR/regex.rs:18:45 + --> $DIR/regex.rs:19:45 | LL | let pipe_in_wrong_position = Regex::new("|"); | ^^^ @@ -9,7 +9,7 @@ LL | let pipe_in_wrong_position = Regex::new("|"); = help: to override `-D warnings` add `#[allow(clippy::trivial_regex)]` error: trivial regex - --> $DIR/regex.rs:20:60 + --> $DIR/regex.rs:21:60 | LL | let pipe_in_wrong_position_builder = RegexBuilder::new("|"); | ^^^ @@ -17,7 +17,7 @@ LL | let pipe_in_wrong_position_builder = RegexBuilder::new("|"); = help: the regex is unlikely to be useful as it is error: regex syntax error: invalid character class range, the start must be <= the end - --> $DIR/regex.rs:22:42 + --> $DIR/regex.rs:23:42 | LL | let wrong_char_ranice = Regex::new("[z-a]"); | ^^^ @@ -26,7 +26,7 @@ LL | let wrong_char_ranice = Regex::new("[z-a]"); = help: to override `-D warnings` add `#[allow(clippy::invalid_regex)]` error: regex syntax error: invalid character class range, the start must be <= the end - --> $DIR/regex.rs:25:37 + --> $DIR/regex.rs:26:37 | LL | let some_unicode = Regex::new("[é-è]"); | ^^^ @@ -35,13 +35,13 @@ error: regex parse error: ( ^ error: unclosed group - --> $DIR/regex.rs:28:33 + --> $DIR/regex.rs:29:33 | LL | let some_regex = Regex::new(OPENING_PAREN); | ^^^^^^^^^^^^^ error: trivial regex - --> $DIR/regex.rs:30:53 + --> $DIR/regex.rs:31:53 | LL | let binary_pipe_in_wrong_position = BRegex::new("|"); | ^^^ @@ -52,7 +52,7 @@ error: regex parse error: ( ^ error: unclosed group - --> $DIR/regex.rs:32:41 + --> $DIR/regex.rs:33:41 | LL | let some_binary_regex = BRegex::new(OPENING_PAREN); | ^^^^^^^^^^^^^ @@ -61,7 +61,7 @@ error: regex parse error: ( ^ error: unclosed group - --> $DIR/regex.rs:33:56 + --> $DIR/regex.rs:34:56 | LL | let some_binary_regex_builder = BRegexBuilder::new(OPENING_PAREN); | ^^^^^^^^^^^^^ @@ -70,7 +70,7 @@ error: regex parse error: ( ^ error: unclosed group - --> $DIR/regex.rs:45:37 + --> $DIR/regex.rs:46:37 | LL | let set_error = RegexSet::new(&[OPENING_PAREN, r"[a-z]+\.(com|org|net)"]); | ^^^^^^^^^^^^^ @@ -79,7 +79,7 @@ error: regex parse error: ( ^ error: unclosed group - --> $DIR/regex.rs:46:39 + --> $DIR/regex.rs:47:39 | LL | let bset_error = BRegexSet::new(&[OPENING_PAREN, r"[a-z]+\.(com|org|net)"]); | ^^^^^^^^^^^^^ @@ -88,7 +88,7 @@ error: regex parse error: \b\c ^^ error: unrecognized escape sequence - --> $DIR/regex.rs:53:42 + --> $DIR/regex.rs:54:42 | LL | let escaped_string_span = Regex::new("\\b\\c"); | ^^^^^^^^ @@ -96,19 +96,19 @@ LL | let escaped_string_span = Regex::new("\\b\\c"); = help: consider using a raw string literal: `r".."` error: regex syntax error: duplicate flag - --> $DIR/regex.rs:55:34 + --> $DIR/regex.rs:56:34 | LL | let aux_span = Regex::new("(?ixi)"); | ^ ^ error: regex syntax error: pattern can match invalid UTF-8 - --> $DIR/regex.rs:61:53 + --> $DIR/regex.rs:62:53 | LL | let invalid_utf8_should_lint = Regex::new("(?-u)."); | ^ error: trivial regex - --> $DIR/regex.rs:66:33 + --> $DIR/regex.rs:67:33 | LL | let trivial_eq = Regex::new("^foobar$"); | ^^^^^^^^^^ @@ -116,7 +116,7 @@ LL | let trivial_eq = Regex::new("^foobar$"); = help: consider using `==` on `str`s error: trivial regex - --> $DIR/regex.rs:69:48 + --> $DIR/regex.rs:70:48 | LL | let trivial_eq_builder = RegexBuilder::new("^foobar$"); | ^^^^^^^^^^ @@ -124,7 +124,7 @@ LL | let trivial_eq_builder = RegexBuilder::new("^foobar$"); = help: consider using `==` on `str`s error: trivial regex - --> $DIR/regex.rs:72:42 + --> $DIR/regex.rs:73:42 | LL | let trivial_starts_with = Regex::new("^foobar"); | ^^^^^^^^^ @@ -132,7 +132,7 @@ LL | let trivial_starts_with = Regex::new("^foobar"); = help: consider using `str::starts_with` error: trivial regex - --> $DIR/regex.rs:75:40 + --> $DIR/regex.rs:76:40 | LL | let trivial_ends_with = Regex::new("foobar$"); | ^^^^^^^^^ @@ -140,7 +140,7 @@ LL | let trivial_ends_with = Regex::new("foobar$"); = help: consider using `str::ends_with` error: trivial regex - --> $DIR/regex.rs:78:39 + --> $DIR/regex.rs:79:39 | LL | let trivial_contains = Regex::new("foobar"); | ^^^^^^^^ @@ -148,7 +148,7 @@ LL | let trivial_contains = Regex::new("foobar"); = help: consider using `str::contains` error: trivial regex - --> $DIR/regex.rs:81:39 + --> $DIR/regex.rs:82:39 | LL | let trivial_contains = Regex::new(NOT_A_REAL_REGEX); | ^^^^^^^^^^^^^^^^ @@ -156,7 +156,7 @@ LL | let trivial_contains = Regex::new(NOT_A_REAL_REGEX); = help: consider using `str::contains` error: trivial regex - --> $DIR/regex.rs:84:40 + --> $DIR/regex.rs:85:40 | LL | let trivial_backslash = Regex::new("a\\.b"); | ^^^^^^^ @@ -164,7 +164,7 @@ LL | let trivial_backslash = Regex::new("a\\.b"); = help: consider using `str::contains` error: trivial regex - --> $DIR/regex.rs:88:36 + --> $DIR/regex.rs:89:36 | LL | let trivial_empty = Regex::new(""); | ^^ @@ -172,7 +172,7 @@ LL | let trivial_empty = Regex::new(""); = help: the regex is unlikely to be useful as it is error: trivial regex - --> $DIR/regex.rs:91:36 + --> $DIR/regex.rs:92:36 | LL | let trivial_empty = Regex::new("^"); | ^^^ @@ -180,7 +180,7 @@ LL | let trivial_empty = Regex::new("^"); = help: the regex is unlikely to be useful as it is error: trivial regex - --> $DIR/regex.rs:94:36 + --> $DIR/regex.rs:95:36 | LL | let trivial_empty = Regex::new("^$"); | ^^^^ @@ -188,7 +188,7 @@ LL | let trivial_empty = Regex::new("^$"); = help: consider using `str::is_empty` error: trivial regex - --> $DIR/regex.rs:97:44 + --> $DIR/regex.rs:98:44 | LL | let binary_trivial_empty = BRegex::new("^$"); | ^^^^ diff --git a/tests/ui/result_map_unit_fn_unfixable.rs b/tests/ui/result_map_unit_fn_unfixable.rs index a4dfc8f293d3f..62798b6d3d6f3 100644 --- a/tests/ui/result_map_unit_fn_unfixable.rs +++ b/tests/ui/result_map_unit_fn_unfixable.rs @@ -1,6 +1,6 @@ #![warn(clippy::result_map_unit_fn)] #![feature(never_type)] -#![allow(unused)] +#![allow(unused, clippy::unnecessary_map_on_constructor)] //@no-rustfix struct HasResult { field: Result, diff --git a/tests/ui/transmute_null_to_fn.rs b/tests/ui/transmute_null_to_fn.rs index 7d780c803ffd5..b07851e864f68 100644 --- a/tests/ui/transmute_null_to_fn.rs +++ b/tests/ui/transmute_null_to_fn.rs @@ -25,6 +25,17 @@ fn transmute_const() { } } +fn issue_11485() { + unsafe { + let _: fn() = std::mem::transmute(0 as *const u8 as *const ()); + //~^ ERROR: transmuting a known null pointer into a function pointer + let _: fn() = std::mem::transmute(std::ptr::null::<()>() as *const u8); + //~^ ERROR: transmuting a known null pointer into a function pointer + let _: fn() = std::mem::transmute(ZPTR as *const u8); + //~^ ERROR: transmuting a known null pointer into a function pointer + } +} + fn main() { one_liners(); transmute_const(); diff --git a/tests/ui/transmute_null_to_fn.stderr b/tests/ui/transmute_null_to_fn.stderr index ab0ac0dd480bc..9073080cbf3c6 100644 --- a/tests/ui/transmute_null_to_fn.stderr +++ b/tests/ui/transmute_null_to_fn.stderr @@ -24,5 +24,29 @@ LL | let _: fn() = std::mem::transmute(ZPTR); | = help: try wrapping your function pointer type in `Option` instead, and using `None` as a null pointer value -error: aborting due to 3 previous errors +error: transmuting a known null pointer into a function pointer + --> $DIR/transmute_null_to_fn.rs:30:23 + | +LL | let _: fn() = std::mem::transmute(0 as *const u8 as *const ()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this transmute results in undefined behavior + | + = help: try wrapping your function pointer type in `Option` instead, and using `None` as a null pointer value + +error: transmuting a known null pointer into a function pointer + --> $DIR/transmute_null_to_fn.rs:32:23 + | +LL | let _: fn() = std::mem::transmute(std::ptr::null::<()>() as *const u8); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this transmute results in undefined behavior + | + = help: try wrapping your function pointer type in `Option` instead, and using `None` as a null pointer value + +error: transmuting a known null pointer into a function pointer + --> $DIR/transmute_null_to_fn.rs:34:23 + | +LL | let _: fn() = std::mem::transmute(ZPTR as *const u8); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this transmute results in undefined behavior + | + = help: try wrapping your function pointer type in `Option` instead, and using `None` as a null pointer value + +error: aborting due to 6 previous errors diff --git a/tests/ui/undocumented_unsafe_blocks.rs b/tests/ui/undocumented_unsafe_blocks.rs deleted file mode 100644 index f4e7f1943ae6d..0000000000000 --- a/tests/ui/undocumented_unsafe_blocks.rs +++ /dev/null @@ -1,534 +0,0 @@ -//@aux-build:proc_macro_unsafe.rs - -#![warn(clippy::undocumented_unsafe_blocks, clippy::unnecessary_safety_comment)] -#![allow(clippy::let_unit_value, clippy::missing_safety_doc)] - -extern crate proc_macro_unsafe; - -// Valid comments - -fn nested_local() { - let _ = { - let _ = { - // SAFETY: - let _ = unsafe {}; - }; - }; -} - -fn deep_nest() { - let _ = { - let _ = { - // SAFETY: - let _ = unsafe {}; - - // Safety: - unsafe {}; - - let _ = { - let _ = { - let _ = { - let _ = { - let _ = { - // Safety: - let _ = unsafe {}; - - // SAFETY: - unsafe {}; - }; - }; - }; - - // Safety: - unsafe {}; - }; - }; - }; - - // Safety: - unsafe {}; - }; - - // SAFETY: - unsafe {}; -} - -fn local_tuple_expression() { - // Safety: - let _ = (42, unsafe {}); -} - -fn line_comment() { - // Safety: - unsafe {} -} - -fn line_comment_newlines() { - // SAFETY: - - unsafe {} -} - -fn line_comment_empty() { - // Safety: - // - // - // - unsafe {} -} - -fn line_comment_with_extras() { - // This is a description - // Safety: - unsafe {} -} - -fn block_comment() { - /* Safety: */ - unsafe {} -} - -fn block_comment_newlines() { - /* SAFETY: */ - - unsafe {} -} - -fn block_comment_with_extras() { - /* This is a description - * SAFETY: - */ - unsafe {} -} - -fn block_comment_terminator_same_line() { - /* This is a description - * Safety: */ - unsafe {} -} - -fn buried_safety() { - // Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor - // incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation - // ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in - // reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint - // occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est - // laborum. Safety: - // Tellus elementum sagittis vitae et leo duis ut diam quam. Sit amet nulla facilisi - // morbi tempus iaculis urna. Amet luctus venenatis lectus magna. At quis risus sed vulputate odio - // ut. Luctus venenatis lectus magna fringilla urna. Tortor id aliquet lectus proin nibh nisl - // condimentum id venenatis. Vulputate dignissim suspendisse in est ante in nibh mauris cursus. - unsafe {} -} - -fn safety_with_prepended_text() { - // This is a test. safety: - unsafe {} -} - -fn local_line_comment() { - // Safety: - let _ = unsafe {}; -} - -fn local_block_comment() { - /* SAFETY: */ - let _ = unsafe {}; -} - -fn comment_array() { - // Safety: - let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; -} - -fn comment_tuple() { - // sAFETY: - let _ = (42, unsafe {}, "test", unsafe {}); -} - -fn comment_unary() { - // SAFETY: - let _ = *unsafe { &42 }; -} - -#[allow(clippy::match_single_binding)] -fn comment_match() { - // SAFETY: - let _ = match unsafe {} { - _ => {}, - }; -} - -fn comment_addr_of() { - // Safety: - let _ = &unsafe {}; -} - -fn comment_repeat() { - // Safety: - let _ = [unsafe {}; 5]; -} - -fn comment_macro_call() { - macro_rules! t { - ($b:expr) => { - $b - }; - } - - t!( - // SAFETY: - unsafe {} - ); -} - -fn comment_macro_def() { - macro_rules! t { - () => { - // Safety: - unsafe {} - }; - } - - t!(); -} - -fn non_ascii_comment() { - // ॐ᧻໒ SaFeTy: ௵∰ - unsafe {}; -} - -fn local_commented_block() { - let _ = - // safety: - unsafe {}; -} - -fn local_nest() { - // safety: - let _ = [(42, unsafe {}, unsafe {}), (52, unsafe {}, unsafe {})]; -} - -fn in_fn_call(x: *const u32) { - fn f(x: u32) {} - - // Safety: reason - f(unsafe { *x }); -} - -fn multi_in_fn_call(x: *const u32) { - fn f(x: u32, y: u32) {} - - // Safety: reason - f(unsafe { *x }, unsafe { *x }); -} - -fn in_multiline_fn_call(x: *const u32) { - fn f(x: u32, y: u32) {} - - f( - // Safety: reason - unsafe { *x }, - 0, - ); -} - -fn in_macro_call(x: *const u32) { - // Safety: reason - println!("{}", unsafe { *x }); -} - -fn in_multiline_macro_call(x: *const u32) { - println!( - "{}", - // Safety: reason - unsafe { *x }, - ); -} - -fn from_proc_macro() { - proc_macro_unsafe::unsafe_block!(token); -} - -fn in_closure(x: *const u32) { - // Safety: reason - let _ = || unsafe { *x }; -} - -// Invalid comments - -#[rustfmt::skip] -fn inline_block_comment() { - /* Safety: */ unsafe {} -} - -fn no_comment() { - unsafe {} -} - -fn no_comment_array() { - let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; -} - -fn no_comment_tuple() { - let _ = (42, unsafe {}, "test", unsafe {}); -} - -fn no_comment_unary() { - let _ = *unsafe { &42 }; -} - -#[allow(clippy::match_single_binding)] -fn no_comment_match() { - let _ = match unsafe {} { - _ => {}, - }; -} - -fn no_comment_addr_of() { - let _ = &unsafe {}; -} - -fn no_comment_repeat() { - let _ = [unsafe {}; 5]; -} - -fn local_no_comment() { - let _ = unsafe {}; -} - -fn no_comment_macro_call() { - macro_rules! t { - ($b:expr) => { - $b - }; - } - - t!(unsafe {}); -} - -fn no_comment_macro_def() { - macro_rules! t { - () => { - unsafe {} - }; - } - - t!(); -} - -fn trailing_comment() { - unsafe {} // SAFETY: -} - -fn internal_comment() { - unsafe { - // SAFETY: - } -} - -fn interference() { - // SAFETY - - let _ = 42; - - unsafe {}; -} - -pub fn print_binary_tree() { - println!("{}", unsafe { String::from_utf8_unchecked(vec![]) }); -} - -mod unsafe_impl_smoke_test { - unsafe trait A {} - - // error: no safety comment - unsafe impl A for () {} - - // Safety: ok - unsafe impl A for (i32) {} - - mod sub_mod { - // error: - unsafe impl B for (u32) {} - unsafe trait B {} - } - - #[rustfmt::skip] - mod sub_mod2 { - // - // SAFETY: ok - // - - unsafe impl B for (u32) {} - unsafe trait B {} - } -} - -mod unsafe_impl_from_macro { - unsafe trait T {} - - // error - macro_rules! no_safety_comment { - ($t:ty) => { - unsafe impl T for $t {} - }; - } - - // ok - no_safety_comment!(()); - - // ok - macro_rules! with_safety_comment { - ($t:ty) => { - // SAFETY: - unsafe impl T for $t {} - }; - } - - // ok - with_safety_comment!((i32)); -} - -mod unsafe_impl_macro_and_not_macro { - unsafe trait T {} - - // error - macro_rules! no_safety_comment { - ($t:ty) => { - unsafe impl T for $t {} - }; - } - - // ok - no_safety_comment!(()); - - // error - unsafe impl T for (i32) {} - - // ok - no_safety_comment!(u32); - - // error - unsafe impl T for (bool) {} -} - -#[rustfmt::skip] -mod unsafe_impl_valid_comment { - unsafe trait SaFety {} - // SaFety: - unsafe impl SaFety for () {} - - unsafe trait MultiLineComment {} - // The following impl is safe - // ... - // Safety: reason - unsafe impl MultiLineComment for () {} - - unsafe trait NoAscii {} - // 安全 SAFETY: 以下のコードは安全です - unsafe impl NoAscii for () {} - - unsafe trait InlineAndPrecedingComment {} - // SAFETY: - /* comment */ unsafe impl InlineAndPrecedingComment for () {} - - unsafe trait BuriedSafety {} - // Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor - // incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation - // ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in - // reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint - // occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est - // laborum. Safety: - // Tellus elementum sagittis vitae et leo duis ut diam quam. Sit amet nulla facilisi - // morbi tempus iaculis urna. Amet luctus venenatis lectus magna. At quis risus sed vulputate odio - // ut. Luctus venenatis lectus magna fringilla urna. Tortor id aliquet lectus proin nibh nisl - // condimentum id venenatis. Vulputate dignissim suspendisse in est ante in nibh mauris cursus. - unsafe impl BuriedSafety for () {} - - unsafe trait MultiLineBlockComment {} - /* This is a description - * Safety: */ - unsafe impl MultiLineBlockComment for () {} -} - -#[rustfmt::skip] -mod unsafe_impl_invalid_comment { - unsafe trait NoComment {} - - unsafe impl NoComment for () {} - - unsafe trait InlineComment {} - - /* SAFETY: */ unsafe impl InlineComment for () {} - - unsafe trait TrailingComment {} - - unsafe impl TrailingComment for () {} // SAFETY: - - unsafe trait Interference {} - // SAFETY: - const BIG_NUMBER: i32 = 1000000; - unsafe impl Interference for () {} -} - -unsafe trait ImplInFn {} - -fn impl_in_fn() { - // error - unsafe impl ImplInFn for () {} - - // SAFETY: ok - unsafe impl ImplInFn for (i32) {} -} - -unsafe trait CrateRoot {} - -// error -unsafe impl CrateRoot for () {} - -// SAFETY: ok -unsafe impl CrateRoot for (i32) {} - -fn issue_9142() { - // SAFETY: ok - let _ = - // we need this comment to avoid rustfmt putting - // it all on one line - unsafe {}; - - // SAFETY: this is more than one level away, so it should warn - let _ = { - if unsafe { true } { - todo!(); - } else { - let bar = unsafe {}; - todo!(); - bar - } - }; -} - -pub unsafe fn a_function_with_a_very_long_name_to_break_the_line() -> u32 { - 1 -} - -pub const unsafe fn a_const_function_with_a_very_long_name_to_break_the_line() -> u32 { - 2 -} - -fn issue_10832() { - // Safety: A safety comment. But it will warn anyways - let _some_variable_with_a_very_long_name_to_break_the_line = - unsafe { a_function_with_a_very_long_name_to_break_the_line() }; - - // Safety: Another safety comment. But it will warn anyways - const _SOME_CONST_WITH_A_VERY_LONG_NAME_TO_BREAK_THE_LINE: u32 = - unsafe { a_const_function_with_a_very_long_name_to_break_the_line() }; - - // Safety: Yet another safety comment. But it will warn anyways - static _SOME_STATIC_WITH_A_VERY_LONG_NAME_TO_BREAK_THE_LINE: u32 = - unsafe { a_const_function_with_a_very_long_name_to_break_the_line() }; -} - -fn main() {} diff --git a/tests/ui/unnecessary_map_on_constructor.fixed b/tests/ui/unnecessary_map_on_constructor.fixed new file mode 100644 index 0000000000000..d0ba7ed749e4b --- /dev/null +++ b/tests/ui/unnecessary_map_on_constructor.fixed @@ -0,0 +1,56 @@ +#![allow(unused)] +#![warn(clippy::unnecessary_map_on_constructor)] + +use std::ffi::OsStr; + +fn fun(t: i32) -> i32 { + t +} + +fn notfun(e: SimpleError) -> SimpleError { + e +} +macro_rules! expands_to_fun { + () => { + fun + }; +} + +#[derive(Copy, Clone)] +struct SimpleError {} + +type SimpleResult = std::result::Result; + +fn main() { + let x: i32 = 4; + + let err = SimpleError {}; + let a = Some(x); + let b: SimpleResult = Ok(x); + let c: SimpleResult = Err(err); + + let a = Some(fun(x)); + let b: SimpleResult = Ok(fun(x)); + let c: SimpleResult = Err(notfun(err)); + + let a = Option::Some(fun(x)); + let b: SimpleResult = SimpleResult::Ok(fun(x)); + let c: SimpleResult = SimpleResult::Err(notfun(err)); + let b: std::result::Result = Ok(fun(x)); + let c: std::result::Result = Err(notfun(err)); + + let a = Some(fun(x)); + let b: SimpleResult = Ok(fun(x)); + let c: SimpleResult = Err(notfun(err)); + + // Should not trigger warning + a.map(fun); + b.map(fun); + c.map_err(notfun); + + b.map_err(notfun); // Ok(_).map_err + c.map(fun); // Err(_).map() + + option_env!("PATH").map(OsStr::new); + Some(x).map(expands_to_fun!()); +} diff --git a/tests/ui/unnecessary_map_on_constructor.rs b/tests/ui/unnecessary_map_on_constructor.rs new file mode 100644 index 0000000000000..e89e7aad4c40e --- /dev/null +++ b/tests/ui/unnecessary_map_on_constructor.rs @@ -0,0 +1,56 @@ +#![allow(unused)] +#![warn(clippy::unnecessary_map_on_constructor)] + +use std::ffi::OsStr; + +fn fun(t: i32) -> i32 { + t +} + +fn notfun(e: SimpleError) -> SimpleError { + e +} +macro_rules! expands_to_fun { + () => { + fun + }; +} + +#[derive(Copy, Clone)] +struct SimpleError {} + +type SimpleResult = std::result::Result; + +fn main() { + let x: i32 = 4; + + let err = SimpleError {}; + let a = Some(x); + let b: SimpleResult = Ok(x); + let c: SimpleResult = Err(err); + + let a = Some(x).map(fun); + let b: SimpleResult = Ok(x).map(fun); + let c: SimpleResult = Err(err).map_err(notfun); + + let a = Option::Some(x).map(fun); + let b: SimpleResult = SimpleResult::Ok(x).map(fun); + let c: SimpleResult = SimpleResult::Err(err).map_err(notfun); + let b: std::result::Result = Ok(x).map(fun); + let c: std::result::Result = Err(err).map_err(notfun); + + let a = Some(fun(x)); + let b: SimpleResult = Ok(fun(x)); + let c: SimpleResult = Err(notfun(err)); + + // Should not trigger warning + a.map(fun); + b.map(fun); + c.map_err(notfun); + + b.map_err(notfun); // Ok(_).map_err + c.map(fun); // Err(_).map() + + option_env!("PATH").map(OsStr::new); + Some(x).map(expands_to_fun!()); +} diff --git a/tests/ui/unnecessary_map_on_constructor.stderr b/tests/ui/unnecessary_map_on_constructor.stderr new file mode 100644 index 0000000000000..d522b68d8726a --- /dev/null +++ b/tests/ui/unnecessary_map_on_constructor.stderr @@ -0,0 +1,53 @@ +error: unnecessary map on constructor Some(_) + --> $DIR/unnecessary_map_on_constructor.rs:32:13 + | +LL | let a = Some(x).map(fun); + | ^^^^^^^^^^^^^^^^ help: try: `Some(fun(x))` + | + = note: `-D clippy::unnecessary-map-on-constructor` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_map_on_constructor)]` + +error: unnecessary map on constructor Ok(_) + --> $DIR/unnecessary_map_on_constructor.rs:33:27 + | +LL | let b: SimpleResult = Ok(x).map(fun); + | ^^^^^^^^^^^^^^ help: try: `Ok(fun(x))` + +error: unnecessary map_err on constructor Err(_) + --> $DIR/unnecessary_map_on_constructor.rs:34:27 + | +LL | let c: SimpleResult = Err(err).map_err(notfun); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Err(notfun(err))` + +error: unnecessary map on constructor Option::Some(_) + --> $DIR/unnecessary_map_on_constructor.rs:36:13 + | +LL | let a = Option::Some(x).map(fun); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Option::Some(fun(x))` + +error: unnecessary map on constructor SimpleResult::Ok(_) + --> $DIR/unnecessary_map_on_constructor.rs:37:27 + | +LL | let b: SimpleResult = SimpleResult::Ok(x).map(fun); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `SimpleResult::Ok(fun(x))` + +error: unnecessary map_err on constructor SimpleResult::Err(_) + --> $DIR/unnecessary_map_on_constructor.rs:38:27 + | +LL | let c: SimpleResult = SimpleResult::Err(err).map_err(notfun); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `SimpleResult::Err(notfun(err))` + +error: unnecessary map on constructor Ok(_) + --> $DIR/unnecessary_map_on_constructor.rs:39:52 + | +LL | let b: std::result::Result = Ok(x).map(fun); + | ^^^^^^^^^^^^^^ help: try: `Ok(fun(x))` + +error: unnecessary map_err on constructor Err(_) + --> $DIR/unnecessary_map_on_constructor.rs:40:52 + | +LL | let c: std::result::Result = Err(err).map_err(notfun); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Err(notfun(err))` + +error: aborting due to 8 previous errors + diff --git a/tests/ui/unnecessary_to_owned.fixed b/tests/ui/unnecessary_to_owned.fixed index 7b662ca92d2ae..67faabc53cb5e 100644 --- a/tests/ui/unnecessary_to_owned.fixed +++ b/tests/ui/unnecessary_to_owned.fixed @@ -1,4 +1,4 @@ -#![allow(clippy::needless_borrow, clippy::ptr_arg)] +#![allow(clippy::needless_borrow, clippy::needless_borrows_for_generic_args, clippy::ptr_arg)] #![warn(clippy::unnecessary_to_owned, clippy::redundant_clone)] use std::borrow::Cow; diff --git a/tests/ui/unnecessary_to_owned.rs b/tests/ui/unnecessary_to_owned.rs index d79778a6a2ed4..99f9136427d4d 100644 --- a/tests/ui/unnecessary_to_owned.rs +++ b/tests/ui/unnecessary_to_owned.rs @@ -1,4 +1,4 @@ -#![allow(clippy::needless_borrow, clippy::ptr_arg)] +#![allow(clippy::needless_borrow, clippy::needless_borrows_for_generic_args, clippy::ptr_arg)] #![warn(clippy::unnecessary_to_owned, clippy::redundant_clone)] use std::borrow::Cow; diff --git a/tests/ui/used_underscore_binding.rs b/tests/ui/used_underscore_binding.rs index c672eff1c2710..a8f404b1400c9 100644 --- a/tests/ui/used_underscore_binding.rs +++ b/tests/ui/used_underscore_binding.rs @@ -1,6 +1,5 @@ //@aux-build:proc_macro_derive.rs -#![feature(rustc_private)] -#![warn(clippy::all)] +#![feature(rustc_private, lint_reasons)] #![warn(clippy::used_underscore_binding)] #![allow(clippy::disallowed_names, clippy::eq_op, clippy::uninlined_format_args)] @@ -107,6 +106,31 @@ async fn await_desugaring() { .await } +struct PhantomField { + _marker: std::marker::PhantomData, +} + +impl std::fmt::Debug for PhantomField { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.debug_struct("PhantomField").field("_marker", &self._marker).finish() + } +} + +struct AllowedField { + #[allow(clippy::used_underscore_binding)] + _allowed: usize, +} + +struct ExpectedField { + #[expect(clippy::used_underscore_binding)] + _expected: usize, +} + +fn lint_levels(allowed: AllowedField, expected: ExpectedField) { + let _ = allowed._allowed; + let _ = expected._expected; +} + fn main() { let foo = 0u32; // tests of unused_underscore lint diff --git a/tests/ui/used_underscore_binding.stderr b/tests/ui/used_underscore_binding.stderr index 289519b172eeb..78d8279810c1f 100644 --- a/tests/ui/used_underscore_binding.stderr +++ b/tests/ui/used_underscore_binding.stderr @@ -1,41 +1,76 @@ error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used - --> $DIR/used_underscore_binding.rs:24:5 + --> $DIR/used_underscore_binding.rs:23:5 | LL | _foo + 1 | ^^^^ | +note: `_foo` is defined here + --> $DIR/used_underscore_binding.rs:22:22 + | +LL | fn prefix_underscore(_foo: u32) -> u32 { + | ^^^^ = note: `-D clippy::used-underscore-binding` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::used_underscore_binding)]` error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used - --> $DIR/used_underscore_binding.rs:29:20 + --> $DIR/used_underscore_binding.rs:28:20 | LL | println!("{}", _foo); | ^^^^ + | +note: `_foo` is defined here + --> $DIR/used_underscore_binding.rs:27:24 + | +LL | fn in_macro_or_desugar(_foo: u32) { + | ^^^^ error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used - --> $DIR/used_underscore_binding.rs:30:16 + --> $DIR/used_underscore_binding.rs:29:16 | LL | assert_eq!(_foo, _foo); | ^^^^ + | +note: `_foo` is defined here + --> $DIR/used_underscore_binding.rs:27:24 + | +LL | fn in_macro_or_desugar(_foo: u32) { + | ^^^^ error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used - --> $DIR/used_underscore_binding.rs:30:22 + --> $DIR/used_underscore_binding.rs:29:22 | LL | assert_eq!(_foo, _foo); | ^^^^ + | +note: `_foo` is defined here + --> $DIR/used_underscore_binding.rs:27:24 + | +LL | fn in_macro_or_desugar(_foo: u32) { + | ^^^^ error: used binding `_underscore_field` which is prefixed with an underscore. A leading underscore signals that a binding will not be used - --> $DIR/used_underscore_binding.rs:43:5 + --> $DIR/used_underscore_binding.rs:42:5 | LL | s._underscore_field += 1; | ^^^^^^^^^^^^^^^^^^^ + | +note: `_underscore_field` is defined here + --> $DIR/used_underscore_binding.rs:36:5 + | +LL | _underscore_field: u32, + | ^^^^^^^^^^^^^^^^^^^^^^ error: used binding `_i` which is prefixed with an underscore. A leading underscore signals that a binding will not be used - --> $DIR/used_underscore_binding.rs:104:16 + --> $DIR/used_underscore_binding.rs:103:16 | LL | uses_i(_i); | ^^ + | +note: `_i` is defined here + --> $DIR/used_underscore_binding.rs:102:13 + | +LL | let _i = 5; + | ^^ error: aborting due to 6 previous errors diff --git a/tests/ui/useless_conversion.fixed b/tests/ui/useless_conversion.fixed index 5259195990592..ed8387b7eb2c5 100644 --- a/tests/ui/useless_conversion.fixed +++ b/tests/ui/useless_conversion.fixed @@ -151,6 +151,8 @@ fn main() { let _ = s3; let s4: Foo<'a'> = Foo; let _ = vec![s4, s4, s4].into_iter(); + + issue11300::bar(); } #[allow(dead_code)] @@ -196,6 +198,95 @@ fn explicit_into_iter_fn_arg() { b(macro_generated!()); } +mod issue11300 { + pub fn foo(i: I) + where + I: IntoIterator + ExactSizeIterator, + { + assert_eq!(i.len(), 3); + } + + trait Helper {} + impl Helper for [i32; 3] {} + impl Helper for std::array::IntoIter {} + impl Helper<()> for std::array::IntoIter {} + + fn foo2(_: I) + where + I: IntoIterator + Helper, + { + } + + trait Helper2 {} + impl Helper2> for i32 {} + impl Helper2<[i32; 3]> for i32 {} + fn foo3(_: I) + where + I: IntoIterator, + i32: Helper2, + { + } + + pub fn bar() { + // This should not trigger the lint: + // `[i32, 3]` does not satisfy the `ExactSizeIterator` bound, so the into_iter call cannot be + // removed and is not useless. + foo([1, 2, 3].into_iter()); + + // This should trigger the lint, receiver type [i32; 3] also implements `Helper` + foo2::([1, 2, 3]); + + // This again should *not* lint, since X = () and I = std::array::IntoIter, + // and `[i32; 3]: Helper<()>` is not true (only `std::array::IntoIter: Helper<()>` is). + foo2::<(), _>([1, 2, 3].into_iter()); + + // This should lint. Removing the `.into_iter()` means that `I` gets substituted with `[i32; 3]`, + // and `i32: Helper2<[i32, 3]>` is true, so this call is indeed unncessary. + foo3([1, 2, 3]); + } + + fn ice() { + struct S1; + impl S1 { + pub fn foo(&self, _: I) {} + } + + S1.foo([1, 2]); + + // ICE that occured in itertools + trait Itertools { + fn interleave_shortest(self, other: J) + where + J: IntoIterator, + Self: Sized; + } + impl Itertools for I { + fn interleave_shortest(self, other: J) + where + J: IntoIterator, + Self: Sized, + { + } + } + let v0: Vec = vec![0, 2, 4]; + let v1: Vec = vec![1, 3, 5, 7]; + v0.into_iter().interleave_shortest(v1); + + trait TraitWithLifetime<'a> {} + impl<'a> TraitWithLifetime<'a> for std::array::IntoIter<&'a i32, 2> {} + + struct Helper; + impl<'a> Helper { + fn with_lt(&self, _: I) + where + I: IntoIterator + TraitWithLifetime<'a>, + { + } + } + Helper.with_lt([&1, &2].into_iter()); + } +} + #[derive(Copy, Clone)] struct Foo; diff --git a/tests/ui/useless_conversion.rs b/tests/ui/useless_conversion.rs index befb2f9a5c32c..991a7762fc64a 100644 --- a/tests/ui/useless_conversion.rs +++ b/tests/ui/useless_conversion.rs @@ -151,6 +151,8 @@ fn main() { let _ = Foo::<'a'>::from(s3); let s4: Foo<'a'> = Foo; let _ = vec![s4, s4, s4].into_iter().into_iter(); + + issue11300::bar(); } #[allow(dead_code)] @@ -196,6 +198,95 @@ fn explicit_into_iter_fn_arg() { b(macro_generated!()); } +mod issue11300 { + pub fn foo(i: I) + where + I: IntoIterator + ExactSizeIterator, + { + assert_eq!(i.len(), 3); + } + + trait Helper {} + impl Helper for [i32; 3] {} + impl Helper for std::array::IntoIter {} + impl Helper<()> for std::array::IntoIter {} + + fn foo2(_: I) + where + I: IntoIterator + Helper, + { + } + + trait Helper2 {} + impl Helper2> for i32 {} + impl Helper2<[i32; 3]> for i32 {} + fn foo3(_: I) + where + I: IntoIterator, + i32: Helper2, + { + } + + pub fn bar() { + // This should not trigger the lint: + // `[i32, 3]` does not satisfy the `ExactSizeIterator` bound, so the into_iter call cannot be + // removed and is not useless. + foo([1, 2, 3].into_iter()); + + // This should trigger the lint, receiver type [i32; 3] also implements `Helper` + foo2::([1, 2, 3].into_iter()); + + // This again should *not* lint, since X = () and I = std::array::IntoIter, + // and `[i32; 3]: Helper<()>` is not true (only `std::array::IntoIter: Helper<()>` is). + foo2::<(), _>([1, 2, 3].into_iter()); + + // This should lint. Removing the `.into_iter()` means that `I` gets substituted with `[i32; 3]`, + // and `i32: Helper2<[i32, 3]>` is true, so this call is indeed unncessary. + foo3([1, 2, 3].into_iter()); + } + + fn ice() { + struct S1; + impl S1 { + pub fn foo(&self, _: I) {} + } + + S1.foo([1, 2].into_iter()); + + // ICE that occured in itertools + trait Itertools { + fn interleave_shortest(self, other: J) + where + J: IntoIterator, + Self: Sized; + } + impl Itertools for I { + fn interleave_shortest(self, other: J) + where + J: IntoIterator, + Self: Sized, + { + } + } + let v0: Vec = vec![0, 2, 4]; + let v1: Vec = vec![1, 3, 5, 7]; + v0.into_iter().interleave_shortest(v1.into_iter()); + + trait TraitWithLifetime<'a> {} + impl<'a> TraitWithLifetime<'a> for std::array::IntoIter<&'a i32, 2> {} + + struct Helper; + impl<'a> Helper { + fn with_lt(&self, _: I) + where + I: IntoIterator + TraitWithLifetime<'a>, + { + } + } + Helper.with_lt([&1, &2].into_iter()); + } +} + #[derive(Copy, Clone)] struct Foo; diff --git a/tests/ui/useless_conversion.stderr b/tests/ui/useless_conversion.stderr index 28e7bb61098f7..c1f8b6b4aa966 100644 --- a/tests/ui/useless_conversion.stderr +++ b/tests/ui/useless_conversion.stderr @@ -119,64 +119,112 @@ LL | let _ = vec![s4, s4, s4].into_iter().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![s4, s4, s4].into_iter()` error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> $DIR/useless_conversion.rs:183:7 + --> $DIR/useless_conversion.rs:185:7 | LL | b(vec![1, 2].into_iter()); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `vec![1, 2]` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> $DIR/useless_conversion.rs:173:13 + --> $DIR/useless_conversion.rs:175:13 | LL | fn b>(_: T) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> $DIR/useless_conversion.rs:184:7 + --> $DIR/useless_conversion.rs:186:7 | LL | c(vec![1, 2].into_iter()); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `vec![1, 2]` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> $DIR/useless_conversion.rs:174:18 + --> $DIR/useless_conversion.rs:176:18 | LL | fn c(_: impl IntoIterator) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> $DIR/useless_conversion.rs:185:7 + --> $DIR/useless_conversion.rs:187:7 | LL | d(vec![1, 2].into_iter()); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `vec![1, 2]` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> $DIR/useless_conversion.rs:177:12 + --> $DIR/useless_conversion.rs:179:12 | LL | T: IntoIterator, | ^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> $DIR/useless_conversion.rs:188:7 + --> $DIR/useless_conversion.rs:190:7 | LL | b(vec![1, 2].into_iter().into_iter()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`s: `vec![1, 2]` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> $DIR/useless_conversion.rs:173:13 + --> $DIR/useless_conversion.rs:175:13 | LL | fn b>(_: T) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> $DIR/useless_conversion.rs:189:7 + --> $DIR/useless_conversion.rs:191:7 | LL | b(vec![1, 2].into_iter().into_iter().into_iter()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`s: `vec![1, 2]` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> $DIR/useless_conversion.rs:173:13 + --> $DIR/useless_conversion.rs:175:13 | LL | fn b>(_: T) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 24 previous errors +error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` + --> $DIR/useless_conversion.rs:237:24 + | +LL | foo2::([1, 2, 3].into_iter()); + | ^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `[1, 2, 3]` + | +note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` + --> $DIR/useless_conversion.rs:216:12 + | +LL | I: IntoIterator + Helper, + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` + --> $DIR/useless_conversion.rs:245:14 + | +LL | foo3([1, 2, 3].into_iter()); + | ^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `[1, 2, 3]` + | +note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` + --> $DIR/useless_conversion.rs:225:12 + | +LL | I: IntoIterator, + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` + --> $DIR/useless_conversion.rs:254:16 + | +LL | S1.foo([1, 2].into_iter()); + | ^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `[1, 2]` + | +note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` + --> $DIR/useless_conversion.rs:251:27 + | +LL | pub fn foo(&self, _: I) {} + | ^^^^^^^^^^^^ + +error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` + --> $DIR/useless_conversion.rs:273:44 + | +LL | v0.into_iter().interleave_shortest(v1.into_iter()); + | ^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `v1` + | +note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` + --> $DIR/useless_conversion.rs:260:20 + | +LL | J: IntoIterator, + | ^^^^^^^^^^^^ + +error: aborting due to 28 previous errors diff --git a/tests/ui/vec_box_sized.fixed b/tests/ui/vec_box_sized.fixed index 4a5ef83856a41..4363d2224afd2 100644 --- a/tests/ui/vec_box_sized.fixed +++ b/tests/ui/vec_box_sized.fixed @@ -49,4 +49,9 @@ mod inner_mod { } } +// https://github.com/rust-lang/rust-clippy/issues/11417 +fn in_closure() { + let _ = |_: Vec>| {}; +} + fn main() {} diff --git a/tests/ui/vec_box_sized.rs b/tests/ui/vec_box_sized.rs index ea020405a30f0..f4e27fe4bd509 100644 --- a/tests/ui/vec_box_sized.rs +++ b/tests/ui/vec_box_sized.rs @@ -49,4 +49,9 @@ mod inner_mod { } } +// https://github.com/rust-lang/rust-clippy/issues/11417 +fn in_closure() { + let _ = |_: Vec>| {}; +} + fn main() {} From 6ad218ceef976d67af4bd065732b1f37595c882c Mon Sep 17 00:00:00 2001 From: blyxyas Date: Wed, 13 Sep 2023 19:43:11 +0200 Subject: [PATCH 05/48] Add colored help --- Cargo.toml | 2 ++ src/driver.rs | 55 ++++++++++++++++++++++---------------------- src/main.rs | 63 +++++++++++++++++++++++++++++---------------------- 3 files changed, 66 insertions(+), 54 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2d8b590dbe311..b07fe5d3521cb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,8 @@ clippy_lints = { path = "clippy_lints" } rustc_tools_util = "0.3.0" tempfile = { version = "3.2", optional = true } termize = "0.1" +color-print = "0.3.4" # Sync version with Cargo +anstream = "0.5.0" [dev-dependencies] ui_test = "0.20" diff --git a/src/driver.rs b/src/driver.rs index 1d89477dcc16b..d47767faed9ed 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -26,6 +26,8 @@ use std::ops::Deref; use std::path::Path; use std::process::exit; +use anstream::println; + /// If a command-line option matches `find_arg`, then apply the predicate `pred` on its value. If /// true, then return it. The parameter is assumed to be either `--arg=value` or `--arg value`. fn arg_value<'a, T: Deref>( @@ -162,39 +164,15 @@ impl rustc_driver::Callbacks for ClippyCallbacks { } } +#[allow(clippy::ignored_unit_patterns)] fn display_help() { - println!( - "\ -Checks a package to catch common mistakes and improve your Rust code. - -Usage: - cargo clippy [options] [--] [...] - -Common options: - -h, --help Print this message - --rustc Pass all args to rustc - -V, --version Print version info and exit - -For the other options see `cargo check --help`. - -To allow or deny a lint from the command line you can use `cargo clippy --` -with: - - -W --warn OPT Set lint warnings - -A --allow OPT Set lint allowed - -D --deny OPT Set lint denied - -F --forbid OPT Set lint forbidden - -You can use tool lints to allow or deny lints from your code, eg.: - - #[allow(clippy::needless_lifetimes)] -" - ); + println!("{}", help_message()); } const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust-clippy/issues/new?template=ice.yml"; #[allow(clippy::too_many_lines)] +#[allow(clippy::ignored_unit_patterns)] pub fn main() { let handler = EarlyErrorHandler::new(ErrorOutputType::default()); @@ -236,6 +214,7 @@ pub fn main() { if orig_args.iter().any(|a| a == "--version" || a == "-V") { let version_info = rustc_tools_util::get_version_info!(); + println!("{version_info}"); exit(0); } @@ -292,3 +271,25 @@ pub fn main() { } })) } + +#[must_use] +fn help_message() -> &'static str { + color_print::cstr!( + "Checks a file to catch common mistakes and improve your Rust code. +Run clippy-driver with the same arguments you use for rustc + +Usage: + clippy-driver [OPTIONS] INPUT + +Common options: + -h, --help Print this message + -V, --version Print version info and exit + --rustc Pass all arguments to rustc + +Allowing / Denying lints +You can use tool lints to allow or deny lints from your code, e.g.: + + #[allow(clippy::needless_lifetimes)] +" + ) +} diff --git a/src/main.rs b/src/main.rs index 26b655076cf65..bbf7d22c85043 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,37 +6,14 @@ use std::env; use std::path::PathBuf; use std::process::{self, Command}; -const CARGO_CLIPPY_HELP: &str = "Checks a package to catch common mistakes and improve your Rust code. - -Usage: - cargo clippy [options] [--] [...] - -Common options: - --no-deps Run Clippy only on the given crate, without linting the dependencies - --fix Automatically apply lint suggestions. This flag implies `--no-deps` and `--all-targets` - -h, --help Print this message - -V, --version Print version info and exit - --explain LINT Print the documentation for a given lint - -For the other options see `cargo check --help`. - -To allow or deny a lint from the command line you can use `cargo clippy --` -with: - - -W --warn OPT Set lint warnings - -A --allow OPT Set lint allowed - -D --deny OPT Set lint denied - -F --forbid OPT Set lint forbidden - -You can use tool lints to allow or deny lints from your code, e.g.: - - #[allow(clippy::needless_lifetimes)] -"; +use anstream::println; +#[allow(clippy::ignored_unit_patterns)] fn show_help() { - println!("{CARGO_CLIPPY_HELP}"); + println!("{}", help_message()); } +#[allow(clippy::ignored_unit_patterns)] fn show_version() { let version_info = rustc_tools_util::get_version_info!(); println!("{version_info}"); @@ -168,6 +145,38 @@ where } } +#[must_use] +pub fn help_message() -> &'static str { + color_print::cstr!( +"Checks a package to catch common mistakes and improve your Rust code. + +Usage: + cargo clippy [OPTIONS] [--] [<>...] + +Common options: + --no-deps Run Clippy only on the given crate, without linting the dependencies + --fix Automatically apply lint suggestions. This flag implies --no-deps and --all-targets + -h, --help Print this message + -V, --version Print version info and exit + --explain [LINT] Print the documentation for a given lint + +See all options with cargo check --help. + +Allowing / Denying lints + +To allow or deny a lint from the command line you can use cargo clippy -- with: + + -W / --warn [LINT] Set lint warnings + -A / --allow [LINT] Set lint allowed + -D / --deny [LINT] Set lint denied + -F / --forbid [LINT] Set lint forbidden + +You can use tool lints to allow or deny lints from your code, e.g.: + + #[allow(clippy::needless_lifetimes)] +" + ) +} #[cfg(test)] mod tests { use super::ClippyCmd; From 1972cc89c4e9db8f325137b58f7197aeef0a49d6 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Mon, 25 Sep 2023 11:55:20 +0000 Subject: [PATCH 06/48] Test that each config value exists in a test clippy.toml --- clippy_lints/Cargo.toml | 3 +++ clippy_lints/src/utils/conf.rs | 41 ++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index dcd9a4adcbd46..834753a230196 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -28,6 +28,9 @@ semver = "1.0" rustc-semver = "1.1" url = "2.2" +[dev-dependencies] +walkdir = "2.3" + [features] deny-warnings = ["clippy_utils/deny-warnings"] # build clippy with internal lints enabled, off by default diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 75c3c7a958a21..c502e50d21f9c 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -744,3 +744,44 @@ fn calculate_dimensions(fields: &[&str]) -> (usize, Vec) { (rows, column_widths) } + +#[cfg(test)] +mod tests { + use rustc_data_structures::fx::{FxHashMap, FxHashSet}; + use serde::de::IgnoredAny; + use std::fs; + use walkdir::WalkDir; + + #[test] + fn configs_are_tested() { + let mut names: FxHashSet = super::metadata::get_configuration_metadata() + .into_iter() + .map(|meta| meta.name.replace('_', "-")) + .collect(); + + let toml_files = WalkDir::new("../tests") + .into_iter() + .map(Result::unwrap) + .filter(|entry| entry.file_name() == "clippy.toml"); + + for entry in toml_files { + let file = fs::read_to_string(entry.path()).unwrap(); + #[allow(clippy::zero_sized_map_values)] + if let Ok(map) = toml::from_str::>(&file) { + for name in map.keys() { + names.remove(name.as_str()); + } + } + } + + assert!( + names.remove("allow-one-hash-in-raw-strings"), + "remove this when #11481 is fixed" + ); + + assert!( + names.is_empty(), + "Configuration variable lacks test: {names:?}\nAdd a test to `tests/ui-toml`" + ); + } +} From 84d6894f2665bc2d3876538f4f2eb7e88d3b92ee Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 25 Sep 2023 16:19:02 +0200 Subject: [PATCH 07/48] it's not Miri but MIR constants --- clippy_lints/src/enum_clike.rs | 4 ++-- clippy_lints/src/matches/overlapping_arms.rs | 6 +++--- clippy_utils/src/consts.rs | 6 +++--- clippy_utils/src/lib.rs | 6 +++--- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/clippy_lints/src/enum_clike.rs b/clippy_lints/src/enum_clike.rs index 3f60e5a7c4d64..646767868e2cf 100644 --- a/clippy_lints/src/enum_clike.rs +++ b/clippy_lints/src/enum_clike.rs @@ -1,7 +1,7 @@ //! lint on C-like enums that are `repr(isize/usize)` and have values that //! don't fit into an `i32` -use clippy_utils::consts::{miri_to_const, Constant}; +use clippy_utils::consts::{mir_to_const, Constant}; use clippy_utils::diagnostics::span_lint; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -51,7 +51,7 @@ impl<'tcx> LateLintPass<'tcx> for UnportableVariant { .const_eval_poly(def_id.to_def_id()) .ok() .map(|val| rustc_middle::mir::Const::from_value(val, ty)); - if let Some(Constant::Int(val)) = constant.and_then(|c| miri_to_const(cx, c)) { + if let Some(Constant::Int(val)) = constant.and_then(|c| mir_to_const(cx, c)) { if let ty::Adt(adt, _) = ty.kind() { if adt.is_enum() { ty = adt.repr().discr_type().to_ty(cx.tcx); diff --git a/clippy_lints/src/matches/overlapping_arms.rs b/clippy_lints/src/matches/overlapping_arms.rs index 7c0485914b83b..8f0083f812cc2 100644 --- a/clippy_lints/src/matches/overlapping_arms.rs +++ b/clippy_lints/src/matches/overlapping_arms.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, constant_full_int, miri_to_const, FullInt}; +use clippy_utils::consts::{constant, constant_full_int, mir_to_const, FullInt}; use clippy_utils::diagnostics::span_lint_and_note; use core::cmp::Ordering; use rustc_hir::{Arm, Expr, PatKind, RangeEnd}; @@ -37,14 +37,14 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) Some(lhs) => constant(cx, cx.typeck_results(), lhs)?, None => { let min_val_const = ty.numeric_min_val(cx.tcx)?; - miri_to_const(cx, mir::Const::from_ty_const(min_val_const, cx.tcx))? + mir_to_const(cx, mir::Const::from_ty_const(min_val_const, cx.tcx))? }, }; let rhs_const = match rhs { Some(rhs) => constant(cx, cx.typeck_results(), rhs)?, None => { let max_val_const = ty.numeric_max_val(cx.tcx)?; - miri_to_const(cx, mir::Const::from_ty_const(max_val_const, cx.tcx))? + mir_to_const(cx, mir::Const::from_ty_const(max_val_const, cx.tcx))? }, }; let lhs_val = lhs_const.int_value(cx, ty)?; diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index d596eed4b7c4d..81584e8f1e4de 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -403,7 +403,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { && adt_def.is_struct() && let Some(desired_field) = field_of_struct(*adt_def, self.lcx, *constant, field) { - miri_to_const(self.lcx, desired_field) + mir_to_const(self.lcx, desired_field) } else { result @@ -483,7 +483,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { .const_eval_resolve(self.param_env, mir::UnevaluatedConst::new(def_id, args), None) .ok() .map(|val| rustc_middle::mir::Const::from_value(val, ty))?; - let result = miri_to_const(self.lcx, result)?; + let result = mir_to_const(self.lcx, result)?; self.source = ConstantSource::Constant; Some(result) }, @@ -655,7 +655,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { } } -pub fn miri_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> Option> { +pub fn mir_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> Option> { use rustc_middle::mir::ConstValue; match result { mir::Const::Val(ConstValue::Scalar(Scalar::Int(int)), _) => match result.ty().kind() { diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 0f7bc04ccba15..3210581299ccf 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -113,7 +113,7 @@ use rustc_span::{sym, Span}; use rustc_target::abi::Integer; use visitors::Visitable; -use crate::consts::{constant, miri_to_const, Constant}; +use crate::consts::{constant, mir_to_const, Constant}; use crate::higher::Range; use crate::ty::{ adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type, @@ -1511,7 +1511,7 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti && let Some(min_val) = bnd_ty.numeric_min_val(cx.tcx) && let const_val = cx.tcx.valtree_to_const_val((bnd_ty, min_val.to_valtree())) && let min_const_kind = Const::from_value(const_val, bnd_ty) - && let Some(min_const) = miri_to_const(cx, min_const_kind) + && let Some(min_const) = mir_to_const(cx, min_const_kind) && let Some(start_const) = constant(cx, cx.typeck_results(), start) { start_const == min_const @@ -1527,7 +1527,7 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti && let Some(max_val) = bnd_ty.numeric_max_val(cx.tcx) && let const_val = cx.tcx.valtree_to_const_val((bnd_ty, max_val.to_valtree())) && let max_const_kind = Const::from_value(const_val, bnd_ty) - && let Some(max_const) = miri_to_const(cx, max_const_kind) + && let Some(max_const) = mir_to_const(cx, max_const_kind) && let Some(end_const) = constant(cx, cx.typeck_results(), end) { end_const == max_const From ce45221163717c60aaff33b2b1b7ce47ace9de87 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 25 Sep 2023 16:23:47 +0200 Subject: [PATCH 08/48] simply some valtree-to-const conversion --- clippy_utils/src/lib.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 3210581299ccf..350b231e90367 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1509,9 +1509,7 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti if let rustc_ty::Adt(_, subst) = ty.kind() && let bnd_ty = subst.type_at(0) && let Some(min_val) = bnd_ty.numeric_min_val(cx.tcx) - && let const_val = cx.tcx.valtree_to_const_val((bnd_ty, min_val.to_valtree())) - && let min_const_kind = Const::from_value(const_val, bnd_ty) - && let Some(min_const) = mir_to_const(cx, min_const_kind) + && let Some(min_const) = mir_to_const(cx, Const::from_ty_const(min_val, cx.tcx)) && let Some(start_const) = constant(cx, cx.typeck_results(), start) { start_const == min_const @@ -1525,9 +1523,7 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti if let rustc_ty::Adt(_, subst) = ty.kind() && let bnd_ty = subst.type_at(0) && let Some(max_val) = bnd_ty.numeric_max_val(cx.tcx) - && let const_val = cx.tcx.valtree_to_const_val((bnd_ty, max_val.to_valtree())) - && let max_const_kind = Const::from_value(const_val, bnd_ty) - && let Some(max_const) = mir_to_const(cx, max_const_kind) + && let Some(max_const) = mir_to_const(cx, Const::from_ty_const(max_val, cx.tcx)) && let Some(end_const) = constant(cx, cx.typeck_results(), end) { end_const == max_const From 0f198579d0f65554e105e1d08dff66b491338376 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 25 Sep 2023 16:59:46 +0200 Subject: [PATCH 09/48] mir_to_const: fix handling of float arrays --- clippy_utils/src/consts.rs | 63 ++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 36 deletions(-) diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 81584e8f1e4de..0bae7056c4fb8 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -9,11 +9,12 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::{BinOp, BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, Item, ItemKind, Node, QPath, UnOp}; use rustc_lexer::tokenize; use rustc_lint::LateContext; -use rustc_middle::mir::interpret::Scalar; +use rustc_middle::mir::interpret::{alloc_range, Scalar}; use rustc_middle::ty::{self, EarlyBinder, FloatTy, GenericArgsRef, List, ScalarInt, Ty, TyCtxt}; use rustc_middle::{bug, mir, span_bug}; use rustc_span::symbol::{Ident, Symbol}; use rustc_span::SyntaxContext; +use rustc_target::abi::Size; use std::cmp::Ordering::{self, Equal}; use std::hash::{Hash, Hasher}; use std::iter; @@ -657,8 +658,12 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { pub fn mir_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> Option> { use rustc_middle::mir::ConstValue; - match result { - mir::Const::Val(ConstValue::Scalar(Scalar::Int(int)), _) => match result.ty().kind() { + let mir::Const::Val(val, _) = result else { + // We only work on evaluated consts. + return None; + }; + match (val, result.ty().kind()) { + (ConstValue::Scalar(Scalar::Int(int)), _) => match result.ty().kind() { ty::Adt(adt_def, _) if adt_def.is_struct() => Some(Constant::Adt(result)), ty::Bool => Some(Constant::Bool(int == ScalarInt::TRUE)), ty::Uint(_) | ty::Int(_) => Some(Constant::Int(int.assert_bits(int.size()))), @@ -671,42 +676,28 @@ pub fn mir_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> ty::RawPtr(_) => Some(Constant::RawPtr(int.assert_bits(int.size()))), _ => None, }, - mir::Const::Val(cv, _) if matches!(result.ty().kind(), ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), ty::Str)) => - { - let data = cv.try_get_slice_bytes_for_diagnostics(lcx.tcx)?; + (_, ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Str) => { + let data = val.try_get_slice_bytes_for_diagnostics(lcx.tcx)?; String::from_utf8(data.to_owned()).ok().map(Constant::Str) }, - mir::Const::Val(ConstValue::Indirect { alloc_id, offset: _ }, _) => { - let alloc = lcx.tcx.global_alloc(alloc_id).unwrap_memory(); - match result.ty().kind() { - ty::Adt(adt_def, _) if adt_def.is_struct() => Some(Constant::Adt(result)), - ty::Array(sub_type, len) => match sub_type.kind() { - ty::Float(FloatTy::F32) => match len.try_to_target_usize(lcx.tcx) { - Some(len) => alloc - .inner() - .inspect_with_uninit_and_ptr_outside_interpreter(0..(4 * usize::try_from(len).unwrap())) - .to_owned() - .array_chunks::<4>() - .map(|&chunk| Some(Constant::F32(f32::from_le_bytes(chunk)))) - .collect::>>>() - .map(Constant::Vec), - _ => None, - }, - ty::Float(FloatTy::F64) => match len.try_to_target_usize(lcx.tcx) { - Some(len) => alloc - .inner() - .inspect_with_uninit_and_ptr_outside_interpreter(0..(8 * usize::try_from(len).unwrap())) - .to_owned() - .array_chunks::<8>() - .map(|&chunk| Some(Constant::F64(f64::from_le_bytes(chunk)))) - .collect::>>>() - .map(Constant::Vec), - _ => None, - }, - _ => None, - }, - _ => None, + (_, ty::Adt(adt_def, _)) if adt_def.is_struct() => Some(Constant::Adt(result)), + (ConstValue::Indirect { alloc_id, offset }, ty::Array(sub_type, len)) => { + let alloc = lcx.tcx.global_alloc(alloc_id).unwrap_memory().inner(); + let len = len.try_to_target_usize(lcx.tcx)?; + let ty::Float(flt) = sub_type.kind() else { + return None; + }; + let size = Size::from_bits(flt.bit_width()); + let mut res = Vec::new(); + for idx in 0..len { + let range = alloc_range(offset + size * idx, size); + let val = alloc.read_scalar(&lcx.tcx, range, /* read_provenance */ false).ok()?; + res.push(match flt { + FloatTy::F32 => Constant::F32(f32::from_bits(val.to_u32().ok()?)), + FloatTy::F64 => Constant::F64(f64::from_bits(val.to_u64().ok()?)), + }); } + Some(Constant::Vec(res)) }, _ => None, } From 55074827b52a2dd1551c19d8f42c59299877e59b Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 26 Sep 2023 02:15:32 +0000 Subject: [PATCH 10/48] Don't store lazyness in DefKind --- clippy_lints/src/init_numbered_fields.rs | 2 +- clippy_utils/src/lib.rs | 2 +- clippy_utils/src/ty/type_certainty/mod.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/init_numbered_fields.rs b/clippy_lints/src/init_numbered_fields.rs index b00fa104f9825..f95d2c2edb1ef 100644 --- a/clippy_lints/src/init_numbered_fields.rs +++ b/clippy_lints/src/init_numbered_fields.rs @@ -50,7 +50,7 @@ impl<'tcx> LateLintPass<'tcx> for NumberedFields { && fields .iter() .all(|f| f.ident.as_str().as_bytes().iter().all(u8::is_ascii_digit)) - && !matches!(cx.qpath_res(path, e.hir_id), Res::Def(DefKind::TyAlias { .. }, ..)) + && !matches!(cx.qpath_res(path, e.hir_id), Res::Def(DefKind::TyAlias, ..)) { let expr_spans = fields .iter() diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index be1c46319c2b6..47cce4603a68c 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -288,7 +288,7 @@ pub fn is_wild(pat: &Pat<'_>) -> bool { /// Checks if the given `QPath` belongs to a type alias. pub fn is_ty_alias(qpath: &QPath<'_>) -> bool { match *qpath { - QPath::Resolved(_, path) => matches!(path.res, Res::Def(DefKind::TyAlias { .. } | DefKind::AssocTy, ..)), + QPath::Resolved(_, path) => matches!(path.res, Res::Def(DefKind::TyAlias | DefKind::AssocTy, ..)), QPath::TypeRelative(ty, _) if let TyKind::Path(qpath) = ty.kind => { is_ty_alias(&qpath) }, _ => false, } diff --git a/clippy_utils/src/ty/type_certainty/mod.rs b/clippy_utils/src/ty/type_certainty/mod.rs index 4b06b12fb94d0..686a3f41499b6 100644 --- a/clippy_utils/src/ty/type_certainty/mod.rs +++ b/clippy_utils/src/ty/type_certainty/mod.rs @@ -220,7 +220,7 @@ fn path_segment_certainty( // See the comment preceding `qpath_certainty`. `def_id` could refer to a type or a value. let certainty = lhs.join_clearing_def_ids(rhs); if resolves_to_type { - if let DefKind::TyAlias { .. } = cx.tcx.def_kind(def_id) { + if let DefKind::TyAlias = cx.tcx.def_kind(def_id) { adt_def_id(cx.tcx.type_of(def_id).instantiate_identity()) .map_or(certainty, |def_id| certainty.with_def_id(def_id)) } else { From fab90003b860409113b60b11b5c3c58547c3f0ad Mon Sep 17 00:00:00 2001 From: koka Date: Mon, 6 Feb 2023 21:09:39 +0900 Subject: [PATCH 11/48] Do not lint when imported item contains underscore --- clippy_lints/src/wildcard_imports.rs | 1 + tests/ui/wildcard_imports.fixed | 28 ++++++++++++++ tests/ui/wildcard_imports.rs | 28 ++++++++++++++ tests/ui/wildcard_imports.stderr | 38 +++++++++++-------- .../wildcard_imports_2021.edition2018.fixed | 28 ++++++++++++++ .../wildcard_imports_2021.edition2018.stderr | 38 +++++++++++-------- .../wildcard_imports_2021.edition2021.fixed | 28 ++++++++++++++ .../wildcard_imports_2021.edition2021.stderr | 38 +++++++++++-------- tests/ui/wildcard_imports_2021.rs | 28 ++++++++++++++ 9 files changed, 207 insertions(+), 48 deletions(-) diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index d09d02a7dfda9..70b83149ce1a6 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -132,6 +132,7 @@ impl LateLintPass<'_> for WildcardImports { if self.warn_on_all || !self.check_exceptions(item, use_path.segments); let used_imports = cx.tcx.names_imported_by_glob_use(item.owner_id.def_id); if !used_imports.is_empty(); // Already handled by `unused_imports` + if !used_imports.contains(&kw::Underscore); then { let mut applicability = Applicability::MachineApplicable; let import_source_snippet = snippet_with_applicability(cx, use_path.span, "..", &mut applicability); diff --git a/tests/ui/wildcard_imports.fixed b/tests/ui/wildcard_imports.fixed index 2828f9d048efc..6fdd728b9b72e 100644 --- a/tests/ui/wildcard_imports.fixed +++ b/tests/ui/wildcard_imports.fixed @@ -69,6 +69,34 @@ mod struct_mod { } } +// issue 9942 +mod underscore_mod { + // allow use of `deref` so that `clippy --fix` includes `Deref`. + #![allow(noop_method_call)] + + mod exports_underscore { + pub use std::ops::Deref as _; + pub fn dummy() {} + } + + mod exports_underscore_ish { + pub use std::ops::Deref as _Deref; + pub fn dummy() {} + } + + fn does_not_lint() { + use self::exports_underscore::*; + let _ = (&0).deref(); + dummy(); + } + + fn does_lint() { + use self::exports_underscore_ish::{_Deref, dummy}; + let _ = (&0).deref(); + dummy(); + } +} + fn main() { foo(); multi_foo(); diff --git a/tests/ui/wildcard_imports.rs b/tests/ui/wildcard_imports.rs index cbe70e505d8cd..20e06d4b36641 100644 --- a/tests/ui/wildcard_imports.rs +++ b/tests/ui/wildcard_imports.rs @@ -69,6 +69,34 @@ mod struct_mod { } } +// issue 9942 +mod underscore_mod { + // allow use of `deref` so that `clippy --fix` includes `Deref`. + #![allow(noop_method_call)] + + mod exports_underscore { + pub use std::ops::Deref as _; + pub fn dummy() {} + } + + mod exports_underscore_ish { + pub use std::ops::Deref as _Deref; + pub fn dummy() {} + } + + fn does_not_lint() { + use self::exports_underscore::*; + let _ = (&0).deref(); + dummy(); + } + + fn does_lint() { + use self::exports_underscore_ish::*; + let _ = (&0).deref(); + dummy(); + } +} + fn main() { foo(); multi_foo(); diff --git a/tests/ui/wildcard_imports.stderr b/tests/ui/wildcard_imports.stderr index 3c750815bafc9..01a5414778c10 100644 --- a/tests/ui/wildcard_imports.stderr +++ b/tests/ui/wildcard_imports.stderr @@ -38,55 +38,61 @@ LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:97:13 + --> $DIR/wildcard_imports.rs:94:13 + | +LL | use self::exports_underscore_ish::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `self::exports_underscore_ish::{_Deref, dummy}` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:125:13 | LL | use crate::fn_mod::*; | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:103:75 + --> $DIR/wildcard_imports.rs:131:75 | LL | use wildcard_imports_helper::inner::inner_for_self_import::{self, *}; | ^ help: try: `inner_extern_foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:104:13 + --> $DIR/wildcard_imports.rs:132:13 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:116:20 + --> $DIR/wildcard_imports.rs:144:20 | LL | use self::{inner::*, inner2::*}; | ^^^^^^^^ help: try: `inner::inner_foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:116:30 + --> $DIR/wildcard_imports.rs:144:30 | LL | use self::{inner::*, inner2::*}; | ^^^^^^^^^ help: try: `inner2::inner_bar` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:123:13 + --> $DIR/wildcard_imports.rs:151:13 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:152:9 + --> $DIR/wildcard_imports.rs:180:9 | LL | use crate::in_fn_test::*; | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:161:9 + --> $DIR/wildcard_imports.rs:189:9 | LL | use crate:: in_fn_test:: * ; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:162:9 + --> $DIR/wildcard_imports.rs:190:9 | LL | use crate:: fn_mod:: | _________^ @@ -94,40 +100,40 @@ LL | | *; | |_________^ help: try: `crate:: fn_mod::foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:173:13 + --> $DIR/wildcard_imports.rs:201:13 | LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:208:17 + --> $DIR/wildcard_imports.rs:236:17 | LL | use super::*; | ^^^^^^^^ help: try: `super::insidefoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:216:13 + --> $DIR/wildcard_imports.rs:244:13 | LL | use crate::super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:225:17 + --> $DIR/wildcard_imports.rs:253:17 | LL | use super::super::*; | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:234:13 + --> $DIR/wildcard_imports.rs:262:13 | LL | use super::super::super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:242:13 + --> $DIR/wildcard_imports.rs:270:13 | LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` -error: aborting due to 21 previous errors +error: aborting due to 22 previous errors diff --git a/tests/ui/wildcard_imports_2021.edition2018.fixed b/tests/ui/wildcard_imports_2021.edition2018.fixed index b27281fa25c56..6a9fe007d654d 100644 --- a/tests/ui/wildcard_imports_2021.edition2018.fixed +++ b/tests/ui/wildcard_imports_2021.edition2018.fixed @@ -64,6 +64,34 @@ mod struct_mod { } } +// issue 9942 +mod underscore_mod { + // allow use of `deref` so that `clippy --fix` includes `Deref`. + #![allow(noop_method_call)] + + mod exports_underscore { + pub use std::ops::Deref as _; + pub fn dummy() {} + } + + mod exports_underscore_ish { + pub use std::ops::Deref as _Deref; + pub fn dummy() {} + } + + fn does_not_lint() { + use exports_underscore::*; + let _ = (&0).deref(); + dummy(); + } + + fn does_lint() { + use exports_underscore_ish::{_Deref, dummy}; + let _ = (&0).deref(); + dummy(); + } +} + fn main() { foo(); multi_foo(); diff --git a/tests/ui/wildcard_imports_2021.edition2018.stderr b/tests/ui/wildcard_imports_2021.edition2018.stderr index 709a665d65c2a..e39f240a4aa2a 100644 --- a/tests/ui/wildcard_imports_2021.edition2018.stderr +++ b/tests/ui/wildcard_imports_2021.edition2018.stderr @@ -38,55 +38,61 @@ LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:91:13 + --> $DIR/wildcard_imports_2021.rs:89:13 + | +LL | use exports_underscore_ish::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `exports_underscore_ish::{_Deref, dummy}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:119:13 | LL | use crate::fn_mod::*; | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:97:75 + --> $DIR/wildcard_imports_2021.rs:125:75 | LL | use wildcard_imports_helper::inner::inner_for_self_import::{self, *}; | ^ help: try: `inner_extern_foo` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:98:13 + --> $DIR/wildcard_imports_2021.rs:126:13 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:110:20 + --> $DIR/wildcard_imports_2021.rs:138:20 | LL | use self::{inner::*, inner2::*}; | ^^^^^^^^ help: try: `inner::inner_foo` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:110:30 + --> $DIR/wildcard_imports_2021.rs:138:30 | LL | use self::{inner::*, inner2::*}; | ^^^^^^^^^ help: try: `inner2::inner_bar` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:117:13 + --> $DIR/wildcard_imports_2021.rs:145:13 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:146:9 + --> $DIR/wildcard_imports_2021.rs:174:9 | LL | use crate::in_fn_test::*; | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:155:9 + --> $DIR/wildcard_imports_2021.rs:183:9 | LL | use crate:: in_fn_test:: * ; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:156:9 + --> $DIR/wildcard_imports_2021.rs:184:9 | LL | use crate:: fn_mod:: | _________^ @@ -94,40 +100,40 @@ LL | | *; | |_________^ help: try: `crate:: fn_mod::foo` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:167:13 + --> $DIR/wildcard_imports_2021.rs:195:13 | LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:202:17 + --> $DIR/wildcard_imports_2021.rs:230:17 | LL | use super::*; | ^^^^^^^^ help: try: `super::insidefoo` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:210:13 + --> $DIR/wildcard_imports_2021.rs:238:13 | LL | use crate::super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:219:17 + --> $DIR/wildcard_imports_2021.rs:247:17 | LL | use super::super::*; | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:228:13 + --> $DIR/wildcard_imports_2021.rs:256:13 | LL | use super::super::super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:236:13 + --> $DIR/wildcard_imports_2021.rs:264:13 | LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` -error: aborting due to 21 previous errors +error: aborting due to 22 previous errors diff --git a/tests/ui/wildcard_imports_2021.edition2021.fixed b/tests/ui/wildcard_imports_2021.edition2021.fixed index b27281fa25c56..6a9fe007d654d 100644 --- a/tests/ui/wildcard_imports_2021.edition2021.fixed +++ b/tests/ui/wildcard_imports_2021.edition2021.fixed @@ -64,6 +64,34 @@ mod struct_mod { } } +// issue 9942 +mod underscore_mod { + // allow use of `deref` so that `clippy --fix` includes `Deref`. + #![allow(noop_method_call)] + + mod exports_underscore { + pub use std::ops::Deref as _; + pub fn dummy() {} + } + + mod exports_underscore_ish { + pub use std::ops::Deref as _Deref; + pub fn dummy() {} + } + + fn does_not_lint() { + use exports_underscore::*; + let _ = (&0).deref(); + dummy(); + } + + fn does_lint() { + use exports_underscore_ish::{_Deref, dummy}; + let _ = (&0).deref(); + dummy(); + } +} + fn main() { foo(); multi_foo(); diff --git a/tests/ui/wildcard_imports_2021.edition2021.stderr b/tests/ui/wildcard_imports_2021.edition2021.stderr index 709a665d65c2a..e39f240a4aa2a 100644 --- a/tests/ui/wildcard_imports_2021.edition2021.stderr +++ b/tests/ui/wildcard_imports_2021.edition2021.stderr @@ -38,55 +38,61 @@ LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:91:13 + --> $DIR/wildcard_imports_2021.rs:89:13 + | +LL | use exports_underscore_ish::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `exports_underscore_ish::{_Deref, dummy}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:119:13 | LL | use crate::fn_mod::*; | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:97:75 + --> $DIR/wildcard_imports_2021.rs:125:75 | LL | use wildcard_imports_helper::inner::inner_for_self_import::{self, *}; | ^ help: try: `inner_extern_foo` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:98:13 + --> $DIR/wildcard_imports_2021.rs:126:13 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:110:20 + --> $DIR/wildcard_imports_2021.rs:138:20 | LL | use self::{inner::*, inner2::*}; | ^^^^^^^^ help: try: `inner::inner_foo` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:110:30 + --> $DIR/wildcard_imports_2021.rs:138:30 | LL | use self::{inner::*, inner2::*}; | ^^^^^^^^^ help: try: `inner2::inner_bar` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:117:13 + --> $DIR/wildcard_imports_2021.rs:145:13 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:146:9 + --> $DIR/wildcard_imports_2021.rs:174:9 | LL | use crate::in_fn_test::*; | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:155:9 + --> $DIR/wildcard_imports_2021.rs:183:9 | LL | use crate:: in_fn_test:: * ; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:156:9 + --> $DIR/wildcard_imports_2021.rs:184:9 | LL | use crate:: fn_mod:: | _________^ @@ -94,40 +100,40 @@ LL | | *; | |_________^ help: try: `crate:: fn_mod::foo` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:167:13 + --> $DIR/wildcard_imports_2021.rs:195:13 | LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:202:17 + --> $DIR/wildcard_imports_2021.rs:230:17 | LL | use super::*; | ^^^^^^^^ help: try: `super::insidefoo` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:210:13 + --> $DIR/wildcard_imports_2021.rs:238:13 | LL | use crate::super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:219:17 + --> $DIR/wildcard_imports_2021.rs:247:17 | LL | use super::super::*; | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:228:13 + --> $DIR/wildcard_imports_2021.rs:256:13 | LL | use super::super::super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:236:13 + --> $DIR/wildcard_imports_2021.rs:264:13 | LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` -error: aborting due to 21 previous errors +error: aborting due to 22 previous errors diff --git a/tests/ui/wildcard_imports_2021.rs b/tests/ui/wildcard_imports_2021.rs index 7dd2103eca7e7..18ebc0f512741 100644 --- a/tests/ui/wildcard_imports_2021.rs +++ b/tests/ui/wildcard_imports_2021.rs @@ -64,6 +64,34 @@ mod struct_mod { } } +// issue 9942 +mod underscore_mod { + // allow use of `deref` so that `clippy --fix` includes `Deref`. + #![allow(noop_method_call)] + + mod exports_underscore { + pub use std::ops::Deref as _; + pub fn dummy() {} + } + + mod exports_underscore_ish { + pub use std::ops::Deref as _Deref; + pub fn dummy() {} + } + + fn does_not_lint() { + use exports_underscore::*; + let _ = (&0).deref(); + dummy(); + } + + fn does_lint() { + use exports_underscore_ish::*; + let _ = (&0).deref(); + dummy(); + } +} + fn main() { foo(); multi_foo(); From f2efdba6e2ab000d8210e8855fc0bcddbcf58b64 Mon Sep 17 00:00:00 2001 From: lcnr Date: Mon, 25 Sep 2023 15:46:38 +0200 Subject: [PATCH 12/48] subst -> instantiate --- clippy_lints/src/methods/unnecessary_to_owned.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 5c5ee26205287..50d6f3b7e55f5 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -401,7 +401,7 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty< = get_callee_generic_args_and_args(cx, parent_expr) { // FIXME: the `instantiate_identity()` below seems incorrect, since we eventually - // call `tcx.try_subst_and_normalize_erasing_regions` further down + // call `tcx.try_instantiate_and_normalize_erasing_regions` further down // (i.e., we are explicitly not in the identity context). let fn_sig = cx.tcx.fn_sig(callee_def_id).instantiate_identity().skip_binder(); if let Some(arg_index) = recv.into_iter().chain(call_args).position(|arg| arg.hir_id == expr.hir_id) @@ -452,7 +452,7 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty< let output_ty = fn_sig.output(); if output_ty.contains(*param_ty) { - if let Ok(new_ty) = cx.tcx.try_subst_and_normalize_erasing_regions( + if let Ok(new_ty) = cx.tcx.try_instantiate_and_normalize_erasing_regions( new_subst, cx.param_env, EarlyBinder::bind(output_ty)) { expr = parent_expr; ty = new_ty; From 6cdff107782be2c00ce37267bc5b2181dc1f12f7 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Tue, 26 Sep 2023 11:40:10 +0000 Subject: [PATCH 13/48] Describe the type of string in raw_strings lints --- clippy_lints/src/raw_strings.rs | 30 +++++++------ tests/ui/needless_raw_string.fixed | 4 ++ tests/ui/needless_raw_string.rs | 4 ++ tests/ui/needless_raw_string.stderr | 46 +++++++++++++++++--- tests/ui/needless_raw_string_hashes.stderr | 30 ++++++------- tests/ui/write_literal_2.rs | 10 ++--- tests/ui/write_literal_2.stderr | 49 ++++++++-------------- 7 files changed, 100 insertions(+), 73 deletions(-) diff --git a/clippy_lints/src/raw_strings.rs b/clippy_lints/src/raw_strings.rs index 2895595e03908..4b46cbc4d895e 100644 --- a/clippy_lints/src/raw_strings.rs +++ b/clippy_lints/src/raw_strings.rs @@ -75,6 +75,7 @@ impl EarlyLintPass for RawStrings { if !snippet(cx, expr.span, prefix).trim().starts_with(prefix) { return; } + let descr = lit.kind.descr(); if !str.contains(['\\', '"']) { span_lint_and_then( @@ -89,20 +90,17 @@ impl EarlyLintPass for RawStrings { let r_pos = expr.span.lo() + BytePos::from_usize(prefix.len() - 1); let start = start.with_lo(r_pos); - if end.is_empty() { - diag.span_suggestion( - start, - "use a string literal instead", - format!("\"{str}\""), - Applicability::MachineApplicable, - ); - } else { - diag.multipart_suggestion( - "try", - vec![(start, String::new()), (end, String::new())], - Applicability::MachineApplicable, - ); + let mut remove = vec![(start, String::new())]; + // avoid debug ICE from empty suggestions + if !end.is_empty() { + remove.push((end, String::new())); } + + diag.multipart_suggestion_verbose( + format!("use a plain {descr} literal instead"), + remove, + Applicability::MachineApplicable, + ); }, ); if !matches!(cx.get_lint_level(NEEDLESS_RAW_STRINGS), rustc_lint::Allow) { @@ -149,9 +147,9 @@ impl EarlyLintPass for RawStrings { let (start, end) = hash_spans(expr.span, prefix, req, max); let message = match max - req { - _ if req == 0 => "remove all the hashes around the literal".to_string(), - 1 => "remove one hash from both sides of the literal".to_string(), - n => format!("remove {n} hashes from both sides of the literal"), + _ if req == 0 => format!("remove all the hashes around the {descr} literal"), + 1 => format!("remove one hash from both sides of the {descr} literal"), + n => format!("remove {n} hashes from both sides of the {descr} literal"), }; diag.multipart_suggestion( diff --git a/tests/ui/needless_raw_string.fixed b/tests/ui/needless_raw_string.fixed index 855498105135e..4ea711fd67a14 100644 --- a/tests/ui/needless_raw_string.fixed +++ b/tests/ui/needless_raw_string.fixed @@ -18,4 +18,8 @@ fn main() { multiline string "; + + "no hashes"; + b"no hashes"; + c"no hashes"; } diff --git a/tests/ui/needless_raw_string.rs b/tests/ui/needless_raw_string.rs index 06d4973038717..b6239f9b1f031 100644 --- a/tests/ui/needless_raw_string.rs +++ b/tests/ui/needless_raw_string.rs @@ -18,4 +18,8 @@ fn main() { multiline string "#; + + r"no hashes"; + br"no hashes"; + cr"no hashes"; } diff --git a/tests/ui/needless_raw_string.stderr b/tests/ui/needless_raw_string.stderr index e6806b31b1d96..276bc84c6c375 100644 --- a/tests/ui/needless_raw_string.stderr +++ b/tests/ui/needless_raw_string.stderr @@ -6,7 +6,7 @@ LL | r#"aaa"#; | = note: `-D clippy::needless-raw-strings` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::needless_raw_strings)]` -help: try +help: use a plain string literal instead | LL - r#"aaa"#; LL + "aaa"; @@ -18,7 +18,7 @@ error: unnecessary raw string literal LL | br#"aaa"#; | ^^^^^^^^^ | -help: try +help: use a plain byte string literal instead | LL - br#"aaa"#; LL + b"aaa"; @@ -30,7 +30,7 @@ error: unnecessary raw string literal LL | cr#"aaa"#; | ^^^^^^^^^ | -help: try +help: use a plain C string literal instead | LL - cr#"aaa"#; LL + c"aaa"; @@ -46,7 +46,7 @@ LL | | string LL | | "#; | |______^ | -help: try +help: use a plain string literal instead | LL ~ " LL | a @@ -55,5 +55,41 @@ LL | string LL ~ "; | -error: aborting due to 4 previous errors +error: unnecessary raw string literal + --> $DIR/needless_raw_string.rs:22:5 + | +LL | r"no hashes"; + | ^^^^^^^^^^^^ + | +help: use a plain string literal instead + | +LL - r"no hashes"; +LL + "no hashes"; + | + +error: unnecessary raw string literal + --> $DIR/needless_raw_string.rs:23:5 + | +LL | br"no hashes"; + | ^^^^^^^^^^^^^ + | +help: use a plain byte string literal instead + | +LL - br"no hashes"; +LL + b"no hashes"; + | + +error: unnecessary raw string literal + --> $DIR/needless_raw_string.rs:24:5 + | +LL | cr"no hashes"; + | ^^^^^^^^^^^^^ + | +help: use a plain C string literal instead + | +LL - cr"no hashes"; +LL + c"no hashes"; + | + +error: aborting due to 7 previous errors diff --git a/tests/ui/needless_raw_string_hashes.stderr b/tests/ui/needless_raw_string_hashes.stderr index 4399c6555c2c0..017160b1a421d 100644 --- a/tests/ui/needless_raw_string_hashes.stderr +++ b/tests/ui/needless_raw_string_hashes.stderr @@ -6,7 +6,7 @@ LL | r#"\aaa"#; | = note: `-D clippy::needless-raw-string-hashes` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::needless_raw_string_hashes)]` -help: remove all the hashes around the literal +help: remove all the hashes around the string literal | LL - r#"\aaa"#; LL + r"\aaa"; @@ -18,7 +18,7 @@ error: unnecessary hashes around raw string literal LL | r##"Hello "world"!"##; | ^^^^^^^^^^^^^^^^^^^^^ | -help: remove one hash from both sides of the literal +help: remove one hash from both sides of the string literal | LL - r##"Hello "world"!"##; LL + r#"Hello "world"!"#; @@ -30,7 +30,7 @@ error: unnecessary hashes around raw string literal LL | r######" "### "## "# "######; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: remove 2 hashes from both sides of the literal +help: remove 2 hashes from both sides of the string literal | LL - r######" "### "## "# "######; LL + r####" "### "## "# "####; @@ -42,7 +42,7 @@ error: unnecessary hashes around raw string literal LL | r######" "aa" "# "## "######; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: remove 3 hashes from both sides of the literal +help: remove 3 hashes from both sides of the string literal | LL - r######" "aa" "# "## "######; LL + r###" "aa" "# "## "###; @@ -54,7 +54,7 @@ error: unnecessary hashes around raw string literal LL | br#"\aaa"#; | ^^^^^^^^^^ | -help: remove all the hashes around the literal +help: remove all the hashes around the byte string literal | LL - br#"\aaa"#; LL + br"\aaa"; @@ -66,7 +66,7 @@ error: unnecessary hashes around raw string literal LL | br##"Hello "world"!"##; | ^^^^^^^^^^^^^^^^^^^^^^ | -help: remove one hash from both sides of the literal +help: remove one hash from both sides of the byte string literal | LL - br##"Hello "world"!"##; LL + br#"Hello "world"!"#; @@ -78,7 +78,7 @@ error: unnecessary hashes around raw string literal LL | br######" "### "## "# "######; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: remove 2 hashes from both sides of the literal +help: remove 2 hashes from both sides of the byte string literal | LL - br######" "### "## "# "######; LL + br####" "### "## "# "####; @@ -90,7 +90,7 @@ error: unnecessary hashes around raw string literal LL | br######" "aa" "# "## "######; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: remove 3 hashes from both sides of the literal +help: remove 3 hashes from both sides of the byte string literal | LL - br######" "aa" "# "## "######; LL + br###" "aa" "# "## "###; @@ -102,7 +102,7 @@ error: unnecessary hashes around raw string literal LL | cr#"\aaa"#; | ^^^^^^^^^^ | -help: remove all the hashes around the literal +help: remove all the hashes around the C string literal | LL - cr#"\aaa"#; LL + cr"\aaa"; @@ -114,7 +114,7 @@ error: unnecessary hashes around raw string literal LL | cr##"Hello "world"!"##; | ^^^^^^^^^^^^^^^^^^^^^^ | -help: remove one hash from both sides of the literal +help: remove one hash from both sides of the C string literal | LL - cr##"Hello "world"!"##; LL + cr#"Hello "world"!"#; @@ -126,7 +126,7 @@ error: unnecessary hashes around raw string literal LL | cr######" "### "## "# "######; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: remove 2 hashes from both sides of the literal +help: remove 2 hashes from both sides of the C string literal | LL - cr######" "### "## "# "######; LL + cr####" "### "## "# "####; @@ -138,7 +138,7 @@ error: unnecessary hashes around raw string literal LL | cr######" "aa" "# "## "######; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: remove 3 hashes from both sides of the literal +help: remove 3 hashes from both sides of the C string literal | LL - cr######" "aa" "# "## "######; LL + cr###" "aa" "# "## "###; @@ -154,7 +154,7 @@ LL | | string LL | | "#; | |______^ | -help: remove all the hashes around the literal +help: remove all the hashes around the string literal | LL ~ r" LL | \a @@ -169,7 +169,7 @@ error: unnecessary hashes around raw string literal LL | r###"rust"###; | ^^^^^^^^^^^^^ | -help: remove all the hashes around the literal +help: remove all the hashes around the string literal | LL - r###"rust"###; LL + r"rust"; @@ -181,7 +181,7 @@ error: unnecessary hashes around raw string literal LL | r#"hello world"#; | ^^^^^^^^^^^^^^^^ | -help: remove all the hashes around the literal +help: remove all the hashes around the string literal | LL - r#"hello world"#; LL + r"hello world"; diff --git a/tests/ui/write_literal_2.rs b/tests/ui/write_literal_2.rs index aa0c13c134080..197e01149e0ad 100644 --- a/tests/ui/write_literal_2.rs +++ b/tests/ui/write_literal_2.rs @@ -1,6 +1,6 @@ //@no-rustfix: overlapping suggestions #![allow(unused_must_use)] -#![warn(clippy::needless_raw_strings, clippy::write_literal)] +#![warn(clippy::write_literal)] use std::io::Write; @@ -11,9 +11,7 @@ fn main() { //~^ ERROR: literal with an empty format string //~| NOTE: `-D clippy::write-literal` implied by `-D warnings` writeln!(v, r"{}", r"{hello}"); - //~^ ERROR: unnecessary raw string literal - //~| NOTE: `-D clippy::needless-raw-strings` implied by `-D warnings` - //~| ERROR: literal with an empty format string + //~^ ERROR: literal with an empty format string writeln!(v, "{}", '\''); //~^ ERROR: literal with an empty format string writeln!(v, "{}", '"'); @@ -26,8 +24,8 @@ fn main() { v, "some {}", "hello \ - //~^ ERROR: literal with an empty format string - world!" + world!", + //~^^ ERROR: literal with an empty format string ); writeln!( v, diff --git a/tests/ui/write_literal_2.stderr b/tests/ui/write_literal_2.stderr index 6d382a267ad81..9a16d2f240dd8 100644 --- a/tests/ui/write_literal_2.stderr +++ b/tests/ui/write_literal_2.stderr @@ -1,14 +1,3 @@ -error: unnecessary raw string literal - --> $DIR/write_literal_2.rs:13:24 - | -LL | writeln!(v, r"{}", r"{hello}"); - | -^^^^^^^^^ - | | - | help: use a string literal instead: `"{hello}"` - | - = note: `-D clippy::needless-raw-strings` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::needless_raw_strings)]` - error: literal with an empty format string --> $DIR/write_literal_2.rs:10:23 | @@ -36,7 +25,7 @@ LL + writeln!(v, r"{{hello}}"); | error: literal with an empty format string - --> $DIR/write_literal_2.rs:17:23 + --> $DIR/write_literal_2.rs:15:23 | LL | writeln!(v, "{}", '\''); | ^^^^ @@ -48,7 +37,7 @@ LL + writeln!(v, "'"); | error: literal with an empty format string - --> $DIR/write_literal_2.rs:19:23 + --> $DIR/write_literal_2.rs:17:23 | LL | writeln!(v, "{}", '"'); | ^^^ @@ -60,13 +49,13 @@ LL + writeln!(v, "\""); | error: literal with an empty format string - --> $DIR/write_literal_2.rs:21:24 + --> $DIR/write_literal_2.rs:19:24 | LL | writeln!(v, r"{}", '"'); | ^^^ error: literal with an empty format string - --> $DIR/write_literal_2.rs:23:24 + --> $DIR/write_literal_2.rs:21:24 | LL | writeln!(v, r"{}", '\''); | ^^^^ @@ -78,22 +67,20 @@ LL + writeln!(v, r"'"); | error: literal with an empty format string - --> $DIR/write_literal_2.rs:28:9 + --> $DIR/write_literal_2.rs:26:9 | LL | / "hello \ -LL | | -LL | | world!" +LL | | world!", | |_______________^ | help: try | LL ~ "some hello \ -LL + -LL ~ world!" +LL ~ world!", | error: literal with an empty format string - --> $DIR/write_literal_2.rs:36:9 + --> $DIR/write_literal_2.rs:34:9 | LL | "1", | ^^^ @@ -105,7 +92,7 @@ LL ~ {} \\ {}", | error: literal with an empty format string - --> $DIR/write_literal_2.rs:37:9 + --> $DIR/write_literal_2.rs:35:9 | LL | "2", | ^^^ @@ -117,7 +104,7 @@ LL ~ "1", | error: literal with an empty format string - --> $DIR/write_literal_2.rs:38:9 + --> $DIR/write_literal_2.rs:36:9 | LL | "3", | ^^^ @@ -130,7 +117,7 @@ LL ~ "2", | error: literal with an empty format string - --> $DIR/write_literal_2.rs:41:23 + --> $DIR/write_literal_2.rs:39:23 | LL | writeln!(v, "{}", "\\"); | ^^^^ @@ -142,7 +129,7 @@ LL + writeln!(v, "\\"); | error: literal with an empty format string - --> $DIR/write_literal_2.rs:43:24 + --> $DIR/write_literal_2.rs:41:24 | LL | writeln!(v, r"{}", "\\"); | ^^^^ @@ -154,7 +141,7 @@ LL + writeln!(v, r"\"); | error: literal with an empty format string - --> $DIR/write_literal_2.rs:45:26 + --> $DIR/write_literal_2.rs:43:26 | LL | writeln!(v, r#"{}"#, "\\"); | ^^^^ @@ -166,7 +153,7 @@ LL + writeln!(v, r#"\"#); | error: literal with an empty format string - --> $DIR/write_literal_2.rs:47:23 + --> $DIR/write_literal_2.rs:45:23 | LL | writeln!(v, "{}", r"\"); | ^^^^ @@ -178,7 +165,7 @@ LL + writeln!(v, "\\"); | error: literal with an empty format string - --> $DIR/write_literal_2.rs:49:23 + --> $DIR/write_literal_2.rs:47:23 | LL | writeln!(v, "{}", "\r"); | ^^^^ @@ -190,16 +177,16 @@ LL + writeln!(v, "\r"); | error: literal with an empty format string - --> $DIR/write_literal_2.rs:52:28 + --> $DIR/write_literal_2.rs:50:28 | LL | writeln!(v, r#"{}{}"#, '#', '"'); | ^^^ error: literal with an empty format string - --> $DIR/write_literal_2.rs:52:33 + --> $DIR/write_literal_2.rs:50:33 | LL | writeln!(v, r#"{}{}"#, '#', '"'); | ^^^ -error: aborting due to 18 previous errors +error: aborting due to 17 previous errors From ec2f62677f5e7e6a1ed7e02fd61100c78c896d86 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Sat, 23 Sep 2023 17:34:05 +0000 Subject: [PATCH 14/48] Add `manual_hash_one` lint --- CHANGELOG.md | 1 + book/src/lint_configuration.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/lib.rs | 2 + clippy_lints/src/manual_hash_one.rs | 133 ++++++++++++++++++++++++++++ clippy_lints/src/utils/conf.rs | 2 +- clippy_utils/src/msrvs.rs | 2 +- clippy_utils/src/visitors.rs | 44 +++++++++ tests/ui/manual_hash_one.fixed | 89 +++++++++++++++++++ tests/ui/manual_hash_one.rs | 89 +++++++++++++++++++ tests/ui/manual_hash_one.stderr | 56 ++++++++++++ 11 files changed, 418 insertions(+), 2 deletions(-) create mode 100644 clippy_lints/src/manual_hash_one.rs create mode 100644 tests/ui/manual_hash_one.fixed create mode 100644 tests/ui/manual_hash_one.rs create mode 100644 tests/ui/manual_hash_one.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c9ab1e2402ca..db54bfbf0b32f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5072,6 +5072,7 @@ Released 2018-09-13 [`manual_find`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find [`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map [`manual_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten +[`manual_hash_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_hash_one [`manual_instant_elapsed`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_instant_elapsed [`manual_is_ascii_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check [`manual_is_finite`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_finite diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index b980083f1f52a..2c958ccbbc246 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -151,6 +151,7 @@ The minimum rust version that the project supports * [`type_repetition_in_bounds`](https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds) * [`tuple_array_conversions`](https://rust-lang.github.io/rust-clippy/master/index.html#tuple_array_conversions) * [`manual_try_fold`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_try_fold) +* [`manual_hash_one`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_hash_one) ## `cognitive-complexity-threshold` diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 4d1281ec1e7c7..b4b84b36044c6 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -280,6 +280,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::manual_clamp::MANUAL_CLAMP_INFO, crate::manual_float_methods::MANUAL_IS_FINITE_INFO, crate::manual_float_methods::MANUAL_IS_INFINITE_INFO, + crate::manual_hash_one::MANUAL_HASH_ONE_INFO, crate::manual_is_ascii_check::MANUAL_IS_ASCII_CHECK_INFO, crate::manual_let_else::MANUAL_LET_ELSE_INFO, crate::manual_main_separator_str::MANUAL_MAIN_SEPARATOR_STR_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 1271be2fd9368..63a3fbcb8976f 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -190,6 +190,7 @@ mod manual_async_fn; mod manual_bits; mod manual_clamp; mod manual_float_methods; +mod manual_hash_one; mod manual_is_ascii_check; mod manual_let_else; mod manual_main_separator_str; @@ -1119,6 +1120,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: msrv(), )) }); + store.register_late_pass(move |_| Box::new(manual_hash_one::ManualHashOne::new(msrv()))); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/manual_hash_one.rs b/clippy_lints/src/manual_hash_one.rs new file mode 100644 index 0000000000000..ea91133545044 --- /dev/null +++ b/clippy_lints/src/manual_hash_one.rs @@ -0,0 +1,133 @@ +use clippy_utils::diagnostics::span_lint_hir_and_then; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::source::snippet_opt; +use clippy_utils::visitors::{is_local_used, local_used_once}; +use clippy_utils::{is_trait_method, path_to_local_id}; +use rustc_errors::Applicability; +use rustc_hir::{BindingAnnotation, ExprKind, Local, Node, PatKind, StmtKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// Checks for cases where [`BuildHasher::hash_one`] can be used. + /// + /// [`BuildHasher::hash_one`]: https://doc.rust-lang.org/std/hash/trait.BuildHasher.html#method.hash_one + /// + /// ### Why is this bad? + /// It is more concise to use the `hash_one` method. + /// + /// ### Example + /// ```rust + /// use std::hash::{BuildHasher, Hash, Hasher}; + /// use std::collections::hash_map::RandomState; + /// + /// let s = RandomState::new(); + /// let value = vec![1, 2, 3]; + /// + /// let mut hasher = s.build_hasher(); + /// value.hash(&mut hasher); + /// let hash = hasher.finish(); + /// ``` + /// Use instead: + /// ```rust + /// use std::hash::BuildHasher; + /// use std::collections::hash_map::RandomState; + /// + /// let s = RandomState::new(); + /// let value = vec![1, 2, 3]; + /// + /// let hash = s.hash_one(&value); + /// ``` + #[clippy::version = "1.74.0"] + pub MANUAL_HASH_ONE, + complexity, + "manual implementations of `BuildHasher::hash_one`" +} + +pub struct ManualHashOne { + msrv: Msrv, +} + +impl ManualHashOne { + #[must_use] + pub fn new(msrv: Msrv) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(ManualHashOne => [MANUAL_HASH_ONE]); + +impl LateLintPass<'_> for ManualHashOne { + fn check_local(&mut self, cx: &LateContext<'_>, local: &Local<'_>) { + // `let mut hasher = seg.build_hasher();` + if let PatKind::Binding(BindingAnnotation::MUT, hasher, _, None) = local.pat.kind + && let Some(init) = local.init + && !init.span.from_expansion() + && let ExprKind::MethodCall(seg, build_hasher, [], _) = init.kind + && seg.ident.name == sym!(build_hasher) + + && let Node::Stmt(local_stmt) = cx.tcx.hir().get_parent(local.hir_id) + && let Node::Block(block) = cx.tcx.hir().get_parent(local_stmt.hir_id) + + && let mut stmts = block.stmts.iter() + .skip_while(|stmt| stmt.hir_id != local_stmt.hir_id) + .skip(1) + .filter(|&stmt| is_local_used(cx, stmt, hasher)) + + // `hashed_value.hash(&mut hasher);` + && let Some(hash_stmt) = stmts.next() + && let StmtKind::Semi(hash_expr) = hash_stmt.kind + && !hash_expr.span.from_expansion() + && let ExprKind::MethodCall(seg, hashed_value, [ref_to_hasher], _) = hash_expr.kind + && seg.ident.name == sym::hash + && is_trait_method(cx, hash_expr, sym::Hash) + && path_to_local_id(ref_to_hasher.peel_borrows(), hasher) + + && let maybe_finish_stmt = stmts.next() + // There should be no more statements referencing `hasher` + && stmts.next().is_none() + + // `hasher.finish()`, may be anywhere in a statement or the trailing expr of the block + && let Some(path_expr) = local_used_once(cx, (maybe_finish_stmt, block.expr), hasher) + && let Node::Expr(finish_expr) = cx.tcx.hir().get_parent(path_expr.hir_id) + && !finish_expr.span.from_expansion() + && let ExprKind::MethodCall(seg, _, [], _) = finish_expr.kind + && seg.ident.name == sym!(finish) + + && self.msrv.meets(msrvs::BUILD_HASHER_HASH_ONE) + { + span_lint_hir_and_then( + cx, + MANUAL_HASH_ONE, + finish_expr.hir_id, + finish_expr.span, + "manual implementation of `BuildHasher::hash_one`", + |diag| { + if let Some(build_hasher) = snippet_opt(cx, build_hasher.span) + && let Some(hashed_value) = snippet_opt(cx, hashed_value.span) + { + diag.multipart_suggestion( + "try", + vec![ + (local_stmt.span, String::new()), + (hash_stmt.span, String::new()), + ( + finish_expr.span, + // `needless_borrows_for_generic_args` will take care of + // removing the `&` when it isn't needed + format!("{build_hasher}.hash_one(&{hashed_value})") + ) + ], + Applicability::MachineApplicable, + ); + + } + }, + ); + } + } + + extract_msrv_attr!(LateContext); +} diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index c502e50d21f9c..23da1de77303e 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -294,7 +294,7 @@ define_Conf! { /// /// Suppress lints whenever the suggested change would cause breakage for other crates. (avoid_breaking_exported_api: bool = true), - /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, OPTION_MAP_UNWRAP_OR, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP, MANUAL_LET_ELSE, UNCHECKED_DURATION_SUBTRACTION, COLLAPSIBLE_STR_REPLACE, SEEK_FROM_CURRENT, SEEK_REWIND, UNNECESSARY_LAZY_EVALUATIONS, TRANSMUTE_PTR_TO_REF, ALMOST_COMPLETE_RANGE, NEEDLESS_BORROW, DERIVABLE_IMPLS, MANUAL_IS_ASCII_CHECK, MANUAL_REM_EUCLID, MANUAL_RETAIN, TYPE_REPETITION_IN_BOUNDS, TUPLE_ARRAY_CONVERSIONS, MANUAL_TRY_FOLD. + /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, OPTION_MAP_UNWRAP_OR, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP, MANUAL_LET_ELSE, UNCHECKED_DURATION_SUBTRACTION, COLLAPSIBLE_STR_REPLACE, SEEK_FROM_CURRENT, SEEK_REWIND, UNNECESSARY_LAZY_EVALUATIONS, TRANSMUTE_PTR_TO_REF, ALMOST_COMPLETE_RANGE, NEEDLESS_BORROW, DERIVABLE_IMPLS, MANUAL_IS_ASCII_CHECK, MANUAL_REM_EUCLID, MANUAL_RETAIN, TYPE_REPETITION_IN_BOUNDS, TUPLE_ARRAY_CONVERSIONS, MANUAL_TRY_FOLD, MANUAL_HASH_ONE. /// /// The minimum rust version that the project supports (msrv: Option = None), diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 0faff05eb2319..df839c2106f14 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -19,7 +19,7 @@ macro_rules! msrv_aliases { // names may refer to stabilized feature flags or library items msrv_aliases! { - 1,71,0 { TUPLE_ARRAY_CONVERSIONS } + 1,71,0 { TUPLE_ARRAY_CONVERSIONS, BUILD_HASHER_HASH_ONE } 1,70,0 { OPTION_IS_SOME_AND, BINARY_HEAP_RETAIN } 1,68,0 { PATH_MAIN_SEPARATOR_STR } 1,65,0 { LET_ELSE, POINTER_CAST_CONSTNESS } diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs index 3b47a451345eb..d752fe7d97ebf 100644 --- a/clippy_utils/src/visitors.rs +++ b/clippy_utils/src/visitors.rs @@ -62,6 +62,27 @@ where } } } +impl<'tcx, A, B> Visitable<'tcx> for (A, B) +where + A: Visitable<'tcx>, + B: Visitable<'tcx>, +{ + fn visit>(self, visitor: &mut V) { + let (a, b) = self; + a.visit(visitor); + b.visit(visitor); + } +} +impl<'tcx, T> Visitable<'tcx> for Option +where + T: Visitable<'tcx>, +{ + fn visit>(self, visitor: &mut V) { + if let Some(x) = self { + x.visit(visitor); + } + } +} macro_rules! visitable_ref { ($t:ident, $f:ident) => { impl<'tcx> Visitable<'tcx> for &'tcx $t<'tcx> { @@ -748,3 +769,26 @@ pub fn contains_break_or_continue(expr: &Expr<'_>) -> bool { }) .is_some() } + +/// If the local is only used once in `visitable` returns the path expression referencing the given +/// local +pub fn local_used_once<'tcx>( + cx: &LateContext<'tcx>, + visitable: impl Visitable<'tcx>, + id: HirId, +) -> Option<&'tcx Expr<'tcx>> { + let mut expr = None; + + let cf = for_each_expr_with_closures(cx, visitable, |e| { + if path_to_local_id(e, id) && expr.replace(e).is_some() { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) + } + }); + if cf.is_some() { + return None; + } + + expr +} diff --git a/tests/ui/manual_hash_one.fixed b/tests/ui/manual_hash_one.fixed new file mode 100644 index 0000000000000..edfd9c4a47bba --- /dev/null +++ b/tests/ui/manual_hash_one.fixed @@ -0,0 +1,89 @@ +#![warn(clippy::manual_hash_one)] +#![allow(clippy::needless_borrows_for_generic_args)] + +use std::hash::{BuildHasher, Hash, Hasher}; + +fn returned(b: impl BuildHasher) -> u64 { + + + b.hash_one(&true) +} + +fn unsized_receiver(b: impl BuildHasher, s: &str) { + + + let _ = b.hash_one(&s[4..10]); +} + +fn owned_value(b: impl BuildHasher, v: Vec) -> Vec { + + + let _ = b.hash_one(&v); + v +} + +fn reused_hasher(b: impl BuildHasher) { + let mut hasher = b.build_hasher(); + true.hash(&mut hasher); + let _ = hasher.finish(); + let _ = hasher.finish(); +} + +fn reused_hasher_in_return(b: impl BuildHasher) -> u64 { + let mut hasher = b.build_hasher(); + true.hash(&mut hasher); + let _ = hasher.finish(); + hasher.finish() +} + +fn no_hash(b: impl BuildHasher) { + let mut hasher = b.build_hasher(); + let _ = hasher.finish(); +} + +fn hash_twice(b: impl BuildHasher) { + let mut hasher = b.build_hasher(); + true.hash(&mut hasher); + true.hash(&mut hasher); + let _ = hasher.finish(); +} + +fn other_hasher(b: impl BuildHasher) { + let mut other_hasher = b.build_hasher(); + + let mut hasher = b.build_hasher(); + true.hash(&mut other_hasher); + let _ = hasher.finish(); +} + +fn finish_then_hash(b: impl BuildHasher) { + let mut hasher = b.build_hasher(); + let _ = hasher.finish(); + true.hash(&mut hasher); +} + +fn in_macro(b: impl BuildHasher) { + macro_rules! m { + ($b:expr) => {{ + let mut hasher = $b.build_hasher(); + true.hash(&mut hasher); + let _ = hasher.finish(); + }}; + } + + m!(b); +} + +#[clippy::msrv = "1.70"] +fn msrv_1_70(b: impl BuildHasher, v: impl Hash) { + let mut hasher = b.build_hasher(); + v.hash(&mut hasher); + let _ = hasher.finish(); +} + +#[clippy::msrv = "1.71"] +fn msrv_1_71(b: impl BuildHasher, v: impl Hash) { + + + let _ = b.hash_one(&v); +} diff --git a/tests/ui/manual_hash_one.rs b/tests/ui/manual_hash_one.rs new file mode 100644 index 0000000000000..ee61522853f0f --- /dev/null +++ b/tests/ui/manual_hash_one.rs @@ -0,0 +1,89 @@ +#![warn(clippy::manual_hash_one)] +#![allow(clippy::needless_borrows_for_generic_args)] + +use std::hash::{BuildHasher, Hash, Hasher}; + +fn returned(b: impl BuildHasher) -> u64 { + let mut hasher = b.build_hasher(); + true.hash(&mut hasher); + hasher.finish() +} + +fn unsized_receiver(b: impl BuildHasher, s: &str) { + let mut hasher = b.build_hasher(); + s[4..10].hash(&mut hasher); + let _ = hasher.finish(); +} + +fn owned_value(b: impl BuildHasher, v: Vec) -> Vec { + let mut hasher = b.build_hasher(); + v.hash(&mut hasher); + let _ = hasher.finish(); + v +} + +fn reused_hasher(b: impl BuildHasher) { + let mut hasher = b.build_hasher(); + true.hash(&mut hasher); + let _ = hasher.finish(); + let _ = hasher.finish(); +} + +fn reused_hasher_in_return(b: impl BuildHasher) -> u64 { + let mut hasher = b.build_hasher(); + true.hash(&mut hasher); + let _ = hasher.finish(); + hasher.finish() +} + +fn no_hash(b: impl BuildHasher) { + let mut hasher = b.build_hasher(); + let _ = hasher.finish(); +} + +fn hash_twice(b: impl BuildHasher) { + let mut hasher = b.build_hasher(); + true.hash(&mut hasher); + true.hash(&mut hasher); + let _ = hasher.finish(); +} + +fn other_hasher(b: impl BuildHasher) { + let mut other_hasher = b.build_hasher(); + + let mut hasher = b.build_hasher(); + true.hash(&mut other_hasher); + let _ = hasher.finish(); +} + +fn finish_then_hash(b: impl BuildHasher) { + let mut hasher = b.build_hasher(); + let _ = hasher.finish(); + true.hash(&mut hasher); +} + +fn in_macro(b: impl BuildHasher) { + macro_rules! m { + ($b:expr) => {{ + let mut hasher = $b.build_hasher(); + true.hash(&mut hasher); + let _ = hasher.finish(); + }}; + } + + m!(b); +} + +#[clippy::msrv = "1.70"] +fn msrv_1_70(b: impl BuildHasher, v: impl Hash) { + let mut hasher = b.build_hasher(); + v.hash(&mut hasher); + let _ = hasher.finish(); +} + +#[clippy::msrv = "1.71"] +fn msrv_1_71(b: impl BuildHasher, v: impl Hash) { + let mut hasher = b.build_hasher(); + v.hash(&mut hasher); + let _ = hasher.finish(); +} diff --git a/tests/ui/manual_hash_one.stderr b/tests/ui/manual_hash_one.stderr new file mode 100644 index 0000000000000..3ce6f41e1f910 --- /dev/null +++ b/tests/ui/manual_hash_one.stderr @@ -0,0 +1,56 @@ +error: manual implementation of `BuildHasher::hash_one` + --> $DIR/manual_hash_one.rs:9:5 + | +LL | hasher.finish() + | ^^^^^^^^^^^^^^^ + | + = note: `-D clippy::manual-hash-one` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::manual_hash_one)]` +help: try + | +LL ~ +LL ~ +LL ~ b.hash_one(&true) + | + +error: manual implementation of `BuildHasher::hash_one` + --> $DIR/manual_hash_one.rs:15:13 + | +LL | let _ = hasher.finish(); + | ^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ +LL ~ +LL ~ let _ = b.hash_one(&s[4..10]); + | + +error: manual implementation of `BuildHasher::hash_one` + --> $DIR/manual_hash_one.rs:21:13 + | +LL | let _ = hasher.finish(); + | ^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ +LL ~ +LL ~ let _ = b.hash_one(&v); + | + +error: manual implementation of `BuildHasher::hash_one` + --> $DIR/manual_hash_one.rs:88:13 + | +LL | let _ = hasher.finish(); + | ^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ +LL ~ +LL ~ let _ = b.hash_one(&v); + | + +error: aborting due to 4 previous errors + From 62b8ef304adf92e2f5d3d8f172fa1e4661183692 Mon Sep 17 00:00:00 2001 From: unexge Date: Wed, 27 Sep 2023 22:06:01 +0100 Subject: [PATCH 15/48] Mention that `missing_assert_message` lint ignores test functions --- clippy_lints/src/missing_assert_message.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/clippy_lints/src/missing_assert_message.rs b/clippy_lints/src/missing_assert_message.rs index c17f00c427515..136947a2c8cd3 100644 --- a/clippy_lints/src/missing_assert_message.rs +++ b/clippy_lints/src/missing_assert_message.rs @@ -15,6 +15,10 @@ declare_clippy_lint! { /// A good custom message should be more about why the failure of the assertion is problematic /// and not what is failed because the assertion already conveys that. /// + /// Although the same reasoning applies to testing functions, this lint ignores them as they would be too noisy. + /// Also, in most cases understanding the test failure would be easier + /// compared to understanding a complex invariant distributed around the codebase. + /// /// ### Known problems /// This lint cannot check the quality of the custom panic messages. /// Hence, you can suppress this lint simply by adding placeholder messages From b413bf6c4ee671df153b930dc2ba5b159c652c1d Mon Sep 17 00:00:00 2001 From: koka Date: Thu, 28 Sep 2023 17:12:00 +0900 Subject: [PATCH 16/48] Fix index of the remaining positional arguments --- clippy_lints/src/write.rs | 94 +++++++++++++++++++++++---------- tests/ui/print_literal.fixed | 4 -- tests/ui/print_literal.rs | 4 -- tests/ui/print_literal.stderr | 72 +++++-------------------- tests/ui/write_literal.fixed | 14 +++-- tests/ui/write_literal.rs | 14 +++-- tests/ui/write_literal.stderr | 74 +++++++++++++------------- tests/ui/write_literal_2.rs | 6 +-- tests/ui/write_literal_2.stderr | 53 ++++--------------- 9 files changed, 147 insertions(+), 188 deletions(-) diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index da083fb14aae1..21b675ff67983 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -3,12 +3,15 @@ use clippy_utils::macros::{find_format_args, format_arg_removal_span, root_macro use clippy_utils::source::{expand_past_previous_comma, snippet_opt}; use clippy_utils::{is_in_cfg_test, is_in_test_function}; use rustc_ast::token::LitKind; -use rustc_ast::{FormatArgPosition, FormatArgs, FormatArgsPiece, FormatOptions, FormatPlaceholder, FormatTrait}; +use rustc_ast::{ + FormatArgPosition, FormatArgPositionKind, FormatArgs, FormatArgsPiece, FormatOptions, FormatPlaceholder, + FormatTrait, +}; use rustc_errors::Applicability; use rustc_hir::{Expr, Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::{sym, BytePos}; +use rustc_span::{sym, BytePos, Span}; declare_clippy_lint! { /// ### What it does @@ -450,6 +453,12 @@ fn check_empty_string(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) { let arg_index = |argument: &FormatArgPosition| argument.index.unwrap_or_else(|pos| pos); + let lint_name = if name.starts_with("write") { + WRITE_LITERAL + } else { + PRINT_LITERAL + }; + let mut counts = vec![0u32; format_args.arguments.all_args().len()]; for piece in &format_args.template { if let FormatArgsPiece::Placeholder(placeholder) = piece { @@ -457,6 +466,12 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) { } } + let mut suggestion: Vec<(Span, String)> = vec![]; + // holds index of replaced positional arguments; used to decrement the index of the remaining + // positional arguments. + let mut replaced_position: Vec = vec![]; + let mut sug_span: Option = None; + for piece in &format_args.template { if let FormatArgsPiece::Placeholder(FormatPlaceholder { argument, @@ -493,12 +508,6 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) { _ => continue, }; - let lint = if name.starts_with("write") { - WRITE_LITERAL - } else { - PRINT_LITERAL - }; - let Some(format_string_snippet) = snippet_opt(cx, format_args.span) else { continue }; let format_string_is_raw = format_string_snippet.starts_with('r'); @@ -519,29 +528,58 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) { }, }; - span_lint_and_then( - cx, - lint, - arg.expr.span, - "literal with an empty format string", - |diag| { - if let Some(replacement) = replacement - // `format!("{}", "a")`, `format!("{named}", named = "b") - // ~~~~~ ~~~~~~~~~~~~~ - && let Some(removal_span) = format_arg_removal_span(format_args, index) - { - let replacement = replacement.replace('{', "{{").replace('}', "}}"); - diag.multipart_suggestion( - "try", - vec![(*placeholder_span, replacement), (removal_span, String::new())], - Applicability::MachineApplicable, - ); - } - }, - ); + sug_span = Some(sug_span.unwrap_or(arg.expr.span).to(arg.expr.span)); + if let Some((_, index)) = positional_arg_piece_span(piece) { + replaced_position.push(index); + } + + if let Some(replacement) = replacement + // `format!("{}", "a")`, `format!("{named}", named = "b") + // ~~~~~ ~~~~~~~~~~~~~ + && let Some(removal_span) = format_arg_removal_span(format_args, index) { + let replacement = replacement.replace('{', "{{").replace('}', "}}"); + suggestion.push((*placeholder_span, replacement)); + suggestion.push((removal_span, String::new())); + } } } + + // Decrement the index of the remaining by the number of replaced positional arguments + if !suggestion.is_empty() { + for piece in &format_args.template { + if let Some((span, index)) = positional_arg_piece_span(piece) + && suggestion.iter().all(|(s, _)| *s != span) { + let decrement = replaced_position.iter().filter(|i| **i < index).count(); + suggestion.push((span, format!("{{{}}}", index.saturating_sub(decrement)))); + } + } + } + + if let Some(span) = sug_span { + span_lint_and_then(cx, lint_name, span, "literal with an empty format string", |diag| { + if !suggestion.is_empty() { + diag.multipart_suggestion("try", suggestion, Applicability::MachineApplicable); + } + }); + } +} + +/// Extract Span and its index from the given `piece`, iff it's positional argument. +fn positional_arg_piece_span(piece: &FormatArgsPiece) -> Option<(Span, usize)> { + match piece { + FormatArgsPiece::Placeholder(FormatPlaceholder { + argument: + FormatArgPosition { + index: Ok(index), + kind: FormatArgPositionKind::Number, + .. + }, + span: Some(span), + .. + }) => Some((*span, *index)), + _ => None, + } } /// Removes the raw marker, `#`s and quotes from a str, and returns if the literal is raw diff --git a/tests/ui/print_literal.fixed b/tests/ui/print_literal.fixed index 88cd3a54b4100..04a484258a4a8 100644 --- a/tests/ui/print_literal.fixed +++ b/tests/ui/print_literal.fixed @@ -39,18 +39,14 @@ fn main() { // throw a warning println!("hello world"); //~^ ERROR: literal with an empty format string - //~| ERROR: literal with an empty format string println!("world hello"); //~^ ERROR: literal with an empty format string - //~| ERROR: literal with an empty format string // named args shouldn't change anything either println!("hello world"); //~^ ERROR: literal with an empty format string - //~| ERROR: literal with an empty format string println!("world hello"); //~^ ERROR: literal with an empty format string - //~| ERROR: literal with an empty format string // The string literal from `file!()` has a callsite span that isn't marked as coming from an // expansion diff --git a/tests/ui/print_literal.rs b/tests/ui/print_literal.rs index bd7444c9606ab..38c036f56c5d0 100644 --- a/tests/ui/print_literal.rs +++ b/tests/ui/print_literal.rs @@ -39,18 +39,14 @@ fn main() { // throw a warning println!("{0} {1}", "hello", "world"); //~^ ERROR: literal with an empty format string - //~| ERROR: literal with an empty format string println!("{1} {0}", "hello", "world"); //~^ ERROR: literal with an empty format string - //~| ERROR: literal with an empty format string // named args shouldn't change anything either println!("{foo} {bar}", foo = "hello", bar = "world"); //~^ ERROR: literal with an empty format string - //~| ERROR: literal with an empty format string println!("{bar} {foo}", foo = "hello", bar = "world"); //~^ ERROR: literal with an empty format string - //~| ERROR: literal with an empty format string // The string literal from `file!()` has a callsite span that isn't marked as coming from an // expansion diff --git a/tests/ui/print_literal.stderr b/tests/ui/print_literal.stderr index 1d9751b92e9c5..b2f85be9d94a5 100644 --- a/tests/ui/print_literal.stderr +++ b/tests/ui/print_literal.stderr @@ -52,97 +52,49 @@ error: literal with an empty format string --> $DIR/print_literal.rs:40:25 | LL | println!("{0} {1}", "hello", "world"); - | ^^^^^^^ + | ^^^^^^^^^^^^^^^^ | help: try | LL - println!("{0} {1}", "hello", "world"); -LL + println!("hello {1}", "world"); +LL + println!("hello world"); | error: literal with an empty format string - --> $DIR/print_literal.rs:40:34 - | -LL | println!("{0} {1}", "hello", "world"); - | ^^^^^^^ - | -help: try - | -LL - println!("{0} {1}", "hello", "world"); -LL + println!("{0} world", "hello"); - | - -error: literal with an empty format string - --> $DIR/print_literal.rs:43:34 + --> $DIR/print_literal.rs:42:25 | LL | println!("{1} {0}", "hello", "world"); - | ^^^^^^^ + | ^^^^^^^^^^^^^^^^ | help: try | LL - println!("{1} {0}", "hello", "world"); -LL + println!("world {0}", "hello"); - | - -error: literal with an empty format string - --> $DIR/print_literal.rs:43:25 - | -LL | println!("{1} {0}", "hello", "world"); - | ^^^^^^^ - | -help: try - | -LL - println!("{1} {0}", "hello", "world"); -LL + println!("{1} hello", "world"); - | - -error: literal with an empty format string - --> $DIR/print_literal.rs:48:35 - | -LL | println!("{foo} {bar}", foo = "hello", bar = "world"); - | ^^^^^^^ - | -help: try - | -LL - println!("{foo} {bar}", foo = "hello", bar = "world"); -LL + println!("hello {bar}", bar = "world"); +LL + println!("world hello"); | error: literal with an empty format string - --> $DIR/print_literal.rs:48:50 + --> $DIR/print_literal.rs:46:35 | LL | println!("{foo} {bar}", foo = "hello", bar = "world"); - | ^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^ | help: try | LL - println!("{foo} {bar}", foo = "hello", bar = "world"); -LL + println!("{foo} world", foo = "hello"); +LL + println!("hello world"); | error: literal with an empty format string - --> $DIR/print_literal.rs:51:50 - | -LL | println!("{bar} {foo}", foo = "hello", bar = "world"); - | ^^^^^^^ - | -help: try - | -LL - println!("{bar} {foo}", foo = "hello", bar = "world"); -LL + println!("world {foo}", foo = "hello"); - | - -error: literal with an empty format string - --> $DIR/print_literal.rs:51:35 + --> $DIR/print_literal.rs:48:35 | LL | println!("{bar} {foo}", foo = "hello", bar = "world"); - | ^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^ | help: try | LL - println!("{bar} {foo}", foo = "hello", bar = "world"); -LL + println!("{bar} hello", bar = "world"); +LL + println!("world hello"); | -error: aborting due to 12 previous errors +error: aborting due to 8 previous errors diff --git a/tests/ui/write_literal.fixed b/tests/ui/write_literal.fixed index ee577574d2898..3d216b76cbf3d 100644 --- a/tests/ui/write_literal.fixed +++ b/tests/ui/write_literal.fixed @@ -43,16 +43,22 @@ fn main() { // throw a warning writeln!(v, "hello world"); //~^ ERROR: literal with an empty format string - //~| ERROR: literal with an empty format string writeln!(v, "world hello"); //~^ ERROR: literal with an empty format string - //~| ERROR: literal with an empty format string // named args shouldn't change anything either writeln!(v, "hello world"); //~^ ERROR: literal with an empty format string - //~| ERROR: literal with an empty format string writeln!(v, "world hello"); //~^ ERROR: literal with an empty format string - //~| ERROR: literal with an empty format string + + // #10128 + writeln!(v, "hello {0} world", 2); + //~^ ERROR: literal with an empty format string + writeln!(v, "world {0} hello", 2); + //~^ ERROR: literal with an empty format string + writeln!(v, "hello {0} {1}, {bar}", 2, 3, bar = 4); + //~^ ERROR: literal with an empty format string + writeln!(v, "hello {0} {1}, world {2}", 2, 3, 4); + //~^ ERROR: literal with an empty format string } diff --git a/tests/ui/write_literal.rs b/tests/ui/write_literal.rs index 588e8fd413a48..79d6daa2e3b5e 100644 --- a/tests/ui/write_literal.rs +++ b/tests/ui/write_literal.rs @@ -43,16 +43,22 @@ fn main() { // throw a warning writeln!(v, "{0} {1}", "hello", "world"); //~^ ERROR: literal with an empty format string - //~| ERROR: literal with an empty format string writeln!(v, "{1} {0}", "hello", "world"); //~^ ERROR: literal with an empty format string - //~| ERROR: literal with an empty format string // named args shouldn't change anything either writeln!(v, "{foo} {bar}", foo = "hello", bar = "world"); //~^ ERROR: literal with an empty format string - //~| ERROR: literal with an empty format string writeln!(v, "{bar} {foo}", foo = "hello", bar = "world"); //~^ ERROR: literal with an empty format string - //~| ERROR: literal with an empty format string + + // #10128 + writeln!(v, "{0} {1} {2}", "hello", 2, "world"); + //~^ ERROR: literal with an empty format string + writeln!(v, "{2} {1} {0}", "hello", 2, "world"); + //~^ ERROR: literal with an empty format string + writeln!(v, "{0} {1} {2}, {bar}", "hello", 2, 3, bar = 4); + //~^ ERROR: literal with an empty format string + writeln!(v, "{0} {1} {2}, {3} {4}", "hello", 2, 3, "world", 4); + //~^ ERROR: literal with an empty format string } diff --git a/tests/ui/write_literal.stderr b/tests/ui/write_literal.stderr index 372a54cf769fb..ee0d536e954f3 100644 --- a/tests/ui/write_literal.stderr +++ b/tests/ui/write_literal.stderr @@ -52,96 +52,96 @@ error: literal with an empty format string --> $DIR/write_literal.rs:44:28 | LL | writeln!(v, "{0} {1}", "hello", "world"); - | ^^^^^^^ + | ^^^^^^^^^^^^^^^^ | help: try | LL - writeln!(v, "{0} {1}", "hello", "world"); -LL + writeln!(v, "hello {1}", "world"); +LL + writeln!(v, "hello world"); | error: literal with an empty format string - --> $DIR/write_literal.rs:44:37 + --> $DIR/write_literal.rs:46:28 | -LL | writeln!(v, "{0} {1}", "hello", "world"); - | ^^^^^^^ +LL | writeln!(v, "{1} {0}", "hello", "world"); + | ^^^^^^^^^^^^^^^^ | help: try | -LL - writeln!(v, "{0} {1}", "hello", "world"); -LL + writeln!(v, "{0} world", "hello"); +LL - writeln!(v, "{1} {0}", "hello", "world"); +LL + writeln!(v, "world hello"); | error: literal with an empty format string - --> $DIR/write_literal.rs:47:37 + --> $DIR/write_literal.rs:50:38 | -LL | writeln!(v, "{1} {0}", "hello", "world"); - | ^^^^^^^ +LL | writeln!(v, "{foo} {bar}", foo = "hello", bar = "world"); + | ^^^^^^^^^^^^^^^^^^^^^^ | help: try | -LL - writeln!(v, "{1} {0}", "hello", "world"); -LL + writeln!(v, "world {0}", "hello"); +LL - writeln!(v, "{foo} {bar}", foo = "hello", bar = "world"); +LL + writeln!(v, "hello world"); | error: literal with an empty format string - --> $DIR/write_literal.rs:47:28 + --> $DIR/write_literal.rs:52:38 | -LL | writeln!(v, "{1} {0}", "hello", "world"); - | ^^^^^^^ +LL | writeln!(v, "{bar} {foo}", foo = "hello", bar = "world"); + | ^^^^^^^^^^^^^^^^^^^^^^ | help: try | -LL - writeln!(v, "{1} {0}", "hello", "world"); -LL + writeln!(v, "{1} hello", "world"); +LL - writeln!(v, "{bar} {foo}", foo = "hello", bar = "world"); +LL + writeln!(v, "world hello"); | error: literal with an empty format string - --> $DIR/write_literal.rs:52:38 + --> $DIR/write_literal.rs:56:32 | -LL | writeln!(v, "{foo} {bar}", foo = "hello", bar = "world"); - | ^^^^^^^ +LL | writeln!(v, "{0} {1} {2}", "hello", 2, "world"); + | ^^^^^^^^^^^^^^^^^^^ | help: try | -LL - writeln!(v, "{foo} {bar}", foo = "hello", bar = "world"); -LL + writeln!(v, "hello {bar}", bar = "world"); +LL - writeln!(v, "{0} {1} {2}", "hello", 2, "world"); +LL + writeln!(v, "hello {0} world", 2); | error: literal with an empty format string - --> $DIR/write_literal.rs:52:53 + --> $DIR/write_literal.rs:58:32 | -LL | writeln!(v, "{foo} {bar}", foo = "hello", bar = "world"); - | ^^^^^^^ +LL | writeln!(v, "{2} {1} {0}", "hello", 2, "world"); + | ^^^^^^^^^^^^^^^^^^^ | help: try | -LL - writeln!(v, "{foo} {bar}", foo = "hello", bar = "world"); -LL + writeln!(v, "{foo} world", foo = "hello"); +LL - writeln!(v, "{2} {1} {0}", "hello", 2, "world"); +LL + writeln!(v, "world {0} hello", 2); | error: literal with an empty format string - --> $DIR/write_literal.rs:55:53 + --> $DIR/write_literal.rs:60:39 | -LL | writeln!(v, "{bar} {foo}", foo = "hello", bar = "world"); - | ^^^^^^^ +LL | writeln!(v, "{0} {1} {2}, {bar}", "hello", 2, 3, bar = 4); + | ^^^^^^^ | help: try | -LL - writeln!(v, "{bar} {foo}", foo = "hello", bar = "world"); -LL + writeln!(v, "world {foo}", foo = "hello"); +LL - writeln!(v, "{0} {1} {2}, {bar}", "hello", 2, 3, bar = 4); +LL + writeln!(v, "hello {0} {1}, {bar}", 2, 3, bar = 4); | error: literal with an empty format string - --> $DIR/write_literal.rs:55:38 + --> $DIR/write_literal.rs:62:41 | -LL | writeln!(v, "{bar} {foo}", foo = "hello", bar = "world"); - | ^^^^^^^ +LL | writeln!(v, "{0} {1} {2}, {3} {4}", "hello", 2, 3, "world", 4); + | ^^^^^^^^^^^^^^^^^^^^^^ | help: try | -LL - writeln!(v, "{bar} {foo}", foo = "hello", bar = "world"); -LL + writeln!(v, "{bar} hello", bar = "world"); +LL - writeln!(v, "{0} {1} {2}, {3} {4}", "hello", 2, 3, "world", 4); +LL + writeln!(v, "hello {0} {1}, world {2}", 2, 3, 4); | error: aborting due to 12 previous errors diff --git a/tests/ui/write_literal_2.rs b/tests/ui/write_literal_2.rs index 197e01149e0ad..b2ed552d46bcc 100644 --- a/tests/ui/write_literal_2.rs +++ b/tests/ui/write_literal_2.rs @@ -31,10 +31,7 @@ fn main() { v, "some {}\ {} \\ {}", - "1", - "2", - "3", - //~^ ERROR: literal with an empty format string + "1", "2", "3", ); writeln!(v, "{}", "\\"); //~^ ERROR: literal with an empty format string @@ -49,7 +46,6 @@ fn main() { // hard mode writeln!(v, r#"{}{}"#, '#', '"'); //~^ ERROR: literal with an empty format string - //~| ERROR: literal with an empty format string // should not lint writeln!(v, r"{}", "\r"); } diff --git a/tests/ui/write_literal_2.stderr b/tests/ui/write_literal_2.stderr index 9a16d2f240dd8..81ef49de082e7 100644 --- a/tests/ui/write_literal_2.stderr +++ b/tests/ui/write_literal_2.stderr @@ -82,42 +82,17 @@ LL ~ world!", error: literal with an empty format string --> $DIR/write_literal_2.rs:34:9 | -LL | "1", - | ^^^ +LL | "1", "2", "3", + | ^^^^^^^^^^^^^ | help: try | LL ~ "some 1\ -LL ~ {} \\ {}", +LL ~ 2 \\ 3", | error: literal with an empty format string - --> $DIR/write_literal_2.rs:35:9 - | -LL | "2", - | ^^^ - | -help: try - | -LL ~ 2 \\ {}", -LL ~ "1", - | - -error: literal with an empty format string - --> $DIR/write_literal_2.rs:36:9 - | -LL | "3", - | ^^^ - | -help: try - | -LL ~ {} \\ 3", -LL | "1", -LL ~ "2", - | - -error: literal with an empty format string - --> $DIR/write_literal_2.rs:39:23 + --> $DIR/write_literal_2.rs:36:23 | LL | writeln!(v, "{}", "\\"); | ^^^^ @@ -129,7 +104,7 @@ LL + writeln!(v, "\\"); | error: literal with an empty format string - --> $DIR/write_literal_2.rs:41:24 + --> $DIR/write_literal_2.rs:38:24 | LL | writeln!(v, r"{}", "\\"); | ^^^^ @@ -141,7 +116,7 @@ LL + writeln!(v, r"\"); | error: literal with an empty format string - --> $DIR/write_literal_2.rs:43:26 + --> $DIR/write_literal_2.rs:40:26 | LL | writeln!(v, r#"{}"#, "\\"); | ^^^^ @@ -153,7 +128,7 @@ LL + writeln!(v, r#"\"#); | error: literal with an empty format string - --> $DIR/write_literal_2.rs:45:23 + --> $DIR/write_literal_2.rs:42:23 | LL | writeln!(v, "{}", r"\"); | ^^^^ @@ -165,7 +140,7 @@ LL + writeln!(v, "\\"); | error: literal with an empty format string - --> $DIR/write_literal_2.rs:47:23 + --> $DIR/write_literal_2.rs:44:23 | LL | writeln!(v, "{}", "\r"); | ^^^^ @@ -177,16 +152,10 @@ LL + writeln!(v, "\r"); | error: literal with an empty format string - --> $DIR/write_literal_2.rs:50:28 - | -LL | writeln!(v, r#"{}{}"#, '#', '"'); - | ^^^ - -error: literal with an empty format string - --> $DIR/write_literal_2.rs:50:33 + --> $DIR/write_literal_2.rs:47:28 | LL | writeln!(v, r#"{}{}"#, '#', '"'); - | ^^^ + | ^^^^^^^^ -error: aborting due to 17 previous errors +error: aborting due to 14 previous errors From 82207f4ff4ab4712a86e152656aaf1aa6a9299c6 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Sun, 27 Aug 2023 11:43:45 +0000 Subject: [PATCH 17/48] Move needless_raw_string_hashes to pedantic --- clippy_lints/src/raw_strings.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/raw_strings.rs b/clippy_lints/src/raw_strings.rs index 2895595e03908..8a7e487466610 100644 --- a/clippy_lints/src/raw_strings.rs +++ b/clippy_lints/src/raw_strings.rs @@ -50,7 +50,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.72.0"] pub NEEDLESS_RAW_STRING_HASHES, - style, + pedantic, "suggests reducing the number of hashes around a raw string literal" } impl_lint_pass!(RawStrings => [NEEDLESS_RAW_STRINGS, NEEDLESS_RAW_STRING_HASHES]); From 330ebbb9f9ee3c4bab37e330726ccc3ac1c26e4a Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Mon, 18 Sep 2023 00:19:34 +0200 Subject: [PATCH 18/48] new lint: `iter_without_into_iter` --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/iter_without_into_iter.rs | 135 +++++++++++++++++++ clippy_lints/src/lib.rs | 2 + tests/ui/iter_without_into_iter.rs | 120 +++++++++++++++++ tests/ui/iter_without_into_iter.stderr | 150 +++++++++++++++++++++ 6 files changed, 409 insertions(+) create mode 100644 clippy_lints/src/iter_without_into_iter.rs create mode 100644 tests/ui/iter_without_into_iter.rs create mode 100644 tests/ui/iter_without_into_iter.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index db54bfbf0b32f..40510d7880054 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5036,6 +5036,7 @@ Released 2018-09-13 [`iter_skip_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_next [`iter_skip_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_zero [`iter_with_drain`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_with_drain +[`iter_without_into_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_without_into_iter [`iterator_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iterator_step_by_zero [`just_underscores_and_digits`]: https://rust-lang.github.io/rust-clippy/master/index.html#just_underscores_and_digits [`large_const_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_const_arrays diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index b4b84b36044c6..06809038927ce 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -229,6 +229,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::items_after_statements::ITEMS_AFTER_STATEMENTS_INFO, crate::items_after_test_module::ITEMS_AFTER_TEST_MODULE_INFO, crate::iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR_INFO, + crate::iter_without_into_iter::ITER_WITHOUT_INTO_ITER_INFO, crate::large_const_arrays::LARGE_CONST_ARRAYS_INFO, crate::large_enum_variant::LARGE_ENUM_VARIANT_INFO, crate::large_futures::LARGE_FUTURES_INFO, diff --git a/clippy_lints/src/iter_without_into_iter.rs b/clippy_lints/src/iter_without_into_iter.rs new file mode 100644 index 0000000000000..08a8e6dbf1899 --- /dev/null +++ b/clippy_lints/src/iter_without_into_iter.rs @@ -0,0 +1,135 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::get_parent_as_impl; +use clippy_utils::source::snippet; +use clippy_utils::ty::{implements_trait, make_normalized_projection}; +use rustc_errors::Applicability; +use rustc_hir::{FnRetTy, ImplItemKind, ImplicitSelfKind, TyKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// Looks for `iter` and `iter_mut` methods without an associated `IntoIterator for (&|&mut) Type` implementation. + /// + /// ### Why is this bad? + /// It's not bad, but having them is idiomatic and allows the type to be used in for loops directly + /// (`for val in &iter {}`), without having to first call `iter()` or `iter_mut()`. + /// + /// ### Example + /// ```rust + /// struct MySlice<'a>(&'a [u8]); + /// impl<'a> MySlice<'a> { + /// pub fn iter(&self) -> std::slice::Iter<'a, u8> { + /// self.0.iter() + /// } + /// } + /// ``` + /// Use instead: + /// ```rust + /// struct MySlice<'a>(&'a [u8]); + /// impl<'a> MySlice<'a> { + /// pub fn iter(&self) -> std::slice::Iter<'a, u8> { + /// self.0.iter() + /// } + /// } + /// impl<'a> IntoIterator for &MySlice<'a> { + /// type Item = &'a u8; + /// type IntoIter = std::slice::Iter<'a, u8>; + /// fn into_iter(self) -> Self::IntoIter { + /// self.iter() + /// } + /// } + /// ``` + #[clippy::version = "1.74.0"] + pub ITER_WITHOUT_INTO_ITER, + pedantic, + "implementing `iter(_mut)` without an associated `IntoIterator for (&|&mut) Type` impl" +} +declare_lint_pass!(IterWithoutIntoIter => [ITER_WITHOUT_INTO_ITER]); + +/// Checks if a given type is nameable in a trait (impl). +/// RPIT is stable, but impl Trait in traits is not (yet), so when we have +/// a function such as `fn iter(&self) -> impl IntoIterator`, we can't +/// suggest `type IntoIter = impl IntoIterator`. +fn is_nameable_in_impl_trait(ty: &rustc_hir::Ty<'_>) -> bool { + !matches!(ty.kind, TyKind::OpaqueDef(..)) +} + +impl LateLintPass<'_> for IterWithoutIntoIter { + fn check_impl_item(&mut self, cx: &LateContext<'_>, item: &rustc_hir::ImplItem<'_>) { + let item_did = item.owner_id.to_def_id(); + let (borrow_prefix, expected_implicit_self) = match item.ident.name { + sym::iter => ("&", ImplicitSelfKind::ImmRef), + sym::iter_mut => ("&mut ", ImplicitSelfKind::MutRef), + _ => return, + }; + + if let ImplItemKind::Fn(sig, _) = item.kind + && let FnRetTy::Return(ret) = sig.decl.output + && is_nameable_in_impl_trait(ret) + && cx.tcx.generics_of(item_did).params.is_empty() + && sig.decl.implicit_self == expected_implicit_self + && sig.decl.inputs.len() == 1 + && let Some(imp) = get_parent_as_impl(cx.tcx, item.hir_id()) + && imp.of_trait.is_none() + && let sig = cx.tcx.liberate_late_bound_regions( + item_did, + cx.tcx.fn_sig(item_did).instantiate_identity() + ) + && let ref_ty = sig.inputs()[0] + && let Some(into_iter_did) = cx.tcx.get_diagnostic_item(sym::IntoIterator) + && let Some(iterator_did) = cx.tcx.get_diagnostic_item(sym::Iterator) + && let ret_ty = sig.output() + // Order is important here, we need to check that the `fn iter` return type actually implements `IntoIterator` + // *before* normalizing `<_ as IntoIterator>::Item` (otherwise make_normalized_projection ICEs) + && implements_trait(cx, ret_ty, iterator_did, &[]) + && let Some(iter_ty) = make_normalized_projection( + cx.tcx, + cx.param_env, + iterator_did, + sym!(Item), + [ret_ty], + ) + // Only lint if the `IntoIterator` impl doesn't actually exist + && !implements_trait(cx, ref_ty, into_iter_did, &[]) + { + let self_ty_snippet = format!("{borrow_prefix}{}", snippet(cx, imp.self_ty.span, "..")); + + span_lint_and_then( + cx, + ITER_WITHOUT_INTO_ITER, + item.span, + &format!("`{}` method without an `IntoIterator` impl for `{self_ty_snippet}`", item.ident), + |diag| { + // Get the lower span of the `impl` block, and insert the suggestion right before it: + // impl X { + // ^ fn iter(&self) -> impl IntoIterator { ... } + // } + let span_behind_impl = cx.tcx + .def_span(cx.tcx.hir().parent_id(item.hir_id()).owner.def_id) + .shrink_to_lo(); + + let sugg = format!( +" +impl IntoIterator for {self_ty_snippet} {{ + type IntoIter = {ret_ty}; + type Iter = {iter_ty}; + fn into_iter() -> Self::IntoIter {{ + self.iter() + }} +}} +" + ); + diag.span_suggestion_verbose( + span_behind_impl, + format!("consider implementing `IntoIterator` for `{self_ty_snippet}`"), + sugg, + // Suggestion is on a best effort basis, might need some adjustments by the user + // such as adding some lifetimes in the associated types, or importing types. + Applicability::Unspecified, + ); + }); + } + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 63a3fbcb8976f..0f35ec2766574 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -169,6 +169,7 @@ mod invalid_upcast_comparisons; mod items_after_statements; mod items_after_test_module; mod iter_not_returning_iterator; +mod iter_without_into_iter; mod large_const_arrays; mod large_enum_variant; mod large_futures; @@ -1121,6 +1122,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: )) }); store.register_late_pass(move |_| Box::new(manual_hash_one::ManualHashOne::new(msrv()))); + store.register_late_pass(|_| Box::new(iter_without_into_iter::IterWithoutIntoIter)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/tests/ui/iter_without_into_iter.rs b/tests/ui/iter_without_into_iter.rs new file mode 100644 index 0000000000000..cedb756c79de1 --- /dev/null +++ b/tests/ui/iter_without_into_iter.rs @@ -0,0 +1,120 @@ +//@no-rustfix +#![warn(clippy::iter_without_into_iter)] + +fn main() { + { + struct S; + impl S { + pub fn iter(&self) -> std::slice::Iter<'_, u8> { + //~^ ERROR: `iter` method without an `IntoIterator` impl + [].iter() + } + pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, u8> { + //~^ ERROR: `iter_mut` method without an `IntoIterator` impl + [].iter_mut() + } + } + } + { + struct S; + impl S { + pub fn iter(&self) -> impl Iterator { + // RPITIT is not stable, so we can't generally suggest it here yet + [].iter() + } + } + } + { + struct S<'a>(&'a mut [u8]); + impl<'a> S<'a> { + pub fn iter(&self) -> std::slice::Iter<'_, u8> { + //~^ ERROR: `iter` method without an `IntoIterator` impl + self.0.iter() + } + pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, u8> { + //~^ ERROR: `iter_mut` method without an `IntoIterator` impl + self.0.iter_mut() + } + } + } + { + // Incompatible signatures + struct S; + impl S { + pub fn iter(self) -> std::slice::Iter<'static, u8> { + todo!() + } + } + struct S2; + impl S2 { + pub async fn iter(&self) -> std::slice::Iter<'static, u8> { + todo!() + } + } + struct S3; + impl S3 { + pub fn iter(&self, _additional_param: ()) -> std::slice::Iter<'static, u8> { + todo!() + } + } + struct S4(T); + impl S4 { + pub fn iter(&self) -> std::slice::Iter<'static, (T, U)> { + todo!() + } + } + struct S5(T); + impl S5 { + pub fn iter(&self) -> std::slice::Iter<'static, T> { + todo!() + } + } + } + { + struct S(T); + impl S { + pub fn iter(&self) -> std::slice::Iter<'_, T> { + //~^ ERROR: `iter` method without an `IntoIterator` impl + todo!() + } + pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, T> { + //~^ ERROR: `iter_mut` method without an `IntoIterator` impl + todo!() + } + } + } + { + struct S(T); + impl S { + pub fn iter(&self) -> std::slice::Iter<'_, T> { + // Don't lint, there's an existing (wrong) IntoIterator impl + todo!() + } + } + + impl<'a, T> IntoIterator for &'a S { + type Item = &'a String; + type IntoIter = std::slice::Iter<'a, String>; + fn into_iter(self) -> Self::IntoIter { + todo!() + } + } + } + { + struct S(T); + impl S { + pub fn iter_mut(&self) -> std::slice::IterMut<'_, T> { + // Don't lint, there's an existing (wrong) IntoIterator impl + todo!() + } + } + + impl<'a, T> IntoIterator for &'a mut S { + type Item = &'a mut String; + type IntoIter = std::slice::IterMut<'a, String>; + fn into_iter(self) -> Self::IntoIter { + todo!() + } + } + } +} diff --git a/tests/ui/iter_without_into_iter.stderr b/tests/ui/iter_without_into_iter.stderr new file mode 100644 index 0000000000000..9d0b99415a50e --- /dev/null +++ b/tests/ui/iter_without_into_iter.stderr @@ -0,0 +1,150 @@ +error: `iter` method without an `IntoIterator` impl for `&S` + --> $DIR/iter_without_into_iter.rs:8:13 + | +LL | / pub fn iter(&self) -> std::slice::Iter<'_, u8> { +LL | | +LL | | [].iter() +LL | | } + | |_____________^ + | + = note: `-D clippy::iter-without-into-iter` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::iter_without_into_iter)]` +help: consider implementing `IntoIterator` for `&S` + | +LL ~ +LL + impl IntoIterator for &S { +LL + type IntoIter = std::slice::Iter<'_, u8>; +LL + type Iter = &u8; +LL + fn into_iter() -> Self::IntoIter { +LL + self.iter() +LL + } +LL + } + | + +error: `iter_mut` method without an `IntoIterator` impl for `&mut S` + --> $DIR/iter_without_into_iter.rs:12:13 + | +LL | / pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, u8> { +LL | | +LL | | [].iter_mut() +LL | | } + | |_____________^ + | +help: consider implementing `IntoIterator` for `&mut S` + | +LL ~ +LL + impl IntoIterator for &mut S { +LL + type IntoIter = std::slice::IterMut<'_, u8>; +LL + type Iter = &mut u8; +LL + fn into_iter() -> Self::IntoIter { +LL + self.iter() +LL + } +LL + } + | + +error: `iter` method without an `IntoIterator` impl for `&S<'a>` + --> $DIR/iter_without_into_iter.rs:30:13 + | +LL | / pub fn iter(&self) -> std::slice::Iter<'_, u8> { +LL | | +LL | | self.0.iter() +LL | | } + | |_____________^ + | +help: consider implementing `IntoIterator` for `&S<'a>` + | +LL ~ +LL + impl IntoIterator for &S<'a> { +LL + type IntoIter = std::slice::Iter<'_, u8>; +LL + type Iter = &u8; +LL + fn into_iter() -> Self::IntoIter { +LL + self.iter() +LL + } +LL + } + | + +error: `iter_mut` method without an `IntoIterator` impl for `&mut S<'a>` + --> $DIR/iter_without_into_iter.rs:34:13 + | +LL | / pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, u8> { +LL | | +LL | | self.0.iter_mut() +LL | | } + | |_____________^ + | +help: consider implementing `IntoIterator` for `&mut S<'a>` + | +LL ~ +LL + impl IntoIterator for &mut S<'a> { +LL + type IntoIter = std::slice::IterMut<'_, u8>; +LL + type Iter = &mut u8; +LL + fn into_iter() -> Self::IntoIter { +LL + self.iter() +LL + } +LL + } + | + +error: `iter` method without an `IntoIterator` impl for `&S5` + --> $DIR/iter_without_into_iter.rs:68:13 + | +LL | / pub fn iter(&self) -> std::slice::Iter<'static, T> { +LL | | todo!() +LL | | } + | |_____________^ + | +help: consider implementing `IntoIterator` for `&S5` + | +LL ~ +LL + impl IntoIterator for &S5 { +LL + type IntoIter = std::slice::Iter<'static, T>; +LL + type Iter = &T; +LL + fn into_iter() -> Self::IntoIter { +LL + self.iter() +LL + } +LL + } + | + +error: `iter` method without an `IntoIterator` impl for `&S` + --> $DIR/iter_without_into_iter.rs:76:13 + | +LL | / pub fn iter(&self) -> std::slice::Iter<'_, T> { +LL | | +LL | | todo!() +LL | | } + | |_____________^ + | +help: consider implementing `IntoIterator` for `&S` + | +LL ~ +LL + impl IntoIterator for &S { +LL + type IntoIter = std::slice::Iter<'_, T>; +LL + type Iter = &T; +LL + fn into_iter() -> Self::IntoIter { +LL + self.iter() +LL + } +LL + } + | + +error: `iter_mut` method without an `IntoIterator` impl for `&mut S` + --> $DIR/iter_without_into_iter.rs:80:13 + | +LL | / pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, T> { +LL | | +LL | | todo!() +LL | | } + | |_____________^ + | +help: consider implementing `IntoIterator` for `&mut S` + | +LL ~ +LL + impl IntoIterator for &mut S { +LL + type IntoIter = std::slice::IterMut<'_, T>; +LL + type Iter = &mut T; +LL + fn into_iter() -> Self::IntoIter { +LL + self.iter() +LL + } +LL + } + | + +error: aborting due to 7 previous errors + From 38fd80a1123e56a24feda899ba4d2599a8d28637 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 28 Sep 2023 16:00:38 +0200 Subject: [PATCH 19/48] Remove `rustc_lint_defs::lint_array` --- .../src/utils/internal_lints/lint_without_lint_pass.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs b/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs index 87380f14f9a4d..bbb5ade8b9f36 100644 --- a/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs +++ b/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs @@ -28,8 +28,8 @@ declare_clippy_lint! { /// know the name of the lint. /// /// ### Known problems - /// Only checks for lints associated using the - /// `declare_lint_pass!`, `impl_lint_pass!`, and `lint_array!` macros. + /// Only checks for lints associated using the `declare_lint_pass!` and + /// `impl_lint_pass!` macros. /// /// ### Example /// ```rust,ignore From a4d11d936ec9750297734d0233e2d7dc5f8728af Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Thu, 28 Sep 2023 23:50:56 +0000 Subject: [PATCH 20/48] Reverse postorder instead of using reversed postorder --- clippy_utils/src/mir/mod.rs | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/clippy_utils/src/mir/mod.rs b/clippy_utils/src/mir/mod.rs index f04467dc19d37..9dbb4c68d13f8 100644 --- a/clippy_utils/src/mir/mod.rs +++ b/clippy_utils/src/mir/mod.rs @@ -30,20 +30,26 @@ pub fn visit_local_usage(locals: &[Local], mir: &Body<'_>, location: Location) - locals.len() ]; - traversal::ReversePostorder::new(mir, location.block).try_fold(init, |usage, (tbb, tdata)| { - // Give up on loops - if tdata.terminator().successors().any(|s| s == location.block) { - return None; - } + traversal::Postorder::new(&mir.basic_blocks, location.block) + .collect::>() + .into_iter() + .rev() + .try_fold(init, |usage, tbb| { + let tdata = &mir.basic_blocks[tbb]; + + // Give up on loops + if tdata.terminator().successors().any(|s| s == location.block) { + return None; + } - let mut v = V { - locals, - location, - results: usage, - }; - v.visit_basic_block_data(tbb, tdata); - Some(v.results) - }) + let mut v = V { + locals, + location, + results: usage, + }; + v.visit_basic_block_data(tbb, tdata); + Some(v.results) + }) } struct V<'a> { From ad5653b296e8b3a001f15a3c02c5b17de8d13d57 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 29 Sep 2023 03:08:34 +0200 Subject: [PATCH 21/48] Add missing lint description headers --- clippy_lints/src/allow_attributes.rs | 2 +- clippy_lints/src/methods/mod.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/allow_attributes.rs b/clippy_lints/src/allow_attributes.rs index e1ef514edfd18..e3f4cf79d315c 100644 --- a/clippy_lints/src/allow_attributes.rs +++ b/clippy_lints/src/allow_attributes.rs @@ -8,6 +8,7 @@ use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { + /// ### What it does /// Checks for usage of the `#[allow]` attribute and suggests replacing it with /// the `#[expect]` (See [RFC 2383](https://rust-lang.github.io/rfcs/2383-lint-reasons.html)) /// @@ -19,7 +20,6 @@ declare_clippy_lint! { /// (`#![allow]`) are usually used to enable or disable lints on a global scale. /// /// ### Why is this bad? - /// /// `#[expect]` attributes suppress the lint emission, but emit a warning, if /// the expectation is unfulfilled. This can be useful to be notified when the /// lint is no longer triggered. diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index e7fcef9e9de27..7b743382fa158 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3368,6 +3368,7 @@ declare_clippy_lint! { } declare_clippy_lint! { + /// ### What it does /// Looks for calls to [`Stdin::read_line`] to read a line from the standard input /// into a string, then later attempting to parse this string into a type without first trimming it, which will /// always fail because the string has a trailing newline in it. From 2d2017942afc221da0295962ac0374bd31cc6d1c Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Thu, 28 Sep 2023 16:31:21 +0200 Subject: [PATCH 22/48] [`manual_let_else`]: only omit block if span is from same ctxt --- clippy_lints/src/manual_let_else.rs | 4 ++-- tests/ui/manual_let_else_match.fixed | 4 ++++ tests/ui/manual_let_else_match.rs | 8 ++++++++ tests/ui/manual_let_else_match.stderr | 12 +++++++++++- 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/manual_let_else.rs b/clippy_lints/src/manual_let_else.rs index c531137b785e8..2117308cd4009 100644 --- a/clippy_lints/src/manual_let_else.rs +++ b/clippy_lints/src/manual_let_else.rs @@ -136,9 +136,9 @@ fn emit_manual_let_else( // for this to be machine applicable. let mut app = Applicability::HasPlaceholders; let (sn_expr, _) = snippet_with_context(cx, expr.span, span.ctxt(), "", &mut app); - let (sn_else, _) = snippet_with_context(cx, else_body.span, span.ctxt(), "", &mut app); + let (sn_else, else_is_mac_call) = snippet_with_context(cx, else_body.span, span.ctxt(), "", &mut app); - let else_bl = if matches!(else_body.kind, ExprKind::Block(..)) { + let else_bl = if matches!(else_body.kind, ExprKind::Block(..)) && !else_is_mac_call { sn_else.into_owned() } else { format!("{{ {sn_else} }}") diff --git a/tests/ui/manual_let_else_match.fixed b/tests/ui/manual_let_else_match.fixed index 09b713f04101e..588ba5edd8f15 100644 --- a/tests/ui/manual_let_else_match.fixed +++ b/tests/ui/manual_let_else_match.fixed @@ -133,3 +133,7 @@ fn not_fire() { [data @ .., 0, 0, 0, 0] | [data @ .., 0, 0] | [data @ ..] => data, }; } + +fn issue11579() { + let Some(msg) = Some("hi") else { unreachable!("can't happen") }; +} diff --git a/tests/ui/manual_let_else_match.rs b/tests/ui/manual_let_else_match.rs index e6af47384200e..c37b5613ff7d5 100644 --- a/tests/ui/manual_let_else_match.rs +++ b/tests/ui/manual_let_else_match.rs @@ -170,3 +170,11 @@ fn not_fire() { [data @ .., 0, 0, 0, 0] | [data @ .., 0, 0] | [data @ ..] => data, }; } + +fn issue11579() { + let msg = match Some("hi") { + //~^ ERROR: this could be rewritten as `let...else` + Some(m) => m, + _ => unreachable!("can't happen"), + }; +} diff --git a/tests/ui/manual_let_else_match.stderr b/tests/ui/manual_let_else_match.stderr index 8ca2c84072d0f..18bfe324ba76a 100644 --- a/tests/ui/manual_let_else_match.stderr +++ b/tests/ui/manual_let_else_match.stderr @@ -92,5 +92,15 @@ LL | | _ => return, LL | | }; | |______^ help: consider writing: `let ([data @ .., 0, 0, 0, 0] | [data @ .., 0, 0] | [data @ .., 0]) = data.as_slice() else { return };` -error: aborting due to 9 previous errors +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else_match.rs:175:5 + | +LL | / let msg = match Some("hi") { +LL | | +LL | | Some(m) => m, +LL | | _ => unreachable!("can't happen"), +LL | | }; + | |______^ help: consider writing: `let Some(msg) = Some("hi") else { unreachable!("can't happen") };` + +error: aborting due to 10 previous errors From 44b6aca96b48299adf0ab44307c40d7f3392d060 Mon Sep 17 00:00:00 2001 From: koka Date: Sat, 30 Sep 2023 23:56:11 +0900 Subject: [PATCH 23/48] Avoid linting in external/proc macro --- clippy_lints/src/std_instead_of_core.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/std_instead_of_core.rs b/clippy_lints/src/std_instead_of_core.rs index 5f54a10d1c41d..b7396535eedff 100644 --- a/clippy_lints/src/std_instead_of_core.rs +++ b/clippy_lints/src/std_instead_of_core.rs @@ -1,9 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_from_proc_macro; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::def_id::DefId; use rustc_hir::{HirId, Path, PathSegment}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::kw; use rustc_span::{sym, Span}; @@ -99,6 +101,8 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports { if let Res::Def(_, def_id) = path.res && let Some(first_segment) = get_first_segment(path) && is_stable(cx, def_id) + && !in_external_macro(cx.sess(), path.span) + && !is_from_proc_macro(cx, &first_segment.ident) { let (lint, used_mod, replace_with) = match first_segment.ident.name { sym::std => match cx.tcx.crate_name(def_id.krate) { From d5cc97e32c9b1919a175aa798e99bd7dde814073 Mon Sep 17 00:00:00 2001 From: koka Date: Sun, 1 Oct 2023 00:47:57 +0900 Subject: [PATCH 24/48] Add macro for test which use std internally --- tests/ui/auxiliary/proc_macro_derive.rs | 12 ++++++++++++ tests/ui/std_instead_of_core.fixed | 11 +++++++++++ tests/ui/std_instead_of_core.rs | 11 +++++++++++ tests/ui/std_instead_of_core.stderr | 22 +++++++++++----------- 4 files changed, 45 insertions(+), 11 deletions(-) diff --git a/tests/ui/auxiliary/proc_macro_derive.rs b/tests/ui/auxiliary/proc_macro_derive.rs index 37f0ec2b37d88..556b886f386d8 100644 --- a/tests/ui/auxiliary/proc_macro_derive.rs +++ b/tests/ui/auxiliary/proc_macro_derive.rs @@ -21,6 +21,18 @@ pub fn derive(_: TokenStream) -> TokenStream { output } +#[proc_macro_derive(ImplStructWithStdDisplay)] +pub fn derive_std(_: TokenStream) -> TokenStream { + quote! { + struct A {} + impl ::std::fmt::Display for A { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + write!(f, "A") + } + } + } +} + #[proc_macro_derive(FieldReassignWithDefault)] pub fn derive_foo(_input: TokenStream) -> TokenStream { quote! { diff --git a/tests/ui/std_instead_of_core.fixed b/tests/ui/std_instead_of_core.fixed index 8027c053fb5b0..a7555704fbe01 100644 --- a/tests/ui/std_instead_of_core.fixed +++ b/tests/ui/std_instead_of_core.fixed @@ -1,8 +1,12 @@ +//@aux-build:proc_macro_derive.rs #![warn(clippy::std_instead_of_core)] #![allow(unused_imports)] extern crate alloc; +#[macro_use] +extern crate proc_macro_derive; + #[warn(clippy::std_instead_of_core)] fn std_instead_of_core() { // Regular import @@ -55,6 +59,13 @@ fn alloc_instead_of_core() { //~^ ERROR: used import from `alloc` instead of `core` } +mod std_in_proc_macro_derive { + #[warn(clippy::alloc_instead_of_core)] + #[allow(unused)] + #[derive(ImplStructWithStdDisplay)] + struct B {} +} + fn main() { std_instead_of_core(); std_instead_of_alloc(); diff --git a/tests/ui/std_instead_of_core.rs b/tests/ui/std_instead_of_core.rs index 63a096384d716..af7f3399f4923 100644 --- a/tests/ui/std_instead_of_core.rs +++ b/tests/ui/std_instead_of_core.rs @@ -1,8 +1,12 @@ +//@aux-build:proc_macro_derive.rs #![warn(clippy::std_instead_of_core)] #![allow(unused_imports)] extern crate alloc; +#[macro_use] +extern crate proc_macro_derive; + #[warn(clippy::std_instead_of_core)] fn std_instead_of_core() { // Regular import @@ -55,6 +59,13 @@ fn alloc_instead_of_core() { //~^ ERROR: used import from `alloc` instead of `core` } +mod std_in_proc_macro_derive { + #[warn(clippy::alloc_instead_of_core)] + #[allow(unused)] + #[derive(ImplStructWithStdDisplay)] + struct B {} +} + fn main() { std_instead_of_core(); std_instead_of_alloc(); diff --git a/tests/ui/std_instead_of_core.stderr b/tests/ui/std_instead_of_core.stderr index ca26f77bd37f9..4f7bdc4045e77 100644 --- a/tests/ui/std_instead_of_core.stderr +++ b/tests/ui/std_instead_of_core.stderr @@ -1,5 +1,5 @@ error: used import from `std` instead of `core` - --> $DIR/std_instead_of_core.rs:9:9 + --> $DIR/std_instead_of_core.rs:13:9 | LL | use std::hash::Hasher; | ^^^ help: consider importing the item from `core`: `core` @@ -8,49 +8,49 @@ LL | use std::hash::Hasher; = help: to override `-D warnings` add `#[allow(clippy::std_instead_of_core)]` error: used import from `std` instead of `core` - --> $DIR/std_instead_of_core.rs:12:11 + --> $DIR/std_instead_of_core.rs:16:11 | LL | use ::std::hash::Hash; | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> $DIR/std_instead_of_core.rs:18:9 + --> $DIR/std_instead_of_core.rs:22:9 | LL | use std::fmt::{Debug, Result}; | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> $DIR/std_instead_of_core.rs:22:15 + --> $DIR/std_instead_of_core.rs:26:15 | LL | let ptr = std::ptr::null::(); | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> $DIR/std_instead_of_core.rs:24:21 + --> $DIR/std_instead_of_core.rs:28:21 | LL | let ptr_mut = ::std::ptr::null_mut::(); | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> $DIR/std_instead_of_core.rs:28:16 + --> $DIR/std_instead_of_core.rs:32:16 | LL | let cell = std::cell::Cell::new(8u32); | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> $DIR/std_instead_of_core.rs:30:27 + --> $DIR/std_instead_of_core.rs:34:27 | LL | let cell_absolute = ::std::cell::Cell::new(8u32); | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> $DIR/std_instead_of_core.rs:39:9 + --> $DIR/std_instead_of_core.rs:43:9 | LL | use std::iter::Iterator; | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `alloc` - --> $DIR/std_instead_of_core.rs:46:9 + --> $DIR/std_instead_of_core.rs:50:9 | LL | use std::vec; | ^^^ help: consider importing the item from `alloc`: `alloc` @@ -59,13 +59,13 @@ LL | use std::vec; = help: to override `-D warnings` add `#[allow(clippy::std_instead_of_alloc)]` error: used import from `std` instead of `alloc` - --> $DIR/std_instead_of_core.rs:48:9 + --> $DIR/std_instead_of_core.rs:52:9 | LL | use std::vec::Vec; | ^^^ help: consider importing the item from `alloc`: `alloc` error: used import from `alloc` instead of `core` - --> $DIR/std_instead_of_core.rs:54:9 + --> $DIR/std_instead_of_core.rs:58:9 | LL | use alloc::slice::from_ref; | ^^^^^ help: consider importing the item from `core`: `core` From 8eb586d1549236be3ba55b205c9dc19e9bfe7f3d Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Sat, 30 Sep 2023 03:00:34 +0200 Subject: [PATCH 25/48] new lint: `into_iter_without_iter` --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/iter_without_into_iter.rs | 120 +++++++++++++++++++- tests/ui/into_iter_without_iter.rs | 124 +++++++++++++++++++++ tests/ui/into_iter_without_iter.stderr | 114 +++++++++++++++++++ 5 files changed, 357 insertions(+), 3 deletions(-) create mode 100644 tests/ui/into_iter_without_iter.rs create mode 100644 tests/ui/into_iter_without_iter.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 40510d7880054..25230e46e8893 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5011,6 +5011,7 @@ Released 2018-09-13 [`integer_division`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_division [`into_iter_on_array`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_array [`into_iter_on_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_ref +[`into_iter_without_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_without_iter [`invalid_atomic_ordering`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_atomic_ordering [`invalid_null_ptr_usage`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_null_ptr_usage [`invalid_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_ref diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 06809038927ce..481c44031cf72 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -229,6 +229,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::items_after_statements::ITEMS_AFTER_STATEMENTS_INFO, crate::items_after_test_module::ITEMS_AFTER_TEST_MODULE_INFO, crate::iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR_INFO, + crate::iter_without_into_iter::INTO_ITER_WITHOUT_ITER_INFO, crate::iter_without_into_iter::ITER_WITHOUT_INTO_ITER_INFO, crate::large_const_arrays::LARGE_CONST_ARRAYS_INFO, crate::large_enum_variant::LARGE_ENUM_VARIANT_INFO, diff --git a/clippy_lints/src/iter_without_into_iter.rs b/clippy_lints/src/iter_without_into_iter.rs index 08a8e6dbf1899..43e1b92c9b9a7 100644 --- a/clippy_lints/src/iter_without_into_iter.rs +++ b/clippy_lints/src/iter_without_into_iter.rs @@ -2,11 +2,13 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::get_parent_as_impl; use clippy_utils::source::snippet; use clippy_utils::ty::{implements_trait, make_normalized_projection}; +use rustc_ast::Mutability; use rustc_errors::Applicability; -use rustc_hir::{FnRetTy, ImplItemKind, ImplicitSelfKind, TyKind}; +use rustc_hir::{FnRetTy, ImplItemKind, ImplicitSelfKind, ItemKind, TyKind}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::sym; +use rustc_span::{sym, Symbol}; declare_clippy_lint! { /// ### What it does @@ -46,7 +48,51 @@ declare_clippy_lint! { pedantic, "implementing `iter(_mut)` without an associated `IntoIterator for (&|&mut) Type` impl" } -declare_lint_pass!(IterWithoutIntoIter => [ITER_WITHOUT_INTO_ITER]); + +declare_clippy_lint! { + /// ### What it does + /// This is the opposite of the `iter_without_into_iter` lint. + /// It looks for `IntoIterator for (&|&mut) Type` implementations without an inherent `iter` or `iter_mut` method. + /// + /// ### Why is this bad? + /// It's not bad, but having them is idiomatic and allows the type to be used in iterator chains + /// by just calling `.iter()`, instead of the more awkward `<&Type>::into_iter` or `(&val).iter()` syntax + /// in case of ambiguity with another `Intoiterator` impl. + /// + /// ### Example + /// ```rust + /// struct MySlice<'a>(&'a [u8]); + /// impl<'a> IntoIterator for &MySlice<'a> { + /// type Item = &'a u8; + /// type IntoIter = std::slice::Iter<'a, u8>; + /// fn into_iter(self) -> Self::IntoIter { + /// self.0.iter() + /// } + /// } + /// ``` + /// Use instead: + /// ```rust + /// struct MySlice<'a>(&'a [u8]); + /// impl<'a> MySlice<'a> { + /// pub fn iter(&self) -> std::slice::Iter<'a, u8> { + /// self.into_iter() + /// } + /// } + /// impl<'a> IntoIterator for &MySlice<'a> { + /// type Item = &'a u8; + /// type IntoIter = std::slice::Iter<'a, u8>; + /// fn into_iter(self) -> Self::IntoIter { + /// self.0.iter() + /// } + /// } + /// ``` + #[clippy::version = "1.74.0"] + pub INTO_ITER_WITHOUT_ITER, + pedantic, + "implementing `IntoIterator for (&|&mut) Type` without an inherent `iter(_mut)` method" +} + +declare_lint_pass!(IterWithoutIntoIter => [ITER_WITHOUT_INTO_ITER, INTO_ITER_WITHOUT_ITER]); /// Checks if a given type is nameable in a trait (impl). /// RPIT is stable, but impl Trait in traits is not (yet), so when we have @@ -56,7 +102,75 @@ fn is_nameable_in_impl_trait(ty: &rustc_hir::Ty<'_>) -> bool { !matches!(ty.kind, TyKind::OpaqueDef(..)) } +fn type_has_inherent_method(cx: &LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> bool { + if let Some(ty_did) = ty.ty_adt_def().map(ty::AdtDef::did) { + cx.tcx.inherent_impls(ty_did).iter().any(|&did| { + cx.tcx + .associated_items(did) + .filter_by_name_unhygienic(method_name) + .next() + .is_some_and(|item| item.kind == ty::AssocKind::Fn) + }) + } else { + false + } +} + impl LateLintPass<'_> for IterWithoutIntoIter { + fn check_item(&mut self, cx: &LateContext<'_>, item: &rustc_hir::Item<'_>) { + if let ItemKind::Impl(imp) = item.kind + && let TyKind::Ref(_, self_ty_without_ref) = &imp.self_ty.kind + && let Some(trait_ref) = imp.of_trait + && trait_ref.trait_def_id().is_some_and(|did| cx.tcx.is_diagnostic_item(sym::IntoIterator, did)) + && let &ty::Ref(_, ty, mtbl) = cx.tcx.type_of(item.owner_id).instantiate_identity().kind() + && let expected_method_name = match mtbl { + Mutability::Mut => sym::iter_mut, + Mutability::Not => sym::iter, + } + && !type_has_inherent_method(cx, ty, expected_method_name) + && let Some(iter_assoc_span) = imp.items.iter().find_map(|item| { + if item.ident.name == sym!(IntoIter) { + Some(cx.tcx.hir().impl_item(item.id).expect_type().span) + } else { + None + } + }) + { + span_lint_and_then( + cx, + INTO_ITER_WITHOUT_ITER, + item.span, + &format!("`IntoIterator` implemented for a reference type without an `{expected_method_name}` method"), + |diag| { + // The suggestion forwards to the `IntoIterator` impl and uses a form of UFCS + // to avoid name ambiguities, as there might be an inherent into_iter method + // that we don't want to call. + let sugg = format!( +" +impl {self_ty_without_ref} {{ + fn {expected_method_name}({ref_self}self) -> {iter_ty} {{ + <{ref_self}Self as IntoIterator>::into_iter(self) + }} +}} +", + self_ty_without_ref = snippet(cx, self_ty_without_ref.ty.span, ".."), + ref_self = mtbl.ref_prefix_str(), + iter_ty = snippet(cx, iter_assoc_span, ".."), + ); + + diag.span_suggestion_verbose( + item.span.shrink_to_lo(), + format!("consider implementing `{expected_method_name}`"), + sugg, + // Just like iter_without_into_iter, this suggestion is on a best effort basis + // and requires potentially adding lifetimes or moving them around. + Applicability::Unspecified + ); + } + ); + } + } + fn check_impl_item(&mut self, cx: &LateContext<'_>, item: &rustc_hir::ImplItem<'_>) { let item_did = item.owner_id.to_def_id(); let (borrow_prefix, expected_implicit_self) = match item.ident.name { diff --git a/tests/ui/into_iter_without_iter.rs b/tests/ui/into_iter_without_iter.rs new file mode 100644 index 0000000000000..6be3bb8abdddc --- /dev/null +++ b/tests/ui/into_iter_without_iter.rs @@ -0,0 +1,124 @@ +//@no-rustfix +#![warn(clippy::into_iter_without_iter)] + +use std::iter::IntoIterator; + +fn main() { + { + struct S; + + impl<'a> IntoIterator for &'a S { + //~^ ERROR: `IntoIterator` implemented for a reference type without an `iter` method + type IntoIter = std::slice::Iter<'a, u8>; + type Item = &'a u8; + fn into_iter(self) -> Self::IntoIter { + todo!() + } + } + impl<'a> IntoIterator for &'a mut S { + //~^ ERROR: `IntoIterator` implemented for a reference type without an `iter_mut` method + type IntoIter = std::slice::IterMut<'a, u8>; + type Item = &'a mut u8; + fn into_iter(self) -> Self::IntoIter { + todo!() + } + } + } + { + struct S(T); + impl<'a, T> IntoIterator for &'a S { + //~^ ERROR: `IntoIterator` implemented for a reference type without an `iter` method + type IntoIter = std::slice::Iter<'a, T>; + type Item = &'a T; + fn into_iter(self) -> Self::IntoIter { + todo!() + } + } + impl<'a, T> IntoIterator for &'a mut S { + //~^ ERROR: `IntoIterator` implemented for a reference type without an `iter_mut` method + type IntoIter = std::slice::IterMut<'a, T>; + type Item = &'a mut T; + fn into_iter(self) -> Self::IntoIter { + todo!() + } + } + } + { + // Both iter and iter_mut methods exist, don't lint + struct S<'a, T>(&'a T); + + impl<'a, T> S<'a, T> { + fn iter(&self) -> std::slice::Iter<'a, T> { + todo!() + } + fn iter_mut(&mut self) -> std::slice::IterMut<'a, T> { + todo!() + } + } + + impl<'a, T> IntoIterator for &S<'a, T> { + type IntoIter = std::slice::Iter<'a, T>; + type Item = &'a T; + fn into_iter(self) -> Self::IntoIter { + todo!() + } + } + + impl<'a, T> IntoIterator for &mut S<'a, T> { + type IntoIter = std::slice::IterMut<'a, T>; + type Item = &'a mut T; + fn into_iter(self) -> Self::IntoIter { + todo!() + } + } + } + { + // Only `iter` exists, no `iter_mut` + struct S<'a, T>(&'a T); + + impl<'a, T> S<'a, T> { + fn iter(&self) -> std::slice::Iter<'a, T> { + todo!() + } + } + + impl<'a, T> IntoIterator for &S<'a, T> { + type IntoIter = std::slice::Iter<'a, T>; + type Item = &'a T; + fn into_iter(self) -> Self::IntoIter { + todo!() + } + } + + impl<'a, T> IntoIterator for &mut S<'a, T> { + //~^ ERROR: `IntoIterator` implemented for a reference type without an `iter_mut` method + type IntoIter = std::slice::IterMut<'a, T>; + type Item = &'a mut T; + fn into_iter(self) -> Self::IntoIter { + todo!() + } + } + } + { + // `iter` exists, but `IntoIterator` is implemented for an alias. inherent_impls doesn't "normalize" + // aliases so that `inherent_impls(Alias)` where `type Alias = S` returns nothing, so this can lead + // to fun FPs. Make sure it doesn't happen here (we're using type_of, which should skip the alias). + struct S; + + impl S { + fn iter(&self) -> std::slice::Iter<'static, u8> { + todo!() + } + } + + type Alias = S; + + impl IntoIterator for &Alias { + type IntoIter = std::slice::Iter<'static, u8>; + type Item = &'static u8; + fn into_iter(self) -> Self::IntoIter { + todo!() + } + } + } +} diff --git a/tests/ui/into_iter_without_iter.stderr b/tests/ui/into_iter_without_iter.stderr new file mode 100644 index 0000000000000..f543d1d8e86c1 --- /dev/null +++ b/tests/ui/into_iter_without_iter.stderr @@ -0,0 +1,114 @@ +error: `IntoIterator` implemented for a reference type without an `iter` method + --> $DIR/into_iter_without_iter.rs:10:9 + | +LL | / impl<'a> IntoIterator for &'a S { +LL | | +LL | | type IntoIter = std::slice::Iter<'a, u8>; +LL | | type Item = &'a u8; +... | +LL | | } +LL | | } + | |_________^ + | + = note: `-D clippy::into-iter-without-iter` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::into_iter_without_iter)]` +help: consider implementing `iter` + | +LL ~ +LL + impl S { +LL + fn iter(&self) -> std::slice::Iter<'a, u8> { +LL + <&Self as IntoIterator>::into_iter(self) +LL + } +LL + } + | + +error: `IntoIterator` implemented for a reference type without an `iter_mut` method + --> $DIR/into_iter_without_iter.rs:18:9 + | +LL | / impl<'a> IntoIterator for &'a mut S { +LL | | +LL | | type IntoIter = std::slice::IterMut<'a, u8>; +LL | | type Item = &'a mut u8; +... | +LL | | } +LL | | } + | |_________^ + | +help: consider implementing `iter_mut` + | +LL ~ +LL + impl S { +LL + fn iter_mut(&mut self) -> std::slice::IterMut<'a, u8> { +LL + <&mut Self as IntoIterator>::into_iter(self) +LL + } +LL + } + | + +error: `IntoIterator` implemented for a reference type without an `iter` method + --> $DIR/into_iter_without_iter.rs:29:9 + | +LL | / impl<'a, T> IntoIterator for &'a S { +LL | | +LL | | type IntoIter = std::slice::Iter<'a, T>; +LL | | type Item = &'a T; +... | +LL | | } +LL | | } + | |_________^ + | +help: consider implementing `iter` + | +LL ~ +LL + impl S { +LL + fn iter(&self) -> std::slice::Iter<'a, T> { +LL + <&Self as IntoIterator>::into_iter(self) +LL + } +LL + } + | + +error: `IntoIterator` implemented for a reference type without an `iter_mut` method + --> $DIR/into_iter_without_iter.rs:37:9 + | +LL | / impl<'a, T> IntoIterator for &'a mut S { +LL | | +LL | | type IntoIter = std::slice::IterMut<'a, T>; +LL | | type Item = &'a mut T; +... | +LL | | } +LL | | } + | |_________^ + | +help: consider implementing `iter_mut` + | +LL ~ +LL + impl S { +LL + fn iter_mut(&mut self) -> std::slice::IterMut<'a, T> { +LL + <&mut Self as IntoIterator>::into_iter(self) +LL + } +LL + } + | + +error: `IntoIterator` implemented for a reference type without an `iter_mut` method + --> $DIR/into_iter_without_iter.rs:93:9 + | +LL | / impl<'a, T> IntoIterator for &mut S<'a, T> { +LL | | +LL | | type IntoIter = std::slice::IterMut<'a, T>; +LL | | type Item = &'a mut T; +... | +LL | | } +LL | | } + | |_________^ + | +help: consider implementing `iter_mut` + | +LL ~ +LL + impl S<'a, T> { +LL + fn iter_mut(&mut self) -> std::slice::IterMut<'a, T> { +LL + <&mut Self as IntoIterator>::into_iter(self) +LL + } +LL + } + | + +error: aborting due to 5 previous errors + From e683e3eeac5603296c328ff45c9e5bb911327756 Mon Sep 17 00:00:00 2001 From: Victor Song Date: Sat, 30 Sep 2023 22:57:54 -0500 Subject: [PATCH 26/48] Don't lint `manual_non_exhaustive` when enum explicitly marked as `non_exhaustive` There are cases where users create a unit variant for the purposes of tracking the number of variants for an nonexhaustive enum. We should check if an enum is explicitly marked as nonexhaustive before reporting `manual_non_exhaustive` in these cases. Fixes #11583 --- clippy_lints/src/manual_non_exhaustive.rs | 4 +++- tests/ui/manual_non_exhaustive_enum.rs | 3 +-- tests/ui/manual_non_exhaustive_enum.stderr | 20 +------------------- 3 files changed, 5 insertions(+), 22 deletions(-) diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index 0e22485db2c59..9fe55d819da78 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -3,6 +3,7 @@ use clippy_utils::is_doc_hidden; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_opt; use rustc_ast::ast::{self, VisibilityKind}; +use rustc_ast::attr; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; @@ -158,7 +159,8 @@ impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum { let mut iter = def.variants.iter().filter_map(|v| { (matches!(v.data, hir::VariantData::Unit(_, _)) && v.ident.as_str().starts_with('_') - && is_doc_hidden(cx.tcx.hir().attrs(v.hir_id))) + && is_doc_hidden(cx.tcx.hir().attrs(v.hir_id)) + && !attr::contains_name(cx.tcx.hir().attrs(item.hir_id()), sym::non_exhaustive)) .then_some((v.def_id, v.span)) }); if let Some((id, span)) = iter.next() diff --git a/tests/ui/manual_non_exhaustive_enum.rs b/tests/ui/manual_non_exhaustive_enum.rs index 0e439dabfd651..e32ba8631761b 100644 --- a/tests/ui/manual_non_exhaustive_enum.rs +++ b/tests/ui/manual_non_exhaustive_enum.rs @@ -10,10 +10,9 @@ enum E { _C, } -// user forgot to remove the marker +// if the user explicitly marks as nonexhaustive we shouldn't warn them #[non_exhaustive] enum Ep { - //~^ ERROR: this seems like a manual implementation of the non-exhaustive pattern A, B, #[doc(hidden)] diff --git a/tests/ui/manual_non_exhaustive_enum.stderr b/tests/ui/manual_non_exhaustive_enum.stderr index ce7e21c94bbdc..7361a4a2cbbe8 100644 --- a/tests/ui/manual_non_exhaustive_enum.stderr +++ b/tests/ui/manual_non_exhaustive_enum.stderr @@ -22,23 +22,5 @@ LL | _C, = note: `-D clippy::manual-non-exhaustive` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::manual_non_exhaustive)]` -error: this seems like a manual implementation of the non-exhaustive pattern - --> $DIR/manual_non_exhaustive_enum.rs:15:1 - | -LL | / enum Ep { -LL | | -LL | | A, -LL | | B, -LL | | #[doc(hidden)] -LL | | _C, -LL | | } - | |_^ - | -help: remove this variant - --> $DIR/manual_non_exhaustive_enum.rs:20:5 - | -LL | _C, - | ^^ - -error: aborting due to 2 previous errors +error: aborting due to previous error From 9dfd60cf4f33c9af8e37d4a4956f56626bac2b3c Mon Sep 17 00:00:00 2001 From: Victor Song Date: Sun, 1 Oct 2023 09:54:45 -0500 Subject: [PATCH 27/48] Remove extraneous `#[non_exhaustive]` check in lint --- clippy_lints/src/manual_non_exhaustive.rs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index 9fe55d819da78..c12727c4a28cc 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -200,16 +200,14 @@ impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum { enum_span, "this seems like a manual implementation of the non-exhaustive pattern", |diag| { - if !cx.tcx.adt_def(enum_id).is_variant_list_non_exhaustive() - && let header_span = cx.sess().source_map().span_until_char(enum_span, '{') - && let Some(snippet) = snippet_opt(cx, header_span) - { - diag.span_suggestion( - header_span, - "add the attribute", - format!("#[non_exhaustive] {snippet}"), - Applicability::Unspecified, - ); + let header_span = cx.sess().source_map().span_until_char(enum_span, '{'); + if let Some(snippet) = snippet_opt(cx, header_span) { + diag.span_suggestion( + header_span, + "add the attribute", + format!("#[non_exhaustive] {snippet}"), + Applicability::Unspecified, + ); } diag.span_help(variant_span, "remove this variant"); }, From 6f1a78ffa85b1fc67517a0522530907d0025a5f5 Mon Sep 17 00:00:00 2001 From: koka Date: Mon, 2 Oct 2023 00:38:01 +0900 Subject: [PATCH 28/48] Use Span#from_expansion instead of in_external_macro --- clippy_lints/src/implicit_hasher.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/implicit_hasher.rs b/clippy_lints/src/implicit_hasher.rs index 64a4a3fa741bc..2b2ea156cd4d3 100644 --- a/clippy_lints/src/implicit_hasher.rs +++ b/clippy_lints/src/implicit_hasher.rs @@ -6,9 +6,8 @@ use rustc_hir as hir; use rustc_hir::intravisit::{walk_body, walk_expr, walk_inf, walk_ty, Visitor}; use rustc_hir::{Body, Expr, ExprKind, GenericArg, Item, ItemKind, QPath, TyKind}; use rustc_hir_analysis::hir_ty_to_ty; -use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; -use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{Ty, TypeckResults}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; @@ -162,7 +161,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher { vis.visit_ty(ty); for target in &vis.found { - if in_external_macro(cx.sess(), generics.span) { + if generics.span.from_expansion() { continue; } let generics_suggestion_span = generics.span.substitute_dummy({ From 0f8b8625bd309721099002813b8db9290b2bb411 Mon Sep 17 00:00:00 2001 From: Michael Schubart Date: Sun, 1 Oct 2023 16:32:16 +0100 Subject: [PATCH 29/48] Fix documentation link The file pointed to by the old link https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/lib.rs#L110 did not talk about categories and levels. The new link (hopefully) points here https://doc.rust-lang.org/stable/clippy/ which has a nice table explaining the mappings. --- book/src/development/adding_lints.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/src/development/adding_lints.md b/book/src/development/adding_lints.md index f6f0c95c72933..e001197842bdc 100644 --- a/book/src/development/adding_lints.md +++ b/book/src/development/adding_lints.md @@ -261,7 +261,7 @@ impl EarlyLintPass for FooFunctions {} [declare_clippy_lint]: https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/lib.rs#L60 [example_lint_page]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure [lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints -[category_level_mapping]: https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/lib.rs#L110 +[category_level_mapping]: ../index.html ## Lint registration From 258b9a856273a0e6bbf6becd1dd769cb20228815 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Mon, 31 Jul 2023 20:22:39 +0000 Subject: [PATCH 30/48] Don't escape unicode escape braces in print_literal --- clippy_lints/src/write.rs | 50 ++++++++++++++++-- tests/ui/print_literal.fixed | 14 +++++ tests/ui/print_literal.rs | 14 +++++ tests/ui/print_literal.stderr | 98 ++++++++++++++++++++++++++++++++++- 4 files changed, 172 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 21b675ff67983..855aefa70cb15 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -486,9 +486,9 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) { && let rustc_ast::ExprKind::Lit(lit) = &arg.expr.kind && !arg.expr.span.from_expansion() && let Some(value_string) = snippet_opt(cx, arg.expr.span) - { + { let (replacement, replace_raw) = match lit.kind { - LitKind::Str | LitKind::StrRaw(_) => match extract_str_literal(&value_string) { + LitKind::Str | LitKind::StrRaw(_) => match extract_str_literal(&value_string) { Some(extracted) => extracted, None => return, }, @@ -538,7 +538,7 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) { // `format!("{}", "a")`, `format!("{named}", named = "b") // ~~~~~ ~~~~~~~~~~~~~ && let Some(removal_span) = format_arg_removal_span(format_args, index) { - let replacement = replacement.replace('{', "{{").replace('}', "}}"); + let replacement = escape_braces(&replacement, !format_string_is_raw && !replace_raw); suggestion.push((*placeholder_span, replacement)); suggestion.push((removal_span, String::new())); } @@ -631,3 +631,47 @@ fn conservative_unescape(literal: &str) -> Result { if err { Err(UnescapeErr::Lint) } else { Ok(unescaped) } } + +/// Replaces `{` with `{{` and `}` with `}}`. If `preserve_unicode_escapes` is `true` the braces in +/// `\u{xxxx}` are left unmodified +#[expect(clippy::match_same_arms)] +fn escape_braces(literal: &str, preserve_unicode_escapes: bool) -> String { + #[derive(Clone, Copy)] + enum State { + Normal, + Backslash, + UnicodeEscape, + } + + let mut escaped = String::with_capacity(literal.len()); + let mut state = State::Normal; + + for ch in literal.chars() { + state = match (ch, state) { + // Escape braces outside of unicode escapes by doubling them up + ('{' | '}', State::Normal) => { + escaped.push(ch); + State::Normal + }, + // If `preserve_unicode_escapes` isn't enabled stay in `State::Normal`, otherwise: + // + // \u{aaaa} \\ \x01 + // ^ ^ ^ + ('\\', State::Normal) if preserve_unicode_escapes => State::Backslash, + // \u{aaaa} + // ^ + ('u', State::Backslash) => State::UnicodeEscape, + // \xAA \\ + // ^ ^ + (_, State::Backslash) => State::Normal, + // \u{aaaa} + // ^ + ('}', State::UnicodeEscape) => State::Normal, + _ => state, + }; + + escaped.push(ch); + } + + escaped +} diff --git a/tests/ui/print_literal.fixed b/tests/ui/print_literal.fixed index 04a484258a4a8..a7157c07f8a98 100644 --- a/tests/ui/print_literal.fixed +++ b/tests/ui/print_literal.fixed @@ -51,4 +51,18 @@ fn main() { // The string literal from `file!()` has a callsite span that isn't marked as coming from an // expansion println!("file: {}", file!()); + + // Braces in unicode escapes should not be escaped + println!("{{}} \x00 \u{ab123} \\\u{ab123} {{:?}}"); + println!("\\\u{1234}"); + // This does not lint because it would have to suggest unescaping the character + println!(r"{}", "\u{ab123}"); + // These are not unicode escapes + println!("\\u{{ab123}} \\u{{{{"); + println!(r"\u{{ab123}} \u{{{{"); + println!("\\{{ab123}} \\u{{{{"); + println!("\\u{{ab123}}"); + println!("\\\\u{{1234}}"); + + println!("mixed: {{hello}} {world}"); } diff --git a/tests/ui/print_literal.rs b/tests/ui/print_literal.rs index 38c036f56c5d0..4b04b42744ccd 100644 --- a/tests/ui/print_literal.rs +++ b/tests/ui/print_literal.rs @@ -51,4 +51,18 @@ fn main() { // The string literal from `file!()` has a callsite span that isn't marked as coming from an // expansion println!("file: {}", file!()); + + // Braces in unicode escapes should not be escaped + println!("{}", "{} \x00 \u{ab123} \\\u{ab123} {:?}"); + println!("{}", "\\\u{1234}"); + // This does not lint because it would have to suggest unescaping the character + println!(r"{}", "\u{ab123}"); + // These are not unicode escapes + println!("{}", r"\u{ab123} \u{{"); + println!(r"{}", r"\u{ab123} \u{{"); + println!("{}", r"\{ab123} \u{{"); + println!("{}", "\\u{ab123}"); + println!("{}", "\\\\u{1234}"); + + println!("mixed: {} {world}", "{hello}"); } diff --git a/tests/ui/print_literal.stderr b/tests/ui/print_literal.stderr index b2f85be9d94a5..8c011d7bc0a6f 100644 --- a/tests/ui/print_literal.stderr +++ b/tests/ui/print_literal.stderr @@ -96,5 +96,101 @@ LL - println!("{bar} {foo}", foo = "hello", bar = "world"); LL + println!("world hello"); | -error: aborting due to 8 previous errors +error: literal with an empty format string + --> $DIR/print_literal.rs:56:20 + | +LL | println!("{}", "{} \x00 \u{ab123} \\\u{ab123} {:?}"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL - println!("{}", "{} \x00 \u{ab123} \\\u{ab123} {:?}"); +LL + println!("{{}} \x00 \u{ab123} \\\u{ab123} {{:?}}"); + | + +error: literal with an empty format string + --> $DIR/print_literal.rs:57:20 + | +LL | println!("{}", "\\\u{1234}"); + | ^^^^^^^^^^^^ + | +help: try + | +LL - println!("{}", "\\\u{1234}"); +LL + println!("\\\u{1234}"); + | + +error: literal with an empty format string + --> $DIR/print_literal.rs:61:20 + | +LL | println!("{}", r"\u{ab123} \u{{"); + | ^^^^^^^^^^^^^^^^^ + | +help: try + | +LL - println!("{}", r"\u{ab123} \u{{"); +LL + println!("\\u{{ab123}} \\u{{{{"); + | + +error: literal with an empty format string + --> $DIR/print_literal.rs:62:21 + | +LL | println!(r"{}", r"\u{ab123} \u{{"); + | ^^^^^^^^^^^^^^^^^ + | +help: try + | +LL - println!(r"{}", r"\u{ab123} \u{{"); +LL + println!(r"\u{{ab123}} \u{{{{"); + | + +error: literal with an empty format string + --> $DIR/print_literal.rs:63:20 + | +LL | println!("{}", r"\{ab123} \u{{"); + | ^^^^^^^^^^^^^^^^ + | +help: try + | +LL - println!("{}", r"\{ab123} \u{{"); +LL + println!("\\{{ab123}} \\u{{{{"); + | + +error: literal with an empty format string + --> $DIR/print_literal.rs:64:20 + | +LL | println!("{}", "\\u{ab123}"); + | ^^^^^^^^^^^^ + | +help: try + | +LL - println!("{}", "\\u{ab123}"); +LL + println!("\\u{{ab123}}"); + | + +error: literal with an empty format string + --> $DIR/print_literal.rs:65:20 + | +LL | println!("{}", "\\\\u{1234}"); + | ^^^^^^^^^^^^^ + | +help: try + | +LL - println!("{}", "\\\\u{1234}"); +LL + println!("\\\\u{{1234}}"); + | + +error: literal with an empty format string + --> $DIR/print_literal.rs:67:35 + | +LL | println!("mixed: {} {world}", "{hello}"); + | ^^^^^^^^^ + | +help: try + | +LL - println!("mixed: {} {world}", "{hello}"); +LL + println!("mixed: {{hello}} {world}"); + | + +error: aborting due to 16 previous errors From 3f0da4dda29c1dd058ab2ee0f4ddf3ff0d8a8bac Mon Sep 17 00:00:00 2001 From: blyxyas Date: Mon, 2 Oct 2023 13:28:45 +0200 Subject: [PATCH 31/48] Move `needless_pass_by_ref_mut`: `suspicious` -> `nursery` --- clippy_lints/src/needless_pass_by_ref_mut.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/needless_pass_by_ref_mut.rs b/clippy_lints/src/needless_pass_by_ref_mut.rs index 57652e5ff546b..3ad9ae0301a68 100644 --- a/clippy_lints/src/needless_pass_by_ref_mut.rs +++ b/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -49,7 +49,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.72.0"] pub NEEDLESS_PASS_BY_REF_MUT, - suspicious, + nursery, "using a `&mut` argument when it's not mutated" } From 07e63291eccb2aad2e42c31e11f614ee9f7592d8 Mon Sep 17 00:00:00 2001 From: blyxyas Date: Mon, 2 Oct 2023 20:14:43 +0200 Subject: [PATCH 32/48] Modify tests to account for the new allow-by-default `needless_pass_by_ref_mut` --- tests/ui/infinite_loop.rs | 2 - tests/ui/infinite_loop.stderr | 33 ++++++--------- tests/ui/let_underscore_future.rs | 2 - tests/ui/let_underscore_future.stderr | 17 ++------ tests/ui/mut_key.rs | 2 - tests/ui/mut_key.stderr | 41 +++++++----------- tests/ui/mut_reference.rs | 2 - tests/ui/mut_reference.stderr | 17 ++------ tests/ui/needless_pass_by_ref_mut.rs | 1 + tests/ui/needless_pass_by_ref_mut.stderr | 42 +++++++++---------- tests/ui/should_impl_trait/method_list_2.rs | 2 - .../ui/should_impl_trait/method_list_2.stderr | 36 ++++++---------- tests/ui/slow_vector_initialization.rs | 2 - tests/ui/slow_vector_initialization.stderr | 11 +---- 14 files changed, 71 insertions(+), 139 deletions(-) diff --git a/tests/ui/infinite_loop.rs b/tests/ui/infinite_loop.rs index 281e12c7b938a..765c670114746 100644 --- a/tests/ui/infinite_loop.rs +++ b/tests/ui/infinite_loop.rs @@ -7,8 +7,6 @@ fn fn_constref(i: &i32) -> i32 { unimplemented!() } fn fn_mutref(i: &mut i32) { - //~^ ERROR: this argument is a mutable reference, but not used mutably - //~| NOTE: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings` unimplemented!() } fn fooi() -> i32 { diff --git a/tests/ui/infinite_loop.stderr b/tests/ui/infinite_loop.stderr index c32b5e323c09e..a78e47d02290c 100644 --- a/tests/ui/infinite_loop.stderr +++ b/tests/ui/infinite_loop.stderr @@ -1,5 +1,5 @@ error: variables in the condition are not mutated in the loop body - --> $DIR/infinite_loop.rs:24:11 + --> $DIR/infinite_loop.rs:22:11 | LL | while y < 10 { | ^^^^^^ @@ -8,7 +8,7 @@ LL | while y < 10 { = note: `#[deny(clippy::while_immutable_condition)]` on by default error: variables in the condition are not mutated in the loop body - --> $DIR/infinite_loop.rs:31:11 + --> $DIR/infinite_loop.rs:29:11 | LL | while y < 10 && x < 3 { | ^^^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | while y < 10 && x < 3 { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> $DIR/infinite_loop.rs:40:11 + --> $DIR/infinite_loop.rs:38:11 | LL | while !cond { | ^^^^^ @@ -24,7 +24,7 @@ LL | while !cond { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> $DIR/infinite_loop.rs:86:11 + --> $DIR/infinite_loop.rs:84:11 | LL | while i < 3 { | ^^^^^ @@ -32,7 +32,7 @@ LL | while i < 3 { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> $DIR/infinite_loop.rs:93:11 + --> $DIR/infinite_loop.rs:91:11 | LL | while i < 3 && j > 0 { | ^^^^^^^^^^^^^^ @@ -40,7 +40,7 @@ LL | while i < 3 && j > 0 { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> $DIR/infinite_loop.rs:99:11 + --> $DIR/infinite_loop.rs:97:11 | LL | while i < 3 { | ^^^^^ @@ -48,7 +48,7 @@ LL | while i < 3 { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> $DIR/infinite_loop.rs:116:11 + --> $DIR/infinite_loop.rs:114:11 | LL | while i < 3 { | ^^^^^ @@ -56,7 +56,7 @@ LL | while i < 3 { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> $DIR/infinite_loop.rs:123:11 + --> $DIR/infinite_loop.rs:121:11 | LL | while i < 3 { | ^^^^^ @@ -64,7 +64,7 @@ LL | while i < 3 { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> $DIR/infinite_loop.rs:191:15 + --> $DIR/infinite_loop.rs:189:15 | LL | while self.count < n { | ^^^^^^^^^^^^^^ @@ -72,7 +72,7 @@ LL | while self.count < n { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> $DIR/infinite_loop.rs:201:11 + --> $DIR/infinite_loop.rs:199:11 | LL | while y < 10 { | ^^^^^^ @@ -82,7 +82,7 @@ LL | while y < 10 { = help: rewrite it as `if cond { loop { } }` error: variables in the condition are not mutated in the loop body - --> $DIR/infinite_loop.rs:210:11 + --> $DIR/infinite_loop.rs:208:11 | LL | while y < 10 { | ^^^^^^ @@ -91,14 +91,5 @@ LL | while y < 10 { = note: this loop contains `return`s or `break`s = help: rewrite it as `if cond { loop { } }` -error: this argument is a mutable reference, but not used mutably - --> $DIR/infinite_loop.rs:9:17 - | -LL | fn fn_mutref(i: &mut i32) { - | ^^^^^^^^ help: consider changing to: `&i32` - | - = note: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::needless_pass_by_ref_mut)]` - -error: aborting due to 12 previous errors +error: aborting due to 11 previous errors diff --git a/tests/ui/let_underscore_future.rs b/tests/ui/let_underscore_future.rs index 873ae667ab71a..c2185e9785d84 100644 --- a/tests/ui/let_underscore_future.rs +++ b/tests/ui/let_underscore_future.rs @@ -9,8 +9,6 @@ fn custom() -> impl Future { } fn do_something_to_future(future: &mut impl Future) {} -//~^ ERROR: this argument is a mutable reference, but not used mutably -//~| NOTE: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings` fn main() { let _ = some_async_fn(); diff --git a/tests/ui/let_underscore_future.stderr b/tests/ui/let_underscore_future.stderr index 3ba99c6377b73..ef927a8083bcc 100644 --- a/tests/ui/let_underscore_future.stderr +++ b/tests/ui/let_underscore_future.stderr @@ -1,5 +1,5 @@ error: non-binding `let` on a future - --> $DIR/let_underscore_future.rs:16:5 + --> $DIR/let_underscore_future.rs:14:5 | LL | let _ = some_async_fn(); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | let _ = some_async_fn(); = help: to override `-D warnings` add `#[allow(clippy::let_underscore_future)]` error: non-binding `let` on a future - --> $DIR/let_underscore_future.rs:18:5 + --> $DIR/let_underscore_future.rs:16:5 | LL | let _ = custom(); | ^^^^^^^^^^^^^^^^^ @@ -17,21 +17,12 @@ LL | let _ = custom(); = help: consider awaiting the future or dropping explicitly with `std::mem::drop` error: non-binding `let` on a future - --> $DIR/let_underscore_future.rs:23:5 + --> $DIR/let_underscore_future.rs:21:5 | LL | let _ = future; | ^^^^^^^^^^^^^^^ | = help: consider awaiting the future or dropping explicitly with `std::mem::drop` -error: this argument is a mutable reference, but not used mutably - --> $DIR/let_underscore_future.rs:11:35 - | -LL | fn do_something_to_future(future: &mut impl Future) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&impl Future` - | - = note: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::needless_pass_by_ref_mut)]` - -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/mut_key.rs b/tests/ui/mut_key.rs index 8069213217683..2d70bfd4c770c 100644 --- a/tests/ui/mut_key.rs +++ b/tests/ui/mut_key.rs @@ -32,8 +32,6 @@ fn should_not_take_this_arg(m: &mut HashMap, _n: usize) -> HashSet = HashMap::new(); //~^ ERROR: mutable key type m.keys().cloned().collect() diff --git a/tests/ui/mut_key.stderr b/tests/ui/mut_key.stderr index 3701769a9ca7f..48eeaff78a789 100644 --- a/tests/ui/mut_key.stderr +++ b/tests/ui/mut_key.stderr @@ -14,103 +14,94 @@ LL | fn should_not_take_this_arg(m: &mut HashMap, _n: usize) -> Hash | ^^^^^^^^^^^^ error: mutable key type - --> $DIR/mut_key.rs:37:5 + --> $DIR/mut_key.rs:35:5 | LL | let _other: HashMap = HashMap::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: mutable key type - --> $DIR/mut_key.rs:65:22 + --> $DIR/mut_key.rs:63:22 | LL | fn tuples_bad(_m: &mut HashMap<(Key, U), bool>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: mutable key type - --> $DIR/mut_key.rs:78:5 + --> $DIR/mut_key.rs:76:5 | LL | let _map = HashMap::, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: mutable key type - --> $DIR/mut_key.rs:80:5 + --> $DIR/mut_key.rs:78:5 | LL | let _map = HashMap::<&mut Cell, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: mutable key type - --> $DIR/mut_key.rs:82:5 + --> $DIR/mut_key.rs:80:5 | LL | let _map = HashMap::<&mut usize, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: mutable key type - --> $DIR/mut_key.rs:85:5 + --> $DIR/mut_key.rs:83:5 | LL | let _map = HashMap::>, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: mutable key type - --> $DIR/mut_key.rs:87:5 + --> $DIR/mut_key.rs:85:5 | LL | let _map = HashMap::, ()>, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: mutable key type - --> $DIR/mut_key.rs:89:5 + --> $DIR/mut_key.rs:87:5 | LL | let _map = HashMap::>, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: mutable key type - --> $DIR/mut_key.rs:91:5 + --> $DIR/mut_key.rs:89:5 | LL | let _map = HashMap::>, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: mutable key type - --> $DIR/mut_key.rs:93:5 + --> $DIR/mut_key.rs:91:5 | LL | let _map = HashMap::>, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: mutable key type - --> $DIR/mut_key.rs:95:5 + --> $DIR/mut_key.rs:93:5 | LL | let _map = HashMap::>>, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: mutable key type - --> $DIR/mut_key.rs:97:5 + --> $DIR/mut_key.rs:95:5 | LL | let _map = HashMap::, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: mutable key type - --> $DIR/mut_key.rs:100:5 + --> $DIR/mut_key.rs:98:5 | LL | let _map = HashMap::>, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: mutable key type - --> $DIR/mut_key.rs:102:5 + --> $DIR/mut_key.rs:100:5 | LL | let _map = HashMap::>, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: mutable key type - --> $DIR/mut_key.rs:104:5 + --> $DIR/mut_key.rs:102:5 | LL | let _map = HashMap::>, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: this argument is a mutable reference, but not used mutably - --> $DIR/mut_key.rs:31:32 - | -LL | fn should_not_take_this_arg(m: &mut HashMap, _n: usize) -> HashSet { - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&HashMap` - | - = note: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::needless_pass_by_ref_mut)]` - -error: aborting due to 18 previous errors +error: aborting due to 17 previous errors diff --git a/tests/ui/mut_reference.rs b/tests/ui/mut_reference.rs index f3db226e4e7fc..1d7faaa5e75e4 100644 --- a/tests/ui/mut_reference.rs +++ b/tests/ui/mut_reference.rs @@ -22,8 +22,6 @@ impl MyStruct { fn takes_an_immutable_reference(&self, a: &i32) {} fn takes_a_mutable_reference(&self, a: &mut i32) {} - //~^ ERROR: this argument is a mutable reference, but not used mutably - //~| NOTE: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings` } #[warn(clippy::unnecessary_mut_passed)] diff --git a/tests/ui/mut_reference.stderr b/tests/ui/mut_reference.stderr index d7a0d0c225250..87db08e2a7420 100644 --- a/tests/ui/mut_reference.stderr +++ b/tests/ui/mut_reference.stderr @@ -1,5 +1,5 @@ error: the function `takes_an_immutable_reference` doesn't need a mutable reference - --> $DIR/mut_reference.rs:32:34 + --> $DIR/mut_reference.rs:30:34 | LL | takes_an_immutable_reference(&mut 42); | ^^^^^^^ @@ -8,25 +8,16 @@ LL | takes_an_immutable_reference(&mut 42); = help: to override `-D warnings` add `#[allow(clippy::unnecessary_mut_passed)]` error: the function `as_ptr` doesn't need a mutable reference - --> $DIR/mut_reference.rs:36:12 + --> $DIR/mut_reference.rs:34:12 | LL | as_ptr(&mut 42); | ^^^^^^^ error: the method `takes_an_immutable_reference` doesn't need a mutable reference - --> $DIR/mut_reference.rs:41:44 + --> $DIR/mut_reference.rs:39:44 | LL | my_struct.takes_an_immutable_reference(&mut 42); | ^^^^^^^ -error: this argument is a mutable reference, but not used mutably - --> $DIR/mut_reference.rs:24:44 - | -LL | fn takes_a_mutable_reference(&self, a: &mut i32) {} - | ^^^^^^^^ help: consider changing to: `&i32` - | - = note: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::needless_pass_by_ref_mut)]` - -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/needless_pass_by_ref_mut.rs b/tests/ui/needless_pass_by_ref_mut.rs index 9cddcb3df2378..39d76f9990022 100644 --- a/tests/ui/needless_pass_by_ref_mut.rs +++ b/tests/ui/needless_pass_by_ref_mut.rs @@ -1,4 +1,5 @@ #![allow(clippy::if_same_then_else, clippy::no_effect, clippy::redundant_closure_call)] +#![warn(clippy::needless_pass_by_ref_mut)] #![feature(lint_reasons)] //@no-rustfix use std::ptr::NonNull; diff --git a/tests/ui/needless_pass_by_ref_mut.stderr b/tests/ui/needless_pass_by_ref_mut.stderr index 0c7fbd5df6d9d..aa937c3f6af2b 100644 --- a/tests/ui/needless_pass_by_ref_mut.stderr +++ b/tests/ui/needless_pass_by_ref_mut.stderr @@ -1,5 +1,5 @@ error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:6:11 + --> $DIR/needless_pass_by_ref_mut.rs:7:11 | LL | fn foo(s: &mut Vec, b: &u32, x: &mut u32) { | ^^^^^^^^^^^^^ help: consider changing to: `&Vec` @@ -8,79 +8,79 @@ LL | fn foo(s: &mut Vec, b: &u32, x: &mut u32) { = help: to override `-D warnings` add `#[allow(clippy::needless_pass_by_ref_mut)]` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:31:12 + --> $DIR/needless_pass_by_ref_mut.rs:32:12 | LL | fn foo6(s: &mut Vec) { | ^^^^^^^^^^^^^ help: consider changing to: `&Vec` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:44:29 + --> $DIR/needless_pass_by_ref_mut.rs:45:29 | LL | fn mushroom(&self, vec: &mut Vec) -> usize { | ^^^^^^^^^^^^^ help: consider changing to: `&Vec` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:49:31 + --> $DIR/needless_pass_by_ref_mut.rs:50:31 | LL | fn badger(&mut self, vec: &mut Vec) -> usize { | ^^^^^^^^^^^^^ help: consider changing to: `&Vec` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:126:16 + --> $DIR/needless_pass_by_ref_mut.rs:127:16 | LL | async fn a1(x: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:130:16 + --> $DIR/needless_pass_by_ref_mut.rs:131:16 | LL | async fn a2(x: &mut i32, y: String) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:134:16 + --> $DIR/needless_pass_by_ref_mut.rs:135:16 | LL | async fn a3(x: &mut i32, y: String, z: String) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:138:16 + --> $DIR/needless_pass_by_ref_mut.rs:139:16 | LL | async fn a4(x: &mut i32, y: i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:142:24 + --> $DIR/needless_pass_by_ref_mut.rs:143:24 | LL | async fn a5(x: i32, y: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:146:24 + --> $DIR/needless_pass_by_ref_mut.rs:147:24 | LL | async fn a6(x: i32, y: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:150:32 + --> $DIR/needless_pass_by_ref_mut.rs:151:32 | LL | async fn a7(x: i32, y: i32, z: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:154:24 + --> $DIR/needless_pass_by_ref_mut.rs:155:24 | LL | async fn a8(x: i32, a: &mut i32, y: i32, z: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:154:45 + --> $DIR/needless_pass_by_ref_mut.rs:155:45 | LL | async fn a8(x: i32, a: &mut i32, y: i32, z: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:188:16 + --> $DIR/needless_pass_by_ref_mut.rs:189:16 | LL | fn cfg_warn(s: &mut u32) {} | ^^^^^^^^ help: consider changing to: `&u32` @@ -88,7 +88,7 @@ LL | fn cfg_warn(s: &mut u32) {} = note: this is cfg-gated and may require further changes error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:194:20 + --> $DIR/needless_pass_by_ref_mut.rs:195:20 | LL | fn cfg_warn(s: &mut u32) {} | ^^^^^^^^ help: consider changing to: `&u32` @@ -96,19 +96,19 @@ LL | fn cfg_warn(s: &mut u32) {} = note: this is cfg-gated and may require further changes error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:208:39 + --> $DIR/needless_pass_by_ref_mut.rs:209:39 | LL | async fn inner_async2(x: &mut i32, y: &mut u32) { | ^^^^^^^^ help: consider changing to: `&u32` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:216:26 + --> $DIR/needless_pass_by_ref_mut.rs:217:26 | LL | async fn inner_async3(x: &mut i32, y: &mut u32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:235:34 + --> $DIR/needless_pass_by_ref_mut.rs:236:34 | LL | pub async fn call_in_closure1(n: &mut str) { | ^^^^^^^^ help: consider changing to: `&str` @@ -116,7 +116,7 @@ LL | pub async fn call_in_closure1(n: &mut str) { = warning: changing this function will impact semver compatibility error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:247:25 + --> $DIR/needless_pass_by_ref_mut.rs:248:25 | LL | pub async fn closure(n: &mut usize) -> impl '_ + FnMut() { | ^^^^^^^^^^ help: consider changing to: `&usize` @@ -124,7 +124,7 @@ LL | pub async fn closure(n: &mut usize) -> impl '_ + FnMut() { = warning: changing this function will impact semver compatibility error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:254:20 + --> $DIR/needless_pass_by_ref_mut.rs:255:20 | LL | pub fn closure2(n: &mut usize) -> impl '_ + FnMut() -> usize { | ^^^^^^^^^^ help: consider changing to: `&usize` @@ -132,7 +132,7 @@ LL | pub fn closure2(n: &mut usize) -> impl '_ + FnMut() -> usize { = warning: changing this function will impact semver compatibility error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:265:26 + --> $DIR/needless_pass_by_ref_mut.rs:266:26 | LL | pub async fn closure4(n: &mut usize) { | ^^^^^^^^^^ help: consider changing to: `&usize` diff --git a/tests/ui/should_impl_trait/method_list_2.rs b/tests/ui/should_impl_trait/method_list_2.rs index 550ec00726817..33211b32d74b3 100644 --- a/tests/ui/should_impl_trait/method_list_2.rs +++ b/tests/ui/should_impl_trait/method_list_2.rs @@ -40,8 +40,6 @@ impl T { pub fn hash(&self, state: &mut T) { //~^ ERROR: method `hash` can be confused for the standard trait method `std::hash::Ha - //~| ERROR: this argument is a mutable reference, but not used mutably - //~| NOTE: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings` unimplemented!() } diff --git a/tests/ui/should_impl_trait/method_list_2.stderr b/tests/ui/should_impl_trait/method_list_2.stderr index 79afddea24798..c257f41134266 100644 --- a/tests/ui/should_impl_trait/method_list_2.stderr +++ b/tests/ui/should_impl_trait/method_list_2.stderr @@ -38,8 +38,6 @@ error: method `hash` can be confused for the standard trait method `std::hash::H | LL | / pub fn hash(&self, state: &mut T) { LL | | -LL | | -LL | | LL | | unimplemented!() LL | | } | |_____^ @@ -47,7 +45,7 @@ LL | | } = help: consider implementing the trait `std::hash::Hash` or choosing a less ambiguous method name error: method `index` can be confused for the standard trait method `std::ops::Index::index` - --> $DIR/method_list_2.rs:48:5 + --> $DIR/method_list_2.rs:46:5 | LL | / pub fn index(&self, index: usize) -> &Self { LL | | @@ -58,7 +56,7 @@ LL | | } = help: consider implementing the trait `std::ops::Index` or choosing a less ambiguous method name error: method `index_mut` can be confused for the standard trait method `std::ops::IndexMut::index_mut` - --> $DIR/method_list_2.rs:53:5 + --> $DIR/method_list_2.rs:51:5 | LL | / pub fn index_mut(&mut self, index: usize) -> &mut Self { LL | | @@ -69,7 +67,7 @@ LL | | } = help: consider implementing the trait `std::ops::IndexMut` or choosing a less ambiguous method name error: method `into_iter` can be confused for the standard trait method `std::iter::IntoIterator::into_iter` - --> $DIR/method_list_2.rs:58:5 + --> $DIR/method_list_2.rs:56:5 | LL | / pub fn into_iter(self) -> Self { LL | | @@ -80,7 +78,7 @@ LL | | } = help: consider implementing the trait `std::iter::IntoIterator` or choosing a less ambiguous method name error: method `mul` can be confused for the standard trait method `std::ops::Mul::mul` - --> $DIR/method_list_2.rs:63:5 + --> $DIR/method_list_2.rs:61:5 | LL | / pub fn mul(self, rhs: Self) -> Self { LL | | @@ -91,7 +89,7 @@ LL | | } = help: consider implementing the trait `std::ops::Mul` or choosing a less ambiguous method name error: method `neg` can be confused for the standard trait method `std::ops::Neg::neg` - --> $DIR/method_list_2.rs:68:5 + --> $DIR/method_list_2.rs:66:5 | LL | / pub fn neg(self) -> Self { LL | | @@ -102,7 +100,7 @@ LL | | } = help: consider implementing the trait `std::ops::Neg` or choosing a less ambiguous method name error: method `next` can be confused for the standard trait method `std::iter::Iterator::next` - --> $DIR/method_list_2.rs:73:5 + --> $DIR/method_list_2.rs:71:5 | LL | / pub fn next(&mut self) -> Option { LL | | @@ -113,7 +111,7 @@ LL | | } = help: consider implementing the trait `std::iter::Iterator` or choosing a less ambiguous method name error: method `not` can be confused for the standard trait method `std::ops::Not::not` - --> $DIR/method_list_2.rs:78:5 + --> $DIR/method_list_2.rs:76:5 | LL | / pub fn not(self) -> Self { LL | | @@ -124,7 +122,7 @@ LL | | } = help: consider implementing the trait `std::ops::Not` or choosing a less ambiguous method name error: method `rem` can be confused for the standard trait method `std::ops::Rem::rem` - --> $DIR/method_list_2.rs:83:5 + --> $DIR/method_list_2.rs:81:5 | LL | / pub fn rem(self, rhs: Self) -> Self { LL | | @@ -135,7 +133,7 @@ LL | | } = help: consider implementing the trait `std::ops::Rem` or choosing a less ambiguous method name error: method `shl` can be confused for the standard trait method `std::ops::Shl::shl` - --> $DIR/method_list_2.rs:88:5 + --> $DIR/method_list_2.rs:86:5 | LL | / pub fn shl(self, rhs: Self) -> Self { LL | | @@ -146,7 +144,7 @@ LL | | } = help: consider implementing the trait `std::ops::Shl` or choosing a less ambiguous method name error: method `shr` can be confused for the standard trait method `std::ops::Shr::shr` - --> $DIR/method_list_2.rs:93:5 + --> $DIR/method_list_2.rs:91:5 | LL | / pub fn shr(self, rhs: Self) -> Self { LL | | @@ -157,7 +155,7 @@ LL | | } = help: consider implementing the trait `std::ops::Shr` or choosing a less ambiguous method name error: method `sub` can be confused for the standard trait method `std::ops::Sub::sub` - --> $DIR/method_list_2.rs:98:5 + --> $DIR/method_list_2.rs:96:5 | LL | / pub fn sub(self, rhs: Self) -> Self { LL | | @@ -167,15 +165,5 @@ LL | | } | = help: consider implementing the trait `std::ops::Sub` or choosing a less ambiguous method name -error: this argument is a mutable reference, but not used mutably - --> $DIR/method_list_2.rs:41:31 - | -LL | pub fn hash(&self, state: &mut T) { - | ^^^^^^ help: consider changing to: `&T` - | - = warning: changing this function will impact semver compatibility - = note: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::needless_pass_by_ref_mut)]` - -error: aborting due to 16 previous errors +error: aborting due to 15 previous errors diff --git a/tests/ui/slow_vector_initialization.rs b/tests/ui/slow_vector_initialization.rs index 5c3086c9d69ea..16f81019574fb 100644 --- a/tests/ui/slow_vector_initialization.rs +++ b/tests/ui/slow_vector_initialization.rs @@ -103,8 +103,6 @@ fn from_empty_vec() { } fn do_stuff(vec: &mut [u8]) {} -//~^ ERROR: this argument is a mutable reference, but not used mutably -//~| NOTE: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings` fn extend_vector_with_manipulations_between() { let len = 300; diff --git a/tests/ui/slow_vector_initialization.stderr b/tests/ui/slow_vector_initialization.stderr index 4d24400ecb596..16a7057653c10 100644 --- a/tests/ui/slow_vector_initialization.stderr +++ b/tests/ui/slow_vector_initialization.stderr @@ -105,14 +105,5 @@ LL | vec1 = vec![]; LL | vec1.resize(10, 0); | ^^^^^^^^^^^^^^^^^^ -error: this argument is a mutable reference, but not used mutably - --> $DIR/slow_vector_initialization.rs:105:18 - | -LL | fn do_stuff(vec: &mut [u8]) {} - | ^^^^^^^^^ help: consider changing to: `&[u8]` - | - = note: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::needless_pass_by_ref_mut)]` - -error: aborting due to 14 previous errors +error: aborting due to 13 previous errors From ea4b38820bafe9d7016cf19d35e977b08f0cbdd3 Mon Sep 17 00:00:00 2001 From: ouz-a Date: Wed, 16 Aug 2023 08:43:30 +0300 Subject: [PATCH 33/48] subtyping_projections --- clippy_utils/src/qualify_min_const_fn.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index 17233058c9c93..55f9cb27ad4db 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -272,6 +272,7 @@ fn check_place<'tcx>(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &B | ProjectionElem::Downcast(..) | ProjectionElem::Subslice { .. } | ProjectionElem::Deref + | ProjectionElem::Subtype(_) | ProjectionElem::Index(_) => {}, } } From 2c525fd758961af9f7b24dc433ec9021b4bff4dc Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 2 Oct 2023 21:31:46 +0000 Subject: [PATCH 34/48] Point to full async fn for future --- tests/ui/crashes/ice-10645.stderr | 4 ++-- tests/ui/future_not_send.stderr | 37 +++++++++++++++++-------------- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/tests/ui/crashes/ice-10645.stderr b/tests/ui/crashes/ice-10645.stderr index fc5347c86cdeb..7fc62d4fcf855 100644 --- a/tests/ui/crashes/ice-10645.stderr +++ b/tests/ui/crashes/ice-10645.stderr @@ -1,8 +1,8 @@ warning: future cannot be sent between threads safely - --> $DIR/ice-10645.rs:5:35 + --> $DIR/ice-10645.rs:5:1 | LL | pub async fn bar<'a, T: 'a>(_: T) {} - | ^ future returned by `bar` is not `Send` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `bar` is not `Send` | note: captured value is not `Send` --> $DIR/ice-10645.rs:5:29 diff --git a/tests/ui/future_not_send.stderr b/tests/ui/future_not_send.stderr index f43e3c8ff9f7b..7ef4947f1d6c5 100644 --- a/tests/ui/future_not_send.stderr +++ b/tests/ui/future_not_send.stderr @@ -1,8 +1,8 @@ error: future cannot be sent between threads safely - --> $DIR/future_not_send.rs:7:62 + --> $DIR/future_not_send.rs:7:1 | LL | async fn private_future(rc: Rc<[u8]>, cell: &Cell) -> bool { - | ^^^^ future returned by `private_future` is not `Send` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `private_future` is not `Send` | note: future is not `Send` as this value is used across an await --> $DIR/future_not_send.rs:9:20 @@ -23,10 +23,10 @@ LL | async fn private_future(rc: Rc<[u8]>, cell: &Cell) -> bool { = help: to override `-D warnings` add `#[allow(clippy::future_not_send)]` error: future cannot be sent between threads safely - --> $DIR/future_not_send.rs:12:42 + --> $DIR/future_not_send.rs:12:1 | LL | pub async fn public_future(rc: Rc<[u8]>) { - | ^ future returned by `public_future` is not `Send` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `public_future` is not `Send` | note: future is not `Send` as this value is used across an await --> $DIR/future_not_send.rs:14:20 @@ -39,10 +39,10 @@ LL | async { true }.await; = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send` error: future cannot be sent between threads safely - --> $DIR/future_not_send.rs:21:63 + --> $DIR/future_not_send.rs:21:1 | LL | async fn private_future2(rc: Rc<[u8]>, cell: &Cell) -> bool { - | ^^^^ future returned by `private_future2` is not `Send` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `private_future2` is not `Send` | note: captured value is not `Send` --> $DIR/future_not_send.rs:21:26 @@ -58,10 +58,10 @@ LL | async fn private_future2(rc: Rc<[u8]>, cell: &Cell) -> bool { = note: `std::cell::Cell` doesn't implement `std::marker::Sync` error: future cannot be sent between threads safely - --> $DIR/future_not_send.rs:26:43 + --> $DIR/future_not_send.rs:26:1 | LL | pub async fn public_future2(rc: Rc<[u8]>) {} - | ^ future returned by `public_future2` is not `Send` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `public_future2` is not `Send` | note: captured value is not `Send` --> $DIR/future_not_send.rs:26:29 @@ -71,10 +71,10 @@ LL | pub async fn public_future2(rc: Rc<[u8]>) {} = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send` error: future cannot be sent between threads safely - --> $DIR/future_not_send.rs:38:39 + --> $DIR/future_not_send.rs:38:5 | LL | async fn private_future(&self) -> usize { - | ^^^^^ future returned by `private_future` is not `Send` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `private_future` is not `Send` | note: future is not `Send` as this value is used across an await --> $DIR/future_not_send.rs:40:24 @@ -87,10 +87,10 @@ LL | async { true }.await; = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Sync` error: future cannot be sent between threads safely - --> $DIR/future_not_send.rs:44:39 + --> $DIR/future_not_send.rs:44:5 | LL | pub async fn public_future(&self) { - | ^ future returned by `public_future` is not `Send` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `public_future` is not `Send` | note: captured value is not `Send` because `&` references cannot be sent unless their referent is `Sync` --> $DIR/future_not_send.rs:44:32 @@ -100,10 +100,13 @@ LL | pub async fn public_future(&self) { = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Sync` error: future cannot be sent between threads safely - --> $DIR/future_not_send.rs:55:37 + --> $DIR/future_not_send.rs:55:1 | -LL | async fn generic_future(t: T) -> T - | ^ future returned by `generic_future` is not `Send` +LL | / async fn generic_future(t: T) -> T +LL | | +LL | | where +LL | | T: Send, + | |____________^ future returned by `generic_future` is not `Send` | note: future is not `Send` as this value is used across an await --> $DIR/future_not_send.rs:61:20 @@ -115,10 +118,10 @@ LL | async { true }.await; = note: `T` doesn't implement `std::marker::Sync` error: future cannot be sent between threads safely - --> $DIR/future_not_send.rs:73:34 + --> $DIR/future_not_send.rs:73:1 | LL | async fn unclear_future(t: T) {} - | ^ future returned by `unclear_future` is not `Send` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `unclear_future` is not `Send` | note: captured value is not `Send` --> $DIR/future_not_send.rs:73:28 From e465264d47fbe0a19c620e320c1b5dbeae446f1e Mon Sep 17 00:00:00 2001 From: koka Date: Tue, 3 Oct 2023 20:36:35 +0900 Subject: [PATCH 35/48] Avoid invoking `ignored_unit_patterns` in macro definition --- clippy_lints/src/ignored_unit_patterns.rs | 4 ++++ tests/ui/auxiliary/proc_macro_derive.rs | 16 +++++++++++++++ tests/ui/ignored_unit_patterns.fixed | 15 ++++++++++++++ tests/ui/ignored_unit_patterns.rs | 15 ++++++++++++++ tests/ui/ignored_unit_patterns.stderr | 24 +++++++++++++++++------ 5 files changed, 68 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/ignored_unit_patterns.rs b/clippy_lints/src/ignored_unit_patterns.rs index d8ead1c9d9f68..ef2a66d4a209a 100644 --- a/clippy_lints/src/ignored_unit_patterns.rs +++ b/clippy_lints/src/ignored_unit_patterns.rs @@ -37,6 +37,10 @@ declare_lint_pass!(IgnoredUnitPatterns => [IGNORED_UNIT_PATTERNS]); impl<'tcx> LateLintPass<'tcx> for IgnoredUnitPatterns { fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx hir::Pat<'tcx>) { + if pat.span.from_expansion() { + return; + } + match cx.tcx.hir().get_parent(pat.hir_id) { Node::Param(param) if matches!(cx.tcx.hir().get_parent(param.hir_id), Node::Item(_)) => { // Ignore function parameters diff --git a/tests/ui/auxiliary/proc_macro_derive.rs b/tests/ui/auxiliary/proc_macro_derive.rs index 556b886f386d8..79a95d775b11e 100644 --- a/tests/ui/auxiliary/proc_macro_derive.rs +++ b/tests/ui/auxiliary/proc_macro_derive.rs @@ -153,3 +153,19 @@ pub fn shadow_derive(_: TokenStream) -> TokenStream { .into(), ]) } + +#[proc_macro_derive(StructIgnoredUnitPattern)] +pub fn derive_ignored_unit_pattern(_: TokenStream) -> TokenStream { + quote! { + struct A; + impl A { + fn a(&self) -> Result<(), ()> { + unimplemented!() + } + + pub fn b(&self) { + let _ = self.a().unwrap(); + } + } + } +} diff --git a/tests/ui/ignored_unit_patterns.fixed b/tests/ui/ignored_unit_patterns.fixed index 6c6f21fee16b0..15eaf1f659abf 100644 --- a/tests/ui/ignored_unit_patterns.fixed +++ b/tests/ui/ignored_unit_patterns.fixed @@ -1,3 +1,4 @@ +//@aux-build:proc_macro_derive.rs #![warn(clippy::ignored_unit_patterns)] #![allow(clippy::let_unit_value, clippy::redundant_pattern_matching, clippy::single_match)] @@ -14,8 +15,22 @@ fn main() { //~^ ERROR: matching over `()` is more explicit let _ = foo().map_err(|()| todo!()); //~^ ERROR: matching over `()` is more explicit + + println!( + "{:?}", + match foo() { + Ok(()) => {}, + //~^ ERROR: matching over `()` is more explicit + Err(()) => {}, + //~^ ERROR: matching over `()` is more explicit + } + ); } +// ignored_unit_patterns in derive macro should be ok +#[derive(proc_macro_derive::StructIgnoredUnitPattern)] +pub struct B; + #[allow(unused)] pub fn moo(_: ()) { let () = foo().unwrap(); diff --git a/tests/ui/ignored_unit_patterns.rs b/tests/ui/ignored_unit_patterns.rs index 5e8c2e03ba2cb..9cac3a440aba3 100644 --- a/tests/ui/ignored_unit_patterns.rs +++ b/tests/ui/ignored_unit_patterns.rs @@ -1,3 +1,4 @@ +//@aux-build:proc_macro_derive.rs #![warn(clippy::ignored_unit_patterns)] #![allow(clippy::let_unit_value, clippy::redundant_pattern_matching, clippy::single_match)] @@ -14,8 +15,22 @@ fn main() { //~^ ERROR: matching over `()` is more explicit let _ = foo().map_err(|_| todo!()); //~^ ERROR: matching over `()` is more explicit + + println!( + "{:?}", + match foo() { + Ok(_) => {}, + //~^ ERROR: matching over `()` is more explicit + Err(_) => {}, + //~^ ERROR: matching over `()` is more explicit + } + ); } +// ignored_unit_patterns in derive macro should be ok +#[derive(proc_macro_derive::StructIgnoredUnitPattern)] +pub struct B; + #[allow(unused)] pub fn moo(_: ()) { let _ = foo().unwrap(); diff --git a/tests/ui/ignored_unit_patterns.stderr b/tests/ui/ignored_unit_patterns.stderr index df5e1d89e906e..cac01a87dba0d 100644 --- a/tests/ui/ignored_unit_patterns.stderr +++ b/tests/ui/ignored_unit_patterns.stderr @@ -1,5 +1,5 @@ error: matching over `()` is more explicit - --> $DIR/ignored_unit_patterns.rs:10:12 + --> $DIR/ignored_unit_patterns.rs:11:12 | LL | Ok(_) => {}, | ^ help: use `()` instead of `_`: `()` @@ -8,28 +8,40 @@ LL | Ok(_) => {}, = help: to override `-D warnings` add `#[allow(clippy::ignored_unit_patterns)]` error: matching over `()` is more explicit - --> $DIR/ignored_unit_patterns.rs:11:13 + --> $DIR/ignored_unit_patterns.rs:12:13 | LL | Err(_) => {}, | ^ help: use `()` instead of `_`: `()` error: matching over `()` is more explicit - --> $DIR/ignored_unit_patterns.rs:13:15 + --> $DIR/ignored_unit_patterns.rs:14:15 | LL | if let Ok(_) = foo() {} | ^ help: use `()` instead of `_`: `()` error: matching over `()` is more explicit - --> $DIR/ignored_unit_patterns.rs:15:28 + --> $DIR/ignored_unit_patterns.rs:16:28 | LL | let _ = foo().map_err(|_| todo!()); | ^ help: use `()` instead of `_`: `()` error: matching over `()` is more explicit - --> $DIR/ignored_unit_patterns.rs:21:9 + --> $DIR/ignored_unit_patterns.rs:22:16 + | +LL | Ok(_) => {}, + | ^ help: use `()` instead of `_`: `()` + +error: matching over `()` is more explicit + --> $DIR/ignored_unit_patterns.rs:24:17 + | +LL | Err(_) => {}, + | ^ help: use `()` instead of `_`: `()` + +error: matching over `()` is more explicit + --> $DIR/ignored_unit_patterns.rs:36:9 | LL | let _ = foo().unwrap(); | ^ help: use `()` instead of `_`: `()` -error: aborting due to 5 previous errors +error: aborting due to 7 previous errors From 1a56f90ee5ff65020e9992dd6e434e9ec0e46435 Mon Sep 17 00:00:00 2001 From: koka Date: Tue, 3 Oct 2023 21:22:54 +0900 Subject: [PATCH 36/48] Fix: avoid changing drop order --- clippy_lints/src/redundant_locals.rs | 10 ++++++++++ tests/ui/redundant_locals.rs | 17 +++++++++++++++++ tests/ui/redundant_locals.stderr | 13 ++++++++++++- 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/redundant_locals.rs b/clippy_lints/src/redundant_locals.rs index a1ebba6a6a8fd..fd0506a658f00 100644 --- a/clippy_lints/src/redundant_locals.rs +++ b/clippy_lints/src/redundant_locals.rs @@ -4,11 +4,13 @@ use clippy_utils::ty::needs_ordered_drop; use rustc_ast::Mutability; use rustc_hir::def::Res; use rustc_hir::{BindingAnnotation, ByRef, Expr, ExprKind, HirId, Local, Node, Pat, PatKind, QPath}; +use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::Ident; use rustc_span::DesugaringKind; +use rustc_trait_selection::infer::InferCtxtExt as _; declare_clippy_lint! { /// ### What it does @@ -70,6 +72,14 @@ impl<'tcx> LateLintPass<'tcx> for RedundantLocals { // the local is user-controlled if !in_external_macro(cx.sess(), local.span); if !is_from_proc_macro(cx, expr); + // the local does not impl Drop trait. see #11599 + let local_ty = cx.typeck_results().node_type(local.hir_id); + if let Some(drop_trait_id) = cx.tcx.lang_items().drop_trait(); + if !cx.tcx.infer_ctxt().build().type_implements_trait( + drop_trait_id, + [local_ty], + cx.param_env + ).must_apply_modulo_regions(); then { span_lint_and_help( cx, diff --git a/tests/ui/redundant_locals.rs b/tests/ui/redundant_locals.rs index c5d93e4365d90..985bc739b263a 100644 --- a/tests/ui/redundant_locals.rs +++ b/tests/ui/redundant_locals.rs @@ -118,3 +118,20 @@ fn macros() { let x = x; } } + +struct WithDrop(usize); +impl Drop for WithDrop { + fn drop(&mut self) {} +} + +struct WithoutDrop(usize); + +fn drop_trait() { + let a = WithDrop(1); + let b = WithDrop(2); + let a = a; + + let c = WithoutDrop(1); + let d = WithoutDrop(2); + let c = c; +} diff --git a/tests/ui/redundant_locals.stderr b/tests/ui/redundant_locals.stderr index 13b872e9576d8..9f8dc6e616249 100644 --- a/tests/ui/redundant_locals.stderr +++ b/tests/ui/redundant_locals.stderr @@ -133,5 +133,16 @@ LL | let x = x; | = help: remove the redefinition of `x` -error: aborting due to 13 previous errors +error: redundant redefinition of a binding + --> $DIR/redundant_locals.rs:134:9 + | +LL | let c = WithoutDrop(1); + | ^ +LL | let d = WithoutDrop(2); +LL | let c = c; + | ^^^^^^^^^^ + | + = help: remove the redefinition of `c` + +error: aborting due to 14 previous errors From c7152679efd5cd7c433f77eae2109170e4cc34e8 Mon Sep 17 00:00:00 2001 From: koka Date: Wed, 4 Oct 2023 00:13:53 +0900 Subject: [PATCH 37/48] Apply review suggestions from @y21 --- clippy_lints/src/redundant_locals.rs | 24 ++---------------------- tests/ui/redundant_locals.rs | 26 +++++++++++++++++++++++--- tests/ui/redundant_locals.stderr | 10 +++++----- 3 files changed, 30 insertions(+), 30 deletions(-) diff --git a/clippy_lints/src/redundant_locals.rs b/clippy_lints/src/redundant_locals.rs index fd0506a658f00..a26bb98c5e23e 100644 --- a/clippy_lints/src/redundant_locals.rs +++ b/clippy_lints/src/redundant_locals.rs @@ -3,14 +3,12 @@ use clippy_utils::is_from_proc_macro; use clippy_utils::ty::needs_ordered_drop; use rustc_ast::Mutability; use rustc_hir::def::Res; -use rustc_hir::{BindingAnnotation, ByRef, Expr, ExprKind, HirId, Local, Node, Pat, PatKind, QPath}; -use rustc_infer::infer::TyCtxtInferExt; +use rustc_hir::{BindingAnnotation, ByRef, ExprKind, HirId, Local, Node, Pat, PatKind, QPath}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::Ident; use rustc_span::DesugaringKind; -use rustc_trait_selection::infer::InferCtxtExt as _; declare_clippy_lint! { /// ### What it does @@ -68,18 +66,10 @@ impl<'tcx> LateLintPass<'tcx> for RedundantLocals { // the local does not change the effect of assignments to the binding. see #11290 if !affects_assignments(cx, mutability, binding_id, local.hir_id); // the local does not affect the code's drop behavior - if !affects_drop_behavior(cx, binding_id, local.hir_id, expr); + if !needs_ordered_drop(cx, cx.typeck_results().expr_ty(expr)); // the local is user-controlled if !in_external_macro(cx.sess(), local.span); if !is_from_proc_macro(cx, expr); - // the local does not impl Drop trait. see #11599 - let local_ty = cx.typeck_results().node_type(local.hir_id); - if let Some(drop_trait_id) = cx.tcx.lang_items().drop_trait(); - if !cx.tcx.infer_ctxt().build().type_implements_trait( - drop_trait_id, - [local_ty], - cx.param_env - ).must_apply_modulo_regions(); then { span_lint_and_help( cx, @@ -114,13 +104,3 @@ fn affects_assignments(cx: &LateContext<'_>, mutability: Mutability, bind: HirId // the binding is mutable and the rebinding is in a different scope than the original binding mutability == Mutability::Mut && hir.get_enclosing_scope(bind) != hir.get_enclosing_scope(rebind) } - -/// Check if a rebinding of a local affects the code's drop behavior. -fn affects_drop_behavior<'tcx>(cx: &LateContext<'tcx>, bind: HirId, rebind: HirId, rebind_expr: &Expr<'tcx>) -> bool { - let hir = cx.tcx.hir(); - - // the rebinding is in a different scope than the original binding - // and the type of the binding cares about drop order - hir.get_enclosing_scope(bind) != hir.get_enclosing_scope(rebind) - && needs_ordered_drop(cx, cx.typeck_results().expr_ty(rebind_expr)) -} diff --git a/tests/ui/redundant_locals.rs b/tests/ui/redundant_locals.rs index 985bc739b263a..e81db300f15a2 100644 --- a/tests/ui/redundant_locals.rs +++ b/tests/ui/redundant_locals.rs @@ -124,14 +124,34 @@ impl Drop for WithDrop { fn drop(&mut self) {} } +struct InnerDrop(WithDrop); + +struct ComposeDrop { + d: WithDrop, +} + struct WithoutDrop(usize); fn drop_trait() { let a = WithDrop(1); let b = WithDrop(2); let a = a; +} - let c = WithoutDrop(1); - let d = WithoutDrop(2); - let c = c; +fn without_drop() { + let a = WithoutDrop(1); + let b = WithoutDrop(2); + let a = a; +} + +fn drop_inner() { + let a = InnerDrop(WithDrop(1)); + let b = InnerDrop(WithDrop(2)); + let a = a; +} + +fn drop_compose() { + let a = ComposeDrop { d: WithDrop(1) }; + let b = ComposeDrop { d: WithDrop(1) }; + let a = a; } diff --git a/tests/ui/redundant_locals.stderr b/tests/ui/redundant_locals.stderr index 9f8dc6e616249..6e9da8fccbdc1 100644 --- a/tests/ui/redundant_locals.stderr +++ b/tests/ui/redundant_locals.stderr @@ -134,15 +134,15 @@ LL | let x = x; = help: remove the redefinition of `x` error: redundant redefinition of a binding - --> $DIR/redundant_locals.rs:134:9 + --> $DIR/redundant_locals.rs:142:9 | -LL | let c = WithoutDrop(1); +LL | let a = WithoutDrop(1); | ^ -LL | let d = WithoutDrop(2); -LL | let c = c; +LL | let b = WithoutDrop(2); +LL | let a = a; | ^^^^^^^^^^ | - = help: remove the redefinition of `c` + = help: remove the redefinition of `a` error: aborting due to 14 previous errors From eab0a75ff942598a57c93f78000a8a9dbb3af9b7 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Wed, 4 Oct 2023 01:04:00 +0200 Subject: [PATCH 38/48] Update version attribute for 1.73 lints --- clippy_lints/src/error_impl_error.rs | 2 +- clippy_lints/src/four_forward_slashes.rs | 2 +- clippy_lints/src/manual_float_methods.rs | 4 ++-- clippy_lints/src/matches/mod.rs | 2 +- clippy_lints/src/methods/mod.rs | 12 ++++++------ clippy_lints/src/needless_pass_by_ref_mut.rs | 2 +- clippy_lints/src/non_canonical_impls.rs | 2 +- clippy_lints/src/operators/mod.rs | 4 ++-- clippy_lints/src/redundant_locals.rs | 2 +- 9 files changed, 16 insertions(+), 16 deletions(-) diff --git a/clippy_lints/src/error_impl_error.rs b/clippy_lints/src/error_impl_error.rs index f24577c738229..6d429fbd03538 100644 --- a/clippy_lints/src/error_impl_error.rs +++ b/clippy_lints/src/error_impl_error.rs @@ -27,7 +27,7 @@ declare_clippy_lint! { /// /// impl std::error::Error for Error { ... } /// ``` - #[clippy::version = "1.72.0"] + #[clippy::version = "1.73.0"] pub ERROR_IMPL_ERROR, restriction, "exported types named `Error` that implement `Error`" diff --git a/clippy_lints/src/four_forward_slashes.rs b/clippy_lints/src/four_forward_slashes.rs index 419c77343441c..0ec52f89e7166 100644 --- a/clippy_lints/src/four_forward_slashes.rs +++ b/clippy_lints/src/four_forward_slashes.rs @@ -28,7 +28,7 @@ declare_clippy_lint! { /// // ... /// } /// ``` - #[clippy::version = "1.72.0"] + #[clippy::version = "1.73.0"] pub FOUR_FORWARD_SLASHES, suspicious, "comments with 4 forward slashes (`////`) likely intended to be doc comments (`///`)" diff --git a/clippy_lints/src/manual_float_methods.rs b/clippy_lints/src/manual_float_methods.rs index 88db7ae6aece0..ed9189a180495 100644 --- a/clippy_lints/src/manual_float_methods.rs +++ b/clippy_lints/src/manual_float_methods.rs @@ -26,7 +26,7 @@ declare_clippy_lint! { /// # let x = 1.0f32; /// if x.is_infinite() {} /// ``` - #[clippy::version = "1.72.0"] + #[clippy::version = "1.73.0"] pub MANUAL_IS_INFINITE, style, "use dedicated method to check if a float is infinite" @@ -51,7 +51,7 @@ declare_clippy_lint! { /// if x.is_finite() {} /// if x.is_finite() {} /// ``` - #[clippy::version = "1.72.0"] + #[clippy::version = "1.73.0"] pub MANUAL_IS_FINITE, style, "use dedicated method to check if a float is finite" diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index 930386a60aa02..a23000e5fe15c 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -961,7 +961,7 @@ declare_clippy_lint! { /// _ => todo!(), /// } /// ``` - #[clippy::version = "1.72.0"] + #[clippy::version = "1.73.0"] pub REDUNDANT_GUARDS, complexity, "checks for unnecessary guards in match expressions" diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 7b743382fa158..a935aea5075d7 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2970,7 +2970,7 @@ declare_clippy_lint! { /// assert_eq!((*any_box).type_id(), TypeId::of::()); /// // ^ dereference first, to call `type_id` on `dyn Any` /// ``` - #[clippy::version = "1.72.0"] + #[clippy::version = "1.73.0"] pub TYPE_ID_ON_BOX, suspicious, "calling `.type_id()` on `Box`" @@ -3391,7 +3391,7 @@ declare_clippy_lint! { /// // ^^^^^^^^^^^ remove the trailing newline /// assert_eq!(num, 42); /// ``` - #[clippy::version = "1.72.0"] + #[clippy::version = "1.73.0"] pub READ_LINE_WITHOUT_TRIM, correctness, "calling `Stdin::read_line`, then trying to parse it without first trimming" @@ -3419,7 +3419,7 @@ declare_clippy_lint! { /// # let c = 'c'; /// matches!(c, '\\' | '.' | '+' | '*' | '(' | ')' | '|' | '[' | ']' | '{' | '}' | '^' | '$' | '#' | '&' | '-' | '~'); /// ``` - #[clippy::version = "1.72.0"] + #[clippy::version = "1.73.0"] pub STRING_LIT_CHARS_ANY, restriction, "checks for `.chars().any(|i| i == c)`" @@ -3454,7 +3454,7 @@ declare_clippy_lint! { /// }) /// } /// ``` - #[clippy::version = "1.72.0"] + #[clippy::version = "1.73.0"] pub FORMAT_COLLECT, perf, "`format!`ing every element in a collection, then collecting the strings into a new `String`" @@ -3475,7 +3475,7 @@ declare_clippy_lint! { /// let y = v.iter().collect::>(); /// assert_eq!(x, y); /// ``` - #[clippy::version = "1.72.0"] + #[clippy::version = "1.73.0"] pub ITER_SKIP_ZERO, correctness, "disallows `.skip(0)`" @@ -3506,7 +3506,7 @@ declare_clippy_lint! { /// # let v = vec![]; /// _ = v.into_iter().filter(|i| i % 2 == 0).map(|i| really_expensive_fn(i)); /// ``` - #[clippy::version = "1.72.0"] + #[clippy::version = "1.73.0"] pub FILTER_MAP_BOOL_THEN, style, "checks for usage of `bool::then` in `Iterator::filter_map`" diff --git a/clippy_lints/src/needless_pass_by_ref_mut.rs b/clippy_lints/src/needless_pass_by_ref_mut.rs index 3ad9ae0301a68..212d6234bdb3c 100644 --- a/clippy_lints/src/needless_pass_by_ref_mut.rs +++ b/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -47,7 +47,7 @@ declare_clippy_lint! { /// 12 + *y /// } /// ``` - #[clippy::version = "1.72.0"] + #[clippy::version = "1.73.0"] pub NEEDLESS_PASS_BY_REF_MUT, nursery, "using a `&mut` argument when it's not mutated" diff --git a/clippy_lints/src/non_canonical_impls.rs b/clippy_lints/src/non_canonical_impls.rs index 20b4b4f03ed47..f159c261d11fb 100644 --- a/clippy_lints/src/non_canonical_impls.rs +++ b/clippy_lints/src/non_canonical_impls.rs @@ -103,7 +103,7 @@ declare_clippy_lint! { /// } /// } /// ``` - #[clippy::version = "1.72.0"] + #[clippy::version = "1.73.0"] pub NON_CANONICAL_PARTIAL_ORD_IMPL, suspicious, "non-canonical implementation of `PartialOrd` on an `Ord` type" diff --git a/clippy_lints/src/operators/mod.rs b/clippy_lints/src/operators/mod.rs index 4635e1164cd31..6b247cf5f6ae1 100644 --- a/clippy_lints/src/operators/mod.rs +++ b/clippy_lints/src/operators/mod.rs @@ -312,7 +312,7 @@ declare_clippy_lint! { /// # let status_code = 200; /// if status_code <= 400 && status_code > 500 {} /// ``` - #[clippy::version = "1.71.0"] + #[clippy::version = "1.73.0"] pub IMPOSSIBLE_COMPARISONS, correctness, "double comparisons that will never evaluate to `true`" @@ -332,7 +332,7 @@ declare_clippy_lint! { /// # let status_code = 200; /// if status_code <= 400 && status_code < 500 {} /// ``` - #[clippy::version = "1.71.0"] + #[clippy::version = "1.73.0"] pub REDUNDANT_COMPARISONS, correctness, "double comparisons where one of them can be removed" diff --git a/clippy_lints/src/redundant_locals.rs b/clippy_lints/src/redundant_locals.rs index a26bb98c5e23e..7864ff3e8ff63 100644 --- a/clippy_lints/src/redundant_locals.rs +++ b/clippy_lints/src/redundant_locals.rs @@ -37,7 +37,7 @@ declare_clippy_lint! { /// // no redefinition with the same name /// } /// ``` - #[clippy::version = "1.72.0"] + #[clippy::version = "1.73.0"] pub REDUNDANT_LOCALS, correctness, "redundant redefinition of a local binding" From 8d920a8b0336a59dcb91dda88a309b244bc5fb02 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Wed, 4 Oct 2023 00:57:12 +0200 Subject: [PATCH 39/48] Changelog for Rust 1.73 :pen: --- CHANGELOG.md | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 92 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 25230e46e8893..108b7f32d3499 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,11 +6,101 @@ document. ## Unreleased / Beta / In Rust Nightly -[37f4c172...master](https://github.com/rust-lang/rust-clippy/compare/37f4c172...master) +[1e8fdf49...master](https://github.com/rust-lang/rust-clippy/compare/1e8fdf49...master) + +## Rust 1.73 + +Current stable, released 2023-10-05 + +[View all 103 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2023-07-02T12%3A24%3A40Z..2023-08-11T11%3A09%3A56Z+base%3Amaster) + +### New Lints + +* [`impossible_comparisons`] + [#10843](https://github.com/rust-lang/rust-clippy/pull/10843) +* [`redundant_comparisons`] + [#10843](https://github.com/rust-lang/rust-clippy/pull/10843) +* [`ignored_unit_patterns`] + [#11242](https://github.com/rust-lang/rust-clippy/pull/11242) +* [`readonly_write_lock`] + [#11210](https://github.com/rust-lang/rust-clippy/pull/11210) +* [`filter_map_bool_then`] + [#11115](https://github.com/rust-lang/rust-clippy/pull/11115) +* [`needless_return_with_question_mark`] + [#11031](https://github.com/rust-lang/rust-clippy/pull/11031) +* [`redundant_guards`] + [#10955](https://github.com/rust-lang/rust-clippy/pull/10955) +* [`redundant_local`] + [#10885](https://github.com/rust-lang/rust-clippy/pull/10885) +* [`absolute_paths`] + [#11003](https://github.com/rust-lang/rust-clippy/pull/11003) +* [`error_impl_error`] + [#11107](https://github.com/rust-lang/rust-clippy/pull/11107) +* [`iter_skip_zero`] + [#11046](https://github.com/rust-lang/rust-clippy/pull/11046) +* [`string_lit_chars_any`] + [#11052](https://github.com/rust-lang/rust-clippy/pull/11052) +* [`four_forward_slashes`] + [#11140](https://github.com/rust-lang/rust-clippy/pull/11140) +* [`format_collect`] + [#11116](https://github.com/rust-lang/rust-clippy/pull/11116) +* [`needless_pass_by_ref_mut`] + [#10900](https://github.com/rust-lang/rust-clippy/pull/10900) +* [`manual_is_infinite`] + [#11049](https://github.com/rust-lang/rust-clippy/pull/11049) +* [`manual_is_finite`] + [#11049](https://github.com/rust-lang/rust-clippy/pull/11049) +* [`non_canonical_partial_ord_impl`] + [#10788](https://github.com/rust-lang/rust-clippy/pull/10788) +* [`read_line_without_trim`] + [#10970](https://github.com/rust-lang/rust-clippy/pull/10970) +* [`type_id_on_box`] + [#10987](https://github.com/rust-lang/rust-clippy/pull/10987) + +### Moves and Deprecations + +* Renamed `unwrap_or_else_default` to [`unwrap_or_default`] + [#10120](https://github.com/rust-lang/rust-clippy/pull/10120) +* Moved [`tuple_array_conversions`] to `pedantic` (Now allow-by-default) + [#11171](https://github.com/rust-lang/rust-clippy/pull/11171) +* Moved [`arc_with_non_send_sync`] to `complexity` (Now warn-by-default) + [#11104](https://github.com/rust-lang/rust-clippy/pull/11104) +* Moved [`needless_raw_string_hashes`] to `pedantic` (Now allow-by-default) + [#11415](https://github.com/rust-lang/rust-clippy/pull/11415) + +### Enhancements + +* [`unwrap_used`]: No longer lints on the never-type or never-like enums + [#11252](https://github.com/rust-lang/rust-clippy/pull/11252) +* [`expect_used`]: No longer lints on the never-type or never-like enums + [#11252](https://github.com/rust-lang/rust-clippy/pull/11252) + +### False Positive Fixes + +* [`panic_in_result_fn`]: No longer triggers on `todo!`, `unimplemented!`, `unreachable!` + [#11123](https://github.com/rust-lang/rust-clippy/pull/11123) + +### Suggestion Fixes/Improvements + +* [`semicolon_if_nothing_returned`]: The suggestion is now machine-applicable with rustfix + [#11083](https://github.com/rust-lang/rust-clippy/pull/11083) + +### ICE Fixes + +* [`filter_map_bool_then`]: No longer crashes on late-bound regions + [#11318](https://github.com/rust-lang/rust-clippy/pull/11318) +* [`unwrap_or_default`]: No longer crashes on alias types for local items + [#11258](https://github.com/rust-lang/rust-clippy/pull/11258) +* [`unnecessary_literal_unwrap`]: No longer crashes on `None.unwrap_or_default()` + [#11106](https://github.com/rust-lang/rust-clippy/pull/11106) +* Fixed MIR-related ICE + [#11130](https://github.com/rust-lang/rust-clippy/pull/11130) +* [`missing_fields_in_debug`]: No longer crashes on non-ADT self types + [#11069](https://github.com/rust-lang/rust-clippy/pull/11069) ## Rust 1.72 -Current stable, released 2023-08-24 +Released 2023-08-24 [View all 131 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2023-05-22T14%3A53%3A59Z..2023-07-01T22%3A57%3A20Z+base%3Amaster) From 948355586fe325acb97be312e9f3c7ef8f13bba7 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Wed, 4 Oct 2023 01:19:38 +0200 Subject: [PATCH 40/48] sudo CI=green --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 108b7f32d3499..63930d5a70506 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,7 +30,7 @@ Current stable, released 2023-10-05 [#11031](https://github.com/rust-lang/rust-clippy/pull/11031) * [`redundant_guards`] [#10955](https://github.com/rust-lang/rust-clippy/pull/10955) -* [`redundant_local`] +* [`redundant_locals`] [#10885](https://github.com/rust-lang/rust-clippy/pull/10885) * [`absolute_paths`] [#11003](https://github.com/rust-lang/rust-clippy/pull/11003) From 404217e631270d9a167cf63cbb3ea50cbe370ebc Mon Sep 17 00:00:00 2001 From: xFrednet Date: Wed, 4 Oct 2023 10:28:52 +0200 Subject: [PATCH 41/48] Address PR Review <3 --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 63930d5a70506..fef25ad8635ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,7 +50,7 @@ Current stable, released 2023-10-05 [#11049](https://github.com/rust-lang/rust-clippy/pull/11049) * [`manual_is_finite`] [#11049](https://github.com/rust-lang/rust-clippy/pull/11049) -* [`non_canonical_partial_ord_impl`] +* [`incorrect_partial_ord_impl_on_ord_type`] [#10788](https://github.com/rust-lang/rust-clippy/pull/10788) * [`read_line_without_trim`] [#10970](https://github.com/rust-lang/rust-clippy/pull/10970) @@ -62,8 +62,8 @@ Current stable, released 2023-10-05 * Renamed `unwrap_or_else_default` to [`unwrap_or_default`] [#10120](https://github.com/rust-lang/rust-clippy/pull/10120) * Moved [`tuple_array_conversions`] to `pedantic` (Now allow-by-default) - [#11171](https://github.com/rust-lang/rust-clippy/pull/11171) -* Moved [`arc_with_non_send_sync`] to `complexity` (Now warn-by-default) + [#11146](https://github.com/rust-lang/rust-clippy/pull/11146) +* Moved [`arc_with_non_send_sync`] to `suspicious` (Now warn-by-default) [#11104](https://github.com/rust-lang/rust-clippy/pull/11104) * Moved [`needless_raw_string_hashes`] to `pedantic` (Now allow-by-default) [#11415](https://github.com/rust-lang/rust-clippy/pull/11415) From 56794fa5f1a508ed0cf1b3f44bb152b3c0c641f1 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 2 Oct 2023 21:24:23 +0000 Subject: [PATCH 42/48] Fix clippy --- clippy_lints/src/methods/unnecessary_literal_unwrap.rs | 8 ++------ .../ui-toml/too_many_arguments/too_many_arguments.stderr | 2 +- tests/ui/crashes/ice-6251.stderr | 4 ++-- tests/ui/functions.stderr | 6 +++--- tests/ui/must_use_unit.stderr | 4 ++-- tests/ui/unnecessary_literal_unwrap.fixed | 4 ++-- tests/ui/unnecessary_literal_unwrap.stderr | 4 ++-- 7 files changed, 14 insertions(+), 18 deletions(-) diff --git a/clippy_lints/src/methods/unnecessary_literal_unwrap.rs b/clippy_lints/src/methods/unnecessary_literal_unwrap.rs index 937aac8d25ef0..24c0ea3f60a99 100644 --- a/clippy_lints/src/methods/unnecessary_literal_unwrap.rs +++ b/clippy_lints/src/methods/unnecessary_literal_unwrap.rs @@ -102,14 +102,10 @@ pub(super) fn check( ]), ("None", "unwrap_or_else", _) => match args[0].kind { hir::ExprKind::Closure(hir::Closure { - fn_decl: - hir::FnDecl { - output: hir::FnRetTy::DefaultReturn(span) | hir::FnRetTy::Return(hir::Ty { span, .. }), - .. - }, + body, .. }) => Some(vec![ - (expr.span.with_hi(span.hi()), String::new()), + (expr.span.with_hi(cx.tcx.hir().body(*body).value.span.lo()), String::new()), (expr.span.with_lo(args[0].span.hi()), String::new()), ]), _ => None, diff --git a/tests/ui-toml/too_many_arguments/too_many_arguments.stderr b/tests/ui-toml/too_many_arguments/too_many_arguments.stderr index a52e1fcb9e3a8..8b9d159b59c3a 100644 --- a/tests/ui-toml/too_many_arguments/too_many_arguments.stderr +++ b/tests/ui-toml/too_many_arguments/too_many_arguments.stderr @@ -2,7 +2,7 @@ error: this function has too many arguments (11/10) --> $DIR/too_many_arguments.rs:4:1 | LL | fn too_many(p1: u8, p2: u8, p3: u8, p4: u8, p5: u8, p6: u8, p7: u8, p8: u8, p9: u8, p10: u8, p11: u8) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::too-many-arguments` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::too_many_arguments)]` diff --git a/tests/ui/crashes/ice-6251.stderr b/tests/ui/crashes/ice-6251.stderr index 11081dc8087e2..aaebdabcbd5e3 100644 --- a/tests/ui/crashes/ice-6251.stderr +++ b/tests/ui/crashes/ice-6251.stderr @@ -12,10 +12,10 @@ LL | fn bug() -> impl Iterator { | + error[E0277]: the size for values of type `[u8]` cannot be known at compilation time - --> $DIR/ice-6251.rs:4:54 + --> $DIR/ice-6251.rs:4:53 | LL | fn bug() -> impl Iterator { - | ^ doesn't have a size known at compile-time + | ^ doesn't have a size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `[u8]` = note: the return type of a function must have a statically known size diff --git a/tests/ui/functions.stderr b/tests/ui/functions.stderr index 371ea1612601e..4b06cd0388990 100644 --- a/tests/ui/functions.stderr +++ b/tests/ui/functions.stderr @@ -2,7 +2,7 @@ error: this function has too many arguments (8/7) --> $DIR/functions.rs:8:1 | LL | fn bad(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool, _eight: ()) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::too-many-arguments` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::too_many_arguments)]` @@ -17,7 +17,7 @@ LL | | two: u32, ... | LL | | eight: () LL | | ) { - | |__^ + | |_^ error: this function has too many arguments (8/7) --> $DIR/functions.rs:48:5 @@ -29,7 +29,7 @@ error: this function has too many arguments (8/7) --> $DIR/functions.rs:58:5 | LL | fn bad_method(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool, _eight: ()) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this public function might dereference a raw pointer but is not marked `unsafe` --> $DIR/functions.rs:68:34 diff --git a/tests/ui/must_use_unit.stderr b/tests/ui/must_use_unit.stderr index e67d9b5b9d8d3..f2ee185857d3e 100644 --- a/tests/ui/must_use_unit.stderr +++ b/tests/ui/must_use_unit.stderr @@ -4,7 +4,7 @@ error: this unit-returning function has a `#[must_use]` attribute LL | #[must_use] | ----------- help: remove the attribute LL | pub fn must_use_default() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::must-use-unit` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::must_use_unit)]` @@ -23,7 +23,7 @@ error: this unit-returning function has a `#[must_use]` attribute LL | #[must_use = "With note"] | ------------------------- help: remove the attribute LL | pub fn must_use_with_note() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 3 previous errors diff --git a/tests/ui/unnecessary_literal_unwrap.fixed b/tests/ui/unnecessary_literal_unwrap.fixed index 87df1f8cb08fb..b17343aa9ba5a 100644 --- a/tests/ui/unnecessary_literal_unwrap.fixed +++ b/tests/ui/unnecessary_literal_unwrap.fixed @@ -23,7 +23,7 @@ fn unwrap_option_none() { let _val: u16 = 234; let _val: u16 = 234; let _val: u16 = { 234 }; - let _val: u16 = { 234 }; + let _val: u16 = { 234 }; panic!(); panic!("this always happens"); @@ -31,7 +31,7 @@ fn unwrap_option_none() { 234; 234; { 234 }; - { 234 }; + { 234 }; } fn unwrap_result_ok() { diff --git a/tests/ui/unnecessary_literal_unwrap.stderr b/tests/ui/unnecessary_literal_unwrap.stderr index 013907f59c46f..4940091be60ae 100644 --- a/tests/ui/unnecessary_literal_unwrap.stderr +++ b/tests/ui/unnecessary_literal_unwrap.stderr @@ -116,7 +116,7 @@ LL | let _val: u16 = None.unwrap_or_else(|| -> u16 { 234 }); help: remove the `None` and `unwrap_or_else()` | LL - let _val: u16 = None.unwrap_or_else(|| -> u16 { 234 }); -LL + let _val: u16 = { 234 }; +LL + let _val: u16 = { 234 }; | error: used `unwrap()` on `None` value @@ -187,7 +187,7 @@ LL | None::.unwrap_or_else(|| -> u16 { 234 }); help: remove the `None` and `unwrap_or_else()` | LL - None::.unwrap_or_else(|| -> u16 { 234 }); -LL + { 234 }; +LL + { 234 }; | error: used `unwrap()` on `Ok` value From c5c6d703de74e1965871c220d35ec40ce40b0b43 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 2 Oct 2023 21:39:07 +0000 Subject: [PATCH 43/48] Point to closure return instead of output if defaulted --- tests/ui/crashes/ice-6251.stderr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ui/crashes/ice-6251.stderr b/tests/ui/crashes/ice-6251.stderr index aaebdabcbd5e3..11081dc8087e2 100644 --- a/tests/ui/crashes/ice-6251.stderr +++ b/tests/ui/crashes/ice-6251.stderr @@ -12,10 +12,10 @@ LL | fn bug() -> impl Iterator { | + error[E0277]: the size for values of type `[u8]` cannot be known at compilation time - --> $DIR/ice-6251.rs:4:53 + --> $DIR/ice-6251.rs:4:54 | LL | fn bug() -> impl Iterator { - | ^ doesn't have a size known at compile-time + | ^ doesn't have a size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `[u8]` = note: the return type of a function must have a statically known size From dcc400191e444c8a57869f5004fdcbab807f7f09 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Wed, 4 Oct 2023 20:24:16 +0000 Subject: [PATCH 44/48] Fix `items_after_test_module` for non root modules, add applicable suggestion --- clippy_lints/src/items_after_test_module.rs | 108 +++++++++++------- clippy_utils/src/lib.rs | 21 ++-- .../after_proc_macros.rs | 11 ++ .../auxiliary/submodule.rs | 4 + .../block_module.stderr | 2 - .../items_after_test_module/in_submodule.rs | 8 ++ .../in_submodule.stderr | 14 +++ .../multiple_modules.rs | 11 ++ .../{block_module.rs => root_module.fixed} | 15 ++- .../ui/items_after_test_module/root_module.rs | 22 ++++ .../root_module.stderr | 20 ++++ 11 files changed, 177 insertions(+), 59 deletions(-) create mode 100644 tests/ui/items_after_test_module/after_proc_macros.rs create mode 100644 tests/ui/items_after_test_module/auxiliary/submodule.rs delete mode 100644 tests/ui/items_after_test_module/block_module.stderr create mode 100644 tests/ui/items_after_test_module/in_submodule.rs create mode 100644 tests/ui/items_after_test_module/in_submodule.stderr create mode 100644 tests/ui/items_after_test_module/multiple_modules.rs rename tests/ui/items_after_test_module/{block_module.rs => root_module.fixed} (86%) create mode 100644 tests/ui/items_after_test_module/root_module.rs create mode 100644 tests/ui/items_after_test_module/root_module.stderr diff --git a/clippy_lints/src/items_after_test_module.rs b/clippy_lints/src/items_after_test_module.rs index 55a43e9156282..41477242bcc0b 100644 --- a/clippy_lints/src/items_after_test_module.rs +++ b/clippy_lints/src/items_after_test_module.rs @@ -1,10 +1,12 @@ -use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::{is_from_proc_macro, is_in_cfg_test}; -use rustc_hir::{HirId, ItemId, ItemKind, Mod}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::lint::in_external_macro; +use clippy_utils::diagnostics::span_lint_hir_and_then; +use clippy_utils::source::snippet_opt; +use clippy_utils::{fulfill_or_allowed, is_cfg_test, is_from_proc_macro}; +use rustc_errors::{Applicability, SuggestionStyle}; +use rustc_hir::{HirId, Item, ItemKind, Mod}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::{sym, Span}; +use rustc_span::hygiene::AstPass; +use rustc_span::{sym, ExpnKind}; declare_clippy_lint! { /// ### What it does @@ -41,46 +43,72 @@ declare_clippy_lint! { declare_lint_pass!(ItemsAfterTestModule => [ITEMS_AFTER_TEST_MODULE]); -impl LateLintPass<'_> for ItemsAfterTestModule { - fn check_mod(&mut self, cx: &LateContext<'_>, _: &Mod<'_>, _: HirId) { - let mut was_test_mod_visited = false; - let mut test_mod_span: Option = None; +fn cfg_test_module<'tcx>(cx: &LateContext<'tcx>, item: &Item<'tcx>) -> bool { + if let ItemKind::Mod(test_mod) = item.kind + && item.span.hi() == test_mod.spans.inner_span.hi() + && is_cfg_test(cx.tcx, item.hir_id()) + && !item.span.from_expansion() + && !is_from_proc_macro(cx, item) + { + true + } else { + false + } +} - let hir = cx.tcx.hir(); - let items = hir.items().collect::>(); +impl LateLintPass<'_> for ItemsAfterTestModule { + fn check_mod(&mut self, cx: &LateContext<'_>, module: &Mod<'_>, _: HirId) { + let mut items = module.item_ids.iter().map(|&id| cx.tcx.hir().item(id)); - for (i, itid) in items.iter().enumerate() { - let item = hir.item(*itid); + let Some((mod_pos, test_mod)) = items.by_ref().enumerate().find(|(_, item)| cfg_test_module(cx, item)) else { + return; + }; - if_chain! { - if was_test_mod_visited; - if i == (items.len() - 3 /* Weird magic number (HIR-translation behaviour) */); - if cx.sess().source_map().lookup_char_pos(item.span.lo()).file.name_hash - == cx.sess().source_map().lookup_char_pos(test_mod_span.unwrap().lo()).file.name_hash; // Will never fail - if !matches!(item.kind, ItemKind::Mod(_)); - if !is_in_cfg_test(cx.tcx, itid.hir_id()); // The item isn't in the testing module itself - if !in_external_macro(cx.sess(), item.span); - if !is_from_proc_macro(cx, item); + let after: Vec<_> = items + .filter(|item| { + // Ignore the generated test main function + !(item.ident.name == sym::main + && item.span.ctxt().outer_expn_data().kind == ExpnKind::AstPass(AstPass::TestHarness)) + }) + .collect(); - then { - span_lint_and_help(cx, ITEMS_AFTER_TEST_MODULE, test_mod_span.unwrap().with_hi(item.span.hi()), "items were found after the testing module", None, "move the items to before the testing module was defined"); - }}; + if let Some(last) = after.last() + && after.iter().all(|&item| { + !matches!(item.kind, ItemKind::Mod(_)) + && !item.span.from_expansion() + && !is_from_proc_macro(cx, item) + }) + && !fulfill_or_allowed(cx, ITEMS_AFTER_TEST_MODULE, after.iter().map(|item| item.hir_id())) + { + let def_spans: Vec<_> = std::iter::once(test_mod.owner_id) + .chain(after.iter().map(|item| item.owner_id)) + .map(|id| cx.tcx.def_span(id)) + .collect(); - if let ItemKind::Mod(module) = item.kind && item.span.hi() == module.spans.inner_span.hi() { - // Check that it works the same way, the only I way I've found for #10713 - for attr in cx.tcx.get_attrs(item.owner_id.to_def_id(), sym::cfg) { - if_chain! { - if attr.has_name(sym::cfg); - if let Some(mitems) = attr.meta_item_list(); - if let [mitem] = &*mitems; - if mitem.has_name(sym::test); - then { - was_test_mod_visited = true; - test_mod_span = Some(item.span); - } + span_lint_hir_and_then( + cx, + ITEMS_AFTER_TEST_MODULE, + test_mod.hir_id(), + def_spans, + "items after a test module", + |diag| { + if let Some(prev) = mod_pos.checked_sub(1) + && let prev = cx.tcx.hir().item(module.item_ids[prev]) + && let items_span = last.span.with_lo(test_mod.span.hi()) + && let Some(items) = snippet_opt(cx, items_span) + { + diag.multipart_suggestion_with_style( + "move the items to before the test module was defined", + vec![ + (prev.span.shrink_to_hi(), items), + (items_span, String::new()) + ], + Applicability::MachineApplicable, + SuggestionStyle::HideCodeAlways, + ); } - } - } + }, + ); } } } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 350b231e90367..6711d007388d0 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -80,7 +80,6 @@ use std::sync::{Mutex, MutexGuard, OnceLock}; use if_chain::if_chain; use itertools::Itertools; use rustc_ast::ast::{self, LitKind, RangeLimits}; -use rustc_ast::Attribute; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::unhash::UnhashMap; use rustc_hir::def::{DefKind, Res}; @@ -2452,11 +2451,12 @@ pub fn is_in_test_function(tcx: TyCtxt<'_>, id: hir::HirId) -> bool { }) } -/// Checks if the item containing the given `HirId` has `#[cfg(test)]` attribute applied +/// Checks if `id` has a `#[cfg(test)]` attribute applied /// -/// Note: Add `//@compile-flags: --test` to UI tests with a `#[cfg(test)]` function -pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: hir::HirId) -> bool { - fn is_cfg_test(attr: &Attribute) -> bool { +/// This only checks directly applied attributes, to see if a node is inside a `#[cfg(test)]` parent +/// use [`is_in_cfg_test`] +pub fn is_cfg_test(tcx: TyCtxt<'_>, id: hir::HirId) -> bool { + tcx.hir().attrs(id).iter().any(|attr| { if attr.has_name(sym::cfg) && let Some(items) = attr.meta_item_list() && let [item] = &*items @@ -2466,11 +2466,14 @@ pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: hir::HirId) -> bool { } else { false } - } + }) +} + +/// Checks if any parent node of `HirId` has `#[cfg(test)]` attribute applied +pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: hir::HirId) -> bool { tcx.hir() - .parent_iter(id) - .flat_map(|(parent_id, _)| tcx.hir().attrs(parent_id)) - .any(is_cfg_test) + .parent_id_iter(id) + .any(|parent_id| is_cfg_test(tcx, parent_id)) } /// Checks if the item of any of its parents has `#[cfg(...)]` attribute applied. diff --git a/tests/ui/items_after_test_module/after_proc_macros.rs b/tests/ui/items_after_test_module/after_proc_macros.rs new file mode 100644 index 0000000000000..d9c0aef88c8cf --- /dev/null +++ b/tests/ui/items_after_test_module/after_proc_macros.rs @@ -0,0 +1,11 @@ +//@aux-build:../auxiliary/proc_macros.rs +extern crate proc_macros; + +proc_macros::with_span! { + span + #[cfg(test)] + mod tests {} +} + +#[test] +fn f() {} diff --git a/tests/ui/items_after_test_module/auxiliary/submodule.rs b/tests/ui/items_after_test_module/auxiliary/submodule.rs new file mode 100644 index 0000000000000..69d61790121c0 --- /dev/null +++ b/tests/ui/items_after_test_module/auxiliary/submodule.rs @@ -0,0 +1,4 @@ +#[cfg(test)] +mod tests {} + +fn in_submodule() {} diff --git a/tests/ui/items_after_test_module/block_module.stderr b/tests/ui/items_after_test_module/block_module.stderr deleted file mode 100644 index 1b6257471618c..0000000000000 --- a/tests/ui/items_after_test_module/block_module.stderr +++ /dev/null @@ -1,2 +0,0 @@ -error: Option 'test' given more than once - diff --git a/tests/ui/items_after_test_module/in_submodule.rs b/tests/ui/items_after_test_module/in_submodule.rs new file mode 100644 index 0000000000000..7132e71764eb0 --- /dev/null +++ b/tests/ui/items_after_test_module/in_submodule.rs @@ -0,0 +1,8 @@ +#[path = "auxiliary/submodule.rs"] +mod submodule; + +#[cfg(test)] +mod tests { + #[test] + fn t() {} +} diff --git a/tests/ui/items_after_test_module/in_submodule.stderr b/tests/ui/items_after_test_module/in_submodule.stderr new file mode 100644 index 0000000000000..4e99876365cf8 --- /dev/null +++ b/tests/ui/items_after_test_module/in_submodule.stderr @@ -0,0 +1,14 @@ +error: items after a test module + --> $DIR/auxiliary/submodule.rs:2:1 + | +LL | mod tests {} + | ^^^^^^^^^ +LL | +LL | fn in_submodule() {} + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::items-after-test-module` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::items_after_test_module)]` + +error: aborting due to previous error + diff --git a/tests/ui/items_after_test_module/multiple_modules.rs b/tests/ui/items_after_test_module/multiple_modules.rs new file mode 100644 index 0000000000000..8ab9e8200f18a --- /dev/null +++ b/tests/ui/items_after_test_module/multiple_modules.rs @@ -0,0 +1,11 @@ +#[cfg(test)] +mod tests { + #[test] + fn f() {} +} + +#[cfg(test)] +mod more_tests { + #[test] + fn g() {} +} diff --git a/tests/ui/items_after_test_module/block_module.rs b/tests/ui/items_after_test_module/root_module.fixed similarity index 86% rename from tests/ui/items_after_test_module/block_module.rs rename to tests/ui/items_after_test_module/root_module.fixed index 5136b2557ec1a..d444100a76b92 100644 --- a/tests/ui/items_after_test_module/block_module.rs +++ b/tests/ui/items_after_test_module/root_module.fixed @@ -1,4 +1,3 @@ -//@compile-flags: --test #![allow(unused)] #![warn(clippy::items_after_test_module)] @@ -6,6 +5,13 @@ fn main() {} fn should_not_lint() {} +fn should_lint() {} + +const SHOULD_ALSO_LINT: usize = 1; +macro_rules! should_lint { + () => {}; +} + #[allow(dead_code)] #[allow(unused)] // Some attributes to check that span replacement is good enough #[allow(clippy::allow_attributes)] @@ -14,10 +20,3 @@ mod tests { #[test] fn hi() {} } - -fn should_lint() {} - -const SHOULD_ALSO_LINT: usize = 1; -macro_rules! should_not_lint { - () => {}; -} diff --git a/tests/ui/items_after_test_module/root_module.rs b/tests/ui/items_after_test_module/root_module.rs new file mode 100644 index 0000000000000..57da01639cca6 --- /dev/null +++ b/tests/ui/items_after_test_module/root_module.rs @@ -0,0 +1,22 @@ +#![allow(unused)] +#![warn(clippy::items_after_test_module)] + +fn main() {} + +fn should_not_lint() {} + +#[allow(dead_code)] +#[allow(unused)] // Some attributes to check that span replacement is good enough +#[allow(clippy::allow_attributes)] +#[cfg(test)] +mod tests { + #[test] + fn hi() {} +} + +fn should_lint() {} + +const SHOULD_ALSO_LINT: usize = 1; +macro_rules! should_lint { + () => {}; +} diff --git a/tests/ui/items_after_test_module/root_module.stderr b/tests/ui/items_after_test_module/root_module.stderr new file mode 100644 index 0000000000000..67bc82ebff91f --- /dev/null +++ b/tests/ui/items_after_test_module/root_module.stderr @@ -0,0 +1,20 @@ +error: items after a test module + --> $DIR/root_module.rs:12:1 + | +LL | mod tests { + | ^^^^^^^^^ +... +LL | fn should_lint() {} + | ^^^^^^^^^^^^^^^^ +LL | +LL | const SHOULD_ALSO_LINT: usize = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | macro_rules! should_lint { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::items-after-test-module` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::items_after_test_module)]` + = help: move the items to before the test module was defined + +error: aborting due to previous error + From 48d2770e527dd21fd8ae71178d360abd005b6ddc Mon Sep 17 00:00:00 2001 From: koka Date: Fri, 6 Oct 2023 22:18:11 +0900 Subject: [PATCH 45/48] Improve `redundant_locals` help message --- clippy_lints/src/redundant_locals.rs | 8 +- tests/ui/redundant_locals.stderr | 140 ++++++++++++++++----------- 2 files changed, 86 insertions(+), 62 deletions(-) diff --git a/clippy_lints/src/redundant_locals.rs b/clippy_lints/src/redundant_locals.rs index 7864ff3e8ff63..197742b5dd41d 100644 --- a/clippy_lints/src/redundant_locals.rs +++ b/clippy_lints/src/redundant_locals.rs @@ -74,10 +74,10 @@ impl<'tcx> LateLintPass<'tcx> for RedundantLocals { span_lint_and_help( cx, REDUNDANT_LOCALS, - vec![binding_pat.span, local.span], - "redundant redefinition of a binding", - None, - &format!("remove the redefinition of `{ident}`"), + local.span, + &format!("redundant redefinition of a binding `{ident}`"), + Some(binding_pat.span), + &format!("`{ident}` is initially defined here"), ); } } diff --git a/tests/ui/redundant_locals.stderr b/tests/ui/redundant_locals.stderr index 6e9da8fccbdc1..d794a87fe7df6 100644 --- a/tests/ui/redundant_locals.stderr +++ b/tests/ui/redundant_locals.stderr @@ -1,148 +1,172 @@ -error: redundant redefinition of a binding - --> $DIR/redundant_locals.rs:11:9 +error: redundant redefinition of a binding `x` + --> $DIR/redundant_locals.rs:12:5 | -LL | let x = 1; - | ^ LL | let x = x; | ^^^^^^^^^^ | - = help: remove the redefinition of `x` +help: `x` is initially defined here + --> $DIR/redundant_locals.rs:11:9 + | +LL | let x = 1; + | ^ = note: `-D clippy::redundant-locals` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::redundant_locals)]` -error: redundant redefinition of a binding - --> $DIR/redundant_locals.rs:16:9 +error: redundant redefinition of a binding `x` + --> $DIR/redundant_locals.rs:17:5 | -LL | let mut x = 1; - | ^^^^^ LL | let mut x = x; | ^^^^^^^^^^^^^^ | - = help: remove the redefinition of `x` +help: `x` is initially defined here + --> $DIR/redundant_locals.rs:16:9 + | +LL | let mut x = 1; + | ^^^^^ -error: redundant redefinition of a binding +error: redundant redefinition of a binding `x` + --> $DIR/redundant_locals.rs:47:5 + | +LL | let x = x; + | ^^^^^^^^^^ + | +help: `x` is initially defined here --> $DIR/redundant_locals.rs:46:14 | LL | fn parameter(x: i32) { | ^ + +error: redundant redefinition of a binding `x` + --> $DIR/redundant_locals.rs:52:5 + | LL | let x = x; | ^^^^^^^^^^ | - = help: remove the redefinition of `x` - -error: redundant redefinition of a binding +help: `x` is initially defined here --> $DIR/redundant_locals.rs:51:9 | LL | let x = 1; | ^ + +error: redundant redefinition of a binding `x` + --> $DIR/redundant_locals.rs:53:5 + | LL | let x = x; | ^^^^^^^^^^ | - = help: remove the redefinition of `x` - -error: redundant redefinition of a binding +help: `x` is initially defined here --> $DIR/redundant_locals.rs:52:9 | LL | let x = x; | ^ + +error: redundant redefinition of a binding `x` + --> $DIR/redundant_locals.rs:54:5 + | LL | let x = x; | ^^^^^^^^^^ | - = help: remove the redefinition of `x` - -error: redundant redefinition of a binding +help: `x` is initially defined here --> $DIR/redundant_locals.rs:53:9 | LL | let x = x; | ^ + +error: redundant redefinition of a binding `x` + --> $DIR/redundant_locals.rs:55:5 + | LL | let x = x; | ^^^^^^^^^^ | - = help: remove the redefinition of `x` - -error: redundant redefinition of a binding +help: `x` is initially defined here --> $DIR/redundant_locals.rs:54:9 | LL | let x = x; | ^ -LL | let x = x; + +error: redundant redefinition of a binding `a` + --> $DIR/redundant_locals.rs:61:5 + | +LL | let a = a; | ^^^^^^^^^^ | - = help: remove the redefinition of `x` - -error: redundant redefinition of a binding +help: `a` is initially defined here --> $DIR/redundant_locals.rs:59:9 | LL | let a = 1; | ^ -LL | let b = 2; -LL | let a = a; + +error: redundant redefinition of a binding `b` + --> $DIR/redundant_locals.rs:62:5 + | +LL | let b = b; | ^^^^^^^^^^ | - = help: remove the redefinition of `a` - -error: redundant redefinition of a binding +help: `b` is initially defined here --> $DIR/redundant_locals.rs:60:9 | LL | let b = 2; | ^ -LL | let a = a; -LL | let b = b; - | ^^^^^^^^^^ - | - = help: remove the redefinition of `b` -error: redundant redefinition of a binding +error: redundant redefinition of a binding `x` + --> $DIR/redundant_locals.rs:68:9 + | +LL | let x = x; + | ^^^^^^^^^^ + | +help: `x` is initially defined here --> $DIR/redundant_locals.rs:67:13 | LL | let x = 1; | ^ + +error: redundant redefinition of a binding `x` + --> $DIR/redundant_locals.rs:75:9 + | LL | let x = x; | ^^^^^^^^^^ | - = help: remove the redefinition of `x` - -error: redundant redefinition of a binding +help: `x` is initially defined here --> $DIR/redundant_locals.rs:74:13 | LL | let x = 1; | ^ + +error: redundant redefinition of a binding `x` + --> $DIR/redundant_locals.rs:78:9 + | LL | let x = x; | ^^^^^^^^^^ | - = help: remove the redefinition of `x` - -error: redundant redefinition of a binding +help: `x` is initially defined here --> $DIR/redundant_locals.rs:77:6 | LL | |x: i32| { | ^ + +error: redundant redefinition of a binding `x` + --> $DIR/redundant_locals.rs:97:9 + | LL | let x = x; | ^^^^^^^^^^ | - = help: remove the redefinition of `x` - -error: redundant redefinition of a binding +help: `x` is initially defined here --> $DIR/redundant_locals.rs:94:9 | LL | let x = 1; | ^ -... -LL | let x = x; - | ^^^^^^^^^^ - | - = help: remove the redefinition of `x` -error: redundant redefinition of a binding - --> $DIR/redundant_locals.rs:142:9 +error: redundant redefinition of a binding `a` + --> $DIR/redundant_locals.rs:144:5 | -LL | let a = WithoutDrop(1); - | ^ -LL | let b = WithoutDrop(2); LL | let a = a; | ^^^^^^^^^^ | - = help: remove the redefinition of `a` +help: `a` is initially defined here + --> $DIR/redundant_locals.rs:142:9 + | +LL | let a = WithoutDrop(1); + | ^ error: aborting due to 14 previous errors From 50754da9fa2f956eed5af672ed3c7820d774ce91 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Fri, 6 Oct 2023 17:32:32 +0200 Subject: [PATCH 46/48] Bump nightly version -> 2023-10-06 --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index 5ce22b65f0074..fe2c77ab47f00 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2023-09-25" +channel = "nightly-2023-10-06" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] From b8677e54d4eb2181f539864d3594a0dd62f2f0d9 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Fri, 6 Oct 2023 17:32:44 +0200 Subject: [PATCH 47/48] Bump Clippy version -> 0.1.75 --- Cargo.toml | 2 +- clippy_lints/Cargo.toml | 2 +- clippy_utils/Cargo.toml | 2 +- declare_clippy_lint/Cargo.toml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1eacc82d560ae..cbcb42dad79b2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.74" +version = "0.1.75" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 834753a230196..4d5b3bf8a948d 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_lints" -version = "0.1.74" +version = "0.1.75" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index 1596bb773976a..8522493f67b3a 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_utils" -version = "0.1.74" +version = "0.1.75" edition = "2021" publish = false diff --git a/declare_clippy_lint/Cargo.toml b/declare_clippy_lint/Cargo.toml index 1470da61fac0c..beea9fd00e7a9 100644 --- a/declare_clippy_lint/Cargo.toml +++ b/declare_clippy_lint/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "declare_clippy_lint" -version = "0.1.74" +version = "0.1.75" edition = "2021" publish = false From 6233d44815ce681062fca851a254f2a37db7916e Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Fri, 6 Oct 2023 17:47:56 +0200 Subject: [PATCH 48/48] Update Cargo.lock --- Cargo.lock | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 60a8f77c07d7c..e28397b391502 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -508,10 +508,12 @@ checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" [[package]] name = "clippy" -version = "0.1.74" +version = "0.1.75" dependencies = [ + "anstream", "clippy_lints", "clippy_utils", + "color-print", "filetime", "futures", "if_chain", @@ -546,7 +548,7 @@ dependencies = [ [[package]] name = "clippy_lints" -version = "0.1.74" +version = "0.1.75" dependencies = [ "arrayvec", "cargo_metadata", @@ -566,11 +568,12 @@ dependencies = [ "unicode-normalization", "unicode-script", "url", + "walkdir", ] [[package]] name = "clippy_utils" -version = "0.1.74" +version = "0.1.75" dependencies = [ "arrayvec", "if_chain", @@ -603,6 +606,27 @@ dependencies = [ "tracing-error", ] +[[package]] +name = "color-print" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a858372ff14bab9b1b30ea504f2a4bc534582aee3e42ba2d41d2a7baba63d5d" +dependencies = [ + "color-print-proc-macro", +] + +[[package]] +name = "color-print-proc-macro" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57e37866456a721d0a404439a1adae37a31be4e0055590d053dfe6981e05003f" +dependencies = [ + "nom", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "color-spantrace" version = "0.2.0" @@ -933,7 +957,7 @@ checksum = "a0afaad2b26fa326569eb264b1363e8ae3357618c43982b3f285f0774ce76b69" [[package]] name = "declare_clippy_lint" -version = "0.1.74" +version = "0.1.75" dependencies = [ "itertools", "quote",