Skip to content

Commit 5b32853

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

File tree

7 files changed

+270
-205
lines changed

7 files changed

+270
-205
lines changed

clippy_lints/src/lib.rs

Lines changed: 1 addition & 0 deletions
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

Lines changed: 159 additions & 155 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
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

1919
use crate::utils::{differing_macro_contexts, span_lint_and_sugg};
@@ -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,170 @@ 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+
struct ImplVisitor<'a, 'tcx> {
8683
cx: &'a LateContext<'a, 'tcx>,
84+
item: &'tcx Item<'tcx>,
8785
self_ty: Ty<'tcx>,
8886
}
8987

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

93-
fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'_>) {
94-
if let TyKind::Path(QPath::Resolved(_, path)) = &hir_ty.kind {
138+
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
139+
NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
140+
}
141+
142+
fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'tcx>) {
143+
if let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind {
95144
match path.res {
96145
def::Res::SelfTy(..) => {},
97146
_ => {
98147
if hir_ty_to_ty(self.cx.tcx, hir_ty) == self.self_ty {
99-
span_use_self_lint(self.cx, path, None);
148+
match self.cx.tcx.hir().find(self.cx.tcx.hir().get_parent_node(hir_ty.hir_id)) {
149+
Some(Node::Expr(Expr {
150+
kind: ExprKind::Path(QPath::TypeRelative(_, last_segment)),
151+
..
152+
})) =>
153+
// FIXME: this span manipulation should not be necessary
154+
// @flip1995 found an ast lowering issue in
155+
// https://github.com/rust-lang/rust/blob/master/src/librustc_ast_lowering/path.rs#L142-L162
156+
{
157+
span_lint(self.cx, hir_ty.span.with_hi(last_segment.ident.span.lo() - BytePos(2)))
158+
},
159+
_ => span_lint(self.cx, hir_ty.span),
160+
}
100161
}
101162
},
102163
}
103164
}
104165

105-
walk_ty(self, hir_ty)
166+
walk_ty(self, hir_ty);
106167
}
107168

108-
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
109-
NestedVisitorMap::None
169+
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
170+
match expr.kind {
171+
ExprKind::Struct(QPath::Resolved(_, path), ..) => {
172+
if self
173+
.cx
174+
.tcx
175+
.typeck_tables_of(expr.hir_id.owner.to_def_id())
176+
.node_type(expr.hir_id)
177+
== self.self_ty
178+
{
179+
match path.res {
180+
def::Res::SelfTy(..) => (),
181+
def::Res::Def(DefKind::Variant, _) => span_lint_ignore_last_segment(self.cx, path),
182+
_ => {
183+
span_lint(self.cx, path.span);
184+
},
185+
}
186+
}
187+
},
188+
ExprKind::Call(
189+
Expr {
190+
kind:
191+
ExprKind::Path(QPath::Resolved(
192+
_,
193+
path
194+
@
195+
Path {
196+
res: def::Res::Def(def::DefKind::Ctor(ctor_of, _), _),
197+
..
198+
},
199+
)),
200+
..
201+
},
202+
_,
203+
) => {
204+
if self
205+
.cx
206+
.tcx
207+
.typeck_tables_of(expr.hir_id.owner.to_def_id())
208+
.node_type(expr.hir_id)
209+
== self.self_ty
210+
{
211+
match ctor_of {
212+
def::CtorOf::Struct => span_lint(self.cx, path.span),
213+
def::CtorOf::Variant => span_lint_ignore_last_segment(self.cx, path),
214+
}
215+
}
216+
},
217+
_ => (),
218+
}
219+
walk_expr(self, expr);
110220
}
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-
};
133221

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);
222+
fn visit_impl_item(&mut self, ii: &'tcx ImplItem<'tcx>) {
223+
let tcx = self.cx.tcx;
224+
let impl_def_id = tcx.hir().local_def_id(self.item.hir_id);
225+
let impl_trait_ref = tcx.impl_trait_ref(impl_def_id);
226+
if_chain! {
227+
if let Some(impl_trait_ref) = impl_trait_ref;
228+
if let ImplItemKind::Fn(FnSig { decl: impl_decl, .. }, impl_body_id) = &ii.kind;
229+
then {
230+
self.check_trait_method_impl_decl(ii, impl_decl, impl_trait_ref);
231+
let body = tcx.hir().body(*impl_body_id);
232+
self.visit_body(body);
233+
} else {
234+
walk_impl_item(self, ii)
235+
}
156236
}
157237
}
158238
}
@@ -176,97 +256,21 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UseSelf {
176256
true
177257
};
178258

259+
// TODO: don't short-circuit upon lifetime parameters
179260
if should_check {
180-
let visitor = &mut UseSelfVisitor {
181-
item_path,
261+
let self_ty= hir_ty_to_ty(cx.tcx, item_type);
262+
let visitor = &mut ImplVisitor {
182263
cx,
264+
item,
265+
self_ty,
183266
};
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);
193-
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-
}
205-
}
206-
}
207-
}
208-
}
209-
}
210-
}
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-
}
239267

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);
268+
for impl_item_ref in refs {
269+
let impl_item = cx.tcx.hir().impl_item(impl_item_ref.id);
270+
visitor.visit_impl_item(impl_item);
246271
}
247272
}
248273
}
249274
}
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())
271275
}
272276
}

0 commit comments

Comments
 (0)