Skip to content

Commit d51db24

Browse files
committed
Auto merge of #6660 - camsteffen:path-to-local, r=llogiq
Cleanup path-to-local checks changelog: none It seemed like too much ceremony going on to check if an expression matches a variable. So I created two util functions `path_to_local(Expr) -> Option<HirId>` and `path_to_local_id(Expr, HirId) -> bool` to make this easier, and used them wherever applicable. I changed logic in a few places to use `HirId` instead of `Symbol` where it was easy to do so. I believe this is more correct and may even fix some bugs. I also removed some calls to `qpath_res`. This is not needed if you are only looking for a `Res::Local`. As a note, I wanted to name the util functions in a way that encourages understanding of the HIR.
2 parents b36d1a4 + 56f7fbb commit d51db24

14 files changed

+180
-301
lines changed

clippy_lints/src/collapsible_match.rs

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
use crate::utils::visitors::LocalUsedVisitor;
2-
use crate::utils::{span_lint_and_then, SpanlessEq};
2+
use crate::utils::{path_to_local, span_lint_and_then, SpanlessEq};
33
use if_chain::if_chain;
44
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
55
use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, QPath, StmtKind, UnOp};
66
use rustc_lint::{LateContext, LateLintPass};
7-
use rustc_middle::ty::{DefIdTree, TyCtxt};
7+
use rustc_middle::ty::{DefIdTree, TyCtxt, TypeckResults};
88
use rustc_session::{declare_lint_pass, declare_tool_lint};
99
use rustc_span::{MultiSpan, Span};
1010

