Align grammar for record patterns and literals

The grammar now looks like this

   [name_ref :] pat
This commit is contained in:
Aleksey Kladov 2020-04-11 23:33:17 +02:00
parent 6b49e774e2
commit 5e5eb6a108
18 changed files with 145 additions and 90 deletions

View file

@ -1,20 +1,20 @@
use std::collections::HashMap; use std::collections::HashMap;
use itertools::Itertools;
use hir::{Adt, ModuleDef, PathResolution, Semantics, Struct}; use hir::{Adt, ModuleDef, PathResolution, Semantics, Struct};
use itertools::Itertools;
use ra_ide_db::RootDatabase; use ra_ide_db::RootDatabase;
use ra_syntax::{ use ra_syntax::{
algo, ast, algo,
ast::{Name, Path, RecordLit, RecordPat}, ast::{self, Path, RecordLit, RecordPat},
AstNode, SyntaxKind, SyntaxNode, match_ast, AstNode, SyntaxKind,
SyntaxKind::*,
SyntaxNode,
}; };
use crate::{ use crate::{
assist_ctx::{Assist, AssistCtx}, assist_ctx::{Assist, AssistCtx},
AssistId, AssistId,
}; };
use ra_syntax::ast::{Expr, NameRef};
// Assist: reorder_fields // Assist: reorder_fields
// //
@ -59,7 +59,6 @@ fn reorder<R: AstNode>(ctx: AssistCtx) -> Option<Assist> {
} }
fn get_fields_kind(node: &SyntaxNode) -> Vec<SyntaxKind> { fn get_fields_kind(node: &SyntaxNode) -> Vec<SyntaxKind> {
use SyntaxKind::*;
match node.kind() { match node.kind() {
RECORD_LIT => vec![RECORD_FIELD], RECORD_LIT => vec![RECORD_FIELD],
RECORD_PAT => vec![RECORD_FIELD_PAT, BIND_PAT], RECORD_PAT => vec![RECORD_FIELD_PAT, BIND_PAT],
@ -68,19 +67,14 @@ fn get_fields_kind(node: &SyntaxNode) -> Vec<SyntaxKind> {
} }
fn get_field_name(node: &SyntaxNode) -> String { fn get_field_name(node: &SyntaxNode) -> String {
use SyntaxKind::*; let res = match_ast! {
match node.kind() { match node {
RECORD_FIELD => { ast::RecordField(field) => { field.field_name().map(|it| it.to_string()) },
if let Some(name) = node.children().find_map(NameRef::cast) { ast::RecordFieldPat(field) => { field.field_name().map(|it| it.to_string()) },
return name.to_string(); _ => None,
}
node.children().find_map(Expr::cast).map(|expr| expr.to_string()).unwrap_or_default()
} }
BIND_PAT | RECORD_FIELD_PAT => { };
node.children().find_map(Name::cast).map(|n| n.to_string()).unwrap_or_default() res.unwrap_or_default()
}
_ => String::new(),
}
} }
fn get_fields(record: &SyntaxNode) -> Vec<SyntaxNode> { fn get_fields(record: &SyntaxNode) -> Vec<SyntaxNode> {

View file

@ -637,7 +637,7 @@ impl ExprCollector<'_> {
let iter = record_field_pat_list.record_field_pats().filter_map(|f| { let iter = record_field_pat_list.record_field_pats().filter_map(|f| {
let ast_pat = f.pat()?; let ast_pat = f.pat()?;
let pat = self.collect_pat(ast_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 }) Some(RecordFieldPat { name, pat })
}); });
fields.extend(iter); fields.extend(iter);

View file

@ -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 { impl AsName for tt::Ident {
fn as_name(&self) -> Name { fn as_name(&self) -> Name {
Name::resolve(&self.text) Name::resolve(&self.text)

View file

@ -1,7 +1,8 @@
use super::{infer, infer_with_mismatches};
use insta::assert_snapshot; use insta::assert_snapshot;
use test_utils::covers; use test_utils::covers;
use super::{infer, infer_with_mismatches};
#[test] #[test]
fn infer_pattern() { fn infer_pattern() {
assert_snapshot!( assert_snapshot!(

View file

@ -7,6 +7,10 @@ pub(super) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
if !ctx.is_pat_binding_or_const { if !ctx.is_pat_binding_or_const {
return; return;
} }
if ctx.record_pat_syntax.is_some() {
return;
}
// FIXME: ideally, we should look at the type we are matching against and // FIXME: ideally, we should look at the type we are matching against and
// suggest variants + auto-imports // suggest variants + auto-imports
ctx.scope().process_all_names(&mut |name, res| { ctx.scope().process_all_names(&mut |name, res| {

View file

@ -2,7 +2,7 @@
use crate::completion::{CompletionContext, Completions}; use crate::completion::{CompletionContext, Completions};
pub(super) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { 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, (None, None) => return None,
(Some(_), Some(_)) => unreachable!("A record cannot be both a literal and a pattern"), (Some(_), Some(_)) => unreachable!("A record cannot be both a literal and a pattern"),
(Some(record_pat), _) => ctx.sema.record_pattern_missing_fields(record_pat), (Some(record_pat), _) => ctx.sema.record_pattern_missing_fields(record_pat),

View file

@ -3,7 +3,14 @@
use crate::completion::{CompletionContext, Completions}; use crate::completion::{CompletionContext, Completions};
pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { 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; return;
} }

View file

@ -30,7 +30,7 @@ pub(crate) struct CompletionContext<'a> {
pub(super) function_syntax: Option<ast::FnDef>, pub(super) function_syntax: Option<ast::FnDef>,
pub(super) use_item_syntax: Option<ast::UseItem>, pub(super) use_item_syntax: Option<ast::UseItem>,
pub(super) record_lit_syntax: Option<ast::RecordLit>, pub(super) record_lit_syntax: Option<ast::RecordLit>,
pub(super) record_lit_pat: Option<ast::RecordPat>, pub(super) record_pat_syntax: Option<ast::RecordPat>,
pub(super) impl_def: Option<ast::ImplDef>, pub(super) impl_def: Option<ast::ImplDef>,
pub(super) is_param: bool, pub(super) is_param: bool,
/// If a name-binding or reference to a const in a pattern. /// If a name-binding or reference to a const in a pattern.
@ -93,7 +93,7 @@ impl<'a> CompletionContext<'a> {
function_syntax: None, function_syntax: None,
use_item_syntax: None, use_item_syntax: None,
record_lit_syntax: None, record_lit_syntax: None,
record_lit_pat: None, record_pat_syntax: None,
impl_def: None, impl_def: None,
is_param: false, is_param: false,
is_pat_binding_or_const: false, is_pat_binding_or_const: false,
@ -182,6 +182,11 @@ impl<'a> CompletionContext<'a> {
self.is_param = true; self.is_param = true;
return; 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); self.classify_name_ref(original_file, name_ref, offset);
} }
@ -211,8 +216,9 @@ impl<'a> CompletionContext<'a> {
self.is_param = true; self.is_param = true;
return; return;
} }
// FIXME: remove this (^) duplication and make the check more precise
if name.syntax().ancestors().find_map(ast::RecordFieldPatList::cast).is_some() { 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); self.sema.find_node_at_offset_with_macros(&original_file, offset);
} }
} }

View file

@ -192,14 +192,30 @@ fn record_field_pat_list(p: &mut Parser) {
match p.current() { match p.current() {
// A trailing `..` is *not* treated as a DOT_DOT_PAT. // A trailing `..` is *not* treated as a DOT_DOT_PAT.
T![.] if p.at(T![..]) => p.bump(T![..]), 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!['{'] => error_block(p, "expected ident"),
T![box] => {
box_pat(p); c => {
} let m = p.start();
_ => { match c {
bind_pat(p, false); // 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!['}']) { if !p.at(T!['}']) {
@ -210,26 +226,6 @@ fn record_field_pat_list(p: &mut Parser) {
m.complete(p, RECORD_FIELD_PAT_LIST); 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 // test placeholder_pat
// fn main() { let _ = (); } // fn main() { let _ = (); }
fn placeholder_pat(p: &mut Parser) -> CompletedMarker { fn placeholder_pat(p: &mut Parser) -> CompletedMarker {

View file

@ -18,8 +18,8 @@ use crate::{
pub use self::{ pub use self::{
expr_extensions::{ArrayExprKind, BinOp, ElseBranch, LiteralKind, PrefixOp, RangeOp}, expr_extensions::{ArrayExprKind, BinOp, ElseBranch, LiteralKind, PrefixOp, RangeOp},
extensions::{ extensions::{
AttrKind, FieldKind, PathSegmentKind, SelfParamKind, SlicePatComponents, StructKind, AttrKind, FieldKind, NameOrNameRef, PathSegmentKind, SelfParamKind, SlicePatComponents,
TypeBoundKind, VisibilityKind, StructKind, TypeBoundKind, VisibilityKind,
}, },
generated::{nodes::*, tokens::*}, generated::{nodes::*, tokens::*},
tokens::*, tokens::*,

View file

@ -1,6 +1,8 @@
//! Various extension methods to ast Nodes, which are hard to code-generate. //! Various extension methods to ast Nodes, which are hard to code-generate.
//! Extensions for various expressions live in a sibling `expr_extensions` module. //! Extensions for various expressions live in a sibling `expr_extensions` module.
use std::fmt;
use itertools::Itertools; use itertools::Itertools;
use ra_parser::SyntaxKind; 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<NameOrNameRef> {
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 { impl ast::EnumVariant {
pub fn parent_enum(&self) -> ast::EnumDef { pub fn parent_enum(&self) -> ast::EnumDef {
self.syntax() self.syntax()

View file

@ -1806,8 +1806,8 @@ impl AstNode for RecordFieldPat {
fn syntax(&self) -> &SyntaxNode { &self.syntax } fn syntax(&self) -> &SyntaxNode { &self.syntax }
} }
impl ast::AttrsOwner for RecordFieldPat {} impl ast::AttrsOwner for RecordFieldPat {}
impl ast::NameOwner for RecordFieldPat {}
impl RecordFieldPat { impl RecordFieldPat {
pub fn name_ref(&self) -> Option<NameRef> { support::child(&self.syntax) }
pub fn colon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![:]) } pub fn colon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![:]) }
pub fn pat(&self) -> Option<Pat> { support::child(&self.syntax) } pub fn pat(&self) -> Option<Pat> { support::child(&self.syntax) }
} }

View file

@ -44,18 +44,20 @@ SOURCE_FILE@[0; 119)
RECORD_FIELD_PAT_LIST@[40; 56) RECORD_FIELD_PAT_LIST@[40; 56)
L_CURLY@[40; 41) "{" L_CURLY@[40; 41) "{"
WHITESPACE@[41; 42) " " WHITESPACE@[41; 42) " "
BIND_PAT@[42; 43) RECORD_FIELD_PAT@[42; 43)
NAME@[42; 43) BIND_PAT@[42; 43)
IDENT@[42; 43) "f" NAME@[42; 43)
IDENT@[42; 43) "f"
COMMA@[43; 44) "," COMMA@[43; 44) ","
WHITESPACE@[44; 45) " " WHITESPACE@[44; 45) " "
BIND_PAT@[45; 54) RECORD_FIELD_PAT@[45; 54)
REF_KW@[45; 48) "ref" BIND_PAT@[45; 54)
WHITESPACE@[48; 49) " " REF_KW@[45; 48) "ref"
MUT_KW@[49; 52) "mut" WHITESPACE@[48; 49) " "
WHITESPACE@[52; 53) " " MUT_KW@[49; 52) "mut"
NAME@[53; 54) WHITESPACE@[52; 53) " "
IDENT@[53; 54) "g" NAME@[53; 54)
IDENT@[53; 54) "g"
WHITESPACE@[54; 55) " " WHITESPACE@[54; 55) " "
R_CURLY@[55; 56) "}" R_CURLY@[55; 56) "}"
WHITESPACE@[56; 57) " " WHITESPACE@[56; 57) " "
@ -79,7 +81,7 @@ SOURCE_FILE@[0; 119)
L_CURLY@[73; 74) "{" L_CURLY@[73; 74) "{"
WHITESPACE@[74; 75) " " WHITESPACE@[74; 75) " "
RECORD_FIELD_PAT@[75; 79) RECORD_FIELD_PAT@[75; 79)
NAME@[75; 76) NAME_REF@[75; 76)
IDENT@[75; 76) "h" IDENT@[75; 76) "h"
COLON@[76; 77) ":" COLON@[76; 77) ":"
WHITESPACE@[77; 78) " " WHITESPACE@[77; 78) " "
@ -110,7 +112,7 @@ SOURCE_FILE@[0; 119)
L_CURLY@[101; 102) "{" L_CURLY@[101; 102) "{"
WHITESPACE@[102; 103) " " WHITESPACE@[102; 103) " "
RECORD_FIELD_PAT@[103; 107) RECORD_FIELD_PAT@[103; 107)
NAME@[103; 104) NAME_REF@[103; 104)
IDENT@[103; 104) "h" IDENT@[103; 104) "h"
COLON@[104; 105) ":" COLON@[104; 105) ":"
WHITESPACE@[105; 106) " " WHITESPACE@[105; 106) " "

View file

@ -44,16 +44,17 @@ SOURCE_FILE@[0; 118)
RECORD_FIELD_PAT_LIST@[50; 81) RECORD_FIELD_PAT_LIST@[50; 81)
L_CURLY@[50; 51) "{" L_CURLY@[50; 51) "{"
WHITESPACE@[51; 52) " " WHITESPACE@[51; 52) " "
BOX_PAT@[52; 57) RECORD_FIELD_PAT@[52; 57)
BOX_KW@[52; 55) "box" BOX_PAT@[52; 57)
WHITESPACE@[55; 56) " " BOX_KW@[52; 55) "box"
BIND_PAT@[56; 57) WHITESPACE@[55; 56) " "
NAME@[56; 57) BIND_PAT@[56; 57)
IDENT@[56; 57) "i" NAME@[56; 57)
IDENT@[56; 57) "i"
COMMA@[57; 58) "," COMMA@[57; 58) ","
WHITESPACE@[58; 59) " " WHITESPACE@[58; 59) " "
RECORD_FIELD_PAT@[59; 79) RECORD_FIELD_PAT@[59; 79)
NAME@[59; 60) NAME_REF@[59; 60)
IDENT@[59; 60) "j" IDENT@[59; 60) "j"
COLON@[60; 61) ":" COLON@[60; 61) ":"
WHITESPACE@[61; 62) " " WHITESPACE@[61; 62) " "

View file

@ -25,7 +25,8 @@ SOURCE_FILE@[0; 63)
L_CURLY@[21; 22) "{" L_CURLY@[21; 22) "{"
WHITESPACE@[22; 23) " " WHITESPACE@[22; 23) " "
RECORD_FIELD_PAT@[23; 27) RECORD_FIELD_PAT@[23; 27)
INT_NUMBER@[23; 24) "0" NAME_REF@[23; 24)
INT_NUMBER@[23; 24) "0"
COLON@[24; 25) ":" COLON@[24; 25) ":"
WHITESPACE@[25; 26) " " WHITESPACE@[25; 26) " "
LITERAL_PAT@[26; 27) LITERAL_PAT@[26; 27)
@ -54,7 +55,7 @@ SOURCE_FILE@[0; 63)
L_CURLY@[46; 47) "{" L_CURLY@[46; 47) "{"
WHITESPACE@[47; 48) " " WHITESPACE@[47; 48) " "
RECORD_FIELD_PAT@[48; 52) RECORD_FIELD_PAT@[48; 52)
NAME@[48; 49) NAME_REF@[48; 49)
IDENT@[48; 49) "x" IDENT@[48; 49) "x"
COLON@[49; 50) ":" COLON@[49; 50) ":"
WHITESPACE@[50; 51) " " WHITESPACE@[50; 51) " "

View file

@ -68,14 +68,16 @@ SOURCE_FILE@[0; 170)
RECORD_FIELD_PAT_LIST@[59; 67) RECORD_FIELD_PAT_LIST@[59; 67)
L_CURLY@[59; 60) "{" L_CURLY@[59; 60) "{"
WHITESPACE@[60; 61) " " WHITESPACE@[60; 61) " "
BIND_PAT@[61; 62) RECORD_FIELD_PAT@[61; 62)
NAME@[61; 62) BIND_PAT@[61; 62)
IDENT@[61; 62) "a" NAME@[61; 62)
IDENT@[61; 62) "a"
COMMA@[62; 63) "," COMMA@[62; 63) ","
WHITESPACE@[63; 64) " " WHITESPACE@[63; 64) " "
BIND_PAT@[64; 65) RECORD_FIELD_PAT@[64; 65)
NAME@[64; 65) BIND_PAT@[64; 65)
IDENT@[64; 65) "b" NAME@[64; 65)
IDENT@[64; 65) "b"
WHITESPACE@[65; 66) " " WHITESPACE@[65; 66) " "
R_CURLY@[66; 67) "}" R_CURLY@[66; 67) "}"
COLON@[67; 68) ":" COLON@[67; 68) ":"

View file

@ -71,14 +71,16 @@ SOURCE_FILE@[0; 137)
RECORD_FIELD_PAT_LIST@[58; 66) RECORD_FIELD_PAT_LIST@[58; 66)
L_CURLY@[58; 59) "{" L_CURLY@[58; 59) "{"
WHITESPACE@[59; 60) " " WHITESPACE@[59; 60) " "
BIND_PAT@[60; 61) RECORD_FIELD_PAT@[60; 61)
NAME@[60; 61) BIND_PAT@[60; 61)
IDENT@[60; 61) "a" NAME@[60; 61)
IDENT@[60; 61) "a"
COMMA@[61; 62) "," COMMA@[61; 62) ","
WHITESPACE@[62; 63) " " WHITESPACE@[62; 63) " "
BIND_PAT@[63; 64) RECORD_FIELD_PAT@[63; 64)
NAME@[63; 64) BIND_PAT@[63; 64)
IDENT@[63; 64) "b" NAME@[63; 64)
IDENT@[63; 64) "b"
WHITESPACE@[64; 65) " " WHITESPACE@[64; 65) " "
R_CURLY@[65; 66) "}" R_CURLY@[65; 66) "}"
COLON@[66; 67) ":" COLON@[66; 67) ":"

View file

@ -511,7 +511,7 @@ pub(crate) const AST_SRC: AstSrc = AstSrc {
T![..], T![..],
T!['}'] T!['}']
} }
struct RecordFieldPat: AttrsOwner, NameOwner { T![:], Pat } struct RecordFieldPat: AttrsOwner { NameRef, T![:], Pat }
struct TupleStructPat { Path, T!['('], args: [Pat], T![')'] } struct TupleStructPat { Path, T!['('], args: [Pat], T![')'] }
struct TuplePat { T!['('], args: [Pat], T![')'] } struct TuplePat { T!['('], args: [Pat], T![')'] }