Skip to content

Commit 2e3a7e3

Browse files
author
Tim Nielens
committed
rework use_self impl based on ty::Ty comparison rust-lang#3410
1 parent 02c9435 commit 2e3a7e3

File tree

6 files changed

+210
-194
lines changed

6 files changed

+210
-194
lines changed

clippy_lints/src/use_self.rs

+101-141
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
11
use if_chain::if_chain;
22
use rustc_errors::Applicability;
33
use rustc_hir as hir;
4-
use rustc_hir::def::{DefKind, Res};
5-
use rustc_hir::intravisit::{walk_item, walk_path, walk_ty, NestedVisitorMap, Visitor};
4+
use rustc_hir::intravisit::{walk_expr, walk_impl_item, walk_ty, NestedVisitorMap, Visitor};
65
use rustc_hir::{
7-
def, FnDecl, FnRetTy, FnSig, GenericArg, HirId, ImplItem, ImplItemKind, Item, ItemKind, Path, PathSegment, QPath,
8-
TyKind,
6+
def, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, ImplItem, ImplItemKind, Item, ItemKind, Path, PathSegment,
7+
QPath, TyKind,
98
};
109
use rustc_lint::{LateContext, LateLintPass, LintContext};
1110
use rustc_middle::hir::map::Map;
1211
use rustc_middle::lint::in_external_macro;
1312
use rustc_middle::ty;
14-
use rustc_middle::ty::{DefIdTree, Ty};
13+
use rustc_middle::ty::Ty;
1514
use rustc_session::{declare_lint_pass, declare_tool_lint};
16-
use rustc_span::symbol::kw;
15+
use rustc_span::Span;
1716
use rustc_typeck::hir_ty_to_ty;
1817

1918
use crate::utils::{differing_macro_contexts, span_lint_and_sugg};
@@ -81,15 +80,87 @@ fn span_use_self_lint(cx: &LateContext<'_, '_>, path: &Path<'_>, last_segment: O
8180
);
8281
}
8382

