Skip to content

Commit 2a3ee5f

Browse files
committed
Fix FP in new_ret_no_self: trigger in trait def instead of impl block
1 parent dead45f commit 2a3ee5f

File tree

3 files changed

+212
-6
lines changed

3 files changed

+212
-6
lines changed

clippy_lints/src/methods/mod.rs

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use rustc_ast::ast;
1515
use rustc_errors::Applicability;
1616
use rustc_hir as hir;
1717
use rustc_hir::intravisit::{self, Visitor};
18+
use rustc_hir::{FnRetTy, FnSig, TraitItem, TraitItemKind};
1819
use rustc_lint::{LateContext, LateLintPass, Lint, LintContext};
1920
use rustc_middle::hir::map::Map;
2021
use rustc_middle::lint::in_external_macro;
@@ -28,11 +29,11 @@ use crate::consts::{constant, Constant};
2829
use crate::utils::usage::mutated_variables;
2930
use crate::utils::{
3031
get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, in_macro, is_copy,
31-
is_ctor_or_promotable_const_function, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment,
32-
match_def_path, match_qpath, match_trait_method, match_type, match_var, method_calls, method_chain_args, paths,
33-
remove_blocks, return_ty, single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite,
34-
span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, sugg, walk_ptrs_ty,
35-
walk_ptrs_ty_depth, SpanlessEq,
32+
is_ctor_or_promotable_const_function, is_expn_of, is_self_ty, is_type_diagnostic_item, iter_input_pats,
33+
last_path_segment, match_def_path, match_qpath, match_trait_method, match_type, match_var, method_calls,
34+
method_chain_args, paths, remove_blocks, return_ty, single_segment_path, snippet, snippet_with_applicability,
35+
snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_sugg,
36+
span_lint_and_then, sugg, walk_ptrs_ty, walk_ptrs_ty_depth, SpanlessEq,
3637
};
3738

3839
declare_clippy_lint! {
@@ -1631,6 +1632,11 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
16311632
}
16321633
}
16331634

1635+
// if this impl block implements a trait, lint in trait definition instead
1636+
if let hir::ItemKind::Impl { of_trait: Some(_), .. } = item.kind {
1637+
return;
1638+
}
1639+
16341640
if let hir::ImplItemKind::Fn(_, _) = impl_item.kind {
16351641
let ret_ty = return_ty(cx, impl_item.hir_id);
16361642

@@ -1670,6 +1676,48 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
16701676
}
16711677
}
16721678
}
1679+
1680+
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
1681+
if_chain! {
1682+
if !in_external_macro(cx.tcx.sess, item.span);
1683+
if !item.span.from_expansion();
1684+
if item.ident.name == sym!(new);
1685+
if let TraitItemKind::Fn(FnSig { decl, .. }, _) = &item.kind;
1686+
if let FnRetTy::Return(ret_ty) = &decl.output;
1687+
1688+
then {
1689+
let mut visitor = HasSelfVisitor { has_self_ty: false };
1690+
visitor.visit_ty(ret_ty);
1691+
if !visitor.has_self_ty {
1692+
span_lint(
1693+
cx,
1694+
NEW_RET_NO_SELF,
1695+
item.span,
1696+
"methods called `new` usually return `Self`",
1697+
);
1698+
}
1699+
}
1700+
}
1701+
}
1702+
}
1703+
1704+
struct HasSelfVisitor {
1705+
pub has_self_ty: bool,
1706+
}
1707+
1708+
impl<'tcx> intravisit::Visitor<'tcx> for HasSelfVisitor {
1709+
type Map = Map<'tcx>;
1710+
1711+
fn visit_ty(&mut self, ty: &'tcx hir::Ty<'_>) {
1712+
if is_self_ty(ty) {
1713+
self.has_self_ty = true;
1714+
} else {
1715+
intravisit::walk_ty(self, ty);
1716+
}
1717+
}
1718+
fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
1719+
intravisit::NestedVisitorMap::None
1720+
}
16731721
}
16741722

16751723
/// Checks for the `OR_FUN_CALL` lint.

