Skip to content

Commit f7d09b4

Browse files
committed
Auto merge of #7333 - camsteffen:match-var, r=llogiq
Factor out match_var and get_pat_name utils ...because checking vars by name is bad, because of shadowing. changelog: none
2 parents ce7b872 + b792bb3 commit f7d09b4

File tree

5 files changed

+92
-144
lines changed

5 files changed

+92
-144
lines changed

clippy_lints/src/bytecount.rs

Lines changed: 25 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
use clippy_utils::diagnostics::span_lint_and_sugg;
22
use clippy_utils::source::snippet_with_applicability;
33
use clippy_utils::ty::match_type;
4-
use clippy_utils::{contains_name, get_pat_name, paths, single_segment_path};
4+
use clippy_utils::visitors::LocalUsedVisitor;
5+
use clippy_utils::{path_to_local_id, paths, peel_ref_operators, remove_blocks, strip_pat_refs};
56
use if_chain::if_chain;
67
use rustc_errors::Applicability;
7-
use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, UnOp};
8+
use rustc_hir::{BinOpKind, Expr, ExprKind, PatKind};
89
use rustc_lint::{LateContext, LateLintPass};
910
use rustc_middle::ty::{self, UintTy};
1011
use rustc_session::{declare_lint_pass, declare_tool_lint};
1112
use rustc_span::sym;
12-
use rustc_span::Symbol;
1313

