]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/methods/inefficient_to_string.rs
Merge remote-tracking branch 'upstream/master' into rustup
[rust.git] / clippy_lints / src / methods / inefficient_to_string.rs
1 use super::INEFFICIENT_TO_STRING;
2 use crate::utils::{
3     is_type_diagnostic_item, match_def_path, paths, snippet_with_applicability, span_lint_and_then, walk_ptrs_ty_depth,
4 };
5 use if_chain::if_chain;
6 use rustc_errors::Applicability;
7 use rustc_hir as hir;
8 use rustc_lint::LateContext;
9 use rustc_middle::ty::{self, Ty};
10 use rustc_span::sym;
11
12 /// Checks for the `INEFFICIENT_TO_STRING` lint
13 pub fn lint<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>, arg_ty: Ty<'tcx>) {
14     if_chain! {
15         if let Some(to_string_meth_did) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
16         if match_def_path(cx, to_string_meth_did, &paths::TO_STRING_METHOD);
17         if let Some(substs) = cx.typeck_results().node_substs_opt(expr.hir_id);
18         let self_ty = substs.type_at(0);
19         let (deref_self_ty, deref_count) = walk_ptrs_ty_depth(self_ty);
20         if deref_count >= 1;
21         if specializes_tostring(cx, deref_self_ty);
22         then {
23             span_lint_and_then(
24                 cx,
25                 INEFFICIENT_TO_STRING,
26                 expr.span,
27                 &format!("calling `to_string` on `{}`", arg_ty),
28                 |diag| {
29                     diag.help(&format!(
30                         "`{}` implements `ToString` through a slower blanket impl, but `{}` has a fast specialization of `ToString`",
31                         self_ty, deref_self_ty
32                     ));
33                     let mut applicability = Applicability::MachineApplicable;
34                     let arg_snippet = snippet_with_applicability(cx, arg.span, "..", &mut applicability);
35                     diag.span_suggestion(
36                         expr.span,
37                         "try dereferencing the receiver",
38                         format!("({}{}).to_string()", "*".repeat(deref_count), arg_snippet),
39                         applicability,
40                     );
41                 },
42             );
43         }
44     }
45 }
46
47 /// Returns whether `ty` specializes `ToString`.
48 /// Currently, these are `str`, `String`, and `Cow<'_, str>`.
49 fn specializes_tostring(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
50     if let ty::Str = ty.kind() {
51         return true;
52     }
53
54     if is_type_diagnostic_item(cx, ty, sym::string_type) {
55         return true;
56     }
57
58     if let ty::Adt(adt, substs) = ty.kind() {
59         match_def_path(cx, adt.did, &paths::COW) && substs.type_at(1).is_str()
60     } else {
61         false
62     }
63 }