1 use clippy_utils
::diagnostics
::span_lint_hir_and_then
;
2 use clippy_utils
::return_ty
;
3 use clippy_utils
::source
::snippet
;
4 use clippy_utils
::sugg
::DiagnosticBuilderExt
;
5 use if_chain
::if_chain
;
6 use rustc_errors
::Applicability
;
8 use rustc_hir
::HirIdSet
;
9 use rustc_lint
::{LateContext, LateLintPass, LintContext}
;
10 use rustc_middle
::lint
::in_external_macro
;
11 use rustc_middle
::ty
::TyS
;
12 use rustc_session
::{declare_tool_lint, impl_lint_pass}
;
15 declare_clippy_lint
! {
17 /// Checks for types with a `fn new() -> Self` method and no
19 /// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html).
21 /// ### Why is this bad?
22 /// The user might expect to be able to use
23 /// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html) as the
24 /// type can be constructed without arguments.
31 /// fn new() -> Self {
37 /// To fix the lint, add a `Default` implementation that delegates to `new`:
42 /// impl Default for Foo {
43 /// fn default() -> Self {
48 #[clippy::version = "pre 1.29.0"]
49 pub NEW_WITHOUT_DEFAULT
,
51 "`fn new() -> Self` method without `Default` implementation"
54 #[derive(Clone, Default)]
55 pub struct NewWithoutDefault
{
56 impling_types
: Option
<HirIdSet
>,
59 impl_lint_pass
!(NewWithoutDefault
=> [NEW_WITHOUT_DEFAULT
]);
61 impl<'tcx
> LateLintPass
<'tcx
> for NewWithoutDefault
{
62 #[allow(clippy::too_many_lines)]
63 fn check_item(&mut self, cx
: &LateContext
<'tcx
>, item
: &'tcx hir
::Item
<'_
>) {
64 if let hir
::ItemKind
::Impl(hir
::Impl
{
67 self_ty
: impl_self_ty
,
72 for assoc_item
in items
{
73 if assoc_item
.kind
== (hir
::AssocItemKind
::Fn { has_self: false }
) {
74 let impl_item
= cx
.tcx
.hir().impl_item(assoc_item
.id
);
75 if in_external_macro(cx
.sess(), impl_item
.span
) {
78 if let hir
::ImplItemKind
::Fn(ref sig
, _
) = impl_item
.kind
{
79 let name
= impl_item
.ident
.name
;
80 let id
= impl_item
.hir_id();
81 if sig
.header
.constness
== hir
::Constness
::Const
{
82 // can't be implemented by default
85 if sig
.header
.unsafety
== hir
::Unsafety
::Unsafe
{
86 // can't be implemented for unsafe new
93 .any(|gen
| matches
!(gen
.kind
, hir
::GenericParamKind
::Type { .. }
))
95 // when the result of `new()` depends on a type parameter we should not require
101 if sig
.decl
.inputs
.is_empty();
103 if cx
.access_levels
.is_reachable(impl_item
.def_id
);
104 let self_def_id
= cx
.tcx
.hir().local_def_id(cx
.tcx
.hir().get_parent_item(id
));
105 let self_ty
= cx
.tcx
.type_of(self_def_id
);
106 if TyS
::same_type(self_ty
, return_ty(cx
, id
));
107 if let Some(default_trait_id
) = cx
.tcx
.get_diagnostic_item(sym
::Default
);
109 if self.impling_types
.is_none() {
110 let mut impls
= HirIdSet
::default();
111 cx
.tcx
.for_each_impl(default_trait_id
, |d
| {
112 if let Some(ty_def
) = cx
.tcx
.type_of(d
).ty_adt_def() {
113 if let Some(local_def_id
) = ty_def
.did
.as_local() {
114 impls
.insert(cx
.tcx
.hir().local_def_id_to_hir_id(local_def_id
));
118 self.impling_types
= Some(impls
);
121 // Check if a Default implementation exists for the Self type, regardless of
124 if let Some(ref impling_types
) = self.impling_types
;
125 if let Some(self_def
) = cx
.tcx
.type_of(self_def_id
).ty_adt_def();
126 if let Some(self_local_did
) = self_def
.did
.as_local();
127 let self_id
= cx
.tcx
.hir().local_def_id_to_hir_id(self_local_did
);
128 if impling_types
.contains(&self_id
);
134 let generics_sugg
= snippet(cx
, generics
.span
, "");
135 let self_ty_fmt
= self_ty
.to_string();
136 let self_type_snip
= snippet(cx
, impl_self_ty
.span
, &self_ty_fmt
);
137 span_lint_hir_and_then(
143 "you should consider adding a `Default` implementation for `{}`",
147 diag
.suggest_prepend_item(
151 &create_new_without_default_suggest_msg(&self_type_snip
, &generics_sugg
),
152 Applicability
::MaybeIncorrect
,
165 fn create_new_without_default_suggest_msg(self_type_snip
: &str, generics_sugg
: &str) -> String
{
168 "impl{} Default for {} {{
169 fn default() -> Self {{
172 }}", generics_sugg
, self_type_snip
)