tests/ui/new_ret_no_self.rs

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,3 +210,133 @@ impl<'a> WithLifetime<'a> {
210210
unimplemented!();
211211
}
212212
}
213+
214+
mod issue5435 {
215+
struct V;
216+
217+
pub trait TraitRetSelf {
218+
// should not trigger lint
219+
fn new() -> Self;
220+
}
221+
222+
pub trait TraitRet {
223+
// should trigger lint as we are in trait definition
224+
fn new() -> String;
225+
}
226+
pub struct StructRet;
227+
impl TraitRet for StructRet {
228+
// should not trigger lint as we are in the impl block
229+
fn new() -> String {
230+
unimplemented!();
231+
}
232+
}
233+
234+
pub trait TraitRet2 {
235+
// should trigger lint
236+
fn new(_: String) -> String;
237+
}
238+
239+
trait TupleReturnerOk {
240+
// should not trigger lint
241+
fn new() -> (Self, u32)
242+
where
243+
Self: Sized,
244+
{
245+
unimplemented!();
246+
}
247+
}
248+
249+
trait TupleReturnerOk2 {
250+
// should not trigger lint (it doesn't matter which element in the tuple is Self)
251+
fn new() -> (u32, Self)
252+
where
253+
Self: Sized,
254+
{
255+
unimplemented!();
256+
}
257+
}
258+
259+
trait TupleReturnerOk3 {
260+
// should not trigger lint (tuple can contain multiple Self)
261+
fn new() -> (Self, Self)
262+
where
263+
Self: Sized,
264+
{
265+
unimplemented!();
266+
}
267+
}
268+
269+
trait TupleReturnerBad {
270+
// should trigger lint
271+
fn new() -> (u32, u32) {
272+
unimplemented!();
273+
}
274+
}
275+
276+
trait MutPointerReturnerOk {
277+
// should not trigger lint
278+
fn new() -> *mut Self
279+
where
280+
Self: Sized,
281+
{
282+
unimplemented!();
283+
}
284+
}
285+
286+
trait MutPointerReturnerOk2 {
287+
// should not trigger lint
288+
fn new() -> *const Self
289+
where
290+
Self: Sized,
291+
{
292+
unimplemented!();
293+
}
294+
}
295+
296+
trait MutPointerReturnerBad {
297+
// should trigger lint
298+
fn new() -> *mut V {
299+
unimplemented!();
300+
}
301+
}
302+
303+
trait GenericReturnerOk {
304+
// should not trigger lint
305+
fn new() -> Option<Self>
306+
where
307+
Self: Sized,
308+
{
309+
unimplemented!();
310+
}
311+
}
312+
313+
trait NestedReturnerOk {
314+
// should not trigger lint
315+
fn new() -> (Option<Self>, u32)
316+
where
317+
Self: Sized,
318+
{
319+
unimplemented!();
320+
}
321+
}
322+
323+
trait NestedReturnerOk2 {
324+
// should not trigger lint
325+
fn new() -> ((Self, u32), u32)
326+
where
327+
Self: Sized,
328+
{
329+
unimplemented!();
330+
}
331+
}
332+
333+
trait NestedReturnerOk3 {
334+
// should not trigger lint
335+
fn new() -> Option<(Self, u32)>
336+
where
337+
Self: Sized,
338+
{
339+
unimplemented!();
340+
}
341+
}
342+
}

tests/ui/new_ret_no_self.stderr

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,5 +48,33 @@ LL | | unimplemented!();
4848
LL | | }
4949
| |_____^
5050

51-
error: aborting due to 6 previous errors
51+
error: methods called `new` usually return `Self`
52+
--> $DIR/new_ret_no_self.rs:224:9
53+
|
54+
LL | fn new() -> String;
55+
| ^^^^^^^^^^^^^^^^^^^
56+
57+
error: methods called `new` usually return `Self`
58+
--> $DIR/new_ret_no_self.rs:236:9
59+
|
60+
LL | fn new(_: String) -> String;
61+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
62+
63+
error: methods called `new` usually return `Self`
64+
--> $DIR/new_ret_no_self.rs:271:9
65+
|
66+
LL | / fn new() -> (u32, u32) {
67+
LL | | unimplemented!();
68+
LL | | }
69+
| |_________^
70+
71+
error: methods called `new` usually return `Self`
72+
--> $DIR/new_ret_no_self.rs:298:9
73+
|
74+
LL | / fn new() -> *mut V {
75+
LL | | unimplemented!();
76+
LL | | }
77+
| |_________^
78+
79+
error: aborting due to 10 previous errors
5280

0 commit comments

Comments
 (0)