84-
// FIXME: always use this (more correct) visitor, not just in method signatures.
85-
struct SemanticUseSelfVisitor<'a, 'tcx> {
83+
struct ImplVisitor<'a, 'tcx> {
8684
cx: &'a LateContext<'a, 'tcx>,
85+
item: &'tcx Item<'tcx>,
8786
self_ty: Ty<'tcx>,
8887
}
8988

90-
impl<'a, 'tcx> Visitor<'tcx> for SemanticUseSelfVisitor<'a, 'tcx> {
89+
impl<'a, 'tcx> ImplVisitor<'a, 'tcx> {
90+
fn check_trait_method_impl_decl(
91+
&mut self,
92+
impl_item: &ImplItem<'tcx>,
93+
impl_decl: &'tcx FnDecl<'tcx>,
94+
impl_trait_ref: ty::TraitRef<'tcx>,
95+
) {
96+
let tcx = self.cx.tcx;
97+
let trait_method = tcx
98+
.associated_items(impl_trait_ref.def_id)
99+
.find_by_name_and_kind(tcx, impl_item.ident, ty::AssocKind::Fn, impl_trait_ref.def_id)
100+
.expect("impl method matches a trait method");
101+
102+
let trait_method_sig = tcx.fn_sig(trait_method.def_id);
103+
let trait_method_sig = tcx.erase_late_bound_regions(&trait_method_sig);
104+
105+
let output_hir_ty = if let FnRetTy::Return(ty) = &impl_decl.output {
106+
Some(&**ty)
107+
} else {
108+
None
109+
};
110+
111+
// `impl_hir_ty` (of type `hir::Ty`) represents the type written in the signature.
112+
// `trait_ty` (of type `ty::Ty`) is the semantic type for the signature in the trait.
113+
// We use `impl_hir_ty` to see if the type was written as `Self`,
114+
// `hir_ty_to_ty(...)` to check semantic types of paths, and
115+
// `trait_ty` to determine which parts of the signature in the trait, mention
116+
// the type being implemented verbatim (as opposed to `Self`).
117+
for (impl_hir_ty, trait_ty) in impl_decl
118+
.inputs
119+
.iter()
120+
.chain(output_hir_ty)
121+
.zip(trait_method_sig.inputs_and_output)
122+
{
123+
// Check if the input/output type in the trait method specifies the implemented
124+
// type verbatim, and only suggest `Self` if that isn't the case.
125+
// This avoids suggestions to e.g. replace `Vec<u8>` with `Vec<Self>`,
126+
// in an `impl Trait for u8`, when the trait always uses `Vec<u8>`.
127+
// See also https://github.com/rust-lang/rust-clippy/issues/2894.
128+
let self_ty = impl_trait_ref.self_ty();
129+
if !trait_ty.walk().any(|inner| inner == self_ty.into()) {
130+
self.visit_ty(&impl_hir_ty);
131+
}
132+
}
133+
}
134+
}
135+
136+
impl<'a, 'tcx> Visitor<'tcx> for ImplVisitor<'a, 'tcx> {
91137
type Map = Map<'tcx>;
92138

139+
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
140+
if let ExprKind::Struct(QPath::Resolved(_, path), ..) = expr.kind {
141+
if self
142+
.cx
143+
.tcx
144+
.typeck_tables_of(expr.hir_id.owner.to_def_id())
145+
.node_type(expr.hir_id)
146+
== self.self_ty
147+
{
148+
match path.res {
149+
def::Res::SelfTy(..) => {},
150+
_ => {
151+
span_use_self_lint(self.cx, path, None);
152+
},
153+
}
154+
span_use_self_lint(self.cx, path, None);
155+
}
156+
}
157+
walk_expr(self, expr)
158+
}
159+
160+
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
161+
NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
162+
}
163+
93164
fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'_>) {
94165
if let TyKind::Path(QPath::Resolved(_, path)) = &hir_ty.kind {
95166
match path.res {
@@ -105,54 +176,20 @@ impl<'a, 'tcx> Visitor<'tcx> for SemanticUseSelfVisitor<'a, 'tcx> {
105176
walk_ty(self, hir_ty)
106177
}
107178

108-
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
109-
NestedVisitorMap::None
110-
}
111-
}
112-
113-
fn check_trait_method_impl_decl<'a, 'tcx>(
114-
cx: &'a LateContext<'a, 'tcx>,
115-
impl_item: &ImplItem<'_>,
116-
impl_decl: &'tcx FnDecl<'_>,
117-
impl_trait_ref: ty::TraitRef<'tcx>,
118-
) {
119-
let trait_method = cx
120-
.tcx
121-
.associated_items(impl_trait_ref.def_id)
122-
.find_by_name_and_kind(cx.tcx, impl_item.ident, ty::AssocKind::Fn, impl_trait_ref.def_id)
123-
.expect("impl method matches a trait method");
124-
125-
let trait_method_sig = cx.tcx.fn_sig(trait_method.def_id);
126-
let trait_method_sig = cx.tcx.erase_late_bound_regions(&trait_method_sig);
127-
128-
let output_hir_ty = if let FnRetTy::Return(ty) = &impl_decl.output {
129-
Some(&**ty)
130-
} else {
131-
None
132-
};
133-
134-
// `impl_hir_ty` (of type `hir::Ty`) represents the type written in the signature.
135-
// `trait_ty` (of type `ty::Ty`) is the semantic type for the signature in the trait.
136-
// We use `impl_hir_ty` to see if the type was written as `Self`,
137-
// `hir_ty_to_ty(...)` to check semantic types of paths, and
138-
// `trait_ty` to determine which parts of the signature in the trait, mention
139-
// the type being implemented verbatim (as opposed to `Self`).
140-
for (impl_hir_ty, trait_ty) in impl_decl
141-
.inputs
142-
.iter()
143-
.chain(output_hir_ty)
144-
.zip(trait_method_sig.inputs_and_output)
145-
{
146-
// Check if the input/output type in the trait method specifies the implemented
147-
// type verbatim, and only suggest `Self` if that isn't the case.
148-
// This avoids suggestions to e.g. replace `Vec<u8>` with `Vec<Self>`,
149-
// in an `impl Trait for u8`, when the trait always uses `Vec<u8>`.
150-
// See also https://github.com/rust-lang/rust-clippy/issues/2894.
151-
let self_ty = impl_trait_ref.self_ty();
152-
if !trait_ty.walk().any(|inner| inner == self_ty.into()) {
153-
let mut visitor = SemanticUseSelfVisitor { cx, self_ty };
154-
155-
visitor.visit_ty(&impl_hir_ty);
179+
fn visit_impl_item(&mut self, ii: &'tcx ImplItem<'tcx>) {
180+
let tcx = self.cx.tcx;
181+
let impl_def_id = tcx.hir().local_def_id(self.item.hir_id);
182+
let impl_trait_ref = tcx.impl_trait_ref(impl_def_id);
183+
if_chain! {
184+
if let Some(impl_trait_ref) = impl_trait_ref;
185+
if let ImplItemKind::Fn(FnSig { decl: impl_decl, .. }, impl_body_id) = &ii.kind;
186+
then {
187+
self.check_trait_method_impl_decl(ii, impl_decl, impl_trait_ref);
188+
let body = tcx.hir().body(*impl_body_id);
189+
self.visit_body(body);
190+
} else {
191+
walk_impl_item(self, ii)
192+
}
156193
}
157194
}
158195
}
@@ -177,96 +214,19 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UseSelf {
177214
};
178215

179216
if should_check {
180-
let visitor = &mut UseSelfVisitor {
181-
item_path,
217+
let self_ty= hir_ty_to_ty(cx.tcx, item_type);
218+
let visitor = &mut ImplVisitor {
182219
cx,
220+
item,
221+
self_ty,
183222
};
184-
let impl_def_id = cx.tcx.hir().local_def_id(item.hir_id);
185-
let impl_trait_ref = cx.tcx.impl_trait_ref(impl_def_id);
186-
187-
if let Some(impl_trait_ref) = impl_trait_ref {
188-
for impl_item_ref in refs {
189-
let impl_item = cx.tcx.hir().impl_item(impl_item_ref.id);
190-
if let ImplItemKind::Fn(FnSig{ decl: impl_decl, .. }, impl_body_id)
191-
= &impl_item.kind {
192-
check_trait_method_impl_decl(cx, impl_item, impl_decl, impl_trait_ref);
193223

194-
let body = cx.tcx.hir().body(*impl_body_id);
195-
visitor.visit_body(body);
196-
} else {
197-
visitor.visit_impl_item(impl_item);
198-
}
199-
}
200-
} else {
201-
for impl_item_ref in refs {
202-
let impl_item = cx.tcx.hir().impl_item(impl_item_ref.id);
203-
visitor.visit_impl_item(impl_item);
204-
}
224+
for impl_item_ref in refs {
225+
let impl_item = cx.tcx.hir().impl_item(impl_item_ref.id);
226+
visitor.visit_impl_item(impl_item);
205227
}
206228
}
207229
}
208230
}
209231
}
210232
}
211-
212-
struct UseSelfVisitor<'a, 'tcx> {
213-
item_path: &'a Path<'a>,
214-
cx: &'a LateContext<'a, 'tcx>,
215-
}
216-
217-
impl<'a, 'tcx> Visitor<'tcx> for UseSelfVisitor<'a, 'tcx> {
218-
type Map = Map<'tcx>;
219-
220-
fn visit_path(&mut self, path: &'tcx Path<'_>, _id: HirId) {
221-
if !path.segments.iter().any(|p| p.ident.span.is_dummy()) {
222-
if path.segments.len() >= 2 {
223-
let last_but_one = &path.segments[path.segments.len() - 2];
224-
if last_but_one.ident.name != kw::SelfUpper {
225-
let enum_def_id = match path.res {
226-
Res::Def(DefKind::Variant, variant_def_id) => self.cx.tcx.parent(variant_def_id),
227-
Res::Def(DefKind::Ctor(def::CtorOf::Variant, _), ctor_def_id) => {
228-
let variant_def_id = self.cx.tcx.parent(ctor_def_id);
229-
variant_def_id.and_then(|def_id| self.cx.tcx.parent(def_id))
230-
},
231-
_ => None,
232-
};
233-
234-
if self.item_path.res.opt_def_id() == enum_def_id {
235-
span_use_self_lint(self.cx, path, Some(last_but_one));
236-
}
237-
}
238-
}
239-
240-
if path.segments.last().expect(SEGMENTS_MSG).ident.name != kw::SelfUpper {
241-
if self.item_path.res == path.res {
242-
span_use_self_lint(self.cx, path, None);
243-
} else if let Res::Def(DefKind::Ctor(def::CtorOf::Struct, _), ctor_def_id) = path.res {
244-
if self.item_path.res.opt_def_id() == self.cx.tcx.parent(ctor_def_id) {
245-
span_use_self_lint(self.cx, path, None);
246-
}
247-
}
248-
}
249-
}
250-
251-
walk_path(self, path);
252-
}
253-
254-
fn visit_item(&mut self, item: &'tcx Item<'_>) {
255-
match item.kind {
256-
ItemKind::Use(..)
257-
| ItemKind::Static(..)
258-
| ItemKind::Enum(..)
259-
| ItemKind::Struct(..)
260-
| ItemKind::Union(..)
261-
| ItemKind::Impl { .. }
262-
| ItemKind::Fn(..) => {
263-
// Don't check statements that shadow `Self` or where `Self` can't be used
264-
},
265-
_ => walk_item(self, item),
266-
}
267-
}
268-
269-
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
270-
NestedVisitorMap::All(self.cx.tcx.hir())
271-
}
272-
}

