|  | 
| 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