Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit c49123d

Browse files
committedDec 11, 2023
Lint against empty match on not-known-valid place
1 parent a8cc4cf commit c49123d

File tree

11 files changed

+484
-31
lines changed

11 files changed

+484
-31
lines changed
 

‎compiler/rustc_lint_defs/src/builtin.rs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ declare_lint_pass! {
3939
DUPLICATE_MACRO_ATTRIBUTES,
4040
ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT,
4141
ELIDED_LIFETIMES_IN_PATHS,
42+
EMPTY_MATCH_ON_UNSAFE_PLACE,
4243
EXPORTED_PRIVATE_DEPENDENCIES,
4344
FFI_UNWIND_CALLS,
4445
FORBIDDEN_LINT_GROUPS,
@@ -4019,6 +4020,76 @@ declare_lint! {
40194020
@feature_gate = sym::non_exhaustive_omitted_patterns_lint;
40204021
}
40214022

4023+
declare_lint! {
4024+
/// The `empty_match_on_unsafe_place` lint detects uses of `match ... {}` on an empty type where
4025+
/// the matched place could contain invalid data in a well-defined program. These matches are
4026+
/// considered exhaustive for backwards-compatibility, but they shouldn't be since a `_` arm
4027+
/// would be reachable.
4028+
///
4029+
/// This will become an error in the future.
4030+
///
4031+
/// ### Example
4032+
///
4033+
/// ```compile_fail
4034+
/// #![feature(min_exhaustive_patterns)]
4035+
/// enum Void {}
4036+
/// let ptr: *const Void = ...;
4037+
/// unsafe {
4038+
/// match *ptr {}
4039+
/// }
4040+
/// ```
4041+
///
4042+
/// This will produce:
4043+
///
4044+
/// ```text
4045+
/// warning: empty match on potentially-invalid data
4046+
/// --> $DIR/empty-types.rs:157:9
4047+
/// |
4048+
/// LL | match *ptr {}
4049+
/// | ^^^^^^^^^^^^^
4050+
/// |
4051+
/// note: this place can hold invalid data, which would make the match reachable
4052+
/// --> $DIR/empty-types.rs:157:15
4053+
/// |
4054+
/// LL | match *ptr {}
4055+
/// | ^^^^
4056+
/// = note: `#[warn(empty_match_on_unsafe_place)]` on by default
4057+
/// help: consider forcing a read of the value
4058+
/// |
4059+
/// LL | match { *ptr } {}
4060+
/// | + +
4061+
/// ```
4062+
///
4063+
/// ### Explanation
4064+
///
4065+
/// Some place expressions (namely pointer dereferences, union field accesses, and
4066+
/// (conservatively) reference dereferences) can hold invalid data without causing UB. For
4067+
/// example, the following is a well-defined program that prints "reachable!".
4068+
///
4069+
/// ```rust
4070+
/// #[derive(Copy, Clone)]
4071+
/// enum Void {}
4072+
/// union Uninit<T: Copy> {
4073+
/// value: T,
4074+
/// uninit: (),
4075+
/// }
4076+
/// unsafe {
4077+
/// let x: Uninit<Void> = Uninit { uninit: () };
4078+
/// match x.value {
4079+
/// _ => println!("reachable!"),
4080+
/// }
4081+
/// }
4082+
/// ```
4083+
///
4084+
/// Therefore when the matched place can hold invalid data, a match with no arm should not be
4085+
/// considered exhaustive. For backwards-compatibility we consider them exhaustive but warn with
4086+
/// this lint. It will become an error in the future.
4087+
pub EMPTY_MATCH_ON_UNSAFE_PLACE,
4088+
Warn,
4089+
"warn about empty matches on a place with potentially-invalid data",
4090+
@feature_gate = sym::min_exhaustive_patterns;
4091+
}
4092+
40224093
declare_lint! {
40234094
/// The `text_direction_codepoint_in_comment` lint detects Unicode codepoints in comments that
40244095
/// change the visual representation of text on screen in a way that does not correspond to

‎compiler/rustc_pattern_analysis/messages.ftl

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
pattern_analysis_empty_match_on_unsafe_place =
2+
empty match on potentially-invalid data
3+
.note = this place can hold invalid data, which would make the match reachable
4+
5+
pattern_analysis_empty_match_on_unsafe_place_wrap_suggestion =
6+
consider forcing a read of the value
7+
18
pattern_analysis_non_exhaustive_omitted_pattern = some variants are not matched explicitly
29
.help = ensure that all variants are matched explicitly by adding the suggested match arms
310
.note = the matched value is of type `{$scrut_ty}` and the `non_exhaustive_omitted_patterns` attribute was found

‎compiler/rustc_pattern_analysis/src/errors.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,27 @@ impl<'tcx> Uncovered<'tcx> {
4343
}
4444
}
4545

46+
#[derive(LintDiagnostic)]
47+
#[diag(pattern_analysis_empty_match_on_unsafe_place)]
48+
pub struct EmptyMatchOnUnsafePlace {
49+
#[note]
50+
pub scrut_span: Span,
51+
#[subdiagnostic]
52+
pub suggestion: EmptyMatchOnUnsafePlaceWrapSuggestion,
53+
}
54+
55+
#[derive(Subdiagnostic)]
56+
#[multipart_suggestion(
57+
pattern_analysis_empty_match_on_unsafe_place_wrap_suggestion,
58+
applicability = "maybe-incorrect"
59+
)]
60+
pub struct EmptyMatchOnUnsafePlaceWrapSuggestion {
61+
#[suggestion_part(code = "{{ ")]
62+
pub scrut_start: Span,
63+
#[suggestion_part(code = " }}")]
64+
pub scrut_end: Span,
65+
}
66+
4667
#[derive(LintDiagnostic)]
4768
#[diag(pattern_analysis_overlapping_range_endpoints)]
4869
#[note]

