Skip to content

Commit 6812c0c

Browse files
authored
Merge pull request rust-lang#3253 from JoshMcguigan/new_ret_no_self-3220
new_ret_no_self
2 parents 8b12eee + 3f386d3 commit 6812c0c

File tree

6 files changed

+157
-22
lines changed

6 files changed

+157
-22
lines changed

clippy_lints/src/methods/mod.rs

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
use crate::rustc::hir;
1212
use crate::rustc::hir::def::Def;
1313
use crate::rustc::lint::{in_external_macro, LateContext, LateLintPass, Lint, LintArray, LintContext, LintPass};
14-
use crate::rustc::ty::{self, Ty};
14+
use crate::rustc::ty::{self, Ty, TyKind, Predicate};
1515
use crate::rustc::{declare_tool_lint, lint_array};
1616
use crate::rustc_errors::Applicability;
1717
use crate::syntax::ast;
@@ -878,6 +878,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
878878
let name = implitem.ident.name;
879879
let parent = cx.tcx.hir.get_parent(implitem.id);
880880
let item = cx.tcx.hir.expect_item(parent);
881+
let def_id = cx.tcx.hir.local_def_id(item.id);
882+
let ty = cx.tcx.type_of(def_id);
881883
if_chain! {
882884
if let hir::ImplItemKind::Method(ref sig, id) = implitem.node;
883885
if let Some(first_arg_ty) = sig.decl.inputs.get(0);
@@ -899,8 +901,6 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
899901
}
900902

901903
// check conventions w.r.t. conversion method names and predicates
902-
let def_id = cx.tcx.hir.local_def_id(item.id);
903-
let ty = cx.tcx.type_of(def_id);
904904
let is_copy = is_copy(cx, ty);
905905
for &(ref conv, self_kinds) in &CONVENTIONS {
906906
if conv.check(&name.as_str()) {
@@ -928,16 +928,37 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
928928
break;
929929
}
930930
}
931+
}
932+
}
933+
934+
if let hir::ImplItemKind::Method(_, _) = implitem.node {
935+
let ret_ty = return_ty(cx, implitem.id);
936+
937+
// if return type is impl trait
938+
if let TyKind::Opaque(def_id, _) = ret_ty.sty {
939+
940+
// then one of the associated types must be Self
941+
for predicate in cx.tcx.predicates_of(def_id).predicates.iter() {
942+
match predicate {
943+
(Predicate::Projection(poly_projection_predicate), _) => {
944+
let binder = poly_projection_predicate.ty();
945+
let associated_type = binder.skip_binder();
946+
let associated_type_is_self_type = same_tys(cx, ty, associated_type);
931947

932-
let ret_ty = return_ty(cx, implitem.id);
933-
if name == "new" &&
934-
!ret_ty.walk().any(|t| same_tys(cx, t, ty)) {
935-
span_lint(cx,
936-
NEW_RET_NO_SELF,
937-
implitem.span,
938-
"methods called `new` usually return `Self`");
948+
// if the associated type is self, early return and do not trigger lint
949+
if associated_type_is_self_type { return; }
950+
},
951+
(_, _) => {},
952+
}
939953
}
940954
}
955+
956+
if name == "new" && !same_tys(cx, ret_ty, ty) {
957+
span_lint(cx,
958+
NEW_RET_NO_SELF,
959+
implitem.span,
960+
"methods called `new` usually return `Self`");
961+
}
941962
}
942963
}
943964
}

clippy_lints/src/types.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1920,6 +1920,7 @@ enum ImplicitHasherType<'tcx> {
19201920

19211921
impl<'tcx> ImplicitHasherType<'tcx> {
19221922
/// Checks that `ty` is a target type without a BuildHasher.
1923+
#[allow(clippy::new_ret_no_self)]
19231924
fn new<'a>(cx: &LateContext<'a, 'tcx>, hir_ty: &hir::Ty) -> Option<Self> {
19241925
if let TyKind::Path(QPath::Resolved(None, ref path)) = hir_ty.node {
19251926
let params: Vec<_> = path.segments.last().as_ref()?.args.as_ref()?

tests/ui/methods.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
#![warn(clippy::all, clippy::pedantic, clippy::option_unwrap_used)]
1515
#![allow(clippy::blacklisted_name, unused, clippy::print_stdout, clippy::non_ascii_literal, clippy::new_without_default,
1616
clippy::new_without_default_derive, clippy::missing_docs_in_private_items, clippy::needless_pass_by_value,
17-
clippy::default_trait_access, clippy::use_self, clippy::useless_format)]
17+
clippy::default_trait_access, clippy::use_self, clippy::new_ret_no_self, clippy::useless_format)]
1818

