]> git.lizzy.rs Git - rust.git/blobdiff - clippy_lints/src/new_without_default.rs
clippy: support `QPath::LangItem`
[rust.git] / clippy_lints / src / new_without_default.rs
index bbe76fae51eed72e591eae2dc31743835256e689..28d1322e94626c604cced668546593bb2193e02e 100644 (file)
@@ -1,27 +1,20 @@
 use crate::utils::paths;
 use crate::utils::sugg::DiagnosticBuilderExt;
-use crate::utils::{get_trait_def_id, implements_trait, return_ty, same_tys, span_lint_hir_and_then};
+use crate::utils::{get_trait_def_id, return_ty, span_lint_hir_and_then};
 use if_chain::if_chain;
-use rustc::hir;
-use rustc::hir::def_id::DefId;
-use rustc::impl_lint_pass;
-use rustc::lint::{in_external_macro, LateContext, LateLintPass, LintArray, LintContext, LintPass};
-use rustc::ty::{self, Ty};
-use rustc::util::nodemap::HirIdSet;
 use rustc_errors::Applicability;
-use rustc_session::declare_tool_lint;
-use syntax::source_map::Span;
+use rustc_hir as hir;
+use rustc_hir::HirIdSet;
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::lint::in_external_macro;
+use rustc_middle::ty::{Ty, TyS};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
 
 declare_clippy_lint! {
     /// **What it does:** Checks for types with a `fn new() -> Self` method and no
     /// implementation of
     /// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html).
     ///
-    /// It detects both the case when a manual
-    /// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html)
-    /// implementation is required and also when it can be created with
-    /// `#[derive(Default)]`
-    ///
     /// **Why is this bad?** The user might expect to be able to use
     /// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html) as the
     /// type can be constructed without arguments.
     /// }
     /// ```
     ///
-    /// Instead, use:
+    /// To fix the lint, add a `Default` implementation that delegates to `new`:
     ///
     /// ```ignore
     /// struct Foo(Bar);
     ///
     /// impl Default for Foo {
     ///     fn default() -> Self {
-    ///         Foo(Bar::new())
+    ///         Foo::new()
     ///     }
     /// }
     /// ```
-    ///
-    /// Or, if
-    /// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html)
-    /// can be derived by `#[derive(Default)]`:
-    ///
-    /// ```rust
-    /// struct Foo;
-    ///
-    /// impl Foo {
-    ///     fn new() -> Self {
-    ///         Foo
-    ///     }
-    /// }
-    /// ```
-    ///
-    /// Instead, use:
-    ///
-    /// ```rust
-    /// #[derive(Default)]
-    /// struct Foo;
-    ///
-    /// impl Foo {
-    ///     fn new() -> Self {
-    ///         Foo
-    ///     }
-    /// }
-    /// ```
-    ///
-    /// You can also have `new()` call `Default::default()`.
     pub NEW_WITHOUT_DEFAULT,
     style,
     "`fn new() -> Self` method without `Default` implementation"
@@ -92,16 +56,20 @@ pub struct NewWithoutDefault {
 
 impl_lint_pass!(NewWithoutDefault => [NEW_WITHOUT_DEFAULT]);
 
-impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NewWithoutDefault {
-    fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::Item<'_>) {
-        if let hir::ItemKind::Impl(_, _, _, _, None, _, ref items) = item.kind {
+impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault {
+    #[allow(clippy::too_many_lines)]
+    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
+        if let hir::ItemKind::Impl {
+            of_trait: None, items, ..
+        } = item.kind
+        {
             for assoc_item in items {
-                if let hir::AssocItemKind::Method { has_self: false } = assoc_item.kind {
+                if let hir::AssocItemKind::Fn { has_self: false } = assoc_item.kind {
                     let impl_item = cx.tcx.hir().impl_item(assoc_item.id);
                     if in_external_macro(cx.sess(), impl_item.span) {
                         return;
                     }
-                    if let hir::ImplItemKind::Method(ref sig, _) = impl_item.kind {
+                    if let hir::ImplItemKind::Fn(ref sig, _) = impl_item.kind {
                         let name = impl_item.ident.name;
                         let id = impl_item.hir_id;
                         if sig.header.constness == hir::Constness::Const {
@@ -112,28 +80,30 @@ fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::Item<'_>)
                             // can't be implemented for unsafe new
                             return;
                         }
-                        if impl_item.generics.params.iter().any(|gen| match gen.kind {
-                            hir::GenericParamKind::Type { .. } => true,
-                            _ => false,
-                        }) {
+                        if impl_item
+                            .generics
+                            .params
+                            .iter()
+                            .any(|gen| matches!(gen.kind, hir::GenericParamKind::Type { .. }))
+                        {
                             // when the result of `new()` depends on a type parameter we should not require
                             // an
                             // impl of `Default`
                             return;
                         }
                         if sig.decl.inputs.is_empty() && name == sym!(new) && cx.access_levels.is_reachable(id) {
-                            let self_did = cx.tcx.hir().local_def_id(cx.tcx.hir().get_parent_item(id));
-                            let self_ty = cx.tcx.type_of(self_did);
+                            let self_def_id = cx.tcx.hir().local_def_id(cx.tcx.hir().get_parent_item(id));
+                            let self_ty = cx.tcx.type_of(self_def_id);
                             if_chain! {
-                                if same_tys(cx, self_ty, return_ty(cx, id));
+                                if TyS::same_type(self_ty, return_ty(cx, id));
                                 if let Some(default_trait_id) = get_trait_def_id(cx, &paths::DEFAULT_TRAIT);
                                 then {
                                     if self.impling_types.is_none() {
                                         let mut impls = HirIdSet::default();
                                         cx.tcx.for_each_impl(default_trait_id, |d| {
                                             if let Some(ty_def) = cx.tcx.type_of(d).ty_adt_def() {
-                                                if let Some(hir_id) = cx.tcx.hir().as_local_hir_id(ty_def.did) {
-                                                    impls.insert(hir_id);
+                                                if let Some(local_def_id) = ty_def.did.as_local() {
+                                                    impls.insert(cx.tcx.hir().local_def_id_to_hir_id(local_def_id));
                                                 }
                                             }
                                         });
@@ -144,56 +114,35 @@ fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::Item<'_>)
                                     // generics
                                     if_chain! {
                                         if let Some(ref impling_types) = self.impling_types;
-                                        if let Some(self_def) = cx.tcx.type_of(self_did).ty_adt_def();
-                                        if self_def.did.is_local();
+                                        if let Some(self_def) = cx.tcx.type_of(self_def_id).ty_adt_def();
+                                        if let Some(self_local_did) = self_def.did.as_local();
                                         then {
-                                            let self_id = cx.tcx.hir().local_def_id_to_hir_id(self_def.did.to_local());
+                                            let self_id = cx.tcx.hir().local_def_id_to_hir_id(self_local_did);
                                             if impling_types.contains(&self_id) {
                                                 return;
                                             }
                                         }
                                     }
 
-                                    if let Some(sp) = can_derive_default(self_ty, cx, default_trait_id) {
-                                        span_lint_hir_and_then(
-                                            cx,
-                                            NEW_WITHOUT_DEFAULT,
-                                            id,
-                                            impl_item.span,
-                                            &format!(
-                                                "you should consider deriving a `Default` implementation for `{}`",
-                                                self_ty
-                                            ),
-                                            |db| {
-                                                db.suggest_item_with_attr(
-                                                    cx,
-                                                    sp,
-                                                    "try this",
-                                                    "#[derive(Default)]",
-                                                    Applicability::MaybeIncorrect,
-                                                );
-                                            });
-                                    } else {
-                                        span_lint_hir_and_then(
-                                            cx,
-                                            NEW_WITHOUT_DEFAULT,
-                                            id,
-                                            impl_item.span,
-                                            &format!(
-                                                "you should consider adding a `Default` implementation for `{}`",
-                                                self_ty
-                                            ),
-                                            |db| {
-                                                db.suggest_prepend_item(
-                                                    cx,
-                                                    item.span,
-                                                    "try this",
-                                                    &create_new_without_default_suggest_msg(self_ty),
-                                                    Applicability::MaybeIncorrect,
-                                                );
-                                            },
-                                        );
-                                    }
+                                    span_lint_hir_and_then(
+                                        cx,
+                                        NEW_WITHOUT_DEFAULT,
+                                        id,
+                                        impl_item.span,
+                                        &format!(
+                                            "you should consider adding a `Default` implementation for `{}`",
+                                            self_ty
+                                        ),
+                                        |diag| {
+                                            diag.suggest_prepend_item(
+                                                cx,
+                                                item.span,
+                                                "try this",
+                                                &create_new_without_default_suggest_msg(self_ty),
+                                                Applicability::MaybeIncorrect,
+                                            );
+                                        },
+                                    );
                                 }
                             }
                         }
@@ -213,18 +162,3 @@ fn default() -> Self {{
     }}
 }}", ty)
 }
-
-fn can_derive_default<'t, 'c>(ty: Ty<'t>, cx: &LateContext<'c, 't>, default_trait_id: DefId) -> Option<Span> {
-    match ty.kind {
-        ty::Adt(adt_def, substs) if adt_def.is_struct() => {
-            for field in adt_def.all_fields() {
-                let f_ty = field.ty(cx.tcx, substs);
-                if !implements_trait(cx, f_ty, default_trait_id, &[]) {
-                    return None;
-                }
-            }
-            Some(cx.tcx.def_span(adt_def.did))
-        },
-        _ => None,
-    }
-}