about summary refs log tree commit diff
path: root/clippy_lints/src/methods
diff options
context:
space:
mode:
authorPhilipp Krones <hello@philkrones.com>2024-07-11 15:29:08 +0200
committerPhilipp Krones <hello@philkrones.com>2024-07-11 15:29:08 +0200
commit1ced73e66a8c2c59a50bbcafa7e10d2772bd3aaf (patch)
treee5cc89346a2b99fe63989f94d501ec01ff94ee2e /clippy_lints/src/methods
parent920cbcdd456504acb6999fc647490fed0fe6eece (diff)
parentef3cfaa4f215a96a69ea81ee6fce8e58bdc51115 (diff)
Merge remote-tracking branch 'upstream/master' into rustup
Diffstat (limited to 'clippy_lints/src/methods')
-rw-r--r--clippy_lints/src/methods/manual_inspect.rs26
-rw-r--r--clippy_lints/src/methods/mod.rs9
-rw-r--r--clippy_lints/src/methods/unnecessary_sort_by.rs3
-rw-r--r--clippy_lints/src/methods/unnecessary_to_owned.rs70
-rw-r--r--clippy_lints/src/methods/unwrap_expect_used.rs4
5 files changed, 90 insertions, 22 deletions
diff --git a/clippy_lints/src/methods/manual_inspect.rs b/clippy_lints/src/methods/manual_inspect.rs
index e3ce64c246a..cac2e11f591 100644
--- a/clippy_lints/src/methods/manual_inspect.rs
+++ b/clippy_lints/src/methods/manual_inspect.rs
@@ -167,14 +167,12 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name:
             } else {
                 edits.extend(addr_of_edits);
             }
-            edits.push((
-                name_span,
-                String::from(match name {
-                    "map" => "inspect",
-                    "map_err" => "inspect_err",
-                    _ => return,
-                }),
-            ));
+            let edit = match name {
+                "map" => "inspect",
+                "map_err" => "inspect_err",
+                _ => return,
+            };
+            edits.push((name_span, edit.to_string()));
             edits.push((
                 final_expr
                     .span
@@ -187,9 +185,15 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name:
             } else {
                 Applicability::MachineApplicable
             };
-            span_lint_and_then(cx, MANUAL_INSPECT, name_span, "", |diag| {
-                diag.multipart_suggestion("try", edits, app);
-            });
+            span_lint_and_then(
+                cx,
+                MANUAL_INSPECT,
+                name_span,
+                format!("using `{name}` over `{edit}`"),
+                |diag| {
+                    diag.multipart_suggestion("try", edits, app);
+                },
+            );
         }
     }
 }
diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs
index 1408f454820..a846552cddf 100644
--- a/clippy_lints/src/methods/mod.rs
+++ b/clippy_lints/src/methods/mod.rs
@@ -628,12 +628,11 @@ declare_clippy_lint! {
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for usage of `_.and_then(|x| Some(y))`, `_.and_then(|x| Ok(y))` or
-    /// `_.or_else(|x| Err(y))`.
+    /// Checks for usage of `_.and_then(|x| Some(y))`, `_.and_then(|x| Ok(y))`
+    /// or `_.or_else(|x| Err(y))`.
     ///
     /// ### Why is this bad?
-    /// Readability, this can be written more concisely as
-    /// `_.map(|x| y)` or `_.map_err(|x| y)`.
+    /// This can be written more concisely as `_.map(|x| y)` or `_.map_err(|x| y)`.
     ///
     /// ### Example
     /// ```no_run
@@ -4121,7 +4120,7 @@ declare_clippy_lint! {
     /// ```no_run
     /// let x = Some(0).inspect(|x| println!("{x}"));
     /// ```
-    #[clippy::version = "1.78.0"]
+    #[clippy::version = "1.81.0"]
     pub MANUAL_INSPECT,
     complexity,
     "use of `map` returning the original item"
diff --git a/clippy_lints/src/methods/unnecessary_sort_by.rs b/clippy_lints/src/methods/unnecessary_sort_by.rs
index 6911da69b94..a92fe9e55a4 100644
--- a/clippy_lints/src/methods/unnecessary_sort_by.rs
+++ b/clippy_lints/src/methods/unnecessary_sort_by.rs
@@ -5,7 +5,8 @@ use clippy_utils::ty::implements_trait;
 use rustc_errors::Applicability;
 use rustc_hir::{Closure, Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath};
 use rustc_lint::LateContext;
-use rustc_middle::ty::{self, GenericArgKind};
+use rustc_middle::ty;
+use rustc_middle::ty::GenericArgKind;
 use rustc_span::sym;
 use rustc_span::symbol::Ident;
 use std::iter;
diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs
index ae9aa83efd6..5d899415d77 100644
--- a/clippy_lints/src/methods/unnecessary_to_owned.rs
+++ b/clippy_lints/src/methods/unnecessary_to_owned.rs
@@ -1,13 +1,15 @@
 use super::implicit_clone::is_clone_like;
 use super::unnecessary_iter_cloned::{self, is_into_iter};
 use clippy_config::msrvs::{self, Msrv};
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::source::{snippet, snippet_opt};
 use clippy_utils::ty::{
     get_iterator_item_ty, implements_trait, is_copy, is_type_diagnostic_item, is_type_lang_item, peel_mid_ty_refs,
 };
 use clippy_utils::visitors::find_all_ret_expressions;
-use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, return_ty};
+use clippy_utils::{
+    fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, match_def_path, paths, return_ty,
+};
 use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::DefId;
@@ -52,6 +54,9 @@ pub fn check<'tcx>(
             if check_into_iter_call_arg(cx, expr, method_name, receiver, msrv) {
                 return;
             }
+            if check_string_from_utf8(cx, expr, receiver) {
+                return;
+            }
             check_other_call_arg(cx, expr, method_name, receiver);
         }
     } else {
@@ -240,6 +245,65 @@ fn check_into_iter_call_arg(
     false
 }
 
+/// Checks for `&String::from_utf8(bytes.{to_vec,to_owned,...}()).unwrap()` coercing to `&str`,
+/// which can be written as just `std::str::from_utf8(bytes).unwrap()`.
+fn check_string_from_utf8<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, receiver: &'tcx Expr<'tcx>) -> bool {
+    if let Some((call, arg)) = skip_addr_of_ancestors(cx, expr)
+        && !arg.span.from_expansion()
+        && let ExprKind::Call(callee, _) = call.kind
+        && fn_def_id(cx, call).is_some_and(|did| match_def_path(cx, did, &paths::STRING_FROM_UTF8))
+        && let Some(unwrap_call) = get_parent_expr(cx, call)
+        && let ExprKind::MethodCall(unwrap_method_name, ..) = unwrap_call.kind
+        && matches!(unwrap_method_name.ident.name, sym::unwrap | sym::expect)
+        && let Some(ref_string) = get_parent_expr(cx, unwrap_call)
+        && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, _) = ref_string.kind
+        && let adjusted_ty = cx.typeck_results().expr_ty_adjusted(ref_string)
+        // `&...` creates a `&String`, so only actually lint if this coerces to a `&str`
+        && matches!(adjusted_ty.kind(), ty::Ref(_, ty, _) if ty.is_str())
+    {
+        span_lint_and_then(
+            cx,
+            UNNECESSARY_TO_OWNED,
+            ref_string.span,
+            "allocating a new `String` only to create a temporary `&str` from it",
+            |diag| {
+                let arg_suggestion = format!(
+                    "{borrow}{recv_snippet}",
+                    recv_snippet = snippet(cx, receiver.span.source_callsite(), ".."),
+                    borrow = if cx.typeck_results().expr_ty(receiver).is_ref() {
+                        ""
+                    } else {
+                        // If not already a reference, prefix with a borrow so that it can coerce to one
+                        "&"
+                    }
+                );
+
+                diag.multipart_suggestion(
+                    "convert from `&[u8]` to `&str` directly",
+                    vec![
+                        // `&String::from_utf8(bytes.to_vec()).unwrap()`
+                        //   ^^^^^^^^^^^^^^^^^
+                        (callee.span, "core::str::from_utf8".into()),
+                        // `&String::from_utf8(bytes.to_vec()).unwrap()`
+                        //  ^
+                        (
+                            ref_string.span.shrink_to_lo().to(unwrap_call.span.shrink_to_lo()),
+                            String::new(),
+                        ),
+                        // `&String::from_utf8(bytes.to_vec()).unwrap()`
+                        //                     ^^^^^^^^^^^^^^
+                        (arg.span, arg_suggestion),
+                    ],
+                    Applicability::MachineApplicable,
+                );
+            },
+        );
+        true
+    } else {
+        false
+    }
+}
+
 /// Checks whether `expr` is an argument in an `into_iter` call and, if so, determines whether its
 /// call of a `to_owned`-like function is unnecessary.
 fn check_split_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol, receiver: &Expr<'_>) -> bool {
diff --git a/clippy_lints/src/methods/unwrap_expect_used.rs b/clippy_lints/src/methods/unwrap_expect_used.rs
index 516b8984ad7..5b0bd0f716a 100644
--- a/clippy_lints/src/methods/unwrap_expect_used.rs
+++ b/clippy_lints/src/methods/unwrap_expect_used.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::ty::{is_never_like, is_type_diagnostic_item};
-use clippy_utils::{is_in_cfg_test, is_in_test_function, is_lint_allowed};
+use clippy_utils::{is_in_test, is_lint_allowed};
 use rustc_hir::Expr;
 use rustc_lint::{LateContext, Lint};
 use rustc_middle::ty;
@@ -61,7 +61,7 @@ pub(super) fn check(
 
     let method_suffix = if is_err { "_err" } else { "" };
 
-    if allow_unwrap_in_tests && (is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id)) {
+    if allow_unwrap_in_tests && is_in_test(cx.tcx, expr.hir_id) {
         return;
     }