Skip to content

Commit efe3fe9

Browse files
Fix single_match lint being emitted when it should not
1 parent 650e0c8 commit efe3fe9

File tree

5 files changed

+85
-22
lines changed

5 files changed

+85
-22
lines changed

clippy_lints/src/matches/mod.rs

+25-2
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ mod wild_in_or_pats;
2727
use clippy_config::Conf;
2828
use clippy_utils::msrvs::{self, Msrv};
2929
use clippy_utils::source::walk_span_to_context;
30-
use clippy_utils::{higher, is_direct_expn_of, is_in_const_context, is_span_match, span_contains_cfg};
30+
use clippy_utils::{
31+
higher, is_direct_expn_of, is_in_const_context, is_span_match, span_contains_cfg, span_extract_comments,
32+
};
3133
use rustc_hir::{Arm, Expr, ExprKind, LetStmt, MatchSource, Pat, PatKind};
3234
use rustc_lint::{LateContext, LateLintPass, LintContext};
3335
use rustc_middle::lint::in_external_macro;
@@ -1059,7 +1061,28 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
10591061
}
10601062

10611063
redundant_pattern_match::check_match(cx, expr, ex, arms);
1062-
single_match::check(cx, ex, arms, expr);
1064+
let source_map = cx.tcx.sess.source_map();
1065+
let mut match_comments = span_extract_comments(source_map, expr.span);
1066+
// We remove comments from inside arms block.
1067+
if !match_comments.is_empty() {
1068+
for arm in arms {
1069+
for comment in span_extract_comments(source_map, arm.body.span) {
1070+
if let Some(index) = match_comments
1071+
.iter()
1072+
.enumerate()
1073+
.find(|(_, cm)| **cm == comment)
1074+
.map(|(index, _)| index)
1075+
{
1076+
match_comments.remove(index);
1077+
}
1078+
}
1079+
}
1080+
}
1081+
// If there are still comments, it means they are outside of the arms, therefore
1082+
// we should not lint.
1083+
if match_comments.is_empty() {
1084+
single_match::check(cx, ex, arms, expr);
1085+
}
10631086
match_bool::check(cx, ex, arms, expr);
10641087
overlapping_arms::check(cx, ex, arms);
10651088
match_wild_enum::check(cx, ex, arms);

clippy_utils/src/lib.rs

+10-4
Original file line numberDiff line numberDiff line change
@@ -2977,12 +2977,18 @@ pub fn span_contains_comment(sm: &SourceMap, span: Span) -> bool {
29772977
///
29782978
/// Comments are returned wrapped with their relevant delimiters
29792979
pub fn span_extract_comment(sm: &SourceMap, span: Span) -> String {
2980+
span_extract_comments(sm, span).join("\n")
2981+
}
2982+
2983+
/// Returns all the comments a given span contains.
2984+
///
2985+
/// Comments are returned wrapped with their relevant delimiters.
2986+
pub fn span_extract_comments(sm: &SourceMap, span: Span) -> Vec<String> {
29802987
let snippet = sm.span_to_snippet(span).unwrap_or_default();
2981-
let res = tokenize_with_text(&snippet)
2988+
tokenize_with_text(&snippet)
29822989
.filter(|(t, ..)| matches!(t, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }))
2983-
.map(|(_, s, _)| s)
2984-
.join("\n");
2985-
res
2990+
.map(|(_, s, _)| s.to_string())
2991+
.collect::<Vec<_>>()
29862992
}
29872993

29882994
pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span {

tests/ui/single_match.fixed

+28-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,13 @@ fn single_match() {
1717
};
1818

1919
let x = Some(1u8);
20-
if let Some(y) = x { println!("{:?}", y) }
20+
match x {
21+
// Note the missing block braces.
22+
// We suggest `if let Some(y) = x { .. }` because the macro
23+
// is expanded before we can do anything.
24+
Some(y) => println!("{:?}", y),
25+
_ => (),
26+
}
2127

2228
let z = (1u8, 1u8);
2329
if let (2..=3, 7..=9) = z { dummy() };
@@ -318,5 +324,25 @@ fn irrefutable_match() {
318324

319325

320326

321-
println!()
327+
println!();
328+
329+
let mut x = vec![1i8];
330+
331+
// Should not lint.
332+
match x.pop() {
333+
// bla
334+
Some(u) => println!("{u}"),
335+
// more comments!
336+
None => {},
337+
}
338+
// Should not lint.
339+
match x.pop() {
340+
// bla
341+
Some(u) => {
342+
// bla
343+
println!("{u}");
344+
},
345+
// bla
346+
None => {},
347+
}
322348
}

tests/ui/single_match.rs

+20
Original file line numberDiff line numberDiff line change
@@ -401,4 +401,24 @@ fn irrefutable_match() {
401401
CONST_I32 => println!(),
402402
_ => {},
403403
}
404+
405+
let mut x = vec![1i8];
406+
407+
// Should not lint.
408+
match x.pop() {
409+
// bla
410+
Some(u) => println!("{u}"),
411+
// more comments!
412+
None => {},
413+
}
414+
// Should not lint.
415+
match x.pop() {
416+
// bla
417+
Some(u) => {
418+
// bla
419+
println!("{u}");
420+
},
421+
// bla
422+
None => {},
423+
}
404424
}

tests/ui/single_match.stderr

+2-14
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,6 @@ LL + println!("{:?}", y);
1818
LL ~ };
1919
|
2020

21-
error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
22-
--> tests/ui/single_match.rs:23:5
23-
|
24-
LL | / match x {
25-
LL | | // Note the missing block braces.
26-
LL | | // We suggest `if let Some(y) = x { .. }` because the macro
27-
LL | | // is expanded before we can do anything.
28-
LL | | Some(y) => println!("{:?}", y),
29-
LL | | _ => (),
30-
LL | | }
31-
| |_____^ help: try: `if let Some(y) = x { println!("{:?}", y) }`
32-
3321
error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
3422
--> tests/ui/single_match.rs:32:5
3523
|
@@ -279,7 +267,7 @@ LL | / match CONST_I32 {
279267
LL | | CONST_I32 => println!(),
280268
LL | | _ => {},
281269
LL | | }
282-
| |_____^ help: try: `println!()`
270+
| |_____^ help: try: `println!();`
283271

284-
error: aborting due to 26 previous errors
272+
error: aborting due to 25 previous errors
285273

0 commit comments

Comments
 (0)