Skip to content

Commit f81d6f0

Browse files
committed
Auto merge of rust-lang#117094 - Nadrieril:warn-lint-on-arm, r=cjgillot
Warn users who set `non_exhaustive_omitted_patterns` lint level on a match arm Before rust-lang#116734, the recommended usage of the [`non_exhaustive_omitted_patterns` lint](rust-lang#89554) was: ```rust match Bar::A { Bar::A => {}, #[warn(non_exhaustive_omitted_patterns)] _ => {}, } ``` After rust-lang#116734 this no longer makes sense, and recommended usage is now: ```rust #[warn(non_exhaustive_omitted_patterns)] match Bar::A { Bar::A => {}, _ => {}, } ``` As you can guess, this silently breaks all uses of the lint that used the previous form. This is a problem in particular because `syn` recommends usage of this lint to its users in the old way. This PR emits a warning when the previous form is used so users can update. r? `@cjgillot`
2 parents 2db26d3 + f0e8330 commit f81d6f0

File tree

8 files changed

+233
-25
lines changed

8 files changed

+233
-25
lines changed

compiler/rustc_mir_build/messages.ftl

+5
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,11 @@ mir_build_non_exhaustive_omitted_pattern = some variants are not matched explici
221221
.help = ensure that all variants are matched explicitly by adding the suggested match arms
222222
.note = the matched value is of type `{$scrut_ty}` and the `non_exhaustive_omitted_patterns` attribute was found
223223
224+
mir_build_non_exhaustive_omitted_pattern_lint_on_arm = the lint level must be set on the whole match
225+
.help = it no longer has any effect to set the lint level on an individual match arm
226+
.label = remove this attribute
227+
.suggestion = set the lint level on the whole match
228+
224229
mir_build_non_exhaustive_patterns_type_not_empty = non-exhaustive patterns: type `{$ty}` is non-empty
225230
.def_note = `{$peeled_ty}` defined here
226231
.type_note = the matched value is of type `{$ty}`

compiler/rustc_mir_build/src/errors.rs

+12
Original file line numberDiff line numberDiff line change
@@ -789,6 +789,18 @@ pub(crate) struct NonExhaustiveOmittedPattern<'tcx> {
789789
pub uncovered: Uncovered<'tcx>,
790790
}
791791

792+
#[derive(LintDiagnostic)]
793+
#[diag(mir_build_non_exhaustive_omitted_pattern_lint_on_arm)]
794+
#[help]
795+
pub(crate) struct NonExhaustiveOmittedPatternLintOnArm {
796+
#[label]
797+
pub lint_span: Span,
798+
#[suggestion(code = "#[{lint_level}({lint_name})]\n", applicability = "maybe-incorrect")]
799+
pub suggest_lint_on_match: Option<Span>,
800+
pub lint_level: &'static str,
801+
pub lint_name: &'static str,
802+
}
803+
792804
#[derive(Subdiagnostic)]
793805
#[label(mir_build_uncovered)]
794806
pub(crate) struct Uncovered<'tcx> {

compiler/rustc_mir_build/src/thir/pattern/check_match.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,11 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
285285
}
286286
}
287287

