Repository: rust-analyzer/rust-analyzer
Pull request #7020 (2020-12-23 11:27:18)

Implement const pat and expr inference


Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 0e80b95301..891cff55e3 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -168,9 +168,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 [[package]]
 name = "chalk-derive"
-version = "0.45.0"
+version = "0.43.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec7dacf94958d1a930b95d049d9443860859af59eadc77849392093eb577bcee"
+checksum = "e2d9e0c8adcced1ab0fea5cb8a38647922893d5b495e363e1814299fd380469b"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -180,20 +180,19 @@ dependencies = [
 
 [[package]]
 name = "chalk-ir"
-version = "0.45.0"
+version = "0.43.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1a5b38ede247def17da87f4badb62396a5753db6048e2011d3089d8b3796c67"
+checksum = "c5218266a5709bc4943de997e64d3fab41c9e9f68efd54a898de53135e987bd3"
 dependencies = [
- "bitflags",
  "chalk-derive",
  "lazy_static",
 ]
 
 [[package]]
 name = "chalk-recursive"
-version = "0.45.0"
+version = "0.43.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a18db146d7a023edc20ad094e8c2284451f7888719645004979617d1f17c041"
+checksum = "ed8f34f13fd4f30251f9f6f1dc56f80363201390ecbcac2fdfc8e33036cd9c4a"
 dependencies = [
  "chalk-derive",
  "chalk-ir",
@@ -204,9 +203,9 @@ dependencies = [
 
 [[package]]
 name = "chalk-solve"
-version = "0.45.0"
+version = "0.43.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f73e0de04a0f394e47ed8118e00541bcf681d7c3c2ef500fa743eb4cf3a4850"
+checksum = "379c9f584488346044709d4c638c38d61a06fe593d4de2ac5f15fd2b0ba4cd9d"
 dependencies = [
  "chalk-derive",
  "chalk-ir",
@@ -508,9 +507,9 @@ checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04"
 
 [[package]]
 name = "heck"
-version = "0.3.2"
+version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac"
+checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
 dependencies = [
  "unicode-segmentation",
 ]
@@ -1075,9 +1074,9 @@ dependencies = [
 
 [[package]]
 name = "parking_lot_core"
-version = "0.8.2"
+version = "0.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9ccb628cad4f84851442432c60ad8e1f607e29752d0bf072cbd0baf28aa34272"
+checksum = "d7c6d9b8427445284a09c55be860a15855ab580a417ccad9da88f5a06787ced0"
 dependencies = [
  "cfg-if 1.0.0",
  "instant",
@@ -1377,9 +1376,9 @@ dependencies = [
 
 [[package]]
 name = "rustc-ap-rustc_lexer"
-version = "695.0.0"
+version = "691.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "390bad134705b0bff02cd9541ac66df751a91c3cc734c3369cd6151ca269caed"
+checksum = "44bc89d9ca7a78fb82e103b389362c55f03800745f8ba14e068b805cfaf783ec"
 dependencies = [
  "unicode-xid",
 ]
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs
index 978c3a3249..1b98504bb3 100644
--- a/crates/hir_def/src/body/lower.rs
+++ b/crates/hir_def/src/body/lower.rs
@@ -246,6 +246,10 @@ impl ExprCollector<'_> {
                     let body = self.collect_block_opt(e.block_expr());
                     self.alloc_expr(Expr::Async { body }, syntax_ptr)
                 }
+                ast::Effect::Const(_) => {
+                    let body = self.collect_block_opt(e.block_expr());
+                    self.alloc_expr(Expr::Const { body }, syntax_ptr)
+                }
             },
             ast::Expr::BlockExpr(e) => self.collect_block(e),
             ast::Expr::LoopExpr(e) => {
@@ -932,10 +936,16 @@ impl ExprCollector<'_> {
                 let inner = self.collect_pat_opt(boxpat.pat());
                 Pat::Box { inner }
             }
-            // FIXME: implement
-            ast::Pat::RangePat(_) | ast::Pat::MacroPat(_) | ast::Pat::ConstBlockPat(_) => {
-                Pat::Missing
+            ast::Pat::ConstBlockPat(const_block_pat) => {
+                if let Some(expr) = const_block_pat.block_expr() {
+                    let expr_id = self.collect_block(expr);
+                    Pat::ConstBlock(expr_id)
+                } else {
+                    Pat::Missing
+                }
             }
+            // FIXME: implement
+            ast::Pat::RangePat(_) | ast::Pat::MacroPat(_) => Pat::Missing,
         };
         let ptr = AstPtr::new(&pat);
         self.alloc_pat(pattern, Either::Left(ptr))
diff --git a/crates/hir_def/src/expr.rs b/crates/hir_def/src/expr.rs
index e5d740a365..3bba30397e 100644
--- a/crates/hir_def/src/expr.rs
+++ b/crates/hir_def/src/expr.rs
@@ -114,6 +114,9 @@ pub enum Expr {
     Async {
         body: ExprId,
     },
+    Const {
+        body: ExprId,
+    },
     Cast {
         expr: ExprId,
         type_ref: TypeRef,
@@ -253,7 +256,10 @@ impl Expr {
                     f(*expr);
                 }
             }
-            Expr::TryBlock { body } | Expr::Unsafe { body } | Expr::Async { body } => f(*body),
+            Expr::TryBlock { body }
+            | Expr::Unsafe { body }
+            | Expr::Async { body }
+            | Expr::Const { body } => f(*body),
             Expr::Loop { body, .. } => f(*body),
             Expr::While { condition, body, .. } => {
                 f(*condition);
@@ -399,12 +405,18 @@ pub enum Pat {
     TupleStruct { path: Option<Path>, args: Vec<PatId>, ellipsis: Option<usize> },
     Ref { pat: PatId, mutability: Mutability },
     Box { inner: PatId },
+    ConstBlock(ExprId),
 }
 
 impl Pat {
     pub fn walk_child_pats(&self, mut f: impl FnMut(PatId)) {
         match self {
-            Pat::Range { .. } | Pat::Lit(..) | Pat::Path(..) | Pat::Wild | Pat::Missing => {}
+            Pat::Range { .. }
+            | Pat::Lit(..)
+            | Pat::Path(..)
+            | Pat::ConstBlock(..)
+            | Pat::Wild
+            | Pat::Missing => {}
             Pat::Bind { subpat, .. } => {
                 subpat.iter().copied().for_each(f);
             }
diff --git a/crates/hir_ty/Cargo.toml b/crates/hir_ty/Cargo.toml
index 965c1780a7..289e812fe5 100644
--- a/crates/hir_ty/Cargo.toml
+++ b/crates/hir_ty/Cargo.toml
@@ -17,9 +17,9 @@ ena = "0.14.0"
 log = "0.4.8"
 rustc-hash = "1.1.0"
 scoped-tls = "1"
-chalk-solve = { version = "0.45", default-features = false }
-chalk-ir = "0.45"
-chalk-recursive = "0.45"
+chalk-solve = { version = "0.43", default-features = false }
+chalk-ir = "0.43"
+chalk-recursive = "0.43"
 
 stdx = { path = "../stdx", version = "0.0.0" }
 hir_def = { path = "../hir_def", version = "0.0.0" }
diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs
index 2cdce2cefc..744569e6ef 100644
--- a/crates/hir_ty/src/infer/expr.rs
+++ b/crates/hir_ty/src/infer/expr.rs
@@ -155,7 +155,7 @@ impl<'a> InferenceContext<'a> {
                 }
                 None => self.infer_block(statements, *tail, expected),
             },
-            Expr::Unsafe { body } => self.infer_expr(*body, expected),
+            Expr::Unsafe { body } | Expr::Const { body } => self.infer_expr(*body, expected),
             Expr::TryBlock { body } => {
                 let _inner = self.infer_expr(*body, expected);
                 // FIXME should be std::result::Result<{inner}, _>
diff --git a/crates/hir_ty/src/infer/pat.rs b/crates/hir_ty/src/infer/pat.rs
index b70ec55eb5..d974f805b5 100644
--- a/crates/hir_ty/src/infer/pat.rs
+++ b/crates/hir_ty/src/infer/pat.rs
@@ -243,6 +243,9 @@ impl<'a> InferenceContext<'a> {
                 }
                 None => Ty::Unknown,
             },
+            Pat::ConstBlock(expr) => {
+                self.infer_expr(*expr, &Expectation::has_type(expected.clone()))
+            }
             Pat::Missing => Ty::Unknown,
         };
         // use a new type variable if we got Ty::Unknown here
@@ -264,8 +267,9 @@ fn is_non_ref_pat(body: &hir_def::body::Body, pat: PatId) -> bool {
         | Pat::Range { .. }
         | Pat::Slice { .. } => true,
         Pat::Or(pats) => pats.iter().all(|p| is_non_ref_pat(body, *p)),
-        // FIXME: Path/Lit might actually evaluate to ref, but inference is unimplemented.
+        // FIXME: ConstBlock/Path/Lit might actually evaluate to ref, but inference is unimplemented.
         Pat::Path(..) => true,
+        Pat::ConstBlock(..) => true,
         Pat::Lit(expr) => match body[*expr] {
             Expr::Literal(Literal::String(..)) => false,
             _ => true,
diff --git a/crates/hir_ty/src/traits/chalk.rs b/crates/hir_ty/src/traits/chalk.rs
index 2196af6776..69eae6f79c 100644
--- a/crates/hir_ty/src/traits/chalk.rs
+++ b/crates/hir_ty/src/traits/chalk.rs
@@ -56,13 +56,8 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
     fn adt_datum(&self, struct_id: AdtId) -> Arc<StructDatum> {
         self.db.struct_datum(self.krate, struct_id)
     }
-    fn adt_repr(&self, _struct_id: AdtId) -> Arc<rust_ir::AdtRepr<Interner>> {
-        // FIXME: keep track of these
-        Arc::new(rust_ir::AdtRepr { c: false, packed: false, int: None })
-    }
-    fn discriminant_type(&self, _ty: chalk_ir::Ty<Interner>) -> chalk_ir::Ty<Interner> {
-        // FIXME: keep track of this
-        chalk_ir::TyKind::Scalar(chalk_ir::Scalar::Uint(chalk_ir::UintTy::U32)).intern(&Interner)
+    fn adt_repr(&self, _struct_id: AdtId) -> rust_ir::AdtRepr {
+        rust_ir::AdtRepr { repr_c: false, repr_packed: false }
     }
     fn impl_datum(&self, impl_id: ImplId) -> Arc<ImplDatum> {
         self.db.impl_datum(self.krate, impl_id)
@@ -462,7 +457,6 @@ fn well_known_trait_from_lang_attr(name: &str) -> Option<WellKnownTrait> {
         "fn" => WellKnownTrait::Fn,
         "unsize" => WellKnownTrait::Unsize,
         "coerce_unsized" => WellKnownTrait::CoerceUnsized,
-        "discriminant_kind" => WellKnownTrait::DiscriminantKind,
         _ => return None,
     })
 }
@@ -479,7 +473,6 @@ fn lang_attr_from_well_known_trait(attr: WellKnownTrait) -> &'static str {
         WellKnownTrait::Unsize => "unsize",
         WellKnownTrait::Unpin => "unpin",
         WellKnownTrait::CoerceUnsized => "coerce_unsized",
-        WellKnownTrait::DiscriminantKind => "discriminant_kind",
     }
 }
 
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 55bc2bceca..66f8bee991 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -5,13 +5,11 @@
 use std::{
     io::Write as _,
     process::{self, Stdio},
-    sync::Arc,
 };
 
 use ide::{
-    AssistConfig, CompletionResolveCapability, FileId, FilePosition, FileRange, HoverAction,
-    HoverGotoTypeData, LineIndex, NavigationTarget, Query, RangeInfo, Runnable, RunnableKind,
-    SearchScope, SymbolKind, TextEdit,
+    CompletionResolveCapability, FileId, FilePosition, FileRange, HoverAction, HoverGotoTypeData,
+    NavigationTarget, Query, RangeInfo, Runnable, RunnableKind, SearchScope, SymbolKind, TextEdit,
 };
 use itertools::Itertools;
 use lsp_server::ErrorCode;
@@ -867,8 +865,58 @@ pub(crate) fn handle_formatting(
     }
 }
 
+fn handle_fixes(
+    snap: &GlobalStateSnapshot,
+    params: &lsp_types::CodeActionParams,
+    res: &mut Vec<lsp_ext::CodeAction>,
+) -> Result<()> {
+    let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
+    let line_index = snap.analysis.file_line_index(file_id)?;
+    let range = from_proto::text_range(&line_index, params.range);
+
+    match &params.context.only {
+        Some(v) => {
+            if !v.iter().any(|it| {
+                it == &lsp_types::CodeActionKind::EMPTY
+                    || it == &lsp_types::CodeActionKind::QUICKFIX
+            }) {
+                return Ok(());
+            }
+        }
+        None => {}
+    };
+
+    let diagnostics = snap.analysis.diagnostics(&snap.config.diagnostics, file_id)?;
+
+    for fix in diagnostics
+        .into_iter()
+        .filter_map(|d| d.fix)
+        .filter(|fix| fix.fix_trigger_range.intersect(range).is_some())
+    {
+        let edit = to_proto::snippet_workspace_edit(&snap, fix.source_change)?;
+        let action = lsp_ext::CodeAction {
+            title: fix.label.to_string(),
+            group: None,
+            kind: Some(CodeActionKind::QUICKFIX),
+            edit: Some(edit),
+            is_preferred: Some(false),
+            data: None,
+        };
+        res.push(action);
+    }
+
+    for fix in snap.check_fixes.get(&file_id).into_iter().flatten() {
+        let fix_range = from_proto::text_range(&line_index, fix.range);
+        if fix_range.intersect(range).is_none() {
+            continue;
+        }
+        res.push(fix.action.clone());
+    }
+    Ok(())
+}
+
 pub(crate) fn handle_code_action(
-    snap: GlobalStateSnapshot,
+    mut snap: GlobalStateSnapshot,
     params: lsp_types::CodeActionParams,
 ) -> Result<Option<Vec<lsp_ext::CodeAction>>> {
     let _p = profile::span("handle_code_action");
@@ -884,35 +932,24 @@ pub(crate) fn handle_code_action(
     let range = from_proto::text_range(&line_index, params.range);
     let frange = FileRange { file_id, range };
 
-    let assists_config = AssistConfig {
-        allowed: params
-            .clone()
-            .context
-            .only
-            .map(|it| it.into_iter().filter_map(from_proto::assist_kind).collect()),
-        ..snap.config.assist
-    };
+    snap.config.assist.allowed = params
+        .clone()
+        .context
+        .only
+        .map(|it| it.into_iter().filter_map(from_proto::assist_kind).collect());
 
     let mut res: Vec<lsp_ext::CodeAction> = Vec::new();
 
-    let include_quick_fixes = match &params.context.only {
-        Some(v) => v.iter().any(|it| {
-            it == &lsp_types::CodeActionKind::EMPTY || it == &lsp_types::CodeActionKind::QUICKFIX
-        }),
-        None => true,
-    };
-    if include_quick_fixes {
-        add_quick_fixes(&snap, frange, &line_index, &mut res)?;
-    }
+    handle_fixes(&snap, &params, &mut res)?;
 
     if snap.config.client_caps.code_action_resolve {
         for (index, assist) in
-            snap.analysis.unresolved_assists(&assists_config, frange)?.into_iter().enumerate()
+            snap.analysis.unresolved_assists(&snap.config.assist, frange)?.into_iter().enumerate()
         {
             res.push(to_proto::unresolved_code_action(&snap, params.clone(), assist, index)?);
         }
     } else {
-        for assist in snap.analysis.resolved_assists(&assists_config, frange)?.into_iter() {
+        for assist in snap.analysis.resolved_assists(&snap.config.assist, frange)?.into_iter() {
             res.push(to_proto::resolved_code_action(&snap, assist)?);
         }
     }
@@ -920,40 +957,6 @@ pub(crate) fn handle_code_action(
     Ok(Some(res))
 }
 
-fn add_quick_fixes(
-    snap: &GlobalStateSnapshot,
-    frange: FileRange,
-    line_index: &Arc<LineIndex>,
-    acc: &mut Vec<lsp_ext::CodeAction>,
-) -> Result<()> {
-    let diagnostics = snap.analysis.diagnostics(&snap.config.diagnostics, frange.file_id)?;
-
-    for fix in diagnostics
-        .into_iter()
-        .filter_map(|d| d.fix)
-        .filter(|fix| fix.fix_trigger_range.intersect(frange.range).is_some())
-    {
-        let edit = to_proto::snippet_workspace_edit(&snap, fix.source_change)?;
-        let action = lsp_ext::CodeAction {
-            title: fix.label.to_string(),
-            group: None,
-            kind: Some(CodeActionKind::QUICKFIX),
-            edit: Some(edit),
-            is_preferred: Some(false),
-            data: None,
-        };
-        acc.push(action);
-    }
-
-    for fix in snap.check_fixes.get(&frange.file_id).into_iter().flatten() {
-        let fix_range = from_proto::text_range(&line_index, fix.range);
-        if fix_range.intersect(frange.range).is_some() {
-            acc.push(fix.action.clone());
-        }
-    }
-    Ok(())
-}
-
 pub(crate) fn handle_code_action_resolve(
     mut snap: GlobalStateSnapshot,
     mut code_action: lsp_ext::CodeAction,
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml
index 21015591c7..c6a6f11e13 100644
--- a/crates/syntax/Cargo.toml
+++ b/crates/syntax/Cargo.toml
@@ -13,7 +13,7 @@ doctest = false
 [dependencies]
 itertools = "0.9.0"
 rowan = "0.10.0"
-rustc_lexer = { version = "695.0.0", package = "rustc-ap-rustc_lexer" }
+rustc_lexer = { version = "691.0.0", package = "rustc-ap-rustc_lexer" }
 rustc-hash = "1.1.0"
 arrayvec = "0.5.1"
 once_cell = "1.3.1"
diff --git a/crates/syntax/src/ast/expr_ext.rs b/crates/syntax/src/ast/expr_ext.rs
index e4a9b945c9..636ce166d7 100644
--- a/crates/syntax/src/ast/expr_ext.rs
+++ b/crates/syntax/src/ast/expr_ext.rs
@@ -358,6 +358,7 @@ pub enum Effect {
     Async(SyntaxToken),
     Unsafe(SyntaxToken),
     Try(SyntaxToken),
+    Const(SyntaxToken),
     // Very much not an effect, but we stuff it into this node anyway
     Label(ast::Label),
 }
@@ -373,6 +374,9 @@ impl ast::EffectExpr {
         if let Some(token) = self.try_token() {
             return Effect::Try(token);
         }
+        if let Some(token) = self.const_token() {
+            return Effect::Const(token);
+        }
         if let Some(label) = self.label() {
             return Effect::Label(label);
         }


Test patch:
diff --git a/crates/hir_ty/src/tests/patterns.rs b/crates/hir_ty/src/tests/patterns.rs
index 5a5f48fd05..2053d8f56e 100644
--- a/crates/hir_ty/src/tests/patterns.rs
+++ b/crates/hir_ty/src/tests/patterns.rs
@@ -774,3 +774,33 @@ fn foo(tuple: Tuple) {
         "#]],
     );
 }
+
+#[test]
+fn const_block_pattern() {
+    check_infer(
+        r#"
+struct Foo(usize);
+fn foo(foo: Foo) {
+    match foo {
+        const { Foo(15 + 32) } => {},
+        _ => {}
+    }
+}"#,
+        expect![[r#"
+            26..29 'foo': Foo
+            36..115 '{     ...   } }': ()
+            42..113 'match ...     }': ()
+            48..51 'foo': Foo
+            62..84 'const ... 32) }': Foo
+            68..84 '{ Foo(... 32) }': Foo
+            70..73 'Foo': Foo(usize) -> Foo
+            70..82 'Foo(15 + 32)': Foo
+            74..76 '15': usize
+            74..81 '15 + 32': usize
+            79..81 '32': usize
+            88..90 '{}': ()
+            100..101 '_': Foo
+            105..107 '{}': ()
+        "#]],
+    );
+}
diff --git a/crates/hir_ty/src/tests/simple.rs b/crates/hir_ty/src/tests/simple.rs
index a569223b4b..a61282d5a7 100644
--- a/crates/hir_ty/src/tests/simple.rs
+++ b/crates/hir_ty/src/tests/simple.rs
@@ -1894,6 +1894,7 @@ fn effects_smoke_test() {
             let x = unsafe { 92 };
             let y = async { async { () }.await };
             let z = try { () };
+            let w = const { 92 };
             let t = 'a: { 92 };
         }
 
@@ -1905,7 +1906,7 @@ fn effects_smoke_test() {
         }
         "#,
         expect![[r#"
-            16..136 '{     ...2 }; }': ()
+            16..162 '{     ...2 }; }': ()
             26..27 'x': i32
             30..43 'unsafe { 92 }': i32
             37..43 '{ 92 }': i32
@@ -1921,9 +1922,13 @@ fn effects_smoke_test() {
             99..109 'try { () }': {unknown}
             103..109 '{ () }': ()
             105..107 '()': ()
-            119..120 't': i32
-            127..133 '{ 92 }': i32
-            129..131 '92': i32
+            119..120 'w': i32
+            123..135 'const { 92 }': i32
+            129..135 '{ 92 }': i32
+            131..133 '92': i32
+            145..146 't': i32
+            153..159 '{ 92 }': i32
+            155..157 '92': i32
         "#]],
     )
 }