From 4ccafea92d00fe35c71fb1ab419df7b4f264d25f Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 29 Dec 2022 14:28:34 +0100 Subject: [PATCH 1/2] Merge commit '4f3ab69ea0a0908260944443c739426cc384ae1a' into clippyup --- CHANGELOG.md | 4 + .../src/casts/cast_slice_different_sizes.rs | 2 +- clippy_lints/src/declared_lints.rs | 4 + clippy_lints/src/dereference.rs | 8 +- clippy_lints/src/floating_point_arithmetic.rs | 2 +- clippy_lints/src/fn_null_check.rs | 106 +++++++ clippy_lints/src/format_args.rs | 2 +- clippy_lints/src/functions/mod.rs | 33 ++- clippy_lints/src/large_const_arrays.rs | 6 +- clippy_lints/src/large_enum_variant.rs | 2 +- clippy_lints/src/large_stack_arrays.rs | 6 +- clippy_lints/src/len_zero.rs | 2 +- clippy_lints/src/lib.rs | 8 +- .../src/loops/explicit_counter_loop.rs | 2 +- clippy_lints/src/manual_async_fn.rs | 2 +- clippy_lints/src/manual_clamp.rs | 5 +- clippy_lints/src/manual_strip.rs | 2 +- clippy_lints/src/matches/manual_filter.rs | 17 +- .../src/matches/match_single_binding.rs | 18 +- clippy_lints/src/matches/match_wild_enum.rs | 2 +- .../src/methods/inefficient_to_string.rs | 2 +- clippy_lints/src/methods/iter_skip_next.rs | 2 +- .../src/methods/option_map_unwrap_or.rs | 2 +- clippy_lints/src/methods/str_splitn.rs | 2 +- .../src/methods/unnecessary_lazy_eval.rs | 2 +- clippy_lints/src/needless_late_init.rs | 6 +- clippy_lints/src/octal_escapes.rs | 4 +- .../src/operators/misrefactored_assign_op.rs | 2 +- .../src/permissions_set_readonly_false.rs | 52 ++++ clippy_lints/src/redundant_clone.rs | 4 +- clippy_lints/src/returns.rs | 8 +- clippy_lints/src/same_name_method.rs | 4 +- clippy_lints/src/size_of_ref.rs | 73 +++++ clippy_lints/src/swap.rs | 4 +- clippy_lints/src/transmute/mod.rs | 31 ++ .../src/transmute/transmute_null_to_fn.rs | 64 ++++ .../src/transmute/transmute_undefined_repr.rs | 14 +- .../transmutes_expressible_as_ptr_casts.rs | 2 +- .../src/transmute/transmuting_null.rs | 3 +- .../src/transmute/useless_transmute.rs | 2 +- .../src/types/redundant_allocation.rs | 8 +- clippy_lints/src/unit_types/unit_arg.rs | 4 +- clippy_lints/src/useless_conversion.rs | 28 +- clippy_lints/src/utils/conf.rs | 2 +- .../internal_lints/metadata_collector.rs | 4 +- clippy_lints/src/write.rs | 4 +- clippy_utils/src/consts.rs | 7 +- clippy_utils/src/diagnostics.rs | 2 +- clippy_utils/src/mir/maybe_storage_live.rs | 52 ---- clippy_utils/src/mir/mod.rs | 2 - clippy_utils/src/mir/possible_borrower.rs | 280 +++++++++++------- clippy_utils/src/sugg.rs | 1 - rust-toolchain | 2 +- tests/ui/crashes/ice-10044.rs | 3 + tests/ui/crashes/ice-10044.stderr | 10 + tests/ui/floating_point_powi.fixed | 9 + tests/ui/floating_point_powi.rs | 9 + tests/ui/floating_point_powi.stderr | 44 ++- tests/ui/fn_null_check.rs | 21 ++ tests/ui/fn_null_check.stderr | 43 +++ tests/ui/manual_filter.fixed | 41 +++ tests/ui/manual_filter.rs | 41 +++ tests/ui/manual_retain.fixed | 2 +- tests/ui/manual_retain.rs | 2 +- tests/ui/match_single_binding.fixed | 13 + tests/ui/match_single_binding.rs | 14 + tests/ui/match_single_binding.stderr | 27 +- .../match_wildcard_for_single_variants.fixed | 22 ++ .../ui/match_wildcard_for_single_variants.rs | 22 ++ .../match_wildcard_for_single_variants.stderr | 8 +- tests/ui/needless_borrow.fixed | 13 +- tests/ui/needless_borrow.rs | 13 +- tests/ui/needless_borrow.stderr | 8 +- tests/ui/needless_return.fixed | 5 + tests/ui/needless_return.rs | 5 + tests/ui/needless_return.stderr | 92 +++--- tests/ui/permissions_set_readonly_false.rs | 29 ++ .../ui/permissions_set_readonly_false.stderr | 13 + tests/ui/redundant_clone.fixed | 6 + tests/ui/redundant_clone.rs | 6 + tests/ui/redundant_clone.stderr | 14 +- tests/ui/size_of_ref.rs | 27 ++ tests/ui/size_of_ref.stderr | 27 ++ tests/ui/transmute_null_to_fn.rs | 28 ++ tests/ui/transmute_null_to_fn.stderr | 27 ++ tests/ui/useless_conversion.fixed | 73 ++++- tests/ui/useless_conversion.rs | 73 ++++- tests/ui/useless_conversion.stderr | 54 +++- tests/ui/wildcard_imports.fixed | 1 - tests/ui/wildcard_imports.rs | 1 - tests/ui/wildcard_imports.stderr | 42 +-- .../wildcard_imports_2021.edition2018.fixed | 240 +++++++++++++++ .../wildcard_imports_2021.edition2018.stderr | 132 +++++++++ .../wildcard_imports_2021.edition2021.fixed | 240 +++++++++++++++ .../wildcard_imports_2021.edition2021.stderr | 132 +++++++++ tests/ui/wildcard_imports_2021.rs | 241 +++++++++++++++ tests/ui/wildcard_imports_2021.stderr | 132 +++++++++ 97 files changed, 2558 insertions(+), 359 deletions(-) create mode 100644 clippy_lints/src/fn_null_check.rs create mode 100644 clippy_lints/src/permissions_set_readonly_false.rs create mode 100644 clippy_lints/src/size_of_ref.rs create mode 100644 clippy_lints/src/transmute/transmute_null_to_fn.rs delete mode 100644 clippy_utils/src/mir/maybe_storage_live.rs create mode 100644 tests/ui/crashes/ice-10044.rs create mode 100644 tests/ui/crashes/ice-10044.stderr create mode 100644 tests/ui/fn_null_check.rs create mode 100644 tests/ui/fn_null_check.stderr create mode 100644 tests/ui/permissions_set_readonly_false.rs create mode 100644 tests/ui/permissions_set_readonly_false.stderr create mode 100644 tests/ui/size_of_ref.rs create mode 100644 tests/ui/size_of_ref.stderr create mode 100644 tests/ui/transmute_null_to_fn.rs create mode 100644 tests/ui/transmute_null_to_fn.stderr create mode 100644 tests/ui/wildcard_imports_2021.edition2018.fixed create mode 100644 tests/ui/wildcard_imports_2021.edition2018.stderr create mode 100644 tests/ui/wildcard_imports_2021.edition2021.fixed create mode 100644 tests/ui/wildcard_imports_2021.edition2021.stderr create mode 100644 tests/ui/wildcard_imports_2021.rs create mode 100644 tests/ui/wildcard_imports_2021.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 903ee938d..02f3188f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4203,6 +4203,7 @@ Released 2018-09-13 [`float_cmp_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp_const [`float_equality_without_abs`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_equality_without_abs [`fn_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_address_comparisons +[`fn_null_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_null_check [`fn_params_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_params_excessive_bools [`fn_to_numeric_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast [`fn_to_numeric_cast_any`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast_any @@ -4460,6 +4461,7 @@ Released 2018-09-13 [`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 [`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 [`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma [`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence @@ -4545,6 +4547,7 @@ Released 2018-09-13 [`single_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match [`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else [`size_of_in_element_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_in_element_count +[`size_of_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_ref [`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next [`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization [`stable_sort_primitive`]: https://rust-lang.github.io/rust-clippy/master/index.html#stable_sort_primitive @@ -4590,6 +4593,7 @@ Released 2018-09-13 [`transmute_int_to_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_bool [`transmute_int_to_char`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_char [`transmute_int_to_float`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_float +[`transmute_null_to_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_null_to_fn [`transmute_num_to_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_num_to_bytes [`transmute_ptr_to_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ptr [`transmute_ptr_to_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref diff --git a/clippy_lints/src/casts/cast_slice_different_sizes.rs b/clippy_lints/src/casts/cast_slice_different_sizes.rs index e862f13e6..c8e54d7b8 100644 --- a/clippy_lints/src/casts/cast_slice_different_sizes.rs +++ b/clippy_lints/src/casts/cast_slice_different_sizes.rs @@ -54,7 +54,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: &Msrv diag.span_suggestion( expr.span, - &format!("replace with `ptr::slice_from_raw_parts{mutbl_fn_str}`"), + format!("replace with `ptr::slice_from_raw_parts{mutbl_fn_str}`"), sugg, rustc_errors::Applicability::HasPlaceholders, ); diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 3cd7d1d7e..2982460c9 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -161,6 +161,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::float_literal::LOSSY_FLOAT_LITERAL_INFO, crate::floating_point_arithmetic::IMPRECISE_FLOPS_INFO, crate::floating_point_arithmetic::SUBOPTIMAL_FLOPS_INFO, + crate::fn_null_check::FN_NULL_CHECK_INFO, crate::format::USELESS_FORMAT_INFO, crate::format_args::FORMAT_IN_FORMAT_ARGS_INFO, crate::format_args::TO_STRING_IN_FORMAT_ARGS_INFO, @@ -494,6 +495,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE_INFO, crate::pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF_INFO, crate::pattern_type_mismatch::PATTERN_TYPE_MISMATCH_INFO, + crate::permissions_set_readonly_false::PERMISSIONS_SET_READONLY_FALSE_INFO, crate::precedence::PRECEDENCE_INFO, crate::ptr::CMP_NULL_INFO, crate::ptr::INVALID_NULL_PTR_USAGE_INFO, @@ -535,6 +537,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES_INFO, crate::single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS_INFO, crate::size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT_INFO, + crate::size_of_ref::SIZE_OF_REF_INFO, crate::slow_vector_initialization::SLOW_VECTOR_INITIALIZATION_INFO, crate::std_instead_of_core::ALLOC_INSTEAD_OF_CORE_INFO, crate::std_instead_of_core::STD_INSTEAD_OF_ALLOC_INFO, @@ -568,6 +571,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::transmute::TRANSMUTE_INT_TO_BOOL_INFO, crate::transmute::TRANSMUTE_INT_TO_CHAR_INFO, crate::transmute::TRANSMUTE_INT_TO_FLOAT_INFO, + crate::transmute::TRANSMUTE_NULL_TO_FN_INFO, crate::transmute::TRANSMUTE_NUM_TO_BYTES_INFO, crate::transmute::TRANSMUTE_PTR_TO_PTR_INFO, crate::transmute::TRANSMUTE_PTR_TO_REF_INFO, diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 7b43d8ccc..728941b8b 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1282,10 +1282,10 @@ fn referent_used_exactly_once<'tcx>( 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) + // If `place.local` were not included 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.at_most_borrowers(cx, &[local, place.local], place.local, location) && used_exactly_once(mir, place.local).unwrap_or(false) } else { false diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 0ed301964..f95b628e6 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -324,7 +324,7 @@ fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: let maybe_neg_sugg = |expr, hir_id| { let sugg = Sugg::hir(cx, expr, ".."); if matches!(op, BinOpKind::Sub) && hir_id == rhs.hir_id { - format!("-{sugg}") + format!("-{}", sugg.maybe_par()) } else { sugg.to_string() } diff --git a/clippy_lints/src/fn_null_check.rs b/clippy_lints/src/fn_null_check.rs new file mode 100644 index 000000000..91c8c340c --- /dev/null +++ b/clippy_lints/src/fn_null_check.rs @@ -0,0 +1,106 @@ +use clippy_utils::consts::{constant, Constant}; +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::{is_integer_literal, is_path_diagnostic_item}; +use rustc_hir::{BinOpKind, Expr, ExprKind, TyKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// Checks for comparing a function pointer to null. + /// + /// ### Why is this bad? + /// Function pointers are assumed to not be null. + /// + /// ### Example + /// ```rust,ignore + /// let fn_ptr: fn() = /* somehow obtained nullable function pointer */ + /// + /// if (fn_ptr as *const ()).is_null() { ... } + /// ``` + /// Use instead: + /// ```rust,ignore + /// let fn_ptr: Option = /* somehow obtained nullable function pointer */ + /// + /// if fn_ptr.is_none() { ... } + /// ``` + #[clippy::version = "1.67.0"] + pub FN_NULL_CHECK, + correctness, + "`fn()` type assumed to be nullable" +} +declare_lint_pass!(FnNullCheck => [FN_NULL_CHECK]); + +fn lint_expr(cx: &LateContext<'_>, expr: &Expr<'_>) { + span_lint_and_help( + cx, + FN_NULL_CHECK, + expr.span, + "function pointer assumed to be nullable, even though it isn't", + None, + "try wrapping your function pointer type in `Option` instead, and using `is_none` to check for null pointer value", + ); +} + +fn is_fn_ptr_cast(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + if let ExprKind::Cast(cast_expr, cast_ty) = expr.kind + && let TyKind::Ptr(_) = cast_ty.kind + { + cx.typeck_results().expr_ty_adjusted(cast_expr).is_fn() + } else { + false + } +} + +impl<'tcx> LateLintPass<'tcx> for FnNullCheck { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + match expr.kind { + // Catching: + // (fn_ptr as * ).is_null() + ExprKind::MethodCall(method_name, receiver, _, _) + if method_name.ident.as_str() == "is_null" && is_fn_ptr_cast(cx, receiver) => + { + lint_expr(cx, expr); + }, + + ExprKind::Binary(op, left, right) if matches!(op.node, BinOpKind::Eq) => { + let to_check: &Expr<'_>; + if is_fn_ptr_cast(cx, left) { + to_check = right; + } else if is_fn_ptr_cast(cx, right) { + to_check = left; + } else { + return; + } + + match to_check.kind { + // Catching: + // (fn_ptr as * ) == (0 as ) + ExprKind::Cast(cast_expr, _) if is_integer_literal(cast_expr, 0) => { + lint_expr(cx, expr); + }, + + // Catching: + // (fn_ptr as * ) == std::ptr::null() + ExprKind::Call(func, []) if is_path_diagnostic_item(cx, func, sym::ptr_null) => { + lint_expr(cx, expr); + }, + + // Catching: + // (fn_ptr as * ) == + _ if matches!( + constant(cx, cx.typeck_results(), to_check), + Some((Constant::RawPtr(0), _)) + ) => + { + lint_expr(cx, expr); + }, + + _ => {}, + } + }, + _ => {}, + } + } +} diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index 69f7c152f..043112bbc 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -382,7 +382,7 @@ fn check_format_in_format_args( call_site, &format!("`format!` in `{name}!` args"), |diag| { - diag.help(&format!( + diag.help(format!( "combine the `format!(..)` arguments with the outer `{name}!(..)` call" )); diag.help("or consider changing `format!` to `format_args!`"); diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs index 91e6ffe64..9dbce3f88 100644 --- a/clippy_lints/src/functions/mod.rs +++ b/clippy_lints/src/functions/mod.rs @@ -63,23 +63,40 @@ declare_clippy_lint! { /// arguments but are not marked `unsafe`. /// /// ### Why is this bad? - /// The function should probably be marked `unsafe`, since - /// for an arbitrary raw pointer, there is no way of telling for sure if it is - /// valid. + /// The function should almost definitely be marked `unsafe`, since for an + /// arbitrary raw pointer, there is no way of telling for sure if it is valid. + /// + /// In general, this lint should **never be disabled** unless it is definitely a + /// false positive (please submit an issue if so) since it breaks Rust's + /// soundness guarantees, directly exposing API users to potentially dangerous + /// program behavior. This is also true for internal APIs, as it is easy to leak + /// unsoundness. + /// + /// ### Context + /// In Rust, an `unsafe {...}` block is used to indicate that the code in that + /// section has been verified in some way that the compiler can not. For a + /// function that accepts a raw pointer then accesses the pointer's data, this is + /// generally impossible as the incoming pointer could point anywhere, valid or + /// not. So, the signature should be marked `unsafe fn`: this indicates that the + /// function's caller must provide some verification that the arguments it sends + /// are valid (and then call the function within an `unsafe` block). /// /// ### Known problems /// * It does not check functions recursively so if the pointer is passed to a /// private non-`unsafe` function which does the dereferencing, the lint won't - /// trigger. + /// trigger (false negative). /// * It only checks for arguments whose type are raw pointers, not raw pointers /// got from an argument in some other way (`fn foo(bar: &[*const u8])` or - /// `some_argument.get_raw_ptr()`). + /// `some_argument.get_raw_ptr()`) (false negative). /// /// ### Example /// ```rust,ignore /// pub fn foo(x: *const u8) { /// println!("{}", unsafe { *x }); /// } + /// + /// // this call "looks" safe but will segfault or worse! + /// // foo(invalid_ptr); /// ``` /// /// Use instead: @@ -87,6 +104,12 @@ declare_clippy_lint! { /// pub unsafe fn foo(x: *const u8) { /// println!("{}", unsafe { *x }); /// } + /// + /// // this would cause a compiler error for calling without `unsafe` + /// // foo(invalid_ptr); + /// + /// // sound call if the caller knows the pointer is valid + /// unsafe { foo(valid_ptr); } /// ``` #[clippy::version = "pre 1.29.0"] pub NOT_UNSAFE_PTR_ARG_DEREF, diff --git a/clippy_lints/src/large_const_arrays.rs b/clippy_lints/src/large_const_arrays.rs index 76c83ab47..db637dfc0 100644 --- a/clippy_lints/src/large_const_arrays.rs +++ b/clippy_lints/src/large_const_arrays.rs @@ -34,12 +34,12 @@ declare_clippy_lint! { } pub struct LargeConstArrays { - maximum_allowed_size: u64, + maximum_allowed_size: u128, } impl LargeConstArrays { #[must_use] - pub fn new(maximum_allowed_size: u64) -> Self { + pub fn new(maximum_allowed_size: u128) -> Self { Self { maximum_allowed_size } } } @@ -56,7 +56,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeConstArrays { if let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind(); if let Ok(element_count) = element_count.try_to_machine_usize(cx.tcx); if let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes()); - if self.maximum_allowed_size < element_count * element_size; + if self.maximum_allowed_size < u128::from(element_count) * u128::from(element_size); then { let hi_pos = item.ident.span.lo() - BytePos::from_usize(1); diff --git a/clippy_lints/src/large_enum_variant.rs b/clippy_lints/src/large_enum_variant.rs index b18456ee5..b8d4abdbb 100644 --- a/clippy_lints/src/large_enum_variant.rs +++ b/clippy_lints/src/large_enum_variant.rs @@ -111,7 +111,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant { ); diag.span_label( def.variants[variants_size[1].ind].span, - &if variants_size[1].fields_size.is_empty() { + if variants_size[1].fields_size.is_empty() { "the second-largest variant carries no data at all".to_owned() } else { format!( diff --git a/clippy_lints/src/large_stack_arrays.rs b/clippy_lints/src/large_stack_arrays.rs index 5857d81ab..89ae83d48 100644 --- a/clippy_lints/src/large_stack_arrays.rs +++ b/clippy_lints/src/large_stack_arrays.rs @@ -24,12 +24,12 @@ declare_clippy_lint! { } pub struct LargeStackArrays { - maximum_allowed_size: u64, + maximum_allowed_size: u128, } impl LargeStackArrays { #[must_use] - pub fn new(maximum_allowed_size: u64) -> Self { + pub fn new(maximum_allowed_size: u128) -> Self { Self { maximum_allowed_size } } } @@ -45,7 +45,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackArrays { && let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes()) && !cx.tcx.hir().parent_iter(expr.hir_id) .any(|(_, node)| matches!(node, Node::Item(Item { kind: ItemKind::Static(..), .. }))) - && self.maximum_allowed_size < element_count * element_size { + && self.maximum_allowed_size < u128::from(element_count) * u128::from(element_size) { span_lint_and_help( cx, LARGE_STACK_ARRAYS, diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index e88d1764a..9eba46756 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -361,7 +361,7 @@ fn check_for_is_empty<'tcx>( db.span_note(span, "`is_empty` defined here"); } if let Some(self_kind) = self_kind { - db.note(&output.expected_sig(self_kind)); + db.note(output.expected_sig(self_kind)); } }); } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 39850d598..dcd8ca81a 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -125,6 +125,7 @@ mod explicit_write; mod fallible_impl_from; mod float_literal; mod floating_point_arithmetic; +mod fn_null_check; mod format; mod format_args; mod format_impl; @@ -234,6 +235,7 @@ mod partialeq_ne_impl; mod partialeq_to_none; mod pass_by_ref_or_value; mod pattern_type_mismatch; +mod permissions_set_readonly_false; mod precedence; mod ptr; mod ptr_offset_with_cast; @@ -263,6 +265,7 @@ mod shadow; mod single_char_lifetime_names; mod single_component_path_imports; mod size_of_in_element_count; +mod size_of_ref; mod slow_vector_initialization; mod std_instead_of_core; mod strings; @@ -334,7 +337,7 @@ pub fn read_conf(sess: &Session, path: &io::Result>) -> Conf { Ok(Some(path)) => path, Ok(None) => return Conf::default(), Err(error) => { - sess.struct_err(&format!("error finding Clippy's configuration file: {error}")) + sess.struct_err(format!("error finding Clippy's configuration file: {error}")) .emit(); return Conf::default(); }, @@ -902,6 +905,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(suspicious_xor_used_as_pow::ConfusingXorAndPow)); store.register_late_pass(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(msrv()))); store.register_late_pass(|_| Box::new(semicolon_block::SemicolonBlock)); + store.register_late_pass(|_| Box::new(fn_null_check::FnNullCheck)); + store.register_late_pass(|_| Box::new(permissions_set_readonly_false::PermissionsSetReadonlyFalse)); + store.register_late_pass(|_| Box::new(size_of_ref::SizeOfRef)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/loops/explicit_counter_loop.rs b/clippy_lints/src/loops/explicit_counter_loop.rs index 14f223481..1953ee8a7 100644 --- a/clippy_lints/src/loops/explicit_counter_loop.rs +++ b/clippy_lints/src/loops/explicit_counter_loop.rs @@ -77,7 +77,7 @@ pub(super) fn check<'tcx>( applicability, ); - diag.note(&format!( + diag.note(format!( "`{name}` is of type `{int_name}`, making it ineligible for `Iterator::enumerate`" )); }, diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs index 075ecbe7e..af7c05635 100644 --- a/clippy_lints/src/manual_async_fn.rs +++ b/clippy_lints/src/manual_async_fn.rs @@ -76,7 +76,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn { let help = format!("make the function `async` and {ret_sugg}"); diag.span_suggestion( header_span, - &help, + help, format!("async {}{ret_snip}", &header_snip[..ret_pos]), Applicability::MachineApplicable ); diff --git a/clippy_lints/src/manual_clamp.rs b/clippy_lints/src/manual_clamp.rs index bb6d628af..f239736d3 100644 --- a/clippy_lints/src/manual_clamp.rs +++ b/clippy_lints/src/manual_clamp.rs @@ -35,6 +35,9 @@ declare_clippy_lint! { /// Some may consider panicking in these situations to be desirable, but it also may /// introduce panicking where there wasn't any before. /// + /// See also [the discussion in the + /// PR](https://github.com/rust-lang/rust-clippy/pull/9484#issuecomment-1278922613). + /// /// ### Examples /// ```rust /// # let (input, min, max) = (0, -2, 1); @@ -78,7 +81,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.66.0"] pub MANUAL_CLAMP, - complexity, + nursery, "using a clamp pattern instead of the clamp function" } impl_lint_pass!(ManualClamp => [MANUAL_CLAMP]); diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index de166b976..c795c1d9a 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -109,7 +109,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip { let test_span = expr.span.until(then.span); span_lint_and_then(cx, MANUAL_STRIP, strippings[0], &format!("stripping a {kind_word} manually"), |diag| { - diag.span_note(test_span, &format!("the {kind_word} was tested here")); + diag.span_note(test_span, format!("the {kind_word} was tested here")); multispan_sugg( diag, &format!("try using the `strip_{kind_word}` method"), diff --git a/clippy_lints/src/matches/manual_filter.rs b/clippy_lints/src/matches/manual_filter.rs index d521a529e..f6bf0e7aa 100644 --- a/clippy_lints/src/matches/manual_filter.rs +++ b/clippy_lints/src/matches/manual_filter.rs @@ -3,7 +3,7 @@ use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::contains_unsafe_block; use clippy_utils::{is_res_lang_ctor, path_res, path_to_local_id}; -use rustc_hir::LangItem::OptionSome; +use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{Arm, Expr, ExprKind, HirId, Pat, PatKind}; use rustc_lint::LateContext; use rustc_span::{sym, SyntaxContext}; @@ -25,15 +25,13 @@ fn get_cond_expr<'tcx>( if let Some(block_expr) = peels_blocks_incl_unsafe_opt(expr); if let ExprKind::If(cond, then_expr, Some(else_expr)) = block_expr.kind; if let PatKind::Binding(_,target, ..) = pat.kind; - if let (then_visitor, else_visitor) - = (is_some_expr(cx, target, ctxt, then_expr), - is_some_expr(cx, target, ctxt, else_expr)); - if then_visitor != else_visitor; // check that one expr resolves to `Some(x)`, the other to `None` + if is_some_expr(cx, target, ctxt, then_expr) && is_none_expr(cx, else_expr) + || is_none_expr(cx, then_expr) && is_some_expr(cx, target, ctxt, else_expr); // check that one expr resolves to `Some(x)`, the other to `None` then { return Some(SomeExpr { expr: peels_blocks_incl_unsafe(cond.peel_drop_temps()), needs_unsafe_block: contains_unsafe_block(cx, expr), - needs_negated: !then_visitor // if the `then_expr` resolves to `None`, need to negate the cond + needs_negated: is_none_expr(cx, then_expr) // if the `then_expr` resolves to `None`, need to negate the cond }) } }; @@ -74,6 +72,13 @@ fn is_some_expr(cx: &LateContext<'_>, target: HirId, ctxt: SyntaxContext, expr: false } +fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + if let Some(inner_expr) = peels_blocks_incl_unsafe_opt(expr) { + return is_res_lang_ctor(cx, path_res(cx, inner_expr), OptionNone); + }; + false +} + // given the closure: `|| ` // returns `|&| ` fn add_ampersand_if_copy(body_str: String, has_copy_trait: bool) -> String { diff --git a/clippy_lints/src/matches/match_single_binding.rs b/clippy_lints/src/matches/match_single_binding.rs index 1bf8d4e96..c94a1f763 100644 --- a/clippy_lints/src/matches/match_single_binding.rs +++ b/clippy_lints/src/matches/match_single_binding.rs @@ -31,19 +31,11 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e }; // Do we need to add ';' to suggestion ? - match match_body.kind { - ExprKind::Block(block, _) => { - // macro + expr_ty(body) == () - if block.span.from_expansion() && cx.typeck_results().expr_ty(match_body).is_unit() { - snippet_body.push(';'); - } - }, - _ => { - // expr_ty(body) == () - if cx.typeck_results().expr_ty(match_body).is_unit() { - snippet_body.push(';'); - } - }, + if let ExprKind::Block(block, _) = match_body.kind { + // macro + expr_ty(body) == () + if block.span.from_expansion() && cx.typeck_results().expr_ty(match_body).is_unit() { + snippet_body.push(';'); + } } let mut applicability = Applicability::MaybeIncorrect; diff --git a/clippy_lints/src/matches/match_wild_enum.rs b/clippy_lints/src/matches/match_wild_enum.rs index 7f8d12483..59de8c038 100644 --- a/clippy_lints/src/matches/match_wild_enum.rs +++ b/clippy_lints/src/matches/match_wild_enum.rs @@ -30,7 +30,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { let mut has_non_wild = false; for arm in arms { match peel_hir_pat_refs(arm.pat).0.kind { - PatKind::Wild => wildcard_span = Some(arm.pat.span), + PatKind::Wild if arm.guard.is_none() => wildcard_span = Some(arm.pat.span), PatKind::Binding(_, _, ident, None) => { wildcard_span = Some(arm.pat.span); wildcard_ident = Some(ident); diff --git a/clippy_lints/src/methods/inefficient_to_string.rs b/clippy_lints/src/methods/inefficient_to_string.rs index d8c821bc9..424482859 100644 --- a/clippy_lints/src/methods/inefficient_to_string.rs +++ b/clippy_lints/src/methods/inefficient_to_string.rs @@ -36,7 +36,7 @@ pub fn check( expr.span, &format!("calling `to_string` on `{arg_ty}`"), |diag| { - diag.help(&format!( + diag.help(format!( "`{self_ty}` implements `ToString` through a slower blanket impl, but `{deref_self_ty}` has a fast specialization of `ToString`" )); let mut applicability = Applicability::MachineApplicable; diff --git a/clippy_lints/src/methods/iter_skip_next.rs b/clippy_lints/src/methods/iter_skip_next.rs index beb772100..279175e20 100644 --- a/clippy_lints/src/methods/iter_skip_next.rs +++ b/clippy_lints/src/methods/iter_skip_next.rs @@ -29,7 +29,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr application = Applicability::Unspecified; diag.span_help( pat.span, - &format!("for this change `{}` has to be mutable", snippet(cx, pat.span, "..")), + format!("for this change `{}` has to be mutable", snippet(cx, pat.span, "..")), ); } } diff --git a/clippy_lints/src/methods/option_map_unwrap_or.rs b/clippy_lints/src/methods/option_map_unwrap_or.rs index 910ee1485..4c6328481 100644 --- a/clippy_lints/src/methods/option_map_unwrap_or.rs +++ b/clippy_lints/src/methods/option_map_unwrap_or.rs @@ -84,7 +84,7 @@ pub(super) fn check<'tcx>( suggestion.push((map_arg_span.with_hi(map_arg_span.lo()), format!("{unwrap_snippet}, "))); } - diag.multipart_suggestion(&format!("use `{suggest}` instead"), suggestion, applicability); + diag.multipart_suggestion(format!("use `{suggest}` instead"), suggestion, applicability); }); } } diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs index 3c01ce1fe..d00708e82 100644 --- a/clippy_lints/src/methods/str_splitn.rs +++ b/clippy_lints/src/methods/str_splitn.rs @@ -167,7 +167,7 @@ fn check_manual_split_once_indirect( }; diag.span_suggestion_verbose( local.span, - &format!("try `{r}split_once`"), + format!("try `{r}split_once`"), format!("let ({lhs}, {rhs}) = {self_snip}.{r}split_once({pat_snip}){unwrap};"), app, ); diff --git a/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/clippy_lints/src/methods/unnecessary_lazy_eval.rs index 0e73459ad..47e2e7441 100644 --- a/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -62,7 +62,7 @@ pub(super) fn check<'tcx>( span_lint_and_then(cx, UNNECESSARY_LAZY_EVALUATIONS, expr.span, msg, |diag| { diag.span_suggestion( span, - &format!("use `{simplify_using}(..)` instead"), + format!("use `{simplify_using}(..)` instead"), format!("{simplify_using}({})", snippet(cx, body_expr.span, "..")), applicability, ); diff --git a/clippy_lints/src/needless_late_init.rs b/clippy_lints/src/needless_late_init.rs index 67debe7e0..5a9387b34 100644 --- a/clippy_lints/src/needless_late_init.rs +++ b/clippy_lints/src/needless_late_init.rs @@ -284,7 +284,7 @@ fn check<'tcx>( diag.span_suggestion( assign.lhs_span, - &format!("declare `{binding_name}` here"), + format!("declare `{binding_name}` here"), let_snippet, Applicability::MachineApplicable, ); @@ -304,7 +304,7 @@ fn check<'tcx>( diag.span_suggestion_verbose( usage.stmt.span.shrink_to_lo(), - &format!("declare `{binding_name}` here"), + format!("declare `{binding_name}` here"), format!("{let_snippet} = "), applicability, ); @@ -335,7 +335,7 @@ fn check<'tcx>( diag.span_suggestion_verbose( usage.stmt.span.shrink_to_lo(), - &format!("declare `{binding_name}` here"), + format!("declare `{binding_name}` here"), format!("{let_snippet} = "), applicability, ); diff --git a/clippy_lints/src/octal_escapes.rs b/clippy_lints/src/octal_escapes.rs index ae0a41db9..7376ab0c8 100644 --- a/clippy_lints/src/octal_escapes.rs +++ b/clippy_lints/src/octal_escapes.rs @@ -125,7 +125,7 @@ fn check_lit(cx: &EarlyContext<'_>, lit: &Lit, span: Span, is_string: bool) { if is_string { "string" } else { "byte string" } ), |diag| { - diag.help(&format!( + diag.help(format!( "octal escapes are not supported, `\\0` is always a null {}", if is_string { "character" } else { "byte" } )); @@ -139,7 +139,7 @@ fn check_lit(cx: &EarlyContext<'_>, lit: &Lit, span: Span, is_string: bool) { // suggestion 2: unambiguous null byte diag.span_suggestion( span, - &format!( + format!( "if the null {} is intended, disambiguate using", if is_string { "character" } else { "byte" } ), diff --git a/clippy_lints/src/operators/misrefactored_assign_op.rs b/clippy_lints/src/operators/misrefactored_assign_op.rs index ae805147f..015f6c14e 100644 --- a/clippy_lints/src/operators/misrefactored_assign_op.rs +++ b/clippy_lints/src/operators/misrefactored_assign_op.rs @@ -50,7 +50,7 @@ fn lint_misrefactored_assign_op( let long = format!("{snip_a} = {}", sugg::make_binop(op.into(), a, r)); diag.span_suggestion( expr.span, - &format!( + format!( "did you mean `{snip_a} = {snip_a} {} {snip_r}` or `{long}`? Consider replacing it with", op.as_str() ), diff --git a/clippy_lints/src/permissions_set_readonly_false.rs b/clippy_lints/src/permissions_set_readonly_false.rs new file mode 100644 index 000000000..e7095ec19 --- /dev/null +++ b/clippy_lints/src/permissions_set_readonly_false.rs @@ -0,0 +1,52 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::paths; +use clippy_utils::ty::match_type; +use rustc_ast::ast::LitKind; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for calls to `std::fs::Permissions.set_readonly` with argument `false`. + /// + /// ### Why is this bad? + /// On Unix platforms this results in the file being world writable, + /// equivalent to `chmod a+w `. + /// ### Example + /// ```rust + /// use std::fs::File; + /// let f = File::create("foo.txt").unwrap(); + /// let metadata = f.metadata().unwrap(); + /// let mut permissions = metadata.permissions(); + /// permissions.set_readonly(false); + /// ``` + #[clippy::version = "1.66.0"] + pub PERMISSIONS_SET_READONLY_FALSE, + suspicious, + "Checks for calls to `std::fs::Permissions.set_readonly` with argument `false`" +} +declare_lint_pass!(PermissionsSetReadonlyFalse => [PERMISSIONS_SET_READONLY_FALSE]); + +impl<'tcx> LateLintPass<'tcx> for PermissionsSetReadonlyFalse { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if let ExprKind::MethodCall(path, receiver, [arg], _) = &expr.kind + && match_type(cx, cx.typeck_results().expr_ty(receiver), &paths::PERMISSIONS) + && path.ident.name == sym!(set_readonly) + && let ExprKind::Lit(lit) = &arg.kind + && LitKind::Bool(false) == lit.node + { + span_lint_and_then( + cx, + PERMISSIONS_SET_READONLY_FALSE, + expr.span, + "call to `set_readonly` with argument `false`", + |diag| { + diag.note("on Unix platforms this results in the file being world writable"); + diag.help("you can set the desired permissions using `PermissionsExt`. For more information, see\n\ + https://doc.rust-lang.org/std/os/unix/fs/trait.PermissionsExt.html"); + } + ); + } + } +} diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index c1677fb3d..0e7c5cca7 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -131,7 +131,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { // `res = clone(arg)` can be turned into `res = move arg;` // if `arg` is the only borrow of `cloned` at this point. - if cannot_move_out || !possible_borrower.only_borrowers(&[arg], cloned, loc) { + if cannot_move_out || !possible_borrower.at_most_borrowers(cx, &[arg], cloned, loc) { continue; } @@ -178,7 +178,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { // StorageDead(pred_arg); // res = to_path_buf(cloned); // ``` - if cannot_move_out || !possible_borrower.only_borrowers(&[arg, cloned], local, loc) { + if cannot_move_out || !possible_borrower.at_most_borrowers(cx, &[arg, cloned], local, loc) { continue; } diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 81143d779..d4d506605 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -6,7 +6,7 @@ use core::ops::ControlFlow; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; -use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, HirId, MatchSource, PatKind, StmtKind}; +use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, HirId, LangItem, MatchSource, PatKind, QPath, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::subst::GenericArgKind; @@ -207,6 +207,12 @@ fn check_final_expr<'tcx>( match &peeled_drop_expr.kind { // simple return is always "bad" ExprKind::Ret(ref inner) => { + // if desugar of `do yeet`, don't lint + if let Some(inner_expr) = inner + && let ExprKind::Call(path_expr, _) = inner_expr.kind + && let ExprKind::Path(QPath::LangItem(LangItem::TryTraitFromYeet, _, _)) = path_expr.kind { + return; + } if cx.tcx.hir().attrs(expr.hir_id).is_empty() { let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner)); if !borrows { diff --git a/clippy_lints/src/same_name_method.rs b/clippy_lints/src/same_name_method.rs index caab5851b..17763128c 100644 --- a/clippy_lints/src/same_name_method.rs +++ b/clippy_lints/src/same_name_method.rs @@ -108,7 +108,7 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod { |diag| { diag.span_note( trait_method_span, - &format!("existing `{method_name}` defined here"), + format!("existing `{method_name}` defined here"), ); }, ); @@ -151,7 +151,7 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod { // iterate on trait_spans? diag.span_note( trait_spans[0], - &format!("existing `{method_name}` defined here"), + format!("existing `{method_name}` defined here"), ); }, ); diff --git a/clippy_lints/src/size_of_ref.rs b/clippy_lints/src/size_of_ref.rs new file mode 100644 index 000000000..3fcdb4288 --- /dev/null +++ b/clippy_lints/src/size_of_ref.rs @@ -0,0 +1,73 @@ +use clippy_utils::{diagnostics::span_lint_and_help, path_def_id, ty::peel_mid_ty_refs}; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// + /// Checks for calls to `std::mem::size_of_val()` where the argument is + /// a reference to a reference. + /// + /// ### Why is this bad? + /// + /// Calling `size_of_val()` with a reference to a reference as the argument + /// yields the size of the reference-type, not the size of the value behind + /// the reference. + /// + /// ### Example + /// ```rust + /// struct Foo { + /// buffer: [u8], + /// } + /// + /// impl Foo { + /// fn size(&self) -> usize { + /// // Note that `&self` as an argument is a `&&Foo`: Because `self` + /// // is already a reference, `&self` is a double-reference. + /// // The return value of `size_of_val()` therefor is the + /// // size of the reference-type, not the size of `self`. + /// std::mem::size_of_val(&self) + /// } + /// } + /// ``` + /// Use instead: + /// ```rust + /// struct Foo { + /// buffer: [u8], + /// } + /// + /// impl Foo { + /// fn size(&self) -> usize { + /// // Correct + /// std::mem::size_of_val(self) + /// } + /// } + /// ``` + #[clippy::version = "1.67.0"] + pub SIZE_OF_REF, + suspicious, + "Argument to `std::mem::size_of_val()` is a double-reference, which is almost certainly unintended" +} +declare_lint_pass!(SizeOfRef => [SIZE_OF_REF]); + +impl LateLintPass<'_> for SizeOfRef { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { + if let ExprKind::Call(path, [arg]) = expr.kind + && let Some(def_id) = path_def_id(cx, path) + && cx.tcx.is_diagnostic_item(sym::mem_size_of_val, def_id) + && let arg_ty = cx.typeck_results().expr_ty(arg) + && peel_mid_ty_refs(arg_ty).1 > 1 + { + span_lint_and_help( + cx, + SIZE_OF_REF, + expr.span, + "argument to `std::mem::size_of_val()` is a reference to a reference", + None, + "dereference the argument to `std::mem::size_of_val()` to get the size of the value instead of the size of the reference-type", + ); + } + } +} diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index c374529d1..17e9cc5f6 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -132,7 +132,7 @@ fn generate_swap_warning(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>, spa applicability, ); if !is_xor_based { - diag.note(&format!("or maybe you should use `{sugg}::mem::replace`?")); + diag.note(format!("or maybe you should use `{sugg}::mem::replace`?")); } }, ); @@ -214,7 +214,7 @@ fn check_suspicious_swap(cx: &LateContext<'_>, block: &Block<'_>) { Applicability::MaybeIncorrect, ); diag.note( - &format!("or maybe you should use `{sugg}::mem::replace`?") + format!("or maybe you should use `{sugg}::mem::replace`?") ); } }); diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs index 83e651aba..691d759d7 100644 --- a/clippy_lints/src/transmute/mod.rs +++ b/clippy_lints/src/transmute/mod.rs @@ -3,6 +3,7 @@ mod transmute_float_to_int; mod transmute_int_to_bool; mod transmute_int_to_char; mod transmute_int_to_float; +mod transmute_null_to_fn; mod transmute_num_to_bytes; mod transmute_ptr_to_ptr; mod transmute_ptr_to_ref; @@ -409,6 +410,34 @@ declare_clippy_lint! { "transmutes from a null pointer to a reference, which is undefined behavior" } +declare_clippy_lint! { + /// ### What it does + /// Checks for null function pointer creation through transmute. + /// + /// ### Why is this bad? + /// Creating a null function pointer is undefined behavior. + /// + /// More info: https://doc.rust-lang.org/nomicon/ffi.html#the-nullable-pointer-optimization + /// + /// ### Known problems + /// Not all cases can be detected at the moment of this writing. + /// For example, variables which hold a null pointer and are then fed to a `transmute` + /// call, aren't detectable yet. + /// + /// ### Example + /// ```rust + /// let null_fn: fn() = unsafe { std::mem::transmute( std::ptr::null::<()>() ) }; + /// ``` + /// Use instead: + /// ```rust + /// let null_fn: Option = None; + /// ``` + #[clippy::version = "1.67.0"] + pub TRANSMUTE_NULL_TO_FN, + correctness, + "transmute results in a null function pointer, which is undefined behavior" +} + pub struct Transmute { msrv: Msrv, } @@ -428,6 +457,7 @@ impl_lint_pass!(Transmute => [ TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS, TRANSMUTE_UNDEFINED_REPR, TRANSMUTING_NULL, + TRANSMUTE_NULL_TO_FN, ]); impl Transmute { #[must_use] @@ -461,6 +491,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { let linted = wrong_transmute::check(cx, e, from_ty, to_ty) | crosspointer_transmute::check(cx, e, from_ty, to_ty) | transmuting_null::check(cx, e, arg, to_ty) + | transmute_null_to_fn::check(cx, e, arg, to_ty) | transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, &self.msrv) | transmute_int_to_char::check(cx, e, from_ty, to_ty, arg, const_context) | transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context) diff --git a/clippy_lints/src/transmute/transmute_null_to_fn.rs b/clippy_lints/src/transmute/transmute_null_to_fn.rs new file mode 100644 index 000000000..e75d7f6bf --- /dev/null +++ b/clippy_lints/src/transmute/transmute_null_to_fn.rs @@ -0,0 +1,64 @@ +use clippy_utils::consts::{constant, Constant}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::{is_integer_literal, is_path_diagnostic_item}; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_middle::ty::Ty; +use rustc_span::symbol::sym; + +use super::TRANSMUTE_NULL_TO_FN; + +fn lint_expr(cx: &LateContext<'_>, expr: &Expr<'_>) { + span_lint_and_then( + cx, + TRANSMUTE_NULL_TO_FN, + expr.span, + "transmuting a known null pointer into a function pointer", + |diag| { + diag.span_label(expr.span, "this transmute results in undefined behavior"); + diag.help( + "try wrapping your function pointer type in `Option` instead, and using `None` as a null pointer value" + ); + }, + ); +} + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'tcx Expr<'_>, to_ty: Ty<'tcx>) -> bool { + if !to_ty.is_fn() { + return false; + } + + match arg.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), _))) => + { + 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. + false + }, + } +} diff --git a/clippy_lints/src/transmute/transmute_undefined_repr.rs b/clippy_lints/src/transmute/transmute_undefined_repr.rs index 34642f4b1..af0242348 100644 --- a/clippy_lints/src/transmute/transmute_undefined_repr.rs +++ b/clippy_lints/src/transmute/transmute_undefined_repr.rs @@ -77,7 +77,7 @@ pub(super) fn check<'tcx>( &format!("transmute from `{from_ty_orig}` which has an undefined layout"), |diag| { if from_ty_orig.peel_refs() != from_ty.peel_refs() { - diag.note(&format!("the contained type `{from_ty}` has an undefined layout")); + diag.note(format!("the contained type `{from_ty}` has an undefined layout")); } }, ); @@ -91,7 +91,7 @@ pub(super) fn check<'tcx>( &format!("transmute to `{to_ty_orig}` which has an undefined layout"), |diag| { if to_ty_orig.peel_refs() != to_ty.peel_refs() { - diag.note(&format!("the contained type `{to_ty}` has an undefined layout")); + diag.note(format!("the contained type `{to_ty}` has an undefined layout")); } }, ); @@ -119,16 +119,16 @@ pub(super) fn check<'tcx>( ), |diag| { if let Some(same_adt_did) = same_adt_did { - diag.note(&format!( + diag.note(format!( "two instances of the same generic type (`{}`) may have different layouts", cx.tcx.item_name(same_adt_did) )); } else { if from_ty_orig.peel_refs() != from_ty { - diag.note(&format!("the contained type `{from_ty}` has an undefined layout")); + diag.note(format!("the contained type `{from_ty}` has an undefined layout")); } if to_ty_orig.peel_refs() != to_ty { - diag.note(&format!("the contained type `{to_ty}` has an undefined layout")); + diag.note(format!("the contained type `{to_ty}` has an undefined layout")); } } }, @@ -146,7 +146,7 @@ pub(super) fn check<'tcx>( &format!("transmute from `{from_ty_orig}` which has an undefined layout"), |diag| { if from_ty_orig.peel_refs() != from_ty { - diag.note(&format!("the contained type `{from_ty}` has an undefined layout")); + diag.note(format!("the contained type `{from_ty}` has an undefined layout")); } }, ); @@ -163,7 +163,7 @@ pub(super) fn check<'tcx>( &format!("transmute into `{to_ty_orig}` which has an undefined layout"), |diag| { if to_ty_orig.peel_refs() != to_ty { - diag.note(&format!("the contained type `{to_ty}` has an undefined layout")); + diag.note(format!("the contained type `{to_ty}` has an undefined layout")); } }, ); diff --git a/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs b/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs index 6b444922a..b79d4e915 100644 --- a/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs +++ b/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs @@ -24,7 +24,7 @@ pub(super) fn check<'tcx>( &format!("transmute from `{from_ty}` to `{to_ty}` which could be expressed as a pointer cast instead"), |diag| { if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) { - let sugg = arg.as_ty(&to_ty.to_string()).to_string(); + let sugg = arg.as_ty(to_ty.to_string()).to_string(); diag.span_suggestion(e.span, "try", sugg, Applicability::MachineApplicable); } }, diff --git a/clippy_lints/src/transmute/transmuting_null.rs b/clippy_lints/src/transmute/transmuting_null.rs index 19ce5ae72..1e407fc41 100644 --- a/clippy_lints/src/transmute/transmuting_null.rs +++ b/clippy_lints/src/transmute/transmuting_null.rs @@ -18,8 +18,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t // Catching transmute over constants that resolve to `null`. let mut const_eval_context = constant_context(cx, cx.typeck_results()); if let ExprKind::Path(ref _qpath) = arg.kind && - let Some(Constant::RawPtr(x)) = const_eval_context.expr(arg) && - x == 0 + let Some(Constant::RawPtr(0)) = const_eval_context.expr(arg) { span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG); return true; diff --git a/clippy_lints/src/transmute/useless_transmute.rs b/clippy_lints/src/transmute/useless_transmute.rs index f919bbd5a..871c3fadb 100644 --- a/clippy_lints/src/transmute/useless_transmute.rs +++ b/clippy_lints/src/transmute/useless_transmute.rs @@ -61,7 +61,7 @@ pub(super) fn check<'tcx>( "transmute from an integer to a pointer", |diag| { if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) { - diag.span_suggestion(e.span, "try", arg.as_ty(&to_ty.to_string()), Applicability::Unspecified); + diag.span_suggestion(e.span, "try", arg.as_ty(to_ty.to_string()), Applicability::Unspecified); } }, ); diff --git a/clippy_lints/src/types/redundant_allocation.rs b/clippy_lints/src/types/redundant_allocation.rs index fae5385ff..f9b9a66b5 100644 --- a/clippy_lints/src/types/redundant_allocation.rs +++ b/clippy_lints/src/types/redundant_allocation.rs @@ -31,7 +31,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ &format!("usage of `{outer_sym}<{generic_snippet}>`"), |diag| { diag.span_suggestion(hir_ty.span, "try", format!("{generic_snippet}"), applicability); - diag.note(&format!( + diag.note(format!( "`{generic_snippet}` is already a pointer, `{outer_sym}<{generic_snippet}>` allocates a pointer on the heap" )); }, @@ -78,7 +78,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ format!("{outer_sym}<{generic_snippet}>"), applicability, ); - diag.note(&format!( + diag.note(format!( "`{inner_sym}<{generic_snippet}>` is already on the heap, `{outer_sym}<{inner_sym}<{generic_snippet}>>` makes an extra allocation" )); }, @@ -91,10 +91,10 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ hir_ty.span, &format!("usage of `{outer_sym}<{inner_sym}<{generic_snippet}>>`"), |diag| { - diag.note(&format!( + diag.note(format!( "`{inner_sym}<{generic_snippet}>` is already on the heap, `{outer_sym}<{inner_sym}<{generic_snippet}>>` makes an extra allocation" )); - diag.help(&format!( + diag.help(format!( "consider using just `{outer_sym}<{generic_snippet}>` or `{inner_sym}<{generic_snippet}>`" )); }, diff --git a/clippy_lints/src/unit_types/unit_arg.rs b/clippy_lints/src/unit_types/unit_arg.rs index f6d3fb00f..ef9f740f7 100644 --- a/clippy_lints/src/unit_types/unit_arg.rs +++ b/clippy_lints/src/unit_types/unit_arg.rs @@ -129,7 +129,7 @@ fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Exp if arg_snippets_without_empty_blocks.is_empty() { db.multipart_suggestion( - &format!("use {singular}unit literal{plural} instead"), + format!("use {singular}unit literal{plural} instead"), args_to_recover .iter() .map(|arg| (arg.span, "()".to_string())) @@ -142,7 +142,7 @@ fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Exp let it_or_them = if plural { "them" } else { "it" }; db.span_suggestion( expr.span, - &format!( + format!( "{or}move the expression{empty_or_s} in front of the call and replace {it_or_them} with the unit literal `()`" ), sugg, diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 3743d5d97..a95e7b613 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -1,11 +1,11 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::source::{snippet, snippet_with_macro_callsite}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{is_type_diagnostic_item, same_type_and_consts}; -use clippy_utils::{get_parent_expr, is_trait_method, match_def_path, paths}; +use clippy_utils::ty::{is_copy, is_type_diagnostic_item, same_type_and_consts}; +use clippy_utils::{get_parent_expr, is_trait_method, match_def_path, path_to_local, paths}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, HirId, MatchSource}; +use rustc_hir::{BindingAnnotation, Expr, ExprKind, HirId, MatchSource, Node, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -81,16 +81,24 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { } } if is_trait_method(cx, e, sym::IntoIterator) && name.ident.name == sym::into_iter { - if let Some(parent_expr) = get_parent_expr(cx, e) { - if let ExprKind::MethodCall(parent_name, ..) = parent_expr.kind { - if parent_name.ident.name != sym::into_iter { - return; - } - } + if get_parent_expr(cx, e).is_some() && + let Some(id) = path_to_local(recv) && + let Node::Pat(pat) = cx.tcx.hir().get(id) && + let PatKind::Binding(ann, ..) = pat.kind && + ann != BindingAnnotation::MUT + { + // Do not remove .into_iter() applied to a non-mutable local variable used in + // a larger expression context as it would differ in mutability. + return; } + let a = cx.typeck_results().expr_ty(e); let b = cx.typeck_results().expr_ty(recv); - if same_type_and_consts(a, b) { + + // If the types are identical then .into_iter() can be removed, unless the type + // implements Copy, in which case .into_iter() returns a copy of the receiver and + // cannot be safely omitted. + if same_type_and_consts(a, b) && !is_copy(cx, b) { let sugg = snippet(cx, recv.span, "").into_owned(); span_lint_and_sugg( cx, diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 3e7d0028c..c1589c771 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -333,7 +333,7 @@ define_Conf! { /// Lint: LARGE_STACK_ARRAYS, LARGE_CONST_ARRAYS. /// /// The maximum allowed size for arrays on the stack - (array_size_threshold: u64 = 512_000), + (array_size_threshold: u128 = 512_000), /// Lint: VEC_BOX. /// /// The size of the boxed type in bytes, where boxing in a `Vec` is allowed diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 857abe77e..929544cd6 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -558,8 +558,8 @@ impl fmt::Display for ClippyConfiguration { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result { writeln!( f, - "* `{}`: `{}`: {} (defaults to `{}`)", - self.name, self.config_type, self.doc, self.default + "* `{}`: `{}`(defaults to `{}`): {}", + self.name, self.config_type, self.default, self.doc ) } } diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 6b321765b..df3350388 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -377,7 +377,7 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_c // print!("\n"), write!(f, "\n") diag.multipart_suggestion( - &format!("use `{name}ln!` instead"), + format!("use `{name}ln!` instead"), vec![(name_span, format!("{name}ln")), (format_string_span, String::new())], Applicability::MachineApplicable, ); @@ -388,7 +388,7 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_c let newline_span = format_string_span.with_lo(hi - BytePos(3)).with_hi(hi - BytePos(1)); diag.multipart_suggestion( - &format!("use `{name}ln!` instead"), + format!("use `{name}ln!` instead"), vec![(name_span, format!("{name}ln")), (newline_span, String::new())], Applicability::MachineApplicable, ); diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 7a637d32b..a67bd8d46 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -620,12 +620,7 @@ pub fn miri_to_const<'tcx>(tcx: TyCtxt<'tcx>, result: mir::ConstantKind<'tcx>) - ty::Float(FloatTy::F64) => Some(Constant::F64(f64::from_bits( int.try_into().expect("invalid f64 bit representation"), ))), - ty::RawPtr(type_and_mut) => { - if let ty::Uint(_) = type_and_mut.ty.kind() { - return Some(Constant::RawPtr(int.assert_bits(int.size()))); - } - None - }, + ty::RawPtr(_) => Some(Constant::RawPtr(int.assert_bits(int.size()))), // FIXME: implement other conversions. _ => None, } diff --git a/clippy_utils/src/diagnostics.rs b/clippy_utils/src/diagnostics.rs index 16b160b6f..812f6fe71 100644 --- a/clippy_utils/src/diagnostics.rs +++ b/clippy_utils/src/diagnostics.rs @@ -17,7 +17,7 @@ use std::env; fn docs_link(diag: &mut Diagnostic, lint: &'static Lint) { if env::var("CLIPPY_DISABLE_DOCS_LINKS").is_err() { if let Some(lint) = lint.name_lower().strip_prefix("clippy::") { - diag.help(&format!( + diag.help(format!( "for further information visit https://rust-lang.github.io/rust-clippy/{}/index.html#{lint}", &option_env!("RUST_RELEASE_NUM").map_or("master".to_string(), |n| { // extract just major + minor version and ignore patch versions diff --git a/clippy_utils/src/mir/maybe_storage_live.rs b/clippy_utils/src/mir/maybe_storage_live.rs deleted file mode 100644 index d262b335d..000000000 --- a/clippy_utils/src/mir/maybe_storage_live.rs +++ /dev/null @@ -1,52 +0,0 @@ -use rustc_index::bit_set::BitSet; -use rustc_middle::mir; -use rustc_mir_dataflow::{AnalysisDomain, CallReturnPlaces, GenKill, GenKillAnalysis}; - -/// Determines liveness of each local purely based on `StorageLive`/`Dead`. -#[derive(Copy, Clone)] -pub(super) struct MaybeStorageLive; - -impl<'tcx> AnalysisDomain<'tcx> for MaybeStorageLive { - type Domain = BitSet; - const NAME: &'static str = "maybe_storage_live"; - - fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { - // bottom = dead - BitSet::new_empty(body.local_decls.len()) - } - - fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain) { - for arg in body.args_iter() { - state.insert(arg); - } - } -} - -impl<'tcx> GenKillAnalysis<'tcx> for MaybeStorageLive { - type Idx = mir::Local; - - fn statement_effect(&self, trans: &mut impl GenKill, stmt: &mir::Statement<'tcx>, _: mir::Location) { - match stmt.kind { - mir::StatementKind::StorageLive(l) => trans.gen(l), - mir::StatementKind::StorageDead(l) => trans.kill(l), - _ => (), - } - } - - fn terminator_effect( - &self, - _trans: &mut impl GenKill, - _terminator: &mir::Terminator<'tcx>, - _loc: mir::Location, - ) { - } - - fn call_return_effect( - &self, - _trans: &mut impl GenKill, - _block: mir::BasicBlock, - _return_places: CallReturnPlaces<'_, 'tcx>, - ) { - // Nothing to do when a call returns successfully - } -} diff --git a/clippy_utils/src/mir/mod.rs b/clippy_utils/src/mir/mod.rs index 818e603f6..26c0015e8 100644 --- a/clippy_utils/src/mir/mod.rs +++ b/clippy_utils/src/mir/mod.rs @@ -5,8 +5,6 @@ use rustc_middle::mir::{ }; use rustc_middle::ty::TyCtxt; -mod maybe_storage_live; - mod possible_borrower; pub use possible_borrower::PossibleBorrowerMap; diff --git a/clippy_utils/src/mir/possible_borrower.rs b/clippy_utils/src/mir/possible_borrower.rs index 25717bf3d..395d46e7a 100644 --- a/clippy_utils/src/mir/possible_borrower.rs +++ b/clippy_utils/src/mir/possible_borrower.rs @@ -1,92 +1,137 @@ -use super::{ - maybe_storage_live::MaybeStorageLive, possible_origin::PossibleOriginVisitor, - transitive_relation::TransitiveRelation, -}; +use super::possible_origin::PossibleOriginVisitor; use crate::ty::is_copy; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_index::bit_set::{BitSet, HybridBitSet}; use rustc_lint::LateContext; -use rustc_middle::mir::{self, visit::Visitor as _, Mutability}; -use rustc_middle::ty::{self, visit::TypeVisitor}; -use rustc_mir_dataflow::{Analysis, ResultsCursor}; +use rustc_middle::mir::{ + self, visit::Visitor as _, BasicBlock, Local, Location, Mutability, Statement, StatementKind, Terminator, +}; +use rustc_middle::ty::{self, visit::TypeVisitor, TyCtxt}; +use rustc_mir_dataflow::{ + fmt::DebugWithContext, impls::MaybeStorageLive, lattice::JoinSemiLattice, Analysis, AnalysisDomain, + CallReturnPlaces, ResultsCursor, +}; +use std::borrow::Cow; use std::ops::ControlFlow; /// Collects the possible borrowers of each local. /// For example, `b = &a; c = &a;` will make `b` and (transitively) `c` /// possible borrowers of `a`. #[allow(clippy::module_name_repetitions)] -struct PossibleBorrowerVisitor<'a, 'b, 'tcx> { - possible_borrower: TransitiveRelation, +struct PossibleBorrowerAnalysis<'b, 'tcx> { + tcx: TyCtxt<'tcx>, body: &'b mir::Body<'tcx>, - cx: &'a LateContext<'tcx>, possible_origin: FxHashMap>, } -impl<'a, 'b, 'tcx> PossibleBorrowerVisitor<'a, 'b, 'tcx> { +#[derive(Clone, Debug, Eq, PartialEq)] +struct PossibleBorrowerState { + map: FxIndexMap>, + domain_size: usize, +} + +impl PossibleBorrowerState { + fn new(domain_size: usize) -> Self { + Self { + map: FxIndexMap::default(), + domain_size, + } + } + + #[allow(clippy::similar_names)] + fn add(&mut self, borrowed: Local, borrower: Local) { + self.map + .entry(borrowed) + .or_insert(BitSet::new_empty(self.domain_size)) + .insert(borrower); + } +} + +impl DebugWithContext for PossibleBorrowerState { + fn fmt_with(&self, _ctxt: &C, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + <_ as std::fmt::Debug>::fmt(self, f) + } + fn fmt_diff_with(&self, _old: &Self, _ctxt: &C, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + unimplemented!() + } +} + +impl JoinSemiLattice for PossibleBorrowerState { + fn join(&mut self, other: &Self) -> bool { + let mut changed = false; + for (&borrowed, borrowers) in other.map.iter() { + if !borrowers.is_empty() { + changed |= self + .map + .entry(borrowed) + .or_insert(BitSet::new_empty(self.domain_size)) + .union(borrowers); + } + } + changed + } +} + +impl<'b, 'tcx> AnalysisDomain<'tcx> for PossibleBorrowerAnalysis<'b, 'tcx> { + type Domain = PossibleBorrowerState; + + const NAME: &'static str = "possible_borrower"; + + fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { + PossibleBorrowerState::new(body.local_decls.len()) + } + + fn initialize_start_block(&self, _body: &mir::Body<'tcx>, _entry_set: &mut Self::Domain) {} +} + +impl<'b, 'tcx> PossibleBorrowerAnalysis<'b, 'tcx> { fn new( - cx: &'a LateContext<'tcx>, + tcx: TyCtxt<'tcx>, body: &'b mir::Body<'tcx>, possible_origin: FxHashMap>, ) -> Self { Self { - possible_borrower: TransitiveRelation::default(), - cx, + tcx, body, possible_origin, } } - - fn into_map( - self, - cx: &'a LateContext<'tcx>, - maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive>, - ) -> PossibleBorrowerMap<'b, 'tcx> { - let mut map = FxHashMap::default(); - for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) { - if is_copy(cx, self.body.local_decls[row].ty) { - continue; - } - - let mut borrowers = self.possible_borrower.reachable_from(row, self.body.local_decls.len()); - borrowers.remove(mir::Local::from_usize(0)); - if !borrowers.is_empty() { - map.insert(row, borrowers); - } - } - - let bs = BitSet::new_empty(self.body.local_decls.len()); - PossibleBorrowerMap { - map, - maybe_live, - bitset: (bs.clone(), bs), - } - } } -impl<'a, 'b, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'b, 'tcx> { - fn visit_assign(&mut self, place: &mir::Place<'tcx>, rvalue: &mir::Rvalue<'_>, _location: mir::Location) { - let lhs = place.local; - match rvalue { - mir::Rvalue::Ref(_, _, borrowed) => { - self.possible_borrower.add(borrowed.local, lhs); - }, - other => { - if ContainsRegion - .visit_ty(place.ty(&self.body.local_decls, self.cx.tcx).ty) - .is_continue() - { - return; - } - rvalue_locals(other, |rhs| { - if lhs != rhs { - self.possible_borrower.add(rhs, lhs); +impl<'b, 'tcx> Analysis<'tcx> for PossibleBorrowerAnalysis<'b, 'tcx> { + fn apply_call_return_effect( + &self, + _state: &mut Self::Domain, + _block: BasicBlock, + _return_places: CallReturnPlaces<'_, 'tcx>, + ) { + } + + fn apply_statement_effect(&self, state: &mut Self::Domain, statement: &Statement<'tcx>, _location: Location) { + if let StatementKind::Assign(box (place, rvalue)) = &statement.kind { + let lhs = place.local; + match rvalue { + mir::Rvalue::Ref(_, _, borrowed) => { + state.add(borrowed.local, lhs); + }, + other => { + if ContainsRegion + .visit_ty(place.ty(&self.body.local_decls, self.tcx).ty) + .is_continue() + { + return; } - }); - }, + rvalue_locals(other, |rhs| { + if lhs != rhs { + state.add(rhs, lhs); + } + }); + }, + } } } - fn visit_terminator(&mut self, terminator: &mir::Terminator<'_>, _loc: mir::Location) { + fn apply_terminator_effect(&self, state: &mut Self::Domain, terminator: &Terminator<'tcx>, _location: Location) { if let mir::TerminatorKind::Call { args, destination: mir::Place { local: dest, .. }, @@ -126,10 +171,10 @@ impl<'a, 'b, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'b, for y in mutable_variables { for x in &immutable_borrowers { - self.possible_borrower.add(*x, y); + state.add(*x, y); } for x in &mutable_borrowers { - self.possible_borrower.add(*x, y); + state.add(*x, y); } } } @@ -165,73 +210,98 @@ fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) { } } -/// Result of `PossibleBorrowerVisitor`. +/// Result of `PossibleBorrowerAnalysis`. #[allow(clippy::module_name_repetitions)] pub struct PossibleBorrowerMap<'b, 'tcx> { - /// Mapping `Local -> its possible borrowers` - pub map: FxHashMap>, - maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive>, - // Caches to avoid allocation of `BitSet` on every query - pub bitset: (BitSet, BitSet), + body: &'b mir::Body<'tcx>, + possible_borrower: ResultsCursor<'b, 'tcx, PossibleBorrowerAnalysis<'b, 'tcx>>, + maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive<'b>>, + pushed: BitSet, + stack: Vec, } -impl<'a, 'b, 'tcx> PossibleBorrowerMap<'b, 'tcx> { - pub fn new(cx: &'a LateContext<'tcx>, mir: &'b mir::Body<'tcx>) -> Self { +impl<'b, 'tcx> PossibleBorrowerMap<'b, 'tcx> { + pub fn new(cx: &LateContext<'tcx>, mir: &'b mir::Body<'tcx>) -> Self { let possible_origin = { let mut vis = PossibleOriginVisitor::new(mir); vis.visit_body(mir); vis.into_map(cx) }; - let maybe_storage_live_result = MaybeStorageLive + let possible_borrower = PossibleBorrowerAnalysis::new(cx.tcx, mir, possible_origin) .into_engine(cx.tcx, mir) - .pass_name("redundant_clone") + .pass_name("possible_borrower") .iterate_to_fixpoint() .into_results_cursor(mir); - let mut vis = PossibleBorrowerVisitor::new(cx, mir, possible_origin); - vis.visit_body(mir); - vis.into_map(cx, maybe_storage_live_result) + let maybe_live = MaybeStorageLive::new(Cow::Owned(BitSet::new_empty(mir.local_decls.len()))) + .into_engine(cx.tcx, mir) + .pass_name("possible_borrower") + .iterate_to_fixpoint() + .into_results_cursor(mir); + PossibleBorrowerMap { + body: mir, + possible_borrower, + maybe_live, + pushed: BitSet::new_empty(mir.local_decls.len()), + stack: Vec::with_capacity(mir.local_decls.len()), + } } - /// Returns true if the set of borrowers of `borrowed` living at `at` matches with `borrowers`. - pub fn only_borrowers(&mut self, borrowers: &[mir::Local], borrowed: mir::Local, at: mir::Location) -> bool { - self.bounded_borrowers(borrowers, borrowers, borrowed, at) - } - - /// Returns true if the set of borrowers of `borrowed` living at `at` includes at least `below` - /// but no more than `above`. - pub fn bounded_borrowers( + /// Returns true if the set of borrowers of `borrowed` living at `at` includes no more than + /// `borrowers`. + /// Notes: + /// 1. It would be nice if `PossibleBorrowerMap` could store `cx` so that `at_most_borrowers` + /// would not require it to be passed in. But a `PossibleBorrowerMap` is stored in `LintPass` + /// `Dereferencing`, which outlives any `LateContext`. + /// 2. In all current uses of `at_most_borrowers`, `borrowers` is a slice of at most two + /// elements. Thus, `borrowers.contains(...)` is effectively a constant-time operation. If + /// `at_most_borrowers`'s uses were to expand beyond this, its implementation might have to be + /// adjusted. + pub fn at_most_borrowers( &mut self, - below: &[mir::Local], - above: &[mir::Local], + cx: &LateContext<'tcx>, + borrowers: &[mir::Local], borrowed: mir::Local, at: mir::Location, ) -> bool { - self.maybe_live.seek_after_primary_effect(at); + if is_copy(cx, self.body.local_decls[borrowed].ty) { + return true; + } - self.bitset.0.clear(); - let maybe_live = &mut self.maybe_live; - if let Some(bitset) = self.map.get(&borrowed) { - for b in bitset.iter().filter(move |b| maybe_live.contains(*b)) { - self.bitset.0.insert(b); + self.possible_borrower.seek_before_primary_effect(at); + self.maybe_live.seek_before_primary_effect(at); + + let possible_borrower = &self.possible_borrower.get().map; + let maybe_live = &self.maybe_live; + + self.pushed.clear(); + self.stack.clear(); + + if let Some(borrowers) = possible_borrower.get(&borrowed) { + for b in borrowers.iter() { + if self.pushed.insert(b) { + self.stack.push(b); + } } } else { - return false; + // Nothing borrows `borrowed` at `at`. + return true; } - self.bitset.1.clear(); - for b in below { - self.bitset.1.insert(*b); + while let Some(borrower) = self.stack.pop() { + if maybe_live.contains(borrower) && !borrowers.contains(&borrower) { + return false; + } + + if let Some(borrowers) = possible_borrower.get(&borrower) { + for b in borrowers.iter() { + if self.pushed.insert(b) { + self.stack.push(b); + } + } + } } - if !self.bitset.0.superset(&self.bitset.1) { - return false; - } - - for b in above { - self.bitset.0.remove(*b); - } - - self.bitset.0.is_empty() + true } pub fn local_is_alive_at(&mut self, local: mir::Local, at: mir::Location) -> bool { diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index b66604f33..4c4c077d7 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -185,7 +185,6 @@ impl<'a> Sugg<'a> { ) -> Self { use rustc_ast::ast::RangeLimits; - #[expect(clippy::match_wildcard_for_single_variants)] match expr.kind { _ if expr.span.ctxt() != ctxt => Sugg::NonParen(snippet_with_context(cx, expr.span, ctxt, default, app).0), ast::ExprKind::AddrOf(..) diff --git a/rust-toolchain b/rust-toolchain index 8e21cef32..9399d4220 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-12-17" +channel = "nightly-2022-12-29" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/tests/ui/crashes/ice-10044.rs b/tests/ui/crashes/ice-10044.rs new file mode 100644 index 000000000..65f38fe71 --- /dev/null +++ b/tests/ui/crashes/ice-10044.rs @@ -0,0 +1,3 @@ +fn main() { + [0; usize::MAX]; +} diff --git a/tests/ui/crashes/ice-10044.stderr b/tests/ui/crashes/ice-10044.stderr new file mode 100644 index 000000000..731f8265a --- /dev/null +++ b/tests/ui/crashes/ice-10044.stderr @@ -0,0 +1,10 @@ +error: statement with no effect + --> $DIR/ice-10044.rs:2:5 + | +LL | [0; usize::MAX]; + | ^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::no-effect` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui/floating_point_powi.fixed b/tests/ui/floating_point_powi.fixed index 884d05fed..8ffd4cc51 100644 --- a/tests/ui/floating_point_powi.fixed +++ b/tests/ui/floating_point_powi.fixed @@ -14,6 +14,15 @@ fn main() { let _ = (y as f32).mul_add(y as f32, x); let _ = x.mul_add(x, y).sqrt(); let _ = y.mul_add(y, x).sqrt(); + + let _ = (x - 1.0).mul_add(x - 1.0, -y); + let _ = (x - 1.0).mul_add(x - 1.0, -y) + 3.0; + let _ = (x - 1.0).mul_add(x - 1.0, -(y + 3.0)); + let _ = (y + 1.0).mul_add(-(y + 1.0), x); + let _ = (3.0 * y).mul_add(-(3.0 * y), x); + let _ = (y + 1.0 + x).mul_add(-(y + 1.0 + x), x); + let _ = (y + 1.0 + 2.0).mul_add(-(y + 1.0 + 2.0), x); + // Cases where the lint shouldn't be applied let _ = x.powi(2); let _ = x.powi(1 + 1); diff --git a/tests/ui/floating_point_powi.rs b/tests/ui/floating_point_powi.rs index e6a1c8953..9ae3455a1 100644 --- a/tests/ui/floating_point_powi.rs +++ b/tests/ui/floating_point_powi.rs @@ -14,6 +14,15 @@ fn main() { let _ = x + (y as f32).powi(2); let _ = (x.powi(2) + y).sqrt(); let _ = (x + y.powi(2)).sqrt(); + + let _ = (x - 1.0).powi(2) - y; + let _ = (x - 1.0).powi(2) - y + 3.0; + let _ = (x - 1.0).powi(2) - (y + 3.0); + let _ = x - (y + 1.0).powi(2); + let _ = x - (3.0 * y).powi(2); + let _ = x - (y + 1.0 + x).powi(2); + let _ = x - (y + 1.0 + 2.0).powi(2); + // Cases where the lint shouldn't be applied let _ = x.powi(2); let _ = x.powi(1 + 1); diff --git a/tests/ui/floating_point_powi.stderr b/tests/ui/floating_point_powi.stderr index 5df0de1fe..fdf6d0880 100644 --- a/tests/ui/floating_point_powi.stderr +++ b/tests/ui/floating_point_powi.stderr @@ -42,5 +42,47 @@ error: multiply and add expressions can be calculated more efficiently and accur LL | let _ = (x + y.powi(2)).sqrt(); | ^^^^^^^^^^^^^^^ help: consider using: `y.mul_add(y, x)` -error: aborting due to 7 previous errors +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_powi.rs:18:13 + | +LL | let _ = (x - 1.0).powi(2) - y; + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x - 1.0).mul_add(x - 1.0, -y)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_powi.rs:19:13 + | +LL | let _ = (x - 1.0).powi(2) - y + 3.0; + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x - 1.0).mul_add(x - 1.0, -y)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_powi.rs:20:13 + | +LL | let _ = (x - 1.0).powi(2) - (y + 3.0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x - 1.0).mul_add(x - 1.0, -(y + 3.0))` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_powi.rs:21:13 + | +LL | let _ = x - (y + 1.0).powi(2); + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(y + 1.0).mul_add(-(y + 1.0), x)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_powi.rs:22:13 + | +LL | let _ = x - (3.0 * y).powi(2); + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(3.0 * y).mul_add(-(3.0 * y), x)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_powi.rs:23:13 + | +LL | let _ = x - (y + 1.0 + x).powi(2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(y + 1.0 + x).mul_add(-(y + 1.0 + x), x)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_powi.rs:24:13 + | +LL | let _ = x - (y + 1.0 + 2.0).powi(2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(y + 1.0 + 2.0).mul_add(-(y + 1.0 + 2.0), x)` + +error: aborting due to 14 previous errors diff --git a/tests/ui/fn_null_check.rs b/tests/ui/fn_null_check.rs new file mode 100644 index 000000000..df5bc8420 --- /dev/null +++ b/tests/ui/fn_null_check.rs @@ -0,0 +1,21 @@ +#![allow(unused)] +#![warn(clippy::fn_null_check)] +#![allow(clippy::cmp_null)] +#![allow(clippy::ptr_eq)] +#![allow(clippy::zero_ptr)] + +pub const ZPTR: *const () = 0 as *const _; +pub const NOT_ZPTR: *const () = 1 as *const _; + +fn main() { + let fn_ptr = main; + + if (fn_ptr as *mut ()).is_null() {} + if (fn_ptr as *const u8).is_null() {} + if (fn_ptr as *const ()) == std::ptr::null() {} + if (fn_ptr as *const ()) == (0 as *const ()) {} + if (fn_ptr as *const ()) == ZPTR {} + + // no lint + if (fn_ptr as *const ()) == NOT_ZPTR {} +} diff --git a/tests/ui/fn_null_check.stderr b/tests/ui/fn_null_check.stderr new file mode 100644 index 000000000..660dd3239 --- /dev/null +++ b/tests/ui/fn_null_check.stderr @@ -0,0 +1,43 @@ +error: function pointer assumed to be nullable, even though it isn't + --> $DIR/fn_null_check.rs:13:8 + | +LL | if (fn_ptr as *mut ()).is_null() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: try wrapping your function pointer type in `Option` instead, and using `is_none` to check for null pointer value + = note: `-D clippy::fn-null-check` implied by `-D warnings` + +error: function pointer assumed to be nullable, even though it isn't + --> $DIR/fn_null_check.rs:14:8 + | +LL | if (fn_ptr as *const u8).is_null() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: try wrapping your function pointer type in `Option` instead, and using `is_none` to check for null pointer value + +error: function pointer assumed to be nullable, even though it isn't + --> $DIR/fn_null_check.rs:15:8 + | +LL | if (fn_ptr as *const ()) == std::ptr::null() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: try wrapping your function pointer type in `Option` instead, and using `is_none` to check for null pointer value + +error: function pointer assumed to be nullable, even though it isn't + --> $DIR/fn_null_check.rs:16:8 + | +LL | if (fn_ptr as *const ()) == (0 as *const ()) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: try wrapping your function pointer type in `Option` instead, and using `is_none` to check for null pointer value + +error: function pointer assumed to be nullable, even though it isn't + --> $DIR/fn_null_check.rs:17:8 + | +LL | if (fn_ptr as *const ()) == ZPTR {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: try wrapping your function pointer type in `Option` instead, and using `is_none` to check for null pointer value + +error: aborting due to 5 previous errors + diff --git a/tests/ui/manual_filter.fixed b/tests/ui/manual_filter.fixed index 3553291b8..ef6780dc9 100644 --- a/tests/ui/manual_filter.fixed +++ b/tests/ui/manual_filter.fixed @@ -116,4 +116,45 @@ fn main() { }, None => None, }; + + match Some(20) { + // Don't Lint, because `Some(3*x)` is not `None` + None => None, + Some(x) => { + if x > 0 { + Some(3 * x) + } else { + Some(x) + } + }, + }; + + // Don't lint: https://github.com/rust-lang/rust-clippy/issues/10088 + let result = if let Some(a) = maybe_some() { + if let Some(b) = maybe_some() { + Some(a + b) + } else { + Some(a) + } + } else { + None + }; + + let allowed_integers = vec![3, 4, 5, 6]; + // Don't lint, since there's a side effect in the else branch + match Some(21) { + Some(x) => { + if allowed_integers.contains(&x) { + Some(x) + } else { + println!("Invalid integer: {x:?}"); + None + } + }, + None => None, + }; +} + +fn maybe_some() -> Option { + Some(0) } diff --git a/tests/ui/manual_filter.rs b/tests/ui/manual_filter.rs index aa9f90f75..ea0ce8317 100644 --- a/tests/ui/manual_filter.rs +++ b/tests/ui/manual_filter.rs @@ -240,4 +240,45 @@ fn main() { }, None => None, }; + + match Some(20) { + // Don't Lint, because `Some(3*x)` is not `None` + None => None, + Some(x) => { + if x > 0 { + Some(3 * x) + } else { + Some(x) + } + }, + }; + + // Don't lint: https://github.com/rust-lang/rust-clippy/issues/10088 + let result = if let Some(a) = maybe_some() { + if let Some(b) = maybe_some() { + Some(a + b) + } else { + Some(a) + } + } else { + None + }; + + let allowed_integers = vec![3, 4, 5, 6]; + // Don't lint, since there's a side effect in the else branch + match Some(21) { + Some(x) => { + if allowed_integers.contains(&x) { + Some(x) + } else { + println!("Invalid integer: {x:?}"); + None + } + }, + None => None, + }; +} + +fn maybe_some() -> Option { + Some(0) } diff --git a/tests/ui/manual_retain.fixed b/tests/ui/manual_retain.fixed index e5ae3cf3e..8f25fea67 100644 --- a/tests/ui/manual_retain.fixed +++ b/tests/ui/manual_retain.fixed @@ -1,6 +1,6 @@ // run-rustfix #![warn(clippy::manual_retain)] -#![allow(unused)] +#![allow(unused, clippy::redundant_clone)] use std::collections::BTreeMap; use std::collections::BTreeSet; use std::collections::BinaryHeap; diff --git a/tests/ui/manual_retain.rs b/tests/ui/manual_retain.rs index 1021f15ed..e6b3995a6 100644 --- a/tests/ui/manual_retain.rs +++ b/tests/ui/manual_retain.rs @@ -1,6 +1,6 @@ // run-rustfix #![warn(clippy::manual_retain)] -#![allow(unused)] +#![allow(unused, clippy::redundant_clone)] use std::collections::BTreeMap; use std::collections::BTreeSet; use std::collections::BinaryHeap; diff --git a/tests/ui/match_single_binding.fixed b/tests/ui/match_single_binding.fixed index a6e315e47..6cfb6661a 100644 --- a/tests/ui/match_single_binding.fixed +++ b/tests/ui/match_single_binding.fixed @@ -133,3 +133,16 @@ fn issue_9575() { println!("Needs curlies"); }; } + +#[allow(dead_code)] +fn issue_9725(r: Option) { + let x = r; + match x { + Some(_) => { + println!("Some"); + }, + None => { + println!("None"); + }, + }; +} diff --git a/tests/ui/match_single_binding.rs b/tests/ui/match_single_binding.rs index cecbd703e..f188aeb5f 100644 --- a/tests/ui/match_single_binding.rs +++ b/tests/ui/match_single_binding.rs @@ -148,3 +148,17 @@ fn issue_9575() { _ => println!("Needs curlies"), }; } + +#[allow(dead_code)] +fn issue_9725(r: Option) { + match r { + x => match x { + Some(_) => { + println!("Some"); + }, + None => { + println!("None"); + }, + }, + }; +} diff --git a/tests/ui/match_single_binding.stderr b/tests/ui/match_single_binding.stderr index 2b9ec7ee7..e960d64ad 100644 --- a/tests/ui/match_single_binding.stderr +++ b/tests/ui/match_single_binding.stderr @@ -213,5 +213,30 @@ LL + println!("Needs curlies"); LL ~ }; | -error: aborting due to 14 previous errors +error: this match could be written as a `let` statement + --> $DIR/match_single_binding.rs:154:5 + | +LL | / match r { +LL | | x => match x { +LL | | Some(_) => { +LL | | println!("Some"); +... | +LL | | }, +LL | | }; + | |_____^ + | +help: consider using a `let` statement + | +LL ~ let x = r; +LL + match x { +LL + Some(_) => { +LL + println!("Some"); +LL + }, +LL + None => { +LL + println!("None"); +LL + }, +LL ~ }; + | + +error: aborting due to 15 previous errors diff --git a/tests/ui/match_wildcard_for_single_variants.fixed b/tests/ui/match_wildcard_for_single_variants.fixed index e675c183e..fc252cdd3 100644 --- a/tests/ui/match_wildcard_for_single_variants.fixed +++ b/tests/ui/match_wildcard_for_single_variants.fixed @@ -132,3 +132,25 @@ fn main() { } } } + +mod issue9993 { + enum Foo { + A(bool), + B, + } + + fn test() { + let _ = match Foo::A(true) { + _ if false => 0, + Foo::A(true) => 1, + Foo::A(false) => 2, + Foo::B => 3, + }; + + let _ = match Foo::B { + _ if false => 0, + Foo::A(_) => 1, + Foo::B => 2, + }; + } +} diff --git a/tests/ui/match_wildcard_for_single_variants.rs b/tests/ui/match_wildcard_for_single_variants.rs index 38c3ffc00..9a5c849e6 100644 --- a/tests/ui/match_wildcard_for_single_variants.rs +++ b/tests/ui/match_wildcard_for_single_variants.rs @@ -132,3 +132,25 @@ fn main() { } } } + +mod issue9993 { + enum Foo { + A(bool), + B, + } + + fn test() { + let _ = match Foo::A(true) { + _ if false => 0, + Foo::A(true) => 1, + Foo::A(false) => 2, + Foo::B => 3, + }; + + let _ = match Foo::B { + _ if false => 0, + Foo::A(_) => 1, + _ => 2, + }; + } +} diff --git a/tests/ui/match_wildcard_for_single_variants.stderr b/tests/ui/match_wildcard_for_single_variants.stderr index 34538dea8..6fa313dc9 100644 --- a/tests/ui/match_wildcard_for_single_variants.stderr +++ b/tests/ui/match_wildcard_for_single_variants.stderr @@ -48,5 +48,11 @@ error: wildcard matches only a single variant and will also match any future add LL | _ => (), | ^ help: try this: `Color::Blue` -error: aborting due to 8 previous errors +error: wildcard matches only a single variant and will also match any future added variants + --> $DIR/match_wildcard_for_single_variants.rs:153:13 + | +LL | _ => 2, + | ^ help: try this: `Foo::B` + +error: aborting due to 9 previous errors diff --git a/tests/ui/needless_borrow.fixed b/tests/ui/needless_borrow.fixed index 4cb7f6b68..31e1cb6c3 100644 --- a/tests/ui/needless_borrow.fixed +++ b/tests/ui/needless_borrow.fixed @@ -1,5 +1,5 @@ // run-rustfix -#![feature(lint_reasons)] +#![feature(custom_inner_attributes, lint_reasons, rustc_private)] #![allow( unused, clippy::uninlined_format_args, @@ -491,3 +491,14 @@ mod issue_9782_method_variant { S.foo::<&[u8; 100]>(&a); } } + +extern crate rustc_lint; +extern crate rustc_span; + +#[allow(dead_code)] +mod span_lint { + use rustc_lint::{LateContext, Lint, LintContext}; + fn foo(cx: &LateContext<'_>, lint: &'static Lint) { + cx.struct_span_lint(lint, rustc_span::Span::default(), "", |diag| diag.note(String::new())); + } +} diff --git a/tests/ui/needless_borrow.rs b/tests/ui/needless_borrow.rs index 9a01190ed..55c2738fc 100644 --- a/tests/ui/needless_borrow.rs +++ b/tests/ui/needless_borrow.rs @@ -1,5 +1,5 @@ // run-rustfix -#![feature(lint_reasons)] +#![feature(custom_inner_attributes, lint_reasons, rustc_private)] #![allow( unused, clippy::uninlined_format_args, @@ -491,3 +491,14 @@ mod issue_9782_method_variant { S.foo::<&[u8; 100]>(&a); } } + +extern crate rustc_lint; +extern crate rustc_span; + +#[allow(dead_code)] +mod span_lint { + use rustc_lint::{LateContext, Lint, LintContext}; + fn foo(cx: &LateContext<'_>, lint: &'static Lint) { + cx.struct_span_lint(lint, rustc_span::Span::default(), "", |diag| diag.note(&String::new())); + } +} diff --git a/tests/ui/needless_borrow.stderr b/tests/ui/needless_borrow.stderr index d26c31712..98a48d683 100644 --- a/tests/ui/needless_borrow.stderr +++ b/tests/ui/needless_borrow.stderr @@ -216,5 +216,11 @@ error: the borrowed expression implements the required traits LL | foo(&a); | ^^ help: change this to: `a` -error: aborting due to 36 previous errors +error: the borrowed expression implements the required traits + --> $DIR/needless_borrow.rs:502:85 + | +LL | cx.struct_span_lint(lint, rustc_span::Span::default(), "", |diag| diag.note(&String::new())); + | ^^^^^^^^^^^^^^ help: change this to: `String::new()` + +error: aborting due to 37 previous errors diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed index 4386aaec4..d451be1f3 100644 --- a/tests/ui/needless_return.fixed +++ b/tests/ui/needless_return.fixed @@ -1,6 +1,7 @@ // run-rustfix #![feature(lint_reasons)] +#![feature(yeet_expr)] #![allow(unused)] #![allow( clippy::if_same_then_else, @@ -272,4 +273,8 @@ mod issue9416 { } } +fn issue9947() -> Result<(), String> { + do yeet "hello"; +} + fn main() {} diff --git a/tests/ui/needless_return.rs b/tests/ui/needless_return.rs index 666dc54b7..e1a1bea2c 100644 --- a/tests/ui/needless_return.rs +++ b/tests/ui/needless_return.rs @@ -1,6 +1,7 @@ // run-rustfix #![feature(lint_reasons)] +#![feature(yeet_expr)] #![allow(unused)] #![allow( clippy::if_same_then_else, @@ -282,4 +283,8 @@ mod issue9416 { } } +fn issue9947() -> Result<(), String> { + do yeet "hello"; +} + fn main() {} diff --git a/tests/ui/needless_return.stderr b/tests/ui/needless_return.stderr index a8b5d86cd..ca2253e65 100644 --- a/tests/ui/needless_return.stderr +++ b/tests/ui/needless_return.stderr @@ -1,5 +1,5 @@ error: unneeded `return` statement - --> $DIR/needless_return.rs:26:5 + --> $DIR/needless_return.rs:27:5 | LL | return true; | ^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:30:5 + --> $DIR/needless_return.rs:31:5 | LL | return true; | ^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:35:9 + --> $DIR/needless_return.rs:36:9 | LL | return true; | ^^^^^^^^^^^ @@ -24,7 +24,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:37:9 + --> $DIR/needless_return.rs:38:9 | LL | return false; | ^^^^^^^^^^^^ @@ -32,7 +32,7 @@ LL | return false; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:43:17 + --> $DIR/needless_return.rs:44:17 | LL | true => return false, | ^^^^^^^^^^^^ @@ -40,7 +40,7 @@ LL | true => return false, = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:45:13 + --> $DIR/needless_return.rs:46:13 | LL | return true; | ^^^^^^^^^^^ @@ -48,7 +48,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:52:9 + --> $DIR/needless_return.rs:53:9 | LL | return true; | ^^^^^^^^^^^ @@ -56,7 +56,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:54:16 + --> $DIR/needless_return.rs:55:16 | LL | let _ = || return true; | ^^^^^^^^^^^ @@ -64,7 +64,7 @@ LL | let _ = || return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:58:5 + --> $DIR/needless_return.rs:59:5 | LL | return the_answer!(); | ^^^^^^^^^^^^^^^^^^^^ @@ -72,7 +72,7 @@ LL | return the_answer!(); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:61:21 + --> $DIR/needless_return.rs:62:21 | LL | fn test_void_fun() { | _____________________^ @@ -82,7 +82,7 @@ LL | | return; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:66:11 + --> $DIR/needless_return.rs:67:11 | LL | if b { | ___________^ @@ -92,7 +92,7 @@ LL | | return; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:68:13 + --> $DIR/needless_return.rs:69:13 | LL | } else { | _____________^ @@ -102,7 +102,7 @@ LL | | return; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:76:14 + --> $DIR/needless_return.rs:77:14 | LL | _ => return, | ^^^^^^ @@ -110,7 +110,7 @@ LL | _ => return, = help: replace `return` with a unit value error: unneeded `return` statement - --> $DIR/needless_return.rs:84:24 + --> $DIR/needless_return.rs:85:24 | LL | let _ = 42; | ________________________^ @@ -120,7 +120,7 @@ LL | | return; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:87:14 + --> $DIR/needless_return.rs:88:14 | LL | _ => return, | ^^^^^^ @@ -128,7 +128,7 @@ LL | _ => return, = help: replace `return` with a unit value error: unneeded `return` statement - --> $DIR/needless_return.rs:100:9 + --> $DIR/needless_return.rs:101:9 | LL | return String::from("test"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -136,7 +136,7 @@ LL | return String::from("test"); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:102:9 + --> $DIR/needless_return.rs:103:9 | LL | return String::new(); | ^^^^^^^^^^^^^^^^^^^^ @@ -144,7 +144,7 @@ LL | return String::new(); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:124:32 + --> $DIR/needless_return.rs:125:32 | LL | bar.unwrap_or_else(|_| return) | ^^^^^^ @@ -152,7 +152,7 @@ LL | bar.unwrap_or_else(|_| return) = help: replace `return` with an empty block error: unneeded `return` statement - --> $DIR/needless_return.rs:128:21 + --> $DIR/needless_return.rs:129:21 | LL | let _ = || { | _____________________^ @@ -162,7 +162,7 @@ LL | | return; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:131:20 + --> $DIR/needless_return.rs:132:20 | LL | let _ = || return; | ^^^^^^ @@ -170,7 +170,7 @@ LL | let _ = || return; = help: replace `return` with an empty block error: unneeded `return` statement - --> $DIR/needless_return.rs:137:32 + --> $DIR/needless_return.rs:138:32 | LL | res.unwrap_or_else(|_| return Foo) | ^^^^^^^^^^ @@ -178,7 +178,7 @@ LL | res.unwrap_or_else(|_| return Foo) = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:146:5 + --> $DIR/needless_return.rs:147:5 | LL | return true; | ^^^^^^^^^^^ @@ -186,7 +186,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:150:5 + --> $DIR/needless_return.rs:151:5 | LL | return true; | ^^^^^^^^^^^ @@ -194,7 +194,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:155:9 + --> $DIR/needless_return.rs:156:9 | LL | return true; | ^^^^^^^^^^^ @@ -202,7 +202,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:157:9 + --> $DIR/needless_return.rs:158:9 | LL | return false; | ^^^^^^^^^^^^ @@ -210,7 +210,7 @@ LL | return false; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:163:17 + --> $DIR/needless_return.rs:164:17 | LL | true => return false, | ^^^^^^^^^^^^ @@ -218,7 +218,7 @@ LL | true => return false, = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:165:13 + --> $DIR/needless_return.rs:166:13 | LL | return true; | ^^^^^^^^^^^ @@ -226,7 +226,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:172:9 + --> $DIR/needless_return.rs:173:9 | LL | return true; | ^^^^^^^^^^^ @@ -234,7 +234,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:174:16 + --> $DIR/needless_return.rs:175:16 | LL | let _ = || return true; | ^^^^^^^^^^^ @@ -242,7 +242,7 @@ LL | let _ = || return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:178:5 + --> $DIR/needless_return.rs:179:5 | LL | return the_answer!(); | ^^^^^^^^^^^^^^^^^^^^ @@ -250,7 +250,7 @@ LL | return the_answer!(); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:181:33 + --> $DIR/needless_return.rs:182:33 | LL | async fn async_test_void_fun() { | _________________________________^ @@ -260,7 +260,7 @@ LL | | return; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:186:11 + --> $DIR/needless_return.rs:187:11 | LL | if b { | ___________^ @@ -270,7 +270,7 @@ LL | | return; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:188:13 + --> $DIR/needless_return.rs:189:13 | LL | } else { | _____________^ @@ -280,7 +280,7 @@ LL | | return; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:196:14 + --> $DIR/needless_return.rs:197:14 | LL | _ => return, | ^^^^^^ @@ -288,7 +288,7 @@ LL | _ => return, = help: replace `return` with a unit value error: unneeded `return` statement - --> $DIR/needless_return.rs:209:9 + --> $DIR/needless_return.rs:210:9 | LL | return String::from("test"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -296,7 +296,7 @@ LL | return String::from("test"); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:211:9 + --> $DIR/needless_return.rs:212:9 | LL | return String::new(); | ^^^^^^^^^^^^^^^^^^^^ @@ -304,7 +304,7 @@ LL | return String::new(); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:227:5 + --> $DIR/needless_return.rs:228:5 | LL | return format!("Hello {}", "world!"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -312,7 +312,7 @@ LL | return format!("Hello {}", "world!"); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:238:9 + --> $DIR/needless_return.rs:239:9 | LL | return true; | ^^^^^^^^^^^ @@ -320,7 +320,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:240:9 + --> $DIR/needless_return.rs:241:9 | LL | return false; | ^^^^^^^^^^^^ @@ -328,7 +328,7 @@ LL | return false; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:247:13 + --> $DIR/needless_return.rs:248:13 | LL | return 10; | ^^^^^^^^^ @@ -336,7 +336,7 @@ LL | return 10; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:250:13 + --> $DIR/needless_return.rs:251:13 | LL | return 100; | ^^^^^^^^^^ @@ -344,7 +344,7 @@ LL | return 100; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:258:9 + --> $DIR/needless_return.rs:259:9 | LL | return 0; | ^^^^^^^^ @@ -352,7 +352,7 @@ LL | return 0; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:265:13 + --> $DIR/needless_return.rs:266:13 | LL | return *(x as *const isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -360,7 +360,7 @@ LL | return *(x as *const isize); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:267:13 + --> $DIR/needless_return.rs:268:13 | LL | return !*(x as *const isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -368,7 +368,7 @@ LL | return !*(x as *const isize); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:274:20 + --> $DIR/needless_return.rs:275:20 | LL | let _ = 42; | ____________________^ @@ -379,7 +379,7 @@ LL | | return; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:281:20 + --> $DIR/needless_return.rs:282:20 | LL | let _ = 42; return; | ^^^^^^^ diff --git a/tests/ui/permissions_set_readonly_false.rs b/tests/ui/permissions_set_readonly_false.rs new file mode 100644 index 000000000..28c00d100 --- /dev/null +++ b/tests/ui/permissions_set_readonly_false.rs @@ -0,0 +1,29 @@ +#![allow(unused)] +#![warn(clippy::permissions_set_readonly_false)] + +use std::fs::File; + +struct A; + +impl A { + pub fn set_readonly(&mut self, b: bool) {} +} + +fn set_readonly(b: bool) {} + +fn main() { + let f = File::create("foo.txt").unwrap(); + let metadata = f.metadata().unwrap(); + let mut permissions = metadata.permissions(); + // lint here + permissions.set_readonly(false); + // no lint + permissions.set_readonly(true); + + let mut a = A; + // no lint here - a is not of type std::fs::Permissions + a.set_readonly(false); + + // no lint here - plain function + set_readonly(false); +} diff --git a/tests/ui/permissions_set_readonly_false.stderr b/tests/ui/permissions_set_readonly_false.stderr new file mode 100644 index 000000000..e7a8ee6cb --- /dev/null +++ b/tests/ui/permissions_set_readonly_false.stderr @@ -0,0 +1,13 @@ +error: call to `set_readonly` with argument `false` + --> $DIR/permissions_set_readonly_false.rs:19:5 + | +LL | permissions.set_readonly(false); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: on Unix platforms this results in the file being world writable + = help: you can set the desired permissions using `PermissionsExt`. For more information, see + https://doc.rust-lang.org/std/os/unix/fs/trait.PermissionsExt.html + = note: `-D clippy::permissions-set-readonly-false` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui/redundant_clone.fixed b/tests/ui/redundant_clone.fixed index 00b427450..a157b6a6f 100644 --- a/tests/ui/redundant_clone.fixed +++ b/tests/ui/redundant_clone.fixed @@ -239,3 +239,9 @@ fn false_negative_5707() { let _z = x.clone(); // pr 7346 can't lint on `x` drop(y); } + +#[allow(unused, clippy::manual_retain)] +fn possible_borrower_improvements() { + let mut s = String::from("foobar"); + s = s.chars().filter(|&c| c != 'o').collect(); +} diff --git a/tests/ui/redundant_clone.rs b/tests/ui/redundant_clone.rs index f899127db..430672e8b 100644 --- a/tests/ui/redundant_clone.rs +++ b/tests/ui/redundant_clone.rs @@ -239,3 +239,9 @@ fn false_negative_5707() { let _z = x.clone(); // pr 7346 can't lint on `x` drop(y); } + +#[allow(unused, clippy::manual_retain)] +fn possible_borrower_improvements() { + let mut s = String::from("foobar"); + s = s.chars().filter(|&c| c != 'o').to_owned().collect(); +} diff --git a/tests/ui/redundant_clone.stderr b/tests/ui/redundant_clone.stderr index 782590034..1bacc2c76 100644 --- a/tests/ui/redundant_clone.stderr +++ b/tests/ui/redundant_clone.stderr @@ -179,5 +179,17 @@ note: this value is dropped without further use LL | foo(&x.clone(), move || { | ^ -error: aborting due to 15 previous errors +error: redundant clone + --> $DIR/redundant_clone.rs:246:40 + | +LL | s = s.chars().filter(|&c| c != 'o').to_owned().collect(); + | ^^^^^^^^^^^ help: remove this + | +note: this value is dropped without further use + --> $DIR/redundant_clone.rs:246:9 + | +LL | s = s.chars().filter(|&c| c != 'o').to_owned().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 16 previous errors diff --git a/tests/ui/size_of_ref.rs b/tests/ui/size_of_ref.rs new file mode 100644 index 000000000..1e83ab829 --- /dev/null +++ b/tests/ui/size_of_ref.rs @@ -0,0 +1,27 @@ +#![allow(unused)] +#![warn(clippy::size_of_ref)] + +use std::mem::size_of_val; + +fn main() { + let x = 5; + let y = &x; + + size_of_val(&x); // no lint + size_of_val(y); // no lint + + size_of_val(&&x); + size_of_val(&y); +} + +struct S { + field: u32, + data: Vec, +} + +impl S { + /// Get size of object including `self`, in bytes. + pub fn size(&self) -> usize { + std::mem::size_of_val(&self) + (std::mem::size_of::() * self.data.capacity()) + } +} diff --git a/tests/ui/size_of_ref.stderr b/tests/ui/size_of_ref.stderr new file mode 100644 index 000000000..d4c13ac32 --- /dev/null +++ b/tests/ui/size_of_ref.stderr @@ -0,0 +1,27 @@ +error: argument to `std::mem::size_of_val()` is a reference to a reference + --> $DIR/size_of_ref.rs:13:5 + | +LL | size_of_val(&&x); + | ^^^^^^^^^^^^^^^^ + | + = help: dereference the argument to `std::mem::size_of_val()` to get the size of the value instead of the size of the reference-type + = note: `-D clippy::size-of-ref` implied by `-D warnings` + +error: argument to `std::mem::size_of_val()` is a reference to a reference + --> $DIR/size_of_ref.rs:14:5 + | +LL | size_of_val(&y); + | ^^^^^^^^^^^^^^^ + | + = help: dereference the argument to `std::mem::size_of_val()` to get the size of the value instead of the size of the reference-type + +error: argument to `std::mem::size_of_val()` is a reference to a reference + --> $DIR/size_of_ref.rs:25:9 + | +LL | std::mem::size_of_val(&self) + (std::mem::size_of::() * self.data.capacity()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: dereference the argument to `std::mem::size_of_val()` to get the size of the value instead of the size of the reference-type + +error: aborting due to 3 previous errors + diff --git a/tests/ui/transmute_null_to_fn.rs b/tests/ui/transmute_null_to_fn.rs new file mode 100644 index 000000000..b3ea3d903 --- /dev/null +++ b/tests/ui/transmute_null_to_fn.rs @@ -0,0 +1,28 @@ +#![allow(dead_code)] +#![warn(clippy::transmute_null_to_fn)] +#![allow(clippy::zero_ptr)] + +// Easy to lint because these only span one line. +fn one_liners() { + unsafe { + let _: fn() = std::mem::transmute(0 as *const ()); + let _: fn() = std::mem::transmute(std::ptr::null::<()>()); + } +} + +pub const ZPTR: *const usize = 0 as *const _; +pub const NOT_ZPTR: *const usize = 1 as *const _; + +fn transmute_const() { + unsafe { + // Should raise a lint. + let _: fn() = std::mem::transmute(ZPTR); + // Should NOT raise a lint. + let _: fn() = std::mem::transmute(NOT_ZPTR); + } +} + +fn main() { + one_liners(); + transmute_const(); +} diff --git a/tests/ui/transmute_null_to_fn.stderr b/tests/ui/transmute_null_to_fn.stderr new file mode 100644 index 000000000..f0c65497d --- /dev/null +++ b/tests/ui/transmute_null_to_fn.stderr @@ -0,0 +1,27 @@ +error: transmuting a known null pointer into a function pointer + --> $DIR/transmute_null_to_fn.rs:8:23 + | +LL | let _: fn() = std::mem::transmute(0 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 + = note: `-D clippy::transmute-null-to-fn` implied by `-D warnings` + +error: transmuting a known null pointer into a function pointer + --> $DIR/transmute_null_to_fn.rs:9:23 + | +LL | let _: fn() = std::mem::transmute(std::ptr::null::<()>()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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:19:23 + | +LL | let _: fn() = std::mem::transmute(ZPTR); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ 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 3 previous errors + diff --git a/tests/ui/useless_conversion.fixed b/tests/ui/useless_conversion.fixed index 70ff08f36..94b206d8e 100644 --- a/tests/ui/useless_conversion.fixed +++ b/tests/ui/useless_conversion.fixed @@ -33,12 +33,71 @@ fn test_issue_3913() -> Result<(), std::io::Error> { Ok(()) } -fn test_issue_5833() -> Result<(), ()> { +fn dont_lint_into_iter_on_immutable_local_implementing_iterator_in_expr() { let text = "foo\r\nbar\n\nbaz\n"; let lines = text.lines(); if Some("ok") == lines.into_iter().next() {} +} - Ok(()) +fn lint_into_iter_on_mutable_local_implementing_iterator_in_expr() { + let text = "foo\r\nbar\n\nbaz\n"; + let mut lines = text.lines(); + if Some("ok") == lines.next() {} +} + +fn lint_into_iter_on_expr_implementing_iterator() { + let text = "foo\r\nbar\n\nbaz\n"; + let mut lines = text.lines(); + if Some("ok") == lines.next() {} +} + +fn lint_into_iter_on_expr_implementing_iterator_2() { + let text = "foo\r\nbar\n\nbaz\n"; + if Some("ok") == text.lines().next() {} +} + +#[allow(const_item_mutation)] +fn lint_into_iter_on_const_implementing_iterator() { + const NUMBERS: std::ops::Range = 0..10; + let _ = NUMBERS.next(); +} + +fn lint_into_iter_on_const_implementing_iterator_2() { + const NUMBERS: std::ops::Range = 0..10; + let mut n = NUMBERS; + n.next(); +} + +#[derive(Clone, Copy)] +struct CopiableCounter { + counter: u32, +} + +impl Iterator for CopiableCounter { + type Item = u32; + + fn next(&mut self) -> Option { + self.counter = self.counter.wrapping_add(1); + Some(self.counter) + } +} + +fn dont_lint_into_iter_on_copy_iter() { + let mut c = CopiableCounter { counter: 0 }; + assert_eq!(c.into_iter().next(), Some(1)); + assert_eq!(c.into_iter().next(), Some(1)); + assert_eq!(c.next(), Some(1)); + assert_eq!(c.next(), Some(2)); +} + +fn dont_lint_into_iter_on_static_copy_iter() { + static mut C: CopiableCounter = CopiableCounter { counter: 0 }; + unsafe { + assert_eq!(C.into_iter().next(), Some(1)); + assert_eq!(C.into_iter().next(), Some(1)); + assert_eq!(C.next(), Some(1)); + assert_eq!(C.next(), Some(2)); + } } fn main() { @@ -46,7 +105,15 @@ fn main() { test_generic2::(10i32); test_questionmark().unwrap(); test_issue_3913().unwrap(); - test_issue_5833().unwrap(); + + dont_lint_into_iter_on_immutable_local_implementing_iterator_in_expr(); + lint_into_iter_on_mutable_local_implementing_iterator_in_expr(); + lint_into_iter_on_expr_implementing_iterator(); + lint_into_iter_on_expr_implementing_iterator_2(); + lint_into_iter_on_const_implementing_iterator(); + lint_into_iter_on_const_implementing_iterator_2(); + dont_lint_into_iter_on_copy_iter(); + dont_lint_into_iter_on_static_copy_iter(); let _: String = "foo".into(); let _: String = From::from("foo"); diff --git a/tests/ui/useless_conversion.rs b/tests/ui/useless_conversion.rs index f2444a8f4..c7ae92794 100644 --- a/tests/ui/useless_conversion.rs +++ b/tests/ui/useless_conversion.rs @@ -33,12 +33,71 @@ fn test_issue_3913() -> Result<(), std::io::Error> { Ok(()) } -fn test_issue_5833() -> Result<(), ()> { +fn dont_lint_into_iter_on_immutable_local_implementing_iterator_in_expr() { let text = "foo\r\nbar\n\nbaz\n"; let lines = text.lines(); if Some("ok") == lines.into_iter().next() {} +} - Ok(()) +fn lint_into_iter_on_mutable_local_implementing_iterator_in_expr() { + let text = "foo\r\nbar\n\nbaz\n"; + let mut lines = text.lines(); + if Some("ok") == lines.into_iter().next() {} +} + +fn lint_into_iter_on_expr_implementing_iterator() { + let text = "foo\r\nbar\n\nbaz\n"; + let mut lines = text.lines().into_iter(); + if Some("ok") == lines.next() {} +} + +fn lint_into_iter_on_expr_implementing_iterator_2() { + let text = "foo\r\nbar\n\nbaz\n"; + if Some("ok") == text.lines().into_iter().next() {} +} + +#[allow(const_item_mutation)] +fn lint_into_iter_on_const_implementing_iterator() { + const NUMBERS: std::ops::Range = 0..10; + let _ = NUMBERS.into_iter().next(); +} + +fn lint_into_iter_on_const_implementing_iterator_2() { + const NUMBERS: std::ops::Range = 0..10; + let mut n = NUMBERS.into_iter(); + n.next(); +} + +#[derive(Clone, Copy)] +struct CopiableCounter { + counter: u32, +} + +impl Iterator for CopiableCounter { + type Item = u32; + + fn next(&mut self) -> Option { + self.counter = self.counter.wrapping_add(1); + Some(self.counter) + } +} + +fn dont_lint_into_iter_on_copy_iter() { + let mut c = CopiableCounter { counter: 0 }; + assert_eq!(c.into_iter().next(), Some(1)); + assert_eq!(c.into_iter().next(), Some(1)); + assert_eq!(c.next(), Some(1)); + assert_eq!(c.next(), Some(2)); +} + +fn dont_lint_into_iter_on_static_copy_iter() { + static mut C: CopiableCounter = CopiableCounter { counter: 0 }; + unsafe { + assert_eq!(C.into_iter().next(), Some(1)); + assert_eq!(C.into_iter().next(), Some(1)); + assert_eq!(C.next(), Some(1)); + assert_eq!(C.next(), Some(2)); + } } fn main() { @@ -46,7 +105,15 @@ fn main() { test_generic2::(10i32); test_questionmark().unwrap(); test_issue_3913().unwrap(); - test_issue_5833().unwrap(); + + dont_lint_into_iter_on_immutable_local_implementing_iterator_in_expr(); + lint_into_iter_on_mutable_local_implementing_iterator_in_expr(); + lint_into_iter_on_expr_implementing_iterator(); + lint_into_iter_on_expr_implementing_iterator_2(); + lint_into_iter_on_const_implementing_iterator(); + lint_into_iter_on_const_implementing_iterator_2(); + dont_lint_into_iter_on_copy_iter(); + dont_lint_into_iter_on_static_copy_iter(); let _: String = "foo".into(); let _: String = From::from("foo"); diff --git a/tests/ui/useless_conversion.stderr b/tests/ui/useless_conversion.stderr index 65ee3807f..be067c684 100644 --- a/tests/ui/useless_conversion.stderr +++ b/tests/ui/useless_conversion.stderr @@ -22,71 +22,101 @@ error: useless conversion to the same type: `i32` LL | let _: i32 = 0i32.into(); | ^^^^^^^^^^^ help: consider removing `.into()`: `0i32` +error: useless conversion to the same type: `std::str::Lines<'_>` + --> $DIR/useless_conversion.rs:45:22 + | +LL | if Some("ok") == lines.into_iter().next() {} + | ^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `lines` + +error: useless conversion to the same type: `std::str::Lines<'_>` + --> $DIR/useless_conversion.rs:50:21 + | +LL | let mut lines = text.lines().into_iter(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `text.lines()` + +error: useless conversion to the same type: `std::str::Lines<'_>` + --> $DIR/useless_conversion.rs:56:22 + | +LL | if Some("ok") == text.lines().into_iter().next() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `text.lines()` + +error: useless conversion to the same type: `std::ops::Range` + --> $DIR/useless_conversion.rs:62:13 + | +LL | let _ = NUMBERS.into_iter().next(); + | ^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `NUMBERS` + +error: useless conversion to the same type: `std::ops::Range` + --> $DIR/useless_conversion.rs:67:17 + | +LL | let mut n = NUMBERS.into_iter(); + | ^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `NUMBERS` + error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion.rs:61:21 + --> $DIR/useless_conversion.rs:128:21 | LL | let _: String = "foo".to_string().into(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `"foo".to_string()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion.rs:62:21 + --> $DIR/useless_conversion.rs:129:21 | LL | let _: String = From::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `From::from()`: `"foo".to_string()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion.rs:63:13 + --> $DIR/useless_conversion.rs:130:13 | LL | let _ = String::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `"foo".to_string()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion.rs:64:13 + --> $DIR/useless_conversion.rs:131:13 | LL | let _ = String::from(format!("A: {:04}", 123)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `format!("A: {:04}", 123)` error: useless conversion to the same type: `std::str::Lines<'_>` - --> $DIR/useless_conversion.rs:65:13 + --> $DIR/useless_conversion.rs:132:13 | LL | let _ = "".lines().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `"".lines()` error: useless conversion to the same type: `std::vec::IntoIter` - --> $DIR/useless_conversion.rs:66:13 + --> $DIR/useless_conversion.rs:133:13 | LL | let _ = vec![1, 2, 3].into_iter().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![1, 2, 3].into_iter()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion.rs:67:21 + --> $DIR/useless_conversion.rs:134:21 | LL | let _: String = format!("Hello {}", "world").into(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `format!("Hello {}", "world")` error: useless conversion to the same type: `i32` - --> $DIR/useless_conversion.rs:72:13 + --> $DIR/useless_conversion.rs:139:13 | LL | let _ = i32::from(a + b) * 3; | ^^^^^^^^^^^^^^^^ help: consider removing `i32::from()`: `(a + b)` error: useless conversion to the same type: `Foo<'a'>` - --> $DIR/useless_conversion.rs:78:23 + --> $DIR/useless_conversion.rs:145:23 | LL | let _: Foo<'a'> = s2.into(); | ^^^^^^^^^ help: consider removing `.into()`: `s2` error: useless conversion to the same type: `Foo<'a'>` - --> $DIR/useless_conversion.rs:80:13 + --> $DIR/useless_conversion.rs:147:13 | LL | let _ = Foo::<'a'>::from(s3); | ^^^^^^^^^^^^^^^^^^^^ help: consider removing `Foo::<'a'>::from()`: `s3` error: useless conversion to the same type: `std::vec::IntoIter>` - --> $DIR/useless_conversion.rs:82:13 + --> $DIR/useless_conversion.rs:149:13 | LL | let _ = vec![s4, s4, s4].into_iter().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![s4, s4, s4].into_iter()` -error: aborting due to 14 previous errors +error: aborting due to 19 previous errors diff --git a/tests/ui/wildcard_imports.fixed b/tests/ui/wildcard_imports.fixed index ef55f1c31..0baec6f0b 100644 --- a/tests/ui/wildcard_imports.fixed +++ b/tests/ui/wildcard_imports.fixed @@ -5,7 +5,6 @@ // the 2015 edition here is needed because edition 2018 changed the module system // (see https://doc.rust-lang.org/edition-guide/rust-2018/path-changes.html) which means the lint // no longer detects some of the cases starting with Rust 2018. -// FIXME: We should likely add another edition 2021 test case for this lint #![warn(clippy::wildcard_imports)] #![allow(unused, clippy::unnecessary_wraps, clippy::let_unit_value)] diff --git a/tests/ui/wildcard_imports.rs b/tests/ui/wildcard_imports.rs index b81285142..db591d56a 100644 --- a/tests/ui/wildcard_imports.rs +++ b/tests/ui/wildcard_imports.rs @@ -5,7 +5,6 @@ // the 2015 edition here is needed because edition 2018 changed the module system // (see https://doc.rust-lang.org/edition-guide/rust-2018/path-changes.html) which means the lint // no longer detects some of the cases starting with Rust 2018. -// FIXME: We should likely add another edition 2021 test case for this lint #![warn(clippy::wildcard_imports)] #![allow(unused, clippy::unnecessary_wraps, clippy::let_unit_value)] diff --git a/tests/ui/wildcard_imports.stderr b/tests/ui/wildcard_imports.stderr index 626c1754f..6b469cdfc 100644 --- a/tests/ui/wildcard_imports.stderr +++ b/tests/ui/wildcard_imports.stderr @@ -1,5 +1,5 @@ error: usage of wildcard import - --> $DIR/wildcard_imports.rs:16:5 + --> $DIR/wildcard_imports.rs:15:5 | LL | use crate::fn_mod::*; | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` @@ -7,85 +7,85 @@ LL | use crate::fn_mod::*; = note: `-D clippy::wildcard-imports` implied by `-D warnings` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:17:5 + --> $DIR/wildcard_imports.rs:16:5 | LL | use crate::mod_mod::*; | ^^^^^^^^^^^^^^^^^ help: try: `crate::mod_mod::inner_mod` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:18:5 + --> $DIR/wildcard_imports.rs:17:5 | LL | use crate::multi_fn_mod::*; | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:20:5 + --> $DIR/wildcard_imports.rs:19:5 | LL | use crate::struct_mod::*; | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::struct_mod::{A, inner_struct_mod}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:24:5 + --> $DIR/wildcard_imports.rs:23:5 | LL | use wildcard_imports_helper::inner::inner_for_self_import::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:25:5 + --> $DIR/wildcard_imports.rs:24:5 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:96:13 + --> $DIR/wildcard_imports.rs:95:13 | LL | use crate::fn_mod::*; | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:102:75 + --> $DIR/wildcard_imports.rs:101:75 | LL | use wildcard_imports_helper::inner::inner_for_self_import::{self, *}; | ^ help: try: `inner_extern_foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:103:13 + --> $DIR/wildcard_imports.rs:102:13 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:114:20 + --> $DIR/wildcard_imports.rs:113:20 | LL | use self::{inner::*, inner2::*}; | ^^^^^^^^ help: try: `inner::inner_foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:114:30 + --> $DIR/wildcard_imports.rs:113:30 | LL | use self::{inner::*, inner2::*}; | ^^^^^^^^^ help: try: `inner2::inner_bar` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:121:13 + --> $DIR/wildcard_imports.rs:120:13 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:150:9 + --> $DIR/wildcard_imports.rs:149:9 | LL | use crate::in_fn_test::*; | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:159:9 + --> $DIR/wildcard_imports.rs:158:9 | LL | use crate:: in_fn_test:: * ; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:160:9 + --> $DIR/wildcard_imports.rs:159:9 | LL | use crate:: fn_mod:: | _________^ @@ -93,37 +93,37 @@ LL | | *; | |_________^ help: try: `crate:: fn_mod::foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:171:13 + --> $DIR/wildcard_imports.rs:170:13 | LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:206:17 + --> $DIR/wildcard_imports.rs:205:17 | LL | use super::*; | ^^^^^^^^ help: try: `super::insidefoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:214:13 + --> $DIR/wildcard_imports.rs:213:13 | LL | use super_imports::*; | ^^^^^^^^^^^^^^^^ help: try: `super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:223:17 + --> $DIR/wildcard_imports.rs:222:17 | LL | use super::super::*; | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:232:13 + --> $DIR/wildcard_imports.rs:231:13 | LL | use super::super::super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:240:13 + --> $DIR/wildcard_imports.rs:239:13 | LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` diff --git a/tests/ui/wildcard_imports_2021.edition2018.fixed b/tests/ui/wildcard_imports_2021.edition2018.fixed new file mode 100644 index 000000000..6d534a10e --- /dev/null +++ b/tests/ui/wildcard_imports_2021.edition2018.fixed @@ -0,0 +1,240 @@ +// revisions: edition2018 edition2021 +//[edition2018] edition:2018 +//[edition2021] edition:2021 +// run-rustfix +// aux-build:wildcard_imports_helper.rs + +#![warn(clippy::wildcard_imports)] +#![allow(unused, clippy::unnecessary_wraps, clippy::let_unit_value)] +#![warn(unused_imports)] + +extern crate wildcard_imports_helper; + +use crate::fn_mod::foo; +use crate::mod_mod::inner_mod; +use crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}; +use crate::struct_mod::{A, inner_struct_mod}; + +#[allow(unused_imports)] +use wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar; +use wildcard_imports_helper::prelude::v1::*; +use wildcard_imports_helper::{ExternA, extern_foo}; + +use std::io::prelude::*; + +struct ReadFoo; + +impl Read for ReadFoo { + fn read(&mut self, _buf: &mut [u8]) -> std::io::Result { + Ok(0) + } +} + +mod fn_mod { + pub fn foo() {} +} + +mod mod_mod { + pub mod inner_mod { + pub fn foo() {} + } +} + +mod multi_fn_mod { + pub fn multi_foo() {} + pub fn multi_bar() {} + pub fn multi_baz() {} + pub mod multi_inner_mod { + pub fn foo() {} + } +} + +mod struct_mod { + pub struct A; + pub struct B; + pub mod inner_struct_mod { + pub struct C; + } + + #[macro_export] + macro_rules! double_struct_import_test { + () => { + let _ = A; + }; + } +} + +fn main() { + foo(); + multi_foo(); + multi_bar(); + multi_inner_mod::foo(); + inner_mod::foo(); + extern_foo(); + inner_extern_bar(); + + let _ = A; + let _ = inner_struct_mod::C; + let _ = ExternA; + let _ = PreludeModAnywhere; + + double_struct_import_test!(); + double_struct_import_test!(); +} + +mod in_fn_test { + pub use self::inner_exported::*; + #[allow(unused_imports)] + pub(crate) use self::inner_exported2::*; + + fn test_intern() { + use crate::fn_mod::foo; + + foo(); + } + + fn test_extern() { + use wildcard_imports_helper::inner::inner_for_self_import::{self, inner_extern_foo}; + use wildcard_imports_helper::{ExternA, extern_foo}; + + inner_for_self_import::inner_extern_foo(); + inner_extern_foo(); + + extern_foo(); + + let _ = ExternA; + } + + fn test_inner_nested() { + use self::{inner::inner_foo, inner2::inner_bar}; + + inner_foo(); + inner_bar(); + } + + fn test_extern_reexported() { + use wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}; + + extern_exported(); + let _ = ExternExportedStruct; + let _ = ExternExportedEnum::A; + } + + mod inner_exported { + pub fn exported() {} + pub struct ExportedStruct; + pub enum ExportedEnum { + A, + } + } + + mod inner_exported2 { + pub(crate) fn exported2() {} + } + + mod inner { + pub fn inner_foo() {} + } + + mod inner2 { + pub fn inner_bar() {} + } +} + +fn test_reexported() { + use crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}; + + exported(); + let _ = ExportedStruct; + let _ = ExportedEnum::A; +} + +#[rustfmt::skip] +fn test_weird_formatting() { + use crate:: in_fn_test::exported; + use crate:: fn_mod::foo; + + exported(); + foo(); +} + +mod super_imports { + fn foofoo() {} + + mod should_be_replaced { + use super::foofoo; + + fn with_super() { + let _ = foofoo(); + } + } + + mod test_should_pass { + use super::*; + + fn with_super() { + let _ = foofoo(); + } + } + + mod test_should_pass_inside_function { + fn with_super_inside_function() { + use super::*; + let _ = foofoo(); + } + } + + mod test_should_pass_further_inside { + fn insidefoo() {} + mod inner { + use super::*; + fn with_super() { + let _ = insidefoo(); + } + } + } + + mod should_be_replaced_further_inside { + fn insidefoo() {} + mod inner { + use super::insidefoo; + fn with_super() { + let _ = insidefoo(); + } + } + } + + mod use_explicit_should_be_replaced { + use crate::super_imports::foofoo; + + fn with_explicit() { + let _ = foofoo(); + } + } + + mod use_double_super_should_be_replaced { + mod inner { + use super::super::foofoo; + + fn with_double_super() { + let _ = foofoo(); + } + } + } + + mod use_super_explicit_should_be_replaced { + use super::super::super_imports::foofoo; + + fn with_super_explicit() { + let _ = foofoo(); + } + } + + mod attestation_should_be_replaced { + use super::foofoo; + + fn with_explicit() { + let _ = foofoo(); + } + } +} diff --git a/tests/ui/wildcard_imports_2021.edition2018.stderr b/tests/ui/wildcard_imports_2021.edition2018.stderr new file mode 100644 index 000000000..acca9f651 --- /dev/null +++ b/tests/ui/wildcard_imports_2021.edition2018.stderr @@ -0,0 +1,132 @@ +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:13:5 + | +LL | use crate::fn_mod::*; + | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` + | + = note: `-D clippy::wildcard-imports` implied by `-D warnings` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:14:5 + | +LL | use crate::mod_mod::*; + | ^^^^^^^^^^^^^^^^^ help: try: `crate::mod_mod::inner_mod` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:15:5 + | +LL | use crate::multi_fn_mod::*; + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:16:5 + | +LL | use crate::struct_mod::*; + | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::struct_mod::{A, inner_struct_mod}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:19:5 + | +LL | use wildcard_imports_helper::inner::inner_for_self_import::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:21:5 + | +LL | use wildcard_imports_helper::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:91:13 + | +LL | use crate::fn_mod::*; + | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:97:75 + | +LL | use wildcard_imports_helper::inner::inner_for_self_import::{self, *}; + | ^ help: try: `inner_extern_foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:98:13 + | +LL | use wildcard_imports_helper::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:109:20 + | +LL | use self::{inner::*, inner2::*}; + | ^^^^^^^^ help: try: `inner::inner_foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:109:30 + | +LL | use self::{inner::*, inner2::*}; + | ^^^^^^^^^ help: try: `inner2::inner_bar` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:116:13 + | +LL | use wildcard_imports_helper::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:145:9 + | +LL | use crate::in_fn_test::*; + | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:154:9 + | +LL | use crate:: in_fn_test:: * ; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:155:9 + | +LL | use crate:: fn_mod:: + | _________^ +LL | | *; + | |_________^ help: try: `crate:: fn_mod::foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:166:13 + | +LL | use super::*; + | ^^^^^^^^ help: try: `super::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:201:17 + | +LL | use super::*; + | ^^^^^^^^ help: try: `super::insidefoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:209:13 + | +LL | use crate::super_imports::*; + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::super_imports::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:218:17 + | +LL | use super::super::*; + | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:227:13 + | +LL | use super::super::super_imports::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:235:13 + | +LL | use super::*; + | ^^^^^^^^ help: try: `super::foofoo` + +error: aborting due to 21 previous errors + diff --git a/tests/ui/wildcard_imports_2021.edition2021.fixed b/tests/ui/wildcard_imports_2021.edition2021.fixed new file mode 100644 index 000000000..6d534a10e --- /dev/null +++ b/tests/ui/wildcard_imports_2021.edition2021.fixed @@ -0,0 +1,240 @@ +// revisions: edition2018 edition2021 +//[edition2018] edition:2018 +//[edition2021] edition:2021 +// run-rustfix +// aux-build:wildcard_imports_helper.rs + +#![warn(clippy::wildcard_imports)] +#![allow(unused, clippy::unnecessary_wraps, clippy::let_unit_value)] +#![warn(unused_imports)] + +extern crate wildcard_imports_helper; + +use crate::fn_mod::foo; +use crate::mod_mod::inner_mod; +use crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}; +use crate::struct_mod::{A, inner_struct_mod}; + +#[allow(unused_imports)] +use wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar; +use wildcard_imports_helper::prelude::v1::*; +use wildcard_imports_helper::{ExternA, extern_foo}; + +use std::io::prelude::*; + +struct ReadFoo; + +impl Read for ReadFoo { + fn read(&mut self, _buf: &mut [u8]) -> std::io::Result { + Ok(0) + } +} + +mod fn_mod { + pub fn foo() {} +} + +mod mod_mod { + pub mod inner_mod { + pub fn foo() {} + } +} + +mod multi_fn_mod { + pub fn multi_foo() {} + pub fn multi_bar() {} + pub fn multi_baz() {} + pub mod multi_inner_mod { + pub fn foo() {} + } +} + +mod struct_mod { + pub struct A; + pub struct B; + pub mod inner_struct_mod { + pub struct C; + } + + #[macro_export] + macro_rules! double_struct_import_test { + () => { + let _ = A; + }; + } +} + +fn main() { + foo(); + multi_foo(); + multi_bar(); + multi_inner_mod::foo(); + inner_mod::foo(); + extern_foo(); + inner_extern_bar(); + + let _ = A; + let _ = inner_struct_mod::C; + let _ = ExternA; + let _ = PreludeModAnywhere; + + double_struct_import_test!(); + double_struct_import_test!(); +} + +mod in_fn_test { + pub use self::inner_exported::*; + #[allow(unused_imports)] + pub(crate) use self::inner_exported2::*; + + fn test_intern() { + use crate::fn_mod::foo; + + foo(); + } + + fn test_extern() { + use wildcard_imports_helper::inner::inner_for_self_import::{self, inner_extern_foo}; + use wildcard_imports_helper::{ExternA, extern_foo}; + + inner_for_self_import::inner_extern_foo(); + inner_extern_foo(); + + extern_foo(); + + let _ = ExternA; + } + + fn test_inner_nested() { + use self::{inner::inner_foo, inner2::inner_bar}; + + inner_foo(); + inner_bar(); + } + + fn test_extern_reexported() { + use wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}; + + extern_exported(); + let _ = ExternExportedStruct; + let _ = ExternExportedEnum::A; + } + + mod inner_exported { + pub fn exported() {} + pub struct ExportedStruct; + pub enum ExportedEnum { + A, + } + } + + mod inner_exported2 { + pub(crate) fn exported2() {} + } + + mod inner { + pub fn inner_foo() {} + } + + mod inner2 { + pub fn inner_bar() {} + } +} + +fn test_reexported() { + use crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}; + + exported(); + let _ = ExportedStruct; + let _ = ExportedEnum::A; +} + +#[rustfmt::skip] +fn test_weird_formatting() { + use crate:: in_fn_test::exported; + use crate:: fn_mod::foo; + + exported(); + foo(); +} + +mod super_imports { + fn foofoo() {} + + mod should_be_replaced { + use super::foofoo; + + fn with_super() { + let _ = foofoo(); + } + } + + mod test_should_pass { + use super::*; + + fn with_super() { + let _ = foofoo(); + } + } + + mod test_should_pass_inside_function { + fn with_super_inside_function() { + use super::*; + let _ = foofoo(); + } + } + + mod test_should_pass_further_inside { + fn insidefoo() {} + mod inner { + use super::*; + fn with_super() { + let _ = insidefoo(); + } + } + } + + mod should_be_replaced_further_inside { + fn insidefoo() {} + mod inner { + use super::insidefoo; + fn with_super() { + let _ = insidefoo(); + } + } + } + + mod use_explicit_should_be_replaced { + use crate::super_imports::foofoo; + + fn with_explicit() { + let _ = foofoo(); + } + } + + mod use_double_super_should_be_replaced { + mod inner { + use super::super::foofoo; + + fn with_double_super() { + let _ = foofoo(); + } + } + } + + mod use_super_explicit_should_be_replaced { + use super::super::super_imports::foofoo; + + fn with_super_explicit() { + let _ = foofoo(); + } + } + + mod attestation_should_be_replaced { + use super::foofoo; + + fn with_explicit() { + let _ = foofoo(); + } + } +} diff --git a/tests/ui/wildcard_imports_2021.edition2021.stderr b/tests/ui/wildcard_imports_2021.edition2021.stderr new file mode 100644 index 000000000..acca9f651 --- /dev/null +++ b/tests/ui/wildcard_imports_2021.edition2021.stderr @@ -0,0 +1,132 @@ +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:13:5 + | +LL | use crate::fn_mod::*; + | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` + | + = note: `-D clippy::wildcard-imports` implied by `-D warnings` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:14:5 + | +LL | use crate::mod_mod::*; + | ^^^^^^^^^^^^^^^^^ help: try: `crate::mod_mod::inner_mod` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:15:5 + | +LL | use crate::multi_fn_mod::*; + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:16:5 + | +LL | use crate::struct_mod::*; + | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::struct_mod::{A, inner_struct_mod}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:19:5 + | +LL | use wildcard_imports_helper::inner::inner_for_self_import::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:21:5 + | +LL | use wildcard_imports_helper::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:91:13 + | +LL | use crate::fn_mod::*; + | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:97:75 + | +LL | use wildcard_imports_helper::inner::inner_for_self_import::{self, *}; + | ^ help: try: `inner_extern_foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:98:13 + | +LL | use wildcard_imports_helper::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:109:20 + | +LL | use self::{inner::*, inner2::*}; + | ^^^^^^^^ help: try: `inner::inner_foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:109:30 + | +LL | use self::{inner::*, inner2::*}; + | ^^^^^^^^^ help: try: `inner2::inner_bar` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:116:13 + | +LL | use wildcard_imports_helper::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:145:9 + | +LL | use crate::in_fn_test::*; + | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:154:9 + | +LL | use crate:: in_fn_test:: * ; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:155:9 + | +LL | use crate:: fn_mod:: + | _________^ +LL | | *; + | |_________^ help: try: `crate:: fn_mod::foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:166:13 + | +LL | use super::*; + | ^^^^^^^^ help: try: `super::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:201:17 + | +LL | use super::*; + | ^^^^^^^^ help: try: `super::insidefoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:209:13 + | +LL | use crate::super_imports::*; + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::super_imports::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:218:17 + | +LL | use super::super::*; + | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:227:13 + | +LL | use super::super::super_imports::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:235:13 + | +LL | use super::*; + | ^^^^^^^^ help: try: `super::foofoo` + +error: aborting due to 21 previous errors + diff --git a/tests/ui/wildcard_imports_2021.rs b/tests/ui/wildcard_imports_2021.rs new file mode 100644 index 000000000..b5ed58e68 --- /dev/null +++ b/tests/ui/wildcard_imports_2021.rs @@ -0,0 +1,241 @@ +// revisions: edition2018 edition2021 +//[edition2018] edition:2018 +//[edition2021] edition:2021 +// run-rustfix +// aux-build:wildcard_imports_helper.rs + +#![warn(clippy::wildcard_imports)] +#![allow(unused, clippy::unnecessary_wraps, clippy::let_unit_value)] +#![warn(unused_imports)] + +extern crate wildcard_imports_helper; + +use crate::fn_mod::*; +use crate::mod_mod::*; +use crate::multi_fn_mod::*; +use crate::struct_mod::*; + +#[allow(unused_imports)] +use wildcard_imports_helper::inner::inner_for_self_import::*; +use wildcard_imports_helper::prelude::v1::*; +use wildcard_imports_helper::*; + +use std::io::prelude::*; + +struct ReadFoo; + +impl Read for ReadFoo { + fn read(&mut self, _buf: &mut [u8]) -> std::io::Result { + Ok(0) + } +} + +mod fn_mod { + pub fn foo() {} +} + +mod mod_mod { + pub mod inner_mod { + pub fn foo() {} + } +} + +mod multi_fn_mod { + pub fn multi_foo() {} + pub fn multi_bar() {} + pub fn multi_baz() {} + pub mod multi_inner_mod { + pub fn foo() {} + } +} + +mod struct_mod { + pub struct A; + pub struct B; + pub mod inner_struct_mod { + pub struct C; + } + + #[macro_export] + macro_rules! double_struct_import_test { + () => { + let _ = A; + }; + } +} + +fn main() { + foo(); + multi_foo(); + multi_bar(); + multi_inner_mod::foo(); + inner_mod::foo(); + extern_foo(); + inner_extern_bar(); + + let _ = A; + let _ = inner_struct_mod::C; + let _ = ExternA; + let _ = PreludeModAnywhere; + + double_struct_import_test!(); + double_struct_import_test!(); +} + +mod in_fn_test { + pub use self::inner_exported::*; + #[allow(unused_imports)] + pub(crate) use self::inner_exported2::*; + + fn test_intern() { + use crate::fn_mod::*; + + foo(); + } + + fn test_extern() { + use wildcard_imports_helper::inner::inner_for_self_import::{self, *}; + use wildcard_imports_helper::*; + + inner_for_self_import::inner_extern_foo(); + inner_extern_foo(); + + extern_foo(); + + let _ = ExternA; + } + + fn test_inner_nested() { + use self::{inner::*, inner2::*}; + + inner_foo(); + inner_bar(); + } + + fn test_extern_reexported() { + use wildcard_imports_helper::*; + + extern_exported(); + let _ = ExternExportedStruct; + let _ = ExternExportedEnum::A; + } + + mod inner_exported { + pub fn exported() {} + pub struct ExportedStruct; + pub enum ExportedEnum { + A, + } + } + + mod inner_exported2 { + pub(crate) fn exported2() {} + } + + mod inner { + pub fn inner_foo() {} + } + + mod inner2 { + pub fn inner_bar() {} + } +} + +fn test_reexported() { + use crate::in_fn_test::*; + + exported(); + let _ = ExportedStruct; + let _ = ExportedEnum::A; +} + +#[rustfmt::skip] +fn test_weird_formatting() { + use crate:: in_fn_test:: * ; + use crate:: fn_mod:: + *; + + exported(); + foo(); +} + +mod super_imports { + fn foofoo() {} + + mod should_be_replaced { + use super::*; + + fn with_super() { + let _ = foofoo(); + } + } + + mod test_should_pass { + use super::*; + + fn with_super() { + let _ = foofoo(); + } + } + + mod test_should_pass_inside_function { + fn with_super_inside_function() { + use super::*; + let _ = foofoo(); + } + } + + mod test_should_pass_further_inside { + fn insidefoo() {} + mod inner { + use super::*; + fn with_super() { + let _ = insidefoo(); + } + } + } + + mod should_be_replaced_further_inside { + fn insidefoo() {} + mod inner { + use super::*; + fn with_super() { + let _ = insidefoo(); + } + } + } + + mod use_explicit_should_be_replaced { + use crate::super_imports::*; + + fn with_explicit() { + let _ = foofoo(); + } + } + + mod use_double_super_should_be_replaced { + mod inner { + use super::super::*; + + fn with_double_super() { + let _ = foofoo(); + } + } + } + + mod use_super_explicit_should_be_replaced { + use super::super::super_imports::*; + + fn with_super_explicit() { + let _ = foofoo(); + } + } + + mod attestation_should_be_replaced { + use super::*; + + fn with_explicit() { + let _ = foofoo(); + } + } +} diff --git a/tests/ui/wildcard_imports_2021.stderr b/tests/ui/wildcard_imports_2021.stderr new file mode 100644 index 000000000..92f6d3153 --- /dev/null +++ b/tests/ui/wildcard_imports_2021.stderr @@ -0,0 +1,132 @@ +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:9:5 + | +LL | use crate::fn_mod::*; + | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` + | + = note: `-D clippy::wildcard-imports` implied by `-D warnings` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:10:5 + | +LL | use crate::mod_mod::*; + | ^^^^^^^^^^^^^^^^^ help: try: `crate::mod_mod::inner_mod` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:11:5 + | +LL | use crate::multi_fn_mod::*; + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:12:5 + | +LL | use crate::struct_mod::*; + | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::struct_mod::{A, inner_struct_mod}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:15:5 + | +LL | use wildcard_imports_helper::inner::inner_for_self_import::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:17:5 + | +LL | use wildcard_imports_helper::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:87:13 + | +LL | use crate::fn_mod::*; + | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:93:75 + | +LL | use wildcard_imports_helper::inner::inner_for_self_import::{self, *}; + | ^ help: try: `inner_extern_foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:94:13 + | +LL | use wildcard_imports_helper::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:105:20 + | +LL | use self::{inner::*, inner2::*}; + | ^^^^^^^^ help: try: `inner::inner_foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:105:30 + | +LL | use self::{inner::*, inner2::*}; + | ^^^^^^^^^ help: try: `inner2::inner_bar` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:112:13 + | +LL | use wildcard_imports_helper::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:141:9 + | +LL | use crate::in_fn_test::*; + | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:150:9 + | +LL | use crate:: in_fn_test:: * ; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:151:9 + | +LL | use crate:: fn_mod:: + | _________^ +LL | | *; + | |_________^ help: try: `crate:: fn_mod::foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:162:13 + | +LL | use super::*; + | ^^^^^^^^ help: try: `super::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:197:17 + | +LL | use super::*; + | ^^^^^^^^ help: try: `super::insidefoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:205:13 + | +LL | use crate::super_imports::*; + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::super_imports::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:214:17 + | +LL | use super::super::*; + | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:223:13 + | +LL | use super::super::super_imports::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:231:13 + | +LL | use super::*; + | ^^^^^^^^ help: try: `super::foofoo` + +error: aborting due to 21 previous errors + From 315bb10405ec6082f46c73791bed24a134a307c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 27 Dec 2022 11:03:59 -0800 Subject: [PATCH 2/2] Account for multiple multiline spans with empty padding Instead of ``` LL | fn oom( | __^ | | _| | || LL | || ) { | ||_- LL | | } | |__^ ``` emit ``` LL | // fn oom( LL | || ) { | ||_- LL | | } | |__^ ``` --- tests/ui/async_yields_async.stderr | 6 ++---- tests/ui/result_map_unit_fn_unfixable.stderr | 5 +---- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/tests/ui/async_yields_async.stderr b/tests/ui/async_yields_async.stderr index 92ba35929..22ce1c6f6 100644 --- a/tests/ui/async_yields_async.stderr +++ b/tests/ui/async_yields_async.stderr @@ -3,8 +3,7 @@ error: an async construct yields a type which is itself awaitable | LL | let _h = async { | _____________________- -LL | | async { - | | _________^ +LL | |/ async { LL | || 3 LL | || } | ||_________^ awaitable value not awaited @@ -37,8 +36,7 @@ error: an async construct yields a type which is itself awaitable | LL | let _j = async || { | ________________________- -LL | | async { - | | _________^ +LL | |/ async { LL | || 3 LL | || } | ||_________^ awaitable value not awaited diff --git a/tests/ui/result_map_unit_fn_unfixable.stderr b/tests/ui/result_map_unit_fn_unfixable.stderr index 2e1eb8eb1..d0e534f63 100644 --- a/tests/ui/result_map_unit_fn_unfixable.stderr +++ b/tests/ui/result_map_unit_fn_unfixable.stderr @@ -19,10 +19,7 @@ LL | x.field.map(|value| if value > 0 { do_nothing(value); do_nothing(value) error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_unfixable.rs:29:5 | -LL | x.field.map(|value| { - | ______^ - | | _____| - | || +LL | // x.field.map(|value| { LL | || do_nothing(value); LL | || do_nothing(value) LL | || });