about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-08-13 14:18:14 +0000
committerbors <bors@rust-lang.org>2024-08-13 14:18:14 +0000
commit00423bb1d85f3513e534abdb26f54a6966e88d47 (patch)
tree39faab5979504ba59597f8c042164c98842970f4
parentacc2c5d67ac433332b5dfb281645fa1913949be8 (diff)
parent4ea0db907cf3069750b578a1086f61b1241abb0f (diff)
downloadrust-00423bb1d85f3513e534abdb26f54a6966e88d47.tar.gz
rust-00423bb1d85f3513e534abdb26f54a6966e88d47.zip
Auto merge of #17853 - ShoyuVanilla:min-exhaustive-pat, r=ShoyuVanilla
feat: `min-exhaustive-patterns`

Resolves #17851
-rw-r--r--src/tools/rust-analyzer/Cargo.lock25
-rw-r--r--src/tools/rust-analyzer/Cargo.toml21
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs65
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs25
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs38
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs22
6 files changed, 150 insertions, 46 deletions
diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock
index 0fb476de6a3..41dc4405099 100644
--- a/src/tools/rust-analyzer/Cargo.lock
+++ b/src/tools/rust-analyzer/Cargo.lock
@@ -1474,9 +1474,9 @@ dependencies = [
 
 [[package]]
 name = "ra-ap-rustc_abi"
-version = "0.53.0"
+version = "0.63.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "80b1d613eee933486c0613a7bc26e515e46f43adf479d1edd5e537f983e9ce46"
+checksum = "b011c39d409940a890414e3a7b239762ac16d88029ad71b050a8374831b93790"
 dependencies = [
  "bitflags 2.6.0",
  "ra-ap-rustc_index",
@@ -1485,9 +1485,9 @@ dependencies = [
 
 [[package]]
 name = "ra-ap-rustc_index"
-version = "0.53.0"
+version = "0.63.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f072060ac77e9e1a02cc20028095993af7e72cc0804779c68bcbf47b16de49c9"
+checksum = "9027acdee649b0b27eb10b7db5be833efee3362d394935c5eed8f0745a9d43ce"
 dependencies = [
  "arrayvec",
  "ra-ap-rustc_index_macros",
@@ -1496,21 +1496,20 @@ dependencies = [
 
 [[package]]
 name = "ra-ap-rustc_index_macros"
-version = "0.53.0"
+version = "0.63.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "82f3d6dcb30a66905388e14756b8f2216131d9f8004922c07f13335840e058d1"
+checksum = "540b86dc0384141ac8e825fc2874cd44bffd4277d99d8ec63ee416f1a98d5997"
 dependencies = [
  "proc-macro2",
  "quote",
  "syn",
- "synstructure",
 ]
 
 [[package]]
 name = "ra-ap-rustc_lexer"
-version = "0.53.0"
+version = "0.63.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dbd8a2b0bdcba9892cbce0b25f6c953d31b0febc1f3420fc692884fce5a23ad8"
+checksum = "3bdf98bb457b47b9ae4aeebf867d0ca440c86925e0b6381658c4a02589748c9d"
 dependencies = [
  "unicode-properties",
  "unicode-xid",
@@ -1518,9 +1517,9 @@ dependencies = [
 
 [[package]]
 name = "ra-ap-rustc_parse_format"
-version = "0.53.0"
+version = "0.63.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70dad7a491c2554590222e0c9212dcb7c2e7aceb668875075012a35ea780d135"
+checksum = "e8fe3556ab6311bb775220563a300e2bf62ec56404521fe0c511a583937683d5"
 dependencies = [
  "ra-ap-rustc_index",
  "ra-ap-rustc_lexer",
@@ -1528,9 +1527,9 @@ dependencies = [
 
 [[package]]
 name = "ra-ap-rustc_pattern_analysis"
-version = "0.53.0"
+version = "0.63.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34768e1faf88c31f2e9ad57b48318a52b507dafac0cddbf01b5d63bfc0b0a365"
+checksum = "1709080fdeb5db630e1c2644026c2962aaa32416cd92f0190c04b0c21e114b91"
 dependencies = [
  "ra-ap-rustc_index",
  "rustc-hash",
diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml
index 561c533a98d..56e80e11e77 100644
--- a/src/tools/rust-analyzer/Cargo.toml
+++ b/src/tools/rust-analyzer/Cargo.toml
@@ -84,11 +84,11 @@ tt = { path = "./crates/tt", version = "0.0.0" }
 vfs-notify = { path = "./crates/vfs-notify", 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_parse_format = { version = "0.53.0", default-features = false }
-ra-ap-rustc_index = { version = "0.53.0", default-features = false }
-ra-ap-rustc_abi = { version = "0.53.0", default-features = false }
-ra-ap-rustc_pattern_analysis = { 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.63.0", default-features = false }
+ra-ap-rustc_index = { version = "0.63.0", default-features = false }
+ra-ap-rustc_abi = { version = "0.63.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.
 test-fixture = { path = "./crates/test-fixture" }
@@ -125,11 +125,11 @@ memmap2 = "0.5.4"
 nohash-hasher = "0.2.0"
 oorandom = "11.1.3"
 object = { version = "0.33.0", default-features = false, features = [
-    "std",
-    "read_core",
-    "elf",
-    "macho",
-    "pe",
+  "std",
+  "read_core",
+  "elf",
+  "macho",
+  "pe",
 ] }
 process-wrap = { version = "8.0.2", features = ["std"] }
 pulldown-cmark-to-cmark = "10.0.4"
@@ -159,7 +159,6 @@ url = "2.3.1"
 xshell = "0.2.5"
 
 
-
 # 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"] }
 
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs
index 06c9b2e0e5b..6e5a7cce9c9 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs
@@ -4,20 +4,25 @@
 
 use std::fmt;
 
+use chalk_solve::rust_ir::AdtKind;
 use either::Either;
-use hir_def::lang_item::LangItem;
-use hir_def::{resolver::HasResolver, AdtId, AssocItemId, DefWithBodyId, HasModule};
-use hir_def::{ItemContainerId, Lookup};
+use hir_def::{
+    lang_item::LangItem,
+    resolver::{HasResolver, ValueNs},
+    AdtId, AssocItemId, DefWithBodyId, HasModule, ItemContainerId, Lookup,
+};
 use intern::sym;
 use itertools::Itertools;
 use rustc_hash::FxHashSet;
 use rustc_pattern_analysis::constructor::Constructor;
-use syntax::{ast, AstNode};
+use syntax::{
+    ast::{self, UnaryOp},
+    AstNode,
+};
 use tracing::debug;
 use triomphe::Arc;
 use typed_arena::Arena;
 
-use crate::Interner;
 use crate::{
     db::HirDatabase,
     diagnostics::match_check::{
@@ -25,7 +30,7 @@ use crate::{
         pat_analysis::{self, DeconstructedPat, MatchCheckCtx, WitnessPat},
     },
     display::HirDisplay,
-    InferenceResult, Ty, TyExt,
+    Adjust, InferenceResult, Interner, Ty, TyExt, TyKind,
 };
 
 pub(crate) use hir_def::{
@@ -236,7 +241,12 @@ impl ExprValidator {
             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,
             Err(()) => return,
         };
@@ -253,6 +263,45 @@ impl ExprValidator {
         }
     }
 
+    // [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) {
         let (Expr::Block { statements, .. }
         | Expr::Async { statements, .. }
@@ -285,7 +334,7 @@ impl ExprValidator {
                 has_guard: false,
                 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,
                 Err(e) => {
                     debug!(?e, "match usefulness error");
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
index a12e201cf3d..7b3abf501d2 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
@@ -69,22 +69,20 @@ pub(crate) struct MatchCheckCtx<'db> {
     body: DefWithBodyId,
     pub(crate) db: &'db dyn HirDatabase,
     exhaustive_patterns: bool,
-    min_exhaustive_patterns: bool,
 }
 
 impl<'db> MatchCheckCtx<'db> {
     pub(crate) fn new(module: ModuleId, body: DefWithBodyId, db: &'db dyn HirDatabase) -> Self {
         let def_map = db.crate_def_map(module.krate());
         let exhaustive_patterns = def_map.is_unstable_feature_enabled(&sym::exhaustive_patterns);
-        let min_exhaustive_patterns =
-            def_map.is_unstable_feature_enabled(&sym::min_exhaustive_patterns);
-        Self { module, body, db, exhaustive_patterns, min_exhaustive_patterns }
+        Self { module, body, db, exhaustive_patterns }
     }
 
     pub(crate) fn compute_match_usefulness(
         &self,
         arms: &[MatchArm<'db>],
         scrut_ty: Ty,
+        known_valid_scrutinee: Option<bool>,
     ) -> Result<UsefulnessReport<'db, Self>, ()> {
         if scrut_ty.contains_unknown() {
             return Err(());
@@ -95,8 +93,7 @@ impl<'db> MatchCheckCtx<'db> {
             }
         }
 
-        // FIXME: Determine place validity correctly. For now, err on the safe side.
-        let place_validity = PlaceValidity::MaybeInvalid;
+        let place_validity = PlaceValidity::from_bool(known_valid_scrutinee.unwrap_or(true));
         // Measured to take ~100ms on modern hardware.
         let complexity_limit = Some(500000);
         compute_match_usefulness(self, arms, scrut_ty, place_validity, complexity_limit)
@@ -307,7 +304,8 @@ impl<'db> MatchCheckCtx<'db> {
             &Str(void) => match void {},
             Wildcard | NonExhaustive | Hidden | PrivateUninhabited => PatKind::Wild,
             Never => PatKind::Never,
-            Missing | F32Range(..) | F64Range(..) | Opaque(..) | Or => {
+            Missing | F16Range(..) | F32Range(..) | F64Range(..) | F128Range(..) | Opaque(..)
+            | Or => {
                 never!("can't convert to pattern: {:?}", pat.ctor());
                 PatKind::Wild
             }
@@ -327,9 +325,6 @@ impl<'db> PatCx for MatchCheckCtx<'db> {
     fn is_exhaustive_patterns_feature_on(&self) -> bool {
         self.exhaustive_patterns
     }
-    fn is_min_exhaustive_patterns_feature_on(&self) -> bool {
-        self.min_exhaustive_patterns
-    }
 
     fn ctor_arity(
         &self,
@@ -356,8 +351,9 @@ impl<'db> PatCx for MatchCheckCtx<'db> {
             },
             Ref => 1,
             Slice(..) => unimplemented!(),
-            Never | Bool(..) | IntRange(..) | F32Range(..) | F64Range(..) | Str(..)
-            | Opaque(..) | NonExhaustive | PrivateUninhabited | Hidden | Missing | Wildcard => 0,
+            Never | Bool(..) | IntRange(..) | F16Range(..) | F32Range(..) | F64Range(..)
+            | F128Range(..) | Str(..) | Opaque(..) | NonExhaustive | PrivateUninhabited
+            | Hidden | Missing | Wildcard => 0,
             Or => {
                 never!("The `Or` constructor doesn't have a fixed arity");
                 0
@@ -419,8 +415,9 @@ impl<'db> PatCx for MatchCheckCtx<'db> {
                 }
             },
             Slice(_) => unreachable!("Found a `Slice` constructor in match checking"),
-            Never | Bool(..) | IntRange(..) | F32Range(..) | F64Range(..) | Str(..)
-            | Opaque(..) | NonExhaustive | PrivateUninhabited | Hidden | Missing | Wildcard => {
+            Never | Bool(..) | IntRange(..) | F16Range(..) | F32Range(..) | F64Range(..)
+            | F128Range(..) | Str(..) | Opaque(..) | NonExhaustive | PrivateUninhabited
+            | Hidden | Missing | Wildcard => {
                 smallvec![]
             }
             Or => {
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
index 97296278c3a..f39738f2fb8 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
@@ -1032,6 +1032,44 @@ fn f() {
         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 {
         use super::*;
 
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs
index 5a6977c2553..ff1eeb0516a 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs
@@ -83,4 +83,26 @@ fn main() {
 "#,
         );
     }
+
+    #[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
+}
+"#,
+        );
+    }
 }