Skip to content

Commit 601ddcc

Browse files
fix(use_self): don't early-return if the outer type has no lifetimes (#15611)
Fixes #13277 Unfortunately breaks another case, which has only been working thanks to the now fixed bug -- see last commit. changelog: [`use_self`]: don't early-return if the outer type has no lifetimes
2 parents 8ca5332 + e7c91ab commit 601ddcc

File tree

8 files changed

+92
-87
lines changed

8 files changed

+92
-87
lines changed

clippy_lints/src/matches/needless_match.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use super::NEEDLESS_MATCH;
22
use clippy_utils::diagnostics::span_lint_and_sugg;
33
use clippy_utils::source::snippet_with_applicability;
4-
use clippy_utils::ty::{is_type_diagnostic_item, same_type_and_consts};
4+
use clippy_utils::ty::{is_type_diagnostic_item, same_type_modulo_regions};
55
use clippy_utils::{
66
SpanlessEq, eq_expr_value, get_parent_expr_for_hir, higher, is_else_clause, is_res_lang_ctor, over, path_res,
77
peel_blocks_with_stmt,
@@ -122,7 +122,7 @@ fn expr_ty_matches_p_ty(cx: &LateContext<'_>, expr: &Expr<'_>, p_expr: &Expr<'_>
122122
// Compare match_expr ty with local in `let local = match match_expr {..}`
123123
Node::LetStmt(local) => {
124124
let results = cx.typeck_results();
125-
return same_type_and_consts(results.node_type(local.hir_id), results.expr_ty(expr));
125+
return same_type_modulo_regions(results.node_type(local.hir_id), results.expr_ty(expr));
126126
},
127127
// compare match_expr ty with RetTy in `fn foo() -> RetTy`
128128
Node::Item(item) => {
@@ -133,7 +133,7 @@ fn expr_ty_matches_p_ty(cx: &LateContext<'_>, expr: &Expr<'_>, p_expr: &Expr<'_>
133133
.instantiate_identity()
134134
.output()
135135
.skip_binder();
136-
return same_type_and_consts(output, cx.typeck_results().expr_ty(expr));
136+
return same_type_modulo_regions(output, cx.typeck_results().expr_ty(expr));
137137
}
138138
},
139139
// check the parent expr for this whole block `{ match match_expr {..} }`

clippy_lints/src/operators/erasing_op.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use clippy_utils::consts::{ConstEvalCtxt, Constant};
22
use clippy_utils::diagnostics::span_lint;
3-
use clippy_utils::ty::same_type_and_consts;
3+
use clippy_utils::ty::same_type_modulo_regions;
44

55
use rustc_hir::{BinOpKind, Expr};
66
use rustc_lint::LateContext;
@@ -29,7 +29,7 @@ pub(super) fn check<'tcx>(
2929
fn different_types(tck: &TypeckResults<'_>, input: &Expr<'_>, output: &Expr<'_>) -> bool {
3030
let input_ty = tck.expr_ty(input).peel_refs();
3131
let output_ty = tck.expr_ty(output).peel_refs();
32-
!same_type_and_consts(input_ty, output_ty)
32+
!same_type_modulo_regions(input_ty, output_ty)
3333
}
3434

3535
fn check_op<'tcx>(

clippy_lints/src/use_self.rs

Lines changed: 29 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,21 @@ use clippy_config::Conf;
22
use clippy_utils::diagnostics::span_lint_and_sugg;
33
use clippy_utils::is_from_proc_macro;
44
use clippy_utils::msrvs::{self, Msrv};
5-
use clippy_utils::ty::{same_type_and_consts, ty_from_hir_ty};
5+
use clippy_utils::ty::{same_type_modulo_regions, ty_from_hir_ty};
66
use rustc_data_structures::fx::FxHashSet;
77
use rustc_errors::Applicability;
88
use rustc_hir::def::{CtorOf, DefKind, Res};
99
use rustc_hir::def_id::LocalDefId;
1010
use rustc_hir::intravisit::{InferKind, Visitor, VisitorExt, walk_ty};
1111
use rustc_hir::{
12-
self as hir, AmbigArg, Expr, ExprKind, FnRetTy, FnSig, GenericArgsParentheses, GenericParam, GenericParamKind,
13-
HirId, Impl, ImplItemKind, Item, ItemKind, Pat, PatExpr, PatExprKind, PatKind, Path, QPath, Ty, TyKind,
12+
self as hir, AmbigArg, Expr, ExprKind, FnRetTy, FnSig, GenericArgsParentheses, GenericParamKind, HirId, Impl,
13+
ImplItemKind, Item, ItemKind, Pat, PatExpr, PatExprKind, PatKind, Path, QPath, Ty, TyKind,
1414
};
1515
use rustc_lint::{LateContext, LateLintPass};
1616
use rustc_middle::ty::Ty as MiddleTy;
1717
use rustc_session::impl_lint_pass;
1818
use rustc_span::Span;
19+
use std::iter;
1920

2021
declare_clippy_lint! {
2122
/// ### What it does
@@ -101,17 +102,11 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
101102
let types_to_skip = generics
102103
.params
103104
.iter()
104-
.filter_map(|param| match param {
105-
GenericParam {
106-
kind:
107-
GenericParamKind::Const {
108-
ty: Ty { hir_id, .. }, ..
109-
},
110-
..
111-
} => Some(*hir_id),
105+
.filter_map(|param| match param.kind {
106+
GenericParamKind::Const { ty, .. } => Some(ty.hir_id),
112107
_ => None,
113108
})
114-
.chain(std::iter::once(self_ty.hir_id))
109+
.chain([self_ty.hir_id])
115110
.collect();
116111
StackItem::Check {
117112
impl_id: item.owner_id.def_id,
@@ -210,11 +205,11 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
210205
&& !types_to_skip.contains(&hir_ty.hir_id)
211206
&& let ty = ty_from_hir_ty(cx, hir_ty.as_unambig_ty())
212207
&& let impl_ty = cx.tcx.type_of(impl_id).instantiate_identity()
213-
&& same_type_and_consts(ty, impl_ty)
208+
&& same_type_modulo_regions(ty, impl_ty)
214209
// Ensure the type we encounter and the one from the impl have the same lifetime parameters. It may be that
215-
// the lifetime parameters of `ty` are elided (`impl<'a> Foo<'a> { fn new() -> Self { Foo{..} } }`, in
210+
// the lifetime parameters of `ty` are elided (`impl<'a> Foo<'a> { fn new() -> Self { Foo{..} } }`), in
216211
// which case we must still trigger the lint.
217-
&& (has_no_lifetime(ty) || same_lifetimes(ty, impl_ty))
212+
&& same_lifetimes(ty, impl_ty)
218213
&& self.msrv.meets(cx, msrvs::TYPE_ALIAS_ENUM_VARIANTS)
219214
{
220215
span_lint(cx, hir_ty.span);
@@ -227,18 +222,16 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
227222
&& cx.typeck_results().expr_ty(expr) == cx.tcx.type_of(impl_id).instantiate_identity()
228223
&& self.msrv.meets(cx, msrvs::TYPE_ALIAS_ENUM_VARIANTS)
229224
{
230-
} else {
231-
return;
232-
}
233-
match expr.kind {
234-
ExprKind::Struct(QPath::Resolved(_, path), ..) => check_path(cx, path),
235-
ExprKind::Call(fun, _) => {
236-
if let ExprKind::Path(QPath::Resolved(_, path)) = fun.kind {
237-
check_path(cx, path);
238-
}
239-
},
240-
ExprKind::Path(QPath::Resolved(_, path)) => check_path(cx, path),
241-
_ => (),
225+
match expr.kind {
226+
ExprKind::Struct(QPath::Resolved(_, path), ..) => check_path(cx, path),
227+
ExprKind::Call(fun, _) => {
228+
if let ExprKind::Path(QPath::Resolved(_, path)) = fun.kind {
229+
check_path(cx, path);
230+
}
231+
},
232+
ExprKind::Path(QPath::Resolved(_, path)) => check_path(cx, path),
233+
_ => (),
234+
}
242235
}
243236
}
244237

@@ -308,36 +301,20 @@ fn lint_path_to_variant(cx: &LateContext<'_>, path: &Path<'_>) {
308301
}
309302
}
310303

311-
/// Returns `true` if types `a` and `b` have the same lifetime parameters, otherwise returns
312-
/// `false`.
304+
/// Checks whether types `a` and `b` have the same lifetime parameters.
313305
///
314306
/// This function does not check that types `a` and `b` are the same types.
315307
fn same_lifetimes<'tcx>(a: MiddleTy<'tcx>, b: MiddleTy<'tcx>) -> bool {
316308
use rustc_middle::ty::{Adt, GenericArgKind};
317-
match (&a.kind(), &b.kind()) {
318-
(&Adt(_, args_a), &Adt(_, args_b)) => {
319-
args_a
320-
.iter()
321-
.zip(args_b.iter())
322-
.all(|(arg_a, arg_b)| match (arg_a.kind(), arg_b.kind()) {
323-
// TODO: Handle inferred lifetimes
324-
(GenericArgKind::Lifetime(inner_a), GenericArgKind::Lifetime(inner_b)) => inner_a == inner_b,
325-
(GenericArgKind::Type(type_a), GenericArgKind::Type(type_b)) => same_lifetimes(type_a, type_b),
326-
_ => true,
327-
})
309+
match (a.kind(), b.kind()) {
310+
(Adt(_, args_a), Adt(_, args_b)) => {
311+
iter::zip(*args_a, *args_b).all(|(arg_a, arg_b)| match (arg_a.kind(), arg_b.kind()) {
312+
// TODO: Handle inferred lifetimes
313+
(GenericArgKind::Lifetime(inner_a), GenericArgKind::Lifetime(inner_b)) => inner_a == inner_b,
314+
(GenericArgKind::Type(type_a), GenericArgKind::Type(type_b)) => same_lifetimes(type_a, type_b),
315+
_ => true,
316+
})
328317
},
329318
_ => a == b,
330319
}
331320
}
332-
333-
/// Returns `true` if `ty` has no lifetime parameter, otherwise returns `false`.
334-
fn has_no_lifetime(ty: MiddleTy<'_>) -> bool {
335-
use rustc_middle::ty::{Adt, GenericArgKind};
336-
match ty.kind() {
337-
&Adt(_, args) => !args
338-
.iter()
339-
// TODO: Handle inferred lifetimes
340-
.any(|arg| matches!(arg.kind(), GenericArgKind::Lifetime(..))),
341-
_ => true,
342-
}
343-
}

clippy_lints/src/useless_conversion.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
22
use clippy_utils::source::{snippet, snippet_with_context};
33
use clippy_utils::sugg::{DiagExt as _, Sugg};
4-
use clippy_utils::ty::{get_type_diagnostic_name, is_copy, is_type_diagnostic_item, same_type_and_consts};
4+
use clippy_utils::ty::{get_type_diagnostic_name, is_copy, is_type_diagnostic_item, same_type_modulo_regions};
55
use clippy_utils::{
66
get_parent_expr, is_inherent_method_call, is_trait_item, is_trait_method, is_ty_alias, path_to_local, sym,
77
};
@@ -184,7 +184,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
184184
&& (is_trait_item(cx, arg, sym::Into) || is_trait_item(cx, arg, sym::From))
185185
&& let ty::FnDef(_, args) = cx.typeck_results().expr_ty(arg).kind()
186186
&& let &[from_ty, to_ty] = args.into_type_list(cx.tcx).as_slice()
187-
&& same_type_and_consts(from_ty, to_ty)
187+
&& same_type_modulo_regions(from_ty, to_ty)
188188
{
189189
span_lint_and_then(
190190
cx,
@@ -207,7 +207,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
207207
if is_trait_method(cx, e, sym::Into) && name.ident.name == sym::into {
208208
let a = cx.typeck_results().expr_ty(e);
209209
let b = cx.typeck_results().expr_ty(recv);
210-
if same_type_and_consts(a, b) {
210+
if same_type_modulo_regions(a, b) {
211211
let mut app = Applicability::MachineApplicable;
212212
let sugg = snippet_with_context(cx, recv.span, e.span.ctxt(), "<expr>", &mut app).0;
213213
span_lint_and_sugg(
@@ -324,7 +324,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
324324
// If the types are identical then .into_iter() can be removed, unless the type
325325
// implements Copy, in which case .into_iter() returns a copy of the receiver and
326326
// cannot be safely omitted.
327-
if same_type_and_consts(a, b) && !is_copy(cx, b) {
327+
if same_type_modulo_regions(a, b) && !is_copy(cx, b) {
328328
// Below we check if the parent method call meets the following conditions:
329329
// 1. First parameter is `&mut self` (requires mutable reference)
330330
// 2. Second parameter implements the `FnMut` trait (e.g., Iterator::any)
@@ -371,7 +371,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
371371
&& is_type_diagnostic_item(cx, a, sym::Result)
372372
&& let ty::Adt(_, args) = a.kind()
373373
&& let Some(a_type) = args.types().next()
374-
&& same_type_and_consts(a_type, b)
374+
&& same_type_modulo_regions(a_type, b)
375375
{
376376
span_lint_and_help(
377377
cx,
@@ -396,7 +396,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
396396
&& is_type_diagnostic_item(cx, a, sym::Result)
397397
&& let ty::Adt(_, args) = a.kind()
398398
&& let Some(a_type) = args.types().next()
399-
&& same_type_and_consts(a_type, b)
399+
&& same_type_modulo_regions(a_type, b)
400400
{
401401
let hint = format!("consider removing `{}()`", snippet(cx, path.span, "TryFrom::try_from"));
402402
span_lint_and_help(
@@ -407,7 +407,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
407407
None,
408408
hint,
409409
);
410-
} else if name == sym::from_fn && same_type_and_consts(a, b) {
410+
} else if name == sym::from_fn && same_type_modulo_regions(a, b) {
411411
let mut app = Applicability::MachineApplicable;
412412
let sugg = Sugg::hir_with_context(cx, arg, e.span.ctxt(), "<expr>", &mut app).maybe_paren();
413413
let sugg_msg = format!("consider removing `{}()`", snippet(cx, path.span, "From::from"));

clippy_utils/src/ty/mod.rs

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -513,25 +513,31 @@ pub fn walk_ptrs_ty_depth(ty: Ty<'_>) -> (Ty<'_>, usize) {
513513
inner(ty, 0)
514514
}
515515

516-
/// Returns `true` if types `a` and `b` are same types having same `Const` generic args,
517-
/// otherwise returns `false`
518-
pub fn same_type_and_consts<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
516+
/// Checks whether `a` and `b` are same types having same `Const` generic args, but ignores
517+
/// lifetimes.
518+
///
519+
/// For example, the function would return `true` for
520+
/// - `u32` and `u32`
521+
/// - `[u8; N]` and `[u8; M]`, if `N=M`
522+
/// - `Option<T>` and `Option<U>`, if `same_type_modulo_regions(T, U)` holds
523+
/// - `&'a str` and `&'b str`
524+
///
525+
/// and `false` for:
526+
/// - `Result<u32, String>` and `Result<usize, String>`
527+
pub fn same_type_modulo_regions<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
519528
match (&a.kind(), &b.kind()) {
520529
(&ty::Adt(did_a, args_a), &ty::Adt(did_b, args_b)) => {
521530
if did_a != did_b {
522531
return false;
523532
}
524533

525-
args_a
526-
.iter()
527-
.zip(args_b.iter())
528-
.all(|(arg_a, arg_b)| match (arg_a.kind(), arg_b.kind()) {
529-
(GenericArgKind::Const(inner_a), GenericArgKind::Const(inner_b)) => inner_a == inner_b,
530-
(GenericArgKind::Type(type_a), GenericArgKind::Type(type_b)) => {
531-
same_type_and_consts(type_a, type_b)
532-
},
533-
_ => true,
534-
})
534+
iter::zip(*args_a, *args_b).all(|(arg_a, arg_b)| match (arg_a.kind(), arg_b.kind()) {
535+
(GenericArgKind::Const(inner_a), GenericArgKind::Const(inner_b)) => inner_a == inner_b,
536+
(GenericArgKind::Type(type_a), GenericArgKind::Type(type_b)) => {
537+
same_type_modulo_regions(type_a, type_b)
538+
},
539+
_ => true,
540+
})
535541
},
536542
_ => a == b,
537543
}

tests/ui/use_self.fixed

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -530,8 +530,8 @@ mod issue7206 {
530530

531531
impl<'a> S2<S<'a>> {
532532
fn new_again() -> Self {
533-
Self::new()
534-
//~^ use_self
533+
S2::new()
534+
// FIXME: ^Broken by PR #15611
535535
}
536536
}
537537
}
@@ -755,3 +755,17 @@ mod crash_check_13128 {
755755
}
756756
}
757757
}
758+
759+
mod issue_13277 {
760+
trait Foo {
761+
type Item<'foo>;
762+
}
763+
struct Bar<'b> {
764+
content: &'b str,
765+
}
766+
impl<'b> Foo for Option<Bar<'b>> {
767+
// when checking whether `Option<Bar<'foo>>` has a lifetime, check not only the outer
768+
// `Option<T>`, but also the inner `Bar<'foo>`
769+
type Item<'foo> = Option<Bar<'foo>>;
770+
}
771+
}

tests/ui/use_self.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -531,7 +531,7 @@ mod issue7206 {
531531
impl<'a> S2<S<'a>> {
532532
fn new_again() -> Self {
533533
S2::new()
534-
//~^ use_self
534+
// FIXME: ^Broken by PR #15611
535535
}
536536
}
537537
}
@@ -755,3 +755,17 @@ mod crash_check_13128 {
755755
}
756756
}
757757
}
758+
759+
mod issue_13277 {
760+
trait Foo {
761+
type Item<'foo>;
762+
}
763+
struct Bar<'b> {
764+
content: &'b str,
765+
}
766+
impl<'b> Foo for Option<Bar<'b>> {
767+
// when checking whether `Option<Bar<'foo>>` has a lifetime, check not only the outer
768+
// `Option<T>`, but also the inner `Bar<'foo>`
769+
type Item<'foo> = Option<Bar<'foo>>;
770+
}
771+
}

tests/ui/use_self.stderr

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -169,12 +169,6 @@ error: unnecessary structure name repetition
169169
LL | A::new::<submod::B>(submod::B {})
170170
| ^ help: use the applicable keyword: `Self`
171171

172-
error: unnecessary structure name repetition
173-
--> tests/ui/use_self.rs:533:13
174-
|
175-
LL | S2::new()
176-
| ^^ help: use the applicable keyword: `Self`
177-
178172
error: unnecessary structure name repetition
179173
--> tests/ui/use_self.rs:571:17
180174
|
@@ -259,5 +253,5 @@ error: unnecessary structure name repetition
259253
LL | E::A => {},
260254
| ^ help: use the applicable keyword: `Self`
261255

262-
error: aborting due to 43 previous errors
256+
error: aborting due to 42 previous errors
263257

0 commit comments

Comments
 (0)