288-
fn new_cx(&self, refutability: RefutableFlag) -> MatchCheckCtxt<'p, 'tcx> {
288+
fn new_cx(
289+
&self,
290+
refutability: RefutableFlag,
291+
match_span: Option<Span>,
292+
) -> MatchCheckCtxt<'p, 'tcx> {
289293
let refutable = match refutability {
290294
Irrefutable => false,
291295
Refutable => true,
@@ -295,6 +299,7 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
295299
param_env: self.param_env,
296300
module: self.tcx.parent_module(self.lint_level).to_def_id(),
297301
pattern_arena: &self.pattern_arena,
302+
match_span,
298303
refutable,
299304
}
300305
}
@@ -325,7 +330,7 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
325330
source: hir::MatchSource,
326331
expr_span: Span,
327332
) {
328-
let cx = self.new_cx(Refutable);
333+
let cx = self.new_cx(Refutable, Some(expr_span));
329334

330335
let mut tarms = Vec::with_capacity(arms.len());
331336
for &arm in arms {
@@ -448,7 +453,7 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
448453
pat: &Pat<'tcx>,
449454
refutability: RefutableFlag,
450455
) -> Result<(MatchCheckCtxt<'p, 'tcx>, UsefulnessReport<'p, 'tcx>), ErrorGuaranteed> {
451-
let cx = self.new_cx(refutability);
456+
let cx = self.new_cx(refutability, None);
452457
let pat = self.lower_pattern(&cx, pat)?;
453458
let arms = [MatchArm { pat, hir_id: self.lint_level, has_guard: false }];
454459
let report = compute_match_usefulness(&cx, &arms, self.lint_level, pat.ty(), pat.span());

compiler/rustc_mir_build/src/thir/pattern/usefulness.rs

+48-21
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,10 @@ use super::deconstruct_pat::{
311311
Constructor, ConstructorSet, DeconstructedPat, IntRange, MaybeInfiniteInt, SplitConstructorSet,
312312
WitnessPat,
313313
};
314-
use crate::errors::{NonExhaustiveOmittedPattern, Overlap, OverlappingRangeEndpoints, Uncovered};
314+
use crate::errors::{
315+
NonExhaustiveOmittedPattern, NonExhaustiveOmittedPatternLintOnArm, Overlap,
316+
OverlappingRangeEndpoints, Uncovered,
317+
};
315318

316319
use rustc_data_structures::captures::Captures;
317320

@@ -337,6 +340,8 @@ pub(crate) struct MatchCheckCtxt<'p, 'tcx> {
337340
pub(crate) module: DefId,
338341
pub(crate) param_env: ty::ParamEnv<'tcx>,
339342
pub(crate) pattern_arena: &'p TypedArena<DeconstructedPat<'p, 'tcx>>,
343+
/// The span of the whole match, if applicable.
344+
pub(crate) match_span: Option<Span>,
340345
/// Only produce `NON_EXHAUSTIVE_OMITTED_PATTERNS` lint on refutable patterns.
341346
pub(crate) refutable: bool,
342347
}
@@ -1149,28 +1154,50 @@ pub(crate) fn compute_match_usefulness<'p, 'tcx>(
11491154

11501155
// Run the non_exhaustive_omitted_patterns lint. Only run on refutable patterns to avoid hitting
11511156
// `if let`s. Only run if the match is exhaustive otherwise the error is redundant.
1152-
if cx.refutable
1153-
&& non_exhaustiveness_witnesses.is_empty()
1154-
&& !matches!(
1157+
if cx.refutable && non_exhaustiveness_witnesses.is_empty() {
1158+
if !matches!(
11551159
cx.tcx.lint_level_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, lint_root).0,
11561160
rustc_session::lint::Level::Allow
1157-
)
1158-
{
1159-
let witnesses = collect_nonexhaustive_missing_variants(cx, &pat_column);
1160-
if !witnesses.is_empty() {
1161-
// Report that a match of a `non_exhaustive` enum marked with `non_exhaustive_omitted_patterns`
1162-
// is not exhaustive enough.
1163-
//
1164-
// NB: The partner lint for structs lives in `compiler/rustc_hir_analysis/src/check/pat.rs`.
1165-
cx.tcx.emit_spanned_lint(
1166-
NON_EXHAUSTIVE_OMITTED_PATTERNS,
1167-
lint_root,
1168-
scrut_span,
1169-
NonExhaustiveOmittedPattern {
1170-
scrut_ty,
1171-
uncovered: Uncovered::new(scrut_span, cx, witnesses),
1172-
},
1173-
);
1161+
) {
1162+
let witnesses = collect_nonexhaustive_missing_variants(cx, &pat_column);
1163+
1164+
if !witnesses.is_empty() {
1165+
// Report that a match of a `non_exhaustive` enum marked with `non_exhaustive_omitted_patterns`
1166+
// is not exhaustive enough.
1167+
//
1168+
// NB: The partner lint for structs lives in `compiler/rustc_hir_analysis/src/check/pat.rs`.
1169+
cx.tcx.emit_spanned_lint(
1170+
NON_EXHAUSTIVE_OMITTED_PATTERNS,
1171+
lint_root,
1172+
scrut_span,
1173+
NonExhaustiveOmittedPattern {
1174+
scrut_ty,
1175+
uncovered: Uncovered::new(scrut_span, cx, witnesses),
1176+
},
1177+
);
1178+
}
1179+
} else {
1180+
// We used to allow putting the `#[allow(non_exhaustive_omitted_patterns)]` on a match
1181+
// arm. This no longer makes sense so we warn users, to avoid silently breaking their
1182+
// usage of the lint.
1183+
for arm in arms {
1184+
let (lint_level, lint_level_source) =
1185+
cx.tcx.lint_level_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, arm.hir_id);
1186+
if !matches!(lint_level, rustc_session::lint::Level::Allow) {
1187+
let decorator = NonExhaustiveOmittedPatternLintOnArm {
1188+
lint_span: lint_level_source.span(),
1189+
suggest_lint_on_match: cx.match_span.map(|span| span.shrink_to_lo()),
1190+
lint_level: lint_level.as_str(),
1191+
lint_name: "non_exhaustive_omitted_patterns",
1192+
};
1193+
1194+
use rustc_errors::DecorateLint;
1195+
let mut err = cx.tcx.sess.struct_span_warn(arm.pat.span(), "");
1196+
err.set_primary_message(decorator.msg());
1197+
decorator.decorate_lint(&mut err);
1198+
err.emit();
1199+
}
1200+
}
11741201
}
11751202
}
11761203

src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@ fn repeat() -> ! {
99
}
1010

1111
pub fn f(x: Ordering) {
12+
#[deny(non_exhaustive_omitted_patterns)]
1213
match x {
1314
Ordering::Relaxed => println!("relaxed"),
1415
Ordering::Release => println!("release"),
1516
Ordering::Acquire => println!("acquire"),
1617
Ordering::AcqRel | Ordering::SeqCst => repeat(),
17-
#[deny(non_exhaustive_omitted_patterns)]
1818
_ => repeat(),
1919
}
2020
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
error: some variants are not matched explicitly
2+
--> $DIR/omitted-patterns-dont-lint-on-arm.rs:15:11
3+
|
4+
LL | match val {
5+
| ^^^ pattern `NonExhaustiveEnum::Struct { .. }` not covered
6+
|
7+
= help: ensure that all variants are matched explicitly by adding the suggested match arms
8+
= note: the matched value is of type `NonExhaustiveEnum` and the `non_exhaustive_omitted_patterns` attribute was found
9+
note: the lint level is defined here
10+
--> $DIR/omitted-patterns-dont-lint-on-arm.rs:14:12
11+
|
12+
LL | #[deny(non_exhaustive_omitted_patterns)]
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
14+
15+
error: some variants are not matched explicitly
16+
--> $DIR/omitted-patterns-dont-lint-on-arm.rs:23:11
17+
|
18+
LL | match val {
19+
| ^^^ pattern `NonExhaustiveEnum::Struct { .. }` not covered
20+
|
21+
= help: ensure that all variants are matched explicitly by adding the suggested match arms
22+
= note: the matched value is of type `NonExhaustiveEnum` and the `non_exhaustive_omitted_patterns` attribute was found
23+
note: the lint level is defined here
24+
--> $DIR/omitted-patterns-dont-lint-on-arm.rs:22:27
25+
|
26+
LL | #[cfg_attr(lint, deny(non_exhaustive_omitted_patterns))]
27+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
28+
29+
warning: the lint level must be set on the whole match
30+
--> $DIR/omitted-patterns-dont-lint-on-arm.rs:34:9
31+
|
32+
LL | #[deny(non_exhaustive_omitted_patterns)]
33+
| ------------------------------- remove this attribute
34+
LL | _ => {}
35+
| ^
36+
|
37+
= help: it no longer has any effect to set the lint level on an individual match arm
38+
help: set the lint level on the whole match
39+
|
40+
LL + #[deny(non_exhaustive_omitted_patterns)]
41+
LL | match val {
42+
|
43+
44+
warning: the lint level must be set on the whole match
45+
--> $DIR/omitted-patterns-dont-lint-on-arm.rs:42:9
46+
|
47+
LL | #[cfg_attr(lint, deny(non_exhaustive_omitted_patterns))]
48+
| ------------------------------- remove this attribute
49+
LL | _ => {}
50+
| ^
51+
|
52+
= help: it no longer has any effect to set the lint level on an individual match arm
53+
help: set the lint level on the whole match
54+
|
55+
LL + #[deny(non_exhaustive_omitted_patterns)]
56+
LL | match val {
57+
|
58+
59+
warning: the lint level must be set on the whole match
60+
--> $DIR/omitted-patterns-dont-lint-on-arm.rs:50:9
61+
|
62+
LL | #[cfg_attr(lint, warn(non_exhaustive_omitted_patterns))]
63+
| ------------------------------- remove this attribute
64+
LL | _ => {}
65+
| ^
66+
|
67+
= help: it no longer has any effect to set the lint level on an individual match arm
68+
help: set the lint level on the whole match
69+
|
70+
LL + #[warn(non_exhaustive_omitted_patterns)]
71+
LL | match val {
72+
|
73+
74+
error: aborting due to 2 previous errors; 3 warnings emitted
75+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
error: some variants are not matched explicitly
2+
--> $DIR/omitted-patterns-dont-lint-on-arm.rs:15:11
3+
|
4+
LL | match val {
5+
| ^^^ pattern `NonExhaustiveEnum::Struct { .. }` not covered
6+
|
7+
= help: ensure that all variants are matched explicitly by adding the suggested match arms
8+
= note: the matched value is of type `NonExhaustiveEnum` and the `non_exhaustive_omitted_patterns` attribute was found
9+
note: the lint level is defined here
10+
--> $DIR/omitted-patterns-dont-lint-on-arm.rs:14:12
11+
|
12+
LL | #[deny(non_exhaustive_omitted_patterns)]
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
14+
15+
warning: the lint level must be set on the whole match
16+
--> $DIR/omitted-patterns-dont-lint-on-arm.rs:34:9
17+
|
18+
LL | #[deny(non_exhaustive_omitted_patterns)]
19+
| ------------------------------- remove this attribute
20+
LL | _ => {}
21+
| ^
22+
|
23+
= help: it no longer has any effect to set the lint level on an individual match arm
24+
help: set the lint level on the whole match
25+
|
26+
LL + #[deny(non_exhaustive_omitted_patterns)]
27+
LL | match val {
28+
|
29+
30+
error: aborting due to previous error; 1 warning emitted
31+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// revisions: normal lint
2+
// Test that putting the lint level on a match arm emits a warning, as this was previously
3+
// meaningful and is no longer.
4+
#![feature(non_exhaustive_omitted_patterns_lint)]
5+
6+
// aux-build:enums.rs
7+
extern crate enums;
8+
9+
use enums::NonExhaustiveEnum;
10+
11+
fn main() {
12+
let val = NonExhaustiveEnum::Unit;
13+
14+
#[deny(non_exhaustive_omitted_patterns)]
15+
match val {
16+
//~^ ERROR some variants are not matched explicitly
17+
NonExhaustiveEnum::Unit => {}
18+
NonExhaustiveEnum::Tuple(_) => {}
19+
_ => {}
20+
}
21+
22+
#[cfg_attr(lint, deny(non_exhaustive_omitted_patterns))]
23+
match val {
24+
//[lint]~^ ERROR some variants are not matched explicitly
25+
NonExhaustiveEnum::Unit => {}
26+
NonExhaustiveEnum::Tuple(_) => {}
27+
_ => {}
28+
}
29+
30+
match val {
31+
NonExhaustiveEnum::Unit => {}
32+
NonExhaustiveEnum::Tuple(_) => {}
33+
#[deny(non_exhaustive_omitted_patterns)]
34+
_ => {}
35+
}
36+
//~^^ WARN lint level must be set on the whole match
37+
38+
match val {
39+
NonExhaustiveEnum::Unit => {}
40+
NonExhaustiveEnum::Tuple(_) => {}
41+
#[cfg_attr(lint, deny(non_exhaustive_omitted_patterns))]
42+
_ => {}
43+
}
44+
//[lint]~^^ WARN lint level must be set on the whole match
45+
46+
match val {
47+
NonExhaustiveEnum::Unit => {}
48+
NonExhaustiveEnum::Tuple(_) => {}
49+
#[cfg_attr(lint, warn(non_exhaustive_omitted_patterns))]
50+
_ => {}
51+
}
52+
//[lint]~^^ WARN lint level must be set on the whole match
53+
}

0 commit comments

Comments
 (0)