Skip to content

Commit c6bf42a

Browse files
committed
Reinstate single_match/single_match_else lints with comments
Commit efe3fe9 removed the ability for `single_match` and `single_match_else` to trigger if comments were present outside of the arms, as those comments would be lost while rewriting the `match` expression. This reinstates the lint, but prevents the suggestion from being applied automatically in the presence of comments by using the `MaybeIncorrect` applicability. Also, a note is added to the lint message to warn the user about the need to preserve the comments if acting upon the suggestion.
1 parent 579b5d6 commit c6bf42a

8 files changed

+175
-75
lines changed

clippy_lints/src/matches/mod.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1110,11 +1110,9 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
11101110
}
11111111
}
11121112
}
1113-
// If there are still comments, it means they are outside of the arms, therefore
1114-
// we should not lint.
1115-
if match_comments.is_empty() {
1116-
single_match::check(cx, ex, arms, expr);
1117-
}
1113+
// If there are still comments, it means they are outside of the arms. Tell the lint
1114+
// code about it.
1115+
single_match::check(cx, ex, arms, expr, !match_comments.is_empty());
11181116
match_bool::check(cx, ex, arms, expr);
11191117
overlapping_arms::check(cx, ex, arms);
11201118
match_wild_enum::check(cx, ex, arms);

clippy_lints/src/matches/single_match.rs

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
use clippy_utils::diagnostics::span_lint_and_sugg;
1+
use clippy_utils::diagnostics::span_lint_and_then;
22
use clippy_utils::source::{SpanRangeExt, expr_block, snippet, snippet_block_with_context};
33
use clippy_utils::ty::implements_trait;
44
use clippy_utils::{
55
is_lint_allowed, is_unit_expr, peel_blocks, peel_hir_pat_refs, peel_middle_ty_refs, peel_n_hir_expr_refs,
66
};
77
use core::ops::ControlFlow;
88
use rustc_arena::DroplessArena;
9-
use rustc_errors::Applicability;
9+
use rustc_errors::{Applicability, Diag};
1010
use rustc_hir::def::{DefKind, Res};
1111
use rustc_hir::intravisit::{Visitor, walk_pat};
1212
use rustc_hir::{Arm, Expr, ExprKind, HirId, Node, Pat, PatExpr, PatExprKind, PatKind, QPath, StmtKind};
@@ -32,7 +32,7 @@ fn empty_arm_has_comment(cx: &LateContext<'_>, span: Span) -> bool {
3232
}
3333

3434
#[rustfmt::skip]
35-
pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>], expr: &'tcx Expr<'_>) {
35+
pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>], expr: &'tcx Expr<'_>, contains_comments: bool) {
3636
if let [arm1, arm2] = arms
3737
&& arm1.guard.is_none()
3838
&& arm2.guard.is_none()
@@ -77,15 +77,31 @@ pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'_>, arms: &'tc
7777
}
7878
}
7979

80-
report_single_pattern(cx, ex, arm1, expr, els);
80+
report_single_pattern(cx, ex, arm1, expr, els, contains_comments);
8181
}
8282
}
8383
}
8484

85-
fn report_single_pattern(cx: &LateContext<'_>, ex: &Expr<'_>, arm: &Arm<'_>, expr: &Expr<'_>, els: Option<&Expr<'_>>) {
85+
fn report_single_pattern(
86+
cx: &LateContext<'_>,
87+
ex: &Expr<'_>,
88+
arm: &Arm<'_>,
89+
expr: &Expr<'_>,
90+
els: Option<&Expr<'_>>,
91+
contains_comments: bool,
92+
) {
8693
let lint = if els.is_some() { SINGLE_MATCH_ELSE } else { SINGLE_MATCH };
8794
let ctxt = expr.span.ctxt();
88-
let mut app = Applicability::MachineApplicable;
95+
let note = |diag: &mut Diag<'_, ()>| {
96+
if contains_comments {
97+
diag.note("you might want to preserve the comments from inside the `match`");
98+
}
99+
};
100+
let mut app = if contains_comments {
101+
Applicability::MaybeIncorrect
102+
} else {
103+
Applicability::MachineApplicable
104+
};
89105
let els_str = els.map_or(String::new(), |els| {
90106
format!(" else {}", expr_block(cx, els, ctxt, "..", Some(expr.span), &mut app))
91107
});
@@ -109,7 +125,10 @@ fn report_single_pattern(cx: &LateContext<'_>, ex: &Expr<'_>, arm: &Arm<'_>, exp
109125
}
110126
(sugg, "try")
111127
};
112-
span_lint_and_sugg(cx, lint, expr.span, msg, help, sugg.to_string(), app);
128+
span_lint_and_then(cx, lint, expr.span, msg, |diag| {
129+
diag.span_suggestion(expr.span, help, sugg.to_string(), app);
130+
note(diag);
131+
});
113132
return;
114133
}
115134

