Skip to content

Commit 13508cf

Browse files
committed
rework use_self impl based on ty::Ty comparison rust-lang#3410
1 parent 02c9435 commit 13508cf

File tree

7 files changed

+291
-198
lines changed

7 files changed

+291
-198
lines changed

clippy_lints/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
1313
#![feature(crate_visibility_modifier)]
1414
#![feature(concat_idents)]
15+
#![feature(bindings_after_at)]
1516

1617
// FIXME: switch to something more ergonomic here, once available.
1718
// (Currently there is no way to opt into sysroot crates without `extern crate`.)

clippy_lints/src/use_self.rs

+172-156
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
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::def::DefKind;
5+
use rustc_hir::intravisit::{walk_expr, walk_impl_item, walk_ty, NestedVisitorMap, Visitor};
66
use rustc_hir::{
7-
def, FnDecl, FnRetTy, FnSig, GenericArg, HirId, ImplItem, ImplItemKind, Item, ItemKind, Path, PathSegment, QPath,
7+
def, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, ImplItem, ImplItemKind, Item, ItemKind, Node, Path, QPath,
88
TyKind,
99
};
1010
use rustc_lint::{LateContext, LateLintPass, LintContext};
1111
use rustc_middle::hir::map::Map;
1212
use rustc_middle::lint::in_external_macro;
1313
use rustc_middle::ty;
14-
use rustc_middle::ty::{DefIdTree, Ty};
14+
use rustc_middle::ty::Ty;
1515
use rustc_session::{declare_lint_pass, declare_tool_lint};
16-
use rustc_span::symbol::kw;
16+
use rustc_span::{BytePos, Span};
1717
use rustc_typeck::hir_ty_to_ty;
1818

19-
use crate::utils::{differing_macro_contexts, span_lint_and_sugg};
19+
use crate::utils::span_lint_and_sugg;
2020

