Skip to content

Commit fbd77ef

Browse files
committed
Auto merge of rust-lang#7411 - camsteffen:use-self-visitor, r=flip1995
Simplify use_self impl changelog: none Mainly to remove an extra visit and simplify the "in body?" logic.
2 parents 95c2ab7 + bae76f9 commit fbd77ef

File tree

1 file changed

+64
-151
lines changed

1 file changed

+64
-151
lines changed

Diff for: clippy_lints/src/use_self.rs

+64-151
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,18 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
22
use clippy_utils::ty::same_type_and_consts;
33
use clippy_utils::{in_macro, meets_msrv, msrvs};
44
use if_chain::if_chain;
5+
use rustc_data_structures::fx::FxHashSet;
56
use rustc_errors::Applicability;
67
use rustc_hir::{
78
self as hir,
89
def::{CtorOf, DefKind, Res},
910
def_id::LocalDefId,
1011
intravisit::{walk_ty, NestedVisitorMap, Visitor},
11-
Expr, ExprKind, FnRetTy, FnSig, GenericArg, HirId, Impl, ImplItemKind, Item, ItemKind, Node, Path, QPath, TyKind,
12+
Expr, ExprKind, FnRetTy, FnSig, GenericArg, HirId, Impl, ImplItemKind, Item, ItemKind, Path, QPath, TyKind,
1213
};
1314
use rustc_lint::{LateContext, LateLintPass, LintContext};
1415
use rustc_middle::hir::map::Map;
15-
use rustc_middle::ty::{AssocKind, Ty};
16+
use rustc_middle::ty::AssocKind;
1617
use rustc_semver::RustcVersion;
1718
use rustc_session::{declare_tool_lint, impl_lint_pass};
1819
use rustc_span::Span;
@@ -73,10 +74,9 @@ impl UseSelf {
7374
#[derive(Debug)]
7475
enum StackItem {
7576
Check {
76-
hir_id: HirId,
77-
impl_trait_ref_def_id: Option<LocalDefId>,
78-
types_to_skip: Vec<HirId>,
79-
types_to_lint: Vec<HirId>,
77+
impl_id: LocalDefId,
78+
in_body: u32,
79+
types_to_skip: FxHashSet<HirId>,
8080
},
8181
NoCheck,
8282
}
@@ -86,60 +86,41 @@ impl_lint_pass!(UseSelf => [USE_SELF]);
8686
const SEGMENTS_MSG: &str = "segments should be composed of at least 1 element";
8787

8888
impl<'tcx> LateLintPass<'tcx> for UseSelf {
89-
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
89+
fn check_item(&mut self, _cx: &LateContext<'_>, item: &Item<'_>) {
90+
if !is_item_interesting(item) {
91+
// This does two things:
92+
// 1) Reduce needless churn on `self.stack`
93+
// 2) Don't push `StackItem::NoCheck` when entering `ItemKind::OpaqueTy`,
94+
// in order to lint `foo() -> impl <..>`
95+
return;
96+
}
9097
// We push the self types of `impl`s on a stack here. Only the top type on the stack is
9198
// relevant for linting, since this is the self type of the `impl` we're currently in. To
9299
// avoid linting on nested items, we push `StackItem::NoCheck` on the stack to signal, that
93100
// we're in an `impl` or nested item, that we don't want to lint
94-
//
95-
// NB: If you push something on the stack in this method, remember to also pop it in the
96-
// `check_item_post` method.
97-
match &item.kind {
98-
ItemKind::Impl(Impl {
99-
self_ty: hir_self_ty,
100-
of_trait,
101-
..
102-
}) => {
103-
let should_check = if let TyKind::Path(QPath::Resolved(_, item_path)) = hir_self_ty.kind {
104-
let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args;
105-
parameters.as_ref().map_or(true, |params| {
106-
!params.parenthesized && !params.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_)))
107-
})
108-
} else {
109-
false
110-
};
111-
let impl_trait_ref_def_id = of_trait.as_ref().map(|_| cx.tcx.hir().local_def_id(item.hir_id()));
112-
if should_check {
113-
self.stack.push(StackItem::Check {
114-
hir_id: hir_self_ty.hir_id,
115-
impl_trait_ref_def_id,
116-
types_to_lint: Vec::new(),
117-
types_to_skip: Vec::new(),
118-
});
119-
} else {
120-
self.stack.push(StackItem::NoCheck);
101+
let stack_item = if_chain! {
102+
if let ItemKind::Impl(Impl { self_ty, .. }) = item.kind;
103+
if let TyKind::Path(QPath::Resolved(_, item_path)) = self_ty.kind;
104+
let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args;
105+
if parameters.as_ref().map_or(true, |params| {
106+
!params.parenthesized && !params.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_)))
107+
});
108+
then {
109+
StackItem::Check {
110+
impl_id: item.def_id,
111+
in_body: 0,
112+
types_to_skip: std::iter::once(self_ty.hir_id).collect(),
121113
}
122-
},
123-
ItemKind::Static(..)
124-
| ItemKind::Const(..)
125-
| ItemKind::Fn(..)
126-
| ItemKind::Enum(..)
127-
| ItemKind::Struct(..)
128-
| ItemKind::Union(..)
129-
| ItemKind::Trait(..) => {
130-
self.stack.push(StackItem::NoCheck);
131-
},
132-
_ => (),
133-
}
114+
} else {
115+
StackItem::NoCheck
116+
}
117+
};
118+
self.stack.push(stack_item);
134119
}
135120

