]>
Commit | Line | Data |
---|---|---|
cdc7bbd5 XL |
1 | use clippy_utils::diagnostics::span_lint_and_then; |
2 | use clippy_utils::source::snippet_with_applicability; | |
3 | use clippy_utils::ty::{is_type_diagnostic_item, walk_ptrs_ty_depth}; | |
4 | use clippy_utils::{match_def_path, paths}; | |
f20569fa XL |
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}; | |
cdc7bbd5 XL |
10 | use rustc_span::symbol::{sym, Symbol}; |
11 | ||
12 | use super::INEFFICIENT_TO_STRING; | |
f20569fa XL |
13 | |
14 | /// Checks for the `INEFFICIENT_TO_STRING` lint | |
f2b60f7d FG |
15 | pub fn check<'tcx>( |
16 | cx: &LateContext<'tcx>, | |
17 | expr: &hir::Expr<'_>, | |
18 | method_name: Symbol, | |
19 | receiver: &hir::Expr<'_>, | |
20 | args: &[hir::Expr<'_>], | |
21 | ) { | |
f20569fa | 22 | if_chain! { |
f2b60f7d | 23 | if args.is_empty() && method_name == sym::to_string; |
f20569fa XL |
24 | if let Some(to_string_meth_did) = cx.typeck_results().type_dependent_def_id(expr.hir_id); |
25 | if match_def_path(cx, to_string_meth_did, &paths::TO_STRING_METHOD); | |
26 | if let Some(substs) = cx.typeck_results().node_substs_opt(expr.hir_id); | |
f2b60f7d | 27 | let arg_ty = cx.typeck_results().expr_ty_adjusted(receiver); |
f20569fa XL |
28 | let self_ty = substs.type_at(0); |
29 | let (deref_self_ty, deref_count) = walk_ptrs_ty_depth(self_ty); | |
30 | if deref_count >= 1; | |
31 | if specializes_tostring(cx, deref_self_ty); | |
32 | then { | |
33 | span_lint_and_then( | |
34 | cx, | |
35 | INEFFICIENT_TO_STRING, | |
36 | expr.span, | |
2b03887a | 37 | &format!("calling `to_string` on `{arg_ty}`"), |
f20569fa XL |
38 | |diag| { |
39 | diag.help(&format!( | |
2b03887a | 40 | "`{self_ty}` implements `ToString` through a slower blanket impl, but `{deref_self_ty}` has a fast specialization of `ToString`" |
f20569fa XL |
41 | )); |
42 | let mut applicability = Applicability::MachineApplicable; | |
f2b60f7d | 43 | let arg_snippet = snippet_with_applicability(cx, receiver.span, "..", &mut applicability); |
f20569fa XL |
44 | diag.span_suggestion( |
45 | expr.span, | |
46 | "try dereferencing the receiver", | |
2b03887a | 47 | format!("({}{arg_snippet}).to_string()", "*".repeat(deref_count)), |
f20569fa XL |
48 | applicability, |
49 | ); | |
50 | }, | |
51 | ); | |
52 | } | |
53 | } | |
54 | } | |
55 | ||
56 | /// Returns whether `ty` specializes `ToString`. | |
57 | /// Currently, these are `str`, `String`, and `Cow<'_, str>`. | |
58 | fn specializes_tostring(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { | |
59 | if let ty::Str = ty.kind() { | |
60 | return true; | |
61 | } | |
62 | ||
c295e0f8 | 63 | if is_type_diagnostic_item(cx, ty, sym::String) { |
f20569fa XL |
64 | return true; |
65 | } | |
66 | ||
67 | if let ty::Adt(adt, substs) = ty.kind() { | |
2b03887a | 68 | cx.tcx.is_diagnostic_item(sym::Cow, adt.did()) && substs.type_at(1).is_str() |
f20569fa XL |
69 | } else { |
70 | false | |
71 | } | |
72 | } |