]> git.lizzy.rs Git - rust.git/commitdiff
Split `new_without_default` and `new_without_default_derive`.
authorAndre Bogus <bogusandre@gmail.com>
Tue, 17 May 2016 06:33:57 +0000 (08:33 +0200)
committerAndre Bogus <bogusandre@gmail.com>
Tue, 24 May 2016 16:22:18 +0000 (18:22 +0200)
This is still very slow, because we do a trait lookup for each field.
Perhaps storing the visited types in a set to reuse types would improve
performance somewhat. Also we may want to pre-decide some known types
(e.g. `Vec<T>`, `Option<T>`).

CHANGELOG.md
README.md
src/lib.rs
src/new_without_default.rs
tests/compile-fail/methods.rs
tests/compile-fail/new_without_default.rs

index 59b03b301c637b791b6d74be30020b92cb97afb9..9a1e7322e76fbe7a307eadd706929a4e9b888246 100644 (file)
@@ -174,6 +174,7 @@ All notable changes to this project will be documented in this file.
 [`neg_multiply`]: https://github.com/Manishearth/rust-clippy/wiki#neg_multiply
 [`new_ret_no_self`]: https://github.com/Manishearth/rust-clippy/wiki#new_ret_no_self
 [`new_without_default`]: https://github.com/Manishearth/rust-clippy/wiki#new_without_default
+[`new_without_default_derive`]: https://github.com/Manishearth/rust-clippy/wiki#new_without_default_derive
 [`no_effect`]: https://github.com/Manishearth/rust-clippy/wiki#no_effect
 [`non_ascii_literal`]: https://github.com/Manishearth/rust-clippy/wiki#non_ascii_literal
 [`nonminimal_bool`]: https://github.com/Manishearth/rust-clippy/wiki#nonminimal_bool
index 6843afb4ede6ed99bf13907c89a83a8bba3187e8..4c156dfb7a0f77d0b5e8f67617698c0242e88fd8 100644 (file)
--- a/README.md
+++ b/README.md
@@ -17,7 +17,7 @@ Table of contents:
 
 ## Lints
 