‎compiler/rustc_pattern_analysis/src/lib.rs

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,17 @@ extern crate rustc_middle;
1414

1515
rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
1616

17-
use lints::PatternColumn;
1817
use rustc_hir::HirId;
19-
use rustc_middle::ty::Ty;
20-
use usefulness::{compute_match_usefulness, UsefulnessReport};
18+
use rustc_middle::ty::{self, Ty};
19+
use rustc_session::lint;
2120

2221
use crate::cx::MatchCheckCtxt;
23-
use crate::lints::{lint_nonexhaustive_missing_variants, lint_overlapping_range_endpoints};
22+
use crate::errors::{EmptyMatchOnUnsafePlace, EmptyMatchOnUnsafePlaceWrapSuggestion};
23+
use crate::lints::{
24+
lint_nonexhaustive_missing_variants, lint_overlapping_range_endpoints, PatternColumn,
25+
};
2426
use crate::pat::DeconstructedPat;
27+
use crate::usefulness::{compute_match_usefulness, UsefulnessReport};
2528

2629
/// The arm of a match expression.
2730
#[derive(Clone, Copy, Debug)]
@@ -41,6 +44,40 @@ pub fn analyze_match<'p, 'tcx>(
4144
) -> UsefulnessReport<'p, 'tcx> {
4245
let pat_column = PatternColumn::new(arms);
4346

47+
if !cx.known_valid_scrutinee && arms.iter().all(|arm| arm.has_guard) {
48+
let is_directly_empty = match scrut_ty.kind() {
49+
ty::Adt(def, ..) => {
50+
def.is_enum()
51+
&& def.variants().is_empty()
52+
&& !cx.is_foreign_non_exhaustive_enum(scrut_ty)
53+
}
54+
ty::Never => true,
55+
_ => false,
56+
};
57+
if is_directly_empty {
58+
if cx.tcx.features().min_exhaustive_patterns {
59+
cx.tcx.emit_spanned_lint(
60+
lint::builtin::EMPTY_MATCH_ON_UNSAFE_PLACE,
61+
cx.match_lint_level,
62+
cx.whole_match_span.unwrap_or(cx.scrut_span),
63+
EmptyMatchOnUnsafePlace {
64+
scrut_span: cx.scrut_span,
65+
suggestion: EmptyMatchOnUnsafePlaceWrapSuggestion {
66+
scrut_start: cx.scrut_span.shrink_to_lo(),
67+
scrut_end: cx.scrut_span.shrink_to_hi(),
68+
},
69+
},
70+
);
71+
}
72+
73+
// For backwards compability we allow an empty match in this case.
74+
return UsefulnessReport {
75+
arm_usefulness: Vec::new(),
76+
non_exhaustiveness_witnesses: Vec::new(),
77+
};
78+
}
79+
}
80+
4481
let report = compute_match_usefulness(cx, arms, scrut_ty);
4582

4683
// Lint on ranges that overlap on their endpoints, which is likely a mistake.

‎compiler/rustc_pattern_analysis/src/usefulness.rs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1186,23 +1186,19 @@ fn compute_exhaustiveness_and_usefulness<'p, 'tcx>(
11861186

11871187
debug!("ty: {ty:?}");
11881188
let pcx = &PatCtxt { cx, ty, is_top_level };
1189-
let ctors_for_ty = &cx.ctors_for_ty(ty);
1190-
let is_integers = matches!(ctors_for_ty, ConstructorSet::Integers { .. }); // For diagnostics.
11911189

11921190
// Whether the place/column we are inspecting is known to contain valid data.
11931191
let mut place_validity = matrix.place_validity[0];
1194-
if !pcx.cx.tcx.features().min_exhaustive_patterns
1195-
|| (is_top_level && matches!(ctors_for_ty, ConstructorSet::NoConstructors))
1196-
{
1197-
// For backwards compability we allow omitting some empty arms that we ideally shouldn't.
1192+
if cx.tcx.features().exhaustive_patterns {
1193+
// Under `exhaustive_patterns` we allow omitting empty arms even when they aren't redundant.
11981194
place_validity = place_validity.allow_omitting_side_effecting_arms();
11991195
}
12001196

12011197
// Analyze the constructors present in this column.
1198+
let ctors_for_ty = &cx.ctors_for_ty(ty);
1199+
let is_integers = matches!(ctors_for_ty, ConstructorSet::Integers { .. }); // For diagnostics.
12021200
let ctors = matrix.heads().map(|p| p.ctor());
12031201
let split_set = ctors_for_ty.split(pcx, ctors);
1204-
1205-
// Decide what constructors to report.
12061202
let all_missing = split_set.present.is_empty();
12071203

12081204
// Build the set of constructors we will specialize with. It must cover the whole type.

‎tests/ui/pattern/usefulness/empty-types.min_exh_pats.stderr

Lines changed: 232 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,23 @@ error: unreachable pattern
192192
LL | _ => {}
193193
| ^
194194

195+
warning: empty match on potentially-invalid data
196+
--> $DIR/empty-types.rs:157:9
197+
|
198+
LL | match *ref_void {}
199+
| ^^^^^^^^^^^^^^^^^^
200+
|
201+
note: this place can hold invalid data, which would make the match reachable
202+
--> $DIR/empty-types.rs:157:15
203+
|
204+
LL | match *ref_void {}
205+
| ^^^^^^^^^
206+
= note: `#[warn(empty_match_on_unsafe_place)]` on by default
207+
help: consider forcing a read of the value
208+
|
209+
LL | match { *ref_void } {}
210+
| + +
211+
195212
error[E0004]: non-exhaustive patterns: `Some(_)` not covered
196213
--> $DIR/empty-types.rs:162:15
197214
|
@@ -210,6 +227,38 @@ LL ~ None => {},
210227
LL + Some(_) => todo!()
211228
|
212229

230+
warning: empty match on potentially-invalid data
231+
--> $DIR/empty-types.rs:179:9
232+
|
233+
LL | match union_void.value {}
234+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
235+
|
236+
note: this place can hold invalid data, which would make the match reachable
237+
--> $DIR/empty-types.rs:179:15
238+
|
239+
LL | match union_void.value {}
240+
| ^^^^^^^^^^^^^^^^
241+
help: consider forcing a read of the value
242+
|
243+
LL | match { union_void.value } {}
244+
| + +
245+
246+
warning: empty match on potentially-invalid data
247+
--> $DIR/empty-types.rs:184:9
248+
|
249+
LL | match *ptr_void {}
250+
| ^^^^^^^^^^^^^^^^^^
251+
|
252+
note: this place can hold invalid data, which would make the match reachable
253+
--> $DIR/empty-types.rs:184:15
254+
|
255+
LL | match *ptr_void {}
256+
| ^^^^^^^^^
257+
help: consider forcing a read of the value
258+
|
259+
LL | match { *ptr_void } {}
260+
| + +
261+
213262
error: unreachable pattern
214263
--> $DIR/empty-types.rs:205:13
215264
|
@@ -240,6 +289,118 @@ error: unreachable pattern
240289
LL | _ => {}
241290
| ^
242291

292+
warning: empty match on potentially-invalid data
293+
--> $DIR/empty-types.rs:233:9
294+
|
295+
LL | match *ptr_never {}
296+
| ^^^^^^^^^^^^^^^^^^^
297+
|
298+
note: this place can hold invalid data, which would make the match reachable
299+
--> $DIR/empty-types.rs:233:15
300+
|
301+
LL | match *ptr_never {}
302+
| ^^^^^^^^^^
303+
help: consider forcing a read of the value
304+
|
305+
LL | match { *ptr_never } {}
306+
| + +
307+
308+
warning: empty match on potentially-invalid data
309+
--> $DIR/empty-types.rs:238:9
310+
|
311+
LL | match *ref_never {}
312+
| ^^^^^^^^^^^^^^^^^^^
313+
|
314+
note: this place can hold invalid data, which would make the match reachable
315+
--> $DIR/empty-types.rs:238:15
316+
|
317+
LL | match *ref_never {}
318+
| ^^^^^^^^^^
319+
help: consider forcing a read of the value
320+
|
321+
LL | match { *ref_never } {}
322+
| + +
323+
324+
warning: empty match on potentially-invalid data
325+
--> $DIR/empty-types.rs:244:9
326+
|
327+
LL | match ref_x.never {}
328+
| ^^^^^^^^^^^^^^^^^^^^
329+
|
330+
note: this place can hold invalid data, which would make the match reachable
331+
--> $DIR/empty-types.rs:244:15
332+
|
333+
LL | match ref_x.never {}
334+
| ^^^^^^^^^^^
335+
help: consider forcing a read of the value
336+
|
337+
LL | match { ref_x.never } {}
338+
| + +
339+
340+
warning: empty match on potentially-invalid data
341+
--> $DIR/empty-types.rs:250:9
342+
|
343+
LL | match nested_ref_x.0.never {}
344+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
345+
|
346+
note: this place can hold invalid data, which would make the match reachable
347+
--> $DIR/empty-types.rs:250:15
348+
|
349+
LL | match nested_ref_x.0.never {}
350+
| ^^^^^^^^^^^^^^^^^^^^
351+
help: consider forcing a read of the value
352+
|
353+
LL | match { nested_ref_x.0.never } {}
354+
| + +
355+
356+
warning: empty match on potentially-invalid data
357+
--> $DIR/empty-types.rs:255:9
358+
|
359+
LL | match (*ptr_never as Void) {}
360+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
361+
|
362+
note: this place can hold invalid data, which would make the match reachable
363+
--> $DIR/empty-types.rs:255:15
364+
|
365+
LL | match (*ptr_never as Void) {}
366+
| ^^^^^^^^^^^^^^^^^^^^
367+
help: consider forcing a read of the value
368+
|
369+
LL | match { (*ptr_never as Void) } {}
370+
| + +
371+
372+
warning: empty match on potentially-invalid data
373+
--> $DIR/empty-types.rs:261:9
374+
|
375+
LL | match union_never.value {}
376+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
377+
|
378+
note: this place can hold invalid data, which would make the match reachable
379+
--> $DIR/empty-types.rs:261:15
380+
|
381+
LL | match union_never.value {}
382+
| ^^^^^^^^^^^^^^^^^
383+
help: consider forcing a read of the value
384+
|
385+
LL | match { union_never.value } {}
386+
| + +
387+
388+
warning: empty match on potentially-invalid data
389+
--> $DIR/empty-types.rs:267:9
390+
|
391+
LL | match slice_never[0] {}
392+
| ^^^^^^^^^^^^^^^^^^^^^^^
393+
|
394+
note: this place can hold invalid data, which would make the match reachable
395+
--> $DIR/empty-types.rs:267:15
396+
|
397+
LL | match slice_never[0] {}
398+
| ^^^^^^^^^^^^^^
399+
help: consider forcing a read of the value
400+
|
401+
LL | match { slice_never[0] } {}
402+
| + +
403+
243404
error: unreachable pattern
244405
--> $DIR/empty-types.rs:285:9
245406
|
@@ -264,6 +425,38 @@ error: unreachable pattern
264425
LL | Err(_) => {}
265426
| ^^^^^^
266427

428+
warning: empty match on potentially-invalid data
429+
--> $DIR/empty-types.rs:308:5
430+
|
431+
LL | match *x {}
432+
| ^^^^^^^^^^^
433+
|
434+
note: this place can hold invalid data, which would make the match reachable
435+
--> $DIR/empty-types.rs:308:11
436+
|
437+
LL | match *x {}
438+
| ^^
439+
help: consider forcing a read of the value
440+
|
441+
LL | match { *x } {}
442+
| + +
443+
444+
warning: empty match on potentially-invalid data
445+
--> $DIR/empty-types.rs:310:5
446+
|
447+
LL | match *x {}
448+
| ^^^^^^^^^^^
449+
|
450+
note: this place can hold invalid data, which would make the match reachable
451+
--> $DIR/empty-types.rs:310:11
452+
|
453+
LL | match *x {}
454+
| ^^
455+
help: consider forcing a read of the value
456+
|
457+
LL | match { *x } {}
458+
| + +
459+
267460
error[E0004]: non-exhaustive patterns: type `(u32, !)` is non-empty
268461
--> $DIR/empty-types.rs:313:11
269462
|
@@ -490,6 +683,22 @@ LL ~ &None => {},
490683
LL + &Some(_) => todo!()
491684
|
492685

686+
warning: empty match on potentially-invalid data
687+
--> $DIR/empty-types.rs:473:5
688+
|
689+
LL | match *ref_never {}
690+
| ^^^^^^^^^^^^^^^^^^^
691+
|
692+
note: this place can hold invalid data, which would make the match reachable
693+
--> $DIR/empty-types.rs:473:11
694+
|
695+
LL | match *ref_never {}
696+
| ^^^^^^^^^^
697+
help: consider forcing a read of the value
698+
|
699+
LL | match { *ref_never } {}
700+
| + +
701+
493702
error[E0004]: non-exhaustive patterns: `Some(_)` not covered
494703
--> $DIR/empty-types.rs:488:11
495704
|
@@ -582,8 +791,28 @@ error: unreachable pattern
582791
LL | _x if false => {}
583792
| ^^
584793

794+
warning: empty match on potentially-invalid data
795+
--> $DIR/empty-types.rs:629:5
796+
|
797+
LL | / match *ref_never {
798+
LL | |
799+
LL | | // useful, !reachable
800+
LL | | _a if false => {}
801+
LL | | }
802+
| |_____^
803+
|
804+
note: this place can hold invalid data, which would make the match reachable
805+
--> $DIR/empty-types.rs:629:11
806+
|
807+
LL | match *ref_never {
808+
| ^^^^^^^^^^
809+
help: consider forcing a read of the value
810+
|
811+
LL | match { *ref_never } {
812+
| + +
813+
585814
error[E0004]: non-exhaustive patterns: `&_` not covered
586-
--> $DIR/empty-types.rs:633:11
815+
--> $DIR/empty-types.rs:634:11
587816
|
588817
LL | match ref_never {
589818
| ^^^^^^^^^ pattern `&_` not covered
@@ -598,7 +827,7 @@ LL + &_ => todo!()
598827
|
599828

600829
error[E0004]: non-exhaustive patterns: `Some(_)` not covered
601-
--> $DIR/empty-types.rs:661:11
830+
--> $DIR/empty-types.rs:662:11
602831
|
603832
LL | match *x {
604833
| ^^ pattern `Some(_)` not covered
@@ -615,7 +844,7 @@ LL ~ None => {},
615844
LL + Some(_) => todo!()
616845
|
617846

618-
error: aborting due to 63 previous errors
847+
error: aborting due to 63 previous errors; 14 warnings emitted
619848

620849
Some errors have detailed explanations: E0004, E0005.
621850
For more information about an error, try `rustc --explain E0004`.

‎tests/ui/pattern/usefulness/empty-types.normal.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -579,7 +579,7 @@ LL | _x if false => {}
579579
| ^^
580580

581581
error[E0004]: non-exhaustive patterns: `&_` not covered
582-
--> $DIR/empty-types.rs:633:11
582+
--> $DIR/empty-types.rs:634:11
583583
|
584584
LL | match ref_never {
585585
| ^^^^^^^^^ pattern `&_` not covered
@@ -594,7 +594,7 @@ LL + &_ => todo!()
594594
|
595595

596596
error[E0004]: non-exhaustive patterns: `Some(_)` not covered
597-
--> $DIR/empty-types.rs:661:11
597+
--> $DIR/empty-types.rs:662:11
598598
|
599599
LL | match *x {
600600
| ^^ pattern `Some(_)` not covered

‎tests/ui/pattern/usefulness/empty-types.rs

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ fn void_same_as_never(x: NeverBundle) {
154154
}
155155

156156
let ref_void: &Void = &x.void;
157-
match *ref_void {}
157+
match *ref_void {} //[min_exh_pats]~ WARN empty match on potentially-invalid data
158158
match *ref_void {
159159
_ => {}
160160
}
@@ -176,12 +176,12 @@ fn void_same_as_never(x: NeverBundle) {
176176
_a => {}
177177
}
178178
let union_void = Uninit::<Void>::new();
179-
match union_void.value {}
179+
match union_void.value {} //[min_exh_pats]~ WARN empty match on potentially-invalid data
180180
match union_void.value {
181181
_ => {}
182182
}
183183
let ptr_void: *const Void = std::ptr::null();
184-
match *ptr_void {}
184+
match *ptr_void {} //[min_exh_pats]~ WARN empty match on potentially-invalid data
185185
match *ptr_void {
186186
_ => {}
187187
}
@@ -230,41 +230,41 @@ fn invalid_scrutinees(x: NeverBundle) {
230230
// These should be considered !known_valid and not warn unreachable.
231231
unsafe {
232232
// A pointer may point to a place with an invalid value.
233-
match *ptr_never {}
233+
match *ptr_never {} //[min_exh_pats]~ WARN empty match on potentially-invalid data
234234
match *ptr_never {
235235
_ => {}
236236
}
237-
// A reference may point to a place with an invalid value.
238-
match *ref_never {}
237+
// We conservatively assume that a reference may point to a place with an invalid value.
238+
match *ref_never {} //[min_exh_pats]~ WARN empty match on potentially-invalid data
239239
match *ref_never {
240240
_ => {}
241241
}
242242
// This field access is a dereference.
243243
let ref_x: &NeverBundle = &x;
244-
match ref_x.never {}
244+
match ref_x.never {} //[min_exh_pats]~ WARN empty match on potentially-invalid data
245245
match ref_x.never {
246246
_ => {}
247247
}
248248
// This nested field access is a dereference.
249249
let nested_ref_x: &NestedNeverBundle = &nested_x;
250-
match nested_ref_x.0.never {}
250+
match nested_ref_x.0.never {} //[min_exh_pats]~ WARN empty match on potentially-invalid data
251251
match nested_ref_x.0.never {
252252
_ => {}
253253
}
254254
// A cast does not load.
255-
match (*ptr_never as Void) {}
255+
match (*ptr_never as Void) {} //[min_exh_pats]~ WARN empty match on potentially-invalid data
256256
match (*ptr_never as Void) {
257257
_ => {}
258258
}
259259
// A union field may contain invalid data.
260260
let union_never = Uninit::<!>::new();
261-
match union_never.value {}
261+
match union_never.value {} //[min_exh_pats]~ WARN empty match on potentially-invalid data
262262
match union_never.value {
263263
_ => {}
264264
}
265265
// Indexing is like a field access. This one accesses behind a reference.
266266
let slice_never: &[!] = &[];
267-
match slice_never[0] {}
267+
match slice_never[0] {} //[min_exh_pats]~ WARN empty match on potentially-invalid data
268268
match slice_never[0] {
269269
_ => {}
270270
}
@@ -305,9 +305,9 @@ fn nested_validity_tracking(bundle: NeverBundle) {
305305
fn invalid_empty_match(bundle: NeverBundle) {
306306
// We allow these two for backwards-compability.
307307
let x: &! = &bundle.never;
308-
match *x {}
308+
match *x {} //[min_exh_pats]~ WARN empty match on potentially-invalid data
309309
let x: &Void = &bundle.void;
310-
match *x {}
310+
match *x {} //[min_exh_pats]~ WARN empty match on potentially-invalid data
311311

312312
let x: &(u32, !) = &bundle.tuple_half_never;
313313
match *x {} //[normal,min_exh_pats]~ ERROR non-exhaustive
@@ -470,7 +470,7 @@ fn bindings(x: NeverBundle) {
470470
}
471471

472472
// On a !known_valid place.
473-
match *ref_never {}
473+
match *ref_never {} //[min_exh_pats]~ WARN empty match on potentially-invalid data
474474
match *ref_never {
475475
// useful, reachable
476476
_ => {}
@@ -627,6 +627,7 @@ fn guards_and_validity(x: NeverBundle) {
627627
}
628628
// The above still applies to the implicit `_` pattern used for exhaustiveness.
629629
match *ref_never {
630+
//[min_exh_pats]~^ WARN empty match on potentially-invalid data
630631
// useful, !reachable
631632
_a if false => {}
632633
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// run-rustfix
2+
#![feature(min_exhaustive_patterns)]
3+
#![deny(empty_match_on_unsafe_place)]
4+
#![allow(unreachable_code)]
5+
6+
#[derive(Copy, Clone)]
7+
enum Void {}
8+
9+
macro_rules! deref {
10+
($e:expr) => {
11+
{ *$e }
12+
};
13+
}
14+
15+
fn main() {
16+
let ptr: *const Void = std::ptr::null();
17+
unsafe {
18+
match { *ptr } {} //~ ERROR empty match on potentially-invalid data
19+
20+
// rustfix unfortunately changes the macro instead of this expression; we'd need better
21+
// spans.
22+
match deref!(ptr) {} //~ ERROR empty match on potentially-invalid data
23+
}
24+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// run-rustfix
2+
#![feature(min_exhaustive_patterns)]
3+
#![deny(empty_match_on_unsafe_place)]
4+
#![allow(unreachable_code)]
5+
6+
#[derive(Copy, Clone)]
7+
enum Void {}
8+
9+
macro_rules! deref {
10+
($e:expr) => {
11+
*$e
12+
};
13+
}
14+
15+
fn main() {
16+
let ptr: *const Void = std::ptr::null();
17+
unsafe {
18+
match *ptr {} //~ ERROR empty match on potentially-invalid data
19+
20+
// rustfix unfortunately changes the macro instead of this expression; we'd need better
21+
// spans.
22+
match deref!(ptr) {} //~ ERROR empty match on potentially-invalid data
23+
}
24+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
error: empty match on potentially-invalid data
2+
--> $DIR/empty_match_on_unsafe_place.rs:18:9
3+
|
4+
LL | match *ptr {}
5+
| ^^^^^^^^^^^^^
6+
|
7+
note: this place can hold invalid data, which would make the match reachable
8+
--> $DIR/empty_match_on_unsafe_place.rs:18:15
9+
|
10+
LL | match *ptr {}
11+
| ^^^^
12+
note: the lint level is defined here
13+
--> $DIR/empty_match_on_unsafe_place.rs:3:9
14+
|
15+
LL | #![deny(empty_match_on_unsafe_place)]
16+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
17+
help: consider forcing a read of the value
18+
|
19+
LL | match { *ptr } {}
20+
| + +
21+
22+
error: empty match on potentially-invalid data
23+
--> $DIR/empty_match_on_unsafe_place.rs:22:9
24+
|
25+
LL | match deref!(ptr) {}
26+
| ^^^^^^^^^^^^^^^^^^^^
27+
|
28+
note: this place can hold invalid data, which would make the match reachable
29+
--> $DIR/empty_match_on_unsafe_place.rs:11:9
30+
|
31+
LL | *$e
32+
| ^^^
33+
...
34+
LL | match deref!(ptr) {}
35+
| ----------- in this macro invocation
36+
= note: this error originates in the macro `deref` (in Nightly builds, run with -Z macro-backtrace for more info)
37+
help: consider forcing a read of the value
38+
|
39+
LL | { *$e }
40+
| + +
41+
42+
error: aborting due to 2 previous errors
43+

0 commit comments

Comments
 (0)
Please sign in to comment.