about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--clippy_lints/src/methods/expect_fun_call.rs199
-rw-r--r--clippy_lints/src/methods/mod.rs206
2 files changed, 204 insertions, 201 deletions
diff --git a/clippy_lints/src/methods/expect_fun_call.rs b/clippy_lints/src/methods/expect_fun_call.rs
new file mode 100644
index 00000000000..6866e9c652a
--- /dev/null
+++ b/clippy_lints/src/methods/expect_fun_call.rs
@@ -0,0 +1,199 @@
+use crate::utils::{is_expn_of, is_type_diagnostic_item, snippet, snippet_with_applicability, span_lint_and_sugg};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_lint::LateContext;
+use rustc_middle::ty;
+use rustc_span::source_map::Span;
+use rustc_span::symbol::sym;
+use std::borrow::Cow;
+
+use super::EXPECT_FUN_CALL;
+
+/// Checks for the `EXPECT_FUN_CALL` lint.
+#[allow(clippy::too_many_lines)]
+pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_span: Span, name: &str, args: &[hir::Expr<'_>]) {
+    // Strip `&`, `as_ref()` and `as_str()` off `arg` until we're left with either a `String` or
+    // `&str`
+    fn get_arg_root<'a>(cx: &LateContext<'_>, arg: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> {
+        let mut arg_root = arg;
+        loop {
+            arg_root = match &arg_root.kind {
+                hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, expr) => expr,
+                hir::ExprKind::MethodCall(method_name, _, call_args, _) => {
+                    if call_args.len() == 1
+                        && (method_name.ident.name == sym::as_str || method_name.ident.name == sym!(as_ref))
+                        && {
+                            let arg_type = cx.typeck_results().expr_ty(&call_args[0]);
+                            let base_type = arg_type.peel_refs();
+                            *base_type.kind() == ty::Str || is_type_diagnostic_item(cx, base_type, sym::string_type)
+                        }
+                    {
+                        &call_args[0]
+                    } else {
+                        break;
+                    }
+                },
+                _ => break,
+            };
+        }
+        arg_root
+    }
+
+    // Only `&'static str` or `String` can be used directly in the `panic!`. Other types should be
+    // converted to string.
+    fn requires_to_string(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool {
+        let arg_ty = cx.typeck_results().expr_ty(arg);
+        if is_type_diagnostic_item(cx, arg_ty, sym::string_type) {
+            return false;
+        }
+        if let ty::Ref(_, ty, ..) = arg_ty.kind() {
+            if *ty.kind() == ty::Str && can_be_static_str(cx, arg) {
+                return false;
+            }
+        };
+        true
+    }
+
+    // Check if an expression could have type `&'static str`, knowing that it
+    // has type `&str` for some lifetime.
+    fn can_be_static_str(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool {
+        match arg.kind {
+            hir::ExprKind::Lit(_) => true,
+            hir::ExprKind::Call(fun, _) => {
+                if let hir::ExprKind::Path(ref p) = fun.kind {
+                    match cx.qpath_res(p, fun.hir_id) {
+                        hir::def::Res::Def(hir::def::DefKind::Fn | hir::def::DefKind::AssocFn, def_id) => matches!(
+                            cx.tcx.fn_sig(def_id).output().skip_binder().kind(),
+                            ty::Ref(ty::ReStatic, ..)
+                        ),
+                        _ => false,
+                    }
+                } else {
+                    false
+                }
+            },
+            hir::ExprKind::MethodCall(..) => {
+                cx.typeck_results()
+                    .type_dependent_def_id(arg.hir_id)
+                    .map_or(false, |method_id| {
+                        matches!(
+                            cx.tcx.fn_sig(method_id).output().skip_binder().kind(),
+                            ty::Ref(ty::ReStatic, ..)
+                        )
+                    })
+            },
+            hir::ExprKind::Path(ref p) => matches!(
+                cx.qpath_res(p, arg.hir_id),
+                hir::def::Res::Def(hir::def::DefKind::Const | hir::def::DefKind::Static, _)
+            ),
+            _ => false,
+        }
+    }
+
+    fn generate_format_arg_snippet(
+        cx: &LateContext<'_>,
+        a: &hir::Expr<'_>,
+        applicability: &mut Applicability,
+    ) -> Vec<String> {
+        if_chain! {
+            if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, ref format_arg) = a.kind;
+            if let hir::ExprKind::Match(ref format_arg_expr, _, _) = format_arg.kind;
+            if let hir::ExprKind::Tup(ref format_arg_expr_tup) = format_arg_expr.kind;
+
+            then {
+                format_arg_expr_tup
+                    .iter()
+                    .map(|a| snippet_with_applicability(cx, a.span, "..", applicability).into_owned())
+                    .collect()
+            } else {
+                unreachable!()
+            }
+        }
+    }
+
+    fn is_call(node: &hir::ExprKind<'_>) -> bool {
+        match node {
+            hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, expr) => {
+                is_call(&expr.kind)
+            },
+            hir::ExprKind::Call(..)
+            | hir::ExprKind::MethodCall(..)
+            // These variants are debatable or require further examination
+            | hir::ExprKind::If(..)
+            | hir::ExprKind::Match(..)
+            | hir::ExprKind::Block{ .. } => true,
+            _ => false,
+        }
+    }
+
+    if args.len() != 2 || name != "expect" || !is_call(&args[1].kind) {
+        return;
+    }
+
+    let receiver_type = cx.typeck_results().expr_ty_adjusted(&args[0]);
+    let closure_args = if is_type_diagnostic_item(cx, receiver_type, sym::option_type) {
+        "||"
+    } else if is_type_diagnostic_item(cx, receiver_type, sym::result_type) {
+        "|_|"
+    } else {
+        return;
+    };
+
+    let arg_root = get_arg_root(cx, &args[1]);
+
+    let span_replace_word = method_span.with_hi(expr.span.hi());
+
+    let mut applicability = Applicability::MachineApplicable;
+
+    //Special handling for `format!` as arg_root
+    if_chain! {
+        if let hir::ExprKind::Block(block, None) = &arg_root.kind;
+        if block.stmts.len() == 1;
+        if let hir::StmtKind::Local(local) = &block.stmts[0].kind;
+        if let Some(arg_root) = &local.init;
+        if let hir::ExprKind::Call(ref inner_fun, ref inner_args) = arg_root.kind;
+        if is_expn_of(inner_fun.span, "format").is_some() && inner_args.len() == 1;
+        if let hir::ExprKind::Call(_, format_args) = &inner_args[0].kind;
+        then {
+            let fmt_spec = &format_args[0];
+            let fmt_args = &format_args[1];
+
+            let mut args = vec![snippet(cx, fmt_spec.span, "..").into_owned()];
+
+            args.extend(generate_format_arg_snippet(cx, fmt_args, &mut applicability));
+
+            let sugg = args.join(", ");
+
+            span_lint_and_sugg(
+                cx,
+                EXPECT_FUN_CALL,
+                span_replace_word,
+                &format!("use of `{}` followed by a function call", name),
+                "try this",
+                format!("unwrap_or_else({} panic!({}))", closure_args, sugg),
+                applicability,
+            );
+
+            return;
+        }
+    }
+
+    let mut arg_root_snippet: Cow<'_, _> = snippet_with_applicability(cx, arg_root.span, "..", &mut applicability);
+    if requires_to_string(cx, arg_root) {
+        arg_root_snippet.to_mut().push_str(".to_string()");
+    }
+
+    span_lint_and_sugg(
+        cx,
+        EXPECT_FUN_CALL,
+        span_replace_word,
+        &format!("use of `{}` followed by a function call", name),
+        "try this",
+        format!(
+            "unwrap_or_else({} {{ panic!(\"{{}}\", {}) }})",
+            closure_args, arg_root_snippet
+        ),
+        applicability,
+    );
+}
diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs
index 857c1bf0ec0..180620e860a 100644
--- a/clippy_lints/src/methods/mod.rs
+++ b/clippy_lints/src/methods/mod.rs
@@ -2,6 +2,7 @@ mod bind_instead_of_map;
 mod bytes_nth;
 mod clone_on_copy;
 mod clone_on_ref_ptr;