tests/ui/use_self.fixed

+42-8
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ mod existential {
8686
struct Foo;
8787

8888
impl Foo {
89-
fn bad(foos: &[Self]) -> impl Iterator<Item = &Self> {
89+
fn bad(foos: &[Self]) -> impl Iterator<Item = &Foo> {
9090
foos.iter()
9191
}
9292

@@ -101,7 +101,7 @@ mod tuple_structs {
101101

102102
impl TS {
103103
pub fn ts() -> Self {
104-
Self(0)
104+
TS(0)
105105
}
106106
}
107107
}
@@ -163,9 +163,9 @@ mod nesting {
163163
}
164164

165165
fn method2() {
166-
let _ = Self::B(42);
167-
let _ = Self::C { field: true };
168-
let _ = Self::A;
166+
let _ = Enum::B(42);
167+
let _ = Self { field: true };
168+
let _ = Enum::A;
169169
}
170170
}
171171
}
@@ -176,11 +176,26 @@ mod issue3410 {
176176
struct B;
177177

178178
trait Trait<T> {
179-
fn a(v: T);
179+
fn a(v: T) -> Self;
180180
}
181181

182182
impl Trait<Vec<A>> for Vec<B> {
183-
fn a(_: Vec<A>) {}
183+
fn a(_: Vec<A>) -> Self {
184+
unimplemented!()
185+
}
186+
}
187+
188+
impl<T> Trait<Vec<A>> for Vec<T>
189+
where
190+
T: Trait<B>,
191+
{
192+
fn a(v: Vec<A>) -> Self {
193+
// fix false positive, should not trigger here
194+
<Vec<B>>::a(v)
195+
.into_iter()
196+
.map(Trait::a)
197+
.collect()
198+
}
184199
}
185200
}
186201

@@ -232,7 +247,7 @@ mod paths_created_by_lowering {
232247
const A: usize = 0;
233248
const B: usize = 1;
234249

235-
async fn g() -> Self {
250+
async fn g() -> S {
236251
Self {}
237252
}
238253

@@ -251,3 +266,22 @@ mod paths_created_by_lowering {
251266
}
252267
}
253268
}
269+
270+
// reused from #1997
271+
mod generics {
272+
struct Foo<T> {
273+
value: T,
274+
}
275+
276+
impl<T> Foo<T> {
277+
// `Self` is applicable here
278+
fn foo(value: T) -> Self<T> {
279+
Self { value }
280+
}
281+
282+
// `Cannot` use `Self` as a return type as the generic types are different
283+
fn bar(value: i32) -> Foo<i32> {
284+
Foo { value }
285+
}
286+
}
287+
}

0 commit comments

Comments
 (0)