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 01/13] 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 367cd6bd4..ea6a9bc56 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 173f9841d..94334f551 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 1ee048bf7..e1b95aa57 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 a03953633..ae6b02ab1 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 772296c50e28920ac2611587d21b543df73e0e83 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Mon, 25 Sep 2023 11:28:58 +0200 Subject: [PATCH 02/13] 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 | 182 ++---- 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 | 73 ++- 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} | 144 +++-- .../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, 4005 insertions(+), 2322 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 7d25b6a2b..30bd47633 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 a7ae4a0ee..8c9ab1e24 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 2d8b590db..66786004f 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 52c795e04..b980083f1 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 a88f2b51c..0546807ba 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 cf07e050c..c586b572b 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 84b99ad5c..f99a51e2b 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 88ffbb554..b00130ffd 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 000000000..5071af5ec --- /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 a4d40df52..4d1281ec1 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 bbce6e1dd..63ec81950 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 fe37fd4a0..148773856 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 bf2add6aa..e789e0da6 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 d4df6f7aa..e332f681b 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 379af9b22..f24577c73 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 4b9ca8c91..b612cc00b 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 c18006a71..0a885984a 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 f4f8bdc2c..b748d3293 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 01c714c41..39abf5c2d 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 76369bccf..1d2f7cb71 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 9b26c3573..a4f3d4983 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 d67d58993..19f1e08b5 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 large_future(_x: [u8; 16 * 1024]) {} /// - /// async fn big_fut(arg: [u8; 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 deba232bd..c06b35ca0 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 f52614b62..1271be2fd 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 6edca2d55..0a2bd89eb 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 c4f6852ae..44dc29c36 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 29af48123..0efeeacc9 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 d3e90e4bb..40e487bf6 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 fafc97097..336572549 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 81223fa8d..e7fcef9e9 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 000000000..3347c8c16 --- /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 000000000..98cd6afc2 --- /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 303f01256..9c8b47fb3 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 if_chain::if_chain; -use rustc_errors::Applicability; -use rustc_hir::intravisit::FnKind; -use rustc_hir::{ - self as hir, def, BinOpKind, BindingAnnotation, Body, ByRef, Expr, ExprKind, FnDecl, Mutability, PatKind, Stmt, - StmtKind, TyKind, -}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::lint::in_external_macro; -use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::def_id::LocalDefId; -use rustc_span::hygiene::DesugaringKind; -use rustc_span::source_map::{ExpnKind, Span}; - +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::{ - get_parent_expr, in_constant, is_integer_literal, is_lint_allowed, is_no_std_crate, iter_input_pats, + 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::{ + BinOpKind, BindingAnnotation, Body, ByRef, Expr, ExprKind, FnDecl, Mutability, PatKind, QPath, Stmt, StmtKind, +}; +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::def_id::LocalDefId; +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); + 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; } - if in_attributes_expansion(expr) || expr.span.is_desugaring(DesugaringKind::Await) { - // Don't lint things expanded by #[derive(...)], etc or `await` desugaring - 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 3b7eccad7..f598a65d2 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 000000000..d55c77a92 --- /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 7b00eabf9..57652e5ff 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(); - while !ctx.async_closures.is_empty() { - let 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); - } + + // 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 async_closures = ctx.async_closures.clone(); + ctx.async_closures.clear(); + 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 5f2a324b0..aee184252 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 4b24f059a..20b4b4f03 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 884663337..2b4e3260c 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 bf031ac84..7dabdcd58 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 e8018462d..2895595e0 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 4944381da..b26365e34 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 79f9d45d5..71a4b3fba 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 704d7abd7..e7915953d 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 000000000..5aa057580 --- /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 5ac4f0aa4..f32e7edad 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 268894755..75c3c7a95 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 6d3493523..94a9a7c24 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 da8654d93..82f9d4e41 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 4ed985f54..250772238 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 f49c3fadb..c38a3e81b 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 bf835f89c..86b77a77f 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 4a5b6fa5c..a3acb8f17 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 a9957b18a..da083fb14 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 6b1a738aa..d596eed4b 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 be1c46319..0f7bc04cc 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 173f9841d..82508bcdb 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 131f3c0aa..f04467dc1 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 9e25d97f5..604dc7691 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 4b06b12fb..d05d9e764 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 9f5116eb7..5ce22b65f 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 9fcc269db..f340cf593 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 000000000..74fc5d249 --- /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 000000000..750f3be84 --- /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 000000000..26b3354d1 --- /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 000000000..6f817a3fd --- /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 000000000..26cb2d77b --- /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 000000000..9df1ec6fa --- /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 000000000..31bb5ee35 --- /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 000000000..0ad7a9799 --- /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 000000000..8f4e178cc --- /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 000000000..11039b1db --- /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 000000000..64a8017fe --- /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 000000000..9ae760ae4 --- /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 000000000..cf7f432bf --- /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 000000000..4d9bc9d48 --- /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 000000000..f85aade6a --- /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 000000000..6918d7528 --- /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 000000000..15d175ef1 --- /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 000000000..468da22a9 --- /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 000000000..a93464860 --- /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 000000000..587d4f9b3 --- /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 000000000..584335dc2 --- /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 000000000..39798ffea --- /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 000000000..67ee57ab6 --- /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 000000000..45bcbce1e --- /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 000000000..3c87c79cf --- /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 000000000..0572373a6 --- /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 000000000..6678a2b47 --- /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 000000000..cdae1da01 --- /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 000000000..972f6aa40 --- /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 000000000..fdaba4ad2 --- /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 000000000..5c2c86c37 --- /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 000000000..40d7dfd93 --- /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 000000000..a34b15f4a --- /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 000000000..df505ed96 --- /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 000000000..dea4d61a9 --- /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 000000000..b0936319d --- /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 b97bb1444..4bed5c149 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 000000000..2f0236122 --- /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 000000000..2859a29f1 --- /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 000000000..a9c42fca4 --- /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 000000000..ebe92d9b5 --- /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 000000000..e2886a8cc --- /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 000000000..923cded5e --- /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 000000000..15906305c --- /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 000000000..7b2d6897d --- /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 000000000..a52e1fcb9 --- /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 000000000..bf2ffdd0e --- /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 000000000..b95f51343 --- /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 000000000..8ca637f72 --- /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 000000000..2f91866aa --- /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 000000000..2454c1038 --- /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 000000000..2ae298497 --- /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 e6dbb3d37..000000000 --- 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 000000000..3b205d536 --- /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 000000000..57ecb902d --- /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 183c07fe7..15edf2a7d 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 77f6aea2e..cc9530f79 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,21 +321,13 @@ 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() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider adding a safety comment on the preceding line -error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:527: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:531:9 | @@ -344,5 +336,61 @@ 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: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: 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 b28e1b7d1..a27813987 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 000000000..7c3ffc290 --- /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 000000000..413bc0bf1 --- /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 000000000..b44fbb554 --- /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 000000000..df9aa37ac --- /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 000000000..55a202eef --- /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 000000000..437692a4d --- /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 000000000..ce1027080 --- /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 000000000..7377921b4 --- /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 000000000..875aaeef6 --- /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 000000000..1752f4885 --- /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 000000000..331c2c59c --- /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 000000000..13ec3a229 --- /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 d0a092093..1ca18170f 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 6547107f5..5e7e545e7 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 79af9a83c..0d69ddbd5 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 da75cb195..f9f111a7c 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 f1cac8c5f..32c7499bf 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 c7a470b5b..25b7431ba 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 7c7f17974..951e4ac74 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 a4943344a..c9bebabdf 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 6d85b1ce9..1bc0047ad 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 6de870a92..6a1b81fdb 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 4108177e3..a41e88f88 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 86ef6edf8..fab698791 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 ac6c3e063..d62360111 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 4815ce6a0..8e51c28b3 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 57374bd5f..f98ce9d50 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 09077c60d..6d942ca89 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 f6a964da4..16cee3fd3 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 df9dc256d..4655acf14 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 ff6ed974d..3754a982c 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 0a52b2522..c2c5f765a 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 34a95d184..0cd6e41b8 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 8e27014d5..e91b78b0a 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 000000000..2a335516f --- /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 000000000..f0567f486 --- /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 000000000..e2cde2c59 --- /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 e1e5e8fd2..9cddcb3df 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 df3df0457..0c7fbd5df 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 e980adeef..c99c2f465 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 6113c5f25..dcc2af69f 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 5a8e3d045..4399c6555 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 f6585aa30..e46c0d735 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 b036e6342..aed079f09 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 d4c04ff90..ee004c0e1 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 99fb4723c..eae2fa176 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 000000000..49767e242 --- /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 000000000..2dfd04621 --- /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 000000000..a73ab4d08 --- /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 b3257c04f..e70f8e71f 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 000000000..a38523a7c --- /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 000000000..33adb6099 --- /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 000000000..0ea42a94a --- /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 bbe3b38e5..c578e7864 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 9afa191ce..d8c2286d5 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 5fee60b8e..6eb1cc753 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 9a1ec3a4d..f23116a7e 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 e2e0ee816..c0206b4ce 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 0a45a6d76..b8d7834e3 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 5259d9ce0..094d9574a 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 91f90157e..6d98d691d 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 a4dfc8f29..62798b6d3 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 7d780c803..b07851e86 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 ab0ac0dd4..9073080cb 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 f4e7f1943..000000000 --- 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 000000000..d0ba7ed74 --- /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 000000000..e89e7aad4 --- /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 000000000..d522b68d8 --- /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 7b662ca92..67faabc53 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 d79778a6a..99f913642 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 c672eff1c..a8f404b14 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 289519b17..78d827981 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 525919599..ed8387b7e 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 befb2f9a5..991a7762f 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 28e7bb610..c1f8b6b4a 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 4a5ef8385..4363d2224 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 ea020405a..f4e27fe4b 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 55074827b52a2dd1551c19d8f42c59299877e59b Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 26 Sep 2023 02:15:32 +0000 Subject: [PATCH 03/13] 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 b00fa104f..f95d2c2ed 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 be1c46319..47cce4603 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 4b06b12fb..686a3f414 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 f2efdba6e2ab000d8210e8855fc0bcddbcf58b64 Mon Sep 17 00:00:00 2001 From: lcnr Date: Mon, 25 Sep 2023 15:46:38 +0200 Subject: [PATCH 04/13] 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 5c5ee2620..50d6f3b7e 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 82207f4ff4ab4712a86e152656aaf1aa6a9299c6 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Sun, 27 Aug 2023 11:43:45 +0000 Subject: [PATCH 05/13] 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 2895595e0..8a7e48746 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 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 06/13] 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 87380f14f..bbb5ade8b 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 07/13] 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 f04467dc1..9dbb4c68d 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]; - let mut v = V { - locals, - location, - results: usage, - }; - v.visit_basic_block_data(tbb, tdata); - Some(v.results) - }) + // 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) + }) } struct V<'a> { From ea4b38820bafe9d7016cf19d35e977b08f0cbdd3 Mon Sep 17 00:00:00 2001 From: ouz-a Date: Wed, 16 Aug 2023 08:43:30 +0300 Subject: [PATCH 08/13] 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 17233058c..55f9cb27a 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 09/13] 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 fc5347c86..7fc62d4fc 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 f43e3c8ff..7ef4947f1 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 56794fa5f1a508ed0cf1b3f44bb152b3c0c641f1 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 2 Oct 2023 21:24:23 +0000 Subject: [PATCH 10/13] 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 937aac8d2..24c0ea3f6 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 a52e1fcb9..8b9d159b5 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 11081dc80..aaebdabcb 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 371ea1612..4b06cd038 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 e67d9b5b9..f2ee18585 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 87df1f8cb..b17343aa9 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 013907f59..4940091be 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 11/13] 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 aaebdabcb..11081dc80 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 50754da9fa2f956eed5af672ed3c7820d774ce91 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Fri, 6 Oct 2023 17:32:32 +0200 Subject: [PATCH 12/13] 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 5ce22b65f..fe2c77ab4 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 13/13] 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 1eacc82d5..cbcb42dad 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 834753a23..4d5b3bf8a 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 1596bb773..8522493f6 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 1470da61f..beea9fd00 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