|
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,103 +58,99 @@ 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 impl_item = cx |
| 78 | + .tcx |
| 79 | + .hir_node_by_def_id(assoc_item.def_id.expect_local()) |
| 80 | + .expect_impl_item() |
| 81 | + && !impl_item.span.in_external_macro(cx.sess().source_map()) |
| 82 | + && let hir::ImplItemKind::Fn(ref sig, _) = impl_item.kind |
| 83 | + && let id = impl_item.owner_id |
| 84 | + // can't be implemented for unsafe new |
| 85 | + && !sig.header.is_unsafe() |
| 86 | + // shouldn't be implemented when it is hidden in docs |
| 87 | + && !cx.tcx.is_doc_hidden(impl_item.owner_id.def_id) |
| 88 | + // when the result of `new()` depends on a parameter we should not require |
| 89 | + // an impl of `Default` |
| 90 | + && impl_item.generics.params.is_empty() |
| 91 | + && sig.decl.inputs.is_empty() |
| 92 | + && cx.effective_visibilities.is_reachable(impl_item.owner_id.def_id) |
| 93 | + && let self_ty = cx.tcx.type_of(item.owner_id).instantiate_identity() |
| 94 | + && self_ty == return_ty(cx, impl_item.owner_id) |
| 95 | + && let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default) |
72 | 96 | {
|
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_reachable(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) |
| 97 | + if self.impling_types.is_none() { |
| 98 | + let mut impls = HirIdSet::default(); |
| 99 | + for &d in cx.tcx.local_trait_impls(default_trait_id) { |
| 100 | + let ty = cx.tcx.type_of(d).instantiate_identity(); |
| 101 | + if let Some(ty_def) = ty.ty_adt_def() |
| 102 | + && let Some(local_def_id) = ty_def.did().as_local() |
101 | 103 | {
|
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 |
| - } |
114 |
| - |
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 |
| - } |
126 |
| - |
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 |
| - ); |
| 104 | + impls.insert(cx.tcx.local_def_id_to_hir_id(local_def_id)); |
155 | 105 | }
|
156 | 106 | }
|
| 107 | + self.impling_types = Some(impls); |
157 | 108 | }
|
| 109 | + |
| 110 | + // Check if a Default implementation exists for the Self type, regardless of |
| 111 | + // generics |
| 112 | + if let Some(ref impling_types) = self.impling_types |
| 113 | + && let self_def = cx.tcx.type_of(item.owner_id).instantiate_identity() |
| 114 | + && let Some(self_def) = self_def.ty_adt_def() |
| 115 | + && let Some(self_local_did) = self_def.did().as_local() |
| 116 | + && let self_id = cx.tcx.local_def_id_to_hir_id(self_local_did) |
| 117 | + && impling_types.contains(&self_id) |
| 118 | + { |
| 119 | + return; |
| 120 | + } |
| 121 | + |
| 122 | + let mut appl = Applicability::MachineApplicable; |
| 123 | + let generics_sugg = snippet_with_applicability(cx, generics.span, "", &mut appl); |
| 124 | + let where_clause_sugg = if generics.has_where_clause_predicates { |
| 125 | + format!( |
| 126 | + "\n{}\n", |
| 127 | + snippet_with_applicability(cx, generics.where_clause_span, "", &mut appl) |
| 128 | + ) |
| 129 | + } else { |
| 130 | + String::new() |
| 131 | + }; |
| 132 | + let self_ty_fmt = self_ty.to_string(); |
| 133 | + let self_type_snip = snippet_with_applicability(cx, impl_self_ty.span, &self_ty_fmt, &mut appl); |
| 134 | + span_lint_hir_and_then( |
| 135 | + cx, |
| 136 | + NEW_WITHOUT_DEFAULT, |
| 137 | + id.into(), |
| 138 | + impl_item.span, |
| 139 | + format!("you should consider adding a `Default` implementation for `{self_type_snip}`"), |
| 140 | + |diag| { |
| 141 | + diag.suggest_prepend_item( |
| 142 | + cx, |
| 143 | + item.span, |
| 144 | + "try adding this", |
| 145 | + &create_new_without_default_suggest_msg( |
| 146 | + &self_type_snip, |
| 147 | + &generics_sugg, |
| 148 | + &where_clause_sugg, |
| 149 | + ), |
| 150 | + appl, |
| 151 | + ); |
| 152 | + }, |
| 153 | + ); |
158 | 154 | }
|
159 | 155 | }
|
160 | 156 | }
|
|
0 commit comments