+mod expect_fun_call;
 mod expect_used;
 mod filetype_is_file;
 mod filter_flat_map;
@@ -50,8 +51,6 @@ mod useless_asref;
 mod wrong_self_convention;
 mod zst_offset;
 
-use std::borrow::Cow;
-
 use bind_instead_of_map::BindInsteadOfMap;
 use if_chain::if_chain;
 use rustc_ast::ast;
@@ -68,10 +67,9 @@ use rustc_span::symbol::{sym, Symbol, SymbolStr};
 use rustc_typeck::hir_ty_to_ty;
 
 use crate::utils::{
-    contains_return, contains_ty, get_trait_def_id, implements_trait, in_macro, is_copy, is_expn_of,
-    is_type_diagnostic_item, iter_input_pats, match_def_path, match_qpath, method_calls, method_chain_args, paths,
-    return_ty, single_segment_path, snippet, snippet_with_applicability, span_lint, span_lint_and_help,
-    span_lint_and_sugg, SpanlessEq,
+    contains_return, contains_ty, get_trait_def_id, implements_trait, in_macro, is_copy, is_type_diagnostic_item,
+    iter_input_pats, match_def_path, match_qpath, method_calls, method_chain_args, paths, return_ty,
+    single_segment_path, snippet_with_applicability, span_lint, span_lint_and_help, span_lint_and_sugg, SpanlessEq,
 };
 
 declare_clippy_lint! {
@@ -1779,7 +1777,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
             },
             hir::ExprKind::MethodCall(ref method_call, ref method_span, ref args, _) => {
                 or_fun_call::check(cx, expr, *method_span, &method_call.ident.as_str(), args);
-                lint_expect_fun_call(cx, expr, *method_span, &method_call.ident.as_str(), args);
+                expect_fun_call::check(cx, expr, *method_span, &method_call.ident.as_str(), args);
 
                 let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0]);
                 if args.len() == 1 && method_call.ident.name == sym::clone {
@@ -1973,200 +1971,6 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
     extract_msrv_attr!(LateContext);
 }
 