@@ -162,7 +181,10 @@ fn report_single_pattern(cx: &LateContext<'_>, ex: &Expr<'_>, arm: &Arm<'_>, exp
162181
(msg, sugg)
163182
};
164183

165-
span_lint_and_sugg(cx, lint, expr.span, msg, "try", sugg, app);
184+
span_lint_and_then(cx, lint, expr.span, msg, |diag| {
185+
diag.span_suggestion(expr.span, "try", sugg.to_string(), app);
186+
note(diag);
187+
});
166188
}
167189

168190
struct PatVisitor<'tcx> {

tests/ui/single_match.fixed

Lines changed: 12 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
//@require-annotations-for-level: WARN
12
#![warn(clippy::single_match)]
23
#![allow(
34
unused,
@@ -18,13 +19,9 @@ fn single_match() {
1819
//~^^^^^^ single_match
1920

2021
let x = Some(1u8);
21-
match x {
22-
// Note the missing block braces.
23-
// We suggest `if let Some(y) = x { .. }` because the macro
24-
// is expanded before we can do anything.
25-
Some(y) => println!("{:?}", y),
26-
_ => (),
27-
}
22+
if let Some(y) = x { println!("{:?}", y) }
23+
//~^^^^^^^ single_match
24+
//~| NOTE: you might want to preserve the comments from inside the `match`
2825

2926
let z = (1u8, 1u8);
3027
if let (2..=3, 7..=9) = z { dummy() };
@@ -358,21 +355,14 @@ fn irrefutable_match() {
358355

359356
let mut x = vec![1i8];
360357

361-
// Should not lint.
362-
match x.pop() {
363-
// bla
364-
Some(u) => println!("{u}"),
365-
// more comments!
366-
None => {},
367-
}
368-
// Should not lint.
369-
match x.pop() {
370-
// bla
371-
Some(u) => {
372-
// bla
373-
println!("{u}");
374-
},
358+
if let Some(u) = x.pop() { println!("{u}") }
359+
//~^^^^^^ single_match
360+
//~| NOTE: you might want to preserve the comments from inside the `match`
361+
362+
if let Some(u) = x.pop() {
375363
// bla
376-
None => {},
364+
println!("{u}");
377365
}
366+
//~^^^^^^^^^ single_match
367+
//~| NOTE: you might want to preserve the comments from inside the `match`
378368
}

tests/ui/single_match.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
//@require-annotations-for-level: WARN
12
#![warn(clippy::single_match)]
23
#![allow(
34
unused,
@@ -28,6 +29,8 @@ fn single_match() {
2829
Some(y) => println!("{:?}", y),
2930
_ => (),
3031
}
32+
//~^^^^^^^ single_match
33+
//~| NOTE: you might want to preserve the comments from inside the `match`
3134

3235
let z = (1u8, 1u8);
3336
match z {
@@ -437,14 +440,15 @@ fn irrefutable_match() {
437440

438441
let mut x = vec![1i8];
439442

440-
// Should not lint.
441443
match x.pop() {
442444
// bla
443445
Some(u) => println!("{u}"),
444446
// more comments!
445447
None => {},
446448
}
447-
// Should not lint.
449+
//~^^^^^^ single_match
450+
//~| NOTE: you might want to preserve the comments from inside the `match`
451+
448452
match x.pop() {
449453
// bla
450454
Some(u) => {
@@ -454,4 +458,6 @@ fn irrefutable_match() {
454458
// bla
455459
None => {},
456460
}
461+
//~^^^^^^^^^ single_match
462+
//~| NOTE: you might want to preserve the comments from inside the `match`
457463
}

0 commit comments

Comments
 (0)