]> git.lizzy.rs Git - rust.git/commitdiff
Lint types with `fn new() -> Self` and no `Default` impl
authormcarton <cartonmartin+git@gmail.com>
Tue, 1 Mar 2016 15:25:15 +0000 (16:25 +0100)
committermcarton <cartonmartin+git@gmail.com>
Tue, 8 Mar 2016 16:00:37 +0000 (17:00 +0100)
README.md
src/lib.rs
src/methods.rs
src/misc.rs
src/new_without_default.rs [new file with mode: 0644]
src/utils/mod.rs
tests/compile-fail/methods.rs
tests/compile-fail/new_without_default.rs [new file with mode: 0755]

index 3b0a4f8820c896a44481060f0bcfc8c62d7e217e..9f3c30d39b3f35d8a04e0f238aaa258127ecd84c 100644 (file)
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@ A collection of lints to catch common mistakes and improve your Rust code.
 [Jump to usage instructions](#usage)
 
 ##Lints
-There are 132 lints included in this crate:
+There are 133 lints included in this crate:
 
 name                                                                                                                 | default | meaning
 ---------------------------------------------------------------------------------------------------------------------|---------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -83,6 +83,7 @@ name
 [needless_return](https://github.com/Manishearth/rust-clippy/wiki#needless_return)                                   | warn    | using a return statement like `return expr;` where an expression would suffice
 [needless_update](https://github.com/Manishearth/rust-clippy/wiki#needless_update)                                   | warn    | using `{ ..base }` when there are no missing fields
 [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
 [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
 [nonsensical_open_options](https://github.com/Manishearth/rust-clippy/wiki#nonsensical_open_options)                 | warn    | nonsensical combination of options for opening a file
index bb9d9b620abae781b507c30087fd477155d92829..8c619a1eaf72c546c22161619a24240c7947a043 100644 (file)
@@ -77,6 +77,7 @@ fn main() {
 pub mod needless_bool;
 pub mod needless_features;
 pub mod needless_update;
+pub mod new_without_default;
 pub mod no_effect;
 pub mod open_options;
 pub mod overflow_check_conditional;
@@ -177,6 +178,7 @@ pub fn plugin_registrar(reg: &mut Registry) {
     reg.register_early_lint_pass(box if_not_else::IfNotElse);
     reg.register_late_lint_pass(box overflow_check_conditional::OverflowCheckConditional);
     reg.register_late_lint_pass(box unused_label::UnusedLabel);
+    reg.register_late_lint_pass(box new_without_default::NewWithoutDefault);
 
     reg.register_lint_group("clippy_pedantic", vec![
         enum_glob_use::ENUM_GLOB_USE,
@@ -285,6 +287,7 @@ pub fn plugin_registrar(reg: &mut Registry) {
         needless_features::UNSTABLE_AS_MUT_SLICE,
         needless_features::UNSTABLE_AS_SLICE,
         needless_update::NEEDLESS_UPDATE,
+        new_without_default::NEW_WITHOUT_DEFAULT,
         no_effect::NO_EFFECT,
         open_options::NONSENSICAL_OPEN_OPTIONS,
         overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL,
index 94548cf9672df3c6dcef6553c5afd34b2acb90eb..663c7dd1bc4b2dd259c5f49c3ac57b9eb3482fe3 100644 (file)
@@ -10,8 +10,8 @@
 use syntax::codemap::Span;
 use syntax::ptr::P;
 use utils::{get_trait_def_id, implements_trait, in_external_macro, in_macro, match_path, match_trait_method,
-            match_type, method_chain_args, snippet, snippet_opt, span_lint, span_lint_and_then, span_note_and_lint,
-            walk_ptrs_ty, walk_ptrs_ty_depth};
+            match_type, method_chain_args, returns_self, snippet, snippet_opt, span_lint,
+            span_lint_and_then, span_note_and_lint, walk_ptrs_ty, walk_ptrs_ty_depth};
 use utils::{BTREEMAP_ENTRY_PATH, DEFAULT_TRAIT_PATH, HASHMAP_ENTRY_PATH, OPTION_PATH, RESULT_PATH, STRING_PATH,
             VEC_PATH};
 use utils::MethodArgs;
@@ -431,26 +431,11 @@ fn check_item(&mut self, cx: &LateContext, item: &Item) {
                         }
                     }
 
-                    if &name.as_str() == &"new" {
-                        let returns_self = if let FunctionRetTy::Return(ref ret_ty) = sig.decl.output {
-                            let ast_ty_to_ty_cache = cx.tcx.ast_ty_to_ty_cache.borrow();
-                            let ret_ty = ast_ty_to_ty_cache.get(&ret_ty.id);
-
-                            if let Some(&ret_ty) = ret_ty {
-                                ret_ty.walk().any(|t| t == ty)
-                            } else {
-                                false
-                            }
-                        } else {
-                            false
-                        };
-
-                        if !returns_self {
-                            span_lint(cx,
-                                      NEW_RET_NO_SELF,
-                                      sig.explicit_self.span,
-                                      "methods called `new` usually return `Self`");
-                        }
+                    if &name.as_str() == &"new" && !returns_self(cx, &sig.decl.output, ty)  {
+                        span_lint(cx,
+                                  NEW_RET_NO_SELF,
+                                  sig.explicit_self.span,
+                                  "methods called `new` usually return `Self`");
                     }
                 }
             }
@@ -485,7 +470,7 @@ fn check_unwrap_or_default(cx: &LateContext, name: &str, fun: &Expr, self_expr:
                         return false;
                     };
 
-                    if implements_trait(cx, arg_ty, default_trait_id, None) {
+                    if implements_trait(cx, arg_ty, default_trait_id, Vec::new()) {
                         span_lint(cx,
                                   OR_FUN_CALL,
                                   span,
@@ -869,7 +854,7 @@ fn get_error_type<'a>(cx: &LateContext, ty: ty::Ty<'a>) -> Option<ty::Ty<'a>> {
 /// This checks whether a given type is known to implement Debug.
 fn has_debug_impl<'a, 'b>(ty: ty::Ty<'a>, cx: &LateContext<'b, 'a>) -> bool {
     match cx.tcx.lang_items.debug_trait() {
-        Some(debug) => implements_trait(cx, ty, debug, Some(vec![])),
+        Some(debug) => implements_trait(cx, ty, debug, Vec::new()),
         None => false,
     }
 }
index fae780e4ced01396c2d4874181591f6cd7e28874..c7aeb7f9a2f606e0abb36f02f77b394f4f3ee84b 100644 (file)
@@ -253,7 +253,7 @@ fn check_to_owned(cx: &LateContext, expr: &Expr, other: &Expr, left: bool, op: S
         None => return,
     };
 
-    if !implements_trait(cx, arg_ty, partial_eq_trait_id, Some(vec![other_ty])) {
+    if !implements_trait(cx, arg_ty, partial_eq_trait_id, vec![other_ty]) {
         return;
     }
 
diff --git a/src/new_without_default.rs b/src/new_without_default.rs
new file mode 100644 (file)
index 0000000..4666336
--- /dev/null
@@ -0,0 +1,63 @@
+use rustc::lint::*;
+use rustc_front::hir;
+use rustc_front::intravisit::FnKind;
+use syntax::ast;
+use syntax::codemap::Span;
+use utils::{get_trait_def_id, implements_trait, in_external_macro, returns_self, span_lint, DEFAULT_TRAIT_PATH};
+
+/// **What it does:** This lints about type with a `fn new() -> Self` method and no `Default`
+/// implementation.
+///
+/// **Why is this bad?** User might expect to be able to use `Default` is the type can be
+/// constructed without arguments.
+///
+/// **Known problems:** Hopefully none.
+///
+/// **Example:**
+///
+/// ```rust,ignore
+/// struct Foo;
+///
+/// impl Foo {
+///     fn new() -> Self {
+///         Foo
+///     }
+/// }
+/// ```
+declare_lint! {
+    pub NEW_WITHOUT_DEFAULT,
+    Warn,
+    "`fn new() -> Self` method without `Default` implementation"
+}
+
+#[derive(Copy,Clone)]
+pub struct NewWithoutDefault;
+
+impl LintPass for NewWithoutDefault {
+    fn get_lints(&self) -> LintArray {
+        lint_array!(NEW_WITHOUT_DEFAULT)
+    }
+}
+
+impl LateLintPass for NewWithoutDefault {
+    fn check_fn(&mut self, cx: &LateContext, kind: FnKind, decl: &hir::FnDecl, _: &hir::Block, span: Span, id: ast::NodeId) {
+        if in_external_macro(cx, span) {
+            return;
+        }
+
+        if let FnKind::Method(name, _, _) = kind {
+            if decl.inputs.is_empty() && name.as_str() == "new" {
+                let ty = cx.tcx.lookup_item_type(cx.tcx.map.local_def_id(cx.tcx.map.get_parent(id))).ty;
+
+                if  returns_self(cx, &decl.output, ty) {
+                    if let Some(default_trait_id) = get_trait_def_id(cx, &DEFAULT_TRAIT_PATH) {
+                        if !implements_trait(cx, ty, default_trait_id, Vec::new()) {
+                            span_lint(cx, NEW_WITHOUT_DEFAULT, span,
+                                      &format!("you should consider adding a `Default` implementation for `{}`", ty));
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
index b001d9530eddf0034995cbd9a129f4623fdcc9e4..46feafb1de40ed9f9ac80f7fe5fee8ba9de285b9 100644 (file)
@@ -264,7 +264,7 @@ pub fn get_trait_def_id(cx: &LateContext, path: &[&str]) -> Option<DefId> {
 /// Check whether a type implements a trait.
 /// See also `get_trait_def_id`.
 pub fn implements_trait<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: ty::Ty<'tcx>, trait_id: DefId,
-                                  ty_params: Option<Vec<ty::Ty<'tcx>>>)
+                                  ty_params: Vec<ty::Ty<'tcx>>)
                                   -> bool {
     cx.tcx.populate_implementations_for_trait_if_necessary(trait_id);
 
@@ -274,7 +274,7 @@ pub fn implements_trait<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: ty::Ty<'tcx>,
                                                      trait_id,
                                                      0,
                                                      ty,
-                                                     ty_params.unwrap_or_default());
+                                                     ty_params);
 
     traits::SelectionContext::new(&infcx).evaluate_obligation_conservatively(&obligation)
 }
@@ -731,3 +731,19 @@ fn get_field<'a>(name: &str, fields: &'a [Field]) -> Option<&'a Expr> {
         None
     }
 }
+
+/// Return whether a method returns `Self`.
+pub fn returns_self(cx: &LateContext, ret: &FunctionRetTy, ty: ty::Ty) -> bool {
+    if let FunctionRetTy::Return(ref ret_ty) = *ret {
+        let ast_ty_to_ty_cache = cx.tcx.ast_ty_to_ty_cache.borrow();
+        let ret_ty = ast_ty_to_ty_cache.get(&ret_ty.id);
+
+        if let Some(&ret_ty) = ret_ty {
+            ret_ty.walk().any(|t| t == ty)
+        } else {
+            false
+        }
+    } else {
+        false
+    }
+}
index c450c953284741702a677d3d167706bbe1e27f77..0acab8be4fb84cbd2d5b6de7a478daa0bb409c99 100644 (file)
@@ -2,7 +2,7 @@
 #![plugin(clippy)]
 
 #![deny(clippy, clippy_pedantic)]
-#![allow(unused, print_stdout, non_ascii_literal)]
+#![allow(unused, print_stdout, non_ascii_literal, new_without_default)]
 
 use std::collections::BTreeMap;
 use std::collections::HashMap;
diff --git a/tests/compile-fail/new_without_default.rs b/tests/compile-fail/new_without_default.rs
new file mode 100755 (executable)
index 0000000..5f00179
--- /dev/null
@@ -0,0 +1,35 @@
+#![feature(plugin)]
+#![plugin(clippy)]
+
+#![allow(dead_code)]
+#![deny(new_without_default)]
+
+struct Foo;
+
+impl Foo {
+    fn new() -> Foo { Foo } //~ERROR: you should consider adding a `Default` implementation for `Foo`
+}
+
+struct Bar;
+
+impl Bar {
+    fn new() -> Self { Bar } //~ERROR: you should consider adding a `Default` implementation for `Bar`
+}
+
+struct Ok;
+
+impl Ok {
+    fn new() -> Self { Ok }
+}
+
+impl Default for Ok {
+    fn default() -> Self { Ok }
+}
+
+struct Params;
+
+impl Params {
+    fn new(_: u32) -> Self { Params }
+}
+
+fn main() {}