diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs index 8f7e753090a..c6d442ec416 100644 --- a/crates/ra_hir/src/expr.rs +++ b/crates/ra_hir/src/expr.rs @@ -329,13 +329,23 @@ impl Expr { pub struct PatId(RawId); impl_arena_id!(PatId); +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct FieldPat { + name: Name, + pat: Option, +} + /// Close relative to rustc's hir::PatKind #[derive(Debug, Clone, Eq, PartialEq)] pub enum Pat { Missing, // do we need this? Wild, Tuple(Vec), - Struct, // TODO + Struct { + path: Option, + args: Vec, + // TODO: 'ellipsis' option + }, Range { start: ExprId, end: ExprId, @@ -802,11 +812,30 @@ impl ExprCollector { Pat::Tuple(args) } ast::PatKind::PlaceholderPat(_) => Pat::Wild, + ast::PatKind::StructPat(p) => { + let path = p.path().and_then(Path::from_ast); + + if let Some(field_list) = p.field_pat_list() { + let fields = field_list + .field_pats() + .into_iter() + .map(|f| FieldPat { + name: Name::new(f.ident), + pat: f.pat.as_ref().map(|p| self.collect_pat(p)), + }) + .collect(); + + Pat::Struct { + path: path, + args: fields, + } + } else { + Pat::Missing + } + } + // TODO: implement - ast::PatKind::FieldPatList(_) - | ast::PatKind::SlicePat(_) - | ast::PatKind::StructPat(_) - | ast::PatKind::RangePat(_) => Pat::Missing, + ast::PatKind::SlicePat(_) | ast::PatKind::RangePat(_) => Pat::Missing, }; let syntax_ptr = LocalSyntaxPtr::new(pat.syntax()); self.alloc_pat(pattern, syntax_ptr) diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index cdecbd0642c..3e1a4f02ee5 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -877,6 +877,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { fn infer_pat(&mut self, pat: PatId, expected: &Expectation) -> Ty { let body = Arc::clone(&self.body); // avoid borrow checker problem + // FIXME: we can do some inference even if the expected ty isnt already + // of the right form let ty = match (&body[pat], &expected.ty) { (Pat::Tuple(ref args), &Ty::Tuple(ref tuple_args)) if args.len() == tuple_args.len() => diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs index 000cfb981a9..3df23b16f04 100644 --- a/crates/ra_syntax/src/ast.rs +++ b/crates/ra_syntax/src/ast.rs @@ -664,6 +664,75 @@ impl LiteralExpr { } } +// STRUCT_PAT@[20; 42) +// PATH@[20; 26) +// PATH_SEGMENT@[20; 26) +// NAME_REF@[20; 26) +// IDENT@[20; 26) "Strukt" +// WHITESPACE@[26; 27) +// FIELD_PAT_LIST@[27; 42) +// L_CURLY@[27; 28) +// WHITESPACE@[28; 29) +// IDENT@[29; 30) "x" +// COLON@[30; 31) +// WHITESPACE@[31; 32) +// BIND_PAT@[32; 33) +// NAME@[32; 33) +// IDENT@[32; 33) "x" +// COMMA@[33; 34) +// WHITESPACE@[34; 35) +// BIND_PAT@[35; 36) +// NAME@[35; 36) +// IDENT@[35; 36) "y" +// COMMA@[36; 37) +// WHITESPACE@[37; 38) +// DOTDOT@[38; 40) +// WHITESPACE@[40; 41) +// R_CURLY@[41; 42) + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct FieldPat { + pub ident: SmolStr, + pub pat: Option>, +} + +impl FieldPatList { + // TODO: try returning an iterator? + // FIXME: shouldnt the parser do this? :o + pub fn field_pats(&self) -> Vec { + let mut child_iter = self.syntax().children(); + let mut pats = Vec::new(); + + while let Some(node) = child_iter.next() { + if node.kind() != IDENT { + continue; + } + + let ident = node.leaf_text().unwrap().clone(); + let mut pat = None; + + // get pat + while let Some(node) = child_iter.next() { + if node.kind() == COMMA { + break; + } + + if let Some(p) = Pat::cast(node) { + pat = Some(p.to_owned()); + } + } + + let field_pat = FieldPat { + ident: ident, + pat: pat, + }; + pats.push(field_pat); + } + + pats + } +} + #[test] fn test_doc_comment_of_items() { let file = SourceFile::parse( diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs index 2fd146bf1e7..251d53bdfbf 100644 --- a/crates/ra_syntax/src/ast/generated.rs +++ b/crates/ra_syntax/src/ast/generated.rs @@ -2156,7 +2156,6 @@ pub enum PatKind<'a> { PlaceholderPat(&'a PlaceholderPat), PathPat(&'a PathPat), StructPat(&'a StructPat), - FieldPatList(&'a FieldPatList), TupleStructPat(&'a TupleStructPat), TuplePat(&'a TuplePat), SlicePat(&'a SlicePat), @@ -2171,7 +2170,6 @@ impl AstNode for Pat { | PLACEHOLDER_PAT | PATH_PAT | STRUCT_PAT - | FIELD_PAT_LIST | TUPLE_STRUCT_PAT | TUPLE_PAT | SLICE_PAT @@ -2191,7 +2189,6 @@ impl Pat { PLACEHOLDER_PAT => PatKind::PlaceholderPat(PlaceholderPat::cast(&self.syntax).unwrap()), PATH_PAT => PatKind::PathPat(PathPat::cast(&self.syntax).unwrap()), STRUCT_PAT => PatKind::StructPat(StructPat::cast(&self.syntax).unwrap()), - FIELD_PAT_LIST => PatKind::FieldPatList(FieldPatList::cast(&self.syntax).unwrap()), TUPLE_STRUCT_PAT => PatKind::TupleStructPat(TupleStructPat::cast(&self.syntax).unwrap()), TUPLE_PAT => PatKind::TuplePat(TuplePat::cast(&self.syntax).unwrap()), SLICE_PAT => PatKind::SlicePat(SlicePat::cast(&self.syntax).unwrap()), @@ -3066,7 +3063,15 @@ impl AstNode for StructPat { } -impl StructPat {} +impl StructPat { + pub fn field_pat_list(&self) -> Option<&FieldPatList> { + super::child_opt(self) + } + + pub fn path(&self) -> Option<&Path> { + super::child_opt(self) + } +} // TokenTree #[derive(Debug, PartialEq, Eq, Hash)] diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron index b524c8aaf2e..33080f66415 100644 --- a/crates/ra_syntax/src/grammar.ron +++ b/crates/ra_syntax/src/grammar.ron @@ -490,8 +490,8 @@ Grammar( "RefPat": ( options: [ "Pat" ]), "BindPat": ( traits: ["NameOwner"] ), "PlaceholderPat": (), - "PathPat": ( options: ["Path"] ), - "StructPat": (), + "PathPat": ( options: [ "Path" ] ), + "StructPat": ( options: ["FieldPatList", "Path"] ), "FieldPatList": (), "TupleStructPat": ( options: ["Path"], @@ -508,7 +508,6 @@ Grammar( "PlaceholderPat", "PathPat", "StructPat", - "FieldPatList", "TupleStructPat", "TuplePat", "SlicePat",