mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-27 05:23:24 +00:00
Merge #3955
3955: Align grammar for record patterns and literals r=matklad a=matklad
The grammar now looks like this
[name_ref :] pat
bors r+
🤖
Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
This commit is contained in:
commit
a8e032820f
19 changed files with 149 additions and 97 deletions
|
@ -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<R: AstNode>(ctx: AssistCtx) -> Option<Assist> {
|
|||
}
|
||||
|
||||
fn get_fields_kind(node: &SyntaxNode) -> Vec<SyntaxKind> {
|
||||
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<SyntaxKind> {
|
|||
}
|
||||
|
||||
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()
|
||||
}
|
||||
BIND_PAT | RECORD_FIELD_PAT => {
|
||||
node.children().find_map(Name::cast).map(|n| n.to_string()).unwrap_or_default()
|
||||
}
|
||||
_ => String::new(),
|
||||
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,
|
||||
}
|
||||
};
|
||||
res.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn get_fields(record: &SyntaxNode) -> Vec<SyntaxNode> {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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!(
|
||||
|
|
|
@ -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| {
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ pub(crate) struct CompletionContext<'a> {
|
|||
pub(super) function_syntax: Option<ast::FnDef>,
|
||||
pub(super) use_item_syntax: Option<ast::UseItem>,
|
||||
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) 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -282,13 +282,10 @@ fn name_ref(p: &mut Parser) {
|
|||
}
|
||||
|
||||
fn name_ref_or_index(p: &mut Parser) {
|
||||
if p.at(IDENT) || p.at(INT_NUMBER) {
|
||||
assert!(p.at(IDENT) || p.at(INT_NUMBER));
|
||||
let m = p.start();
|
||||
p.bump_any();
|
||||
m.complete(p, NAME_REF);
|
||||
} else {
|
||||
p.err_and_bump("expected identifier");
|
||||
}
|
||||
}
|
||||
|
||||
fn error_block(p: &mut Parser, message: &str) {
|
||||
|
|
|
@ -192,16 +192,32 @@ 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"),
|
||||
|
||||
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!['}']) {
|
||||
p.expect(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 {
|
||||
|
|
|
@ -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::*,
|
||||
|
|
|
@ -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<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 {
|
||||
pub fn parent_enum(&self) -> ast::EnumDef {
|
||||
self.syntax()
|
||||
|
|
|
@ -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<NameRef> { support::child(&self.syntax) }
|
||||
pub fn colon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![:]) }
|
||||
pub fn pat(&self) -> Option<Pat> { support::child(&self.syntax) }
|
||||
}
|
||||
|
|
|
@ -44,11 +44,13 @@ SOURCE_FILE@[0; 119)
|
|||
RECORD_FIELD_PAT_LIST@[40; 56)
|
||||
L_CURLY@[40; 41) "{"
|
||||
WHITESPACE@[41; 42) " "
|
||||
RECORD_FIELD_PAT@[42; 43)
|
||||
BIND_PAT@[42; 43)
|
||||
NAME@[42; 43)
|
||||
IDENT@[42; 43) "f"
|
||||
COMMA@[43; 44) ","
|
||||
WHITESPACE@[44; 45) " "
|
||||
RECORD_FIELD_PAT@[45; 54)
|
||||
BIND_PAT@[45; 54)
|
||||
REF_KW@[45; 48) "ref"
|
||||
WHITESPACE@[48; 49) " "
|
||||
|
@ -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) " "
|
||||
|
|
|
@ -44,6 +44,7 @@ SOURCE_FILE@[0; 118)
|
|||
RECORD_FIELD_PAT_LIST@[50; 81)
|
||||
L_CURLY@[50; 51) "{"
|
||||
WHITESPACE@[51; 52) " "
|
||||
RECORD_FIELD_PAT@[52; 57)
|
||||
BOX_PAT@[52; 57)
|
||||
BOX_KW@[52; 55) "box"
|
||||
WHITESPACE@[55; 56) " "
|
||||
|
@ -53,7 +54,7 @@ SOURCE_FILE@[0; 118)
|
|||
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) " "
|
||||
|
|
|
@ -25,6 +25,7 @@ SOURCE_FILE@[0; 63)
|
|||
L_CURLY@[21; 22) "{"
|
||||
WHITESPACE@[22; 23) " "
|
||||
RECORD_FIELD_PAT@[23; 27)
|
||||
NAME_REF@[23; 24)
|
||||
INT_NUMBER@[23; 24) "0"
|
||||
COLON@[24; 25) ":"
|
||||
WHITESPACE@[25; 26) " "
|
||||
|
@ -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) " "
|
||||
|
|
|
@ -68,11 +68,13 @@ SOURCE_FILE@[0; 170)
|
|||
RECORD_FIELD_PAT_LIST@[59; 67)
|
||||
L_CURLY@[59; 60) "{"
|
||||
WHITESPACE@[60; 61) " "
|
||||
RECORD_FIELD_PAT@[61; 62)
|
||||
BIND_PAT@[61; 62)
|
||||
NAME@[61; 62)
|
||||
IDENT@[61; 62) "a"
|
||||
COMMA@[62; 63) ","
|
||||
WHITESPACE@[63; 64) " "
|
||||
RECORD_FIELD_PAT@[64; 65)
|
||||
BIND_PAT@[64; 65)
|
||||
NAME@[64; 65)
|
||||
IDENT@[64; 65) "b"
|
||||
|
|
|
@ -71,11 +71,13 @@ SOURCE_FILE@[0; 137)
|
|||
RECORD_FIELD_PAT_LIST@[58; 66)
|
||||
L_CURLY@[58; 59) "{"
|
||||
WHITESPACE@[59; 60) " "
|
||||
RECORD_FIELD_PAT@[60; 61)
|
||||
BIND_PAT@[60; 61)
|
||||
NAME@[60; 61)
|
||||
IDENT@[60; 61) "a"
|
||||
COMMA@[61; 62) ","
|
||||
WHITESPACE@[62; 63) " "
|
||||
RECORD_FIELD_PAT@[63; 64)
|
||||
BIND_PAT@[63; 64)
|
||||
NAME@[63; 64)
|
||||
IDENT@[63; 64) "b"
|
||||
|
|
|
@ -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![')'] }
|
||||
|
|
Loading…
Reference in a new issue