diff --git a/crates/ra_assists/src/handlers/reorder_fields.rs b/crates/ra_assists/src/handlers/reorder_fields.rs index 692dd1315e..a43e53a119 100644 --- a/crates/ra_assists/src/handlers/reorder_fields.rs +++ b/crates/ra_assists/src/handlers/reorder_fields.rs @@ -1,20 +1,20 @@ use std::collections::HashMap; -use itertools::Itertools; - use hir::{Adt, ModuleDef, PathResolution, Semantics, Struct}; +use itertools::Itertools; use ra_ide_db::RootDatabase; use ra_syntax::{ - algo, ast, - ast::{Name, Path, RecordLit, RecordPat}, - AstNode, SyntaxKind, SyntaxNode, + algo, + ast::{self, Path, RecordLit, RecordPat}, + match_ast, AstNode, SyntaxKind, + SyntaxKind::*, + SyntaxNode, }; use crate::{ assist_ctx::{Assist, AssistCtx}, AssistId, }; -use ra_syntax::ast::{Expr, NameRef}; // Assist: reorder_fields // @@ -59,7 +59,6 @@ fn reorder(ctx: AssistCtx) -> Option { } fn get_fields_kind(node: &SyntaxNode) -> Vec { - use SyntaxKind::*; match node.kind() { RECORD_LIT => vec![RECORD_FIELD], RECORD_PAT => vec![RECORD_FIELD_PAT, BIND_PAT], @@ -68,19 +67,14 @@ fn get_fields_kind(node: &SyntaxNode) -> Vec { } fn get_field_name(node: &SyntaxNode) -> String { - use SyntaxKind::*; - match node.kind() { - RECORD_FIELD => { - if let Some(name) = node.children().find_map(NameRef::cast) { - return name.to_string(); - } - node.children().find_map(Expr::cast).map(|expr| expr.to_string()).unwrap_or_default() + let res = match_ast! { + match node { + ast::RecordField(field) => { field.field_name().map(|it| it.to_string()) }, + ast::RecordFieldPat(field) => { field.field_name().map(|it| it.to_string()) }, + _ => None, } - BIND_PAT | RECORD_FIELD_PAT => { - node.children().find_map(Name::cast).map(|n| n.to_string()).unwrap_or_default() - } - _ => String::new(), - } + }; + res.unwrap_or_default() } fn get_fields(record: &SyntaxNode) -> Vec { diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs index c057dc8f25..6caa87db47 100644 --- a/crates/ra_hir_def/src/body/lower.rs +++ b/crates/ra_hir_def/src/body/lower.rs @@ -637,7 +637,7 @@ impl ExprCollector<'_> { let iter = record_field_pat_list.record_field_pats().filter_map(|f| { let ast_pat = f.pat()?; let pat = self.collect_pat(ast_pat); - let name = f.name()?.as_name(); + let name = f.field_name()?.as_name(); Some(RecordFieldPat { name, pat }) }); fields.extend(iter); diff --git a/crates/ra_hir_expand/src/name.rs b/crates/ra_hir_expand/src/name.rs index 25cc1e9fce..fecce224ee 100644 --- a/crates/ra_hir_expand/src/name.rs +++ b/crates/ra_hir_expand/src/name.rs @@ -83,6 +83,15 @@ impl AsName for ast::Name { } } +impl AsName for ast::NameOrNameRef { + fn as_name(&self) -> Name { + match self { + ast::NameOrNameRef::Name(it) => it.as_name(), + ast::NameOrNameRef::NameRef(it) => it.as_name(), + } + } +} + impl AsName for tt::Ident { fn as_name(&self) -> Name { Name::resolve(&self.text) diff --git a/crates/ra_hir_ty/src/tests/patterns.rs b/crates/ra_hir_ty/src/tests/patterns.rs index 6e5d2247ca..07cbc521a3 100644 --- a/crates/ra_hir_ty/src/tests/patterns.rs +++ b/crates/ra_hir_ty/src/tests/patterns.rs @@ -1,7 +1,8 @@ -use super::{infer, infer_with_mismatches}; use insta::assert_snapshot; use test_utils::covers; +use super::{infer, infer_with_mismatches}; + #[test] fn infer_pattern() { assert_snapshot!( diff --git a/crates/ra_ide/src/completion/complete_pattern.rs b/crates/ra_ide/src/completion/complete_pattern.rs index 1b7d3122f8..a8b4ce114d 100644 --- a/crates/ra_ide/src/completion/complete_pattern.rs +++ b/crates/ra_ide/src/completion/complete_pattern.rs @@ -7,6 +7,10 @@ pub(super) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { if !ctx.is_pat_binding_or_const { return; } + if ctx.record_pat_syntax.is_some() { + return; + } + // FIXME: ideally, we should look at the type we are matching against and // suggest variants + auto-imports ctx.scope().process_all_names(&mut |name, res| { diff --git a/crates/ra_ide/src/completion/complete_record.rs b/crates/ra_ide/src/completion/complete_record.rs index f46bcee5c4..83a553155b 100644 --- a/crates/ra_ide/src/completion/complete_record.rs +++ b/crates/ra_ide/src/completion/complete_record.rs @@ -2,7 +2,7 @@ use crate::completion::{CompletionContext, Completions}; pub(super) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { - let missing_fields = match (ctx.record_lit_pat.as_ref(), ctx.record_lit_syntax.as_ref()) { + let missing_fields = match (ctx.record_pat_syntax.as_ref(), ctx.record_lit_syntax.as_ref()) { (None, None) => return None, (Some(_), Some(_)) => unreachable!("A record cannot be both a literal and a pattern"), (Some(record_pat), _) => ctx.sema.record_pattern_missing_fields(record_pat), diff --git a/crates/ra_ide/src/completion/complete_unqualified_path.rs b/crates/ra_ide/src/completion/complete_unqualified_path.rs index 0b0da6ee49..2d8e0776ca 100644 --- a/crates/ra_ide/src/completion/complete_unqualified_path.rs +++ b/crates/ra_ide/src/completion/complete_unqualified_path.rs @@ -3,7 +3,14 @@ use crate::completion::{CompletionContext, Completions}; pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { - if !(ctx.is_trivial_path && !ctx.is_pat_binding_or_const && !ctx.record_lit_syntax.is_some()) { + if !ctx.is_trivial_path { + return; + } + + if ctx.is_pat_binding_or_const + || ctx.record_lit_syntax.is_some() + || ctx.record_pat_syntax.is_some() + { return; } diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs index 14a4a14d7b..8b34015950 100644 --- a/crates/ra_ide/src/completion/completion_context.rs +++ b/crates/ra_ide/src/completion/completion_context.rs @@ -30,7 +30,7 @@ pub(crate) struct CompletionContext<'a> { pub(super) function_syntax: Option, pub(super) use_item_syntax: Option, pub(super) record_lit_syntax: Option, - pub(super) record_lit_pat: Option, + pub(super) record_pat_syntax: Option, pub(super) impl_def: Option, pub(super) is_param: bool, /// If a name-binding or reference to a const in a pattern. @@ -93,7 +93,7 @@ impl<'a> CompletionContext<'a> { function_syntax: None, use_item_syntax: None, record_lit_syntax: None, - record_lit_pat: None, + record_pat_syntax: None, impl_def: None, is_param: false, is_pat_binding_or_const: false, @@ -182,6 +182,11 @@ impl<'a> CompletionContext<'a> { self.is_param = true; return; } + // FIXME: remove this (V) duplication and make the check more precise + if name_ref.syntax().ancestors().find_map(ast::RecordFieldPatList::cast).is_some() { + self.record_pat_syntax = + self.sema.find_node_at_offset_with_macros(&original_file, offset); + } self.classify_name_ref(original_file, name_ref, offset); } @@ -211,8 +216,9 @@ impl<'a> CompletionContext<'a> { self.is_param = true; return; } + // FIXME: remove this (^) duplication and make the check more precise if name.syntax().ancestors().find_map(ast::RecordFieldPatList::cast).is_some() { - self.record_lit_pat = + self.record_pat_syntax = self.sema.find_node_at_offset_with_macros(&original_file, offset); } } diff --git a/crates/ra_parser/src/grammar/patterns.rs b/crates/ra_parser/src/grammar/patterns.rs index 936d27575c..68fb2fc733 100644 --- a/crates/ra_parser/src/grammar/patterns.rs +++ b/crates/ra_parser/src/grammar/patterns.rs @@ -192,14 +192,30 @@ fn record_field_pat_list(p: &mut Parser) { match p.current() { // A trailing `..` is *not* treated as a DOT_DOT_PAT. T![.] if p.at(T![..]) => p.bump(T![..]), - - IDENT | INT_NUMBER if p.nth(1) == T![:] => record_field_pat(p), T!['{'] => error_block(p, "expected ident"), - T![box] => { - box_pat(p); - } - _ => { - bind_pat(p, false); + + c => { + let m = p.start(); + match c { + // test record_field_pat + // fn foo() { + // let S { 0: 1 } = (); + // let S { x: 1 } = (); + // } + IDENT | INT_NUMBER if p.nth(1) == T![:] => { + name_ref_or_index(p); + p.bump(T![:]); + pattern(p); + } + T![box] => { + // FIXME: not all box patterns should be allowed + box_pat(p); + } + _ => { + bind_pat(p, false); + } + } + m.complete(p, RECORD_FIELD_PAT); } } if !p.at(T!['}']) { @@ -210,26 +226,6 @@ fn record_field_pat_list(p: &mut Parser) { m.complete(p, RECORD_FIELD_PAT_LIST); } -// test record_field_pat -// fn foo() { -// let S { 0: 1 } = (); -// let S { x: 1 } = (); -// } -fn record_field_pat(p: &mut Parser) { - assert!(p.at(IDENT) || p.at(INT_NUMBER)); - assert!(p.nth(1) == T![:]); - - let m = p.start(); - - if !p.eat(INT_NUMBER) { - name(p) - } - - p.bump_any(); - pattern(p); - m.complete(p, RECORD_FIELD_PAT); -} - // test placeholder_pat // fn main() { let _ = (); } fn placeholder_pat(p: &mut Parser) -> CompletedMarker { diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs index 99c6b72197..7fca5661ef 100644 --- a/crates/ra_syntax/src/ast.rs +++ b/crates/ra_syntax/src/ast.rs @@ -18,8 +18,8 @@ use crate::{ pub use self::{ expr_extensions::{ArrayExprKind, BinOp, ElseBranch, LiteralKind, PrefixOp, RangeOp}, extensions::{ - AttrKind, FieldKind, PathSegmentKind, SelfParamKind, SlicePatComponents, StructKind, - TypeBoundKind, VisibilityKind, + AttrKind, FieldKind, NameOrNameRef, PathSegmentKind, SelfParamKind, SlicePatComponents, + StructKind, TypeBoundKind, VisibilityKind, }, generated::{nodes::*, tokens::*}, tokens::*, diff --git a/crates/ra_syntax/src/ast/extensions.rs b/crates/ra_syntax/src/ast/extensions.rs index 63e272fbfa..f2ea5088e7 100644 --- a/crates/ra_syntax/src/ast/extensions.rs +++ b/crates/ra_syntax/src/ast/extensions.rs @@ -1,6 +1,8 @@ //! Various extension methods to ast Nodes, which are hard to code-generate. //! Extensions for various expressions live in a sibling `expr_extensions` module. +use std::fmt; + use itertools::Itertools; use ra_parser::SyntaxKind; @@ -217,6 +219,34 @@ impl ast::RecordField { } } +pub enum NameOrNameRef { + Name(ast::Name), + NameRef(ast::NameRef), +} + +impl fmt::Display for NameOrNameRef { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + NameOrNameRef::Name(it) => fmt::Display::fmt(it, f), + NameOrNameRef::NameRef(it) => fmt::Display::fmt(it, f), + } + } +} + +impl ast::RecordFieldPat { + /// Deals with field init shorthand + pub fn field_name(&self) -> Option { + if let Some(name_ref) = self.name_ref() { + return Some(NameOrNameRef::NameRef(name_ref)); + } + if let Some(ast::Pat::BindPat(pat)) = self.pat() { + let name = pat.name()?; + return Some(NameOrNameRef::Name(name)); + } + None + } +} + impl ast::EnumVariant { pub fn parent_enum(&self) -> ast::EnumDef { self.syntax() diff --git a/crates/ra_syntax/src/ast/generated/nodes.rs b/crates/ra_syntax/src/ast/generated/nodes.rs index f1098755b6..188f0df968 100644 --- a/crates/ra_syntax/src/ast/generated/nodes.rs +++ b/crates/ra_syntax/src/ast/generated/nodes.rs @@ -1806,8 +1806,8 @@ impl AstNode for RecordFieldPat { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl ast::AttrsOwner for RecordFieldPat {} -impl ast::NameOwner for RecordFieldPat {} impl RecordFieldPat { + pub fn name_ref(&self) -> Option { support::child(&self.syntax) } pub fn colon_token(&self) -> Option { support::token(&self.syntax, T![:]) } pub fn pat(&self) -> Option { support::child(&self.syntax) } } diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0102_record_field_pat_list.rast b/crates/ra_syntax/test_data/parser/inline/ok/0102_record_field_pat_list.rast index c2614543ca..fcd099de98 100644 --- a/crates/ra_syntax/test_data/parser/inline/ok/0102_record_field_pat_list.rast +++ b/crates/ra_syntax/test_data/parser/inline/ok/0102_record_field_pat_list.rast @@ -44,18 +44,20 @@ SOURCE_FILE@[0; 119) RECORD_FIELD_PAT_LIST@[40; 56) L_CURLY@[40; 41) "{" WHITESPACE@[41; 42) " " - BIND_PAT@[42; 43) - NAME@[42; 43) - IDENT@[42; 43) "f" + RECORD_FIELD_PAT@[42; 43) + BIND_PAT@[42; 43) + NAME@[42; 43) + IDENT@[42; 43) "f" COMMA@[43; 44) "," WHITESPACE@[44; 45) " " - BIND_PAT@[45; 54) - REF_KW@[45; 48) "ref" - WHITESPACE@[48; 49) " " - MUT_KW@[49; 52) "mut" - WHITESPACE@[52; 53) " " - NAME@[53; 54) - IDENT@[53; 54) "g" + RECORD_FIELD_PAT@[45; 54) + BIND_PAT@[45; 54) + REF_KW@[45; 48) "ref" + WHITESPACE@[48; 49) " " + MUT_KW@[49; 52) "mut" + WHITESPACE@[52; 53) " " + NAME@[53; 54) + IDENT@[53; 54) "g" WHITESPACE@[54; 55) " " R_CURLY@[55; 56) "}" WHITESPACE@[56; 57) " " @@ -79,7 +81,7 @@ SOURCE_FILE@[0; 119) L_CURLY@[73; 74) "{" WHITESPACE@[74; 75) " " RECORD_FIELD_PAT@[75; 79) - NAME@[75; 76) + NAME_REF@[75; 76) IDENT@[75; 76) "h" COLON@[76; 77) ":" WHITESPACE@[77; 78) " " @@ -110,7 +112,7 @@ SOURCE_FILE@[0; 119) L_CURLY@[101; 102) "{" WHITESPACE@[102; 103) " " RECORD_FIELD_PAT@[103; 107) - NAME@[103; 104) + NAME_REF@[103; 104) IDENT@[103; 104) "h" COLON@[104; 105) ":" WHITESPACE@[105; 106) " " diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0143_box_pat.rast b/crates/ra_syntax/test_data/parser/inline/ok/0143_box_pat.rast index f756730704..1d245f8f31 100644 --- a/crates/ra_syntax/test_data/parser/inline/ok/0143_box_pat.rast +++ b/crates/ra_syntax/test_data/parser/inline/ok/0143_box_pat.rast @@ -44,16 +44,17 @@ SOURCE_FILE@[0; 118) RECORD_FIELD_PAT_LIST@[50; 81) L_CURLY@[50; 51) "{" WHITESPACE@[51; 52) " " - BOX_PAT@[52; 57) - BOX_KW@[52; 55) "box" - WHITESPACE@[55; 56) " " - BIND_PAT@[56; 57) - NAME@[56; 57) - IDENT@[56; 57) "i" + RECORD_FIELD_PAT@[52; 57) + BOX_PAT@[52; 57) + BOX_KW@[52; 55) "box" + WHITESPACE@[55; 56) " " + BIND_PAT@[56; 57) + NAME@[56; 57) + IDENT@[56; 57) "i" COMMA@[57; 58) "," WHITESPACE@[58; 59) " " RECORD_FIELD_PAT@[59; 79) - NAME@[59; 60) + NAME_REF@[59; 60) IDENT@[59; 60) "j" COLON@[60; 61) ":" WHITESPACE@[61; 62) " " diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0145_record_field_pat.rast b/crates/ra_syntax/test_data/parser/inline/ok/0145_record_field_pat.rast index 0d786f597c..cac2ffdcf8 100644 --- a/crates/ra_syntax/test_data/parser/inline/ok/0145_record_field_pat.rast +++ b/crates/ra_syntax/test_data/parser/inline/ok/0145_record_field_pat.rast @@ -25,7 +25,8 @@ SOURCE_FILE@[0; 63) L_CURLY@[21; 22) "{" WHITESPACE@[22; 23) " " RECORD_FIELD_PAT@[23; 27) - INT_NUMBER@[23; 24) "0" + NAME_REF@[23; 24) + INT_NUMBER@[23; 24) "0" COLON@[24; 25) ":" WHITESPACE@[25; 26) " " LITERAL_PAT@[26; 27) @@ -54,7 +55,7 @@ SOURCE_FILE@[0; 63) L_CURLY@[46; 47) "{" WHITESPACE@[47; 48) " " RECORD_FIELD_PAT@[48; 52) - NAME@[48; 49) + NAME_REF@[48; 49) IDENT@[48; 49) "x" COLON@[49; 50) ":" WHITESPACE@[50; 51) " " diff --git a/crates/ra_syntax/test_data/parser/ok/0063_trait_fn_patterns.rast b/crates/ra_syntax/test_data/parser/ok/0063_trait_fn_patterns.rast index 9b5954ebd2..d0623ba904 100644 --- a/crates/ra_syntax/test_data/parser/ok/0063_trait_fn_patterns.rast +++ b/crates/ra_syntax/test_data/parser/ok/0063_trait_fn_patterns.rast @@ -68,14 +68,16 @@ SOURCE_FILE@[0; 170) RECORD_FIELD_PAT_LIST@[59; 67) L_CURLY@[59; 60) "{" WHITESPACE@[60; 61) " " - BIND_PAT@[61; 62) - NAME@[61; 62) - IDENT@[61; 62) "a" + RECORD_FIELD_PAT@[61; 62) + BIND_PAT@[61; 62) + NAME@[61; 62) + IDENT@[61; 62) "a" COMMA@[62; 63) "," WHITESPACE@[63; 64) " " - BIND_PAT@[64; 65) - NAME@[64; 65) - IDENT@[64; 65) "b" + RECORD_FIELD_PAT@[64; 65) + BIND_PAT@[64; 65) + NAME@[64; 65) + IDENT@[64; 65) "b" WHITESPACE@[65; 66) " " R_CURLY@[66; 67) "}" COLON@[67; 68) ":" diff --git a/crates/ra_syntax/test_data/parser/ok/0064_impl_fn_params.rast b/crates/ra_syntax/test_data/parser/ok/0064_impl_fn_params.rast index b30030de36..5e96b695bb 100644 --- a/crates/ra_syntax/test_data/parser/ok/0064_impl_fn_params.rast +++ b/crates/ra_syntax/test_data/parser/ok/0064_impl_fn_params.rast @@ -71,14 +71,16 @@ SOURCE_FILE@[0; 137) RECORD_FIELD_PAT_LIST@[58; 66) L_CURLY@[58; 59) "{" WHITESPACE@[59; 60) " " - BIND_PAT@[60; 61) - NAME@[60; 61) - IDENT@[60; 61) "a" + RECORD_FIELD_PAT@[60; 61) + BIND_PAT@[60; 61) + NAME@[60; 61) + IDENT@[60; 61) "a" COMMA@[61; 62) "," WHITESPACE@[62; 63) " " - BIND_PAT@[63; 64) - NAME@[63; 64) - IDENT@[63; 64) "b" + RECORD_FIELD_PAT@[63; 64) + BIND_PAT@[63; 64) + NAME@[63; 64) + IDENT@[63; 64) "b" WHITESPACE@[64; 65) " " R_CURLY@[65; 66) "}" COLON@[66; 67) ":" diff --git a/xtask/src/ast_src.rs b/xtask/src/ast_src.rs index 69cba91684..9c02f7c6f0 100644 --- a/xtask/src/ast_src.rs +++ b/xtask/src/ast_src.rs @@ -511,7 +511,7 @@ pub(crate) const AST_SRC: AstSrc = AstSrc { T![..], T!['}'] } - struct RecordFieldPat: AttrsOwner, NameOwner { T![:], Pat } + struct RecordFieldPat: AttrsOwner { NameRef, T![:], Pat } struct TupleStructPat { Path, T!['('], args: [Pat], T![')'] } struct TuplePat { T!['('], args: [Pat], T![')'] }