Auto merge of #17853 - ShoyuVanilla:min-exhaustive-pat, r=ShoyuVanilla
feat: `min-exhaustive-patterns` Resolves #17851
This commit is contained in:
commit
00423bb1d8
@ -1474,9 +1474,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ra-ap-rustc_abi"
|
name = "ra-ap-rustc_abi"
|
||||||
version = "0.53.0"
|
version = "0.63.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "80b1d613eee933486c0613a7bc26e515e46f43adf479d1edd5e537f983e9ce46"
|
checksum = "b011c39d409940a890414e3a7b239762ac16d88029ad71b050a8374831b93790"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.6.0",
|
||||||
"ra-ap-rustc_index",
|
"ra-ap-rustc_index",
|
||||||
@ -1485,9 +1485,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ra-ap-rustc_index"
|
name = "ra-ap-rustc_index"
|
||||||
version = "0.53.0"
|
version = "0.63.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f072060ac77e9e1a02cc20028095993af7e72cc0804779c68bcbf47b16de49c9"
|
checksum = "9027acdee649b0b27eb10b7db5be833efee3362d394935c5eed8f0745a9d43ce"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayvec",
|
"arrayvec",
|
||||||
"ra-ap-rustc_index_macros",
|
"ra-ap-rustc_index_macros",
|
||||||
@ -1496,21 +1496,20 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ra-ap-rustc_index_macros"
|
name = "ra-ap-rustc_index_macros"
|
||||||
version = "0.53.0"
|
version = "0.63.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "82f3d6dcb30a66905388e14756b8f2216131d9f8004922c07f13335840e058d1"
|
checksum = "540b86dc0384141ac8e825fc2874cd44bffd4277d99d8ec63ee416f1a98d5997"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn",
|
||||||
"synstructure",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ra-ap-rustc_lexer"
|
name = "ra-ap-rustc_lexer"
|
||||||
version = "0.53.0"
|
version = "0.63.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dbd8a2b0bdcba9892cbce0b25f6c953d31b0febc1f3420fc692884fce5a23ad8"
|
checksum = "3bdf98bb457b47b9ae4aeebf867d0ca440c86925e0b6381658c4a02589748c9d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-properties",
|
"unicode-properties",
|
||||||
"unicode-xid",
|
"unicode-xid",
|
||||||
@ -1518,9 +1517,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ra-ap-rustc_parse_format"
|
name = "ra-ap-rustc_parse_format"
|
||||||
version = "0.53.0"
|
version = "0.63.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "70dad7a491c2554590222e0c9212dcb7c2e7aceb668875075012a35ea780d135"
|
checksum = "e8fe3556ab6311bb775220563a300e2bf62ec56404521fe0c511a583937683d5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ra-ap-rustc_index",
|
"ra-ap-rustc_index",
|
||||||
"ra-ap-rustc_lexer",
|
"ra-ap-rustc_lexer",
|
||||||
@ -1528,9 +1527,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ra-ap-rustc_pattern_analysis"
|
name = "ra-ap-rustc_pattern_analysis"
|
||||||
version = "0.53.0"
|
version = "0.63.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "34768e1faf88c31f2e9ad57b48318a52b507dafac0cddbf01b5d63bfc0b0a365"
|
checksum = "1709080fdeb5db630e1c2644026c2962aaa32416cd92f0190c04b0c21e114b91"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ra-ap-rustc_index",
|
"ra-ap-rustc_index",
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
|
@ -84,11 +84,11 @@ tt = { path = "./crates/tt", version = "0.0.0" }
|
|||||||
vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" }
|
vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" }
|
||||||
vfs = { path = "./crates/vfs", version = "0.0.0" }
|
vfs = { path = "./crates/vfs", version = "0.0.0" }
|
||||||
|
|
||||||
ra-ap-rustc_lexer = { version = "0.53.0", default-features = false }
|
ra-ap-rustc_lexer = { version = "0.63.0", default-features = false }
|
||||||
ra-ap-rustc_parse_format = { version = "0.53.0", default-features = false }
|
ra-ap-rustc_parse_format = { version = "0.63.0", default-features = false }
|
||||||
ra-ap-rustc_index = { version = "0.53.0", default-features = false }
|
ra-ap-rustc_index = { version = "0.63.0", default-features = false }
|
||||||
ra-ap-rustc_abi = { version = "0.53.0", default-features = false }
|
ra-ap-rustc_abi = { version = "0.63.0", default-features = false }
|
||||||
ra-ap-rustc_pattern_analysis = { version = "0.53.0", default-features = false }
|
ra-ap-rustc_pattern_analysis = { version = "0.63.0", default-features = false }
|
||||||
|
|
||||||
# local crates that aren't published to crates.io. These should not have versions.
|
# local crates that aren't published to crates.io. These should not have versions.
|
||||||
test-fixture = { path = "./crates/test-fixture" }
|
test-fixture = { path = "./crates/test-fixture" }
|
||||||
@ -159,7 +159,6 @@ url = "2.3.1"
|
|||||||
xshell = "0.2.5"
|
xshell = "0.2.5"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# We need to freeze the version of the crate, as the raw-api feature is considered unstable
|
# We need to freeze the version of the crate, as the raw-api feature is considered unstable
|
||||||
dashmap = { version = "=5.5.3", features = ["raw-api"] }
|
dashmap = { version = "=5.5.3", features = ["raw-api"] }
|
||||||
|
|
||||||
|
@ -4,20 +4,25 @@
|
|||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
|
use chalk_solve::rust_ir::AdtKind;
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use hir_def::lang_item::LangItem;
|
use hir_def::{
|
||||||
use hir_def::{resolver::HasResolver, AdtId, AssocItemId, DefWithBodyId, HasModule};
|
lang_item::LangItem,
|
||||||
use hir_def::{ItemContainerId, Lookup};
|
resolver::{HasResolver, ValueNs},
|
||||||
|
AdtId, AssocItemId, DefWithBodyId, HasModule, ItemContainerId, Lookup,
|
||||||
|
};
|
||||||
use intern::sym;
|
use intern::sym;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
use rustc_pattern_analysis::constructor::Constructor;
|
use rustc_pattern_analysis::constructor::Constructor;
|
||||||
use syntax::{ast, AstNode};
|
use syntax::{
|
||||||
|
ast::{self, UnaryOp},
|
||||||
|
AstNode,
|
||||||
|
};
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
use triomphe::Arc;
|
use triomphe::Arc;
|
||||||
use typed_arena::Arena;
|
use typed_arena::Arena;
|
||||||
|
|
||||||
use crate::Interner;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::HirDatabase,
|
db::HirDatabase,
|
||||||
diagnostics::match_check::{
|
diagnostics::match_check::{
|
||||||
@ -25,7 +30,7 @@
|
|||||||
pat_analysis::{self, DeconstructedPat, MatchCheckCtx, WitnessPat},
|
pat_analysis::{self, DeconstructedPat, MatchCheckCtx, WitnessPat},
|
||||||
},
|
},
|
||||||
display::HirDisplay,
|
display::HirDisplay,
|
||||||
InferenceResult, Ty, TyExt,
|
Adjust, InferenceResult, Interner, Ty, TyExt, TyKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) use hir_def::{
|
pub(crate) use hir_def::{
|
||||||
@ -236,7 +241,12 @@ fn validate_match(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let report = match cx.compute_match_usefulness(m_arms.as_slice(), scrut_ty.clone()) {
|
let known_valid_scrutinee = Some(self.is_known_valid_scrutinee(scrutinee_expr, db));
|
||||||
|
let report = match cx.compute_match_usefulness(
|
||||||
|
m_arms.as_slice(),
|
||||||
|
scrut_ty.clone(),
|
||||||
|
known_valid_scrutinee,
|
||||||
|
) {
|
||||||
Ok(report) => report,
|
Ok(report) => report,
|
||||||
Err(()) => return,
|
Err(()) => return,
|
||||||
};
|
};
|
||||||
@ -253,6 +263,45 @@ fn validate_match(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// [rustc's `is_known_valid_scrutinee`](https://github.com/rust-lang/rust/blob/c9bd03cb724e13cca96ad320733046cbdb16fbbe/compiler/rustc_mir_build/src/thir/pattern/check_match.rs#L288)
|
||||||
|
//
|
||||||
|
// While the above function in rustc uses thir exprs, r-a doesn't have them.
|
||||||
|
// So, the logic here is getting same result as "hir lowering + match with lowered thir"
|
||||||
|
// with "hir only"
|
||||||
|
fn is_known_valid_scrutinee(&self, scrutinee_expr: ExprId, db: &dyn HirDatabase) -> bool {
|
||||||
|
if self
|
||||||
|
.infer
|
||||||
|
.expr_adjustments
|
||||||
|
.get(&scrutinee_expr)
|
||||||
|
.is_some_and(|adjusts| adjusts.iter().any(|a| matches!(a.kind, Adjust::Deref(..))))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
match &self.body[scrutinee_expr] {
|
||||||
|
Expr::UnaryOp { op: UnaryOp::Deref, .. } => false,
|
||||||
|
Expr::Path(path) => {
|
||||||
|
let value_or_partial = self
|
||||||
|
.owner
|
||||||
|
.resolver(db.upcast())
|
||||||
|
.resolve_path_in_value_ns_fully(db.upcast(), path);
|
||||||
|
value_or_partial.map_or(true, |v| !matches!(v, ValueNs::StaticId(_)))
|
||||||
|
}
|
||||||
|
Expr::Field { expr, .. } => match self.infer.type_of_expr[*expr].kind(Interner) {
|
||||||
|
TyKind::Adt(adt, ..)
|
||||||
|
if db.adt_datum(self.owner.krate(db.upcast()), *adt).kind == AdtKind::Union =>
|
||||||
|
{
|
||||||
|
false
|
||||||
|
}
|
||||||
|
_ => self.is_known_valid_scrutinee(*expr, db),
|
||||||
|
},
|
||||||
|
Expr::Index { base, .. } => self.is_known_valid_scrutinee(*base, db),
|
||||||
|
Expr::Cast { expr, .. } => self.is_known_valid_scrutinee(*expr, db),
|
||||||
|
Expr::Missing => false,
|
||||||
|
_ => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn validate_block(&mut self, db: &dyn HirDatabase, expr: &Expr) {
|
fn validate_block(&mut self, db: &dyn HirDatabase, expr: &Expr) {
|
||||||
let (Expr::Block { statements, .. }
|
let (Expr::Block { statements, .. }
|
||||||
| Expr::Async { statements, .. }
|
| Expr::Async { statements, .. }
|
||||||
@ -285,7 +334,7 @@ fn validate_block(&mut self, db: &dyn HirDatabase, expr: &Expr) {
|
|||||||
has_guard: false,
|
has_guard: false,
|
||||||
arm_data: (),
|
arm_data: (),
|
||||||
};
|
};
|
||||||
let report = match cx.compute_match_usefulness(&[match_arm], ty.clone()) {
|
let report = match cx.compute_match_usefulness(&[match_arm], ty.clone(), None) {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
debug!(?e, "match usefulness error");
|
debug!(?e, "match usefulness error");
|
||||||
|
@ -69,22 +69,20 @@ pub(crate) struct MatchCheckCtx<'db> {
|
|||||||
body: DefWithBodyId,
|
body: DefWithBodyId,
|
||||||
pub(crate) db: &'db dyn HirDatabase,
|
pub(crate) db: &'db dyn HirDatabase,
|
||||||
exhaustive_patterns: bool,
|
exhaustive_patterns: bool,
|
||||||
min_exhaustive_patterns: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'db> MatchCheckCtx<'db> {
|
impl<'db> MatchCheckCtx<'db> {
|
||||||
pub(crate) fn new(module: ModuleId, body: DefWithBodyId, db: &'db dyn HirDatabase) -> Self {
|
pub(crate) fn new(module: ModuleId, body: DefWithBodyId, db: &'db dyn HirDatabase) -> Self {
|
||||||
let def_map = db.crate_def_map(module.krate());
|
let def_map = db.crate_def_map(module.krate());
|
||||||
let exhaustive_patterns = def_map.is_unstable_feature_enabled(&sym::exhaustive_patterns);
|
let exhaustive_patterns = def_map.is_unstable_feature_enabled(&sym::exhaustive_patterns);
|
||||||
let min_exhaustive_patterns =
|
Self { module, body, db, exhaustive_patterns }
|
||||||
def_map.is_unstable_feature_enabled(&sym::min_exhaustive_patterns);
|
|
||||||
Self { module, body, db, exhaustive_patterns, min_exhaustive_patterns }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn compute_match_usefulness(
|
pub(crate) fn compute_match_usefulness(
|
||||||
&self,
|
&self,
|
||||||
arms: &[MatchArm<'db>],
|
arms: &[MatchArm<'db>],
|
||||||
scrut_ty: Ty,
|
scrut_ty: Ty,
|
||||||
|
known_valid_scrutinee: Option<bool>,
|
||||||
) -> Result<UsefulnessReport<'db, Self>, ()> {
|
) -> Result<UsefulnessReport<'db, Self>, ()> {
|
||||||
if scrut_ty.contains_unknown() {
|
if scrut_ty.contains_unknown() {
|
||||||
return Err(());
|
return Err(());
|
||||||
@ -95,8 +93,7 @@ pub(crate) fn compute_match_usefulness(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Determine place validity correctly. For now, err on the safe side.
|
let place_validity = PlaceValidity::from_bool(known_valid_scrutinee.unwrap_or(true));
|
||||||
let place_validity = PlaceValidity::MaybeInvalid;
|
|
||||||
// Measured to take ~100ms on modern hardware.
|
// Measured to take ~100ms on modern hardware.
|
||||||
let complexity_limit = Some(500000);
|
let complexity_limit = Some(500000);
|
||||||
compute_match_usefulness(self, arms, scrut_ty, place_validity, complexity_limit)
|
compute_match_usefulness(self, arms, scrut_ty, place_validity, complexity_limit)
|
||||||
@ -307,7 +304,8 @@ pub(crate) fn hoist_witness_pat(&self, pat: &WitnessPat<'db>) -> Pat {
|
|||||||
&Str(void) => match void {},
|
&Str(void) => match void {},
|
||||||
Wildcard | NonExhaustive | Hidden | PrivateUninhabited => PatKind::Wild,
|
Wildcard | NonExhaustive | Hidden | PrivateUninhabited => PatKind::Wild,
|
||||||
Never => PatKind::Never,
|
Never => PatKind::Never,
|
||||||
Missing | F32Range(..) | F64Range(..) | Opaque(..) | Or => {
|
Missing | F16Range(..) | F32Range(..) | F64Range(..) | F128Range(..) | Opaque(..)
|
||||||
|
| Or => {
|
||||||
never!("can't convert to pattern: {:?}", pat.ctor());
|
never!("can't convert to pattern: {:?}", pat.ctor());
|
||||||
PatKind::Wild
|
PatKind::Wild
|
||||||
}
|
}
|
||||||
@ -327,9 +325,6 @@ impl<'db> PatCx for MatchCheckCtx<'db> {
|
|||||||
fn is_exhaustive_patterns_feature_on(&self) -> bool {
|
fn is_exhaustive_patterns_feature_on(&self) -> bool {
|
||||||
self.exhaustive_patterns
|
self.exhaustive_patterns
|
||||||
}
|
}
|
||||||
fn is_min_exhaustive_patterns_feature_on(&self) -> bool {
|
|
||||||
self.min_exhaustive_patterns
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ctor_arity(
|
fn ctor_arity(
|
||||||
&self,
|
&self,
|
||||||
@ -356,8 +351,9 @@ fn ctor_arity(
|
|||||||
},
|
},
|
||||||
Ref => 1,
|
Ref => 1,
|
||||||
Slice(..) => unimplemented!(),
|
Slice(..) => unimplemented!(),
|
||||||
Never | Bool(..) | IntRange(..) | F32Range(..) | F64Range(..) | Str(..)
|
Never | Bool(..) | IntRange(..) | F16Range(..) | F32Range(..) | F64Range(..)
|
||||||
| Opaque(..) | NonExhaustive | PrivateUninhabited | Hidden | Missing | Wildcard => 0,
|
| F128Range(..) | Str(..) | Opaque(..) | NonExhaustive | PrivateUninhabited
|
||||||
|
| Hidden | Missing | Wildcard => 0,
|
||||||
Or => {
|
Or => {
|
||||||
never!("The `Or` constructor doesn't have a fixed arity");
|
never!("The `Or` constructor doesn't have a fixed arity");
|
||||||
0
|
0
|
||||||
@ -419,8 +415,9 @@ fn ctor_sub_tys<'a>(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
Slice(_) => unreachable!("Found a `Slice` constructor in match checking"),
|
Slice(_) => unreachable!("Found a `Slice` constructor in match checking"),
|
||||||
Never | Bool(..) | IntRange(..) | F32Range(..) | F64Range(..) | Str(..)
|
Never | Bool(..) | IntRange(..) | F16Range(..) | F32Range(..) | F64Range(..)
|
||||||
| Opaque(..) | NonExhaustive | PrivateUninhabited | Hidden | Missing | Wildcard => {
|
| F128Range(..) | Str(..) | Opaque(..) | NonExhaustive | PrivateUninhabited
|
||||||
|
| Hidden | Missing | Wildcard => {
|
||||||
smallvec![]
|
smallvec![]
|
||||||
}
|
}
|
||||||
Or => {
|
Or => {
|
||||||
|
@ -1032,6 +1032,44 @@ fn exponential_match() {
|
|||||||
check_diagnostics_no_bails(&code);
|
check_diagnostics_no_bails(&code);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn min_exhaustive() {
|
||||||
|
check_diagnostics(
|
||||||
|
r#"
|
||||||
|
//- minicore: result
|
||||||
|
fn test(x: Result<i32, !>) {
|
||||||
|
match x {
|
||||||
|
Ok(_y) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
check_diagnostics(
|
||||||
|
r#"
|
||||||
|
//- minicore: result
|
||||||
|
fn test(ptr: *const Result<i32, !>) {
|
||||||
|
unsafe {
|
||||||
|
match *ptr {
|
||||||
|
//^^^^ error: missing match arm: `Err(!)` not covered
|
||||||
|
Ok(_x) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
check_diagnostics(
|
||||||
|
r#"
|
||||||
|
//- minicore: result
|
||||||
|
fn test(x: Result<i32, &'static !>) {
|
||||||
|
match x {
|
||||||
|
//^ error: missing match arm: `Err(_)` not covered
|
||||||
|
Ok(_y) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
mod rust_unstable {
|
mod rust_unstable {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
@ -80,6 +80,28 @@ fn main() {
|
|||||||
//^^^^ error: non-exhaustive pattern: `Some(_)` not covered
|
//^^^^ error: non-exhaustive pattern: `Some(_)` not covered
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn min_exhaustive() {
|
||||||
|
check_diagnostics(
|
||||||
|
r#"
|
||||||
|
//- minicore: result
|
||||||
|
fn test(x: Result<i32, !>) {
|
||||||
|
let Ok(_y) = x;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
check_diagnostics(
|
||||||
|
r#"
|
||||||
|
//- minicore: result
|
||||||
|
fn test(x: Result<i32, &'static !>) {
|
||||||
|
let Ok(_y) = x;
|
||||||
|
//^^^^^^ error: non-exhaustive pattern: `Err(_)` not covered
|
||||||
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user