Skip to content

Commit 9e605ef

Browse files
committed
Auto merge of rust-lang#8443 - Jarcho:match_cfg_arm, r=flip1995
Don't lint `match` expressions with `cfg`ed arms Somehow there are no open issues related to this for any of the affected lints. At least none that I could fine from a quick search. changelog: Don't lint `match` expressions with `cfg`ed arms in many cases
2 parents 29ee5e2 + 78345b4 commit 9e605ef

15 files changed

+314
-156
lines changed

clippy_lints/src/matches/match_like_matches.rs

+24-21
Original file line numberDiff line numberDiff line change
@@ -3,49 +3,52 @@ use clippy_utils::source::snippet_with_applicability;
33
use clippy_utils::{higher, is_wild};
44
use rustc_ast::{Attribute, LitKind};
55
use rustc_errors::Applicability;
6-
use rustc_hir::{BorrowKind, Expr, ExprKind, Guard, MatchSource, Pat};
6+
use rustc_hir::{Arm, BorrowKind, Expr, ExprKind, Guard, Pat};
77
use rustc_lint::LateContext;
88
use rustc_middle::ty;
99
use rustc_span::source_map::Spanned;
1010

1111
use super::MATCH_LIKE_MATCHES_MACRO;
1212

1313
/// Lint a `match` or `if let .. { .. } else { .. }` expr that could be replaced by `matches!`
14-
pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
14+
pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
1515
if let Some(higher::IfLet {
1616
let_pat,
1717
let_expr,
1818
if_then,
1919
if_else: Some(if_else),
2020
}) = higher::IfLet::hir(cx, expr)
2121
{
22-
return find_matches_sugg(
22+
find_matches_sugg(
2323
cx,
2424
let_expr,
2525
IntoIterator::into_iter([(&[][..], Some(let_pat), if_then, None), (&[][..], None, if_else, None)]),
2626
expr,
2727
true,
2828
);
2929
}
30+
}
3031

31-
if let ExprKind::Match(scrut, arms, MatchSource::Normal) = expr.kind {
32-
return find_matches_sugg(
33-
cx,
34-
scrut,
35-
arms.iter().map(|arm| {
36-
(
37-
cx.tcx.hir().attrs(arm.hir_id),
38-
Some(arm.pat),
39-
arm.body,
40-
arm.guard.as_ref(),
41-
)
42-
}),
43-
expr,
44-
false,
45-
);
46-
}
47-
48-
false
32+
pub(super) fn check_match<'tcx>(
33+
cx: &LateContext<'tcx>,
34+
e: &'tcx Expr<'_>,
35+
scrutinee: &'tcx Expr<'_>,
36+
arms: &'tcx [Arm<'tcx>],
37+
) -> bool {
38+
find_matches_sugg(
39+
cx,
40+
scrutinee,
41+
arms.iter().map(|arm| {
42+
(
43+
cx.tcx.hir().attrs(arm.hir_id),
44+
Some(arm.pat),
45+
arm.body,
46+
arm.guard.as_ref(),
47+
)
48+
}),
49+
e,
50+
false,
51+
)
4952
}
5053

5154
/// Lint a `match` or `if let` for replacement by `matches!`

clippy_lints/src/matches/match_same_arms.rs

+75-77
Original file line numberDiff line numberDiff line change
@@ -1,96 +1,94 @@
11
use clippy_utils::diagnostics::span_lint_and_then;
22
use clippy_utils::source::snippet;
33
use clippy_utils::{path_to_local, search_same, SpanlessEq, SpanlessHash};
4-
use rustc_hir::{Arm, Expr, ExprKind, HirId, HirIdMap, HirIdSet, MatchSource, Pat, PatKind};
4+
use rustc_hir::{Arm, Expr, HirId, HirIdMap, HirIdSet, Pat, PatKind};
55
use rustc_lint::LateContext;
66
use std::collections::hash_map::Entry;
77

88
use super::MATCH_SAME_ARMS;
99

