Skip to content

Commit 5fabd44

Browse files
committed
Lint types with fn new() -> Self and no Default impl
1 parent be140e9 commit 5fabd44

File tree

8 files changed

+139
-28
lines changed

8 files changed

+139
-28
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ A collection of lints to catch common mistakes and improve your Rust code.
88
[Jump to usage instructions](#usage)
99

1010
##Lints
11-
There are 130 lints included in this crate:
11+
There are 131 lints included in this crate:
1212

1313
name | default | meaning
1414
---------------------------------------------------------------------------------------------------------------------|---------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -83,6 +83,7 @@ name
8383
[needless_return](https://github.com/Manishearth/rust-clippy/wiki#needless_return) | warn | using a return statement like `return expr;` where an expression would suffice
8484
[needless_update](https://github.com/Manishearth/rust-clippy/wiki#needless_update) | warn | using `{ ..base }` when there are no missing fields
8585
[new_ret_no_self](https://github.com/Manishearth/rust-clippy/wiki#new_ret_no_self) | warn | not returning `Self` in a `new` method
86+
[new_without_default](https://github.com/Manishearth/rust-clippy/wiki#new_without_default) | warn | `fn new() -> Self` method without `Default` implementation
8687
[no_effect](https://github.com/Manishearth/rust-clippy/wiki#no_effect) | warn | statements with no effect
8788
[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
8889
[nonsensical_open_options](https://github.com/Manishearth/rust-clippy/wiki#nonsensical_open_options) | warn | nonsensical combination of options for opening a file

src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ pub mod mutex_atomic;
7777
pub mod needless_bool;
7878
pub mod needless_features;
7979
pub mod needless_update;
80+
pub mod new_without_default;
8081
pub mod no_effect;
8182
pub mod open_options;
8283
pub mod panic;
@@ -175,6 +176,7 @@ pub fn plugin_registrar(reg: &mut Registry) {
175176
reg.register_late_lint_pass(box swap::Swap);
176177
reg.register_early_lint_pass(box if_not_else::IfNotElse);
177178
reg.register_late_lint_pass(box unused_label::UnusedLabel::new());
179+
reg.register_late_lint_pass(box new_without_default::NewWithoutDefault);
178180

179181
reg.register_lint_group("clippy_pedantic", vec![
180182
enum_glob_use::ENUM_GLOB_USE,
@@ -282,6 +284,7 @@ pub fn plugin_registrar(reg: &mut Registry) {
282284
needless_features::UNSTABLE_AS_MUT_SLICE,
283285
needless_features::UNSTABLE_AS_SLICE,
284286
needless_update::NEEDLESS_UPDATE,
287+
new_without_default::NEW_WITHOUT_DEFAULT,
285288
no_effect::NO_EFFECT,
286289
open_options::NONSENSICAL_OPEN_OPTIONS,
287290
panic::PANIC_PARAMS,

src/methods.rs

Lines changed: 9 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ use std::{fmt, iter};
1010
use syntax::codemap::Span;
1111
use syntax::ptr::P;
1212
use utils::{get_trait_def_id, implements_trait, in_external_macro, in_macro, match_path, match_trait_method,
13-
match_type, method_chain_args, snippet, snippet_opt, span_lint, span_lint_and_then, span_note_and_lint,
14-
walk_ptrs_ty, walk_ptrs_ty_depth};
13+
match_type, method_chain_args, returns_self, snippet, snippet_opt, span_lint,
14+
span_lint_and_then, span_note_and_lint, walk_ptrs_ty, walk_ptrs_ty_depth};
1515
use utils::{BTREEMAP_ENTRY_PATH, DEFAULT_TRAIT_PATH, HASHMAP_ENTRY_PATH, OPTION_PATH, RESULT_PATH, STRING_PATH,
1616
VEC_PATH};
1717
use utils::MethodArgs;
@@ -431,26 +431,11 @@ impl LateLintPass for MethodsPass {
431431
}
432432
}
433433

434-
if &name.as_str() == &"new" {
435-
let returns_self = if let FunctionRetTy::Return(ref ret_ty) = sig.decl.output {
436-
let ast_ty_to_ty_cache = cx.tcx.ast_ty_to_ty_cache.borrow();
437-
let ret_ty = ast_ty_to_ty_cache.get(&ret_ty.id);
438-
439-
if let Some(&ret_ty) = ret_ty {
440-
ret_ty.walk().any(|t| t == ty)
441-
} else {
442-
false
443-
}
444-
} else {
445-
false
446-
};
447-
448-
if !returns_self {
449-
span_lint(cx,
450-
NEW_RET_NO_SELF,
451-
sig.explicit_self.span,
452-
"methods called `new` usually return `Self`");
453-
}
434+
if &name.as_str() == &"new" && !returns_self(cx, &sig.decl.output, ty) {
435+
span_lint(cx,
436+
NEW_RET_NO_SELF,
437+
sig.explicit_self.span,
438+
"methods called `new` usually return `Self`");
454439
}
455440
}
456441
}
@@ -485,7 +470,7 @@ fn lint_or_fun_call(cx: &LateContext, expr: &Expr, name: &str, args: &[P<Expr>])
485470
return false;
486471
};
487472

488-
if implements_trait(cx, arg_ty, default_trait_id, None) {
473+
if implements_trait(cx, arg_ty, default_trait_id, Vec::new()) {
489474
span_lint(cx,
490475
OR_FUN_CALL,
491476
span,
@@ -869,7 +854,7 @@ fn get_error_type<'a>(cx: &LateContext, ty: ty::Ty<'a>) -> Option<ty::Ty<'a>> {
869854
/// This checks whether a given type is known to implement Debug.
870855
fn has_debug_impl<'a, 'b>(ty: ty::Ty<'a>, cx: &LateContext<'b, 'a>) -> bool {
871856
match cx.tcx.lang_items.debug_trait() {
872-
Some(debug) => implements_trait(cx, ty, debug, Some(vec![])),
857+
Some(debug) => implements_trait(cx, ty, debug, Vec::new()),
873858
None => false,
874859
}
875860
}

src/misc.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ fn check_to_owned(cx: &LateContext, expr: &Expr, other: &Expr, left: bool, op: S
253253
None => return,
254254
};
255255

256-
if !implements_trait(cx, arg_ty, partial_eq_trait_id, Some(vec![other_ty])) {
256+
if !implements_trait(cx, arg_ty, partial_eq_trait_id, vec![other_ty]) {
257257
return;
258258
}
259259

src/new_without_default.rs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
use rustc::lint::*;
2+
use rustc_front::hir;
3+
use rustc_front::intravisit::FnKind;
4+
use syntax::ast;
5+
use syntax::codemap::Span;
6+
use utils::{get_trait_def_id, implements_trait, in_external_macro, returns_self, span_lint, DEFAULT_TRAIT_PATH};
7+
8+
/// **What it does:** This lints about type with a `fn new() -> Self` method and no `Default`
9+
/// implementation.
10+
///
11+
/// **Why is this bad?** User might expect to be able to use `Default` is the type can be
12+
/// constructed without arguments.
13+
///
14+
/// **Known problems:** Hopefully none.
15+
///
16+
/// **Example:**
17+
///
18+
/// ```rust,ignore
19+
/// struct Foo;
20+
///
21+
/// impl Foo {
22+
/// fn new() -> Self {
23+
/// Foo
24+
/// }
25+
/// }
26+
/// ```
27+
declare_lint! {
28+
pub NEW_WITHOUT_DEFAULT,
29+
Warn,
30+
"`fn new() -> Self` method without `Default` implementation"
31+
}
32+
33+
#[derive(Copy,Clone)]
34+
pub struct NewWithoutDefault;
35+
36+
impl LintPass for NewWithoutDefault {
37+
fn get_lints(&self) -> LintArray {
38+
lint_array!(NEW_WITHOUT_DEFAULT)
39+
}
40+
}
41+
42+
impl LateLintPass for NewWithoutDefault {
43+
fn check_fn(&mut self, cx: &LateContext, kind: FnKind, decl: &hir::FnDecl, _: &hir::Block, span: Span, id: ast::NodeId) {
44+
if in_external_macro(cx, span) {
45+
return;
46+
}
47+
48+
if let FnKind::Method(name, _, _) = kind {
49+
if decl.inputs.is_empty() && name.as_str() == "new" {
50+
let ty = cx.tcx.lookup_item_type(cx.tcx.map.local_def_id(cx.tcx.map.get_parent(id))).ty;
51+
52+
if returns_self(cx, &decl.output, ty) {
53+
if let Some(default_trait_id) = get_trait_def_id(cx, &DEFAULT_TRAIT_PATH) {
54+
if !implements_trait(cx, ty, default_trait_id, Vec::new()) {
55+
span_lint(cx, NEW_WITHOUT_DEFAULT, span,
56+
&format!("you should consider adding a `Default` implementation for `{}`", ty));
57+
}
58+
}
59+
}
60+
}
61+
}
62+
}
63+
}

src/unused_label.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@ pub struct UnusedLabel {
3232
used_labels: HashSet<InternedString>,
3333
}
3434

35+
impl ::std::default::Default for UnusedLabel {
36+
fn default() -> Self {
37+
Self::new()
38+
}
39+
}
40+
3541
impl UnusedLabel {
3642
pub fn new() -> UnusedLabel {
3743
UnusedLabel {

src/utils/mod.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ pub fn get_trait_def_id(cx: &LateContext, path: &[&str]) -> Option<DefId> {
258258
/// Check whether a type implements a trait.
259259
/// See also `get_trait_def_id`.
260260
pub fn implements_trait<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: ty::Ty<'tcx>, trait_id: DefId,
261-
ty_params: Option<Vec<ty::Ty<'tcx>>>)
261+
ty_params: Vec<ty::Ty<'tcx>>)
262262
-> bool {
263263
cx.tcx.populate_implementations_for_trait_if_necessary(trait_id);
264264

@@ -268,7 +268,7 @@ pub fn implements_trait<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: ty::Ty<'tcx>,
268268
trait_id,
269269
0,
270270
ty,
271-
ty_params.unwrap_or_default());
271+
ty_params);
272272

273273
traits::SelectionContext::new(&infcx).evaluate_obligation_conservatively(&obligation)
274274
}
@@ -673,3 +673,19 @@ pub fn camel_case_from(s: &str) -> usize {
673673
}
674674
last_i
675675
}
676+
677+
/// Return whether a method returns `Self`.
678+
pub fn returns_self(cx: &LateContext, ret: &FunctionRetTy, ty: ty::Ty) -> bool {
679+
if let FunctionRetTy::Return(ref ret_ty) = *ret {
680+
let ast_ty_to_ty_cache = cx.tcx.ast_ty_to_ty_cache.borrow();
681+
let ret_ty = ast_ty_to_ty_cache.get(&ret_ty.id);
682+
683+
if let Some(&ret_ty) = ret_ty {
684+
ret_ty.walk().any(|t| t == ty)
685+
} else {
686+
false
687+
}
688+
} else {
689+
false
690+
}
691+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#![feature(plugin)]
2+
#![plugin(clippy)]
3+
4+
#![allow(dead_code)]
5+
#![deny(new_without_default)]
6+
7+
use std::marker::PhantomData;
8+
9+
struct Foo;
10+
11+
impl Foo {
12+
fn new() -> Foo { Foo } //~ERROR: you should consider adding a `Default` implementation for `Foo`
13+
}
14+
15+
struct Bar;
16+
17+
impl Bar {
18+
fn new() -> Self { Bar } //~ERROR: you should consider adding a `Default` implementation for `Bar`
19+
}
20+
21+
struct Ok;
22+
23+
impl Ok {
24+
fn new() -> Self { Ok }
25+
}
26+
27+
impl Default for Ok {
28+
fn default() -> Self { Ok }
29+
}
30+
31+
struct Params;
32+
33+
impl Params {
34+
fn new(_: u32) -> Self { Params }
35+
}
36+
37+
fn main() {}

0 commit comments

Comments
 (0)