Skip to content

Commit d924164

Browse files
committed
Auto merge of rust-lang#6435 - xFrednet:5552-false-positive-match-single-binding, r=ebroto
Fixing a false positive for the `match_single_binding` lint rust-lang#5552 This is a fix for a false positive in the `match_single_binding` lint when using `#[cfg()]` on a branch. It is sadly a bit hacky but maybe the best solution as rust removes the other branch from the AST before we can even validate it. This fix looks at the code snippet itself and returns if it includes another thick arrow `=>` besides the one matching arm we found. This can again cause false negatives if someone has the following code: ```rust match x { // => <-- Causes a false negative _ => 1, } ``` I thought about making the code more complex and maybe validating against other things like the `#[cfg()]` macro but I believe that this is the best solution. This has basically switched the issue from a false positive to a false negative in a very specific case. I'm happy to make some changes if you have any suggestions 🙃. --- Fixes rust-lang#5552 changelog: Fixed a false positive in the `match_single_binding` lint with `#[cfg()]` macro
2 parents a642b42 + a37af06 commit d924164

File tree

4 files changed

+93
-3
lines changed

4 files changed

+93
-3
lines changed

clippy_lints/src/matches.rs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ use crate::utils::usage::is_unused;
44
use crate::utils::{
55
expr_block, get_arg_name, get_parent_expr, in_macro, indent_of, is_allowed, is_expn_of, is_refutable,
66
is_type_diagnostic_item, is_wild, match_qpath, match_type, match_var, meets_msrv, multispan_sugg, remove_blocks,
7-
snippet, snippet_block, snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg,
8-
span_lint_and_then,
7+
snippet, snippet_block, snippet_opt, snippet_with_applicability, span_lint_and_help, span_lint_and_note,
8+
span_lint_and_sugg, span_lint_and_then,
99
};
1010
use crate::utils::{paths, search_same, SpanlessEq, SpanlessHash};
1111
use if_chain::if_chain;
@@ -1237,6 +1237,24 @@ fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[A
12371237
if in_macro(expr.span) || arms.len() != 1 || is_refutable(cx, arms[0].pat) {
12381238
return;
12391239
}
1240+
1241+
// HACK:
1242+
// This is a hack to deal with arms that are excluded by macros like `#[cfg]`. It is only used here
1243+
// to prevent false positives as there is currently no better way to detect if code was excluded by
1244+
// a macro. See PR #6435
1245+
if_chain! {
1246+
if let Some(match_snippet) = snippet_opt(cx, expr.span);
1247+
if let Some(arm_snippet) = snippet_opt(cx, arms[0].span);
1248+
if let Some(ex_snippet) = snippet_opt(cx, ex.span);
1249+
let rest_snippet = match_snippet.replace(&arm_snippet, "").replace(&ex_snippet, "");
1250+
if rest_snippet.contains("=>");
1251+
then {
1252+
// The code it self contains another thick arrow "=>"
1253+
// -> Either another arm or a comment
1254+
return;
1255+
}
1256+
}
1257+
12401258
let matched_vars = ex.span;
12411259
let bind_names = arms[0].pat.span;
12421260
let match_body = remove_blocks(&arms[0].body);

tests/ui/match_single_binding.fixed

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,4 +87,32 @@ fn main() {
8787
unwrapped
8888
})
8989
.collect::<Vec<u8>>();
90+
// Ok
91+
let x = 1;
92+
match x {
93+
#[cfg(disabled_feature)]
94+
0 => println!("Disabled branch"),
95+
_ => println!("Enabled branch"),
96+
}
97+
// Lint
98+
let x = 1;
99+
let y = 1;
100+
println!("Single branch");
101+
// Ok
102+
let x = 1;
103+
let y = 1;
104+
match match y {
105+
0 => 1,
106+
_ => 2,
107+
} {
108+
#[cfg(disabled_feature)]
109+
0 => println!("Array index start"),
110+
_ => println!("Not an array index start"),
111+
}
112+
// False negative
113+
let x = 1;
114+
match x {
115+
// =>
116+
_ => println!("Not an array index start"),
117+
}
90118
}

tests/ui/match_single_binding.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,4 +99,37 @@ fn main() {
9999
unwrapped => unwrapped,
100100
})
101101
.collect::<Vec<u8>>();
102+
// Ok
103+
let x = 1;
104+
match x {
105+
#[cfg(disabled_feature)]
106+
0 => println!("Disabled branch"),
107+
_ => println!("Enabled branch"),
108+
}
109+
// Lint
110+
let x = 1;
111+
let y = 1;
112+
match match y {
113+
0 => 1,
114+
_ => 2,
115+
} {
116+
_ => println!("Single branch"),
117+
}
118+
// Ok
119+
let x = 1;
120+
let y = 1;
121+
match match y {
122+
0 => 1,
123+
_ => 2,
124+
} {
125+
#[cfg(disabled_feature)]
126+
0 => println!("Array index start"),
127+
_ => println!("Not an array index start"),
128+
}
129+
// False negative
130+
let x = 1;
131+
match x {
132+
// =>
133+
_ => println!("Not an array index start"),
134+
}
102135
}

tests/ui/match_single_binding.stderr

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,5 +167,16 @@ LL | unwrapped
167167
LL | })
168168
|
169169

170-
error: aborting due to 11 previous errors
170+
error: this match could be replaced by its body itself
171+
--> $DIR/match_single_binding.rs:112:5
172+
|
173+
LL | / match match y {
174+
LL | | 0 => 1,
175+
LL | | _ => 2,
176+
LL | | } {
177+
LL | | _ => println!("Single branch"),
178+
LL | | }
179+
| |_____^ help: consider using the match body instead: `println!("Single branch");`
180+
181+
error: aborting due to 12 previous errors
171182

0 commit comments

Comments
 (0)