1414
declare_clippy_lint! {
1515
/// **What it does:** Checks for naive byte counts
@@ -38,42 +38,43 @@ declare_lint_pass!(ByteCount => [NAIVE_BYTECOUNT]);
3838
impl<'tcx> LateLintPass<'tcx> for ByteCount {
3939
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
4040
if_chain! {
41-
if let ExprKind::MethodCall(count, _, count_args, _) = expr.kind;
41+
if let ExprKind::MethodCall(count, _, [count_recv], _) = expr.kind;
4242
if count.ident.name == sym!(count);
43-
if count_args.len() == 1;
44-
if let ExprKind::MethodCall(filter, _, filter_args, _) = count_args[0].kind;
43+
if let ExprKind::MethodCall(filter, _, [filter_recv, filter_arg], _) = count_recv.kind;
4544
if filter.ident.name == sym!(filter);
46-
if filter_args.len() == 2;
47-
if let ExprKind::Closure(_, _, body_id, _, _) = filter_args[1].kind;
45+
if let ExprKind::Closure(_, _, body_id, _, _) = filter_arg.kind;
4846
let body = cx.tcx.hir().body(body_id);
49-
if body.params.len() == 1;
50-
if let Some(argname) = get_pat_name(body.params[0].pat);
47+
if let [param] = body.params;
48+
if let PatKind::Binding(_, arg_id, _, _) = strip_pat_refs(param.pat).kind;
5149
if let ExprKind::Binary(ref op, l, r) = body.value.kind;
5250
if op.node == BinOpKind::Eq;
5351
if match_type(cx,
54-
cx.typeck_results().expr_ty(&filter_args[0]).peel_refs(),
52+
cx.typeck_results().expr_ty(filter_recv).peel_refs(),
5553
&paths::SLICE_ITER);
54+
let operand_is_arg = |expr| {
55+
let expr = peel_ref_operators(cx, remove_blocks(expr));
56+
path_to_local_id(expr, arg_id)
57+
};
58+
let needle = if operand_is_arg(l) {
59+
r
60+
} else if operand_is_arg(r) {
61+
l
62+
} else {
63+
return;
64+
};
65+
if ty::Uint(UintTy::U8) == *cx.typeck_results().expr_ty(needle).peel_refs().kind();
66+
if !LocalUsedVisitor::new(cx, arg_id).check_expr(needle);
5667
then {
57-
let needle = match get_path_name(l) {
58-
Some(name) if check_arg(name, argname, r) => r,
59-
_ => match get_path_name(r) {
60-
Some(name) if check_arg(name, argname, l) => l,
61-
_ => { return; }
62-
}
63-
};
64-
if ty::Uint(UintTy::U8) != *cx.typeck_results().expr_ty(needle).peel_refs().kind() {
65-
return;
66-
}
6768
let haystack = if let ExprKind::MethodCall(path, _, args, _) =
68-
filter_args[0].kind {
69+
filter_recv.kind {
6970
let p = path.ident.name;
7071
if (p == sym::iter || p == sym!(iter_mut)) && args.len() == 1 {
7172
&args[0]
7273
} else {
73-
&filter_args[0]
74+
&filter_recv
7475
}
7576
} else {
76-
&filter_args[0]
77+
&filter_recv
7778
};
7879
let mut applicability = Applicability::MaybeIncorrect;
7980
span_lint_and_sugg(
@@ -91,24 +92,3 @@ impl<'tcx> LateLintPass<'tcx> for ByteCount {
9192
};
9293
}
9394
}
94-
95-
fn check_arg(name: Symbol, arg: Symbol, needle: &Expr<'_>) -> bool {
96-
name == arg && !contains_name(name, needle)
97-
}
98-
99-
fn get_path_name(expr: &Expr<'_>) -> Option<Symbol> {
100-
match expr.kind {
101-
ExprKind::Box(e) | ExprKind::AddrOf(BorrowKind::Ref, _, e) | ExprKind::Unary(UnOp::Deref, e) => {
102-
get_path_name(e)
103-
},
104-
ExprKind::Block(b, _) => {
105-
if b.stmts.is_empty() {
106-
b.expr.as_ref().and_then(|p| get_path_name(p))
107-
} else {
108-
None
109-
}
110-
},
111-
ExprKind::Path(ref qpath) => single_segment_path(qpath).map(|ps| ps.ident.name),
112-
_ => None,
113-
}
114-
}

clippy_lints/src/collapsible_match.rs

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
use clippy_utils::diagnostics::span_lint_and_then;
22
use clippy_utils::visitors::LocalUsedVisitor;
3-
use clippy_utils::{is_lang_ctor, path_to_local, SpanlessEq};
3+
use clippy_utils::{is_lang_ctor, path_to_local, peel_ref_operators, SpanlessEq};
44
use if_chain::if_chain;
55
use rustc_hir::LangItem::OptionNone;
6-
use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, StmtKind, UnOp};
6+
use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, StmtKind};
77
use rustc_lint::{LateContext, LateLintPass};
8-
use rustc_middle::ty::TypeckResults;
98
use rustc_session::{declare_lint_pass, declare_tool_lint};
109
use rustc_span::{MultiSpan, Span};
1110

@@ -73,7 +72,7 @@ fn check_arm<'tcx>(arm: &Arm<'tcx>, wild_outer_arm: &Arm<'tcx>, cx: &LateContext
7372
if arms_inner.iter().all(|arm| arm.guard.is_none());
7473
// match expression must be a local binding
7574
// match <local> { .. }
76-
if let Some(binding_id) = path_to_local(strip_ref_operators(expr_in, cx.typeck_results()));
75+
if let Some(binding_id) = path_to_local(peel_ref_operators(cx, expr_in));
7776
// one of the branches must be "wild-like"
7877
if let Some(wild_inner_arm_idx) = arms_inner.iter().rposition(|arm_inner| arm_is_wild_like(cx, arm_inner));
7978
let (wild_inner_arm, non_wild_inner_arm) =
@@ -163,16 +162,3 @@ fn pat_contains_or(pat: &Pat<'_>) -> bool {
163162
});
164163
result
165164
}
166-
167-
/// Removes `AddrOf` operators (`&`) or deref operators (`*`), but only if a reference type is
168-
/// dereferenced. An overloaded deref such as `Vec` to slice would not be removed.
169-
fn strip_ref_operators<'hir>(mut expr: &'hir Expr<'hir>, typeck_results: &TypeckResults<'_>) -> &'hir Expr<'hir> {
170-
loop {
171-
match expr.kind {
172-
ExprKind::AddrOf(_, _, e) => expr = e,
173-
ExprKind::Unary(UnOp::Deref, e) if typeck_results.expr_ty(e).is_ref() => expr = e,
174-
_ => break,
175-
}
176-
}
177-
expr
178-
}