10-
pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) {
11-
if let ExprKind::Match(_, arms, MatchSource::Normal) = expr.kind {
12-
let hash = |&(_, arm): &(usize, &Arm<'_>)| -> u64 {
13-
let mut h = SpanlessHash::new(cx);
14-
h.hash_expr(arm.body);
15-
h.finish()
16-
};
10+
pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) {
11+
let hash = |&(_, arm): &(usize, &Arm<'_>)| -> u64 {
12+
let mut h = SpanlessHash::new(cx);
13+
h.hash_expr(arm.body);
14+
h.finish()
15+
};
1716

18-
let eq = |&(lindex, lhs): &(usize, &Arm<'_>), &(rindex, rhs): &(usize, &Arm<'_>)| -> bool {
19-
let min_index = usize::min(lindex, rindex);
20-
let max_index = usize::max(lindex, rindex);
17+
let eq = |&(lindex, lhs): &(usize, &Arm<'_>), &(rindex, rhs): &(usize, &Arm<'_>)| -> bool {
18+
let min_index = usize::min(lindex, rindex);
19+
let max_index = usize::max(lindex, rindex);
2120

22-
let mut local_map: HirIdMap<HirId> = HirIdMap::default();
23-
let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| {
24-
if_chain! {
25-
if let Some(a_id) = path_to_local(a);
26-
if let Some(b_id) = path_to_local(b);
27-
let entry = match local_map.entry(a_id) {
28-
Entry::Vacant(entry) => entry,
29-
// check if using the same bindings as before
30-
Entry::Occupied(entry) => return *entry.get() == b_id,
31-
};
32-
// the names technically don't have to match; this makes the lint more conservative
33-
if cx.tcx.hir().name(a_id) == cx.tcx.hir().name(b_id);
34-
if cx.typeck_results().expr_ty(a) == cx.typeck_results().expr_ty(b);
35-
if pat_contains_local(lhs.pat, a_id);
36-
if pat_contains_local(rhs.pat, b_id);
37-
then {
38-
entry.insert(b_id);
39-
true
40-
} else {
41-
false
42-
}
21+
let mut local_map: HirIdMap<HirId> = HirIdMap::default();
22+
let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| {
23+
if_chain! {
24+
if let Some(a_id) = path_to_local(a);
25+
if let Some(b_id) = path_to_local(b);
26+
let entry = match local_map.entry(a_id) {
27+
Entry::Vacant(entry) => entry,
28+
// check if using the same bindings as before
29+
Entry::Occupied(entry) => return *entry.get() == b_id,
30+
};
31+
// the names technically don't have to match; this makes the lint more conservative
32+
if cx.tcx.hir().name(a_id) == cx.tcx.hir().name(b_id);
33+
if cx.typeck_results().expr_ty(a) == cx.typeck_results().expr_ty(b);
34+
if pat_contains_local(lhs.pat, a_id);
35+
if pat_contains_local(rhs.pat, b_id);
36+
then {
37+
entry.insert(b_id);
38+
true
39+
} else {
40+
false
4341
}
44-
};
45-
// Arms with a guard are ignored, those can’t always be merged together
46-
// This is also the case for arms in-between each there is an arm with a guard
47-
(min_index..=max_index).all(|index| arms[index].guard.is_none())
48-
&& SpanlessEq::new(cx)
49-
.expr_fallback(eq_fallback)
50-
.eq_expr(lhs.body, rhs.body)
51-
// these checks could be removed to allow unused bindings
52-
&& bindings_eq(lhs.pat, local_map.keys().copied().collect())
53-
&& bindings_eq(rhs.pat, local_map.values().copied().collect())
42+
}
5443
};
44+
// Arms with a guard are ignored, those can’t always be merged together
45+
// This is also the case for arms in-between each there is an arm with a guard
46+
(min_index..=max_index).all(|index| arms[index].guard.is_none())
47+
&& SpanlessEq::new(cx)
48+
.expr_fallback(eq_fallback)
49+
.eq_expr(lhs.body, rhs.body)
50+
// these checks could be removed to allow unused bindings
51+
&& bindings_eq(lhs.pat, local_map.keys().copied().collect())
52+
&& bindings_eq(rhs.pat, local_map.values().copied().collect())
53+
};
5554

56-
let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect();
57-
for (&(_, i), &(_, j)) in search_same(&indexed_arms, hash, eq) {
58-
span_lint_and_then(
59-
cx,
60-
MATCH_SAME_ARMS,
61-
j.body.span,
62-
"this `match` has identical arm bodies",
63-
|diag| {
64-
diag.span_note(i.body.span, "same as this");
55+
let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect();
56+
for (&(_, i), &(_, j)) in search_same(&indexed_arms, hash, eq) {
57+
span_lint_and_then(
58+
cx,
59+
MATCH_SAME_ARMS,
60+
j.body.span,
61+
"this `match` has identical arm bodies",
62+
|diag| {
63+
diag.span_note(i.body.span, "same as this");
6564

66-
// Note: this does not use `span_suggestion` on purpose:
67-
// there is no clean way
68-
// to remove the other arm. Building a span and suggest to replace it to ""
69-
// makes an even more confusing error message. Also in order not to make up a
70-
// span for the whole pattern, the suggestion is only shown when there is only
71-
// one pattern. The user should know about `|` if they are already using it…
65+
// Note: this does not use `span_suggestion` on purpose:
66+
// there is no clean way
67+
// to remove the other arm. Building a span and suggest to replace it to ""
68+
// makes an even more confusing error message. Also in order not to make up a
69+
// span for the whole pattern, the suggestion is only shown when there is only
70+
// one pattern. The user should know about `|` if they are already using it…
7271

73-
let lhs = snippet(cx, i.pat.span, "<pat1>");
74-
let rhs = snippet(cx, j.pat.span, "<pat2>");
72+
let lhs = snippet(cx, i.pat.span, "<pat1>");
73+
let rhs = snippet(cx, j.pat.span, "<pat2>");
7574

76-
if let PatKind::Wild = j.pat.kind {
77-
// if the last arm is _, then i could be integrated into _
78-
// note that i.pat cannot be _, because that would mean that we're
79-
// hiding all the subsequent arms, and rust won't compile
80-
diag.span_note(
81-
i.body.span,
82-
&format!(
83-
"`{}` has the same arm body as the `_` wildcard, consider removing it",
84-
lhs
85-
),
86-
);
87-
} else {
88-
diag.span_help(i.pat.span, &format!("consider refactoring into `{} | {}`", lhs, rhs,))
89-
.help("...or consider changing the match arm bodies");
90-
}
91-
},
92-
);
93-
}
75+
if let PatKind::Wild = j.pat.kind {
76+
// if the last arm is _, then i could be integrated into _
77+
// note that i.pat cannot be _, because that would mean that we're
78+
// hiding all the subsequent arms, and rust won't compile
79+
diag.span_note(
80+
i.body.span,
81+
&format!(
82+
"`{}` has the same arm body as the `_` wildcard, consider removing it",
83+
lhs
84+
),
85+
);
86+
} else {
87+
diag.span_help(i.pat.span, &format!("consider refactoring into `{} | {}`", lhs, rhs,))
88+
.help("...or consider changing the match arm bodies");
89+
}
90+
},
91+
);
9492
}
9593
}
9694

clippy_lints/src/matches/match_single_binding.rs

+1-18
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use clippy_utils::diagnostics::span_lint_and_sugg;
2-
use clippy_utils::source::{indent_of, snippet_block, snippet_opt, snippet_with_applicability};
2+
use clippy_utils::source::{indent_of, snippet_block, snippet_with_applicability};
33
use clippy_utils::sugg::Sugg;
44
use clippy_utils::{get_parent_expr, is_refutable, peel_blocks};
55
use rustc_errors::Applicability;
@@ -14,23 +14,6 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e
1414
return;
1515
}
1616

17-
// HACK:
18-
// This is a hack to deal with arms that are excluded by macros like `#[cfg]`. It is only used here
19-
// to prevent false positives as there is currently no better way to detect if code was excluded by
20-
// a macro. See PR #6435
21-
if_chain! {
22-
if let Some(match_snippet) = snippet_opt(cx, expr.span);
23-
if let Some(arm_snippet) = snippet_opt(cx, arms[0].span);
24-
if let Some(ex_snippet) = snippet_opt(cx, ex.span);
25-
let rest_snippet = match_snippet.replace(&arm_snippet, "").replace(&ex_snippet, "");
26-
if rest_snippet.contains("=>");
27-
then {
28-
// The code it self contains another thick arrow "=>"
29-
// -> Either another arm or a comment
30-
return;
31-
}
32-
}
33-
3417
let matched_vars = ex.span;
3518
let bind_names = arms[0].pat.span;
3619
let match_body = peel_blocks(arms[0].body);

0 commit comments

Comments
 (0)