|
1 | 1 | use clippy_utils::diagnostics::span_lint_hir_and_then; |
2 | 2 | use clippy_utils::return_ty; |
3 | | -use clippy_utils::source::snippet; |
| 3 | +use clippy_utils::source::snippet_with_applicability; |
4 | 4 | use clippy_utils::sugg::DiagExt; |
5 | 5 | use rustc_errors::Applicability; |
6 | 6 | use rustc_hir as hir; |
@@ -58,116 +58,132 @@ impl_lint_pass!(NewWithoutDefault => [NEW_WITHOUT_DEFAULT]); |
58 | 58 |
|
59 | 59 | impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { |
60 | 60 | fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { |
61 | | - if let hir::ItemKind::Impl(hir::Impl { |
| 61 | + let hir::ItemKind::Impl(hir::Impl { |
62 | 62 | of_trait: None, |
63 | 63 | generics, |
64 | 64 | self_ty: impl_self_ty, |
65 | 65 | .. |
66 | 66 | }) = item.kind |
| 67 | + else { |
| 68 | + return; |
| 69 | + }; |
| 70 | + |
| 71 | + for assoc_item in cx |
| 72 | + .tcx |
| 73 | + .associated_items(item.owner_id.def_id) |
| 74 | + .filter_by_name_unhygienic(sym::new) |
67 | 75 | { |
68 | | - for assoc_item in cx |
69 | | - .tcx |
70 | | - .associated_items(item.owner_id.def_id) |
71 | | - .filter_by_name_unhygienic(sym::new) |
| 76 | + if let AssocKind::Fn { has_self: false, .. } = assoc_item.kind |
| 77 | + && let assoc_item_hir_id = cx.tcx.local_def_id_to_hir_id(assoc_item.def_id.expect_local()) |
| 78 | + && let impl_item = cx.tcx.hir_node(assoc_item_hir_id).expect_impl_item() |
| 79 | + && !impl_item.span.in_external_macro(cx.sess().source_map()) |
| 80 | + && let hir::ImplItemKind::Fn(ref sig, _) = impl_item.kind |
| 81 | + && let id = impl_item.owner_id |
| 82 | + // can't be implemented for unsafe new |
| 83 | + && !sig.header.is_unsafe() |
| 84 | + // shouldn't be implemented when it is hidden in docs |
| 85 | + && !cx.tcx.is_doc_hidden(impl_item.owner_id.def_id) |
| 86 | + // when the result of `new()` depends on a parameter we should not require |
| 87 | + // an impl of `Default` |
| 88 | + && impl_item.generics.params.is_empty() |
| 89 | + && sig.decl.inputs.is_empty() |
| 90 | + && cx.effective_visibilities.is_exported(impl_item.owner_id.def_id) |
| 91 | + && let self_ty = cx.tcx.type_of(item.owner_id).instantiate_identity() |
| 92 | + && self_ty == return_ty(cx, impl_item.owner_id) |
| 93 | + && let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default) |
72 | 94 | { |
73 | | - if let AssocKind::Fn { has_self: false, .. } = assoc_item.kind { |
74 | | - let impl_item = cx |
75 | | - .tcx |
76 | | - .hir_node_by_def_id(assoc_item.def_id.expect_local()) |
77 | | - .expect_impl_item(); |
78 | | - if impl_item.span.in_external_macro(cx.sess().source_map()) { |
79 | | - return; |
80 | | - } |
81 | | - if let hir::ImplItemKind::Fn(ref sig, _) = impl_item.kind { |
82 | | - let id = impl_item.owner_id; |
83 | | - if sig.header.is_unsafe() { |
84 | | - // can't be implemented for unsafe new |
85 | | - return; |
86 | | - } |
87 | | - if cx.tcx.is_doc_hidden(impl_item.owner_id.def_id) { |
88 | | - // shouldn't be implemented when it is hidden in docs |
89 | | - return; |
90 | | - } |
91 | | - if !impl_item.generics.params.is_empty() { |
92 | | - // when the result of `new()` depends on a parameter we should not require |
93 | | - // an impl of `Default` |
94 | | - return; |
95 | | - } |
96 | | - if sig.decl.inputs.is_empty() |
97 | | - && cx.effective_visibilities.is_exported(impl_item.owner_id.def_id) |
98 | | - && let self_ty = cx.tcx.type_of(item.owner_id).instantiate_identity() |
99 | | - && self_ty == return_ty(cx, impl_item.owner_id) |
100 | | - && let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default) |
| 95 | + if self.impling_types.is_none() { |
| 96 | + let mut impls = HirIdSet::default(); |
| 97 | + for &d in cx.tcx.local_trait_impls(default_trait_id) { |
| 98 | + let ty = cx.tcx.type_of(d).instantiate_identity(); |
| 99 | + if let Some(ty_def) = ty.ty_adt_def() |
| 100 | + && let Some(local_def_id) = ty_def.did().as_local() |
101 | 101 | { |
102 | | - if self.impling_types.is_none() { |
103 | | - let mut impls = HirIdSet::default(); |
104 | | - for &d in cx.tcx.local_trait_impls(default_trait_id) { |
105 | | - let ty = cx.tcx.type_of(d).instantiate_identity(); |
106 | | - if let Some(ty_def) = ty.ty_adt_def() |
107 | | - && let Some(local_def_id) = ty_def.did().as_local() |
108 | | - { |
109 | | - impls.insert(cx.tcx.local_def_id_to_hir_id(local_def_id)); |
110 | | - } |
111 | | - } |
112 | | - self.impling_types = Some(impls); |
113 | | - } |
| 102 | + impls.insert(cx.tcx.local_def_id_to_hir_id(local_def_id)); |
| 103 | + } |
| 104 | + } |
| 105 | + self.impling_types = Some(impls); |
| 106 | + } |
114 | 107 |
|
115 | | - // Check if a Default implementation exists for the Self type, regardless of |
116 | | - // generics |
117 | | - if let Some(ref impling_types) = self.impling_types |
118 | | - && let self_def = cx.tcx.type_of(item.owner_id).instantiate_identity() |
119 | | - && let Some(self_def) = self_def.ty_adt_def() |
120 | | - && let Some(self_local_did) = self_def.did().as_local() |
121 | | - && let self_id = cx.tcx.local_def_id_to_hir_id(self_local_did) |
122 | | - && impling_types.contains(&self_id) |
123 | | - { |
124 | | - return; |
125 | | - } |
| 108 | + // Check if a Default implementation exists for the Self type, regardless of |
| 109 | + // generics |
| 110 | + if let Some(ref impling_types) = self.impling_types |
| 111 | + && let self_def = cx.tcx.type_of(item.owner_id).instantiate_identity() |
| 112 | + && let Some(self_def) = self_def.ty_adt_def() |
| 113 | + && let Some(self_local_did) = self_def.did().as_local() |
| 114 | + && let self_id = cx.tcx.local_def_id_to_hir_id(self_local_did) |
| 115 | + && impling_types.contains(&self_id) |
| 116 | + { |
| 117 | + return; |
| 118 | + } |
126 | 119 |
|
127 | | - let generics_sugg = snippet(cx, generics.span, ""); |
128 | | - let where_clause_sugg = if generics.has_where_clause_predicates { |
129 | | - format!("\n{}\n", snippet(cx, generics.where_clause_span, "")) |
130 | | - } else { |
131 | | - String::new() |
132 | | - }; |
133 | | - let self_ty_fmt = self_ty.to_string(); |
134 | | - let self_type_snip = snippet(cx, impl_self_ty.span, &self_ty_fmt); |
135 | | - span_lint_hir_and_then( |
136 | | - cx, |
137 | | - NEW_WITHOUT_DEFAULT, |
138 | | - id.into(), |
139 | | - impl_item.span, |
140 | | - format!("you should consider adding a `Default` implementation for `{self_type_snip}`"), |
141 | | - |diag| { |
142 | | - diag.suggest_prepend_item( |
143 | | - cx, |
144 | | - item.span, |
145 | | - "try adding this", |
146 | | - &create_new_without_default_suggest_msg( |
147 | | - &self_type_snip, |
148 | | - &generics_sugg, |
149 | | - &where_clause_sugg, |
150 | | - ), |
151 | | - Applicability::MachineApplicable, |
152 | | - ); |
153 | | - }, |
154 | | - ); |
| 120 | + let mut app = Applicability::MachineApplicable; |
| 121 | + let attrs_sugg = { |
| 122 | + let mut sugg = String::new(); |
| 123 | + for attr in cx.tcx.hir_attrs(assoc_item_hir_id) { |
| 124 | + if !attr.has_name(sym::cfg_trace) { |
| 125 | + // This might be some other attribute that the `impl Default` ought to inherit. |
| 126 | + // But it could also be one of the many attributes that: |
| 127 | + // - can't be put on an impl block -- like `#[inline]` |
| 128 | + // - we can't even build a suggestion for, since `Attribute::span` may panic. |
| 129 | + // |
| 130 | + // Because of all that, remain on the safer side -- don't inherit this attr, and just |
| 131 | + // reduce the applicability |
| 132 | + app = Applicability::MaybeIncorrect; |
| 133 | + continue; |
155 | 134 | } |
| 135 | + |
| 136 | + sugg.push_str(&snippet_with_applicability(cx.sess(), attr.span(), "_", &mut app)); |
| 137 | + sugg.push('\n'); |
156 | 138 | } |
157 | | - } |
| 139 | + sugg |
| 140 | + }; |
| 141 | + let generics_sugg = snippet_with_applicability(cx, generics.span, "", &mut app); |
| 142 | + let where_clause_sugg = if generics.has_where_clause_predicates { |
| 143 | + format!( |
| 144 | + "\n{}\n", |
| 145 | + snippet_with_applicability(cx, generics.where_clause_span, "", &mut app) |
| 146 | + ) |
| 147 | + } else { |
| 148 | + String::new() |
| 149 | + }; |
| 150 | + let self_ty_fmt = self_ty.to_string(); |
| 151 | + let self_type_snip = snippet_with_applicability(cx, impl_self_ty.span, &self_ty_fmt, &mut app); |
| 152 | + span_lint_hir_and_then( |
| 153 | + cx, |
| 154 | + NEW_WITHOUT_DEFAULT, |
| 155 | + id.into(), |
| 156 | + impl_item.span, |
| 157 | + format!("you should consider adding a `Default` implementation for `{self_type_snip}`"), |
| 158 | + |diag| { |
| 159 | + diag.suggest_prepend_item( |
| 160 | + cx, |
| 161 | + item.span, |
| 162 | + "try adding this", |
| 163 | + &create_new_without_default_suggest_msg( |
| 164 | + &attrs_sugg, |
| 165 | + &self_type_snip, |
| 166 | + &generics_sugg, |
| 167 | + &where_clause_sugg, |
| 168 | + ), |
| 169 | + app, |
| 170 | + ); |
| 171 | + }, |
| 172 | + ); |
158 | 173 | } |
159 | 174 | } |
160 | 175 | } |
161 | 176 | } |
162 | 177 |
|
163 | 178 | fn create_new_without_default_suggest_msg( |
| 179 | + attrs_sugg: &str, |
164 | 180 | self_type_snip: &str, |
165 | 181 | generics_sugg: &str, |
166 | 182 | where_clause_sugg: &str, |
167 | 183 | ) -> String { |
168 | 184 | #[rustfmt::skip] |
169 | 185 | format!( |
170 | | -"impl{generics_sugg} Default for {self_type_snip}{where_clause_sugg} {{ |
| 186 | +"{attrs_sugg}impl{generics_sugg} Default for {self_type_snip}{where_clause_sugg} {{ |
171 | 187 | fn default() -> Self {{ |
172 | 188 | Self::new() |
173 | 189 | }} |
|
0 commit comments