clippy_lints/src/manual_map.rs

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,17 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
33
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
44
use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable};
55
use clippy_utils::{
6-
can_move_expr_to_closure, in_constant, is_allowed, is_else_clause, is_lang_ctor, match_var, peel_hir_expr_refs,
6+
can_move_expr_to_closure, in_constant, is_allowed, is_else_clause, is_lang_ctor, path_to_local_id,
7+
peel_hir_expr_refs,
78
};
89
use rustc_ast::util::parser::PREC_POSTFIX;
910
use rustc_errors::Applicability;
1011
use rustc_hir::LangItem::{OptionNone, OptionSome};
11-
use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, Pat, PatKind};
12+
use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, HirId, MatchSource, Mutability, Pat, PatKind};
1213
use rustc_lint::{LateContext, LateLintPass, LintContext};
1314
use rustc_middle::lint::in_external_macro;
1415
use rustc_session::{declare_lint_pass, declare_tool_lint};
15-
use rustc_span::{
16-
symbol::{sym, Ident},
17-
SyntaxContext,
18-
};
16+
use rustc_span::{sym, SyntaxContext};
1917

2018
declare_clippy_lint! {
2119
/// **What it does:** Checks for usages of `match` which could be implemented using `map`
@@ -141,13 +139,13 @@ impl LateLintPass<'_> for ManualMap {
141139
scrutinee_str.into()
142140
};
143141

