]>
Commit | Line | Data |
---|---|---|
cdc7bbd5 XL |
1 | use clippy_utils::diagnostics::span_lint_and_help; |
2 | use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; | |
3 | use clippy_utils::{get_trait_def_id, paths, return_ty, trait_ref_of_method}; | |
f20569fa XL |
4 | use if_chain::if_chain; |
5 | use rustc_hir::{ImplItem, ImplItemKind}; | |
6 | use rustc_lint::{LateContext, LateLintPass}; | |
7 | use rustc_session::{declare_lint_pass, declare_tool_lint}; | |
8 | use rustc_span::sym; | |
9 | ||
f20569fa | 10 | declare_clippy_lint! { |
94222f64 XL |
11 | /// ### What it does |
12 | /// Checks for the definition of inherent methods with a signature of `to_string(&self) -> String`. | |
f20569fa | 13 | /// |
94222f64 XL |
14 | /// ### Why is this bad? |
15 | /// This method is also implicitly defined if a type implements the `Display` trait. As the functionality of `Display` is much more versatile, it should be preferred. | |
f20569fa | 16 | /// |
94222f64 | 17 | /// ### Example |
f20569fa | 18 | /// ```rust |
f20569fa XL |
19 | /// pub struct A; |
20 | /// | |
21 | /// impl A { | |
22 | /// pub fn to_string(&self) -> String { | |
23 | /// "I am A".to_string() | |
24 | /// } | |
25 | /// } | |
26 | /// ``` | |
27 | /// | |
923072b8 | 28 | /// Use instead: |
f20569fa | 29 | /// ```rust |
f20569fa XL |
30 | /// use std::fmt; |
31 | /// | |
32 | /// pub struct A; | |
33 | /// | |
34 | /// impl fmt::Display for A { | |
35 | /// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
36 | /// write!(f, "I am A") | |
37 | /// } | |
38 | /// } | |
39 | /// ``` | |
a2a8927a | 40 | #[clippy::version = "1.38.0"] |
f20569fa XL |
41 | pub INHERENT_TO_STRING, |
42 | style, | |
43 | "type implements inherent method `to_string()`, but should instead implement the `Display` trait" | |
44 | } | |
45 | ||
46 | declare_clippy_lint! { | |
94222f64 XL |
47 | /// ### What it does |
48 | /// Checks for the definition of inherent methods with a signature of `to_string(&self) -> String` and if the type implementing this method also implements the `Display` trait. | |
f20569fa | 49 | /// |
94222f64 XL |
50 | /// ### Why is this bad? |
51 | /// This method is also implicitly defined if a type implements the `Display` trait. The less versatile inherent method will then shadow the implementation introduced by `Display`. | |
f20569fa | 52 | /// |
94222f64 | 53 | /// ### Example |
f20569fa | 54 | /// ```rust |
f20569fa XL |
55 | /// use std::fmt; |
56 | /// | |
57 | /// pub struct A; | |
58 | /// | |
59 | /// impl A { | |
60 | /// pub fn to_string(&self) -> String { | |
61 | /// "I am A".to_string() | |
62 | /// } | |
63 | /// } | |
64 | /// | |
65 | /// impl fmt::Display for A { | |
66 | /// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
67 | /// write!(f, "I am A, too") | |
68 | /// } | |
69 | /// } | |
70 | /// ``` | |
71 | /// | |
923072b8 | 72 | /// Use instead: |
f20569fa | 73 | /// ```rust |
f20569fa XL |
74 | /// use std::fmt; |
75 | /// | |
76 | /// pub struct A; | |
77 | /// | |
78 | /// impl fmt::Display for A { | |
79 | /// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
80 | /// write!(f, "I am A") | |
81 | /// } | |
82 | /// } | |
83 | /// ``` | |
a2a8927a | 84 | #[clippy::version = "1.38.0"] |
f20569fa XL |
85 | pub INHERENT_TO_STRING_SHADOW_DISPLAY, |
86 | correctness, | |
87 | "type implements inherent method `to_string()`, which gets shadowed by the implementation of the `Display` trait" | |
88 | } | |
89 | ||
90 | declare_lint_pass!(InherentToString => [INHERENT_TO_STRING, INHERENT_TO_STRING_SHADOW_DISPLAY]); | |
91 | ||
92 | impl<'tcx> LateLintPass<'tcx> for InherentToString { | |
93 | fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) { | |
94 | if impl_item.span.from_expansion() { | |
95 | return; | |
96 | } | |
97 | ||
98 | if_chain! { | |
99 | // Check if item is a method, called to_string and has a parameter 'self' | |
100 | if let ImplItemKind::Fn(ref signature, _) = impl_item.kind; | |
101 | if impl_item.ident.name.as_str() == "to_string"; | |
102 | let decl = &signature.decl; | |
103 | if decl.implicit_self.has_implicit_self(); | |
104 | if decl.inputs.len() == 1; | |
105 | if impl_item.generics.params.is_empty(); | |
106 | ||
107 | // Check if return type is String | |
c295e0f8 | 108 | if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id()), sym::String); |
f20569fa XL |
109 | |
110 | // Filters instances of to_string which are required by a trait | |
5099ac24 | 111 | if trait_ref_of_method(cx, impl_item.def_id).is_none(); |
f20569fa XL |
112 | |
113 | then { | |
114 | show_lint(cx, impl_item); | |
115 | } | |
116 | } | |
117 | } | |
118 | } | |
119 | ||
120 | fn show_lint(cx: &LateContext<'_>, item: &ImplItem<'_>) { | |
121 | let display_trait_id = get_trait_def_id(cx, &paths::DISPLAY_TRAIT).expect("Failed to get trait ID of `Display`!"); | |
122 | ||
123 | // Get the real type of 'self' | |
124 | let self_type = cx.tcx.fn_sig(item.def_id).input(0); | |
125 | let self_type = self_type.skip_binder().peel_refs(); | |
126 | ||
127 | // Emit either a warning or an error | |
128 | if implements_trait(cx, self_type, display_trait_id, &[]) { | |
129 | span_lint_and_help( | |
130 | cx, | |
131 | INHERENT_TO_STRING_SHADOW_DISPLAY, | |
132 | item.span, | |
133 | &format!( | |
134 | "type `{}` implements inherent method `to_string(&self) -> String` which shadows the implementation of `Display`", | |
3c0e092e | 135 | self_type |
f20569fa XL |
136 | ), |
137 | None, | |
3c0e092e | 138 | &format!("remove the inherent method from type `{}`", self_type), |
f20569fa XL |
139 | ); |
140 | } else { | |
141 | span_lint_and_help( | |
142 | cx, | |
143 | INHERENT_TO_STRING, | |
144 | item.span, | |
145 | &format!( | |
146 | "implementation of inherent method `to_string(&self) -> String` for type `{}`", | |
3c0e092e | 147 | self_type |
f20569fa XL |
148 | ), |
149 | None, | |
3c0e092e | 150 | &format!("implement trait `Display` for type `{}` instead", self_type), |
f20569fa XL |
151 | ); |
152 | } | |
153 | } |