1919
use std::collections::BTreeMap;
2020
use std::collections::HashMap;
@@ -43,7 +43,7 @@ impl T {
4343

4444
fn to_something(self) -> u32 { 0 }
4545

46-
fn new(self) {}
46+
fn new(self) -> Self { unimplemented!(); }
4747
}
4848

4949
struct Lt<'a> {

tests/ui/methods.stderr

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,9 @@ error: methods called `to_*` usually take self by reference; consider choosing a
2323
error: methods called `new` usually take no self; consider choosing a less ambiguous name
2424
--> $DIR/methods.rs:46:12
2525
|
26-
46 | fn new(self) {}
26+
46 | fn new(self) -> Self { unimplemented!(); }
2727
| ^^^^
2828

29-
error: methods called `new` usually return `Self`
30-
--> $DIR/methods.rs:46:5
31-
|
32-
46 | fn new(self) {}
33-
| ^^^^^^^^^^^^^^^
34-
|
35-
= note: `-D clippy::new-ret-no-self` implied by `-D warnings`
36-
3729
error: called `map(f).unwrap_or(a)` on an Option value. This can be done more directly by calling `map_or(a, f)` instead
3830
--> $DIR/methods.rs:114:13
3931
|
@@ -465,5 +457,5 @@ error: used unwrap() on an Option value. If you don't want to handle the None ca
465457
|
466458
= note: `-D clippy::option-unwrap-used` implied by `-D warnings`
467459

468-
error: aborting due to 58 previous errors
460+
error: aborting due to 57 previous errors
469461

tests/ui/new_ret_no_self.rs

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
#![warn(clippy::new_ret_no_self)]
2+
#![allow(dead_code, clippy::trivially_copy_pass_by_ref)]
3+
4+
fn main(){}
5+
6+
trait R {
7+
type Item;
8+
}
9+
10+
trait Q {
11+
type Item;
12+
type Item2;
13+
}
14+
15+
struct S;
16+
17+
impl R for S {
18+
type Item = Self;
19+
}
20+
21+
impl S {
22+
// should not trigger the lint
23+
pub fn new() -> impl R<Item = Self> {
24+
S
25+
}
26+
}
27+
28+
struct S2;
29+
30+
impl R for S2 {
31+
type Item = Self;
32+
}
33+
34+
impl S2 {
35+
// should not trigger the lint
36+
pub fn new(_: String) -> impl R<Item = Self> {
37+
S2
38+
}
39+
}
40+
41+
struct S3;
42+
43+
impl R for S3 {
44+
type Item = u32;
45+
}
46+
47+
impl S3 {
48+
// should trigger the lint
49+
pub fn new(_: String) -> impl R<Item = u32> {
50+
S3
51+
}
52+
}
53+
54+
struct S4;
55+
56+
impl Q for S4 {
57+
type Item = u32;
58+
type Item2 = Self;
59+
}
60+
61+
impl S4 {
62+
// should not trigger the lint
63+
pub fn new(_: String) -> impl Q<Item = u32, Item2 = Self> {
64+
S4
65+
}
66+
}
67+
68+
struct T;
69+
70+
impl T {
71+
// should not trigger lint
72+
pub fn new() -> Self {
73+
unimplemented!();
74+
}
75+
}
76+
77+
struct U;
78+
79+
impl U {
80+
// should trigger lint
81+
pub fn new() -> u32 {
82+
unimplemented!();
83+
}
84+
}
85+
86+
struct V;
87+
88+
impl V {
89+
// should trigger lint
90+
pub fn new(_: String) -> u32 {
91+
unimplemented!();
92+
}
93+
}

tests/ui/new_ret_no_self.stderr

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
error: methods called `new` usually return `Self`
2+
--> $DIR/new_ret_no_self.rs:49:5
3+
|
4+
49 | / pub fn new(_: String) -> impl R<Item = u32> {
5+
50 | | S3
6+
51 | | }
7+
| |_____^
8+
|
9+
= note: `-D clippy::new-ret-no-self` implied by `-D warnings`
10+
11+
error: methods called `new` usually return `Self`
12+
--> $DIR/new_ret_no_self.rs:81:5
13+
|
14+
81 | / pub fn new() -> u32 {
15+
82 | | unimplemented!();
16+
83 | | }
17+
| |_____^
18+
19+
error: methods called `new` usually return `Self`
20+
--> $DIR/new_ret_no_self.rs:90:5
21+
|
22+
90 | / pub fn new(_: String) -> u32 {
23+
91 | | unimplemented!();
24+
92 | | }
25+
| |_____^
26+
27+
error: aborting due to 3 previous errors
28+

0 commit comments

Comments
 (0)