136121
fn check_item_post(&mut self, _: &LateContext<'_>, item: &Item<'_>) {
137-
use ItemKind::{Const, Enum, Fn, Impl, Static, Struct, Trait, Union};
138-
match item.kind {
139-
Impl { .. } | Static(..) | Const(..) | Fn(..) | Enum(..) | Struct(..) | Union(..) | Trait(..) => {
140-
self.stack.pop();
141-
},
142-
_ => (),
122+
if is_item_interesting(item) {
123+
self.stack.pop();
143124
}
144125
}
145126

@@ -149,11 +130,11 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
149130
if_chain! {
150131
if let ImplItemKind::Fn(FnSig { decl, .. }, ..) = impl_item.kind;
151132
if let Some(&mut StackItem::Check {
152-
impl_trait_ref_def_id: Some(def_id),
133+
impl_id,
153134
ref mut types_to_skip,
154135
..
155136
}) = self.stack.last_mut();
156-
if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(def_id);
137+
if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(impl_id);
157138
then {
158139
// `self_ty` is the semantic self type of `impl <trait> for <type>`. This cannot be
159140
// `Self`.
@@ -201,52 +182,39 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
201182
}
202183
}
203184

204-
fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx hir::Body<'_>) {
185+
fn check_body(&mut self, _: &LateContext<'_>, _: &hir::Body<'_>) {
205186
// `hir_ty_to_ty` cannot be called in `Body`s or it will panic (sometimes). But in bodies
206187
// we can use `cx.typeck_results.node_type(..)` to get the `ty::Ty` from a `hir::Ty`.
207188
// However the `node_type()` method can *only* be called in bodies.
208-
//
209-
// This method implementation determines which types should get linted in a `Body` and
210-
// which shouldn't, with a visitor. We could directly lint in the visitor, but then we
211-
// could only allow this lint on item scope. And we would have to check if those types are
212-
// already dealt with in `check_ty` anyway.
213-
if let Some(StackItem::Check {
214-
hir_id,
215-
types_to_lint,
216-
types_to_skip,
217-
..
218-
}) = self.stack.last_mut()
219-
{
220-
let self_ty = ty_from_hir_id(cx, *hir_id);
189+
if let Some(&mut StackItem::Check { ref mut in_body, .. }) = self.stack.last_mut() {
190+
*in_body = in_body.saturating_add(1);
191+
}
192+
}
221193

222-
let mut visitor = LintTyCollector {
223-
cx,
224-
self_ty,
225-
types_to_lint: vec![],
226-
types_to_skip: vec![],
227-
};
228-
visitor.visit_expr(&body.value);
229-
types_to_lint.extend(visitor.types_to_lint);
230-
types_to_skip.extend(visitor.types_to_skip);
194+
fn check_body_post(&mut self, _: &LateContext<'_>, _: &hir::Body<'_>) {
195+
if let Some(&mut StackItem::Check { ref mut in_body, .. }) = self.stack.last_mut() {
196+
*in_body = in_body.saturating_sub(1);
231197
}
232198
}
233199

234200
fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) {
235201
if_chain! {
236-
if !in_macro(hir_ty.span) && !in_impl(cx, hir_ty);
202+
if !in_macro(hir_ty.span);
237203
if meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS);
238-
if let Some(StackItem::Check {
239-
hir_id,
240-
types_to_lint,
241-
types_to_skip,
242-
..
204+
if let Some(&StackItem::Check {
205+
impl_id,
206+
in_body,
207+
ref types_to_skip,
243208
}) = self.stack.last();
209+
if let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind;
210+
if !matches!(path.res, Res::SelfTy(..) | Res::Def(DefKind::TyParam, _));
244211
if !types_to_skip.contains(&hir_ty.hir_id);
245-
if types_to_lint.contains(&hir_ty.hir_id)
246-
|| {
247-
let self_ty = ty_from_hir_id(cx, *hir_id);
248-
should_lint_ty(hir_ty, hir_ty_to_ty(cx.tcx, hir_ty), self_ty)
249-
};
212+
let ty = if in_body > 0 {
213+
cx.typeck_results().node_type(hir_ty.hir_id)
214+
} else {
215+
hir_ty_to_ty(cx.tcx, hir_ty)
216+
};
217+
if same_type_and_consts(ty, cx.tcx.type_of(impl_id));
250218
let hir = cx.tcx.hir();
251219
let id = hir.get_parent_node(hir_ty.hir_id);
252220
if !hir.opt_span(id).map_or(false, in_macro);
@@ -260,8 +228,8 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
260228
if_chain! {
261229
if !in_macro(expr.span);
262230
if meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS);
263-
if let Some(StackItem::Check { hir_id, .. }) = self.stack.last();
264-
if cx.typeck_results().expr_ty(expr) == ty_from_hir_id(cx, *hir_id);
231+
if let Some(&StackItem::Check { impl_id, .. }) = self.stack.last();
232+
if cx.typeck_results().expr_ty(expr) == cx.tcx.type_of(impl_id);
265233
then {} else { return; }
266234
}
267235
match expr.kind {
@@ -309,35 +277,6 @@ impl<'tcx> Visitor<'tcx> for SkipTyCollector {
309277
}
310278
}
311279

312-
struct LintTyCollector<'a, 'tcx> {
313-
cx: &'a LateContext<'tcx>,
314-
self_ty: Ty<'tcx>,
315-
types_to_lint: Vec<HirId>,
316-
types_to_skip: Vec<HirId>,
317-
}
318-
319-
impl<'a, 'tcx> Visitor<'tcx> for LintTyCollector<'a, 'tcx> {
320-
type Map = Map<'tcx>;
321-
322-
fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'_>) {
323-
if_chain! {
324-
if let Some(ty) = self.cx.typeck_results().node_type_opt(hir_ty.hir_id);
325-
if should_lint_ty(hir_ty, ty, self.self_ty);
326-
then {
327-
self.types_to_lint.push(hir_ty.hir_id);
328-
} else {
329-
self.types_to_skip.push(hir_ty.hir_id);
330-
}
331-
}
332-
333-
walk_ty(self, hir_ty);
334-
}
335-
336-
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
337-
NestedVisitorMap::None
338-
}
339-
}
340-
341280
fn span_lint(cx: &LateContext<'_>, span: Span) {
342281
span_lint_and_sugg(
343282
cx,
@@ -359,36 +298,10 @@ fn lint_path_to_variant(cx: &LateContext<'_>, path: &Path<'_>) {
359298
}
360299
}
361300

362-
fn ty_from_hir_id<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Ty<'tcx> {
363-
if let Some(Node::Ty(hir_ty)) = cx.tcx.hir().find(hir_id) {
364-
hir_ty_to_ty(cx.tcx, hir_ty)
365-
} else {
366-
unreachable!("This function should only be called with `HirId`s that are for sure `Node::Ty`")
367-
}
368-
}
369-
370-
fn in_impl(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'_>) -> bool {
371-
let map = cx.tcx.hir();
372-
let parent = map.get_parent_node(hir_ty.hir_id);
373-
if_chain! {
374-
if let Some(Node::Item(item)) = map.find(parent);
375-
if let ItemKind::Impl { .. } = item.kind;
376-
then {
377-
true
378-
} else {
379-
false
380-
}
381-
}
382-
}
383-
384-
fn should_lint_ty(hir_ty: &hir::Ty<'_>, ty: Ty<'_>, self_ty: Ty<'_>) -> bool {
385-
if_chain! {
386-
if same_type_and_consts(ty, self_ty);
387-
if let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind;
388-
then {
389-
!matches!(path.res, Res::SelfTy(..) | Res::Def(DefKind::TyParam, _))
390-
} else {
391-
false
392-
}
393-
}
301+
fn is_item_interesting(item: &Item<'_>) -> bool {
302+
use rustc_hir::ItemKind::{Const, Enum, Fn, Impl, Static, Struct, Trait, Union};
303+
matches!(
304+
item.kind,
305+
Impl { .. } | Static(..) | Const(..) | Fn(..) | Enum(..) | Struct(..) | Union(..) | Trait(..)
306+
)
394307
}

0 commit comments

Comments
 (0)