144-
let body_str = if let PatKind::Binding(annotation, _, some_binding, None) = some_pat.kind {
145-
match can_pass_as_func(cx, some_binding, some_expr) {
142+
let body_str = if let PatKind::Binding(annotation, id, some_binding, None) = some_pat.kind {
143+
match can_pass_as_func(cx, id, some_expr) {
146144
Some(func) if func.span.ctxt() == some_expr.span.ctxt() => {
147145
snippet_with_applicability(cx, func.span, "..", &mut app).into_owned()
148146
},
149147
_ => {
150-
if match_var(some_expr, some_binding.name)
148+
if path_to_local_id(some_expr, id)
151149
&& !is_allowed(cx, MATCH_AS_REF, expr.hir_id)
152150
&& binding_ref.is_some()
153151
{
@@ -199,10 +197,10 @@ impl LateLintPass<'_> for ManualMap {
199197

200198
// Checks whether the expression could be passed as a function, or whether a closure is needed.
201199
// Returns the function to be passed to `map` if it exists.
202-
fn can_pass_as_func(cx: &LateContext<'tcx>, binding: Ident, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
200+
fn can_pass_as_func(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
203201
match expr.kind {
204202
ExprKind::Call(func, [arg])
205-
if match_var(arg, binding.name) && cx.typeck_results().expr_adjustments(arg).is_empty() =>
203+
if path_to_local_id(arg, binding) && cx.typeck_results().expr_adjustments(arg).is_empty() =>
206204
{
207205
Some(func)
208206
},

clippy_utils/src/lib.rs

Lines changed: 41 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ use rustc_hir::LangItem::{ResultErr, ResultOk};
7272
use rustc_hir::{
7373
def, Arm, BindingAnnotation, Block, Body, Constness, Destination, Expr, ExprKind, FnDecl, GenericArgs, HirId, Impl,
7474
ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource, Node, Param, Pat, PatKind, Path,
75-
PathSegment, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind,
75+
PathSegment, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind, UnOp,
7676
};
7777
use rustc_lint::{LateContext, Level, Lint, LintContext};
7878
use rustc_middle::hir::exports::Export;
@@ -326,16 +326,6 @@ pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol)
326326
.map_or(false, |did| is_diag_trait_item(cx, did, diag_item))
327327
}
328328

329-
/// Checks if an expression references a variable of the given name.
330-
pub fn match_var(expr: &Expr<'_>, var: Symbol) -> bool {
331-
if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind {
332-
if let [p] = path.segments {
333-
return p.ident.name == var;
334-
}
335-
}
336-
false
337-
}
338-
339329
pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
340330
match *path {
341331
QPath::Resolved(_, path) => path.segments.last().expect("A path must have at least one segment"),
@@ -707,16 +697,6 @@ pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
707697
}
708698
}
709699

710-
/// Gets the name of a `Pat`, if any.
711-
pub fn get_pat_name(pat: &Pat<'_>) -> Option<Symbol> {
712-
match pat.kind {
713-
PatKind::Binding(.., ref spname, _) => Some(spname.name),
714-
PatKind::Path(ref qpath) => single_segment_path(qpath).map(|ps| ps.ident.name),
715-
PatKind::Box(p) | PatKind::Ref(p, _) => get_pat_name(&*p),
716-
_ => None,
717-
}
718-
}
719-
720700
pub struct ContainsName {
721701
pub name: Symbol,
722702
pub result: bool,
@@ -1404,47 +1384,42 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
14041384
/// Checks if an expression represents the identity function
14051385
/// Only examines closures and `std::convert::identity`
14061386
pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1407-
/// Returns true if the expression is a binding to the given pattern
1408-
fn is_expr_pat_binding(cx: &LateContext<'_>, expr: &Expr<'_>, pat: &Pat<'_>) -> bool {
1409-
if let PatKind::Binding(_, _, ident, _) = pat.kind {
1410-
if match_var(expr, ident.name) {
1411-
return !(cx.typeck_results().hir_owner == expr.hir_id.owner && is_adjusted(cx, expr));
1412-
}
1413-
}
1414-
1415-
false
1416-
}
1417-
14181387
/// Checks if a function's body represents the identity function. Looks for bodies of the form:
14191388
/// * `|x| x`
14201389
/// * `|x| return x`
14211390
/// * `|x| { return x }`
14221391
/// * `|x| { return x; }`
14231392
fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
1424-
let body = remove_blocks(&func.value);
1425-
1426-
let value_pat = if let [value_param] = func.params {
1427-
value_param.pat
1428-
} else {
1429-
return false;
1393+
let id = if_chain! {
1394+
if let [param] = func.params;
1395+
if let PatKind::Binding(_, id, _, _) = param.pat.kind;
1396+
then {
1397+
id
1398+
} else {
1399+
return false;
1400+
}
14301401
};
14311402

1432-
match body.kind {
1433-
ExprKind::Path(QPath::Resolved(None, _)) => is_expr_pat_binding(cx, body, value_pat),
1434-
ExprKind::Ret(Some(ret_val)) => is_expr_pat_binding(cx, ret_val, value_pat),
1435-
ExprKind::Block(block, _) => {
1436-
if_chain! {
1437-
if let &[block_stmt] = &block.stmts;
1438-
if let StmtKind::Semi(expr) | StmtKind::Expr(expr) = block_stmt.kind;
1439-
if let ExprKind::Ret(Some(ret_val)) = expr.kind;
1440-
then {
1441-
is_expr_pat_binding(cx, ret_val, value_pat)
1442-
} else {
1443-
false
1403+
let mut expr = &func.value;
1404+
loop {
1405+
match expr.kind {
1406+
#[rustfmt::skip]
1407+
ExprKind::Block(&Block { stmts: [], expr: Some(e), .. }, _, )
1408+
| ExprKind::Ret(Some(e)) => expr = e,
1409+
#[rustfmt::skip]
1410+
ExprKind::Block(&Block { stmts: [stmt], expr: None, .. }, _) => {
1411+
if_chain! {
1412+
if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind;
1413+
if let ExprKind::Ret(Some(ret_val)) = e.kind;
1414+
then {
1415+
expr = ret_val;
1416+
} else {
1417+
return false;
1418+
}
14441419
}
1445-
}
1446-
},
1447-
_ => false,
1420+
},
1421+
_ => return path_to_local_id(expr, id) && cx.typeck_results().expr_adjustments(expr).is_empty(),
1422+
}
14481423
}
14491424
}
14501425

@@ -1710,6 +1685,19 @@ pub fn peel_hir_expr_refs(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
17101685
(e, count)
17111686
}
17121687

1688+
/// Removes `AddrOf` operators (`&`) or deref operators (`*`), but only if a reference type is
1689+
/// dereferenced. An overloaded deref such as `Vec` to slice would not be removed.
1690+
pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
1691+
loop {
1692+
match expr.kind {
1693+
ExprKind::AddrOf(_, _, e) => expr = e,
1694+
ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => expr = e,
1695+
_ => break,
1696+
}
1697+
}
1698+
expr
1699+
}
1700+
17131701
#[macro_export]
17141702
macro_rules! unwrap_cargo_metadata {
17151703
($cx: ident, $lint: ident, $deps: expr) => {{

0 commit comments

Comments
 (0)