Skip to content

Commit 00423bb

Browse files
committed
Auto merge of #17853 - ShoyuVanilla:min-exhaustive-pat, r=ShoyuVanilla
feat: `min-exhaustive-patterns` Resolves #17851
2 parents acc2c5d + 4ea0db9 commit 00423bb

File tree

6 files changed

+150
-46
lines changed

6 files changed

+150
-46
lines changed

src/tools/rust-analyzer/Cargo.lock

+12-13
Original file line numberDiff line numberDiff line change
@@ -1474,9 +1474,9 @@ dependencies = [
14741474

14751475
[[package]]
14761476
name = "ra-ap-rustc_abi"
1477-
version = "0.53.0"
1477+
version = "0.63.0"
14781478
source = "registry+https://github.com/rust-lang/crates.io-index"
1479-
checksum = "80b1d613eee933486c0613a7bc26e515e46f43adf479d1edd5e537f983e9ce46"
1479+
checksum = "b011c39d409940a890414e3a7b239762ac16d88029ad71b050a8374831b93790"
14801480
dependencies = [
14811481
"bitflags 2.6.0",
14821482
"ra-ap-rustc_index",
@@ -1485,9 +1485,9 @@ dependencies = [
14851485

14861486
[[package]]
14871487
name = "ra-ap-rustc_index"
1488-
version = "0.53.0"
1488+
version = "0.63.0"
14891489
source = "registry+https://github.com/rust-lang/crates.io-index"
1490-
checksum = "f072060ac77e9e1a02cc20028095993af7e72cc0804779c68bcbf47b16de49c9"
1490+
checksum = "9027acdee649b0b27eb10b7db5be833efee3362d394935c5eed8f0745a9d43ce"
14911491
dependencies = [
14921492
"arrayvec",
14931493
"ra-ap-rustc_index_macros",
@@ -1496,41 +1496,40 @@ dependencies = [
14961496

14971497
[[package]]
14981498
name = "ra-ap-rustc_index_macros"
1499-
version = "0.53.0"
1499+
version = "0.63.0"
15001500
source = "registry+https://github.com/rust-lang/crates.io-index"
1501-
checksum = "82f3d6dcb30a66905388e14756b8f2216131d9f8004922c07f13335840e058d1"
1501+
checksum = "540b86dc0384141ac8e825fc2874cd44bffd4277d99d8ec63ee416f1a98d5997"
15021502
dependencies = [
15031503
"proc-macro2",
15041504
"quote",
15051505
"syn",
1506-
"synstructure",
15071506
]
15081507

15091508
[[package]]
15101509
name = "ra-ap-rustc_lexer"
1511-
version = "0.53.0"
1510+
version = "0.63.0"
15121511
source = "registry+https://github.com/rust-lang/crates.io-index"
1513-
checksum = "dbd8a2b0bdcba9892cbce0b25f6c953d31b0febc1f3420fc692884fce5a23ad8"
1512+
checksum = "3bdf98bb457b47b9ae4aeebf867d0ca440c86925e0b6381658c4a02589748c9d"
15141513
dependencies = [
15151514
"unicode-properties",
15161515
"unicode-xid",
15171516
]
15181517

15191518
[[package]]
15201519
name = "ra-ap-rustc_parse_format"
1521-
version = "0.53.0"
1520+
version = "0.63.0"
15221521
source = "registry+https://github.com/rust-lang/crates.io-index"
1523-
checksum = "70dad7a491c2554590222e0c9212dcb7c2e7aceb668875075012a35ea780d135"
1522+
checksum = "e8fe3556ab6311bb775220563a300e2bf62ec56404521fe0c511a583937683d5"
15241523
dependencies = [
15251524
"ra-ap-rustc_index",
15261525
"ra-ap-rustc_lexer",
15271526
]
15281527

15291528
[[package]]
15301529
name = "ra-ap-rustc_pattern_analysis"
1531-
version = "0.53.0"
1530+
version = "0.63.0"
15321531
source = "registry+https://github.com/rust-lang/crates.io-index"
1533-
checksum = "34768e1faf88c31f2e9ad57b48318a52b507dafac0cddbf01b5d63bfc0b0a365"
1532+
checksum = "1709080fdeb5db630e1c2644026c2962aaa32416cd92f0190c04b0c21e114b91"
15341533
dependencies = [
15351534
"ra-ap-rustc_index",
15361535
"rustc-hash",

src/tools/rust-analyzer/Cargo.toml

+10-11
Original file line numberDiff line numberDiff line change
@@ -84,11 +84,11 @@ tt = { path = "./crates/tt", version = "0.0.0" }
8484
vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" }
8585
vfs = { path = "./crates/vfs", version = "0.0.0" }
8686

87-
ra-ap-rustc_lexer = { version = "0.53.0", default-features = false }
88-
ra-ap-rustc_parse_format = { version = "0.53.0", default-features = false }
89-
ra-ap-rustc_index = { version = "0.53.0", default-features = false }
90-
ra-ap-rustc_abi = { version = "0.53.0", default-features = false }
91-
ra-ap-rustc_pattern_analysis = { version = "0.53.0", default-features = false }
87+
ra-ap-rustc_lexer = { version = "0.63.0", default-features = false }
88+
ra-ap-rustc_parse_format = { version = "0.63.0", default-features = false }
89+
ra-ap-rustc_index = { version = "0.63.0", default-features = false }
90+
ra-ap-rustc_abi = { version = "0.63.0", default-features = false }
91+
ra-ap-rustc_pattern_analysis = { version = "0.63.0", default-features = false }
9292

9393
# local crates that aren't published to crates.io. These should not have versions.
9494
test-fixture = { path = "./crates/test-fixture" }
@@ -125,11 +125,11 @@ memmap2 = "0.5.4"
125125
nohash-hasher = "0.2.0"
126126
oorandom = "11.1.3"
127127
object = { version = "0.33.0", default-features = false, features = [
128-
"std",
129-
"read_core",
130-
"elf",
131-
"macho",
132-
"pe",
128+
"std",
129+
"read_core",
130+
"elf",
131+
"macho",
132+
"pe",
133133
] }
134134
process-wrap = { version = "8.0.2", features = ["std"] }
135135
pulldown-cmark-to-cmark = "10.0.4"
@@ -159,7 +159,6 @@ url = "2.3.1"
159159
xshell = "0.2.5"
160160

161161

162-
163162
# We need to freeze the version of the crate, as the raw-api feature is considered unstable
164163
dashmap = { version = "=5.5.3", features = ["raw-api"] }
165164

src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs

+57-8
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,33 @@
44
55
use std::fmt;
66

7+
use chalk_solve::rust_ir::AdtKind;
78
use either::Either;
8-
use hir_def::lang_item::LangItem;
9-
use hir_def::{resolver::HasResolver, AdtId, AssocItemId, DefWithBodyId, HasModule};
10-
use hir_def::{ItemContainerId, Lookup};
9+
use hir_def::{
10+
lang_item::LangItem,
11+
resolver::{HasResolver, ValueNs},
12+
AdtId, AssocItemId, DefWithBodyId, HasModule, ItemContainerId, Lookup,
13+
};
1114
use intern::sym;
1215
use itertools::Itertools;
1316
use rustc_hash::FxHashSet;
1417
use rustc_pattern_analysis::constructor::Constructor;
15-
use syntax::{ast, AstNode};
18+
use syntax::{
19+
ast::{self, UnaryOp},
20+
AstNode,
21+
};
1622
use tracing::debug;
1723
use triomphe::Arc;
1824
use typed_arena::Arena;
1925

20-
use crate::Interner;
2126
use crate::{
2227
db::HirDatabase,
2328
diagnostics::match_check::{
2429
self,
2530
pat_analysis::{self, DeconstructedPat, MatchCheckCtx, WitnessPat},
2631
},
2732
display::HirDisplay,
28-
InferenceResult, Ty, TyExt,
33+
Adjust, InferenceResult, Interner, Ty, TyExt, TyKind,
2934
};
3035

3136
pub(crate) use hir_def::{
@@ -236,7 +241,12 @@ impl ExprValidator {
236241
return;
237242
}
238243

239-
let report = match cx.compute_match_usefulness(m_arms.as_slice(), scrut_ty.clone()) {
244+
let known_valid_scrutinee = Some(self.is_known_valid_scrutinee(scrutinee_expr, db));
245+
let report = match cx.compute_match_usefulness(
246+
m_arms.as_slice(),
247+
scrut_ty.clone(),
248+
known_valid_scrutinee,
249+
) {
240250
Ok(report) => report,
241251
Err(()) => return,
242252
};
@@ -253,6 +263,45 @@ impl ExprValidator {
253263
}
254264
}
255265

266+
// [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)
267+
//
268+
// While the above function in rustc uses thir exprs, r-a doesn't have them.
269+
// So, the logic here is getting same result as "hir lowering + match with lowered thir"
270+
// with "hir only"
271+
fn is_known_valid_scrutinee(&self, scrutinee_expr: ExprId, db: &dyn HirDatabase) -> bool {
272+
if self
273+
.infer
274+
.expr_adjustments
275+
.get(&scrutinee_expr)
276+
.is_some_and(|adjusts| adjusts.iter().any(|a| matches!(a.kind, Adjust::Deref(..))))
277+
{
278+
return false;
279+
}
280+
281+
match &self.body[scrutinee_expr] {
282+
Expr::UnaryOp { op: UnaryOp::Deref, .. } => false,
283+
Expr::Path(path) => {
284+
let value_or_partial = self
285+
.owner
286+
.resolver(db.upcast())
287+
.resolve_path_in_value_ns_fully(db.upcast(), path);
288+
value_or_partial.map_or(true, |v| !matches!(v, ValueNs::StaticId(_)))
289+
}
290+
Expr::Field { expr, .. } => match self.infer.type_of_expr[*expr].kind(Interner) {
291+
TyKind::Adt(adt, ..)
292+
if db.adt_datum(self.owner.krate(db.upcast()), *adt).kind == AdtKind::Union =>
293+
{
294+
false
295+
}
296+
_ => self.is_known_valid_scrutinee(*expr, db),
297+
},
298+
Expr::Index { base, .. } => self.is_known_valid_scrutinee(*base, db),
299+
Expr::Cast { expr, .. } => self.is_known_valid_scrutinee(*expr, db),
300+
Expr::Missing => false,
301+
_ => true,
302+
}
303+
}
304+
256305
fn validate_block(&mut self, db: &dyn HirDatabase, expr: &Expr) {
257306
let (Expr::Block { statements, .. }
258307
| Expr::Async { statements, .. }
@@ -285,7 +334,7 @@ impl ExprValidator {
285334
has_guard: false,
286335
arm_data: (),
287336
};
288-
let report = match cx.compute_match_usefulness(&[match_arm], ty.clone()) {
337+
let report = match cx.compute_match_usefulness(&[match_arm], ty.clone(), None) {
289338
Ok(v) => v,
290339
Err(e) => {
291340
debug!(?e, "match usefulness error");

src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs

+11-14
Original file line numberDiff line numberDiff line change
@@ -69,22 +69,20 @@ pub(crate) struct MatchCheckCtx<'db> {
6969
body: DefWithBodyId,
7070
pub(crate) db: &'db dyn HirDatabase,
7171
exhaustive_patterns: bool,
72-
min_exhaustive_patterns: bool,
7372
}
7473

7574
impl<'db> MatchCheckCtx<'db> {
7675
pub(crate) fn new(module: ModuleId, body: DefWithBodyId, db: &'db dyn HirDatabase) -> Self {
7776
let def_map = db.crate_def_map(module.krate());
7877
let exhaustive_patterns = def_map.is_unstable_feature_enabled(&sym::exhaustive_patterns);
79-
let min_exhaustive_patterns =
80-
def_map.is_unstable_feature_enabled(&sym::min_exhaustive_patterns);
81-
Self { module, body, db, exhaustive_patterns, min_exhaustive_patterns }
78+
Self { module, body, db, exhaustive_patterns }
8279
}
8380

8481
pub(crate) fn compute_match_usefulness(
8582
&self,
8683
arms: &[MatchArm<'db>],
8784
scrut_ty: Ty,
85+
known_valid_scrutinee: Option<bool>,
8886
) -> Result<UsefulnessReport<'db, Self>, ()> {
8987
if scrut_ty.contains_unknown() {
9088
return Err(());
@@ -95,8 +93,7 @@ impl<'db> MatchCheckCtx<'db> {
9593
}
9694
}
9795

98-
// FIXME: Determine place validity correctly. For now, err on the safe side.
99-
let place_validity = PlaceValidity::MaybeInvalid;
96+
let place_validity = PlaceValidity::from_bool(known_valid_scrutinee.unwrap_or(true));
10097
// Measured to take ~100ms on modern hardware.
10198
let complexity_limit = Some(500000);
10299
compute_match_usefulness(self, arms, scrut_ty, place_validity, complexity_limit)
@@ -307,7 +304,8 @@ impl<'db> MatchCheckCtx<'db> {
307304
&Str(void) => match void {},
308305
Wildcard | NonExhaustive | Hidden | PrivateUninhabited => PatKind::Wild,
309306
Never => PatKind::Never,
310-
Missing | F32Range(..) | F64Range(..) | Opaque(..) | Or => {
307+
Missing | F16Range(..) | F32Range(..) | F64Range(..) | F128Range(..) | Opaque(..)
308+
| Or => {
311309
never!("can't convert to pattern: {:?}", pat.ctor());
312310
PatKind::Wild
313311
}
@@ -327,9 +325,6 @@ impl<'db> PatCx for MatchCheckCtx<'db> {
327325
fn is_exhaustive_patterns_feature_on(&self) -> bool {
328326
self.exhaustive_patterns
329327
}
330-
fn is_min_exhaustive_patterns_feature_on(&self) -> bool {
331-
self.min_exhaustive_patterns
332-
}
333328

334329
fn ctor_arity(
335330
&self,
@@ -356,8 +351,9 @@ impl<'db> PatCx for MatchCheckCtx<'db> {
356351
},
357352
Ref => 1,
358353
Slice(..) => unimplemented!(),
359-
Never | Bool(..) | IntRange(..) | F32Range(..) | F64Range(..) | Str(..)
360-
| Opaque(..) | NonExhaustive | PrivateUninhabited | Hidden | Missing | Wildcard => 0,
354+
Never | Bool(..) | IntRange(..) | F16Range(..) | F32Range(..) | F64Range(..)
355+
| F128Range(..) | Str(..) | Opaque(..) | NonExhaustive | PrivateUninhabited
356+
| Hidden | Missing | Wildcard => 0,
361357
Or => {
362358
never!("The `Or` constructor doesn't have a fixed arity");
363359
0
@@ -419,8 +415,9 @@ impl<'db> PatCx for MatchCheckCtx<'db> {
419415
}
420416
},
421417
Slice(_) => unreachable!("Found a `Slice` constructor in match checking"),
422-
Never | Bool(..) | IntRange(..) | F32Range(..) | F64Range(..) | Str(..)
423-
| Opaque(..) | NonExhaustive | PrivateUninhabited | Hidden | Missing | Wildcard => {
418+
Never | Bool(..) | IntRange(..) | F16Range(..) | F32Range(..) | F64Range(..)
419+
| F128Range(..) | Str(..) | Opaque(..) | NonExhaustive | PrivateUninhabited
420+
| Hidden | Missing | Wildcard => {
424421
smallvec![]
425422
}
426423
Or => {

src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs

+38
Original file line numberDiff line numberDiff line change
@@ -1032,6 +1032,44 @@ fn f() {
10321032
check_diagnostics_no_bails(&code);
10331033
}
10341034

1035+
#[test]
1036+
fn min_exhaustive() {
1037+
check_diagnostics(
1038+
r#"
1039+
//- minicore: result
1040+
fn test(x: Result<i32, !>) {
1041+
match x {
1042+
Ok(_y) => {}
1043+
}
1044+
}
1045+
"#,
1046+
);
1047+
check_diagnostics(
1048+
r#"
1049+
//- minicore: result
1050+
fn test(ptr: *const Result<i32, !>) {
1051+
unsafe {
1052+
match *ptr {
1053+
//^^^^ error: missing match arm: `Err(!)` not covered
1054+
Ok(_x) => {}
1055+
}
1056+
}
1057+
}
1058+
"#,
1059+
);
1060+
check_diagnostics(
1061+
r#"
1062+
//- minicore: result
1063+
fn test(x: Result<i32, &'static !>) {
1064+
match x {
1065+
//^ error: missing match arm: `Err(_)` not covered
1066+
Ok(_y) => {}
1067+
}
1068+
}
1069+
"#,
1070+
);
1071+
}
1072+
10351073
mod rust_unstable {
10361074
use super::*;
10371075

src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs

+22
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,28 @@ fn main() {
8080
//^^^^ error: non-exhaustive pattern: `Some(_)` not covered
8181
}
8282
}
83+
"#,
84+
);
85+
}
86+
87+
#[test]
88+
fn min_exhaustive() {
89+
check_diagnostics(
90+
r#"
91+
//- minicore: result
92+
fn test(x: Result<i32, !>) {
93+
let Ok(_y) = x;
94+
}
95+
"#,
96+
);
97+
98+
check_diagnostics(
99+
r#"
100+
//- minicore: result
101+
fn test(x: Result<i32, &'static !>) {
102+
let Ok(_y) = x;
103+
//^^^^^^ error: non-exhaustive pattern: `Err(_)` not covered
104+
}
83105
"#,
84106
);
85107
}

0 commit comments

Comments
 (0)