@@ -72,7 +72,7 @@ fn check_arm(arm: &Arm<'_>, wild_outer_arm: &Arm<'_>, cx: &LateContext<'_>) {
7272
if arms_inner.iter().all(|arm| arm.guard.is_none());
7373
// match expression must be a local binding
7474
// match <local> { .. }
75-
if let Some(binding_id) = addr_adjusted_binding(expr_in, cx);
75+
if let Some(binding_id) = path_to_local(strip_ref_operators(expr_in, cx.typeck_results()));
7676
// one of the branches must be "wild-like"
7777
if let Some(wild_inner_arm_idx) = arms_inner.iter().rposition(|arm_inner| arm_is_wild_like(arm_inner, cx.tcx));
7878
let (wild_inner_arm, non_wild_inner_arm) =
@@ -175,19 +175,15 @@ fn is_none_ctor(res: Res, tcx: TyCtxt<'_>) -> bool {
175175
false
176176
}
177177

178-
/// Retrieves a binding ID with optional `&` and/or `*` operators removed. (e.g. `&**foo`)
179-
/// Returns `None` if a non-reference type is de-referenced.
180-
/// For example, if `Vec` is de-referenced to a slice, `None` is returned.
181-
fn addr_adjusted_binding(mut expr: &Expr<'_>, cx: &LateContext<'_>) -> Option<HirId> {
178+
/// Removes `AddrOf` operators (`&`) or deref operators (`*`), but only if a reference type is
179+
/// dereferenced. An overloaded deref such as `Vec` to slice would not be removed.
180+
fn strip_ref_operators<'hir>(mut expr: &'hir Expr<'hir>, typeck_results: &TypeckResults<'_>) -> &'hir Expr<'hir> {
182181
loop {
183182
match expr.kind {
184183
ExprKind::AddrOf(_, _, e) => expr = e,
185-
ExprKind::Path(QPath::Resolved(None, path)) => match path.res {
186-
Res::Local(binding_id) => break Some(binding_id),
187-
_ => break None,
188-
},
189-
ExprKind::Unary(UnOp::UnDeref, e) if cx.typeck_results().expr_ty(e).is_ref() => expr = e,
190-
_ => break None,
184+
ExprKind::Unary(UnOp::UnDeref, e) if typeck_results.expr_ty(e).is_ref() => expr = e,
185+
_ => break,
191186
}
192187
}
188+
expr
193189
}

clippy_lints/src/eval_order_dependence.rs

Lines changed: 23 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
use crate::utils::{get_parent_expr, span_lint, span_lint_and_note};
2-
use if_chain::if_chain;
1+
use crate::utils::{get_parent_expr, path_to_local, path_to_local_id, span_lint, span_lint_and_note};
32
use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
4-
use rustc_hir::{def, BinOpKind, Block, Expr, ExprKind, Guard, HirId, Local, Node, QPath, Stmt, StmtKind};
3+
use rustc_hir::{BinOpKind, Block, Expr, ExprKind, Guard, HirId, Local, Node, Stmt, StmtKind};
54
use rustc_lint::{LateContext, LateLintPass};
65
use rustc_middle::hir::map::Map;
76
use rustc_middle::ty;
@@ -72,20 +71,14 @@ impl<'tcx> LateLintPass<'tcx> for EvalOrderDependence {
7271
// Find a write to a local variable.
7372
match expr.kind {
7473
ExprKind::Assign(ref lhs, ..) | ExprKind::AssignOp(_, ref lhs, _) => {
75-
if let ExprKind::Path(ref qpath) = lhs.kind {
76-
if let QPath::Resolved(_, ref path) = *qpath {
77-
if path.segments.len() == 1 {
78-
if let def::Res::Local(var) = cx.qpath_res(qpath, lhs.hir_id) {
79-
let mut visitor = ReadVisitor {
80-
cx,
81-
var,
82-
write_expr: expr,
83-
last_expr: expr,
84-
};
85-
check_for_unsequenced_reads(&mut visitor);
86-
}
87-
}
88-
}
74+
if let Some(var) = path_to_local(lhs) {
75+
let mut visitor = ReadVisitor {
76+
cx,
77+
var,
78+
write_expr: expr,
79+
last_expr: expr,
80+
};
81+
check_for_unsequenced_reads(&mut visitor);
8982
}
9083
},
9184
_ => {},
@@ -304,27 +297,20 @@ impl<'a, 'tcx> Visitor<'tcx> for ReadVisitor<'a, 'tcx> {
304297
return;
305298
}
306299

307-
match expr.kind {
308-
ExprKind::Path(ref qpath) => {
309-
if_chain! {
310-
if let QPath::Resolved(None, ref path) = *qpath;
311-
if path.segments.len() == 1;
312-
if let def::Res::Local(local_id) = self.cx.qpath_res(qpath, expr.hir_id);
313-
if local_id == self.var;
314-
// Check that this is a read, not a write.
315-
if !is_in_assignment_position(self.cx, expr);
316-
then {
317-
span_lint_and_note(
318-
self.cx,
319-
EVAL_ORDER_DEPENDENCE,
320-
expr.span,
321-
"unsequenced read of a variable",
322-
Some(self.write_expr.span),
323-
"whether read occurs before this write depends on evaluation order"
324-
);
325-
}
326-
}
300+
if path_to_local_id(expr, self.var) {
301+
// Check that this is a read, not a write.
302+
if !is_in_assignment_position(self.cx, expr) {
303+
span_lint_and_note(
304+
self.cx,
305+
EVAL_ORDER_DEPENDENCE,
306+
expr.span,
307+
"unsequenced read of a variable",
308+
Some(self.write_expr.span),
309+
"whether read occurs before this write depends on evaluation order",
310+
);
327311
}
312+
}
313+
match expr.kind {
328314
// We're about to descend a closure. Since we don't know when (or
329315
// if) the closure will be evaluated, any reads in it might not
330316
// occur here (or ever). Like above, bail to avoid false positives.

clippy_lints/src/functions.rs

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
use crate::utils::{
22
attr_by_name, attrs::is_proc_macro, is_must_use_ty, is_trait_impl_item, is_type_diagnostic_item, iter_input_pats,
3-
last_path_segment, match_def_path, must_use_attr, return_ty, snippet, snippet_opt, span_lint, span_lint_and_help,
4-
span_lint_and_then, trait_ref_of_method, type_is_unsafe_function,
3+
last_path_segment, match_def_path, must_use_attr, path_to_local, return_ty, snippet, snippet_opt, span_lint,
4+
span_lint_and_help, span_lint_and_then, trait_ref_of_method, type_is_unsafe_function,
55
};
66
use if_chain::if_chain;
77
use rustc_ast::ast::Attribute;
88
use rustc_data_structures::fx::FxHashSet;
99
use rustc_errors::Applicability;
1010
use rustc_hir as hir;
1111
use rustc_hir::intravisit;
12-
use rustc_hir::{def::Res, def_id::DefId};
12+
use rustc_hir::{def::Res, def_id::DefId, QPath};
1313
use rustc_lint::{LateContext, LateLintPass, LintContext};
1414
use rustc_middle::hir::map::Map;
1515
use rustc_middle::lint::in_external_macro;
@@ -658,16 +658,14 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for DerefVisitor<'a, 'tcx> {
658658

659659
impl<'a, 'tcx> DerefVisitor<'a, 'tcx> {
660660
fn check_arg(&self, ptr: &hir::Expr<'_>) {
661-
if let hir::ExprKind::Path(ref qpath) = ptr.kind {
662-
if let Res::Local(id) = self.cx.qpath_res(qpath, ptr.hir_id) {
663-
if self.ptrs.contains(&id) {
664-
span_lint(
665-
self.cx,
666-
NOT_UNSAFE_PTR_ARG_DEREF,
667-
ptr.span,
668-
"this public function dereferences a raw pointer but is not marked `unsafe`",
669-
);
670-
}
661+
if let Some(id) = path_to_local(ptr) {
662+
if self.ptrs.contains(&id) {
663+
span_lint(
664+
self.cx,
665+
NOT_UNSAFE_PTR_ARG_DEREF,
666+
ptr.span,
667+
"this public function dereferences a raw pointer but is not marked `unsafe`",
668+
);
671669
}
672670
}
673671
}
@@ -698,7 +696,7 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for StaticMutVisitor<'a, 'tcx> {
698696
arg.span,
699697
&mut tys,
700698
)
701-
&& is_mutated_static(self.cx, arg)
699+
&& is_mutated_static(arg)
702700
{
703701
self.mutates_static = true;
704702
return;
@@ -707,7 +705,7 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for StaticMutVisitor<'a, 'tcx> {
707705
}
708706
},
709707
Assign(ref target, ..) | AssignOp(_, ref target, _) | AddrOf(_, hir::Mutability::Mut, ref target) => {
710-
self.mutates_static |= is_mutated_static(self.cx, target)
708+
self.mutates_static |= is_mutated_static(target)
711709
},
712710
_ => {},
713711
}
@@ -718,12 +716,13 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for StaticMutVisitor<'a, 'tcx> {
718716
}
719717
}
720718

721-
fn is_mutated_static(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> bool {
719+
fn is_mutated_static(e: &hir::Expr<'_>) -> bool {
722720
use hir::ExprKind::{Field, Index, Path};
723721

724722
match e.kind {
725-
Path(ref qpath) => !matches!(cx.qpath_res(qpath, e.hir_id), Res::Local(_)),
726-
Field(ref inner, _) | Index(ref inner, _) => is_mutated_static(cx, inner),
723+
Path(QPath::Resolved(_, path)) => !matches!(path.res, Res::Local(_)),
724+
Path(_) => true,
725+
Field(ref inner, _) | Index(ref inner, _) => is_mutated_static(inner),
727726
_ => false,
728727
}
729728
}

clippy_lints/src/let_if_seq.rs

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
use crate::utils::{snippet, span_lint_and_then, visitors::LocalUsedVisitor};
1+
use crate::utils::{path_to_local_id, snippet, span_lint_and_then, visitors::LocalUsedVisitor};
22
use if_chain::if_chain;
33
use rustc_errors::Applicability;
44
use rustc_hir as hir;
5-
use rustc_hir::def::Res;
65
use rustc_hir::BindingAnnotation;
76
use rustc_lint::{LateContext, LateLintPass};
87
use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -66,7 +65,7 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq {
6665
if let hir::ExprKind::If(ref cond, ref then, ref else_) = if_.kind;
6766
if !LocalUsedVisitor::new(canonical_id).check_expr(cond);
6867
if let hir::ExprKind::Block(ref then, _) = then.kind;
69-
if let Some(value) = check_assign(cx, canonical_id, &*then);
68+
if let Some(value) = check_assign(canonical_id, &*then);
7069
if !LocalUsedVisitor::new(canonical_id).check_expr(value);
7170
then {
7271
let span = stmt.span.to(if_.span);
@@ -79,7 +78,7 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq {
7978

8079
let (default_multi_stmts, default) = if let Some(ref else_) = *else_ {
8180
if let hir::ExprKind::Block(ref else_, _) = else_.kind {
82-
if let Some(default) = check_assign(cx, canonical_id, else_) {
81+
if let Some(default) = check_assign(canonical_id, else_) {
8382
(else_.stmts.len() > 1, default)
8483
} else if let Some(ref default) = local.init {
8584
(true, &**default)
@@ -134,19 +133,13 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq {
134133
}
135134
}
136135

137-
fn check_assign<'tcx>(
138-
cx: &LateContext<'tcx>,
139-
decl: hir::HirId,
140-
block: &'tcx hir::Block<'_>,
141-
) -> Option<&'tcx hir::Expr<'tcx>> {
136+
fn check_assign<'tcx>(decl: hir::HirId, block: &'tcx hir::Block<'_>) -> Option<&'tcx hir::Expr<'tcx>> {
142137
if_chain! {
143138
if block.expr.is_none();
144139
if let Some(expr) = block.stmts.iter().last();
145140
if let hir::StmtKind::Semi(ref expr) = expr.kind;
146141
if let hir::ExprKind::Assign(ref var, ref value, _) = expr.kind;
147-
if let hir::ExprKind::Path(ref qpath) = var.kind;
148-
if let Res::Local(local_id) = cx.qpath_res(qpath, var.hir_id);
149-
if decl == local_id;
142+
if path_to_local_id(var, decl);
150143
then {
151144
let mut v = LocalUsedVisitor::new(decl);
152145

0 commit comments

Comments
 (0)