2121
declare_clippy_lint! {
2222
/// **What it does:** Checks for unnecessary repetition of structure name when a
@@ -57,19 +57,7 @@ declare_lint_pass!(UseSelf => [USE_SELF]);
5757

5858
const SEGMENTS_MSG: &str = "segments should be composed of at least 1 element";
5959

60-
fn span_use_self_lint(cx: &LateContext<'_, '_>, path: &Path<'_>, last_segment: Option<&PathSegment<'_>>) {
61-
let last_segment = last_segment.unwrap_or_else(|| path.segments.last().expect(SEGMENTS_MSG));
62-
63-
// Path segments only include actual path, no methods or fields.
64-
let last_path_span = last_segment.ident.span;
65-
66-
if differing_macro_contexts(path.span, last_path_span) {
67-
return;
68-
}
69-
70-
// Only take path up to the end of last_path_span.
71-
let span = path.span.with_hi(last_path_span.hi());
72-
60+
fn span_lint<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, span: Span) {
7361
span_lint_and_sugg(
7462
cx,
7563
USE_SELF,
@@ -81,78 +69,182 @@ fn span_use_self_lint(cx: &LateContext<'_, '_>, path: &Path<'_>, last_segment: O
8169
);
8270
}
8371

84-
// FIXME: always use this (more correct) visitor, not just in method signatures.
85-
struct SemanticUseSelfVisitor<'a, 'tcx> {
72+
fn span_lint_ignore_last_segment<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, path: &'tcx Path<'tcx>) {
73+
if path.segments.len() > 1 {
74+
span_lint(
75+
cx,
76+
path.span
77+
.with_hi(path.segments[path.segments.len() - 1].ident.span.lo() - BytePos(2)),
78+
)
79+
}
80+
}
81+
82+
fn span_lint_on_qpath_resolved<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, qpath: &'tcx QPath<'tcx>, enum_variant: bool) {
83+
match qpath {
84+
QPath::Resolved(_, path) => {
85+
if enum_variant {
86+
span_lint_ignore_last_segment(cx, path);
87+
} else {
88+
span_lint(cx, path.span);
89+
}
90+
},
91+
_ => (),
92+
};
93+
}
94+
95+
struct ImplVisitor<'a, 'tcx> {
8696
cx: &'a LateContext<'a, 'tcx>,
97+
item: &'tcx Item<'tcx>,
8798
self_ty: Ty<'tcx>,
8899
}
89100

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

93-
fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'_>) {
94-
if let TyKind::Path(QPath::Resolved(_, path)) = &hir_ty.kind {
151+
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
152+
NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
153+
}
154+
155+
fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'tcx>) {
156+
if let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind {
95157
match path.res {
96158
def::Res::SelfTy(..) => {},
97159
_ => {
98160
if hir_ty_to_ty(self.cx.tcx, hir_ty) == self.self_ty {
99-
span_use_self_lint(self.cx, path, None);
161+
match self.cx.tcx.hir().find(self.cx.tcx.hir().get_parent_node(hir_ty.hir_id)) {
162+
Some(Node::Expr(Expr {
163+
kind: ExprKind::Path(QPath::TypeRelative(_, last_segment)),
164+
..
165+
})) =>
166+
// FIXME: this span manipulation should not be necessary
167+
// @flip1995 found an ast lowering issue in
168+
// https://github.com/rust-lang/rust/blob/master/src/librustc_ast_lowering/path.rs#L142-L162
169+
{
170+
span_lint(self.cx, hir_ty.span.with_hi(last_segment.ident.span.lo() - BytePos(2)))
171+
},
172+
_ => span_lint(self.cx, hir_ty.span),
173+
}
100174
}
101175
},
102176
}
103177
}
104178

105-
walk_ty(self, hir_ty)
106-
}
107-
108-
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
109-
NestedVisitorMap::None
179+
walk_ty(self, hir_ty);
110180
}
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");
124181

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-
};
182+
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
183+
fn expr_ty_matches<'a, 'tcx>(expr: &'tcx Expr<'tcx>, self_ty: Ty<'tcx>, cx: &LateContext<'a, 'tcx>) -> bool {
184+
cx.tcx
185+
.typeck_tables_of(expr.hir_id.owner.to_def_id())
186+
.node_type(expr.hir_id)
187+
== self_ty
188+
}
189+
match expr.kind {
190+
ExprKind::Struct(QPath::Resolved(_, path), ..) => {
191+
if expr_ty_matches(expr, self.self_ty, self.cx) {
192+
match path.res {
193+
def::Res::SelfTy(..) => (),
194+
def::Res::Def(DefKind::Variant, _) => span_lint_ignore_last_segment(self.cx, path),
195+
_ => {
196+
span_lint(self.cx, path.span);
197+
},
198+
}
199+
}
200+
},
201+
// type-relative fn calls (`Foo::new()`) and tuple-like instantiation (`Foo(arg)` or `Enum::Foo(arg)`)
202+
ExprKind::Call(
203+
fun
204+
@ Expr {
205+
kind: ExprKind::Path(ref qpath),
206+
..
207+
},
208+
_,
209+
) => {
210+
if expr_ty_matches(expr, self.self_ty, self.cx) {
211+
let res = self.cx.tables.qpath_res(qpath, fun.hir_id);
133212

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 };
213+
if let def::Res::Def(DefKind::Ctor(ctor_of, _), ..) = res {
214+
match ctor_of {
215+
def::CtorOf::Variant => span_lint_on_qpath_resolved(self.cx, qpath, true),
216+
def::CtorOf::Struct => span_lint_on_qpath_resolved(self.cx, qpath, false),
217+
}
218+
} else if let def::Res::Def(DefKind::Variant, ..) = res {
219+
span_lint_on_qpath_resolved(self.cx, qpath, true);
220+
}
221+
}
222+
},
223+
// unit enum variants (`Enum::A`)
224+
ExprKind::Path(ref qpath) => {
225+
if expr_ty_matches(expr, self.self_ty, self.cx) {
226+
span_lint_on_qpath_resolved(self.cx, qpath, true);
227+
}
228+
},
229+
_ => (),
230+
}
231+
walk_expr(self, expr);
232+
}
154233

155-
visitor.visit_ty(&impl_hir_ty);
234+
fn visit_impl_item(&mut self, ii: &'tcx ImplItem<'tcx>) {
235+
let tcx = self.cx.tcx;
236+
let impl_def_id = tcx.hir().local_def_id(self.item.hir_id);
237+
let impl_trait_ref = tcx.impl_trait_ref(impl_def_id);
238+
if_chain! {
239+
if let Some(impl_trait_ref) = impl_trait_ref;
240+
if let ImplItemKind::Fn(FnSig { decl: impl_decl, .. }, impl_body_id) = &ii.kind;
241+
then {
242+
self.check_trait_method_impl_decl(ii, impl_decl, impl_trait_ref);
243+
let body = tcx.hir().body(*impl_body_id);
244+
self.visit_body(body);
245+
} else {
246+
walk_impl_item(self, ii)
247+
}
156248
}
157249
}
158250
}
@@ -176,97 +268,21 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UseSelf {
176268
true
177269
};
178270

271+
// TODO: don't short-circuit upon lifetime parameters
179272
if should_check {
180-
let visitor = &mut UseSelfVisitor {
181-
item_path,
273+
let self_ty= hir_ty_to_ty(cx.tcx, item_type);
274+
let visitor = &mut ImplVisitor {
182275
cx,
276+
item,
277+
self_ty,
183278
};
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);
193279

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-
}
280+
for impl_item_ref in refs {
281+
let impl_item = cx.tcx.hir().impl_item(impl_item_ref.id);
282+
visitor.visit_impl_item(impl_item);
205283
}
206284
}
207285
}
208286
}
209287
}
210288
}
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-
}

0 commit comments

Comments
 (0)