-/// Checks for the `EXPECT_FUN_CALL` lint.
-#[allow(clippy::too_many_lines)]
-fn lint_expect_fun_call(
-    cx: &LateContext<'_>,
-    expr: &hir::Expr<'_>,
-    method_span: Span,
-    name: &str,
-    args: &[hir::Expr<'_>],
-) {
-    // Strip `&`, `as_ref()` and `as_str()` off `arg` until we're left with either a `String` or
-    // `&str`
-    fn get_arg_root<'a>(cx: &LateContext<'_>, arg: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> {
-        let mut arg_root = arg;
-        loop {
-            arg_root = match &arg_root.kind {
-                hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, expr) => expr,
-                hir::ExprKind::MethodCall(method_name, _, call_args, _) => {
-                    if call_args.len() == 1
-                        && (method_name.ident.name == sym::as_str || method_name.ident.name == sym!(as_ref))
-                        && {
-                            let arg_type = cx.typeck_results().expr_ty(&call_args[0]);
-                            let base_type = arg_type.peel_refs();
-                            *base_type.kind() == ty::Str || is_type_diagnostic_item(cx, base_type, sym::string_type)
-                        }
-                    {
-                        &call_args[0]
-                    } else {
-                        break;
-                    }
-                },
-                _ => break,
-            };
-        }
-        arg_root
-    }
-
-    // Only `&'static str` or `String` can be used directly in the `panic!`. Other types should be
-    // converted to string.
-    fn requires_to_string(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool {
-        let arg_ty = cx.typeck_results().expr_ty(arg);
-        if is_type_diagnostic_item(cx, arg_ty, sym::string_type) {
-            return false;
-        }
-        if let ty::Ref(_, ty, ..) = arg_ty.kind() {
-            if *ty.kind() == ty::Str && can_be_static_str(cx, arg) {
-                return false;
-            }
-        };
-        true
-    }
-
-    // Check if an expression could have type `&'static str`, knowing that it
-    // has type `&str` for some lifetime.
-    fn can_be_static_str(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool {
-        match arg.kind {
-            hir::ExprKind::Lit(_) => true,
-            hir::ExprKind::Call(fun, _) => {
-                if let hir::ExprKind::Path(ref p) = fun.kind {
-                    match cx.qpath_res(p, fun.hir_id) {
-                        hir::def::Res::Def(hir::def::DefKind::Fn | hir::def::DefKind::AssocFn, def_id) => matches!(
-                            cx.tcx.fn_sig(def_id).output().skip_binder().kind(),
-                            ty::Ref(ty::ReStatic, ..)
-                        ),
-                        _ => false,
-                    }
-                } else {
-                    false
-                }
-            },
-            hir::ExprKind::MethodCall(..) => {
-                cx.typeck_results()
-                    .type_dependent_def_id(arg.hir_id)
-                    .map_or(false, |method_id| {
-                        matches!(
-                            cx.tcx.fn_sig(method_id).output().skip_binder().kind(),
-                            ty::Ref(ty::ReStatic, ..)
-                        )
-                    })
-            },
-            hir::ExprKind::Path(ref p) => matches!(
-                cx.qpath_res(p, arg.hir_id),
-                hir::def::Res::Def(hir::def::DefKind::Const | hir::def::DefKind::Static, _)
-            ),
-            _ => false,
-        }
-    }
-
-    fn generate_format_arg_snippet(
-        cx: &LateContext<'_>,
-        a: &hir::Expr<'_>,
-        applicability: &mut Applicability,
-    ) -> Vec<String> {
-        if_chain! {
-            if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, ref format_arg) = a.kind;
-            if let hir::ExprKind::Match(ref format_arg_expr, _, _) = format_arg.kind;
-            if let hir::ExprKind::Tup(ref format_arg_expr_tup) = format_arg_expr.kind;
-
-            then {
-                format_arg_expr_tup
-                    .iter()
-                    .map(|a| snippet_with_applicability(cx, a.span, "..", applicability).into_owned())
-                    .collect()
-            } else {
-                unreachable!()
-            }
-        }
-    }
-
-    fn is_call(node: &hir::ExprKind<'_>) -> bool {
-        match node {
-            hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, expr) => {
-                is_call(&expr.kind)
-            },
-            hir::ExprKind::Call(..)
-            | hir::ExprKind::MethodCall(..)
-            // These variants are debatable or require further examination
-            | hir::ExprKind::If(..)
-            | hir::ExprKind::Match(..)
-            | hir::ExprKind::Block{ .. } => true,
-            _ => false,
-        }
-    }
-
-    if args.len() != 2 || name != "expect" || !is_call(&args[1].kind) {
-        return;
-    }
-
-    let receiver_type = cx.typeck_results().expr_ty_adjusted(&args[0]);
-    let closure_args = if is_type_diagnostic_item(cx, receiver_type, sym::option_type) {
-        "||"
-    } else if is_type_diagnostic_item(cx, receiver_type, sym::result_type) {
-        "|_|"
-    } else {
-        return;
-    };
-
-    let arg_root = get_arg_root(cx, &args[1]);
-
-    let span_replace_word = method_span.with_hi(expr.span.hi());
-
-    let mut applicability = Applicability::MachineApplicable;
-
-    //Special handling for `format!` as arg_root
-    if_chain! {
-        if let hir::ExprKind::Block(block, None) = &arg_root.kind;
-        if block.stmts.len() == 1;
-        if let hir::StmtKind::Local(local) = &block.stmts[0].kind;
-        if let Some(arg_root) = &local.init;
-        if let hir::ExprKind::Call(ref inner_fun, ref inner_args) = arg_root.kind;
-        if is_expn_of(inner_fun.span, "format").is_some() && inner_args.len() == 1;
-        if let hir::ExprKind::Call(_, format_args) = &inner_args[0].kind;
-        then {
-            let fmt_spec = &format_args[0];
-            let fmt_args = &format_args[1];
-
-            let mut args = vec![snippet(cx, fmt_spec.span, "..").into_owned()];
-
-            args.extend(generate_format_arg_snippet(cx, fmt_args, &mut applicability));
-
-            let sugg = args.join(", ");
-
-            span_lint_and_sugg(
-                cx,
-                EXPECT_FUN_CALL,
-                span_replace_word,
-                &format!("use of `{}` followed by a function call", name),
-                "try this",
-                format!("unwrap_or_else({} panic!({}))", closure_args, sugg),
-                applicability,
-            );
-
-            return;
-        }
-    }
-
-    let mut arg_root_snippet: Cow<'_, _> = snippet_with_applicability(cx, arg_root.span, "..", &mut applicability);
-    if requires_to_string(cx, arg_root) {
-        arg_root_snippet.to_mut().push_str(".to_string()");
-    }
-
-    span_lint_and_sugg(
-        cx,
-        EXPECT_FUN_CALL,
-        span_replace_word,
-        &format!("use of `{}` followed by a function call", name),
-        "try this",
-        format!(
-            "unwrap_or_else({} {{ panic!(\"{{}}\", {}) }})",
-            closure_args, arg_root_snippet
-        ),
-        applicability,
-    );
-}
-
 fn derefs_to_slice<'tcx>(
     cx: &LateContext<'tcx>,
     expr: &'tcx hir::Expr<'tcx>,