Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

new_ret_no_self #3253

Merged
merged 8 commits into from
Oct 13, 2018
41 changes: 31 additions & 10 deletions clippy_lints/src/methods/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
use crate::rustc::hir;
use crate::rustc::hir::def::Def;
use crate::rustc::lint::{in_external_macro, LateContext, LateLintPass, Lint, LintArray, LintContext, LintPass};
use crate::rustc::ty::{self, Ty};
use crate::rustc::ty::{self, Ty, TyKind, Predicate};
use crate::rustc::{declare_tool_lint, lint_array};
use crate::rustc_errors::Applicability;
use crate::syntax::ast;
Expand Down Expand Up @@ -878,6 +878,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
let name = implitem.ident.name;
let parent = cx.tcx.hir.get_parent(implitem.id);
let item = cx.tcx.hir.expect_item(parent);
let def_id = cx.tcx.hir.local_def_id(item.id);
let ty = cx.tcx.type_of(def_id);
if_chain! {
if let hir::ImplItemKind::Method(ref sig, id) = implitem.node;
if let Some(first_arg_ty) = sig.decl.inputs.get(0);
Expand All @@ -899,8 +901,6 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
}

// check conventions w.r.t. conversion method names and predicates
let def_id = cx.tcx.hir.local_def_id(item.id);
let ty = cx.tcx.type_of(def_id);
let is_copy = is_copy(cx, ty);
for &(ref conv, self_kinds) in &CONVENTIONS {
if conv.check(&name.as_str()) {
Expand Down Expand Up @@ -928,16 +928,37 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
break;
}
}
}
}

if let hir::ImplItemKind::Method(_, _) = implitem.node {
let ret_ty = return_ty(cx, implitem.id);

// if return type is impl trait
if let TyKind::Opaque(def_id, _) = ret_ty.sty {

// then one of the associated types must be Self
for predicate in cx.tcx.predicates_of(def_id).predicates.iter() {
match predicate {
(Predicate::Projection(poly_projection_predicate), _) => {
let binder = poly_projection_predicate.ty();
let associated_type = binder.skip_binder();
let associated_type_is_self_type = same_tys(cx, ty, associated_type);

let ret_ty = return_ty(cx, implitem.id);
if name == "new" &&
!ret_ty.walk().any(|t| same_tys(cx, t, ty)) {
span_lint(cx,
NEW_RET_NO_SELF,
implitem.span,
"methods called `new` usually return `Self`");
// if the associated type is self, early return and do not trigger lint
if associated_type_is_self_type { return; }
},
(_, _) => {},
}
}
}

if name == "new" && !same_tys(cx, ret_ty, ty) {
span_lint(cx,
NEW_RET_NO_SELF,
implitem.span,
"methods called `new` usually return `Self`");
}
}
}
}
Expand Down
1 change: 1 addition & 0 deletions clippy_lints/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1920,6 +1920,7 @@ enum ImplicitHasherType<'tcx> {

impl<'tcx> ImplicitHasherType<'tcx> {
/// Checks that `ty` is a target type without a BuildHasher.
#[allow(clippy::new_ret_no_self)]
fn new<'a>(cx: &LateContext<'a, 'tcx>, hir_ty: &hir::Ty) -> Option<Self> {
if let TyKind::Path(QPath::Resolved(None, ref path)) = hir_ty.node {
let params: Vec<_> = path.segments.last().as_ref()?.args.as_ref()?
Expand Down
4 changes: 2 additions & 2 deletions tests/ui/methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
#![warn(clippy::all, clippy::pedantic, clippy::option_unwrap_used)]
#![allow(clippy::blacklisted_name, unused, clippy::print_stdout, clippy::non_ascii_literal, clippy::new_without_default,
clippy::new_without_default_derive, clippy::missing_docs_in_private_items, clippy::needless_pass_by_value,
clippy::default_trait_access, clippy::use_self, clippy::useless_format)]
clippy::default_trait_access, clippy::use_self, clippy::new_ret_no_self, clippy::useless_format)]

use std::collections::BTreeMap;
use std::collections::HashMap;
Expand Down Expand Up @@ -43,7 +43,7 @@ impl T {

fn to_something(self) -> u32 { 0 }

fn new(self) {}
fn new(self) -> Self { unimplemented!(); }
}

struct Lt<'a> {
Expand Down
12 changes: 2 additions & 10 deletions tests/ui/methods.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,9 @@ error: methods called `to_*` usually take self by reference; consider choosing a
error: methods called `new` usually take no self; consider choosing a less ambiguous name
--> $DIR/methods.rs:46:12
|
46 | fn new(self) {}
46 | fn new(self) -> Self { unimplemented!(); }
| ^^^^

error: methods called `new` usually return `Self`
--> $DIR/methods.rs:46:5
|
46 | fn new(self) {}
| ^^^^^^^^^^^^^^^
|
= note: `-D clippy::new-ret-no-self` implied by `-D warnings`

error: called `map(f).unwrap_or(a)` on an Option value. This can be done more directly by calling `map_or(a, f)` instead
--> $DIR/methods.rs:114:13
|
Expand Down Expand Up @@ -465,5 +457,5 @@ error: used unwrap() on an Option value. If you don't want to handle the None ca
|
= note: `-D clippy::option-unwrap-used` implied by `-D warnings`

error: aborting due to 58 previous errors
error: aborting due to 57 previous errors

93 changes: 93 additions & 0 deletions tests/ui/new_ret_no_self.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#![warn(clippy::new_ret_no_self)]
#![allow(dead_code, clippy::trivially_copy_pass_by_ref)]

fn main(){}

trait R {
type Item;
}

trait Q {
type Item;
type Item2;
}

struct S;

impl R for S {
type Item = Self;
}

impl S {
// should not trigger the lint
pub fn new() -> impl R<Item = Self> {
S
}
}

struct S2;

impl R for S2 {
type Item = Self;
}

impl S2 {
// should not trigger the lint
pub fn new(_: String) -> impl R<Item = Self> {
S2
}
}

struct S3;

impl R for S3 {
type Item = u32;
}

impl S3 {
// should trigger the lint
pub fn new(_: String) -> impl R<Item = u32> {
S3
}
}

struct S4;

impl Q for S4 {
type Item = u32;
type Item2 = Self;
}

impl S4 {
// should not trigger the lint
pub fn new(_: String) -> impl Q<Item = u32, Item2 = Self> {
S4
}
}

struct T;

impl T {
// should not trigger lint
pub fn new() -> Self {
unimplemented!();
}
}

struct U;

impl U {
// should trigger lint
pub fn new() -> u32 {
unimplemented!();
}
}

struct V;

impl V {
// should trigger lint
pub fn new(_: String) -> u32 {
unimplemented!();
}
}
28 changes: 28 additions & 0 deletions tests/ui/new_ret_no_self.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
error: methods called `new` usually return `Self`
--> $DIR/new_ret_no_self.rs:49:5
|
49 | / pub fn new(_: String) -> impl R<Item = u32> {
50 | | S3
51 | | }
| |_____^
|
= note: `-D clippy::new-ret-no-self` implied by `-D warnings`

error: methods called `new` usually return `Self`
--> $DIR/new_ret_no_self.rs:81:5
|
81 | / pub fn new() -> u32 {
82 | | unimplemented!();
83 | | }
| |_____^

error: methods called `new` usually return `Self`
--> $DIR/new_ret_no_self.rs:90:5
|
90 | / pub fn new(_: String) -> u32 {
91 | | unimplemented!();
92 | | }
| |_____^

error: aborting due to 3 previous errors