From d48d5b8b6ca59b462b3a84dad9868daff2eddb6d Mon Sep 17 00:00:00 2001 From: Marcus Klaas de Vries Date: Thu, 17 Jan 2019 13:08:18 +0100 Subject: Add initial (flawed) implementation of binding annotations --- .../ra_hir/src/code_model_impl/function/scope.rs | 2 +- crates/ra_hir/src/expr.rs | 49 +++++++++++++++++++++- crates/ra_hir/src/ty.rs | 26 +++++++++++- crates/ra_hir/src/ty/tests.rs | 4 ++ 4 files changed, 78 insertions(+), 3 deletions(-) (limited to 'crates/ra_hir/src') diff --git a/crates/ra_hir/src/code_model_impl/function/scope.rs b/crates/ra_hir/src/code_model_impl/function/scope.rs index c551e445a..7e8eb7704 100644 --- a/crates/ra_hir/src/code_model_impl/function/scope.rs +++ b/crates/ra_hir/src/code_model_impl/function/scope.rs @@ -88,7 +88,7 @@ impl FnScopes { fn add_bindings(&mut self, body: &Body, scope: ScopeId, pat: PatId) { match &body[pat] { - Pat::Bind { name } => self.scopes[scope].entries.push(ScopeEntry { + Pat::Bind { name, .. } => self.scopes[scope].entries.push(ScopeEntry { name: name.clone(), pat, }), diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs index e7235c1a1..4d372c97b 100644 --- a/crates/ra_hir/src/expr.rs +++ b/crates/ra_hir/src/expr.rs @@ -329,6 +329,43 @@ impl Expr { pub struct PatId(RawId); impl_arena_id!(PatId); +// copied verbatim from librustc::hir + +/// Explicit binding annotations given in the HIR for a binding. Note +/// that this is not the final binding *mode* that we infer after type +/// inference. +#[derive(Clone, PartialEq, Eq, Debug, Copy)] +pub enum BindingAnnotation { + /// No binding annotation given: this means that the final binding mode + /// will depend on whether we have skipped through a `&` reference + /// when matching. For example, the `x` in `Some(x)` will have binding + /// mode `None`; if you do `let Some(x) = &Some(22)`, it will + /// ultimately be inferred to be by-reference. + /// + /// Note that implicit reference skipping is not implemented yet (#42640). + Unannotated, + + /// Annotated with `mut x` -- could be either ref or not, similar to `None`. + Mutable, + + /// Annotated as `ref`, like `ref x` + Ref, + + /// Annotated as `ref mut x`. + RefMut, +} + +impl BindingAnnotation { + fn new(is_mutable: bool, is_ref: bool) -> Self { + match (is_mutable, is_ref) { + (true, true) => BindingAnnotation::RefMut, + (false, true) => BindingAnnotation::Ref, + (true, false) => BindingAnnotation::Mutable, + (false, false) => BindingAnnotation::Unannotated, + } + } +} + #[derive(Debug, Clone, Eq, PartialEq)] pub struct FieldPat { pub(crate) name: Name, @@ -359,7 +396,9 @@ pub enum Pat { Path(Path), Lit(ExprId), Bind { + mode: BindingAnnotation, name: Name, + sub_pat: Option, }, TupleStruct { path: Option, @@ -793,7 +832,13 @@ impl ExprCollector { .name() .map(|nr| nr.as_name()) .unwrap_or_else(Name::missing); - Pat::Bind { name } + let annotation = BindingAnnotation::new(bp.is_mutable(), bp.is_ref()); + let sub_pat = bp.pat().map(|subpat| self.collect_pat(subpat)); + Pat::Bind { + name, + mode: annotation, + sub_pat, + } } ast::PatKind::TupleStructPat(p) => { let path = p.path().and_then(Path::from_ast); @@ -882,6 +927,8 @@ pub(crate) fn collect_fn_body_syntax(node: &ast::FnDef) -> BodySyntaxMapping { let param = collector.alloc_pat( Pat::Bind { name: Name::self_param(), + mode: BindingAnnotation::Unannotated, + sub_pat: None, }, self_param, ); diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 8ad80990e..bc842dd26 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -37,7 +37,7 @@ use crate::{ db::HirDatabase, type_ref::{TypeRef, Mutability}, name::KnownName, - expr::{Body, Expr, MatchArm, Literal, ExprId, Pat, PatId, UnaryOp, BinaryOp, Statement, FieldPat}, + expr::{Body, Expr, BindingAnnotation, MatchArm, Literal, ExprId, Pat, PatId, UnaryOp, BinaryOp, Statement, FieldPat}, }; /// The ID of a type variable. @@ -985,6 +985,30 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { path: ref p, args: ref fields, } => self.infer_struct(p.as_ref(), fields), + Pat::Path(path) => { + // is this right? + self.module + .resolve_path(self.db, &path) + .take_values() + .map_or(Ty::Unknown, |resolved| self.db.type_for_def(resolved)) + } + Pat::Bind { + mode, + name: _name, + sub_pat, + } => { + let subty = if let Some(subpat) = sub_pat { + self.infer_pat(*subpat, expected) + } else { + Ty::Unknown + }; + + match mode { + BindingAnnotation::Ref => Ty::Ref(subty.into(), Mutability::Shared), + BindingAnnotation::RefMut => Ty::Ref(subty.into(), Mutability::Mut), + BindingAnnotation::Mutable | BindingAnnotation::Unannotated => subty, + } + } _ => Ty::Unknown, }; // use a new type variable if we got Ty::Unknown here diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index dfc83bb17..e817a8da9 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -377,6 +377,10 @@ fn test(x: &i32) { } let lambda = |a: u64, b, c: i32| { a + b; c }; + + let ref ref_to_x = x; + let mut mut_x = x; + let ref mut mut_ref_to_x = x; } "#, "pattern.txt", -- cgit v1.2.3