Skip to content

Commit 5521a75

Browse files
committed
Merge pull request #654 from mcarton/new
Lints about `new` methods
2 parents ed60e41 + e8c2aa2 commit 5521a75

File tree

4 files changed

+123
-59
lines changed

4 files changed

+123
-59
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 120 lints included in this crate:
11+
There are 121 lints included in this crate:
1212

1313
name | default | meaning
1414
---------------------------------------------------------------------------------------------------------------|---------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -78,6 +78,7 @@ name
7878
[needless_range_loop](https://github.com/Manishearth/rust-clippy/wiki#needless_range_loop) | warn | for-looping over a range of indices where an iterator over items would do
7979
[needless_return](https://github.com/Manishearth/rust-clippy/wiki#needless_return) | warn | using a return statement like `return expr;` where an expression would suffice
8080
[needless_update](https://github.com/Manishearth/rust-clippy/wiki#needless_update) | warn | using `{ ..base }` when there are no missing fields
81+
[new_ret_no_self](https://github.com/Manishearth/rust-clippy/wiki#new_ret_no_self) | warn | not returning `Self` in a `new` method
8182
[no_effect](https://github.com/Manishearth/rust-clippy/wiki#no_effect) | warn | statements with no effect
8283
[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
8384
[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: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,7 @@ pub fn plugin_registrar(reg: &mut Registry) {
234234
methods::CLONE_ON_COPY,
235235
methods::EXTEND_FROM_SLICE,
236236
methods::FILTER_NEXT,
237+
methods::NEW_RET_NO_SELF,
237238
methods::OK_EXPECT,
238239
methods::OPTION_MAP_UNWRAP_OR,
239240
methods::OPTION_MAP_UNWRAP_OR_ELSE,

src/methods.rs

Lines changed: 106 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
use rustc_front::hir::*;
21
use rustc::lint::*;
3-
use rustc::middle::ty;
42
use rustc::middle::subst::{Subst, TypeSpace};
5-
use std::iter;
3+
use rustc::middle::ty;
4+
use rustc_front::hir::*;
65
use std::borrow::Cow;
7-
use syntax::ptr::P;
6+
use std::{fmt, iter};
87
use syntax::codemap::Span;
8+
use syntax::ptr::P;
99

1010
use utils::{get_trait_def_id, implements_trait, in_external_macro, in_macro, match_path, match_trait_method,
1111
match_type, method_chain_args, snippet, snippet_opt, span_lint, span_lint_and_then, span_note_and_lint,
@@ -274,11 +274,27 @@ declare_lint! {
274274
/// println!("{:p} {:p}",*y, z); // prints out the same pointer
275275
/// }
276276
/// ```
277-
///
278277
declare_lint! {
279278
pub CLONE_DOUBLE_REF, Warn, "using `clone` on `&&T`"
280279
}
281280

281+
/// **What it does:** This lint warns about `new` not returning `Self`.
282+
///
283+
/// **Why is this bad?** As a convention, `new` methods are used to make a new instance of a type.
284+
///
285+
/// **Known problems:** None.
286+
///
287+
/// **Example:**
288+
/// ```rust
289+
/// impl Foo {
290+
/// fn new(..) -> NotAFoo {
291+
/// }
292+
/// }
293+
/// ```
294+
declare_lint! {
295+
pub NEW_RET_NO_SELF, Warn, "not returning `Self` in a `new` method"
296+
}
297+
282298
impl LintPass for MethodsPass {
283299
fn get_lints(&self) -> LintArray {
284300
lint_array!(EXTEND_FROM_SLICE,
@@ -295,7 +311,8 @@ impl LintPass for MethodsPass {
295311
OR_FUN_CALL,
296312
CHARS_NEXT_CMP,
297313
CLONE_ON_COPY,
298-
CLONE_DOUBLE_REF)
314+
CLONE_DOUBLE_REF,
315+
NEW_RET_NO_SELF)
299316
}
300317
}
301318

@@ -368,10 +385,11 @@ impl LateLintPass for MethodsPass {
368385
}
369386
}
370387
}
388+
371389
// check conventions w.r.t. conversion method names and predicates
372390
let is_copy = is_copy(cx, &ty, &item);
373-
for &(prefix, self_kinds) in &CONVENTIONS {
374-
if name.as_str().starts_with(prefix) &&
391+
for &(ref conv, self_kinds) in &CONVENTIONS {
392+
if conv.check(&name.as_str()) &&
375393
!self_kinds.iter().any(|k| k.matches(&sig.explicit_self.node, is_copy)) {
376394
let lint = if item.vis == Visibility::Public {
377395
WRONG_PUB_SELF_CONVENTION
@@ -381,15 +399,38 @@ impl LateLintPass for MethodsPass {
381399
span_lint(cx,
382400
lint,
383401
sig.explicit_self.span,
384-
&format!("methods called `{}*` usually take {}; consider choosing a less \
402+
&format!("methods called `{}` usually take {}; consider choosing a less \
385403
ambiguous name",
386-
prefix,
404+
conv,
387405
&self_kinds.iter()
388406
.map(|k| k.description())
389407
.collect::<Vec<_>>()
390408
.join(" or ")));
391409
}
392410
}
411+
412+
if &name.as_str() == &"new" {
413+
let returns_self = if let FunctionRetTy::Return(ref ret_ty) = sig.decl.output {
414+
let ast_ty_to_ty_cache = cx.tcx.ast_ty_to_ty_cache.borrow();
415+
let ty = ast_ty_to_ty_cache.get(&ty.id);
416+
let ret_ty = ast_ty_to_ty_cache.get(&ret_ty.id);
417+
418+
match (ty, ret_ty) {
419+
(Some(&ty), Some(&ret_ty)) => ret_ty.walk().any(|t| t == ty),
420+
_ => false,
421+
}
422+
}
423+
else {
424+
false
425+
};
426+
427+
if !returns_self {
428+
span_lint(cx,
429+
NEW_RET_NO_SELF,
430+
sig.explicit_self.span,
431+
"methods called `new` usually return `Self`");
432+
}
433+
}
393434
}
394435
}
395436
}
@@ -789,69 +830,61 @@ fn get_error_type<'a>(cx: &LateContext, ty: ty::Ty<'a>) -> Option<ty::Ty<'a>> {
789830
None
790831
}
791832

792-
/// This checks whether a given type is known to implement Debug. It's
793-
/// conservative, i.e. it should not return false positives, but will return
794-
/// false negatives.
833+
/// This checks whether a given type is known to implement Debug.
795834
fn has_debug_impl<'a, 'b>(ty: ty::Ty<'a>, cx: &LateContext<'b, 'a>) -> bool {
796-
let no_ref_ty = walk_ptrs_ty(ty);
797-
let debug = match cx.tcx.lang_items.debug_trait() {
798-
Some(debug) => debug,
799-
None => return false,
800-
};
801-
let debug_def = cx.tcx.lookup_trait_def(debug);
802-
let mut debug_impl_exists = false;
803-
debug_def.for_each_relevant_impl(cx.tcx, no_ref_ty, |d| {
804-
let self_ty = &cx.tcx.impl_trait_ref(d).and_then(|im| im.substs.self_ty());
805-
if let Some(self_ty) = *self_ty {
806-
if !self_ty.flags.get().contains(ty::TypeFlags::HAS_PARAMS) {
807-
debug_impl_exists = true;
808-
}
809-
}
810-
});
811-
debug_impl_exists
835+
match cx.tcx.lang_items.debug_trait() {
836+
Some(debug) => implements_trait(cx, ty, debug, Some(vec![])),
837+
None => false,
838+
}
839+
}
840+
841+
enum Convention {
842+
Eq(&'static str),
843+
StartsWith(&'static str),
812844
}
813845

814846
#[cfg_attr(rustfmt, rustfmt_skip)]
815-
const CONVENTIONS: [(&'static str, &'static [SelfKind]); 5] = [
816-
("into_", &[SelfKind::Value]),
817-
("to_", &[SelfKind::Ref]),
818-
("as_", &[SelfKind::Ref, SelfKind::RefMut]),
819-
("is_", &[SelfKind::Ref, SelfKind::No]),
820-
("from_", &[SelfKind::No]),
847+
const CONVENTIONS: [(Convention, &'static [SelfKind]); 6] = [
848+
(Convention::Eq("new"), &[SelfKind::No]),
849+
(Convention::StartsWith("as_"), &[SelfKind::Ref, SelfKind::RefMut]),
850+
(Convention::StartsWith("from_"), &[SelfKind::No]),
851+
(Convention::StartsWith("into_"), &[SelfKind::Value]),
852+
(Convention::StartsWith("is_"), &[SelfKind::Ref, SelfKind::No]),
853+
(Convention::StartsWith("to_"), &[SelfKind::Ref]),
821854
];
822855

823856
#[cfg_attr(rustfmt, rustfmt_skip)]
824857
const TRAIT_METHODS: [(&'static str, usize, SelfKind, OutType, &'static str); 30] = [
825858
("add", 2, SelfKind::Value, OutType::Any, "std::ops::Add"),
826-
("sub", 2, SelfKind::Value, OutType::Any, "std::ops::Sub"),
827-
("mul", 2, SelfKind::Value, OutType::Any, "std::ops::Mul"),
828-
("div", 2, SelfKind::Value, OutType::Any, "std::ops::Div"),
829-
("rem", 2, SelfKind::Value, OutType::Any, "std::ops::Rem"),
830-
("shl", 2, SelfKind::Value, OutType::Any, "std::ops::Shl"),
831-
("shr", 2, SelfKind::Value, OutType::Any, "std::ops::Shr"),
859+
("as_mut", 1, SelfKind::RefMut, OutType::Ref, "std::convert::AsMut"),
860+
("as_ref", 1, SelfKind::Ref, OutType::Ref, "std::convert::AsRef"),
832861
("bitand", 2, SelfKind::Value, OutType::Any, "std::ops::BitAnd"),
833862
("bitor", 2, SelfKind::Value, OutType::Any, "std::ops::BitOr"),
834863
("bitxor", 2, SelfKind::Value, OutType::Any, "std::ops::BitXor"),
835-
("neg", 1, SelfKind::Value, OutType::Any, "std::ops::Neg"),
836-
("not", 1, SelfKind::Value, OutType::Any, "std::ops::Not"),
837-
("drop", 1, SelfKind::RefMut, OutType::Unit, "std::ops::Drop"),
838-
("index", 2, SelfKind::Ref, OutType::Ref, "std::ops::Index"),
839-
("index_mut", 2, SelfKind::RefMut, OutType::Ref, "std::ops::IndexMut"),
840-
("deref", 1, SelfKind::Ref, OutType::Ref, "std::ops::Deref"),
841-
("deref_mut", 1, SelfKind::RefMut, OutType::Ref, "std::ops::DerefMut"),
842-
("clone", 1, SelfKind::Ref, OutType::Any, "std::clone::Clone"),
843864
("borrow", 1, SelfKind::Ref, OutType::Ref, "std::borrow::Borrow"),
844865
("borrow_mut", 1, SelfKind::RefMut, OutType::Ref, "std::borrow::BorrowMut"),
845-
("as_ref", 1, SelfKind::Ref, OutType::Ref, "std::convert::AsRef"),
846-
("as_mut", 1, SelfKind::RefMut, OutType::Ref, "std::convert::AsMut"),
847-
("eq", 2, SelfKind::Ref, OutType::Bool, "std::cmp::PartialEq"),
866+
("clone", 1, SelfKind::Ref, OutType::Any, "std::clone::Clone"),
848867
("cmp", 2, SelfKind::Ref, OutType::Any, "std::cmp::Ord"),
849868
("default", 0, SelfKind::No, OutType::Any, "std::default::Default"),
850-
("hash", 2, SelfKind::Ref, OutType::Unit, "std::hash::Hash"),
851-
("next", 1, SelfKind::RefMut, OutType::Any, "std::iter::Iterator"),
852-
("into_iter", 1, SelfKind::Value, OutType::Any, "std::iter::IntoIterator"),
869+
("deref", 1, SelfKind::Ref, OutType::Ref, "std::ops::Deref"),
870+
("deref_mut", 1, SelfKind::RefMut, OutType::Ref, "std::ops::DerefMut"),
871+
("div", 2, SelfKind::Value, OutType::Any, "std::ops::Div"),
872+
("drop", 1, SelfKind::RefMut, OutType::Unit, "std::ops::Drop"),
873+
("eq", 2, SelfKind::Ref, OutType::Bool, "std::cmp::PartialEq"),
853874
("from_iter", 1, SelfKind::No, OutType::Any, "std::iter::FromIterator"),
854875
("from_str", 1, SelfKind::No, OutType::Any, "std::str::FromStr"),
876+
("hash", 2, SelfKind::Ref, OutType::Unit, "std::hash::Hash"),
877+
("index", 2, SelfKind::Ref, OutType::Ref, "std::ops::Index"),
878+
("index_mut", 2, SelfKind::RefMut, OutType::Ref, "std::ops::IndexMut"),
879+
("into_iter", 1, SelfKind::Value, OutType::Any, "std::iter::IntoIterator"),
880+
("mul", 2, SelfKind::Value, OutType::Any, "std::ops::Mul"),
881+
("neg", 1, SelfKind::Value, OutType::Any, "std::ops::Neg"),
882+
("next", 1, SelfKind::RefMut, OutType::Any, "std::iter::Iterator"),
883+
("not", 1, SelfKind::Value, OutType::Any, "std::ops::Not"),
884+
("rem", 2, SelfKind::Value, OutType::Any, "std::ops::Rem"),
885+
("shl", 2, SelfKind::Value, OutType::Any, "std::ops::Shl"),
886+
("shr", 2, SelfKind::Value, OutType::Any, "std::ops::Shr"),
887+
("sub", 2, SelfKind::Value, OutType::Any, "std::ops::Sub"),
855888
];
856889

857890
#[derive(Clone, Copy)]
@@ -896,6 +929,24 @@ impl SelfKind {
896929
}
897930
}
898931

932+
impl Convention {
933+
fn check(&self, other: &str) -> bool {
934+
match *self {
935+
Convention::Eq(this) => this == other,
936+
Convention::StartsWith(this) => other.starts_with(this),
937+
}
938+
}
939+
}
940+
941+
impl fmt::Display for Convention {
942+
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
943+
match *self {
944+
Convention::Eq(this) => this.fmt(f),
945+
Convention::StartsWith(this) => this.fmt(f).and_then(|_| '*'.fmt(f)),
946+
}
947+
}
948+
}
949+
899950
#[derive(Clone, Copy)]
900951
enum OutType {
901952
Unit,

tests/compile-fail/methods.rs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,28 @@ impl T {
2222
fn into_u16(&self) -> u16 { 0 } //~ERROR methods called `into_*` usually take self by value
2323

2424
fn to_something(self) -> u32 { 0 } //~ERROR methods called `to_*` usually take self by reference
25+
26+
fn new(self) {}
27+
//~^ ERROR methods called `new` usually take no self
28+
//~| ERROR methods called `new` usually return `Self`
2529
}
2630

2731
#[derive(Clone,Copy)]
2832
struct U;
2933

3034
impl U {
35+
fn new() -> Self { U }
3136
fn to_something(self) -> u32 { 0 } // ok because U is Copy
3237
}
3338

39+
struct V<T> {
40+
_dummy: T
41+
}
42+
43+
impl<T> V<T> {
44+
fn new() -> Option<V<T>> { None }
45+
}
46+
3447
impl Mul<T> for T {
3548
type Output = T;
3649
fn mul(self, other: T) -> T { self } // no error, obviously
@@ -274,10 +287,8 @@ fn main() {
274287
// the error type implements `Debug`
275288
let res2: Result<i32, MyError> = Ok(0);
276289
res2.ok().expect("oh noes!");
277-
// we currently don't warn if the error type has a type parameter
278-
// (but it would be nice if we did)
279290
let res3: Result<u32, MyErrorWithParam<u8>>= Ok(0);
280-
res3.ok().expect("whoof");
291+
res3.ok().expect("whoof"); //~ERROR called `ok().expect()`
281292
let res4: Result<u32, io::Error> = Ok(0);
282293
res4.ok().expect("argh"); //~ERROR called `ok().expect()`
283294
let res5: io::Result<u32> = Ok(0);

0 commit comments

Comments
 (0)