-There are 150 lints included in this crate:
+There are 151 lints included in this crate:
 
 name                                                                                                                 | default | meaning
 ---------------------------------------------------------------------------------------------------------------------|---------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -107,6 +107,7 @@ name
 [neg_multiply](https://github.com/Manishearth/rust-clippy/wiki#neg_multiply)                                         | warn    | Warns on multiplying integers with -1
 [new_ret_no_self](https://github.com/Manishearth/rust-clippy/wiki#new_ret_no_self)                                   | warn    | not returning `Self` in a `new` method
 [new_without_default](https://github.com/Manishearth/rust-clippy/wiki#new_without_default)                           | warn    | `fn new() -> Self` method without `Default` implementation
+[new_without_default_derive](https://github.com/Manishearth/rust-clippy/wiki#new_without_default_derive)             | warn    | `fn new() -> Self` without `#[derive]`able `Default` implementation
 [no_effect](https://github.com/Manishearth/rust-clippy/wiki#no_effect)                                               | warn    | statements with no effect
 [non_ascii_literal](https://github.com/Manishearth/rust-clippy/wiki#non_ascii_literal)                               | allow   | using any literal non-ASCII chars in a string literal; suggests using the \\u escape instead
 [nonminimal_bool](https://github.com/Manishearth/rust-clippy/wiki#nonminimal_bool)                                   | allow   | checks for boolean expressions that can be written more concisely
index bcf8f86b10ae566fcad898de16f6dfab02418151..41c26cf71096d020f03246a1683057af7cf66606 100644 (file)
@@ -527,6 +527,7 @@ pub fn plugin_registrar(reg: &mut Registry) {
         needless_update::NEEDLESS_UPDATE,
         neg_multiply::NEG_MULTIPLY,
         new_without_default::NEW_WITHOUT_DEFAULT,
+        new_without_default::NEW_WITHOUT_DEFAULT_DERIVE,
         no_effect::NO_EFFECT,
         no_effect::UNNECESSARY_OPERATION,
         non_expressive_names::MANY_SINGLE_CHAR_NAMES,
index 46021ec883678909c7fb56f3233c8cab7909333d..08d517014ee49a1df583c88e496971267d960985 100644 (file)
@@ -1,6 +1,8 @@
 use rustc::hir::intravisit::FnKind;
+use rustc::hir::def_id::DefId;
 use rustc::hir;
 use rustc::lint::*;
+use rustc::ty;
 use syntax::ast;
 use syntax::codemap::Span;
 use utils::paths;
 /// **Example:**
 ///
 /// ```rust,ignore
-/// struct Foo;
+/// struct Foo(Bar);
 ///
 /// impl Foo {
 ///     fn new() -> Self {
-///         Foo
+///         Foo(Bar::new())
 ///     }
 /// }
 /// ```
 /// Instead, use:
 ///
 /// ```rust
-/// struct Foo;
+/// struct Foo(Bar);
 ///
 /// impl Default for Foo {
 ///     fn default() -> Self {
-///         Foo
+///         Foo(Bar::new())
 ///     }
 /// }
 /// ```
 ///
 /// You can also have `new()` call `Default::default()`
-///
 declare_lint! {
     pub NEW_WITHOUT_DEFAULT,
     Warn,
     "`fn new() -> Self` method without `Default` implementation"
 }
 
+/// **What it does:** This lints about type with a `fn new() -> Self` method
+/// and no implementation of
+/// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html)
+///
+/// **Why is this bad?** 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.
+///
+/// **Known problems:** Hopefully none.
+///
+/// **Example:**
+///
+/// ```rust,ignore
+/// struct Foo;
+///
+/// impl Foo {
+///     fn new() -> Self {
+///         Foo
+///     }
+/// }
+/// ```
+///
+/// Just prepend `#[derive(Default)]` before the `struct` definition
+declare_lint! {
+    pub NEW_WITHOUT_DEFAULT_DERIVE,
+    Warn,
+    "`fn new() -> Self` without `#[derive]`able `Default` implementation"
+}
+
 #[derive(Copy,Clone)]
 pub struct NewWithoutDefault;
 
 impl LintPass for NewWithoutDefault {
     fn get_lints(&self) -> LintArray {
-        lint_array!(NEW_WITHOUT_DEFAULT)
+        lint_array!(NEW_WITHOUT_DEFAULT, NEW_WITHOUT_DEFAULT_DERIVE)
     }
 }
 
@@ -66,8 +97,8 @@ fn check_fn(&mut self, cx: &LateContext, kind: FnKind, decl: &hir::FnDecl, _: &h
 
         if let FnKind::Method(name, _, _, _) = kind {
             if decl.inputs.is_empty() && name.as_str() == "new" {
-                let self_ty = cx.tcx.lookup_item_type(cx.tcx.map.local_def_id(cx.tcx.map.get_parent(id))).ty;
-
+                let self_ty = cx.tcx.lookup_item_type(cx.tcx.map.local_def_id(
+                    cx.tcx.map.get_parent(id))).ty;
                 if_let_chain!{[
                     self_ty.walk_shallow().next().is_none(), // implements_trait does not work with generics
                     let Some(ret_ty) = return_ty(cx, id),
@@ -75,10 +106,43 @@ fn check_fn(&mut self, cx: &LateContext, kind: FnKind, decl: &hir::FnDecl, _: &h
                     let Some(default_trait_id) = get_trait_def_id(cx, &paths::DEFAULT_TRAIT),
                     !implements_trait(cx, self_ty, default_trait_id, Vec::new())
                 ], {
-                    span_lint(cx, NEW_WITHOUT_DEFAULT, span,
-                              &format!("you should consider adding a `Default` implementation for `{}`", self_ty));
+                    if can_derive_default(self_ty, cx, default_trait_id) {
+                        span_lint(cx,
+                                  NEW_WITHOUT_DEFAULT_DERIVE, span,
+                                  &format!("you should consider deriving a \
+                                           `Default` implementation for `{}`",
+                                           self_ty)).
+                                  span_suggestion(span,
+                                                  "try this",
+                                                  "#[derive(Default)]".into());
+                    } else {
+                        span_lint(cx,
+                                  NEW_WITHOUT_DEFAULT, span,
+                                  &format!("you should consider adding a \
+                                           `Default` implementation for `{}`",
+                                           self_ty)).
+                                  span_suggestion(span,
+                                                  "try this",
+                             format!("impl Default for {} {{ fn default() -> \
+                                    Self {{ {}::new() }} }}", self_ty, self_ty));
+                    }
                 }}
             }
         }
     }
 }
+
+fn can_derive_default<'t, 'c>(ty: ty::Ty<'t>, cx: &LateContext<'c, 't>, default_trait_id: DefId) -> bool {
+    match ty.sty {
+        ty::TyStruct(ref adt_def, ref substs) => {
+            for field in adt_def.all_fields() {
+                let f_ty = field.ty(cx.tcx, substs);
+                if !implements_trait(cx, f_ty, default_trait_id, Vec::new()) {
+                    return false
+                }
+            }
+            true
+        },
+        _ => false
+    }
+}
index f6e4a9a31e054c4c369577b503e7f88158ebf063..78ffbb1a58a46efbd57b733e801fae1353616814 100644 (file)
@@ -3,7 +3,7 @@
 #![plugin(clippy)]
 
 #![deny(clippy, clippy_pedantic)]
-#![allow(blacklisted_name, unused, print_stdout, non_ascii_literal, new_without_default)]
+#![allow(blacklisted_name, unused, print_stdout, non_ascii_literal, new_without_default, new_without_default_derive)]
 
 use std::collections::BTreeMap;
 use std::collections::HashMap;
index 30015f6c9e8bd83d72cab54ae1945e8065e534e4..17f2a8d7b415034f9ea2ce632952474b4d5ab5cd 100644 (file)
@@ -2,18 +2,18 @@
 #![plugin(clippy)]
 
 #![allow(dead_code)]
-#![deny(new_without_default)]
+#![deny(new_without_default, new_without_default_derive)]
 
 struct Foo;
 
 impl Foo {
-    fn new() -> Foo { Foo } //~ERROR: you should consider adding a `Default` implementation for `Foo`
+    fn new() -> Foo { Foo } //~ERROR: you should consider deriving a `Default` implementation for `Foo`
 }
 
 struct Bar;
 
 impl Bar {
-    fn new() -> Self { Bar } //~ERROR: you should consider adding a `Default` implementation for `Bar`
+    fn new() -> Self { Bar } //~ERROR: you should consider deriving a `Default` implementation for `Bar`
 }
 
 struct Ok;