From 55df8364d64c54e204d31931d45ce15bf651157b Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 10 Dec 2023 18:34:38 +0100 Subject: [PATCH] Lint against empty match on not-known-valid place --- compiler/rustc_lint_defs/src/builtin.rs | 71 ++++++ compiler/rustc_pattern_analysis/messages.ftl | 7 + compiler/rustc_pattern_analysis/src/errors.rs | 21 ++ compiler/rustc_pattern_analysis/src/lib.rs | 57 +++-- .../rustc_pattern_analysis/src/usefulness.rs | 12 +- .../empty-types.min_exh_pats.stderr | 235 +++++++++++++++++- .../usefulness/empty-types.normal.stderr | 4 +- tests/ui/pattern/usefulness/empty-types.rs | 29 +-- .../empty_match_on_unsafe_place.fixed | 24 ++ .../usefulness/empty_match_on_unsafe_place.rs | 24 ++ .../empty_match_on_unsafe_place.stderr | 43 ++++ 11 files changed, 486 insertions(+), 41 deletions(-) create mode 100644 tests/ui/pattern/usefulness/empty_match_on_unsafe_place.fixed create mode 100644 tests/ui/pattern/usefulness/empty_match_on_unsafe_place.rs create mode 100644 tests/ui/pattern/usefulness/empty_match_on_unsafe_place.stderr diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index f9b66239bf9a4..4f7c78794ac08 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -39,6 +39,7 @@ declare_lint_pass! { DUPLICATE_MACRO_ATTRIBUTES, ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT, ELIDED_LIFETIMES_IN_PATHS, + EMPTY_MATCH_ON_UNSAFE_PLACE, EXPORTED_PRIVATE_DEPENDENCIES, FFI_UNWIND_CALLS, FORBIDDEN_LINT_GROUPS, @@ -4018,6 +4019,76 @@ declare_lint! { @feature_gate = sym::non_exhaustive_omitted_patterns_lint; } +declare_lint! { + /// The `empty_match_on_unsafe_place` lint detects uses of `match ... {}` on an empty type where + /// the matched place could contain invalid data in a well-defined program. These matches are + /// considered exhaustive for backwards-compatibility, but they shouldn't be since a `_` arm + /// would be reachable. + /// + /// This will become an error in the future. + /// + /// ### Example + /// + /// ```compile_fail + /// #![feature(min_exhaustive_patterns)] + /// enum Void {} + /// let ptr: *const Void = ...; + /// unsafe { + /// match *ptr {} + /// } + /// ``` + /// + /// This will produce: + /// + /// ```text + /// warning: empty match on potentially-invalid data + /// --> $DIR/empty-types.rs:157:9 + /// | + /// LL | match *ptr {} + /// | ^^^^^^^^^^^^^ + /// | + /// note: this place can hold invalid data, which would make the match reachable + /// --> $DIR/empty-types.rs:157:15 + /// | + /// LL | match *ptr {} + /// | ^^^^ + /// = note: `#[warn(empty_match_on_unsafe_place)]` on by default + /// help: consider forcing a read of the value + /// | + /// LL | match { *ptr } {} + /// | + + + /// ``` + /// + /// ### Explanation + /// + /// Some place expressions (namely pointer dereferences, union field accesses, and + /// (conservatively) reference dereferences) can hold invalid data without causing UB. For + /// example, the following is a well-defined program that prints "reachable!". + /// + /// ```rust + /// #[derive(Copy, Clone)] + /// enum Void {} + /// union Uninit { + /// value: T, + /// uninit: (), + /// } + /// unsafe { + /// let x: Uninit = Uninit { uninit: () }; + /// match x.value { + /// _ => println!("reachable!"), + /// } + /// } + /// ``` + /// + /// Therefore when the matched place can hold invalid data, a match with no arm should not be + /// considered exhaustive. For backwards-compatibility we consider them exhaustive but warn with + /// this lint. It will become an error in the future. + pub EMPTY_MATCH_ON_UNSAFE_PLACE, + Warn, + "warn about empty matches on a place with potentially-invalid data", + @feature_gate = sym::min_exhaustive_patterns; +} + declare_lint! { /// The `text_direction_codepoint_in_comment` lint detects Unicode codepoints in comments that /// change the visual representation of text on screen in a way that does not correspond to diff --git a/compiler/rustc_pattern_analysis/messages.ftl b/compiler/rustc_pattern_analysis/messages.ftl index 827928f97d7cb..99c72e579d08a 100644 --- a/compiler/rustc_pattern_analysis/messages.ftl +++ b/compiler/rustc_pattern_analysis/messages.ftl @@ -1,3 +1,10 @@ +pattern_analysis_empty_match_on_unsafe_place = + empty match on potentially-invalid data + .note = this place can hold invalid data, which would make the match reachable + +pattern_analysis_empty_match_on_unsafe_place_wrap_suggestion = + consider forcing a read of the value + pattern_analysis_non_exhaustive_omitted_pattern = some variants are not matched explicitly .help = ensure that all variants are matched explicitly by adding the suggested match arms .note = the matched value is of type `{$scrut_ty}` and the `non_exhaustive_omitted_patterns` attribute was found diff --git a/compiler/rustc_pattern_analysis/src/errors.rs b/compiler/rustc_pattern_analysis/src/errors.rs index 88770b0c43b37..bbf583817ad95 100644 --- a/compiler/rustc_pattern_analysis/src/errors.rs +++ b/compiler/rustc_pattern_analysis/src/errors.rs @@ -43,6 +43,27 @@ impl<'tcx> Uncovered<'tcx> { } } +#[derive(LintDiagnostic)] +#[diag(pattern_analysis_empty_match_on_unsafe_place)] +pub struct EmptyMatchOnUnsafePlace { + #[note] + pub scrut_span: Span, + #[subdiagnostic] + pub suggestion: EmptyMatchOnUnsafePlaceWrapSuggestion, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion( + pattern_analysis_empty_match_on_unsafe_place_wrap_suggestion, + applicability = "maybe-incorrect" +)] +pub struct EmptyMatchOnUnsafePlaceWrapSuggestion { + #[suggestion_part(code = "{{ ")] + pub scrut_start: Span, + #[suggestion_part(code = " }}")] + pub scrut_end: Span, +} + #[derive(LintDiagnostic)] #[diag(pattern_analysis_overlapping_range_endpoints)] #[note] diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs index b40e1371931a5..119b159239ea3 100644 --- a/compiler/rustc_pattern_analysis/src/lib.rs +++ b/compiler/rustc_pattern_analysis/src/lib.rs @@ -26,15 +26,7 @@ use rustc_index::Idx; use rustc_middle::ty::Ty; use crate::constructor::{Constructor, ConstructorSet}; -#[cfg(feature = "rustc")] -use crate::lints::{ - lint_nonexhaustive_missing_variants, lint_overlapping_range_endpoints, PatternColumn, -}; use crate::pat::DeconstructedPat; -#[cfg(feature = "rustc")] -use crate::rustc::RustcMatchCheckCtxt; -#[cfg(feature = "rustc")] -use crate::usefulness::{compute_match_usefulness, ValidityConstraint}; // It's not possible to only enable the `typed_arena` dependency when the `rustc` feature is off, so // we use another feature instead. The crate won't compile if one of these isn't enabled. @@ -110,26 +102,63 @@ impl<'p, Cx: TypeCx> Copy for MatchArm<'p, Cx> {} /// useful, and runs some lints. #[cfg(feature = "rustc")] pub fn analyze_match<'p, 'tcx>( - tycx: &RustcMatchCheckCtxt<'p, 'tcx>, + tycx: &rustc::RustcMatchCheckCtxt<'p, 'tcx>, arms: &[rustc::MatchArm<'p, 'tcx>], scrut_ty: Ty<'tcx>, ) -> rustc::UsefulnessReport<'p, 'tcx> { + use rustc_middle::ty; + use rustc_session::lint; + // Arena to store the extra wildcards we construct during analysis. let wildcard_arena = tycx.pattern_arena; - let scrut_validity = ValidityConstraint::from_bool(tycx.known_valid_scrutinee); + let scrut_validity = usefulness::ValidityConstraint::from_bool(tycx.known_valid_scrutinee); let cx = MatchCtxt { tycx, wildcard_arena }; - let report = compute_match_usefulness(cx, arms, scrut_ty, scrut_validity); + if !tycx.known_valid_scrutinee && arms.iter().all(|arm| arm.has_guard) { + let is_directly_empty = match scrut_ty.kind() { + ty::Adt(def, ..) => { + def.is_enum() + && def.variants().is_empty() + && !tycx.is_foreign_non_exhaustive_enum(scrut_ty) + } + ty::Never => true, + _ => false, + }; + if is_directly_empty { + if tycx.tcx.features().min_exhaustive_patterns { + tycx.tcx.emit_spanned_lint( + lint::builtin::EMPTY_MATCH_ON_UNSAFE_PLACE, + tycx.match_lint_level, + tycx.whole_match_span.unwrap_or(tycx.scrut_span), + errors::EmptyMatchOnUnsafePlace { + scrut_span: tycx.scrut_span, + suggestion: errors::EmptyMatchOnUnsafePlaceWrapSuggestion { + scrut_start: tycx.scrut_span.shrink_to_lo(), + scrut_end: tycx.scrut_span.shrink_to_hi(), + }, + }, + ); + } + + // For backwards compability we allow an empty match in this case. + return rustc::UsefulnessReport { + arm_usefulness: Vec::new(), + non_exhaustiveness_witnesses: Vec::new(), + }; + } + } + + let report = usefulness::compute_match_usefulness(cx, arms, scrut_ty, scrut_validity); - let pat_column = PatternColumn::new(arms); + let pat_column = lints::PatternColumn::new(arms); // Lint on ranges that overlap on their endpoints, which is likely a mistake. - lint_overlapping_range_endpoints(cx, &pat_column); + lints::lint_overlapping_range_endpoints(cx, &pat_column); // Run the non_exhaustive_omitted_patterns lint. Only run on refutable patterns to avoid hitting // `if let`s. Only run if the match is exhaustive otherwise the error is redundant. if tycx.refutable && report.non_exhaustiveness_witnesses.is_empty() { - lint_nonexhaustive_missing_variants(cx, arms, &pat_column, scrut_ty) + lints::lint_nonexhaustive_missing_variants(cx, arms, &pat_column, scrut_ty) } report diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs index 292f656e954c2..904a488fdb5e4 100644 --- a/compiler/rustc_pattern_analysis/src/usefulness.rs +++ b/compiler/rustc_pattern_analysis/src/usefulness.rs @@ -1199,23 +1199,19 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>( debug!("ty: {ty:?}"); let pcx = &PlaceCtxt { mcx, ty, is_scrutinee: is_top_level }; - let ctors_for_ty = pcx.ctors_for_ty(); - let is_integers = matches!(ctors_for_ty, ConstructorSet::Integers { .. }); // For diagnostics. // Whether the place/column we are inspecting is known to contain valid data. let mut place_validity = matrix.place_validity[0]; - if !mcx.tycx.is_min_exhaustive_patterns_feature_on() - || (is_top_level && matches!(ctors_for_ty, ConstructorSet::NoConstructors)) - { - // For backwards compability we allow omitting some empty arms that we ideally shouldn't. + if mcx.tycx.is_exhaustive_patterns_feature_on() { + // Under `exhaustive_patterns` we allow omitting empty arms even when they aren't redundant. place_validity = place_validity.allow_omitting_side_effecting_arms(); } // Analyze the constructors present in this column. + let ctors_for_ty = pcx.ctors_for_ty(); + let is_integers = matches!(ctors_for_ty, ConstructorSet::Integers { .. }); // For diagnostics. let ctors = matrix.heads().map(|p| p.ctor()); let split_set = ctors_for_ty.split(pcx, ctors); - - // Decide what constructors to report. let all_missing = split_set.present.is_empty(); // Build the set of constructors we will specialize with. It must cover the whole type. diff --git a/tests/ui/pattern/usefulness/empty-types.min_exh_pats.stderr b/tests/ui/pattern/usefulness/empty-types.min_exh_pats.stderr index 98eac28201515..ea8cc6e912245 100644 --- a/tests/ui/pattern/usefulness/empty-types.min_exh_pats.stderr +++ b/tests/ui/pattern/usefulness/empty-types.min_exh_pats.stderr @@ -192,6 +192,23 @@ error: unreachable pattern LL | _ => {} | ^ +warning: empty match on potentially-invalid data + --> $DIR/empty-types.rs:157:9 + | +LL | match *ref_void {} + | ^^^^^^^^^^^^^^^^^^ + | +note: this place can hold invalid data, which would make the match reachable + --> $DIR/empty-types.rs:157:15 + | +LL | match *ref_void {} + | ^^^^^^^^^ + = note: `#[warn(empty_match_on_unsafe_place)]` on by default +help: consider forcing a read of the value + | +LL | match { *ref_void } {} + | + + + error[E0004]: non-exhaustive patterns: `Some(_)` not covered --> $DIR/empty-types.rs:162:15 | @@ -210,6 +227,38 @@ LL ~ None => {}, LL + Some(_) => todo!() | +warning: empty match on potentially-invalid data + --> $DIR/empty-types.rs:179:9 + | +LL | match union_void.value {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: this place can hold invalid data, which would make the match reachable + --> $DIR/empty-types.rs:179:15 + | +LL | match union_void.value {} + | ^^^^^^^^^^^^^^^^ +help: consider forcing a read of the value + | +LL | match { union_void.value } {} + | + + + +warning: empty match on potentially-invalid data + --> $DIR/empty-types.rs:184:9 + | +LL | match *ptr_void {} + | ^^^^^^^^^^^^^^^^^^ + | +note: this place can hold invalid data, which would make the match reachable + --> $DIR/empty-types.rs:184:15 + | +LL | match *ptr_void {} + | ^^^^^^^^^ +help: consider forcing a read of the value + | +LL | match { *ptr_void } {} + | + + + error: unreachable pattern --> $DIR/empty-types.rs:205:13 | @@ -240,6 +289,118 @@ error: unreachable pattern LL | _ => {} | ^ +warning: empty match on potentially-invalid data + --> $DIR/empty-types.rs:233:9 + | +LL | match *ptr_never {} + | ^^^^^^^^^^^^^^^^^^^ + | +note: this place can hold invalid data, which would make the match reachable + --> $DIR/empty-types.rs:233:15 + | +LL | match *ptr_never {} + | ^^^^^^^^^^ +help: consider forcing a read of the value + | +LL | match { *ptr_never } {} + | + + + +warning: empty match on potentially-invalid data + --> $DIR/empty-types.rs:238:9 + | +LL | match *ref_never {} + | ^^^^^^^^^^^^^^^^^^^ + | +note: this place can hold invalid data, which would make the match reachable + --> $DIR/empty-types.rs:238:15 + | +LL | match *ref_never {} + | ^^^^^^^^^^ +help: consider forcing a read of the value + | +LL | match { *ref_never } {} + | + + + +warning: empty match on potentially-invalid data + --> $DIR/empty-types.rs:244:9 + | +LL | match ref_x.never {} + | ^^^^^^^^^^^^^^^^^^^^ + | +note: this place can hold invalid data, which would make the match reachable + --> $DIR/empty-types.rs:244:15 + | +LL | match ref_x.never {} + | ^^^^^^^^^^^ +help: consider forcing a read of the value + | +LL | match { ref_x.never } {} + | + + + +warning: empty match on potentially-invalid data + --> $DIR/empty-types.rs:250:9 + | +LL | match nested_ref_x.0.never {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: this place can hold invalid data, which would make the match reachable + --> $DIR/empty-types.rs:250:15 + | +LL | match nested_ref_x.0.never {} + | ^^^^^^^^^^^^^^^^^^^^ +help: consider forcing a read of the value + | +LL | match { nested_ref_x.0.never } {} + | + + + +warning: empty match on potentially-invalid data + --> $DIR/empty-types.rs:255:9 + | +LL | match (*ptr_never as Void) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: this place can hold invalid data, which would make the match reachable + --> $DIR/empty-types.rs:255:15 + | +LL | match (*ptr_never as Void) {} + | ^^^^^^^^^^^^^^^^^^^^ +help: consider forcing a read of the value + | +LL | match { (*ptr_never as Void) } {} + | + + + +warning: empty match on potentially-invalid data + --> $DIR/empty-types.rs:261:9 + | +LL | match union_never.value {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: this place can hold invalid data, which would make the match reachable + --> $DIR/empty-types.rs:261:15 + | +LL | match union_never.value {} + | ^^^^^^^^^^^^^^^^^ +help: consider forcing a read of the value + | +LL | match { union_never.value } {} + | + + + +warning: empty match on potentially-invalid data + --> $DIR/empty-types.rs:267:9 + | +LL | match slice_never[0] {} + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +note: this place can hold invalid data, which would make the match reachable + --> $DIR/empty-types.rs:267:15 + | +LL | match slice_never[0] {} + | ^^^^^^^^^^^^^^ +help: consider forcing a read of the value + | +LL | match { slice_never[0] } {} + | + + + error: unreachable pattern --> $DIR/empty-types.rs:285:9 | @@ -264,6 +425,38 @@ error: unreachable pattern LL | Err(_) => {} | ^^^^^^ +warning: empty match on potentially-invalid data + --> $DIR/empty-types.rs:308:5 + | +LL | match *x {} + | ^^^^^^^^^^^ + | +note: this place can hold invalid data, which would make the match reachable + --> $DIR/empty-types.rs:308:11 + | +LL | match *x {} + | ^^ +help: consider forcing a read of the value + | +LL | match { *x } {} + | + + + +warning: empty match on potentially-invalid data + --> $DIR/empty-types.rs:310:5 + | +LL | match *x {} + | ^^^^^^^^^^^ + | +note: this place can hold invalid data, which would make the match reachable + --> $DIR/empty-types.rs:310:11 + | +LL | match *x {} + | ^^ +help: consider forcing a read of the value + | +LL | match { *x } {} + | + + + error[E0004]: non-exhaustive patterns: type `(u32, !)` is non-empty --> $DIR/empty-types.rs:313:11 | @@ -490,6 +683,22 @@ LL ~ &None => {}, LL + &Some(_) => todo!() | +warning: empty match on potentially-invalid data + --> $DIR/empty-types.rs:473:5 + | +LL | match *ref_never {} + | ^^^^^^^^^^^^^^^^^^^ + | +note: this place can hold invalid data, which would make the match reachable + --> $DIR/empty-types.rs:473:11 + | +LL | match *ref_never {} + | ^^^^^^^^^^ +help: consider forcing a read of the value + | +LL | match { *ref_never } {} + | + + + error[E0004]: non-exhaustive patterns: `Some(_)` not covered --> $DIR/empty-types.rs:488:11 | @@ -582,8 +791,28 @@ error: unreachable pattern LL | _x if false => {} | ^^ +warning: empty match on potentially-invalid data + --> $DIR/empty-types.rs:629:5 + | +LL | / match *ref_never { +LL | | +LL | | // useful, !reachable +LL | | _a if false => {} +LL | | } + | |_____^ + | +note: this place can hold invalid data, which would make the match reachable + --> $DIR/empty-types.rs:629:11 + | +LL | match *ref_never { + | ^^^^^^^^^^ +help: consider forcing a read of the value + | +LL | match { *ref_never } { + | + + + error[E0004]: non-exhaustive patterns: `&_` not covered - --> $DIR/empty-types.rs:633:11 + --> $DIR/empty-types.rs:634:11 | LL | match ref_never { | ^^^^^^^^^ pattern `&_` not covered @@ -598,7 +827,7 @@ LL + &_ => todo!() | error[E0004]: non-exhaustive patterns: `Some(_)` not covered - --> $DIR/empty-types.rs:661:11 + --> $DIR/empty-types.rs:662:11 | LL | match *x { | ^^ pattern `Some(_)` not covered @@ -615,7 +844,7 @@ LL ~ None => {}, LL + Some(_) => todo!() | -error: aborting due to 63 previous errors +error: aborting due to 63 previous errors; 14 warnings emitted Some errors have detailed explanations: E0004, E0005. For more information about an error, try `rustc --explain E0004`. diff --git a/tests/ui/pattern/usefulness/empty-types.normal.stderr b/tests/ui/pattern/usefulness/empty-types.normal.stderr index 178e50a7db93f..fb41d2482f860 100644 --- a/tests/ui/pattern/usefulness/empty-types.normal.stderr +++ b/tests/ui/pattern/usefulness/empty-types.normal.stderr @@ -579,7 +579,7 @@ LL | _x if false => {} | ^^ error[E0004]: non-exhaustive patterns: `&_` not covered - --> $DIR/empty-types.rs:633:11 + --> $DIR/empty-types.rs:634:11 | LL | match ref_never { | ^^^^^^^^^ pattern `&_` not covered @@ -594,7 +594,7 @@ LL + &_ => todo!() | error[E0004]: non-exhaustive patterns: `Some(_)` not covered - --> $DIR/empty-types.rs:661:11 + --> $DIR/empty-types.rs:662:11 | LL | match *x { | ^^ pattern `Some(_)` not covered diff --git a/tests/ui/pattern/usefulness/empty-types.rs b/tests/ui/pattern/usefulness/empty-types.rs index 1349aa1ddba7c..004eac9a9cdff 100644 --- a/tests/ui/pattern/usefulness/empty-types.rs +++ b/tests/ui/pattern/usefulness/empty-types.rs @@ -155,7 +155,7 @@ fn void_same_as_never(x: NeverBundle) { } let ref_void: &Void = &x.void; - match *ref_void {} + match *ref_void {} //[min_exh_pats]~ WARN empty match on potentially-invalid data match *ref_void { _ => {} } @@ -177,12 +177,12 @@ fn void_same_as_never(x: NeverBundle) { _a => {} } let union_void = Uninit::::new(); - match union_void.value {} + match union_void.value {} //[min_exh_pats]~ WARN empty match on potentially-invalid data match union_void.value { _ => {} } let ptr_void: *const Void = std::ptr::null(); - match *ptr_void {} + match *ptr_void {} //[min_exh_pats]~ WARN empty match on potentially-invalid data match *ptr_void { _ => {} } @@ -231,41 +231,41 @@ fn invalid_scrutinees(x: NeverBundle) { // These should be considered !known_valid and not warn unreachable. unsafe { // A pointer may point to a place with an invalid value. - match *ptr_never {} + match *ptr_never {} //[min_exh_pats]~ WARN empty match on potentially-invalid data match *ptr_never { _ => {} } - // A reference may point to a place with an invalid value. - match *ref_never {} + // We conservatively assume that a reference may point to a place with an invalid value. + match *ref_never {} //[min_exh_pats]~ WARN empty match on potentially-invalid data match *ref_never { _ => {} } // This field access is a dereference. let ref_x: &NeverBundle = &x; - match ref_x.never {} + match ref_x.never {} //[min_exh_pats]~ WARN empty match on potentially-invalid data match ref_x.never { _ => {} } // This nested field access is a dereference. let nested_ref_x: &NestedNeverBundle = &nested_x; - match nested_ref_x.0.never {} + match nested_ref_x.0.never {} //[min_exh_pats]~ WARN empty match on potentially-invalid data match nested_ref_x.0.never { _ => {} } // A cast does not load. - match (*ptr_never as Void) {} + match (*ptr_never as Void) {} //[min_exh_pats]~ WARN empty match on potentially-invalid data match (*ptr_never as Void) { _ => {} } // A union field may contain invalid data. let union_never = Uninit::::new(); - match union_never.value {} + match union_never.value {} //[min_exh_pats]~ WARN empty match on potentially-invalid data match union_never.value { _ => {} } // Indexing is like a field access. This one accesses behind a reference. let slice_never: &[!] = &[]; - match slice_never[0] {} + match slice_never[0] {} //[min_exh_pats]~ WARN empty match on potentially-invalid data match slice_never[0] { _ => {} } @@ -306,9 +306,9 @@ fn nested_validity_tracking(bundle: NeverBundle) { fn invalid_empty_match(bundle: NeverBundle) { // We allow these two for backwards-compability. let x: &! = &bundle.never; - match *x {} + match *x {} //[min_exh_pats]~ WARN empty match on potentially-invalid data let x: &Void = &bundle.void; - match *x {} + match *x {} //[min_exh_pats]~ WARN empty match on potentially-invalid data let x: &(u32, !) = &bundle.tuple_half_never; match *x {} //[normal,min_exh_pats]~ ERROR non-exhaustive @@ -471,7 +471,7 @@ fn bindings(x: NeverBundle) { } // On a !known_valid place. - match *ref_never {} + match *ref_never {} //[min_exh_pats]~ WARN empty match on potentially-invalid data match *ref_never { // useful, reachable _ => {} @@ -628,6 +628,7 @@ fn guards_and_validity(x: NeverBundle) { } // The above still applies to the implicit `_` pattern used for exhaustiveness. match *ref_never { + //[min_exh_pats]~^ WARN empty match on potentially-invalid data // useful, !reachable _a if false => {} } diff --git a/tests/ui/pattern/usefulness/empty_match_on_unsafe_place.fixed b/tests/ui/pattern/usefulness/empty_match_on_unsafe_place.fixed new file mode 100644 index 0000000000000..f047f652e27e0 --- /dev/null +++ b/tests/ui/pattern/usefulness/empty_match_on_unsafe_place.fixed @@ -0,0 +1,24 @@ +// run-rustfix +#![feature(min_exhaustive_patterns)] +#![deny(empty_match_on_unsafe_place)] +#![allow(unreachable_code)] + +#[derive(Copy, Clone)] +enum Void {} + +macro_rules! deref { + ($e:expr) => { + { *$e } + }; +} + +fn main() { + let ptr: *const Void = std::ptr::null(); + unsafe { + match { *ptr } {} //~ ERROR empty match on potentially-invalid data + + // rustfix unfortunately changes the macro instead of this expression; we'd need better + // spans. + match deref!(ptr) {} //~ ERROR empty match on potentially-invalid data + } +} diff --git a/tests/ui/pattern/usefulness/empty_match_on_unsafe_place.rs b/tests/ui/pattern/usefulness/empty_match_on_unsafe_place.rs new file mode 100644 index 0000000000000..38f44ac9e3481 --- /dev/null +++ b/tests/ui/pattern/usefulness/empty_match_on_unsafe_place.rs @@ -0,0 +1,24 @@ +// run-rustfix +#![feature(min_exhaustive_patterns)] +#![deny(empty_match_on_unsafe_place)] +#![allow(unreachable_code)] + +#[derive(Copy, Clone)] +enum Void {} + +macro_rules! deref { + ($e:expr) => { + *$e + }; +} + +fn main() { + let ptr: *const Void = std::ptr::null(); + unsafe { + match *ptr {} //~ ERROR empty match on potentially-invalid data + + // rustfix unfortunately changes the macro instead of this expression; we'd need better + // spans. + match deref!(ptr) {} //~ ERROR empty match on potentially-invalid data + } +} diff --git a/tests/ui/pattern/usefulness/empty_match_on_unsafe_place.stderr b/tests/ui/pattern/usefulness/empty_match_on_unsafe_place.stderr new file mode 100644 index 0000000000000..de3d7d3a917f2 --- /dev/null +++ b/tests/ui/pattern/usefulness/empty_match_on_unsafe_place.stderr @@ -0,0 +1,43 @@ +error: empty match on potentially-invalid data + --> $DIR/empty_match_on_unsafe_place.rs:18:9 + | +LL | match *ptr {} + | ^^^^^^^^^^^^^ + | +note: this place can hold invalid data, which would make the match reachable + --> $DIR/empty_match_on_unsafe_place.rs:18:15 + | +LL | match *ptr {} + | ^^^^ +note: the lint level is defined here + --> $DIR/empty_match_on_unsafe_place.rs:3:9 + | +LL | #![deny(empty_match_on_unsafe_place)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider forcing a read of the value + | +LL | match { *ptr } {} + | + + + +error: empty match on potentially-invalid data + --> $DIR/empty_match_on_unsafe_place.rs:22:9 + | +LL | match deref!(ptr) {} + | ^^^^^^^^^^^^^^^^^^^^ + | +note: this place can hold invalid data, which would make the match reachable + --> $DIR/empty_match_on_unsafe_place.rs:11:9 + | +LL | *$e + | ^^^ +... +LL | match deref!(ptr) {} + | ----------- in this macro invocation + = note: this error originates in the macro `deref` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider forcing a read of the value + | +LL | { *$e } + | + + + +error: aborting due to 2 previous errors +