diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index bc42c07224e1a..fa03c953aa599 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -5765,6 +5765,7 @@ Released 2018-09-13 [`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive [`manual_ok_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_err [`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or +[`manual_option_as_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_option_as_slice [`manual_pattern_char_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_pattern_char_comparison [`manual_range_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains [`manual_range_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_patterns @@ -5773,6 +5774,7 @@ Released 2018-09-13 [`manual_retain`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_retain [`manual_rotate`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_rotate [`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic +[`manual_slice_fill`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_slice_fill [`manual_slice_size_calculation`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_slice_size_calculation [`manual_split_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_split_once [`manual_str_repeat`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_str_repeat @@ -5954,6 +5956,7 @@ Released 2018-09-13 [`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 +[`precedence_bits`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence_bits [`print_in_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_in_format_impl [`print_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_literal [`print_stderr`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_stderr @@ -6026,6 +6029,7 @@ Released 2018-09-13 [`result_map_unwrap_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unwrap_or_else [`result_unit_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unit_err [`result_unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unwrap_used +[`return_and_then`]: https://rust-lang.github.io/rust-clippy/master/index.html#return_and_then [`return_self_not_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#return_self_not_must_use [`reverse_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#reverse_range_loop [`reversed_empty_ranges`]: https://rust-lang.github.io/rust-clippy/master/index.html#reversed_empty_ranges diff --git a/src/tools/clippy/book/src/attribs.md b/src/tools/clippy/book/src/attribs.md index cf99497bc0fcb..9b7f597050411 100644 --- a/src/tools/clippy/book/src/attribs.md +++ b/src/tools/clippy/book/src/attribs.md @@ -5,7 +5,7 @@ To do this, Clippy provides attributes that can be applied to items in the 3rd p ## `#[clippy::format_args]` -_Available since Clippy v1.84_ +_Available since Clippy v1.85_ This attribute can be added to a macro that supports `format!`, `println!`, or similar syntax. It tells Clippy that the macro is a formatting macro, and that the arguments to the macro diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md index b8f9fff9c6138..dab2630a56fc4 100644 --- a/src/tools/clippy/book/src/lint_configuration.md +++ b/src/tools/clippy/book/src/lint_configuration.md @@ -752,6 +752,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`manual_rem_euclid`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_rem_euclid) * [`manual_repeat_n`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_repeat_n) * [`manual_retain`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_retain) +* [`manual_slice_fill`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_slice_fill) * [`manual_split_once`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_split_once) * [`manual_str_repeat`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_str_repeat) * [`manual_strip`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip) diff --git a/src/tools/clippy/book/src/lints.md b/src/tools/clippy/book/src/lints.md index 442dc63914e9d..1dd5179766017 100644 --- a/src/tools/clippy/book/src/lints.md +++ b/src/tools/clippy/book/src/lints.md @@ -101,5 +101,18 @@ The `clippy::cargo` group gives you suggestions on how to improve your your crate and are not sure if you have all useful information in your `Cargo.toml`. +## Nursery + +The `clippy::nursery` group contains lints which are buggy or need more work. It is **not** +recommended to enable the whole group, but rather cherry-pick lints that are useful for your +code base and your use case. + +## Deprecated + +The `clippy::deprecated` is empty lints that exist to ensure that `#[allow(lintname)]` still +compiles after the lint was deprecated. Deprecation "removes" lints by removing their +functionality and marking them as deprecated, which may cause further warnings but cannot +cause a compiler error. + [Clippy lint documentation]: https://rust-lang.github.io/rust-clippy/ [Clippy 1.0 RFC]: https://github.com/rust-lang/rfcs/blob/master/text/2476-clippy-uno.md#lint-audit-and-categories diff --git a/src/tools/clippy/clippy_config/src/conf.rs b/src/tools/clippy/clippy_config/src/conf.rs index 552141476f3ac..a1591188bee71 100644 --- a/src/tools/clippy/clippy_config/src/conf.rs +++ b/src/tools/clippy/clippy_config/src/conf.rs @@ -621,6 +621,7 @@ define_Conf! { manual_rem_euclid, manual_repeat_n, manual_retain, + manual_slice_fill, manual_split_once, manual_str_repeat, manual_strip, diff --git a/src/tools/clippy/clippy_dev/src/new_lint.rs b/src/tools/clippy/clippy_dev/src/new_lint.rs index 35dd986ff614f..cc4b26867a206 100644 --- a/src/tools/clippy/clippy_dev/src/new_lint.rs +++ b/src/tools/clippy/clippy_dev/src/new_lint.rs @@ -255,8 +255,9 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { let name_camel = to_camel_case(lint.name); let name_upper = lint_name.to_uppercase(); - result.push_str(&if enable_msrv { - formatdoc!( + if enable_msrv { + let _: fmt::Result = writedoc!( + result, r" use clippy_utils::msrvs::{{self, Msrv}}; use clippy_config::Conf; @@ -265,22 +266,24 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { use rustc_session::impl_lint_pass; " - ) + ); } else { - formatdoc!( + let _: fmt::Result = writedoc!( + result, r" {pass_import} use rustc_lint::{{{context_import}, {pass_type}}}; use rustc_session::declare_lint_pass; " - ) - }); + ); + } let _: fmt::Result = writeln!(result, "{}", get_lint_declaration(&name_upper, category)); - result.push_str(&if enable_msrv { - formatdoc!( + if enable_msrv { + let _: fmt::Result = writedoc!( + result, r" pub struct {name_camel} {{ msrv: Msrv, @@ -301,16 +304,17 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { // TODO: Add MSRV level to `clippy_config/src/msrvs.rs` if needed. // TODO: Update msrv config comment in `clippy_config/src/conf.rs` " - ) + ); } else { - formatdoc!( + let _: fmt::Result = writedoc!( + result, r" declare_lint_pass!({name_camel} => [{name_upper}]); impl {pass_type}{pass_lifetimes} for {name_camel} {{}} " - ) - }); + ); + } result } diff --git a/src/tools/clippy/clippy_dev/src/update_lints.rs b/src/tools/clippy/clippy_dev/src/update_lints.rs index fc0780f89a7f7..b80ee5aac7e76 100644 --- a/src/tools/clippy/clippy_dev/src/update_lints.rs +++ b/src/tools/clippy/clippy_dev/src/update_lints.rs @@ -985,17 +985,23 @@ mod tests { Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()), ]; let mut expected: HashMap> = HashMap::new(); - expected.insert("group1".to_string(), vec![ - Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()), - Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()), - ]); - expected.insert("group2".to_string(), vec![Lint::new( - "should_assert_eq2", - "group2", - "\"abc\"", - "module_name", - Range::default(), - )]); + expected.insert( + "group1".to_string(), + vec![ + Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()), + Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()), + ], + ); + expected.insert( + "group2".to_string(), + vec![Lint::new( + "should_assert_eq2", + "group2", + "\"abc\"", + "module_name", + Range::default(), + )], + ); assert_eq!(expected, Lint::by_lint_group(lints.into_iter())); } } diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs index 86bcf8edd578b..9fbeab5bf2e1f 100644 --- a/src/tools/clippy/clippy_lints/src/declared_lints.rs +++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs @@ -292,6 +292,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::loops::MANUAL_FIND_INFO, crate::loops::MANUAL_FLATTEN_INFO, crate::loops::MANUAL_MEMCPY_INFO, + crate::loops::MANUAL_SLICE_FILL_INFO, crate::loops::MANUAL_WHILE_LET_SOME_INFO, crate::loops::MISSING_SPIN_LOOP_INFO, crate::loops::MUT_RANGE_BOUND_INFO, @@ -321,6 +322,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::manual_let_else::MANUAL_LET_ELSE_INFO, crate::manual_main_separator_str::MANUAL_MAIN_SEPARATOR_STR_INFO, crate::manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE_INFO, + crate::manual_option_as_slice::MANUAL_OPTION_AS_SLICE_INFO, crate::manual_range_patterns::MANUAL_RANGE_PATTERNS_INFO, crate::manual_rem_euclid::MANUAL_REM_EUCLID_INFO, crate::manual_retain::MANUAL_RETAIN_INFO, @@ -463,6 +465,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::methods::REPEAT_ONCE_INFO, crate::methods::RESULT_FILTER_MAP_INFO, crate::methods::RESULT_MAP_OR_INTO_OPTION_INFO, + crate::methods::RETURN_AND_THEN_INFO, crate::methods::SEARCH_IS_SOME_INFO, crate::methods::SEEK_FROM_CURRENT_INFO, crate::methods::SEEK_TO_START_INSTEAD_OF_REWIND_INFO, @@ -626,6 +629,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::permissions_set_readonly_false::PERMISSIONS_SET_READONLY_FALSE_INFO, crate::pointers_in_nomem_asm_block::POINTERS_IN_NOMEM_ASM_BLOCK_INFO, crate::precedence::PRECEDENCE_INFO, + crate::precedence::PRECEDENCE_BITS_INFO, crate::ptr::CMP_NULL_INFO, crate::ptr::INVALID_NULL_PTR_USAGE_INFO, crate::ptr::MUT_FROM_REF_INFO, diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs index 123e358d7c3df..233ebe00d8e76 100644 --- a/src/tools/clippy/clippy_lints/src/dereference.rs +++ b/src/tools/clippy/clippy_lints/src/dereference.rs @@ -291,10 +291,13 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { && let Some(ty) = use_node.defined_ty(cx) && TyCoercionStability::for_defined_ty(cx, ty, use_node.is_return()).is_deref_stable() { - self.state = Some((State::ExplicitDeref { mutability: None }, StateData { - first_expr: expr, - adjusted_ty, - })); + self.state = Some(( + State::ExplicitDeref { mutability: None }, + StateData { + first_expr: expr, + adjusted_ty, + }, + )); } }, RefOp::Method { mutbl, is_ufcs } @@ -456,10 +459,13 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { && next_adjust.is_none_or(|a| matches!(a.kind, Adjust::Deref(_) | Adjust::Borrow(_))) && iter.all(|a| matches!(a.kind, Adjust::Deref(_) | Adjust::Borrow(_))) { - self.state = Some((State::Borrow { mutability }, StateData { - first_expr: expr, - adjusted_ty, - })); + self.state = Some(( + State::Borrow { mutability }, + StateData { + first_expr: expr, + adjusted_ty, + }, + )); } }, _ => {}, @@ -503,10 +509,13 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { let stability = state.stability; report(cx, expr, State::DerefedBorrow(state), data, typeck); if stability.is_deref_stable() { - self.state = Some((State::Borrow { mutability }, StateData { - first_expr: expr, - adjusted_ty, - })); + self.state = Some(( + State::Borrow { mutability }, + StateData { + first_expr: expr, + adjusted_ty, + }, + )); } }, (Some((State::DerefedBorrow(state), data)), RefOp::Deref) => { @@ -531,10 +540,13 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { } else if stability.is_deref_stable() && let Some(parent) = get_parent_expr(cx, expr) { - self.state = Some((State::ExplicitDeref { mutability: None }, StateData { - first_expr: parent, - adjusted_ty, - })); + self.state = Some(( + State::ExplicitDeref { mutability: None }, + StateData { + first_expr: parent, + adjusted_ty, + }, + )); } }, @@ -1124,17 +1136,20 @@ impl<'tcx> Dereferencing<'tcx> { if let Some(outer_pat) = self.ref_locals.get_mut(&local) { if let Some(pat) = outer_pat { // Check for auto-deref - if !matches!(cx.typeck_results().expr_adjustments(e), [ - Adjustment { - kind: Adjust::Deref(_), - .. - }, - Adjustment { - kind: Adjust::Deref(_), + if !matches!( + cx.typeck_results().expr_adjustments(e), + [ + Adjustment { + kind: Adjust::Deref(_), + .. + }, + Adjustment { + kind: Adjust::Deref(_), + .. + }, .. - }, - .. - ]) { + ] + ) { match get_parent_expr(cx, e) { // Field accesses are the same no matter the number of references. Some(Expr { diff --git a/src/tools/clippy/clippy_lints/src/doc/empty_line_after.rs b/src/tools/clippy/clippy_lints/src/doc/empty_line_after.rs index 099194d4e7467..6e85c6af642b5 100644 --- a/src/tools/clippy/clippy_lints/src/doc/empty_line_after.rs +++ b/src/tools/clippy/clippy_lints/src/doc/empty_line_after.rs @@ -233,10 +233,13 @@ fn check_gaps(cx: &LateContext<'_>, gaps: &[Gap<'_>]) -> bool { if let Some(owner) = cx.last_node_with_lint_attrs.as_owner() { let def_id = owner.to_def_id(); let def_descr = cx.tcx.def_descr(def_id); - diag.span_label(cx.tcx.def_span(def_id), match kind { - StopKind::Attr => format!("the attribute applies to this {def_descr}"), - StopKind::Doc(_) => format!("the comment documents this {def_descr}"), - }); + diag.span_label( + cx.tcx.def_span(def_id), + match kind { + StopKind::Attr => format!("the attribute applies to this {def_descr}"), + StopKind::Doc(_) => format!("the comment documents this {def_descr}"), + }, + ); } diag.multipart_suggestion_with_style( diff --git a/src/tools/clippy/clippy_lints/src/endian_bytes.rs b/src/tools/clippy/clippy_lints/src/endian_bytes.rs index 29deaaf3bc7af..a7670ffce887c 100644 --- a/src/tools/clippy/clippy_lints/src/endian_bytes.rs +++ b/src/tools/clippy/clippy_lints/src/endian_bytes.rs @@ -6,6 +6,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::Ty; use rustc_session::declare_lint_pass; use rustc_span::Symbol; +use std::fmt::Write; declare_clippy_lint! { /// ### What it does @@ -183,7 +184,7 @@ fn maybe_lint_endian_bytes(cx: &LateContext<'_>, expr: &Expr<'_>, prefix: Prefix help_str.push_str("either of "); } - help_str.push_str(&format!("`{ty}::{}` ", lint.as_name(prefix))); + write!(help_str, "`{ty}::{}` ", lint.as_name(prefix)).unwrap(); if i != len && !only_one { help_str.push_str("or "); diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs index 52b699274bbbd..f90bf9157aada 100644 --- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs +++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs @@ -1,13 +1,13 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::higher::VecArgs; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::{snippet_opt, snippet_with_applicability}; use clippy_utils::ty::get_type_diagnostic_name; use clippy_utils::usage::{local_used_after_expr, local_used_in}; use clippy_utils::{ get_path_from_caller_to_method_type, is_adjusted, is_no_std_crate, path_to_local, path_to_local_id, }; use rustc_errors::Applicability; -use rustc_hir::{BindingMode, Expr, ExprKind, FnRetTy, Param, PatKind, QPath, Safety, TyKind}; +use rustc_hir::{BindingMode, Expr, ExprKind, FnRetTy, GenericArgs, Param, PatKind, QPath, Safety, TyKind}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{ @@ -239,6 +239,11 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx && !cx.tcx.has_attr(method_def_id, sym::track_caller) && check_sig(closure_sig, cx.tcx.fn_sig(method_def_id).skip_binder().skip_binder()) { + let mut app = Applicability::MachineApplicable; + let generic_args = match path.args.and_then(GenericArgs::span_ext) { + Some(span) => format!("::{}", snippet_with_applicability(cx, span, "<..>", &mut app)), + None => String::new(), + }; span_lint_and_then( cx, REDUNDANT_CLOSURE_FOR_METHOD_CALLS, @@ -251,8 +256,8 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx diag.span_suggestion( expr.span, "replace the closure with the method itself", - format!("{}::{}", type_name, path.ident.name), - Applicability::MachineApplicable, + format!("{}::{}{}", type_name, path.ident.name, generic_args), + app, ); }, ); diff --git a/src/tools/clippy/clippy_lints/src/excessive_nesting.rs b/src/tools/clippy/clippy_lints/src/excessive_nesting.rs index 36567b3ded0b1..1d3ae89494414 100644 --- a/src/tools/clippy/clippy_lints/src/excessive_nesting.rs +++ b/src/tools/clippy/clippy_lints/src/excessive_nesting.rs @@ -124,7 +124,9 @@ struct NestingVisitor<'conf, 'cx> { impl NestingVisitor<'_, '_> { fn check_indent(&mut self, span: Span, id: NodeId) -> bool { - if self.nest_level > self.conf.excessive_nesting_threshold && !span.in_external_macro(self.cx.sess().source_map()) { + if self.nest_level > self.conf.excessive_nesting_threshold + && !span.in_external_macro(self.cx.sess().source_map()) + { self.conf.nodes.insert(id); return true; diff --git a/src/tools/clippy/clippy_lints/src/format_push_string.rs b/src/tools/clippy/clippy_lints/src/format_push_string.rs index 8b1f86cbb9139..68cc50f39391f 100644 --- a/src/tools/clippy/clippy_lints/src/format_push_string.rs +++ b/src/tools/clippy/clippy_lints/src/format_push_string.rs @@ -11,7 +11,7 @@ declare_clippy_lint! { /// Detects cases where the result of a `format!` call is /// appended to an existing `String`. /// - /// ### Why restrict this? + /// ### Why is this bad? /// Introduces an extra, avoidable heap allocation. /// /// ### Known problems @@ -35,7 +35,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.62.0"] pub FORMAT_PUSH_STRING, - restriction, + pedantic, "`format!(..)` appended to existing `String`" } declare_lint_pass!(FormatPushString => [FORMAT_PUSH_STRING]); diff --git a/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs b/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs index 6f5c5d6b3ea3f..c68499ce9f781 100644 --- a/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs +++ b/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs @@ -7,8 +7,8 @@ use clippy_utils::macros::macro_backtrace; use clippy_utils::source::snippet; use rustc_hir::{Expr, ExprKind, Item, ItemKind, Node}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty; +use rustc_middle::ty::layout::LayoutOf; use rustc_session::impl_lint_pass; use rustc_span::{Span, sym}; diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index 4b700673d0f80..8887ab7ec0d7b 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -217,6 +217,7 @@ mod manual_is_power_of_two; mod manual_let_else; mod manual_main_separator_str; mod manual_non_exhaustive; +mod manual_option_as_slice; mod manual_range_patterns; mod manual_rem_euclid; mod manual_retain; @@ -976,5 +977,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(unneeded_struct_pattern::UnneededStructPattern)); store.register_late_pass(|_| Box::::default()); store.register_late_pass(move |_| Box::new(non_std_lazy_statics::NonStdLazyStatic::new(conf))); + store.register_late_pass(|_| Box::new(manual_option_as_slice::ManualOptionAsSlice::new(conf))); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_slice_fill.rs b/src/tools/clippy/clippy_lints/src/loops/manual_slice_fill.rs new file mode 100644 index 0000000000000..7c6564235796a --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/loops/manual_slice_fill.rs @@ -0,0 +1,111 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::eager_or_lazy::switch_to_eager_eval; +use clippy_utils::macros::span_is_local; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::source::{HasSession, snippet_with_applicability}; +use clippy_utils::ty::implements_trait; +use clippy_utils::{higher, peel_blocks_with_stmt, span_contains_comment}; +use rustc_ast::ast::LitKind; +use rustc_ast::{RangeLimits, UnOp}; +use rustc_data_structures::packed::Pu128; +use rustc_errors::Applicability; +use rustc_hir::QPath::Resolved; +use rustc_hir::def::Res; +use rustc_hir::{Expr, ExprKind, Pat}; +use rustc_lint::LateContext; +use rustc_span::source_map::Spanned; +use rustc_span::sym; + +use super::MANUAL_SLICE_FILL; + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + pat: &'tcx Pat<'_>, + arg: &'tcx Expr<'_>, + body: &'tcx Expr<'_>, + expr: &'tcx Expr<'_>, + msrv: &Msrv, +) { + if !msrv.meets(msrvs::SLICE_FILL) { + return; + } + + // `for _ in 0..slice.len() { slice[_] = value; }` + if let Some(higher::Range { + start: Some(start), + end: Some(end), + limits: RangeLimits::HalfOpen, + }) = higher::Range::hir(arg) + && let ExprKind::Lit(Spanned { + node: LitKind::Int(Pu128(0), _), + .. + }) = start.kind + && let ExprKind::Block(..) = body.kind + // Check if the body is an assignment to a slice element. + && let ExprKind::Assign(assignee, assignval, _) = peel_blocks_with_stmt(body).kind + && let ExprKind::Index(slice, _, _) = assignee.kind + // Check if `len()` is used for the range end. + && let ExprKind::MethodCall(path, recv,..) = end.kind + && path.ident.name == sym::len + // Check if the slice which is being assigned to is the same as the one being iterated over. + && let ExprKind::Path(Resolved(_, recv_path)) = recv.kind + && let ExprKind::Path(Resolved(_, slice_path)) = slice.kind + && recv_path.res == slice_path.res + && !assignval.span.from_expansion() + // It is generally not equivalent to use the `fill` method if `assignval` can have side effects + && switch_to_eager_eval(cx, assignval) + && span_is_local(assignval.span) + // The `fill` method requires that the slice's element type implements the `Clone` trait. + && let Some(clone_trait) = cx.tcx.lang_items().clone_trait() + && implements_trait(cx, cx.typeck_results().expr_ty(slice), clone_trait, &[]) + { + sugg(cx, body, expr, slice.span, assignval.span); + } + // `for _ in &mut slice { *_ = value; }` + else if let ExprKind::AddrOf(_, _, recv) = arg.kind + // Check if the body is an assignment to a slice element. + && let ExprKind::Assign(assignee, assignval, _) = peel_blocks_with_stmt(body).kind + && let ExprKind::Unary(UnOp::Deref, slice_iter) = assignee.kind + && let ExprKind::Path(Resolved(_, recv_path)) = recv.kind + // Check if the slice which is being assigned to is the same as the one being iterated over. + && let ExprKind::Path(Resolved(_, slice_path)) = slice_iter.kind + && let Res::Local(local) = slice_path.res + && local == pat.hir_id + && !assignval.span.from_expansion() + && switch_to_eager_eval(cx, assignval) + && span_is_local(assignval.span) + // The `fill` method cannot be used if the slice's element type does not implement the `Clone` trait. + && let Some(clone_trait) = cx.tcx.lang_items().clone_trait() + && implements_trait(cx, cx.typeck_results().expr_ty(recv), clone_trait, &[]) + { + sugg(cx, body, expr, recv_path.span, assignval.span); + } +} + +fn sugg<'tcx>( + cx: &LateContext<'tcx>, + body: &'tcx Expr<'_>, + expr: &'tcx Expr<'_>, + slice_span: rustc_span::Span, + assignval_span: rustc_span::Span, +) { + let mut app = if span_contains_comment(cx.sess().source_map(), body.span) { + Applicability::MaybeIncorrect // Comments may be informational. + } else { + Applicability::MachineApplicable + }; + + span_lint_and_sugg( + cx, + MANUAL_SLICE_FILL, + expr.span, + "manually filling a slice", + "try", + format!( + "{}.fill({});", + snippet_with_applicability(cx, slice_span, "..", &mut app), + snippet_with_applicability(cx, assignval_span, "..", &mut app), + ), + app, + ); +} diff --git a/src/tools/clippy/clippy_lints/src/loops/mod.rs b/src/tools/clippy/clippy_lints/src/loops/mod.rs index c5e75af2303c6..cdc8c18c3b74e 100644 --- a/src/tools/clippy/clippy_lints/src/loops/mod.rs +++ b/src/tools/clippy/clippy_lints/src/loops/mod.rs @@ -8,6 +8,7 @@ mod iter_next_loop; mod manual_find; mod manual_flatten; mod manual_memcpy; +mod manual_slice_fill; mod manual_while_let_some; mod missing_spin_loop; mod mut_range_bound; @@ -714,6 +715,31 @@ declare_clippy_lint! { "possibly unintended infinite loop" } +declare_clippy_lint! { + /// ### What it does + /// Checks for manually filling a slice with a value. + /// + /// ### Why is this bad? + /// Using the `fill` method is more idiomatic and concise. + /// + /// ### Example + /// ```no_run + /// let mut some_slice = [1, 2, 3, 4, 5]; + /// for i in 0..some_slice.len() { + /// some_slice[i] = 0; + /// } + /// ``` + /// Use instead: + /// ```no_run + /// let mut some_slice = [1, 2, 3, 4, 5]; + /// some_slice.fill(0); + /// ``` + #[clippy::version = "1.86.0"] + pub MANUAL_SLICE_FILL, + style, + "manually filling a slice with a value" +} + pub struct Loops { msrv: Msrv, enforce_iter_loop_reborrow: bool, @@ -750,6 +776,7 @@ impl_lint_pass!(Loops => [ MANUAL_WHILE_LET_SOME, UNUSED_ENUMERATE_INDEX, INFINITE_LOOP, + MANUAL_SLICE_FILL, ]); impl<'tcx> LateLintPass<'tcx> for Loops { @@ -823,6 +850,7 @@ impl Loops { ) { let is_manual_memcpy_triggered = manual_memcpy::check(cx, pat, arg, body, expr); if !is_manual_memcpy_triggered { + manual_slice_fill::check(cx, pat, arg, body, expr, &self.msrv); needless_range_loop::check(cx, pat, arg, body, expr); explicit_counter_loop::check(cx, pat, arg, body, expr, label); } diff --git a/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs b/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs index 816ca17b3d2d5..04357cdd8f663 100644 --- a/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs +++ b/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs @@ -181,13 +181,16 @@ fn build_suggestion( ExprKind::Lit(Spanned { node: LitKind::Int(_, LitIntType::Unsuffixed), .. - }) | ExprKind::Unary(UnOp::Neg, Expr { - kind: ExprKind::Lit(Spanned { - node: LitKind::Int(_, LitIntType::Unsuffixed), + }) | ExprKind::Unary( + UnOp::Neg, + Expr { + kind: ExprKind::Lit(Spanned { + node: LitKind::Int(_, LitIntType::Unsuffixed), + .. + }), .. - }), - .. - }) + } + ) ) { format!("_{}", cx.typeck_results().expr_ty(rhs)) } else { diff --git a/src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs b/src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs new file mode 100644 index 0000000000000..5c40c945c690f --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs @@ -0,0 +1,225 @@ +use clippy_config::Conf; +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; +use clippy_utils::{is_none_arm, msrvs, peel_hir_expr_refs}; +use rustc_errors::Applicability; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::{Arm, Expr, ExprKind, LangItem, Pat, PatKind, QPath, is_range_literal}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::impl_lint_pass; +use rustc_span::{Span, Symbol, sym}; + +declare_clippy_lint! { + /// ### What it does + /// This detects various manual reimplementations of `Option::as_slice`. + /// + /// ### Why is this bad? + /// Those implementations are both more complex than calling `as_slice` + /// and unlike that incur a branch, pessimizing performance and leading + /// to more generated code. + /// + /// ### Example + /// ```no_run + ///# let opt = Some(1); + /// _ = opt.as_ref().map_or(&[][..], std::slice::from_ref); + /// _ = match opt.as_ref() { + /// Some(f) => std::slice::from_ref(f), + /// None => &[], + /// }; + /// ``` + /// Use instead: + /// ```no_run + ///# let opt = Some(1); + /// _ = opt.as_slice(); + /// _ = opt.as_slice(); + /// ``` + #[clippy::version = "1.85.0"] + pub MANUAL_OPTION_AS_SLICE, + complexity, + "manual `Option::as_slice`" +} + +pub struct ManualOptionAsSlice { + msrv: msrvs::Msrv, +} + +impl ManualOptionAsSlice { + pub fn new(conf: &Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } + } +} + +impl_lint_pass!(ManualOptionAsSlice => [MANUAL_OPTION_AS_SLICE]); + +impl LateLintPass<'_> for ManualOptionAsSlice { + extract_msrv_attr!(LateContext); + + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + let span = expr.span; + if span.from_expansion() + || !self.msrv.meets(if clippy_utils::is_in_const_context(cx) { + msrvs::CONST_OPTION_AS_SLICE + } else { + msrvs::OPTION_AS_SLICE + }) + { + return; + } + match expr.kind { + ExprKind::Match(scrutinee, [arm1, arm2], _) => { + if is_none_arm(cx, arm2) && check_arms(cx, arm2, arm1) + || is_none_arm(cx, arm1) && check_arms(cx, arm1, arm2) + { + check_as_ref(cx, scrutinee, span); + } + }, + ExprKind::If(cond, then, Some(other)) => { + if let ExprKind::Let(let_expr) = cond.kind + && let Some(binding) = extract_ident_from_some_pat(cx, let_expr.pat) + && check_some_body(cx, binding, then) + && is_empty_slice(cx, other.peel_blocks()) + { + check_as_ref(cx, let_expr.init, span); + } + }, + ExprKind::MethodCall(seg, callee, [], _) => { + if seg.ident.name.as_str() == "unwrap_or_default" { + check_map(cx, callee, span); + } + }, + ExprKind::MethodCall(seg, callee, [or], _) => match seg.ident.name.as_str() { + "unwrap_or" => { + if is_empty_slice(cx, or) { + check_map(cx, callee, span); + } + }, + "unwrap_or_else" => { + if returns_empty_slice(cx, or) { + check_map(cx, callee, span); + } + }, + _ => {}, + }, + ExprKind::MethodCall(seg, callee, [or_else, map], _) => match seg.ident.name.as_str() { + "map_or" => { + if is_empty_slice(cx, or_else) && is_slice_from_ref(cx, map) { + check_as_ref(cx, callee, span); + } + }, + "map_or_else" => { + if returns_empty_slice(cx, or_else) && is_slice_from_ref(cx, map) { + check_as_ref(cx, callee, span); + } + }, + _ => {}, + }, + _ => {}, + } + } +} + +fn check_map(cx: &LateContext<'_>, map: &Expr<'_>, span: Span) { + if let ExprKind::MethodCall(seg, callee, [mapping], _) = map.kind + && seg.ident.name == sym::map + && is_slice_from_ref(cx, mapping) + { + check_as_ref(cx, callee, span); + } +} + +fn check_as_ref(cx: &LateContext<'_>, expr: &Expr<'_>, span: Span) { + if let ExprKind::MethodCall(seg, callee, [], _) = expr.kind + && seg.ident.name == sym::as_ref + && let ty::Adt(adtdef, ..) = cx.typeck_results().expr_ty(callee).kind() + && cx.tcx.is_diagnostic_item(sym::Option, adtdef.did()) + { + if let Some(snippet) = clippy_utils::source::snippet_opt(cx, callee.span) { + span_lint_and_sugg( + cx, + MANUAL_OPTION_AS_SLICE, + span, + "use `Option::as_slice`", + "use", + format!("{snippet}.as_slice()"), + Applicability::MachineApplicable, + ); + } else { + span_lint(cx, MANUAL_OPTION_AS_SLICE, span, "use `Option_as_slice`"); + } + } +} + +fn extract_ident_from_some_pat(cx: &LateContext<'_>, pat: &Pat<'_>) -> Option { + if let PatKind::TupleStruct(QPath::Resolved(None, path), [binding], _) = pat.kind + && let Res::Def(DefKind::Ctor(..), def_id) = path.res + && let PatKind::Binding(_mode, _hir_id, ident, _inner_pat) = binding.kind + && clippy_utils::is_lang_item_or_ctor(cx, def_id, LangItem::OptionSome) + { + Some(ident.name) + } else { + None + } +} + +/// Returns true if `expr` is `std::slice::from_ref()`. Used in `if let`s. +fn check_some_body(cx: &LateContext<'_>, name: Symbol, expr: &Expr<'_>) -> bool { + if let ExprKind::Call(slice_from_ref, [arg]) = expr.peel_blocks().kind + && is_slice_from_ref(cx, slice_from_ref) + && let ExprKind::Path(QPath::Resolved(None, path)) = arg.kind + && let [seg] = path.segments + { + seg.ident.name == name + } else { + false + } +} + +fn check_arms(cx: &LateContext<'_>, none_arm: &Arm<'_>, some_arm: &Arm<'_>) -> bool { + if none_arm.guard.is_none() + && some_arm.guard.is_none() + && is_empty_slice(cx, none_arm.body) + && let Some(name) = extract_ident_from_some_pat(cx, some_arm.pat) + { + check_some_body(cx, name, some_arm.body) + } else { + false + } +} + +fn returns_empty_slice(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + match expr.kind { + ExprKind::Path(_) => clippy_utils::is_path_diagnostic_item(cx, expr, sym::default_fn), + ExprKind::Closure(cl) => is_empty_slice(cx, cx.tcx.hir().body(cl.body).value), + _ => false, + } +} + +/// Returns if expr returns an empty slice. If: +/// - An indexing operation to an empty array with a built-in range. `[][..]` +/// - An indexing operation with a zero-ended range. `expr[..0]` +/// - A reference to an empty array. `&[]` +/// - Or a call to `Default::default`. +fn is_empty_slice(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + let expr = peel_hir_expr_refs(expr.peel_blocks()).0; + match expr.kind { + ExprKind::Index(arr, range, _) => match arr.kind { + ExprKind::Array([]) => is_range_literal(range), + ExprKind::Array(_) => { + let Some(range) = clippy_utils::higher::Range::hir(range) else { + return false; + }; + range.end.is_some_and(|e| clippy_utils::is_integer_const(cx, e, 0)) + }, + _ => false, + }, + ExprKind::Array([]) => true, + ExprKind::Call(def, []) => clippy_utils::is_path_diagnostic_item(cx, def, sym::default_fn), + _ => false, + } +} + +fn is_slice_from_ref(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + clippy_utils::is_expr_path_def_path(cx, expr, &["core", "slice", "raw", "from_ref"]) +} diff --git a/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs b/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs index 7b95399c907cb..87d2faa225c52 100644 --- a/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs +++ b/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs @@ -9,8 +9,8 @@ use rustc_span::sym; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::higher::IfLetOrMatch; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::implements_trait; -use clippy_utils::{is_default_equivalent, is_in_const_context, peel_blocks, span_contains_comment}; +use clippy_utils::ty::{expr_type_is_certain, implements_trait}; +use clippy_utils::{is_default_equivalent, is_in_const_context, path_res, peel_blocks, span_contains_comment}; declare_clippy_lint! { /// ### What it does @@ -158,6 +158,36 @@ fn handle<'tcx>(cx: &LateContext<'tcx>, if_let_or_match: IfLetOrMatch<'tcx>, exp } else { Applicability::MachineApplicable }; + + // We now check if the condition is a None variant, in which case we need to specify the type + if path_res(cx, condition) + .opt_def_id() + .is_some_and(|id| Some(cx.tcx.parent(id)) == cx.tcx.lang_items().option_none_variant()) + { + return span_lint_and_sugg( + cx, + MANUAL_UNWRAP_OR_DEFAULT, + expr.span, + format!("{expr_name} can be simplified with `.unwrap_or_default()`"), + "replace it with", + format!("{receiver}::<{expr_type}>.unwrap_or_default()"), + applicability, + ); + } + + // We check if the expression type is still uncertain, in which case we ask the user to specify it + if !expr_type_is_certain(cx, condition) { + return span_lint_and_sugg( + cx, + MANUAL_UNWRAP_OR_DEFAULT, + expr.span, + format!("{expr_name} can be simplified with `.unwrap_or_default()`"), + format!("ascribe the type {expr_type} and replace your expression with"), + format!("{receiver}.unwrap_or_default()"), + Applicability::Unspecified, + ); + } + span_lint_and_sugg( cx, MANUAL_UNWRAP_OR_DEFAULT, diff --git a/src/tools/clippy/clippy_lints/src/matches/match_as_ref.rs b/src/tools/clippy/clippy_lints/src/matches/match_as_ref.rs index b1889d26c9321..1cb4b512a30e5 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_as_ref.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_as_ref.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{is_res_lang_ctor, path_res, peel_blocks}; +use clippy_utils::{is_none_arm, is_res_lang_ctor, path_res, peel_blocks}; use rustc_errors::Applicability; -use rustc_hir::{Arm, BindingMode, ByRef, Expr, ExprKind, LangItem, Mutability, PatExpr, PatExprKind, PatKind, QPath}; +use rustc_hir::{Arm, BindingMode, ByRef, Expr, ExprKind, LangItem, Mutability, PatKind, QPath}; use rustc_lint::LateContext; use rustc_middle::ty; @@ -55,14 +55,6 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: } } -// Checks if arm has the form `None => None` -fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { - matches!( - arm.pat.kind, - PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), .. }) if is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), LangItem::OptionNone) - ) -} - // Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`) fn is_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option { if let PatKind::TupleStruct(ref qpath, [first_pat, ..], _) = arm.pat.kind diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index 2f447775fa5b3..f501cf060c2a5 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -95,6 +95,7 @@ mod readonly_write_lock; mod redundant_as_str; mod repeat_once; mod result_map_or_else_none; +mod return_and_then; mod search_is_some; mod seek_from_current; mod seek_to_start_instead_of_rewind; @@ -3517,7 +3518,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.73.0"] pub FORMAT_COLLECT, - perf, + pedantic, "`format!`ing every element in a collection, then collecting the strings into a new `String`" } @@ -4365,7 +4366,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for string slices immediantly followed by `as_bytes`. + /// Checks for string slices immediately followed by `as_bytes`. /// /// ### Why is this bad? /// It involves doing an unnecessary UTF-8 alignment check which is less efficient, and can cause a panic. @@ -4391,6 +4392,46 @@ declare_clippy_lint! { "slicing a string and immediately calling as_bytes is less efficient and can lead to panics" } +declare_clippy_lint! { + /// ### What it does + /// + /// Detect functions that end with `Option::and_then` or `Result::and_then`, and suggest using a question mark (`?`) instead. + /// + /// ### Why is this bad? + /// + /// The `and_then` method is used to chain a computation that returns an `Option` or a `Result`. + /// This can be replaced with the `?` operator, which is more concise and idiomatic. + /// + /// ### Example + /// + /// ```no_run + /// fn test(opt: Option) -> Option { + /// opt.and_then(|n| { + /// if n > 1 { + /// Some(n + 1) + /// } else { + /// None + /// } + /// }) + /// } + /// ``` + /// Use instead: + /// ```no_run + /// fn test(opt: Option) -> Option { + /// let n = opt?; + /// if n > 1 { + /// Some(n + 1) + /// } else { + /// None + /// } + /// } + /// ``` + #[clippy::version = "1.86.0"] + pub RETURN_AND_THEN, + restriction, + "using `Option::and_then` or `Result::and_then` to chain a computation that returns an `Option` or a `Result`" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Msrv, @@ -4560,6 +4601,7 @@ impl_lint_pass!(Methods => [ USELESS_NONZERO_NEW_UNCHECKED, MANUAL_REPEAT_N, SLICED_STRING_AS_BYTES, + RETURN_AND_THEN, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -4789,7 +4831,10 @@ impl Methods { let biom_option_linted = bind_instead_of_map::check_and_then_some(cx, expr, recv, arg); let biom_result_linted = bind_instead_of_map::check_and_then_ok(cx, expr, recv, arg); if !biom_option_linted && !biom_result_linted { - unnecessary_lazy_eval::check(cx, expr, recv, arg, "and"); + let ule_and_linted = unnecessary_lazy_eval::check(cx, expr, recv, arg, "and"); + if !ule_and_linted { + return_and_then::check(cx, expr, recv, arg); + } } }, ("any", [arg]) => { @@ -5003,7 +5048,9 @@ impl Methods { get_first::check(cx, expr, recv, arg); get_last_with_len::check(cx, expr, recv, arg); }, - ("get_or_insert_with", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"), + ("get_or_insert_with", [arg]) => { + unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"); + }, ("hash", [arg]) => { unit_hash::check(cx, expr, recv, arg); }, @@ -5144,7 +5191,9 @@ impl Methods { }, _ => iter_nth_zero::check(cx, expr, recv, n_arg), }, - ("ok_or_else", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"), + ("ok_or_else", [arg]) => { + unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"); + }, ("open", [_]) => { open_options::check(cx, expr, recv); }, @@ -5437,9 +5486,12 @@ impl ShouldImplTraitCase { fn lifetime_param_cond(&self, impl_item: &hir::ImplItem<'_>) -> bool { self.lint_explicit_lifetime || !impl_item.generics.params.iter().any(|p| { - matches!(p.kind, hir::GenericParamKind::Lifetime { - kind: hir::LifetimeParamKind::Explicit - }) + matches!( + p.kind, + hir::GenericParamKind::Lifetime { + kind: hir::LifetimeParamKind::Explicit + } + ) }) } } diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_option_take.rs b/src/tools/clippy/clippy_lints/src/methods/needless_option_take.rs index c41ce2481d741..88b9c69f6f949 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_option_take.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_option_take.rs @@ -1,5 +1,6 @@ -use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::is_type_diagnostic_item; +use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::LateContext; use rustc_span::sym; @@ -10,13 +11,22 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &' // Checks if expression type is equal to sym::Option and if the expr is not a syntactic place if !recv.is_syntactic_place_expr() && is_expr_option(cx, recv) { if let Some(function_name) = source_of_temporary_value(recv) { - span_lint_and_note( + span_lint_and_then( cx, NEEDLESS_OPTION_TAKE, expr.span, "called `Option::take()` on a temporary value", - None, - format!("`{function_name}` creates a temporary value, so calling take() has no effect"), + |diag| { + diag.note(format!( + "`{function_name}` creates a temporary value, so calling take() has no effect" + )); + diag.span_suggestion( + expr.span.with_lo(recv.span.hi()), + "remove", + "", + Applicability::MachineApplicable, + ); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/methods/return_and_then.rs b/src/tools/clippy/clippy_lints/src/methods/return_and_then.rs new file mode 100644 index 0000000000000..7b1199ad1e2d2 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/return_and_then.rs @@ -0,0 +1,67 @@ +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, GenericArg, Ty}; +use rustc_span::sym; +use std::ops::ControlFlow; + +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::{indent_of, reindent_multiline, snippet_with_applicability}; +use clippy_utils::ty::get_type_diagnostic_name; +use clippy_utils::visitors::for_each_unconsumed_temporary; +use clippy_utils::{is_expr_final_block_expr, peel_blocks}; + +use super::RETURN_AND_THEN; + +/// lint if `and_then` is the last expression in a block, and +/// there are no references or temporaries in the receiver +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &hir::Expr<'_>, + recv: &'tcx hir::Expr<'tcx>, + arg: &'tcx hir::Expr<'_>, +) { + if !is_expr_final_block_expr(cx.tcx, expr) { + return; + } + + let recv_type = cx.typeck_results().expr_ty(recv); + if !matches!(get_type_diagnostic_name(cx, recv_type), Some(sym::Option | sym::Result)) { + return; + } + + let has_ref_type = matches!(recv_type.kind(), ty::Adt(_, args) if args + .first() + .and_then(|arg0: &GenericArg<'tcx>| GenericArg::as_type(*arg0)) + .is_some_and(Ty::is_ref)); + let has_temporaries = for_each_unconsumed_temporary(cx, recv, |_| ControlFlow::Break(())).is_break(); + if has_ref_type && has_temporaries { + return; + } + + let hir::ExprKind::Closure(&hir::Closure { body, fn_decl, .. }) = arg.kind else { + return; + }; + + let closure_arg = fn_decl.inputs[0]; + let closure_expr = peel_blocks(cx.tcx.hir().body(body).value); + + let mut applicability = Applicability::MachineApplicable; + let arg_snip = snippet_with_applicability(cx, closure_arg.span, "_", &mut applicability); + let recv_snip = snippet_with_applicability(cx, recv.span, "_", &mut applicability); + let body_snip = snippet_with_applicability(cx, closure_expr.span, "..", &mut applicability); + let inner = match body_snip.strip_prefix('{').and_then(|s| s.strip_suffix('}')) { + Some(s) => s.trim_start_matches('\n').trim_end(), + None => &body_snip, + }; + + let msg = "use the question mark operator instead of an `and_then` call"; + let sugg = format!( + "let {} = {}?;\n{}", + arg_snip, + recv_snip, + reindent_multiline(inner.into(), false, indent_of(cx, expr.span)) + ); + + span_lint_and_sugg(cx, RETURN_AND_THEN, expr.span, msg, "try", sugg, applicability); +} diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs index c27d1fb4903b3..e7adf3b43ba58 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs @@ -123,32 +123,60 @@ pub(super) fn check( if let hir::ExprKind::Lit(lit) = init.kind { match lit.node { ast::LitKind::Bool(false) => { - check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Or, Replacement { - method_name: "any", - has_args: true, - has_generic_return: false, - }); + check_fold_with_op( + cx, + expr, + acc, + fold_span, + hir::BinOpKind::Or, + Replacement { + method_name: "any", + has_args: true, + has_generic_return: false, + }, + ); }, ast::LitKind::Bool(true) => { - check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::And, Replacement { - method_name: "all", - has_args: true, - has_generic_return: false, - }); + check_fold_with_op( + cx, + expr, + acc, + fold_span, + hir::BinOpKind::And, + Replacement { + method_name: "all", + has_args: true, + has_generic_return: false, + }, + ); }, ast::LitKind::Int(Pu128(0), _) => { - check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Add, Replacement { - method_name: "sum", - has_args: false, - has_generic_return: needs_turbofish(cx, expr), - }); + check_fold_with_op( + cx, + expr, + acc, + fold_span, + hir::BinOpKind::Add, + Replacement { + method_name: "sum", + has_args: false, + has_generic_return: needs_turbofish(cx, expr), + }, + ); }, ast::LitKind::Int(Pu128(1), _) => { - check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Mul, Replacement { - method_name: "product", - has_args: false, - has_generic_return: needs_turbofish(cx, expr), - }); + check_fold_with_op( + cx, + expr, + acc, + fold_span, + hir::BinOpKind::Mul, + Replacement { + method_name: "product", + has_args: false, + has_generic_return: needs_turbofish(cx, expr), + }, + ); }, _ => (), } diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs index 1673a6f8b3a4e..7af550fa7c683 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -18,7 +18,7 @@ pub(super) fn check<'tcx>( recv: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>, simplify_using: &str, -) { +) -> bool { let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option); let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result); let is_bool = cx.typeck_results().expr_ty(recv).is_bool(); @@ -29,7 +29,7 @@ pub(super) fn check<'tcx>( let body_expr = &body.value; if usage::BindingUsageFinder::are_params_used(cx, body) || is_from_proc_macro(cx, expr) { - return; + return false; } if eager_or_lazy::switch_to_eager_eval(cx, body_expr) { @@ -71,8 +71,10 @@ pub(super) fn check<'tcx>( applicability, ); }); + return true; } } } } + false } diff --git a/src/tools/clippy/clippy_lints/src/misc.rs b/src/tools/clippy/clippy_lints/src/misc.rs index fa0eb9a94b73a..fca416d9e64c7 100644 --- a/src/tools/clippy/clippy_lints/src/misc.rs +++ b/src/tools/clippy/clippy_lints/src/misc.rs @@ -168,7 +168,7 @@ impl<'tcx> LateLintPass<'tcx> for LintPass { TOPLEVEL_REF_ARG, arg.hir_id, arg.pat.span, - "`ref` directly on a function argument is ignored. \ + "`ref` directly on a function parameter does not prevent taking ownership of the passed argument. \ Consider using a reference type instead", ); } diff --git a/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs b/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs index 302db2c914ca5..9acede4f32d66 100644 --- a/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs +++ b/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs @@ -152,16 +152,19 @@ fn collect_unsafe_exprs<'tcx>( ExprKind::AssignOp(_, lhs, rhs) | ExprKind::Assign(lhs, rhs, _) => { if matches!( lhs.kind, - ExprKind::Path(QPath::Resolved(_, hir::Path { - res: Res::Def( - DefKind::Static { - mutability: Mutability::Mut, - .. - }, - _ - ), - .. - })) + ExprKind::Path(QPath::Resolved( + _, + hir::Path { + res: Res::Def( + DefKind::Static { + mutability: Mutability::Mut, + .. + }, + _ + ), + .. + } + )) ) { unsafe_ops.push(("modification of a mutable static occurs here", expr.span)); collect_unsafe_exprs(cx, rhs, unsafe_ops); diff --git a/src/tools/clippy/clippy_lints/src/mutex_atomic.rs b/src/tools/clippy/clippy_lints/src/mutex_atomic.rs index 86c084423b713..49fd29d1dd6dc 100644 --- a/src/tools/clippy/clippy_lints/src/mutex_atomic.rs +++ b/src/tools/clippy/clippy_lints/src/mutex_atomic.rs @@ -18,11 +18,14 @@ declare_clippy_lint! { /// /// On the other hand, `Mutex`es are, in general, easier to /// verify correctness. An atomic does not behave the same as - /// an equivalent mutex. See [this issue](https://github.com/rust-lang/rust-clippy/issues/4295)'s commentary for more details. + /// an equivalent mutex. See [this issue](https://github.com/rust-lang/rust-clippy/issues/4295)'s + /// commentary for more details. /// /// ### Known problems - /// This lint cannot detect if the mutex is actually used - /// for waiting before a critical section. + /// * This lint cannot detect if the mutex is actually used + /// for waiting before a critical section. + /// * This lint has a false positive that warns without considering the case + /// where `Mutex` is used together with `Condvar`. /// /// ### Example /// ```no_run @@ -48,14 +51,23 @@ declare_clippy_lint! { /// Checks for usage of `Mutex` where `X` is an integral /// type. /// - /// ### Why is this bad? + /// ### Why restrict this? /// Using a mutex just to make access to a plain integer /// sequential is /// shooting flies with cannons. `std::sync::atomic::AtomicUsize` is leaner and faster. /// + /// On the other hand, `Mutex`es are, in general, easier to + /// verify correctness. An atomic does not behave the same as + /// an equivalent mutex. See [this issue](https://github.com/rust-lang/rust-clippy/issues/4295)'s + /// commentary for more details. + /// /// ### Known problems - /// This lint cannot detect if the mutex is actually used - /// for waiting before a critical section. + /// * This lint cannot detect if the mutex is actually used + /// for waiting before a critical section. + /// * This lint has a false positive that warns without considering the case + /// where `Mutex` is used together with `Condvar`. + /// * This lint suggest using `AtomicU64` instead of `Mutex`, but + /// `AtomicU64` is not available on some 32-bit platforms. /// /// ### Example /// ```no_run @@ -70,7 +82,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "pre 1.29.0"] pub MUTEX_INTEGER, - nursery, + restriction, "using a mutex for an integer type" } @@ -108,7 +120,7 @@ fn get_atomic_name(ty: Ty<'_>) -> Option<&'static str> { UintTy::U32 => Some("AtomicU32"), UintTy::U64 => Some("AtomicU64"), UintTy::Usize => Some("AtomicUsize"), - // There's no `AtomicU128`. + // `AtomicU128` is unstable and only available on a few platforms: https://github.com/rust-lang/rust/issues/99069 UintTy::U128 => None, } }, @@ -119,7 +131,7 @@ fn get_atomic_name(ty: Ty<'_>) -> Option<&'static str> { IntTy::I32 => Some("AtomicI32"), IntTy::I64 => Some("AtomicI64"), IntTy::Isize => Some("AtomicIsize"), - // There's no `AtomicI128`. + // `AtomicU128` is unstable and only available on a few platforms: https://github.com/rust-lang/rust/issues/99069 IntTy::I128 => None, } }, diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs index 30846fb46ac1b..2855703b9d56e 100644 --- a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs +++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs @@ -181,9 +181,14 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { && !is_copy(cx, ty) && ty.is_sized(cx.tcx, cx.typing_env()) && !allowed_traits.iter().any(|&t| { - implements_trait_with_env_from_iter(cx.tcx, cx.typing_env(), ty, t, None, [None::< - ty::GenericArg<'tcx>, - >]) + implements_trait_with_env_from_iter( + cx.tcx, + cx.typing_env(), + ty, + t, + None, + [None::>], + ) }) && !implements_borrow_trait && !all_borrowable_trait diff --git a/src/tools/clippy/clippy_lints/src/operators/mod.rs b/src/tools/clippy/clippy_lints/src/operators/mod.rs index d9845bc3b0f74..9ad32c2bd3963 100644 --- a/src/tools/clippy/clippy_lints/src/operators/mod.rs +++ b/src/tools/clippy/clippy_lints/src/operators/mod.rs @@ -265,9 +265,6 @@ declare_clippy_lint! { /// `x.trailing_zeros() >= 4` is much clearer than `x & 15 /// == 0` /// - /// ### Known problems - /// llvm generates better code for `x & 15 == 0` on x86 - /// /// ### Example /// ```no_run /// # let x = 1; diff --git a/src/tools/clippy/clippy_lints/src/precedence.rs b/src/tools/clippy/clippy_lints/src/precedence.rs index 421b2b7475553..ec6835db897ed 100644 --- a/src/tools/clippy/clippy_lints/src/precedence.rs +++ b/src/tools/clippy/clippy_lints/src/precedence.rs @@ -3,17 +3,14 @@ use clippy_utils::source::snippet_with_applicability; use rustc_ast::ast::BinOpKind::{Add, BitAnd, BitOr, BitXor, Div, Mul, Rem, Shl, Shr, Sub}; use rustc_ast::ast::{BinOpKind, Expr, ExprKind}; use rustc_errors::Applicability; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_lint::{EarlyContext, EarlyLintPass, Lint}; use rustc_session::declare_lint_pass; use rustc_span::source_map::Spanned; declare_clippy_lint! { /// ### What it does - /// Checks for operations where precedence may be unclear - /// and suggests to add parentheses. Currently it catches the following: - /// * mixed usage of arithmetic and bit shifting/combining operators without - /// parentheses - /// * mixed usage of bitmasking and bit shifting operators without parentheses + /// Checks for operations where precedence may be unclear and suggests to add parentheses. + /// It catches a mixed usage of arithmetic and bit shifting/combining operators without parentheses /// /// ### Why is this bad? /// Not everyone knows the precedence of those operators by @@ -21,15 +18,32 @@ declare_clippy_lint! { /// code. /// /// ### Example - /// * `1 << 2 + 3` equals 32, while `(1 << 2) + 3` equals 7 - /// * `0x2345 & 0xF000 >> 12` equals 5, while `(0x2345 & 0xF000) >> 12` equals 2 + /// `1 << 2 + 3` equals 32, while `(1 << 2) + 3` equals 7 #[clippy::version = "pre 1.29.0"] pub PRECEDENCE, complexity, "operations where precedence may be unclear" } -declare_lint_pass!(Precedence => [PRECEDENCE]); +declare_clippy_lint! { + /// ### What it does + /// Checks for bit shifting operations combined with bit masking/combining operators + /// and suggest using parentheses. + /// + /// ### Why restrict this? + /// Not everyone knows the precedence of those operators by + /// heart, so expressions like these may trip others trying to reason about the + /// code. + /// + /// ### Example + /// `0x2345 & 0xF000 >> 12` equals 5, while `(0x2345 & 0xF000) >> 12` equals 2 + #[clippy::version = "1.86.0"] + pub PRECEDENCE_BITS, + restriction, + "operations mixing bit shifting with bit combining/masking" +} + +declare_lint_pass!(Precedence => [PRECEDENCE, PRECEDENCE_BITS]); impl EarlyLintPass for Precedence { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { @@ -38,10 +52,10 @@ impl EarlyLintPass for Precedence { } if let ExprKind::Binary(Spanned { node: op, .. }, ref left, ref right) = expr.kind { - let span_sugg = |expr: &Expr, sugg, appl| { + let span_sugg = |lint: &'static Lint, expr: &Expr, sugg, appl| { span_lint_and_sugg( cx, - PRECEDENCE, + lint, expr.span, "operator precedence might not be obvious", "consider parenthesizing your expression", @@ -57,37 +71,41 @@ impl EarlyLintPass for Precedence { match (op, get_bin_opt(left), get_bin_opt(right)) { ( BitAnd | BitOr | BitXor, - Some(Shl | Shr | Add | Div | Mul | Rem | Sub), - Some(Shl | Shr | Add | Div | Mul | Rem | Sub), + Some(left_op @ (Shl | Shr | Add | Div | Mul | Rem | Sub)), + Some(right_op @ (Shl | Shr | Add | Div | Mul | Rem | Sub)), ) - | (Shl | Shr, Some(Add | Div | Mul | Rem | Sub), Some(Add | Div | Mul | Rem | Sub)) => { + | ( + Shl | Shr, + Some(left_op @ (Add | Div | Mul | Rem | Sub)), + Some(right_op @ (Add | Div | Mul | Rem | Sub)), + ) => { let sugg = format!( "({}) {} ({})", snippet_with_applicability(cx, left.span, "..", &mut applicability), op.as_str(), snippet_with_applicability(cx, right.span, "..", &mut applicability) ); - span_sugg(expr, sugg, applicability); + span_sugg(lint_for(&[op, left_op, right_op]), expr, sugg, applicability); }, - (BitAnd | BitOr | BitXor, Some(Shl | Shr | Add | Div | Mul | Rem | Sub), _) - | (Shl | Shr, Some(Add | Div | Mul | Rem | Sub), _) => { + (BitAnd | BitOr | BitXor, Some(side_op @ (Shl | Shr | Add | Div | Mul | Rem | Sub)), _) + | (Shl | Shr, Some(side_op @ (Add | Div | Mul | Rem | Sub)), _) => { let sugg = format!( "({}) {} {}", snippet_with_applicability(cx, left.span, "..", &mut applicability), op.as_str(), snippet_with_applicability(cx, right.span, "..", &mut applicability) ); - span_sugg(expr, sugg, applicability); + span_sugg(lint_for(&[op, side_op]), expr, sugg, applicability); }, - (BitAnd | BitOr | BitXor, _, Some(Shl | Shr | Add | Div | Mul | Rem | Sub)) - | (Shl | Shr, _, Some(Add | Div | Mul | Rem | Sub)) => { + (BitAnd | BitOr | BitXor, _, Some(side_op @ (Shl | Shr | Add | Div | Mul | Rem | Sub))) + | (Shl | Shr, _, Some(side_op @ (Add | Div | Mul | Rem | Sub))) => { let sugg = format!( "{} {} ({})", snippet_with_applicability(cx, left.span, "..", &mut applicability), op.as_str(), snippet_with_applicability(cx, right.span, "..", &mut applicability) ); - span_sugg(expr, sugg, applicability); + span_sugg(lint_for(&[op, side_op]), expr, sugg, applicability); }, _ => (), } @@ -106,3 +124,11 @@ fn get_bin_opt(expr: &Expr) -> Option { fn is_bit_op(op: BinOpKind) -> bool { matches!(op, BitXor | BitAnd | BitOr | Shl | Shr) } + +fn lint_for(ops: &[BinOpKind]) -> &'static Lint { + if ops.iter().all(|op| is_bit_op(*op)) { + PRECEDENCE_BITS + } else { + PRECEDENCE + } +} diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs index 506adf0f2cc5e..0b67594a9b199 100644 --- a/src/tools/clippy/clippy_lints/src/ptr.rs +++ b/src/tools/clippy/clippy_lints/src/ptr.rs @@ -1,5 +1,6 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then}; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::source::SpanRangeExt; +use clippy_utils::sugg::Sugg; use clippy_utils::visitors::contains_unsafe_block; use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_def_id, path_to_local, std_or_core}; use hir::LifetimeName; @@ -250,15 +251,24 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { } fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Binary(ref op, l, r) = expr.kind { - if (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne) && (is_null_path(cx, l) || is_null_path(cx, r)) { - span_lint( - cx, - CMP_NULL, - expr.span, - "comparing with null is better expressed by the `.is_null()` method", - ); - } + if let ExprKind::Binary(op, l, r) = expr.kind + && (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne) + { + let non_null_path_snippet = match (is_null_path(cx, l), is_null_path(cx, r)) { + (true, false) if let Some(sugg) = Sugg::hir_opt(cx, r) => sugg.maybe_par(), + (false, true) if let Some(sugg) = Sugg::hir_opt(cx, l) => sugg.maybe_par(), + _ => return, + }; + + span_lint_and_sugg( + cx, + CMP_NULL, + expr.span, + "comparing with null is better expressed by the `.is_null()` method", + "try", + format!("{non_null_path_snippet}.is_null()"), + Applicability::MachineApplicable, + ); } else { check_invalid_ptr_usage(cx, expr); } diff --git a/src/tools/clippy/clippy_lints/src/redundant_clone.rs b/src/tools/clippy/clippy_lints/src/redundant_clone.rs index b9e0106fc86b3..fb1bc494bd948 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_clone.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_clone.rs @@ -349,10 +349,14 @@ fn visit_clone_usage(cloned: mir::Local, clone: mir::Local, mir: &mir::Body<'_>, local_use_locs: _, local_consume_or_mutate_locs: clone_consume_or_mutate_locs, }, - )) = visit_local_usage(&[cloned, clone], mir, mir::Location { - block: bb, - statement_index: mir.basic_blocks[bb].statements.len(), - }) + )) = visit_local_usage( + &[cloned, clone], + mir, + mir::Location { + block: bb, + statement_index: mir.basic_blocks[bb].statements.len(), + }, + ) .map(|mut vec| (vec.remove(0), vec.remove(0))) { CloneUsage { diff --git a/src/tools/clippy/clippy_lints/src/redundant_else.rs b/src/tools/clippy/clippy_lints/src/redundant_else.rs index 3476f56cf338f..a1b5a3aff3236 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_else.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_else.rs @@ -1,8 +1,12 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::{indent_of, reindent_multiline, snippet}; use rustc_ast::ast::{Block, Expr, ExprKind, Stmt, StmtKind}; use rustc_ast::visit::{Visitor, walk_expr}; +use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_session::declare_lint_pass; +use rustc_span::Span; +use std::borrow::Cow; declare_clippy_lint! { /// ### What it does @@ -75,13 +79,27 @@ impl EarlyLintPass for RedundantElse { _ => break, } } - span_lint_and_help( + + let mut app = Applicability::MachineApplicable; + if let ExprKind::Block(block, _) = &els.kind { + for stmt in &block.stmts { + // If the `else` block contains a local binding or a macro invocation, Clippy shouldn't auto-fix it + if matches!(&stmt.kind, StmtKind::Let(_) | StmtKind::MacCall(_)) { + app = Applicability::Unspecified; + break; + } + } + } + + // FIXME: The indentation of the suggestion would be the same as the one of the macro invocation in this implementation, see https://github.com/rust-lang/rust-clippy/pull/13936#issuecomment-2569548202 + span_lint_and_sugg( cx, REDUNDANT_ELSE, - els.span, + els.span.with_lo(then.span.hi()), "redundant else block", - None, "remove the `else` block and move the contents out", + make_sugg(cx, els.span, "..", Some(expr.span)).to_string(), + app, ); } } @@ -136,3 +154,23 @@ impl BreakVisitor { self.check(stmt, Self::visit_stmt) } } + +// Extract the inner contents of an `else` block str +// e.g. `{ foo(); bar(); }` -> `foo(); bar();` +fn extract_else_block(mut block: &str) -> String { + block = block.strip_prefix("{").unwrap_or(block); + block = block.strip_suffix("}").unwrap_or(block); + block.trim_end().to_string() +} + +fn make_sugg<'a>( + cx: &EarlyContext<'_>, + els_span: Span, + default: &'a str, + indent_relative_to: Option, +) -> Cow<'a, str> { + let extracted = extract_else_block(&snippet(cx, els_span, default)); + let indent = indent_relative_to.and_then(|s| indent_of(cx, s)); + + reindent_multiline(extracted.into(), false, indent) +} diff --git a/src/tools/clippy/clippy_lints/src/same_name_method.rs b/src/tools/clippy/clippy_lints/src/same_name_method.rs index 8d31641d4836c..29914d4379fef 100644 --- a/src/tools/clippy/clippy_lints/src/same_name_method.rs +++ b/src/tools/clippy/clippy_lints/src/same_name_method.rs @@ -62,10 +62,13 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod { && let TyKind::Path(QPath::Resolved(_, Path { res, .. })) = self_ty.kind { if !map.contains_key(res) { - map.insert(*res, ExistingName { - impl_methods: BTreeMap::new(), - trait_methods: BTreeMap::new(), - }); + map.insert( + *res, + ExistingName { + impl_methods: BTreeMap::new(), + trait_methods: BTreeMap::new(), + }, + ); } let existing_name = map.get_mut(res).unwrap(); diff --git a/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs b/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs index f72ff10dd43c4..b22c638fc3633 100644 --- a/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs +++ b/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs @@ -63,8 +63,7 @@ fn get_pointee_ty_and_count_expr<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ) -> Option<(Ty<'tcx>, &'tcx Expr<'tcx>)> { - const METHODS: [&str; 11] = [ - "write_bytes", + const METHODS: [&str; 10] = [ "copy_to", "copy_from", "copy_to_nonoverlapping", @@ -79,7 +78,7 @@ fn get_pointee_ty_and_count_expr<'tcx>( if let ExprKind::Call(func, [.., count]) = expr.kind // Find calls to ptr::{copy, copy_nonoverlapping} - // and ptr::{swap_nonoverlapping, write_bytes}, + // and ptr::swap_nonoverlapping, && let ExprKind::Path(ref func_qpath) = func.kind && let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id() && matches!(cx.tcx.get_diagnostic_name(def_id), Some( @@ -88,7 +87,6 @@ fn get_pointee_ty_and_count_expr<'tcx>( | sym::ptr_slice_from_raw_parts | sym::ptr_slice_from_raw_parts_mut | sym::ptr_swap_nonoverlapping - | sym::ptr_write_bytes | sym::slice_from_raw_parts | sym::slice_from_raw_parts_mut )) @@ -99,7 +97,7 @@ fn get_pointee_ty_and_count_expr<'tcx>( return Some((pointee_ty, count)); } if let ExprKind::MethodCall(method_path, ptr_self, [.., count], _) = expr.kind - // Find calls to copy_{from,to}{,_nonoverlapping} and write_bytes methods + // Find calls to copy_{from,to}{,_nonoverlapping} && let method_ident = method_path.ident.as_str() && METHODS.iter().any(|m| *m == method_ident) @@ -121,6 +119,8 @@ impl<'tcx> LateLintPass<'tcx> for SizeOfInElementCount { instead of a count of elements of `T`"; if let Some((pointee_ty, count_expr)) = get_pointee_ty_and_count_expr(cx, expr) + // Using a number of bytes for a byte type isn't suspicious + && pointee_ty != cx.tcx.types.u8 // Find calls to functions with an element count parameter and get // the pointee type and count parameter expression diff --git a/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs b/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs index 569812d810655..9993e6ae18b9d 100644 --- a/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs +++ b/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs @@ -55,13 +55,11 @@ impl<'tcx> LateLintPass<'tcx> for ToDigitIsSome { if let hir::ExprKind::Path(to_digits_path) = &to_digits_call.kind && let to_digits_call_res = cx.qpath_res(to_digits_path, to_digits_call.hir_id) && let Some(to_digits_def_id) = to_digits_call_res.opt_def_id() - && match_def_path(cx, to_digits_def_id, &[ - "core", - "char", - "methods", - "", - "to_digit", - ]) + && match_def_path( + cx, + to_digits_def_id, + &["core", "char", "methods", "", "to_digit"], + ) { Some((false, char_arg, radix_arg)) } else { diff --git a/src/tools/clippy/clippy_lints/src/types/mod.rs b/src/tools/clippy/clippy_lints/src/types/mod.rs index 391c36df492c6..579cbf447a2b0 100644 --- a/src/tools/clippy/clippy_lints/src/types/mod.rs +++ b/src/tools/clippy/clippy_lints/src/types/mod.rs @@ -386,22 +386,30 @@ impl<'tcx> LateLintPass<'tcx> for Types { let is_exported = cx.effective_visibilities.is_exported(def_id); - self.check_fn_decl(cx, decl, CheckTyContext { - is_in_trait_impl, - in_body: matches!(fn_kind, FnKind::Closure), - is_exported, - ..CheckTyContext::default() - }); + self.check_fn_decl( + cx, + decl, + CheckTyContext { + is_in_trait_impl, + in_body: matches!(fn_kind, FnKind::Closure), + is_exported, + ..CheckTyContext::default() + }, + ); } fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { let is_exported = cx.effective_visibilities.is_exported(item.owner_id.def_id); match item.kind { - ItemKind::Static(ty, _, _) | ItemKind::Const(ty, _, _) => self.check_ty(cx, ty, CheckTyContext { - is_exported, - ..CheckTyContext::default() - }), + ItemKind::Static(ty, _, _) | ItemKind::Const(ty, _, _) => self.check_ty( + cx, + ty, + CheckTyContext { + is_exported, + ..CheckTyContext::default() + }, + ), // functions, enums, structs, impls and traits are covered _ => (), } @@ -419,10 +427,14 @@ impl<'tcx> LateLintPass<'tcx> for Types { false }; - self.check_ty(cx, ty, CheckTyContext { - is_in_trait_impl, - ..CheckTyContext::default() - }); + self.check_ty( + cx, + ty, + CheckTyContext { + is_in_trait_impl, + ..CheckTyContext::default() + }, + ); }, // Methods are covered by check_fn. // Type aliases are ignored because oftentimes it's impossible to @@ -438,10 +450,14 @@ impl<'tcx> LateLintPass<'tcx> for Types { let is_exported = cx.effective_visibilities.is_exported(field.def_id); - self.check_ty(cx, field.ty, CheckTyContext { - is_exported, - ..CheckTyContext::default() - }); + self.check_ty( + cx, + field.ty, + CheckTyContext { + is_exported, + ..CheckTyContext::default() + }, + ); } fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &TraitItem<'tcx>) { @@ -469,10 +485,14 @@ impl<'tcx> LateLintPass<'tcx> for Types { fn check_local(&mut self, cx: &LateContext<'tcx>, local: &LetStmt<'tcx>) { if let Some(ty) = local.ty { - self.check_ty(cx, ty, CheckTyContext { - in_body: true, - ..CheckTyContext::default() - }); + self.check_ty( + cx, + ty, + CheckTyContext { + in_body: true, + ..CheckTyContext::default() + }, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs b/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs index 3fc08e8192d4c..207f2ef4563a2 100644 --- a/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs +++ b/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs @@ -257,10 +257,13 @@ fn is_default_method_on_current_ty<'tcx>(tcx: TyCtxt<'tcx>, qpath: QPath<'tcx>, } if matches!( ty.kind, - TyKind::Path(QPath::Resolved(_, hir::Path { - res: Res::SelfTyAlias { .. }, - .. - },)) + TyKind::Path(QPath::Resolved( + _, + hir::Path { + res: Res::SelfTyAlias { .. }, + .. + }, + )) ) { return true; } diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_semicolon.rs b/src/tools/clippy/clippy_lints/src/unnecessary_semicolon.rs index efbc536dcb4d1..e5267620c4fb9 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_semicolon.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_semicolon.rs @@ -37,7 +37,7 @@ declare_clippy_lint! { #[derive(Default)] pub struct UnnecessarySemicolon { - last_statements: Vec, + last_statements: Vec<(HirId, bool)>, } impl_lint_pass!(UnnecessarySemicolon => [UNNECESSARY_SEMICOLON]); @@ -45,27 +45,25 @@ impl_lint_pass!(UnnecessarySemicolon => [UNNECESSARY_SEMICOLON]); impl UnnecessarySemicolon { /// Enter or leave a block, remembering the last statement of the block. fn handle_block(&mut self, cx: &LateContext<'_>, block: &Block<'_>, enter: bool) { - // Up to edition 2021, removing the semicolon of the last statement of a block - // may result in the scrutinee temporary values to live longer than the block - // variables. To avoid this problem, we do not lint the last statement of an - // expressionless block. - if cx.tcx.sess.edition() <= Edition2021 - && block.expr.is_none() + // The last statement of an expressionless block deserves a special treatment. + if block.expr.is_none() && let Some(last_stmt) = block.stmts.last() { if enter { - self.last_statements.push(last_stmt.hir_id); + let block_ty = cx.typeck_results().node_type(block.hir_id); + self.last_statements.push((last_stmt.hir_id, block_ty.is_unit())); } else { self.last_statements.pop(); } } } - /// Checks if `stmt` is the last statement in an expressionless block for edition ≤ 2021. - fn is_last_in_block(&self, stmt: &Stmt<'_>) -> bool { + /// Checks if `stmt` is the last statement in an expressionless block. In this case, + /// return `Some` with a boolean which is `true` if the block type is `()`. + fn is_last_in_block(&self, stmt: &Stmt<'_>) -> Option { self.last_statements .last() - .is_some_and(|last_stmt_id| last_stmt_id == &stmt.hir_id) + .and_then(|&(stmt_id, is_unit)| (stmt_id == stmt.hir_id).then_some(is_unit)) } } @@ -90,8 +88,22 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessarySemicolon { ) && cx.typeck_results().expr_ty(expr) == cx.tcx.types.unit { - if self.is_last_in_block(stmt) && leaks_droppable_temporary_with_limited_lifetime(cx, expr) { - return; + if let Some(block_is_unit) = self.is_last_in_block(stmt) { + if cx.tcx.sess.edition() <= Edition2021 && leaks_droppable_temporary_with_limited_lifetime(cx, expr) { + // The expression contains temporaries with limited lifetimes in edition lower than 2024. Those may + // survive until after the end of the current scope instead of until the end of the statement, so do + // not lint this situation. + return; + } + + if !block_is_unit { + // Although the expression returns `()`, the block doesn't. This may happen if the expression + // returns early in all code paths, such as a `return value` in the condition of an `if` statement, + // in which case the block type would be `!`. Do not lint in this case, as the statement would + // become the block expression; the block type would become `()` and this may not type correctly + // if the expected type for the block is not `()`. + return; + } } let semi_span = expr.span.shrink_to_hi().to(stmt.span.shrink_to_hi()); diff --git a/src/tools/clippy/clippy_lints/src/unneeded_struct_pattern.rs b/src/tools/clippy/clippy_lints/src/unneeded_struct_pattern.rs index 40ba70d451dbd..a74eab8b6ae50 100644 --- a/src/tools/clippy/clippy_lints/src/unneeded_struct_pattern.rs +++ b/src/tools/clippy/clippy_lints/src/unneeded_struct_pattern.rs @@ -32,7 +32,7 @@ declare_clippy_lint! { /// None => 0, /// }; /// ``` - #[clippy::version = "1.83.0"] + #[clippy::version = "1.86.0"] pub UNNEEDED_STRUCT_PATTERN, style, "using struct pattern to match against unit variant" diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs index 31ae002e47d98..11c14c1477764 100644 --- a/src/tools/clippy/clippy_lints/src/write.rs +++ b/src/tools/clippy/clippy_lints/src/write.rs @@ -522,7 +522,7 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) { let replacement = match (format_string_is_raw, replace_raw) { (false, false) => Some(replacement), - (false, true) => Some(replacement.replace('"', "\\\"").replace('\\', "\\\\")), + (false, true) => Some(replacement.replace('\\', "\\\\").replace('"', "\\\"")), (true, false) => match conservative_unescape(&replacement) { Ok(unescaped) => Some(unescaped), Err(UnescapeErr::Lint) => None, diff --git a/src/tools/clippy/clippy_utils/README.md b/src/tools/clippy/clippy_utils/README.md index 251e3dfe41bed..41f3b1cbd5079 100644 --- a/src/tools/clippy/clippy_utils/README.md +++ b/src/tools/clippy/clippy_utils/README.md @@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain: ``` -nightly-2025-01-28 +nightly-2025-02-06 ``` diff --git a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs index 798f4575c2e10..ab5f97199ce33 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs @@ -662,12 +662,12 @@ pub fn eq_fn_header(l: &FnHeader, r: &FnHeader) -> bool { && eq_ext(&l.ext, &r.ext) } +#[expect(clippy::ref_option, reason = "This is the type how it is stored in the AST")] pub fn eq_opt_fn_contract(l: &Option>, r: &Option>) -> bool { match (l, r) { (Some(l), Some(r)) => { - eq_expr_opt(l.requires.as_ref(), r.requires.as_ref()) - && eq_expr_opt(l.ensures.as_ref(), r.ensures.as_ref()) - } + eq_expr_opt(l.requires.as_ref(), r.requires.as_ref()) && eq_expr_opt(l.ensures.as_ref(), r.ensures.as_ref()) + }, (None, None) => true, (Some(_), None) | (None, Some(_)) => false, } diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 5a5227af90743..337684b68f86e 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -341,6 +341,15 @@ pub fn is_wild(pat: &Pat<'_>) -> bool { matches!(pat.kind, PatKind::Wild) } +// Checks if arm has the form `None => None` +pub fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { + matches!( + arm.pat.kind, + PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), .. }) + if is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), OptionNone) + ) +} + /// Checks if the given `QPath` belongs to a type alias. pub fn is_ty_alias(qpath: &QPath<'_>) -> bool { match *qpath { diff --git a/src/tools/clippy/clippy_utils/src/mir/mod.rs b/src/tools/clippy/clippy_utils/src/mir/mod.rs index ccbbccd0dbffc..85250f81dc47b 100644 --- a/src/tools/clippy/clippy_utils/src/mir/mod.rs +++ b/src/tools/clippy/clippy_utils/src/mir/mod.rs @@ -112,10 +112,14 @@ pub fn block_in_cycle(body: &Body<'_>, block: BasicBlock) -> bool { /// Convenience wrapper around `visit_local_usage`. pub fn used_exactly_once(mir: &Body<'_>, local: Local) -> Option { - visit_local_usage(&[local], mir, Location { - block: START_BLOCK, - statement_index: 0, - }) + visit_local_usage( + &[local], + mir, + Location { + block: START_BLOCK, + statement_index: 0, + }, + ) .map(|mut vec| { let LocalUsage { local_use_locs, .. } = vec.remove(0); let mut locations = local_use_locs diff --git a/src/tools/clippy/clippy_utils/src/msrvs.rs b/src/tools/clippy/clippy_utils/src/msrvs.rs index d73cb7e35611c..24f73b9df2684 100644 --- a/src/tools/clippy/clippy_utils/src/msrvs.rs +++ b/src/tools/clippy/clippy_utils/src/msrvs.rs @@ -1,4 +1,5 @@ use rustc_ast::attr::AttributeExt; + use rustc_attr_parsing::{RustcVersion, parse_version}; use rustc_session::Session; use rustc_span::{Symbol, sym}; @@ -18,12 +19,14 @@ macro_rules! msrv_aliases { // names may refer to stabilized feature flags or library items msrv_aliases! { + 1,84,0 { CONST_OPTION_AS_SLICE } 1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY, CONST_MUT_REFS, CONST_UNWRAP } 1,82,0 { IS_NONE_OR, REPEAT_N, RAW_REF_OP } 1,81,0 { LINT_REASONS_STABILIZATION, ERROR_IN_CORE, EXPLICIT_SELF_TYPE_ELISION } 1,80,0 { BOX_INTO_ITER, LAZY_CELL } 1,77,0 { C_STR_LITERALS } 1,76,0 { PTR_FROM_REF, OPTION_RESULT_INSPECT } + 1,75,0 { OPTION_AS_SLICE } 1,74,0 { REPR_RUST } 1,73,0 { MANUAL_DIV_CEIL } 1,71,0 { TUPLE_ARRAY_CONVERSIONS, BUILD_HASHER_HASH_ONE } @@ -40,7 +43,7 @@ msrv_aliases! { 1,53,0 { OR_PATTERNS, MANUAL_BITS, BTREE_MAP_RETAIN, BTREE_SET_RETAIN, ARRAY_INTO_ITERATOR } 1,52,0 { STR_SPLIT_ONCE, REM_EUCLID_CONST } 1,51,0 { BORROW_AS_PTR, SEEK_FROM_CURRENT, UNSIGNED_ABS } - 1,50,0 { BOOL_THEN, CLAMP } + 1,50,0 { BOOL_THEN, CLAMP, SLICE_FILL } 1,47,0 { TAU, IS_ASCII_DIGIT_CONST, ARRAY_IMPL_ANY_LEN, SATURATING_SUB_CONST } 1,46,0 { CONST_IF_MATCH } 1,45,0 { STR_STRIP_PREFIX } diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index 5a3a3d0cedc42..c7890f33f27ee 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -179,7 +179,10 @@ fn check_rvalue<'tcx>( )) } }, - Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::UbChecks | NullOp::ContractChecks, _) + Rvalue::NullaryOp( + NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::UbChecks | NullOp::ContractChecks, + _, + ) | Rvalue::ShallowInitBox(_, _) => Ok(()), Rvalue::UnaryOp(_, operand) => { let ty = operand.ty(body, tcx); diff --git a/src/tools/clippy/clippy_utils/src/source.rs b/src/tools/clippy/clippy_utils/src/source.rs index eecbfb3936ac4..b8b800b93ceaa 100644 --- a/src/tools/clippy/clippy_utils/src/source.rs +++ b/src/tools/clippy/clippy_utils/src/source.rs @@ -726,12 +726,15 @@ pub fn str_literal_to_char_literal( &snip[1..(snip.len() - 1)] }; - let hint = format!("'{}'", match ch { - "'" => "\\'", - r"\" => "\\\\", - "\\\"" => "\"", // no need to escape `"` in `'"'` - _ => ch, - }); + let hint = format!( + "'{}'", + match ch { + "'" => "\\'", + r"\" => "\\\\", + "\\\"" => "\"", // no need to escape `"` in `'"'` + _ => ch, + } + ); Some(hint) } else { diff --git a/src/tools/clippy/clippy_utils/src/str_utils.rs b/src/tools/clippy/clippy_utils/src/str_utils.rs index 1588ee452daea..421b25a77fe8b 100644 --- a/src/tools/clippy/clippy_utils/src/str_utils.rs +++ b/src/tools/clippy/clippy_utils/src/str_utils.rs @@ -370,11 +370,9 @@ mod test { assert_eq!(camel_case_split("AbcDef"), vec!["Abc", "Def"]); assert_eq!(camel_case_split("Abc"), vec!["Abc"]); assert_eq!(camel_case_split("abcDef"), vec!["abc", "Def"]); - assert_eq!(camel_case_split("\u{f6}\u{f6}AabABcd"), vec![ - "\u{f6}\u{f6}", - "Aab", - "A", - "Bcd" - ]); + assert_eq!( + camel_case_split("\u{f6}\u{f6}AabABcd"), + vec!["\u{f6}\u{f6}", "Aab", "A", "Bcd"] + ); } } diff --git a/src/tools/clippy/lintcheck/src/config.rs b/src/tools/clippy/lintcheck/src/config.rs index af243f94274d0..83c3d7aba021a 100644 --- a/src/tools/clippy/lintcheck/src/config.rs +++ b/src/tools/clippy/lintcheck/src/config.rs @@ -2,6 +2,7 @@ use clap::{Parser, Subcommand, ValueEnum}; use std::num::NonZero; use std::path::PathBuf; +#[allow(clippy::struct_excessive_bools)] #[derive(Parser, Clone, Debug)] #[command(args_conflicts_with_subcommands = true)] pub(crate) struct LintcheckConfig { @@ -11,6 +12,9 @@ pub(crate) struct LintcheckConfig { short = 'j', value_name = "N", default_value_t = 0, + default_value_if("perf", "true", Some("1")), // Limit jobs to 1 when benchmarking + conflicts_with("perf"), + required = false, hide_default_value = true )] pub max_jobs: usize, @@ -46,6 +50,11 @@ pub(crate) struct LintcheckConfig { /// Run clippy on the dependencies of crates specified in crates-toml #[clap(long, conflicts_with("max_jobs"))] pub recursive: bool, + /// Also produce a `perf.data` file, implies --jobs=1, + /// the `perf.data` file can be found at + /// `target/lintcheck/sources/-/perf.data` + #[clap(long)] + pub perf: bool, #[command(subcommand)] pub subcommand: Option, } diff --git a/src/tools/clippy/lintcheck/src/main.rs b/src/tools/clippy/lintcheck/src/main.rs index e88d9f427becd..8d0d41ab9450f 100644 --- a/src/tools/clippy/lintcheck/src/main.rs +++ b/src/tools/clippy/lintcheck/src/main.rs @@ -116,7 +116,25 @@ impl Crate { clippy_args.extend(lint_levels_args.iter().map(String::as_str)); - let mut cmd = Command::new("cargo"); + let mut cmd; + + if config.perf { + cmd = Command::new("perf"); + cmd.args(&[ + "record", + "-e", + "instructions", // Only count instructions + "-g", // Enable call-graph, useful for flamegraphs and produces richer reports + "--quiet", // Do not tamper with lintcheck's normal output + "-o", + "perf.data", + "--", + "cargo", + ]); + } else { + cmd = Command::new("cargo"); + } + cmd.arg(if config.fix { "fix" } else { "check" }) .arg("--quiet") .current_dir(&self.path) @@ -234,12 +252,22 @@ fn normalize_diag( } /// Builds clippy inside the repo to make sure we have a clippy executable we can use. -fn build_clippy() -> String { - let output = Command::new("cargo") - .args(["run", "--bin=clippy-driver", "--", "--version"]) - .stderr(Stdio::inherit()) - .output() - .unwrap(); +fn build_clippy(release_build: bool) -> String { + let mut build_cmd = Command::new("cargo"); + build_cmd.args([ + "run", + "--bin=clippy-driver", + if release_build { "-r" } else { "" }, + "--", + "--version", + ]); + + if release_build { + build_cmd.env("CARGO_PROFILE_RELEASE_DEBUG", "true"); + } + + let output = build_cmd.stderr(Stdio::inherit()).output().unwrap(); + if !output.status.success() { eprintln!("Error: Failed to compile Clippy!"); std::process::exit(1); @@ -270,13 +298,18 @@ fn main() { #[allow(clippy::too_many_lines)] fn lintcheck(config: LintcheckConfig) { - let clippy_ver = build_clippy(); - let clippy_driver_path = fs::canonicalize(format!("target/debug/clippy-driver{EXE_SUFFIX}")).unwrap(); + let clippy_ver = build_clippy(config.perf); + let clippy_driver_path = fs::canonicalize(format!( + "target/{}/clippy-driver{EXE_SUFFIX}", + if config.perf { "release" } else { "debug" } + )) + .unwrap(); // assert that clippy is found assert!( clippy_driver_path.is_file(), - "target/debug/clippy-driver binary not found! {}", + "target/{}/clippy-driver binary not found! {}", + if config.perf { "release" } else { "debug" }, clippy_driver_path.display() ); diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain index c15d1fe6cd348..ab760287e83ae 100644 --- a/src/tools/clippy/rust-toolchain +++ b/src/tools/clippy/rust-toolchain @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2025-01-28" +channel = "nightly-2025-02-06" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" diff --git a/src/tools/clippy/tests/ui/cmp_null.fixed b/src/tools/clippy/tests/ui/cmp_null.fixed new file mode 100644 index 0000000000000..e5ab765bc8614 --- /dev/null +++ b/src/tools/clippy/tests/ui/cmp_null.fixed @@ -0,0 +1,32 @@ +#![warn(clippy::cmp_null)] +#![allow(unused_mut)] + +use std::ptr; + +fn main() { + let x = 0; + let p: *const usize = &x; + if p.is_null() { + //~^ ERROR: comparing with null is better expressed by the `.is_null()` method + //~| NOTE: `-D clippy::cmp-null` implied by `-D warnings` + println!("This is surprising!"); + } + if p.is_null() { + //~^ ERROR: comparing with null is better expressed by the `.is_null()` method + println!("This is surprising!"); + } + + let mut y = 0; + let mut m: *mut usize = &mut y; + if m.is_null() { + //~^ ERROR: comparing with null is better expressed by the `.is_null()` method + println!("This is surprising, too!"); + } + if m.is_null() { + //~^ ERROR: comparing with null is better expressed by the `.is_null()` method + println!("This is surprising, too!"); + } + + let _ = (x as *const ()).is_null(); + //~^ ERROR: comparing with null is better expressed by the `.is_null()` method +} diff --git a/src/tools/clippy/tests/ui/cmp_null.rs b/src/tools/clippy/tests/ui/cmp_null.rs index ef1d93940aa6e..257f7ba266277 100644 --- a/src/tools/clippy/tests/ui/cmp_null.rs +++ b/src/tools/clippy/tests/ui/cmp_null.rs @@ -11,10 +11,22 @@ fn main() { //~| NOTE: `-D clippy::cmp-null` implied by `-D warnings` println!("This is surprising!"); } + if ptr::null() == p { + //~^ ERROR: comparing with null is better expressed by the `.is_null()` method + println!("This is surprising!"); + } + let mut y = 0; let mut m: *mut usize = &mut y; if m == ptr::null_mut() { //~^ ERROR: comparing with null is better expressed by the `.is_null()` method println!("This is surprising, too!"); } + if ptr::null_mut() == m { + //~^ ERROR: comparing with null is better expressed by the `.is_null()` method + println!("This is surprising, too!"); + } + + let _ = x as *const () == ptr::null(); + //~^ ERROR: comparing with null is better expressed by the `.is_null()` method } diff --git a/src/tools/clippy/tests/ui/cmp_null.stderr b/src/tools/clippy/tests/ui/cmp_null.stderr index 8362904a5ba9d..f3b35f3afba8b 100644 --- a/src/tools/clippy/tests/ui/cmp_null.stderr +++ b/src/tools/clippy/tests/ui/cmp_null.stderr @@ -2,16 +2,34 @@ error: comparing with null is better expressed by the `.is_null()` method --> tests/ui/cmp_null.rs:9:8 | LL | if p == ptr::null() { - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ help: try: `p.is_null()` | = note: `-D clippy::cmp-null` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::cmp_null)]` error: comparing with null is better expressed by the `.is_null()` method - --> tests/ui/cmp_null.rs:16:8 + --> tests/ui/cmp_null.rs:14:8 + | +LL | if ptr::null() == p { + | ^^^^^^^^^^^^^^^^ help: try: `p.is_null()` + +error: comparing with null is better expressed by the `.is_null()` method + --> tests/ui/cmp_null.rs:21:8 | LL | if m == ptr::null_mut() { - | ^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^ help: try: `m.is_null()` + +error: comparing with null is better expressed by the `.is_null()` method + --> tests/ui/cmp_null.rs:25:8 + | +LL | if ptr::null_mut() == m { + | ^^^^^^^^^^^^^^^^^^^^ help: try: `m.is_null()` + +error: comparing with null is better expressed by the `.is_null()` method + --> tests/ui/cmp_null.rs:30:13 + | +LL | let _ = x as *const () == ptr::null(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(x as *const ()).is_null()` -error: aborting due to 2 previous errors +error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/eta.fixed b/src/tools/clippy/tests/ui/eta.fixed index f1baf28200e96..abccc30ef87dd 100644 --- a/src/tools/clippy/tests/ui/eta.fixed +++ b/src/tools/clippy/tests/ui/eta.fixed @@ -116,6 +116,11 @@ fn test_redundant_closures_containing_method_calls() { t.iter().filter(|x| x.trait_foo_ref()); t.iter().map(|x| x.trait_foo_ref()); } + + fn issue14096() { + let x = Some("42"); + let _ = x.map(str::parse::); + } } struct Thunk(Box T>); diff --git a/src/tools/clippy/tests/ui/eta.rs b/src/tools/clippy/tests/ui/eta.rs index c52a51880bfc9..9bcee4eba3409 100644 --- a/src/tools/clippy/tests/ui/eta.rs +++ b/src/tools/clippy/tests/ui/eta.rs @@ -116,6 +116,11 @@ fn test_redundant_closures_containing_method_calls() { t.iter().filter(|x| x.trait_foo_ref()); t.iter().map(|x| x.trait_foo_ref()); } + + fn issue14096() { + let x = Some("42"); + let _ = x.map(|x| x.parse::()); + } } struct Thunk(Box T>); diff --git a/src/tools/clippy/tests/ui/eta.stderr b/src/tools/clippy/tests/ui/eta.stderr index 1731a4377f534..ac58e87bc5ecd 100644 --- a/src/tools/clippy/tests/ui/eta.stderr +++ b/src/tools/clippy/tests/ui/eta.stderr @@ -71,142 +71,148 @@ LL | let e: std::vec::Vec = vec!['a', 'b', 'c'].iter().map(|c| c.to_as | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_ascii_uppercase` error: redundant closure - --> tests/ui/eta.rs:169:22 + --> tests/ui/eta.rs:122:23 + | +LL | let _ = x.map(|x| x.parse::()); + | ^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `str::parse::` + +error: redundant closure + --> tests/ui/eta.rs:174:22 | LL | requires_fn_once(|| x()); | ^^^^^^ help: replace the closure with the function itself: `x` error: redundant closure - --> tests/ui/eta.rs:176:27 + --> tests/ui/eta.rs:181:27 | LL | let a = Some(1u8).map(|a| foo_ptr(a)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `foo_ptr` error: redundant closure - --> tests/ui/eta.rs:181:27 + --> tests/ui/eta.rs:186:27 | LL | let a = Some(1u8).map(|a| closure(a)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `closure` error: redundant closure - --> tests/ui/eta.rs:213:28 + --> tests/ui/eta.rs:218:28 | LL | x.into_iter().for_each(|x| add_to_res(x)); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res` error: redundant closure - --> tests/ui/eta.rs:214:28 + --> tests/ui/eta.rs:219:28 | LL | y.into_iter().for_each(|x| add_to_res(x)); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res` error: redundant closure - --> tests/ui/eta.rs:215:28 + --> tests/ui/eta.rs:220:28 | LL | z.into_iter().for_each(|x| add_to_res(x)); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `add_to_res` error: redundant closure - --> tests/ui/eta.rs:222:21 + --> tests/ui/eta.rs:227:21 | LL | Some(1).map(|n| closure(n)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut closure` error: redundant closure - --> tests/ui/eta.rs:226:21 + --> tests/ui/eta.rs:231:21 | LL | Some(1).map(|n| in_loop(n)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `in_loop` error: redundant closure - --> tests/ui/eta.rs:319:18 + --> tests/ui/eta.rs:324:18 | LL | takes_fn_mut(|| f()); | ^^^^^^ help: replace the closure with the function itself: `&mut f` error: redundant closure - --> tests/ui/eta.rs:322:19 + --> tests/ui/eta.rs:327:19 | LL | takes_fn_once(|| f()); | ^^^^^^ help: replace the closure with the function itself: `&mut f` error: redundant closure - --> tests/ui/eta.rs:326:26 + --> tests/ui/eta.rs:331:26 | LL | move || takes_fn_mut(|| f_used_once()) | ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut f_used_once` error: redundant closure - --> tests/ui/eta.rs:338:19 + --> tests/ui/eta.rs:343:19 | LL | array_opt.map(|a| a.as_slice()); | ^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `<[u8; 3]>::as_slice` error: redundant closure - --> tests/ui/eta.rs:341:19 + --> tests/ui/eta.rs:346:19 | LL | slice_opt.map(|s| s.len()); | ^^^^^^^^^^^ help: replace the closure with the method itself: `<[u8]>::len` error: redundant closure - --> tests/ui/eta.rs:344:17 + --> tests/ui/eta.rs:349:17 | LL | ptr_opt.map(|p| p.is_null()); | ^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `<*const usize>::is_null` error: redundant closure - --> tests/ui/eta.rs:348:17 + --> tests/ui/eta.rs:353:17 | LL | dyn_opt.map(|d| d.method_on_dyn()); | ^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `::method_on_dyn` error: redundant closure - --> tests/ui/eta.rs:408:19 + --> tests/ui/eta.rs:413:19 | LL | let _ = f(&0, |x, y| f2(x, y)); | ^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `f2` error: redundant closure - --> tests/ui/eta.rs:436:22 + --> tests/ui/eta.rs:441:22 | LL | test.map(|t| t.method()) | ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `Test::method` error: redundant closure - --> tests/ui/eta.rs:440:22 + --> tests/ui/eta.rs:445:22 | LL | test.map(|t| t.method()) | ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `super::Outer::method` error: redundant closure - --> tests/ui/eta.rs:453:18 + --> tests/ui/eta.rs:458:18 | LL | test.map(|t| t.method()) | ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `test_mod::Test::method` error: redundant closure - --> tests/ui/eta.rs:460:30 + --> tests/ui/eta.rs:465:30 | LL | test.map(|t| t.method()) | ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `crate::issue_10854::d::Test::method` error: redundant closure - --> tests/ui/eta.rs:479:38 + --> tests/ui/eta.rs:484:38 | LL | let x = Box::new(|| None.map(|x| f(x))); | ^^^^^^^^ help: replace the closure with the function itself: `&f` error: redundant closure - --> tests/ui/eta.rs:483:38 + --> tests/ui/eta.rs:488:38 | LL | let x = Box::new(|| None.map(|x| f(x))); | ^^^^^^^^ help: replace the closure with the function itself: `f` error: redundant closure - --> tests/ui/eta.rs:500:35 + --> tests/ui/eta.rs:505:35 | LL | let _field = bind.or_else(|| get_default()).unwrap(); | ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `get_default` -error: aborting due to 34 previous errors +error: aborting due to 35 previous errors diff --git a/src/tools/clippy/tests/ui/ignored_unit_patterns.fixed b/src/tools/clippy/tests/ui/ignored_unit_patterns.fixed index fde4043730986..118f0b4889529 100644 --- a/src/tools/clippy/tests/ui/ignored_unit_patterns.fixed +++ b/src/tools/clippy/tests/ui/ignored_unit_patterns.fixed @@ -21,12 +21,15 @@ fn main() { let _ = foo().map_err(|()| todo!()); //~^ ERROR: matching over `()` is more explicit - println!("{:?}", match foo() { - Ok(()) => {}, - //~^ ERROR: matching over `()` is more explicit - Err(()) => {}, - //~^ ERROR: matching over `()` is more explicit - }); + println!( + "{:?}", + match foo() { + Ok(()) => {}, + //~^ ERROR: matching over `()` is more explicit + Err(()) => {}, + //~^ ERROR: matching over `()` is more explicit + } + ); } // ignored_unit_patterns in derive macro should be ok diff --git a/src/tools/clippy/tests/ui/ignored_unit_patterns.rs b/src/tools/clippy/tests/ui/ignored_unit_patterns.rs index 528844d76e051..92feb9e6c2814 100644 --- a/src/tools/clippy/tests/ui/ignored_unit_patterns.rs +++ b/src/tools/clippy/tests/ui/ignored_unit_patterns.rs @@ -21,12 +21,15 @@ fn main() { let _ = foo().map_err(|_| todo!()); //~^ ERROR: matching over `()` is more explicit - println!("{:?}", match foo() { - Ok(_) => {}, - //~^ ERROR: matching over `()` is more explicit - Err(_) => {}, - //~^ ERROR: matching over `()` is more explicit - }); + println!( + "{:?}", + match foo() { + Ok(_) => {}, + //~^ ERROR: matching over `()` is more explicit + Err(_) => {}, + //~^ ERROR: matching over `()` is more explicit + } + ); } // ignored_unit_patterns in derive macro should be ok diff --git a/src/tools/clippy/tests/ui/ignored_unit_patterns.stderr b/src/tools/clippy/tests/ui/ignored_unit_patterns.stderr index 54ff4454d6bdf..00a254e39192c 100644 --- a/src/tools/clippy/tests/ui/ignored_unit_patterns.stderr +++ b/src/tools/clippy/tests/ui/ignored_unit_patterns.stderr @@ -26,31 +26,31 @@ LL | let _ = foo().map_err(|_| todo!()); | ^ help: use `()` instead of `_`: `()` error: matching over `()` is more explicit - --> tests/ui/ignored_unit_patterns.rs:25:12 + --> tests/ui/ignored_unit_patterns.rs:27:16 | -LL | Ok(_) => {}, - | ^ help: use `()` instead of `_`: `()` +LL | Ok(_) => {}, + | ^ help: use `()` instead of `_`: `()` error: matching over `()` is more explicit - --> tests/ui/ignored_unit_patterns.rs:27:13 + --> tests/ui/ignored_unit_patterns.rs:29:17 | -LL | Err(_) => {}, - | ^ help: use `()` instead of `_`: `()` +LL | Err(_) => {}, + | ^ help: use `()` instead of `_`: `()` error: matching over `()` is more explicit - --> tests/ui/ignored_unit_patterns.rs:38:9 + --> tests/ui/ignored_unit_patterns.rs:41:9 | LL | let _ = foo().unwrap(); | ^ help: use `()` instead of `_`: `()` error: matching over `()` is more explicit - --> tests/ui/ignored_unit_patterns.rs:47:13 + --> tests/ui/ignored_unit_patterns.rs:50:13 | LL | (1, _) => unimplemented!(), | ^ help: use `()` instead of `_`: `()` error: matching over `()` is more explicit - --> tests/ui/ignored_unit_patterns.rs:54:13 + --> tests/ui/ignored_unit_patterns.rs:57:13 | LL | for (x, _) in v { | ^ help: use `()` instead of `_`: `()` diff --git a/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.rs b/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.rs index c917fa7f2d0b1..2f8640cd3f50f 100644 --- a/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.rs +++ b/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.rs @@ -1,5 +1,10 @@ #![warn(clippy::manual_memcpy)] -#![allow(clippy::assigning_clones, clippy::useless_vec, clippy::needless_range_loop)] +#![allow( + clippy::assigning_clones, + clippy::useless_vec, + clippy::needless_range_loop, + clippy::manual_slice_fill +)] //@no-rustfix const LOOP_OFFSET: usize = 5000; diff --git a/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.stderr b/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.stderr index 803053b2db2ef..c881e3fac769f 100644 --- a/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.stderr +++ b/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.stderr @@ -1,5 +1,5 @@ error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:9:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:14:5 | LL | / for i in 0..src.len() { LL | | @@ -12,7 +12,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::manual_memcpy)]` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:16:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:21:5 | LL | / for i in 0..src.len() { LL | | @@ -21,7 +21,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[10..(src.len() + 10)].copy_from_slice(&src[..]);` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:22:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:27:5 | LL | / for i in 0..src.len() { LL | | @@ -30,7 +30,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[..src.len()].copy_from_slice(&src[10..(src.len() + 10)]);` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:28:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:33:5 | LL | / for i in 11..src.len() { LL | | @@ -39,7 +39,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[11..src.len()].copy_from_slice(&src[(11 - 10)..(src.len() - 10)]);` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:34:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:39:5 | LL | / for i in 0..dst.len() { LL | | @@ -48,7 +48,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst.copy_from_slice(&src[..dst.len()]);` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:48:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:53:5 | LL | / for i in 10..256 { LL | | @@ -64,7 +64,7 @@ LL + dst2[(10 + 500)..(256 + 500)].copy_from_slice(&src[10..256]); | error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:61:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:66:5 | LL | / for i in 10..LOOP_OFFSET { LL | | @@ -73,7 +73,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[(10 + LOOP_OFFSET)..(LOOP_OFFSET + LOOP_OFFSET)].copy_from_slice(&src[(10 - some_var)..(LOOP_OFFSET - some_var)]);` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:75:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:80:5 | LL | / for i in 0..src_vec.len() { LL | | @@ -82,7 +82,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst_vec[..src_vec.len()].copy_from_slice(&src_vec[..]);` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:105:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:110:5 | LL | / for i in from..from + src.len() { LL | | @@ -91,7 +91,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[from..(from + src.len())].copy_from_slice(&src[..(from + src.len() - from)]);` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:110:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:115:5 | LL | / for i in from..from + 3 { LL | | @@ -100,7 +100,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[from..(from + 3)].copy_from_slice(&src[..(from + 3 - from)]);` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:116:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:121:5 | LL | / for i in 0..5 { LL | | @@ -109,7 +109,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[..5].copy_from_slice(&src);` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:122:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:127:5 | LL | / for i in 0..0 { LL | | @@ -118,7 +118,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[..0].copy_from_slice(&src[..0]);` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:146:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:151:5 | LL | / for i in 0..4 { LL | | @@ -127,7 +127,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst.copy_from_slice(&src[..4]);` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:152:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:157:5 | LL | / for i in 0..5 { LL | | @@ -136,7 +136,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[..5].copy_from_slice(&src);` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:158:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:163:5 | LL | / for i in 0..5 { LL | | @@ -145,7 +145,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst.copy_from_slice(&src);` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:205:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:210:5 | LL | / for i in 0..5 { LL | | @@ -154,7 +154,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst.copy_from_slice(&src[0]);` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:211:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:216:5 | LL | / for i in 0..5 { LL | | @@ -163,7 +163,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst.copy_from_slice(&src[0][1]);` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:219:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:224:5 | LL | / for i in 0..src.len() { LL | | diff --git a/src/tools/clippy/tests/ui/manual_option_as_slice.fixed b/src/tools/clippy/tests/ui/manual_option_as_slice.fixed new file mode 100644 index 0000000000000..48337d7654dea --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_option_as_slice.fixed @@ -0,0 +1,62 @@ +#![warn(clippy::manual_option_as_slice)] +#![allow(clippy::redundant_closure, clippy::unwrap_or_default)] + +fn check(x: Option) { + _ = x.as_slice(); + + _ = x.as_slice(); + + _ = x.as_slice(); + //~^ manual_option_as_slice + + _ = x.as_slice(); + //~^ manual_option_as_slice + + _ = x.as_slice(); + //~^ manual_option_as_slice + + _ = x.as_slice(); + //~^ manual_option_as_slice + + { + use std::slice::from_ref; + _ = x.as_slice(); + //~^ manual_option_as_slice + } + + // possible false positives + let y = x.as_ref(); + _ = match y { + // as_ref outside + Some(f) => &[f][..], + None => &[][..], + }; + _ = match x.as_ref() { + Some(f) => std::slice::from_ref(f), + None => &[0], + }; + _ = match x.as_ref() { + Some(42) => &[23], + Some(f) => std::slice::from_ref(f), + None => &[], + }; + let b = &[42]; + _ = if let Some(_f) = x.as_ref() { + std::slice::from_ref(b) + } else { + &[] + }; + _ = x.as_ref().map_or(&[42][..], std::slice::from_ref); + _ = x.as_ref().map_or_else(|| &[42][..1], std::slice::from_ref); + _ = x.as_ref().map(|f| std::slice::from_ref(f)).unwrap_or_default(); +} + +#[clippy::msrv = "1.74"] +fn check_msrv(x: Option) { + _ = x.as_ref().map_or(&[][..], std::slice::from_ref); +} + +fn main() { + check(Some(1)); + check_msrv(Some(175)); +} diff --git a/src/tools/clippy/tests/ui/manual_option_as_slice.rs b/src/tools/clippy/tests/ui/manual_option_as_slice.rs new file mode 100644 index 0000000000000..561a8b534014b --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_option_as_slice.rs @@ -0,0 +1,71 @@ +#![warn(clippy::manual_option_as_slice)] +#![allow(clippy::redundant_closure, clippy::unwrap_or_default)] + +fn check(x: Option) { + _ = match x.as_ref() { + //~^ manual_option_as_slice + Some(f) => std::slice::from_ref(f), + None => &[], + }; + + _ = if let Some(f) = x.as_ref() { + //~^ manual_option_as_slice + std::slice::from_ref(f) + } else { + &[] + }; + + _ = x.as_ref().map_or(&[][..], std::slice::from_ref); + //~^ manual_option_as_slice + + _ = x.as_ref().map_or_else(Default::default, std::slice::from_ref); + //~^ manual_option_as_slice + + _ = x.as_ref().map(std::slice::from_ref).unwrap_or_default(); + //~^ manual_option_as_slice + + _ = x.as_ref().map_or_else(|| &[42][..0], std::slice::from_ref); + //~^ manual_option_as_slice + + { + use std::slice::from_ref; + _ = x.as_ref().map_or_else(<&[_]>::default, from_ref); + //~^ manual_option_as_slice + } + + // possible false positives + let y = x.as_ref(); + _ = match y { + // as_ref outside + Some(f) => &[f][..], + None => &[][..], + }; + _ = match x.as_ref() { + Some(f) => std::slice::from_ref(f), + None => &[0], + }; + _ = match x.as_ref() { + Some(42) => &[23], + Some(f) => std::slice::from_ref(f), + None => &[], + }; + let b = &[42]; + _ = if let Some(_f) = x.as_ref() { + std::slice::from_ref(b) + } else { + &[] + }; + _ = x.as_ref().map_or(&[42][..], std::slice::from_ref); + _ = x.as_ref().map_or_else(|| &[42][..1], std::slice::from_ref); + _ = x.as_ref().map(|f| std::slice::from_ref(f)).unwrap_or_default(); +} + +#[clippy::msrv = "1.74"] +fn check_msrv(x: Option) { + _ = x.as_ref().map_or(&[][..], std::slice::from_ref); +} + +fn main() { + check(Some(1)); + check_msrv(Some(175)); +} diff --git a/src/tools/clippy/tests/ui/manual_option_as_slice.stderr b/src/tools/clippy/tests/ui/manual_option_as_slice.stderr new file mode 100644 index 0000000000000..569269d3e2b79 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_option_as_slice.stderr @@ -0,0 +1,58 @@ +error: use `Option::as_slice` + --> tests/ui/manual_option_as_slice.rs:5:9 + | +LL | _ = match x.as_ref() { + | _________^ +LL | | +LL | | Some(f) => std::slice::from_ref(f), +LL | | None => &[], +LL | | }; + | |_____^ help: use: `x.as_slice()` + | + = note: `-D clippy::manual-option-as-slice` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::manual_option_as_slice)]` + +error: use `Option::as_slice` + --> tests/ui/manual_option_as_slice.rs:11:9 + | +LL | _ = if let Some(f) = x.as_ref() { + | _________^ +LL | | +LL | | std::slice::from_ref(f) +LL | | } else { +LL | | &[] +LL | | }; + | |_____^ help: use: `x.as_slice()` + +error: use `Option::as_slice` + --> tests/ui/manual_option_as_slice.rs:18:9 + | +LL | _ = x.as_ref().map_or(&[][..], std::slice::from_ref); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.as_slice()` + +error: use `Option::as_slice` + --> tests/ui/manual_option_as_slice.rs:21:9 + | +LL | _ = x.as_ref().map_or_else(Default::default, std::slice::from_ref); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.as_slice()` + +error: use `Option::as_slice` + --> tests/ui/manual_option_as_slice.rs:24:9 + | +LL | _ = x.as_ref().map(std::slice::from_ref).unwrap_or_default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.as_slice()` + +error: use `Option::as_slice` + --> tests/ui/manual_option_as_slice.rs:27:9 + | +LL | _ = x.as_ref().map_or_else(|| &[42][..0], std::slice::from_ref); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.as_slice()` + +error: use `Option::as_slice` + --> tests/ui/manual_option_as_slice.rs:32:13 + | +LL | _ = x.as_ref().map_or_else(<&[_]>::default, from_ref); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.as_slice()` + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/manual_slice_fill.fixed b/src/tools/clippy/tests/ui/manual_slice_fill.fixed new file mode 100644 index 0000000000000..397a156a2dc77 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_slice_fill.fixed @@ -0,0 +1,101 @@ +#![warn(clippy::manual_slice_fill)] +#![allow(clippy::needless_range_loop)] + +macro_rules! assign_element { + ($slice:ident, $index:expr) => { + $slice[$index] = 0; + }; +} + +macro_rules! assign_element_2 { + ($i:expr) => { + $i = 0; + }; +} + +struct NoClone; + +fn num() -> usize { + 5 +} + +fn should_lint() { + let mut some_slice = [1, 2, 3, 4, 5]; + + some_slice.fill(0); + + let x = 5; + some_slice.fill(x); + + some_slice.fill(0); + + // This should trigger `manual_slice_fill`, but the applicability is `MaybeIncorrect` since comments + // within the loop might be purely informational. + some_slice.fill(0); +} + +fn should_not_lint() { + let mut some_slice = [1, 2, 3, 4, 5]; + + // Should not lint because we can't determine if the scope of the loop is intended to access all the + // elements of the slice. + for i in 0..5 { + some_slice[i] = 0; + } + + // Should not lint, as using a function to assign values to elements might be + // intentional. For example, the contents of `num()` could be temporary and subject to change + // later. + for i in 0..some_slice.len() { + some_slice[i] = num(); + } + + // Should not lint because this loop isn't equivalent to `fill`. + for i in 0..some_slice.len() { + some_slice[i] = 0; + println!("foo"); + } + + // Should not lint because it may be intentional to use a macro to perform an operation equivalent + // to `fill`. + for i in 0..some_slice.len() { + assign_element!(some_slice, i); + } + + let another_slice = [1, 2, 3]; + // Should not lint because the range is not for `some_slice`. + for i in 0..another_slice.len() { + some_slice[i] = 0; + } + + let mut vec: Vec> = Vec::with_capacity(5); + // Should not lint because `NoClone` does not have `Clone` trait. + for i in 0..vec.len() { + vec[i] = None; + } + + // Should not lint, as using a function to assign values to elements might be + // intentional. For example, the contents of `num()` could be temporary and subject to change + // later. + for i in &mut some_slice { + *i = num(); + } + + // Should not lint because this loop isn't equivalent to `fill`. + for i in &mut some_slice { + *i = 0; + println!("foo"); + } + + // Should not lint because it may be intentional to use a macro to perform an operation equivalent + // to `fill`. + for i in &mut some_slice { + assign_element_2!(*i); + } + + let mut vec: Vec> = Vec::with_capacity(5); + // Should not lint because `NoClone` does not have `Clone` trait. + for i in &mut vec { + *i = None; + } +} diff --git a/src/tools/clippy/tests/ui/manual_slice_fill.rs b/src/tools/clippy/tests/ui/manual_slice_fill.rs new file mode 100644 index 0000000000000..c25127ca613ab --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_slice_fill.rs @@ -0,0 +1,110 @@ +#![warn(clippy::manual_slice_fill)] +#![allow(clippy::needless_range_loop)] + +macro_rules! assign_element { + ($slice:ident, $index:expr) => { + $slice[$index] = 0; + }; +} + +macro_rules! assign_element_2 { + ($i:expr) => { + $i = 0; + }; +} + +struct NoClone; + +fn num() -> usize { + 5 +} + +fn should_lint() { + let mut some_slice = [1, 2, 3, 4, 5]; + + for i in 0..some_slice.len() { + some_slice[i] = 0; + } + + let x = 5; + for i in 0..some_slice.len() { + some_slice[i] = x; + } + + for i in &mut some_slice { + *i = 0; + } + + // This should trigger `manual_slice_fill`, but the applicability is `MaybeIncorrect` since comments + // within the loop might be purely informational. + for i in 0..some_slice.len() { + some_slice[i] = 0; + // foo + } +} + +fn should_not_lint() { + let mut some_slice = [1, 2, 3, 4, 5]; + + // Should not lint because we can't determine if the scope of the loop is intended to access all the + // elements of the slice. + for i in 0..5 { + some_slice[i] = 0; + } + + // Should not lint, as using a function to assign values to elements might be + // intentional. For example, the contents of `num()` could be temporary and subject to change + // later. + for i in 0..some_slice.len() { + some_slice[i] = num(); + } + + // Should not lint because this loop isn't equivalent to `fill`. + for i in 0..some_slice.len() { + some_slice[i] = 0; + println!("foo"); + } + + // Should not lint because it may be intentional to use a macro to perform an operation equivalent + // to `fill`. + for i in 0..some_slice.len() { + assign_element!(some_slice, i); + } + + let another_slice = [1, 2, 3]; + // Should not lint because the range is not for `some_slice`. + for i in 0..another_slice.len() { + some_slice[i] = 0; + } + + let mut vec: Vec> = Vec::with_capacity(5); + // Should not lint because `NoClone` does not have `Clone` trait. + for i in 0..vec.len() { + vec[i] = None; + } + + // Should not lint, as using a function to assign values to elements might be + // intentional. For example, the contents of `num()` could be temporary and subject to change + // later. + for i in &mut some_slice { + *i = num(); + } + + // Should not lint because this loop isn't equivalent to `fill`. + for i in &mut some_slice { + *i = 0; + println!("foo"); + } + + // Should not lint because it may be intentional to use a macro to perform an operation equivalent + // to `fill`. + for i in &mut some_slice { + assign_element_2!(*i); + } + + let mut vec: Vec> = Vec::with_capacity(5); + // Should not lint because `NoClone` does not have `Clone` trait. + for i in &mut vec { + *i = None; + } +} diff --git a/src/tools/clippy/tests/ui/manual_slice_fill.stderr b/src/tools/clippy/tests/ui/manual_slice_fill.stderr new file mode 100644 index 0000000000000..3aa980f691916 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_slice_fill.stderr @@ -0,0 +1,38 @@ +error: manually filling a slice + --> tests/ui/manual_slice_fill.rs:25:5 + | +LL | / for i in 0..some_slice.len() { +LL | | some_slice[i] = 0; +LL | | } + | |_____^ help: try: `some_slice.fill(0);` + | + = note: `-D clippy::manual-slice-fill` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::manual_slice_fill)]` + +error: manually filling a slice + --> tests/ui/manual_slice_fill.rs:30:5 + | +LL | / for i in 0..some_slice.len() { +LL | | some_slice[i] = x; +LL | | } + | |_____^ help: try: `some_slice.fill(x);` + +error: manually filling a slice + --> tests/ui/manual_slice_fill.rs:34:5 + | +LL | / for i in &mut some_slice { +LL | | *i = 0; +LL | | } + | |_____^ help: try: `some_slice.fill(0);` + +error: manually filling a slice + --> tests/ui/manual_slice_fill.rs:40:5 + | +LL | / for i in 0..some_slice.len() { +LL | | some_slice[i] = 0; +LL | | // foo +LL | | } + | |_____^ help: try: `some_slice.fill(0);` + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or_default_unfixable.rs b/src/tools/clippy/tests/ui/manual_unwrap_or_default_unfixable.rs new file mode 100644 index 0000000000000..acc54b52816cc --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_unwrap_or_default_unfixable.rs @@ -0,0 +1,15 @@ +//@no-rustfix +fn issue_12670() { + // no auto: type not found + #[allow(clippy::match_result_ok)] + let _ = if let Some(x) = "1".parse().ok() { + x + } else { + i32::default() + }; + let _ = if let Some(x) = None { x } else { i32::default() }; + // auto fix with unwrap_or_default + let a: Option = None; + let _ = if let Some(x) = a { x } else { i32::default() }; + let _ = if let Some(x) = Some(99) { x } else { i32::default() }; +} diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or_default_unfixable.stderr b/src/tools/clippy/tests/ui/manual_unwrap_or_default_unfixable.stderr new file mode 100644 index 0000000000000..3849d33cf254b --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_unwrap_or_default_unfixable.stderr @@ -0,0 +1,34 @@ +error: if let can be simplified with `.unwrap_or_default()` + --> tests/ui/manual_unwrap_or_default_unfixable.rs:5:13 + | +LL | let _ = if let Some(x) = "1".parse().ok() { + | _____________^ +LL | | x +LL | | } else { +LL | | i32::default() +LL | | }; + | |_____^ help: ascribe the type i32 and replace your expression with: `"1".parse().ok().unwrap_or_default()` + | + = note: `-D clippy::manual-unwrap-or-default` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::manual_unwrap_or_default)]` + +error: if let can be simplified with `.unwrap_or_default()` + --> tests/ui/manual_unwrap_or_default_unfixable.rs:10:13 + | +LL | let _ = if let Some(x) = None { x } else { i32::default() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `None::.unwrap_or_default()` + +error: if let can be simplified with `.unwrap_or_default()` + --> tests/ui/manual_unwrap_or_default_unfixable.rs:13:13 + | +LL | let _ = if let Some(x) = a { x } else { i32::default() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `a.unwrap_or_default()` + +error: if let can be simplified with `.unwrap_or_default()` + --> tests/ui/manual_unwrap_or_default_unfixable.rs:14:13 + | +LL | let _ = if let Some(x) = Some(99) { x } else { i32::default() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `Some(99).unwrap_or_default()` + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/needless_option_take.fixed b/src/tools/clippy/tests/ui/needless_option_take.fixed new file mode 100644 index 0000000000000..6514b67ef7a71 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_option_take.fixed @@ -0,0 +1,58 @@ +struct MyStruct; + +impl MyStruct { + pub fn get_option() -> Option { + todo!() + } +} + +fn return_option() -> Option { + todo!() +} + +fn main() { + println!("Testing non erroneous option_take_on_temporary"); + let mut option = Some(1); + let _ = Box::new(move || option.take().unwrap()); + + println!("Testing non erroneous option_take_on_temporary"); + let x = Some(3); + x.as_ref(); + + let x = Some(3); + x.as_ref(); + //~^ ERROR: called `Option::take()` on a temporary value + + println!("Testing non erroneous option_take_on_temporary"); + let mut x = Some(3); + let y = x.as_mut(); + + let mut x = Some(3); + let y = x.as_mut(); + //~^ ERROR: called `Option::take()` on a temporary value + let y = x.replace(289); + //~^ ERROR: called `Option::take()` on a temporary value + + let y = Some(3).as_mut(); + //~^ ERROR: called `Option::take()` on a temporary value + + let y = Option::as_mut(&mut x); + //~^ ERROR: called `Option::take()` on a temporary value + + let x = return_option(); + let x = return_option(); + //~^ ERROR: called `Option::take()` on a temporary value + + let x = MyStruct::get_option(); + let x = MyStruct::get_option(); + //~^ ERROR: called `Option::take()` on a temporary value + + let mut my_vec = vec![1, 2, 3]; + my_vec.push(4); + let y = my_vec.first(); + let y = my_vec.first(); + //~^ ERROR: called `Option::take()` on a temporary value + + let y = my_vec.first(); + //~^ ERROR: called `Option::take()` on a temporary value +} diff --git a/src/tools/clippy/tests/ui/needless_option_take.stderr b/src/tools/clippy/tests/ui/needless_option_take.stderr index e036bd53170ab..3fc339ed79e2a 100644 --- a/src/tools/clippy/tests/ui/needless_option_take.stderr +++ b/src/tools/clippy/tests/ui/needless_option_take.stderr @@ -2,7 +2,9 @@ error: called `Option::take()` on a temporary value --> tests/ui/needless_option_take.rs:23:5 | LL | x.as_ref().take(); - | ^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^------- + | | + | help: remove | = note: `as_ref` creates a temporary value, so calling take() has no effect = note: `-D clippy::needless-option-take` implied by `-D warnings` @@ -12,7 +14,9 @@ error: called `Option::take()` on a temporary value --> tests/ui/needless_option_take.rs:31:13 | LL | let y = x.as_mut().take(); - | ^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^------- + | | + | help: remove | = note: `as_mut` creates a temporary value, so calling take() has no effect @@ -20,7 +24,9 @@ error: called `Option::take()` on a temporary value --> tests/ui/needless_option_take.rs:33:13 | LL | let y = x.replace(289).take(); - | ^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^------- + | | + | help: remove | = note: `replace` creates a temporary value, so calling take() has no effect @@ -28,7 +34,9 @@ error: called `Option::take()` on a temporary value --> tests/ui/needless_option_take.rs:36:13 | LL | let y = Some(3).as_mut().take(); - | ^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^------- + | | + | help: remove | = note: `as_mut` creates a temporary value, so calling take() has no effect @@ -36,7 +44,9 @@ error: called `Option::take()` on a temporary value --> tests/ui/needless_option_take.rs:39:13 | LL | let y = Option::as_mut(&mut x).take(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^------- + | | + | help: remove | = note: `as_mut` creates a temporary value, so calling take() has no effect @@ -44,7 +54,9 @@ error: called `Option::take()` on a temporary value --> tests/ui/needless_option_take.rs:43:13 | LL | let x = return_option().take(); - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^------- + | | + | help: remove | = note: `return_option` creates a temporary value, so calling take() has no effect @@ -52,7 +64,9 @@ error: called `Option::take()` on a temporary value --> tests/ui/needless_option_take.rs:47:13 | LL | let x = MyStruct::get_option().take(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^------- + | | + | help: remove | = note: `get_option` creates a temporary value, so calling take() has no effect @@ -60,7 +74,9 @@ error: called `Option::take()` on a temporary value --> tests/ui/needless_option_take.rs:53:13 | LL | let y = my_vec.first().take(); - | ^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^------- + | | + | help: remove | = note: `first` creates a temporary value, so calling take() has no effect @@ -68,7 +84,9 @@ error: called `Option::take()` on a temporary value --> tests/ui/needless_option_take.rs:56:13 | LL | let y = my_vec.first().take(); - | ^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^------- + | | + | help: remove | = note: `first` creates a temporary value, so calling take() has no effect diff --git a/src/tools/clippy/tests/ui/needless_range_loop.rs b/src/tools/clippy/tests/ui/needless_range_loop.rs index 3f2421953301e..75f1896eded17 100644 --- a/src/tools/clippy/tests/ui/needless_range_loop.rs +++ b/src/tools/clippy/tests/ui/needless_range_loop.rs @@ -2,7 +2,8 @@ #![allow( clippy::uninlined_format_args, clippy::unnecessary_literal_unwrap, - clippy::useless_vec + clippy::useless_vec, + clippy::manual_slice_fill )] //@no-rustfix static STATIC: [usize; 4] = [0, 1, 8, 16]; diff --git a/src/tools/clippy/tests/ui/needless_range_loop.stderr b/src/tools/clippy/tests/ui/needless_range_loop.stderr index dc2cf437e02ea..503d796e5e8d5 100644 --- a/src/tools/clippy/tests/ui/needless_range_loop.stderr +++ b/src/tools/clippy/tests/ui/needless_range_loop.stderr @@ -1,5 +1,5 @@ error: the loop variable `i` is only used to index `vec` - --> tests/ui/needless_range_loop.rs:15:14 + --> tests/ui/needless_range_loop.rs:16:14 | LL | for i in 0..vec.len() { | ^^^^^^^^^^^^ @@ -12,7 +12,7 @@ LL | for in &vec { | ~~~~~~ ~~~~ error: the loop variable `i` is only used to index `vec` - --> tests/ui/needless_range_loop.rs:26:14 + --> tests/ui/needless_range_loop.rs:27:14 | LL | for i in 0..vec.len() { | ^^^^^^^^^^^^ @@ -23,7 +23,7 @@ LL | for in &vec { | ~~~~~~ ~~~~ error: the loop variable `j` is only used to index `STATIC` - --> tests/ui/needless_range_loop.rs:32:14 + --> tests/ui/needless_range_loop.rs:33:14 | LL | for j in 0..4 { | ^^^^ @@ -34,7 +34,7 @@ LL | for in &STATIC { | ~~~~~~ ~~~~~~~ error: the loop variable `j` is only used to index `CONST` - --> tests/ui/needless_range_loop.rs:37:14 + --> tests/ui/needless_range_loop.rs:38:14 | LL | for j in 0..4 { | ^^^^ @@ -45,7 +45,7 @@ LL | for in &CONST { | ~~~~~~ ~~~~~~ error: the loop variable `i` is used to index `vec` - --> tests/ui/needless_range_loop.rs:42:14 + --> tests/ui/needless_range_loop.rs:43:14 | LL | for i in 0..vec.len() { | ^^^^^^^^^^^^ @@ -56,7 +56,7 @@ LL | for (i, ) in vec.iter().enumerate() { | ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~ error: the loop variable `i` is only used to index `vec2` - --> tests/ui/needless_range_loop.rs:51:14 + --> tests/ui/needless_range_loop.rs:52:14 | LL | for i in 0..vec.len() { | ^^^^^^^^^^^^ @@ -67,7 +67,7 @@ LL | for in vec2.iter().take(vec.len()) { | ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: the loop variable `i` is only used to index `vec` - --> tests/ui/needless_range_loop.rs:56:14 + --> tests/ui/needless_range_loop.rs:57:14 | LL | for i in 5..vec.len() { | ^^^^^^^^^^^^ @@ -78,7 +78,7 @@ LL | for in vec.iter().skip(5) { | ~~~~~~ ~~~~~~~~~~~~~~~~~~ error: the loop variable `i` is only used to index `vec` - --> tests/ui/needless_range_loop.rs:61:14 + --> tests/ui/needless_range_loop.rs:62:14 | LL | for i in 0..MAX_LEN { | ^^^^^^^^^^ @@ -89,7 +89,7 @@ LL | for in vec.iter().take(MAX_LEN) { | ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~ error: the loop variable `i` is only used to index `vec` - --> tests/ui/needless_range_loop.rs:66:14 + --> tests/ui/needless_range_loop.rs:67:14 | LL | for i in 0..=MAX_LEN { | ^^^^^^^^^^^ @@ -100,7 +100,7 @@ LL | for in vec.iter().take(MAX_LEN + 1) { | ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: the loop variable `i` is only used to index `vec` - --> tests/ui/needless_range_loop.rs:71:14 + --> tests/ui/needless_range_loop.rs:72:14 | LL | for i in 5..10 { | ^^^^^ @@ -111,7 +111,7 @@ LL | for in vec.iter().take(10).skip(5) { | ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: the loop variable `i` is only used to index `vec` - --> tests/ui/needless_range_loop.rs:76:14 + --> tests/ui/needless_range_loop.rs:77:14 | LL | for i in 5..=10 { | ^^^^^^ @@ -122,7 +122,7 @@ LL | for in vec.iter().take(10 + 1).skip(5) { | ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: the loop variable `i` is used to index `vec` - --> tests/ui/needless_range_loop.rs:81:14 + --> tests/ui/needless_range_loop.rs:82:14 | LL | for i in 5..vec.len() { | ^^^^^^^^^^^^ @@ -133,7 +133,7 @@ LL | for (i, ) in vec.iter().enumerate().skip(5) { | ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: the loop variable `i` is used to index `vec` - --> tests/ui/needless_range_loop.rs:86:14 + --> tests/ui/needless_range_loop.rs:87:14 | LL | for i in 5..10 { | ^^^^^ @@ -144,7 +144,7 @@ LL | for (i, ) in vec.iter().enumerate().take(10).skip(5) { | ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: the loop variable `i` is used to index `vec` - --> tests/ui/needless_range_loop.rs:92:14 + --> tests/ui/needless_range_loop.rs:93:14 | LL | for i in 0..vec.len() { | ^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/precedence.fixed b/src/tools/clippy/tests/ui/precedence.fixed index 9864dd2550b62..52144a18bac0c 100644 --- a/src/tools/clippy/tests/ui/precedence.fixed +++ b/src/tools/clippy/tests/ui/precedence.fixed @@ -20,10 +20,10 @@ fn main() { 1 ^ (1 - 1); 3 | (2 - 1); 3 & (5 - 2); - 0x0F00 & (0x00F0 << 4); - 0x0F00 & (0xF000 >> 4); - (0x0F00 << 1) ^ 3; - (0x0F00 << 1) | 2; + 0x0F00 & 0x00F0 << 4; + 0x0F00 & 0xF000 >> 4; + 0x0F00 << 1 ^ 3; + 0x0F00 << 1 | 2; let b = 3; trip!(b * 8); diff --git a/src/tools/clippy/tests/ui/precedence.stderr b/src/tools/clippy/tests/ui/precedence.stderr index 329422cb8a69b..68ad5cb4829ad 100644 --- a/src/tools/clippy/tests/ui/precedence.stderr +++ b/src/tools/clippy/tests/ui/precedence.stderr @@ -43,29 +43,5 @@ error: operator precedence might not be obvious LL | 3 & 5 - 2; | ^^^^^^^^^ help: consider parenthesizing your expression: `3 & (5 - 2)` -error: operator precedence might not be obvious - --> tests/ui/precedence.rs:23:5 - | -LL | 0x0F00 & 0x00F0 << 4; - | ^^^^^^^^^^^^^^^^^^^^ help: consider parenthesizing your expression: `0x0F00 & (0x00F0 << 4)` - -error: operator precedence might not be obvious - --> tests/ui/precedence.rs:24:5 - | -LL | 0x0F00 & 0xF000 >> 4; - | ^^^^^^^^^^^^^^^^^^^^ help: consider parenthesizing your expression: `0x0F00 & (0xF000 >> 4)` - -error: operator precedence might not be obvious - --> tests/ui/precedence.rs:25:5 - | -LL | 0x0F00 << 1 ^ 3; - | ^^^^^^^^^^^^^^^ help: consider parenthesizing your expression: `(0x0F00 << 1) ^ 3` - -error: operator precedence might not be obvious - --> tests/ui/precedence.rs:26:5 - | -LL | 0x0F00 << 1 | 2; - | ^^^^^^^^^^^^^^^ help: consider parenthesizing your expression: `(0x0F00 << 1) | 2` - -error: aborting due to 11 previous errors +error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/precedence_bits.fixed b/src/tools/clippy/tests/ui/precedence_bits.fixed new file mode 100644 index 0000000000000..82fea0d14e43b --- /dev/null +++ b/src/tools/clippy/tests/ui/precedence_bits.fixed @@ -0,0 +1,35 @@ +#![warn(clippy::precedence_bits)] +#![allow( + unused_must_use, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::precedence +)] +#![allow(clippy::identity_op)] +#![allow(clippy::eq_op)] + +macro_rules! trip { + ($a:expr) => { + match $a & 0b1111_1111u8 { + 0 => println!("a is zero ({})", $a), + _ => println!("a is {}", $a), + } + }; +} + +fn main() { + 1 << 2 + 3; + 1 + 2 << 3; + 4 >> 1 + 1; + 1 + 3 >> 2; + 1 ^ 1 - 1; + 3 | 2 - 1; + 3 & 5 - 2; + 0x0F00 & (0x00F0 << 4); + 0x0F00 & (0xF000 >> 4); + (0x0F00 << 1) ^ 3; + (0x0F00 << 1) | 2; + + let b = 3; + trip!(b * 8); +} diff --git a/src/tools/clippy/tests/ui/precedence_bits.rs b/src/tools/clippy/tests/ui/precedence_bits.rs new file mode 100644 index 0000000000000..9b353308b6ee3 --- /dev/null +++ b/src/tools/clippy/tests/ui/precedence_bits.rs @@ -0,0 +1,35 @@ +#![warn(clippy::precedence_bits)] +#![allow( + unused_must_use, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::precedence +)] +#![allow(clippy::identity_op)] +#![allow(clippy::eq_op)] + +macro_rules! trip { + ($a:expr) => { + match $a & 0b1111_1111u8 { + 0 => println!("a is zero ({})", $a), + _ => println!("a is {}", $a), + } + }; +} + +fn main() { + 1 << 2 + 3; + 1 + 2 << 3; + 4 >> 1 + 1; + 1 + 3 >> 2; + 1 ^ 1 - 1; + 3 | 2 - 1; + 3 & 5 - 2; + 0x0F00 & 0x00F0 << 4; + 0x0F00 & 0xF000 >> 4; + 0x0F00 << 1 ^ 3; + 0x0F00 << 1 | 2; + + let b = 3; + trip!(b * 8); +} diff --git a/src/tools/clippy/tests/ui/precedence_bits.stderr b/src/tools/clippy/tests/ui/precedence_bits.stderr new file mode 100644 index 0000000000000..f468186b363c8 --- /dev/null +++ b/src/tools/clippy/tests/ui/precedence_bits.stderr @@ -0,0 +1,29 @@ +error: operator precedence might not be obvious + --> tests/ui/precedence_bits.rs:28:5 + | +LL | 0x0F00 & 0x00F0 << 4; + | ^^^^^^^^^^^^^^^^^^^^ help: consider parenthesizing your expression: `0x0F00 & (0x00F0 << 4)` + | + = note: `-D clippy::precedence-bits` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::precedence_bits)]` + +error: operator precedence might not be obvious + --> tests/ui/precedence_bits.rs:29:5 + | +LL | 0x0F00 & 0xF000 >> 4; + | ^^^^^^^^^^^^^^^^^^^^ help: consider parenthesizing your expression: `0x0F00 & (0xF000 >> 4)` + +error: operator precedence might not be obvious + --> tests/ui/precedence_bits.rs:30:5 + | +LL | 0x0F00 << 1 ^ 3; + | ^^^^^^^^^^^^^^^ help: consider parenthesizing your expression: `(0x0F00 << 1) ^ 3` + +error: operator precedence might not be obvious + --> tests/ui/precedence_bits.rs:31:5 + | +LL | 0x0F00 << 1 | 2; + | ^^^^^^^^^^^^^^^ help: consider parenthesizing your expression: `(0x0F00 << 1) | 2` + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/print_literal.fixed b/src/tools/clippy/tests/ui/print_literal.fixed index 1705a7ff01bb4..328e9a9b999f6 100644 --- a/src/tools/clippy/tests/ui/print_literal.fixed +++ b/src/tools/clippy/tests/ui/print_literal.fixed @@ -66,3 +66,17 @@ fn main() { println!("mixed: {{hello}} {world}"); } + +fn issue_13959() { + println!("\""); + println!( + " + foo + \\ + \\\\ + \" + \\\" + bar +" + ); +} diff --git a/src/tools/clippy/tests/ui/print_literal.rs b/src/tools/clippy/tests/ui/print_literal.rs index d10b26b5887cc..3130d0b6998eb 100644 --- a/src/tools/clippy/tests/ui/print_literal.rs +++ b/src/tools/clippy/tests/ui/print_literal.rs @@ -66,3 +66,18 @@ fn main() { println!("mixed: {} {world}", "{hello}"); } + +fn issue_13959() { + println!("{}", r#"""#); + println!( + "{}", + r#" + foo + \ + \\ + " + \" + bar +"# + ); +} diff --git a/src/tools/clippy/tests/ui/print_literal.stderr b/src/tools/clippy/tests/ui/print_literal.stderr index c4cbb8bed7074..d967b7c24070a 100644 --- a/src/tools/clippy/tests/ui/print_literal.stderr +++ b/src/tools/clippy/tests/ui/print_literal.stderr @@ -192,5 +192,41 @@ LL - println!("mixed: {} {world}", "{hello}"); LL + println!("mixed: {{hello}} {world}"); | -error: aborting due to 16 previous errors +error: literal with an empty format string + --> tests/ui/print_literal.rs:71:20 + | +LL | println!("{}", r#"""#); + | ^^^^^^ + | +help: try + | +LL - println!("{}", r#"""#); +LL + println!("\""); + | + +error: literal with an empty format string + --> tests/ui/print_literal.rs:74:9 + | +LL | / r#" +LL | | foo +LL | | \ +LL | | \\ +... | +LL | | bar +LL | | "# + | |__^ + | +help: try + | +LL ~ " +LL + foo +LL + \\ +LL + \\\\ +LL + \" +LL + \\\" +LL + bar +LL ~ " + | + +error: aborting due to 18 previous errors diff --git a/src/tools/clippy/tests/ui/redundant_else.fixed b/src/tools/clippy/tests/ui/redundant_else.fixed new file mode 100644 index 0000000000000..47aa79302d2cf --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_else.fixed @@ -0,0 +1,154 @@ +#![warn(clippy::redundant_else)] +#![allow(clippy::needless_return, clippy::if_same_then_else, clippy::needless_late_init)] + +fn main() { + loop { + // break + if foo() { + println!("Love your neighbor;"); + break; + } + //~^ ERROR: redundant else block + println!("yet don't pull down your hedge."); + // continue + if foo() { + println!("He that lies down with Dogs,"); + continue; + } + //~^ ERROR: redundant else block + println!("shall rise up with fleas."); + // match block + if foo() { + match foo() { + 1 => break, + _ => return, + } + } + //~^ ERROR: redundant else block + println!("You may delay, but time will not."); + } + // else if + if foo() { + return; + } else if foo() { + return; + } + //~^ ERROR: redundant else block + println!("A fat kitchen makes a lean will."); + // let binding outside of block + let _ = { + if foo() { + return; + } + //~^ ERROR: redundant else block + 1 + }; + // else if with let binding outside of block + let _ = { + if foo() { + return; + } else if foo() { + return; + } + //~^ ERROR: redundant else block + 2 + }; + // inside if let + let _ = if let Some(1) = foo() { + let _ = 1; + if foo() { + return; + } + //~^ ERROR: redundant else block + 1 + } else { + 1 + }; + + // + // non-lint cases + // + + // sanity check + if foo() { + let _ = 1; + } else { + println!("Who is wise? He that learns from every one."); + } + // else if without else + if foo() { + return; + } else if foo() { + foo() + }; + // nested if return + if foo() { + if foo() { + return; + } + } else { + foo() + }; + // match with non-breaking branch + if foo() { + match foo() { + 1 => foo(), + _ => return, + } + } else { + println!("Three may keep a secret, if two of them are dead."); + } + // let binding + let _ = if foo() { + return; + } else { + 1 + }; + // assign + let mut a; + a = if foo() { + return; + } else { + 1 + }; + // assign-op + a += if foo() { + return; + } else { + 1 + }; + // if return else if else + if foo() { + return; + } else if foo() { + 1 + } else { + 2 + }; + // if else if return else + if foo() { + 1 + } else if foo() { + return; + } else { + 2 + }; + // else if with let binding + let _ = if foo() { + return; + } else if foo() { + return; + } else { + 2 + }; + // inside function call + Box::new(if foo() { + return; + } else { + 1 + }); +} + +fn foo() -> T { + unimplemented!("I'm not Santa Claus") +} diff --git a/src/tools/clippy/tests/ui/redundant_else.stderr b/src/tools/clippy/tests/ui/redundant_else.stderr index b649a210b5fa6..ecc16f7cda5e5 100644 --- a/src/tools/clippy/tests/ui/redundant_else.stderr +++ b/src/tools/clippy/tests/ui/redundant_else.stderr @@ -1,88 +1,123 @@ error: redundant else block - --> tests/ui/redundant_else.rs:10:16 + --> tests/ui/redundant_else.rs:10:10 | LL | } else { - | ________________^ + | __________^ LL | | LL | | println!("yet don't pull down your hedge."); LL | | } | |_________^ | - = help: remove the `else` block and move the contents out = note: `-D clippy::redundant-else` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::redundant_else)]` +help: remove the `else` block and move the contents out + | +LL ~ } +LL + +LL + println!("yet don't pull down your hedge."); + | error: redundant else block - --> tests/ui/redundant_else.rs:18:16 + --> tests/ui/redundant_else.rs:18:10 | LL | } else { - | ________________^ + | __________^ LL | | LL | | println!("shall rise up with fleas."); LL | | } | |_________^ | - = help: remove the `else` block and move the contents out +help: remove the `else` block and move the contents out + | +LL ~ } +LL + +LL + println!("shall rise up with fleas."); + | error: redundant else block - --> tests/ui/redundant_else.rs:28:16 + --> tests/ui/redundant_else.rs:28:10 | LL | } else { - | ________________^ + | __________^ LL | | LL | | println!("You may delay, but time will not."); LL | | } | |_________^ | - = help: remove the `else` block and move the contents out +help: remove the `else` block and move the contents out + | +LL ~ } +LL + +LL + println!("You may delay, but time will not."); + | error: redundant else block - --> tests/ui/redundant_else.rs:38:12 + --> tests/ui/redundant_else.rs:38:6 | LL | } else { - | ____________^ + | ______^ LL | | LL | | println!("A fat kitchen makes a lean will."); LL | | } | |_____^ | - = help: remove the `else` block and move the contents out +help: remove the `else` block and move the contents out + | +LL ~ } +LL + +LL + println!("A fat kitchen makes a lean will."); + | error: redundant else block - --> tests/ui/redundant_else.rs:46:16 + --> tests/ui/redundant_else.rs:46:10 | LL | } else { - | ________________^ + | __________^ LL | | LL | | 1 LL | | } | |_________^ | - = help: remove the `else` block and move the contents out +help: remove the `else` block and move the contents out + | +LL ~ } +LL + +LL + 1 + | error: redundant else block - --> tests/ui/redundant_else.rs:57:16 + --> tests/ui/redundant_else.rs:57:10 | LL | } else { - | ________________^ + | __________^ LL | | LL | | 2 LL | | } | |_________^ | - = help: remove the `else` block and move the contents out +help: remove the `else` block and move the contents out + | +LL ~ } +LL + +LL + 2 + | error: redundant else block - --> tests/ui/redundant_else.rs:67:16 + --> tests/ui/redundant_else.rs:67:10 | LL | } else { - | ________________^ + | __________^ LL | | LL | | 1 LL | | } | |_________^ | - = help: remove the `else` block and move the contents out +help: remove the `else` block and move the contents out + | +LL ~ } +LL + +LL + 1 + | error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/return_and_then.fixed b/src/tools/clippy/tests/ui/return_and_then.fixed new file mode 100644 index 0000000000000..9736a51ac8687 --- /dev/null +++ b/src/tools/clippy/tests/ui/return_and_then.fixed @@ -0,0 +1,67 @@ +#![warn(clippy::return_and_then)] + +fn main() { + fn test_opt_block(opt: Option) -> Option { + let n = opt?; + let mut ret = n + 1; + ret += n; + if n > 1 { Some(ret) } else { None } + } + + fn test_opt_func(opt: Option) -> Option { + let n = opt?; + test_opt_block(Some(n)) + } + + fn test_call_chain() -> Option { + let n = gen_option(1)?; + test_opt_block(Some(n)) + } + + fn test_res_block(opt: Result) -> Result { + let n = opt?; + if n > 1 { Ok(n + 1) } else { Err(n) } + } + + fn test_res_func(opt: Result) -> Result { + let n = opt?; + test_res_block(Ok(n)) + } + + fn test_ref_only() -> Option { + // ref: empty string + let x = Some("")?; + if x.len() > 2 { Some(3) } else { None } + } + + fn test_tmp_only() -> Option { + // unused temporary: vec![1, 2, 4] + let x = Some(match (vec![1, 2, 3], vec![1, 2, 4]) { + (a, _) if a.len() > 1 => a, + (_, b) => b, + })?; + if x.len() > 2 { Some(3) } else { None } + } + + // should not lint + fn test_tmp_ref() -> Option { + String::from("") + .strip_prefix("<") + .and_then(|s| s.strip_suffix(">").map(String::from)) + } + + // should not lint + fn test_unconsumed_tmp() -> Option { + [1, 2, 3] + .iter() + .map(|x| x + 1) + .collect::>() // temporary Vec created here + .as_slice() // creates temporary slice + .first() // creates temporary reference + .and_then(|x| test_opt_block(Some(*x))) + } +} + +fn gen_option(n: i32) -> Option { + Some(n) +} diff --git a/src/tools/clippy/tests/ui/return_and_then.rs b/src/tools/clippy/tests/ui/return_and_then.rs new file mode 100644 index 0000000000000..8bcbdfc3a6325 --- /dev/null +++ b/src/tools/clippy/tests/ui/return_and_then.rs @@ -0,0 +1,63 @@ +#![warn(clippy::return_and_then)] + +fn main() { + fn test_opt_block(opt: Option) -> Option { + opt.and_then(|n| { + let mut ret = n + 1; + ret += n; + if n > 1 { Some(ret) } else { None } + }) + } + + fn test_opt_func(opt: Option) -> Option { + opt.and_then(|n| test_opt_block(Some(n))) + } + + fn test_call_chain() -> Option { + gen_option(1).and_then(|n| test_opt_block(Some(n))) + } + + fn test_res_block(opt: Result) -> Result { + opt.and_then(|n| if n > 1 { Ok(n + 1) } else { Err(n) }) + } + + fn test_res_func(opt: Result) -> Result { + opt.and_then(|n| test_res_block(Ok(n))) + } + + fn test_ref_only() -> Option { + // ref: empty string + Some("").and_then(|x| if x.len() > 2 { Some(3) } else { None }) + } + + fn test_tmp_only() -> Option { + // unused temporary: vec![1, 2, 4] + Some(match (vec![1, 2, 3], vec![1, 2, 4]) { + (a, _) if a.len() > 1 => a, + (_, b) => b, + }) + .and_then(|x| if x.len() > 2 { Some(3) } else { None }) + } + + // should not lint + fn test_tmp_ref() -> Option { + String::from("") + .strip_prefix("<") + .and_then(|s| s.strip_suffix(">").map(String::from)) + } + + // should not lint + fn test_unconsumed_tmp() -> Option { + [1, 2, 3] + .iter() + .map(|x| x + 1) + .collect::>() // temporary Vec created here + .as_slice() // creates temporary slice + .first() // creates temporary reference + .and_then(|x| test_opt_block(Some(*x))) + } +} + +fn gen_option(n: i32) -> Option { + Some(n) +} diff --git a/src/tools/clippy/tests/ui/return_and_then.stderr b/src/tools/clippy/tests/ui/return_and_then.stderr new file mode 100644 index 0000000000000..b2e8bf2ca45a0 --- /dev/null +++ b/src/tools/clippy/tests/ui/return_and_then.stderr @@ -0,0 +1,101 @@ +error: use the question mark operator instead of an `and_then` call + --> tests/ui/return_and_then.rs:5:9 + | +LL | / opt.and_then(|n| { +LL | | let mut ret = n + 1; +LL | | ret += n; +LL | | if n > 1 { Some(ret) } else { None } +LL | | }) + | |__________^ + | + = note: `-D clippy::return-and-then` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::return_and_then)]` +help: try + | +LL ~ let n = opt?; +LL + let mut ret = n + 1; +LL + ret += n; +LL + if n > 1 { Some(ret) } else { None } + | + +error: use the question mark operator instead of an `and_then` call + --> tests/ui/return_and_then.rs:13:9 + | +LL | opt.and_then(|n| test_opt_block(Some(n))) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ let n = opt?; +LL + test_opt_block(Some(n)) + | + +error: use the question mark operator instead of an `and_then` call + --> tests/ui/return_and_then.rs:17:9 + | +LL | gen_option(1).and_then(|n| test_opt_block(Some(n))) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ let n = gen_option(1)?; +LL + test_opt_block(Some(n)) + | + +error: use the question mark operator instead of an `and_then` call + --> tests/ui/return_and_then.rs:21:9 + | +LL | opt.and_then(|n| if n > 1 { Ok(n + 1) } else { Err(n) }) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ let n = opt?; +LL + if n > 1 { Ok(n + 1) } else { Err(n) } + | + +error: use the question mark operator instead of an `and_then` call + --> tests/ui/return_and_then.rs:25:9 + | +LL | opt.and_then(|n| test_res_block(Ok(n))) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ let n = opt?; +LL + test_res_block(Ok(n)) + | + +error: use the question mark operator instead of an `and_then` call + --> tests/ui/return_and_then.rs:30:9 + | +LL | Some("").and_then(|x| if x.len() > 2 { Some(3) } else { None }) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ let x = Some("")?; +LL + if x.len() > 2 { Some(3) } else { None } + | + +error: use the question mark operator instead of an `and_then` call + --> tests/ui/return_and_then.rs:35:9 + | +LL | / Some(match (vec![1, 2, 3], vec![1, 2, 4]) { +LL | | (a, _) if a.len() > 1 => a, +LL | | (_, b) => b, +LL | | }) +LL | | .and_then(|x| if x.len() > 2 { Some(3) } else { None }) + | |_______________________________________________________________^ + | +help: try + | +LL ~ let x = Some(match (vec![1, 2, 3], vec![1, 2, 4]) { +LL + (a, _) if a.len() > 1 => a, +LL + (_, b) => b, +LL + })?; +LL + if x.len() > 2 { Some(3) } else { None } + | + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/size_of_in_element_count/expressions.rs b/src/tools/clippy/tests/ui/size_of_in_element_count/expressions.rs index 91b7ea3922c55..f405ba200acdf 100644 --- a/src/tools/clippy/tests/ui/size_of_in_element_count/expressions.rs +++ b/src/tools/clippy/tests/ui/size_of_in_element_count/expressions.rs @@ -8,11 +8,11 @@ fn main() { const SIZE: usize = 128; const HALF_SIZE: usize = SIZE / 2; const DOUBLE_SIZE: usize = SIZE * 2; - let mut x = [2u8; SIZE]; - let mut y = [2u8; SIZE]; + let mut x = [2u16; SIZE]; + let mut y = [2u16; SIZE]; // Count expression involving multiplication of size_of (Should trigger the lint) - unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; + unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; //~^ ERROR: found a count of bytes instead of a count of elements of `T` // Count expression involving nested multiplications of size_of (Should trigger the lint) @@ -20,22 +20,19 @@ fn main() { //~^ ERROR: found a count of bytes instead of a count of elements of `T` // Count expression involving divisions of size_of (Should trigger the lint) - unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::() / 2) }; + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::() / 2) }; //~^ ERROR: found a count of bytes instead of a count of elements of `T` // Count expression involving divisions by size_of (Should not trigger the lint) - unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / size_of::()) }; + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / size_of::()) }; // Count expression involving divisions by multiple size_of (Should not trigger the lint) - unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / (2 * size_of::())) }; + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / (2 * size_of::())) }; // Count expression involving recursive divisions by size_of (Should trigger the lint) - unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / (2 / size_of::())) }; + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / (2 / size_of::())) }; //~^ ERROR: found a count of bytes instead of a count of elements of `T` // No size_of calls (Should not trigger the lint) unsafe { copy(x.as_ptr(), y.as_mut_ptr(), SIZE) }; - - // Different types for pointee and size_of (Should not trigger the lint) - unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::() / 2 * SIZE) }; } diff --git a/src/tools/clippy/tests/ui/size_of_in_element_count/expressions.stderr b/src/tools/clippy/tests/ui/size_of_in_element_count/expressions.stderr index 6396afd7f3955..74be0d7773dff 100644 --- a/src/tools/clippy/tests/ui/size_of_in_element_count/expressions.stderr +++ b/src/tools/clippy/tests/ui/size_of_in_element_count/expressions.stderr @@ -1,8 +1,8 @@ error: found a count of bytes instead of a count of elements of `T` --> tests/ui/size_of_in_element_count/expressions.rs:15:62 | -LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type = note: `-D clippy::size-of-in-element-count` implied by `-D warnings` @@ -19,16 +19,16 @@ LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * si error: found a count of bytes instead of a count of elements of `T` --> tests/ui/size_of_in_element_count/expressions.rs:23:47 | -LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::() / 2) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::() / 2) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of `T` --> tests/ui/size_of_in_element_count/expressions.rs:33:47 | -LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / (2 / size_of::())) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / (2 / size_of::())) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type diff --git a/src/tools/clippy/tests/ui/size_of_in_element_count/functions.rs b/src/tools/clippy/tests/ui/size_of_in_element_count/functions.rs index 3501cbdf81cfb..af18136a1dbe1 100644 --- a/src/tools/clippy/tests/ui/size_of_in_element_count/functions.rs +++ b/src/tools/clippy/tests/ui/size_of_in_element_count/functions.rs @@ -11,57 +11,52 @@ fn main() { const SIZE: usize = 128; const HALF_SIZE: usize = SIZE / 2; const DOUBLE_SIZE: usize = SIZE * 2; - let mut x = [2u8; SIZE]; - let mut y = [2u8; SIZE]; + let mut x = [2u16; SIZE]; + let mut y = [2u16; SIZE]; // Count is size_of (Should trigger the lint) - unsafe { copy_nonoverlapping::(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; + unsafe { copy_nonoverlapping::(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; //~^ ERROR: found a count of bytes instead of a count of elements of `T` unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; //~^ ERROR: found a count of bytes instead of a count of elements of `T` - unsafe { x.as_ptr().copy_to(y.as_mut_ptr(), size_of::()) }; + unsafe { x.as_ptr().copy_to(y.as_mut_ptr(), size_of::()) }; //~^ ERROR: found a count of bytes instead of a count of elements of `T` - unsafe { x.as_ptr().copy_to_nonoverlapping(y.as_mut_ptr(), size_of::()) }; + unsafe { x.as_ptr().copy_to_nonoverlapping(y.as_mut_ptr(), size_of::()) }; //~^ ERROR: found a count of bytes instead of a count of elements of `T` - unsafe { y.as_mut_ptr().copy_from(x.as_ptr(), size_of::()) }; + unsafe { y.as_mut_ptr().copy_from(x.as_ptr(), size_of::()) }; //~^ ERROR: found a count of bytes instead of a count of elements of `T` - unsafe { y.as_mut_ptr().copy_from_nonoverlapping(x.as_ptr(), size_of::()) }; + unsafe { y.as_mut_ptr().copy_from_nonoverlapping(x.as_ptr(), size_of::()) }; //~^ ERROR: found a count of bytes instead of a count of elements of `T` - unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; //~^ ERROR: found a count of bytes instead of a count of elements of `T` unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; //~^ ERROR: found a count of bytes instead of a count of elements of `T` - unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::() * SIZE) }; - //~^ ERROR: found a count of bytes instead of a count of elements of `T` - unsafe { write_bytes(y.as_mut_ptr(), 0u8, size_of::() * SIZE) }; - //~^ ERROR: found a count of bytes instead of a count of elements of `T` - - unsafe { swap_nonoverlapping(y.as_mut_ptr(), x.as_mut_ptr(), size_of::() * SIZE) }; + unsafe { swap_nonoverlapping(y.as_mut_ptr(), x.as_mut_ptr(), size_of::() * SIZE) }; //~^ ERROR: found a count of bytes instead of a count of elements of `T` - slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE); + slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE); //~^ ERROR: found a count of bytes instead of a count of elements of `T` - slice_from_raw_parts(y.as_ptr(), size_of::() * SIZE); + slice_from_raw_parts(y.as_ptr(), size_of::() * SIZE); //~^ ERROR: found a count of bytes instead of a count of elements of `T` - unsafe { from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE) }; + unsafe { from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE) }; //~^ ERROR: found a count of bytes instead of a count of elements of `T` - unsafe { from_raw_parts(y.as_ptr(), size_of::() * SIZE) }; + unsafe { from_raw_parts(y.as_ptr(), size_of::() * SIZE) }; //~^ ERROR: found a count of bytes instead of a count of elements of `T` - unsafe { y.as_mut_ptr().sub(size_of::()) }; + unsafe { y.as_mut_ptr().sub(size_of::()) }; //~^ ERROR: found a count of bytes instead of a count of elements of `T` - y.as_ptr().wrapping_sub(size_of::()); + y.as_ptr().wrapping_sub(size_of::()); //~^ ERROR: found a count of bytes instead of a count of elements of `T` - unsafe { y.as_ptr().add(size_of::()) }; + unsafe { y.as_ptr().add(size_of::()) }; //~^ ERROR: found a count of bytes instead of a count of elements of `T` - y.as_mut_ptr().wrapping_add(size_of::()); + y.as_mut_ptr().wrapping_add(size_of::()); //~^ ERROR: found a count of bytes instead of a count of elements of `T` - unsafe { y.as_ptr().offset(size_of::() as isize) }; + unsafe { y.as_ptr().offset(size_of::() as isize) }; //~^ ERROR: found a count of bytes instead of a count of elements of `T` - y.as_mut_ptr().wrapping_offset(size_of::() as isize); + y.as_mut_ptr().wrapping_offset(size_of::() as isize); //~^ ERROR: found a count of bytes instead of a count of elements of `T` } diff --git a/src/tools/clippy/tests/ui/size_of_in_element_count/functions.stderr b/src/tools/clippy/tests/ui/size_of_in_element_count/functions.stderr index abde7dc7cd2d3..de54789b22518 100644 --- a/src/tools/clippy/tests/ui/size_of_in_element_count/functions.stderr +++ b/src/tools/clippy/tests/ui/size_of_in_element_count/functions.stderr @@ -1,8 +1,8 @@ error: found a count of bytes instead of a count of elements of `T` - --> tests/ui/size_of_in_element_count/functions.rs:18:68 + --> tests/ui/size_of_in_element_count/functions.rs:18:69 | -LL | unsafe { copy_nonoverlapping::(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; - | ^^^^^^^^^^^^^^^ +LL | unsafe { copy_nonoverlapping::(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type = note: `-D clippy::size-of-in-element-count` implied by `-D warnings` @@ -19,40 +19,40 @@ LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x error: found a count of bytes instead of a count of elements of `T` --> tests/ui/size_of_in_element_count/functions.rs:23:49 | -LL | unsafe { x.as_ptr().copy_to(y.as_mut_ptr(), size_of::()) }; - | ^^^^^^^^^^^^^^^ +LL | unsafe { x.as_ptr().copy_to(y.as_mut_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of `T` --> tests/ui/size_of_in_element_count/functions.rs:25:64 | -LL | unsafe { x.as_ptr().copy_to_nonoverlapping(y.as_mut_ptr(), size_of::()) }; - | ^^^^^^^^^^^^^^^ +LL | unsafe { x.as_ptr().copy_to_nonoverlapping(y.as_mut_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of `T` --> tests/ui/size_of_in_element_count/functions.rs:27:51 | -LL | unsafe { y.as_mut_ptr().copy_from(x.as_ptr(), size_of::()) }; - | ^^^^^^^^^^^^^^^ +LL | unsafe { y.as_mut_ptr().copy_from(x.as_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of `T` --> tests/ui/size_of_in_element_count/functions.rs:29:66 | -LL | unsafe { y.as_mut_ptr().copy_from_nonoverlapping(x.as_ptr(), size_of::()) }; - | ^^^^^^^^^^^^^^^ +LL | unsafe { y.as_mut_ptr().copy_from_nonoverlapping(x.as_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of `T` --> tests/ui/size_of_in_element_count/functions.rs:32:47 | -LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; - | ^^^^^^^^^^^^^^^ +LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type @@ -65,108 +65,92 @@ LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of `T` - --> tests/ui/size_of_in_element_count/functions.rs:37:46 + --> tests/ui/size_of_in_element_count/functions.rs:37:66 | -LL | unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::() * SIZE) }; - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | unsafe { swap_nonoverlapping(y.as_mut_ptr(), x.as_mut_ptr(), size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of `T` - --> tests/ui/size_of_in_element_count/functions.rs:39:47 + --> tests/ui/size_of_in_element_count/functions.rs:40:46 | -LL | unsafe { write_bytes(y.as_mut_ptr(), 0u8, size_of::() * SIZE) }; - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE); + | ^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of `T` - --> tests/ui/size_of_in_element_count/functions.rs:42:66 + --> tests/ui/size_of_in_element_count/functions.rs:42:38 | -LL | unsafe { swap_nonoverlapping(y.as_mut_ptr(), x.as_mut_ptr(), size_of::() * SIZE) }; - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | slice_from_raw_parts(y.as_ptr(), size_of::() * SIZE); + | ^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of `T` - --> tests/ui/size_of_in_element_count/functions.rs:45:46 + --> tests/ui/size_of_in_element_count/functions.rs:45:49 | -LL | slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE); - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | unsafe { from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of `T` - --> tests/ui/size_of_in_element_count/functions.rs:47:38 + --> tests/ui/size_of_in_element_count/functions.rs:47:41 | -LL | slice_from_raw_parts(y.as_ptr(), size_of::() * SIZE); - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | unsafe { from_raw_parts(y.as_ptr(), size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of `T` - --> tests/ui/size_of_in_element_count/functions.rs:50:49 + --> tests/ui/size_of_in_element_count/functions.rs:50:33 | -LL | unsafe { from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE) }; - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | unsafe { y.as_mut_ptr().sub(size_of::()) }; + | ^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of `T` - --> tests/ui/size_of_in_element_count/functions.rs:52:41 + --> tests/ui/size_of_in_element_count/functions.rs:52:29 | -LL | unsafe { from_raw_parts(y.as_ptr(), size_of::() * SIZE) }; - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | y.as_ptr().wrapping_sub(size_of::()); + | ^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of `T` - --> tests/ui/size_of_in_element_count/functions.rs:55:33 + --> tests/ui/size_of_in_element_count/functions.rs:54:29 | -LL | unsafe { y.as_mut_ptr().sub(size_of::()) }; - | ^^^^^^^^^^^^^^^ +LL | unsafe { y.as_ptr().add(size_of::()) }; + | ^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of `T` - --> tests/ui/size_of_in_element_count/functions.rs:57:29 + --> tests/ui/size_of_in_element_count/functions.rs:56:33 | -LL | y.as_ptr().wrapping_sub(size_of::()); - | ^^^^^^^^^^^^^^^ +LL | y.as_mut_ptr().wrapping_add(size_of::()); + | ^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of `T` - --> tests/ui/size_of_in_element_count/functions.rs:59:29 + --> tests/ui/size_of_in_element_count/functions.rs:58:32 | -LL | unsafe { y.as_ptr().add(size_of::()) }; - | ^^^^^^^^^^^^^^^ +LL | unsafe { y.as_ptr().offset(size_of::() as isize) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of `T` - --> tests/ui/size_of_in_element_count/functions.rs:61:33 + --> tests/ui/size_of_in_element_count/functions.rs:60:36 | -LL | y.as_mut_ptr().wrapping_add(size_of::()); - | ^^^^^^^^^^^^^^^ +LL | y.as_mut_ptr().wrapping_offset(size_of::() as isize); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of `T` - --> tests/ui/size_of_in_element_count/functions.rs:63:32 - | -LL | unsafe { y.as_ptr().offset(size_of::() as isize) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type - -error: found a count of bytes instead of a count of elements of `T` - --> tests/ui/size_of_in_element_count/functions.rs:65:36 - | -LL | y.as_mut_ptr().wrapping_offset(size_of::() as isize); - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type - -error: aborting due to 21 previous errors +error: aborting due to 19 previous errors diff --git a/src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.stderr b/src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.stderr index fb8fb1a009012..26166e2fc8dac 100644 --- a/src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.stderr +++ b/src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.stderr @@ -1,4 +1,4 @@ -error: `ref` directly on a function argument is ignored. Consider using a reference type instead +error: `ref` directly on a function parameter does not prevent taking ownership of the passed argument. Consider using a reference type instead --> tests/ui/toplevel_ref_arg_non_rustfix.rs:9:15 | LL | fn the_answer(ref mut x: u8) { @@ -7,7 +7,7 @@ LL | fn the_answer(ref mut x: u8) { = note: `-D clippy::toplevel-ref-arg` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::toplevel_ref_arg)]` -error: `ref` directly on a function argument is ignored. Consider using a reference type instead +error: `ref` directly on a function parameter does not prevent taking ownership of the passed argument. Consider using a reference type instead --> tests/ui/toplevel_ref_arg_non_rustfix.rs:20:24 | LL | fn fun_example(ref _x: usize) {} diff --git a/src/tools/clippy/tests/ui/unnecessary_semicolon.edition2021.fixed b/src/tools/clippy/tests/ui/unnecessary_semicolon.edition2021.fixed index 7a3b79553dea8..343c88b98155a 100644 --- a/src/tools/clippy/tests/ui/unnecessary_semicolon.edition2021.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_semicolon.edition2021.fixed @@ -55,3 +55,9 @@ fn no_borrow_issue(a: u32, b: u32) { None => {}, } } + +fn issue14100() -> bool { + // Removing the `;` would make the block type be `()` instead of `!`, and this could no longer be + // cast into the `bool` function return type. + if return true {}; +} diff --git a/src/tools/clippy/tests/ui/unnecessary_semicolon.edition2024.fixed b/src/tools/clippy/tests/ui/unnecessary_semicolon.edition2024.fixed index d186d5e7ebc4e..1cba5760eb0a0 100644 --- a/src/tools/clippy/tests/ui/unnecessary_semicolon.edition2024.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_semicolon.edition2024.fixed @@ -55,3 +55,9 @@ fn no_borrow_issue(a: u32, b: u32) { None => {}, } } + +fn issue14100() -> bool { + // Removing the `;` would make the block type be `()` instead of `!`, and this could no longer be + // cast into the `bool` function return type. + if return true {}; +} diff --git a/src/tools/clippy/tests/ui/unnecessary_semicolon.rs b/src/tools/clippy/tests/ui/unnecessary_semicolon.rs index 3028c5b27b34d..6abbbd79aaf20 100644 --- a/src/tools/clippy/tests/ui/unnecessary_semicolon.rs +++ b/src/tools/clippy/tests/ui/unnecessary_semicolon.rs @@ -55,3 +55,9 @@ fn no_borrow_issue(a: u32, b: u32) { None => {}, }; } + +fn issue14100() -> bool { + // Removing the `;` would make the block type be `()` instead of `!`, and this could no longer be + // cast into the `bool` function return type. + if return true {}; +} diff --git a/src/tools/clippy/tests/ui/write_literal.fixed b/src/tools/clippy/tests/ui/write_literal.fixed index 3d216b76cbf3d..f1def776e1bca 100644 --- a/src/tools/clippy/tests/ui/write_literal.fixed +++ b/src/tools/clippy/tests/ui/write_literal.fixed @@ -62,3 +62,19 @@ fn main() { writeln!(v, "hello {0} {1}, world {2}", 2, 3, 4); //~^ ERROR: literal with an empty format string } + +fn issue_13959() { + let mut v = Vec::new(); + writeln!(v, "\""); + writeln!( + v, + " + foo + \\ + \\\\ + \" + \\\" + bar +" + ); +} diff --git a/src/tools/clippy/tests/ui/write_literal.rs b/src/tools/clippy/tests/ui/write_literal.rs index 79d6daa2e3b5e..1b7df91b47e1f 100644 --- a/src/tools/clippy/tests/ui/write_literal.rs +++ b/src/tools/clippy/tests/ui/write_literal.rs @@ -62,3 +62,20 @@ fn main() { writeln!(v, "{0} {1} {2}, {3} {4}", "hello", 2, 3, "world", 4); //~^ ERROR: literal with an empty format string } + +fn issue_13959() { + let mut v = Vec::new(); + writeln!(v, "{}", r#"""#); + writeln!( + v, + "{}", + r#" + foo + \ + \\ + " + \" + bar +"# + ); +} diff --git a/src/tools/clippy/tests/ui/write_literal.stderr b/src/tools/clippy/tests/ui/write_literal.stderr index 9f4cdfd91e8ab..35c93d567cd35 100644 --- a/src/tools/clippy/tests/ui/write_literal.stderr +++ b/src/tools/clippy/tests/ui/write_literal.stderr @@ -144,5 +144,41 @@ LL - writeln!(v, "{0} {1} {2}, {3} {4}", "hello", 2, 3, "world", 4); LL + writeln!(v, "hello {0} {1}, world {2}", 2, 3, 4); | -error: aborting due to 12 previous errors +error: literal with an empty format string + --> tests/ui/write_literal.rs:68:23 + | +LL | writeln!(v, "{}", r#"""#); + | ^^^^^^ + | +help: try + | +LL - writeln!(v, "{}", r#"""#); +LL + writeln!(v, "\""); + | + +error: literal with an empty format string + --> tests/ui/write_literal.rs:72:9 + | +LL | / r#" +LL | | foo +LL | | \ +LL | | \\ +... | +LL | | bar +LL | | "# + | |__^ + | +help: try + | +LL ~ " +LL + foo +LL + \\ +LL + \\\\ +LL + \" +LL + \\\" +LL + bar +LL ~ " + | + +error: aborting due to 14 previous errors diff --git a/src/tools/clippy/util/gh-pages/index_template.html b/src/tools/clippy/util/gh-pages/index_template.html index deb0ef0b49966..a9b6462800307 100644 --- a/src/tools/clippy/util/gh-pages/index_template.html +++ b/src/tools/clippy/util/gh-pages/index_template.html @@ -24,14 +24,16 @@ {# #} {# #} {# #} + {# #} + {# #} + {# #} {# #} {# #} - {# #}
{# #} {# #}
{# #}
Theme
{# #} - {# #} {# #} {# #} {# #} @@ -39,11 +41,12 @@ {# #} {# #} {# #}
{# #}
{# #} + {# #}
{# #} {# #}
{# #}
{# #} - {# #} - {# #}
{# #} @@ -145,13 +148,13 @@

Clippy Lints

{# #} {% for lint in lints %}
{# #} {# #} -