aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_assists/src/handlers/merge_imports.rs33
-rw-r--r--crates/ra_assists/src/handlers/reorder_fields.rs32
-rw-r--r--crates/ra_hir_def/src/body/lower.rs29
-rw-r--r--crates/ra_hir_def/src/expr.rs6
-rw-r--r--crates/ra_hir_expand/src/name.rs9
-rw-r--r--crates/ra_hir_ty/src/_match.rs350
-rw-r--r--crates/ra_hir_ty/src/expr.rs13
-rw-r--r--crates/ra_hir_ty/src/infer/pat.rs6
-rw-r--r--crates/ra_hir_ty/src/tests/patterns.rs3
-rw-r--r--crates/ra_ide/src/call_info.rs21
-rw-r--r--crates/ra_ide/src/completion/complete_pattern.rs4
-rw-r--r--crates/ra_ide/src/completion/complete_record.rs2
-rw-r--r--crates/ra_ide/src/completion/complete_unqualified_path.rs9
-rw-r--r--crates/ra_ide/src/completion/completion_context.rs12
-rw-r--r--crates/ra_parser/src/grammar.rs11
-rw-r--r--crates/ra_parser/src/grammar/patterns.rs50
-rw-r--r--crates/ra_syntax/src/ast.rs4
-rw-r--r--crates/ra_syntax/src/ast/extensions.rs30
-rw-r--r--crates/ra_syntax/src/ast/generated/nodes.rs2
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0102_record_field_pat_list.rast26
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0143_box_pat.rast15
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0145_record_field_pat.rast5
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0063_trait_fn_patterns.rast14
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0064_impl_fn_params.rast14
-rw-r--r--xtask/src/ast_src.rs2
25 files changed, 557 insertions, 145 deletions
diff --git a/crates/ra_assists/src/handlers/merge_imports.rs b/crates/ra_assists/src/handlers/merge_imports.rs
index 0958f52f1..ef0ce0586 100644
--- a/crates/ra_assists/src/handlers/merge_imports.rs
+++ b/crates/ra_assists/src/handlers/merge_imports.rs
@@ -1,7 +1,7 @@
1use std::iter::successors; 1use std::iter::successors;
2 2
3use ra_syntax::{ 3use ra_syntax::{
4 algo::{neighbor, SyntaxRewriter}, 4 algo::{neighbor, skip_trivia_token, SyntaxRewriter},
5 ast::{self, edit::AstNodeEdit, make}, 5 ast::{self, edit::AstNodeEdit, make},
6 AstNode, Direction, InsertPosition, SyntaxElement, T, 6 AstNode, Direction, InsertPosition, SyntaxElement, T,
7}; 7};
@@ -72,9 +72,18 @@ fn try_merge_trees(old: &ast::UseTree, new: &ast::UseTree) -> Option<ast::UseTre
72 let lhs = old.split_prefix(&lhs_prefix); 72 let lhs = old.split_prefix(&lhs_prefix);
73 let rhs = new.split_prefix(&rhs_prefix); 73 let rhs = new.split_prefix(&rhs_prefix);
74 74
75 let should_insert_comma = lhs
76 .use_tree_list()?
77 .r_curly_token()
78 .and_then(|it| skip_trivia_token(it.prev_token()?, Direction::Prev))
79 .map(|it| it.kind() != T![,])
80 .unwrap_or(true);
81
75 let mut to_insert: Vec<SyntaxElement> = Vec::new(); 82 let mut to_insert: Vec<SyntaxElement> = Vec::new();
76 to_insert.push(make::token(T![,]).into()); 83 if should_insert_comma {
77 to_insert.push(make::tokens::single_space().into()); 84 to_insert.push(make::token(T![,]).into());
85 to_insert.push(make::tokens::single_space().into());
86 }
78 to_insert.extend( 87 to_insert.extend(
79 rhs.use_tree_list()? 88 rhs.use_tree_list()?
80 .syntax() 89 .syntax()
@@ -247,4 +256,22 @@ use {
247", 256",
248 ); 257 );
249 } 258 }
259
260 #[test]
261 fn test_double_comma() {
262 check_assist(
263 merge_imports,
264 r"
265use foo::bar::baz;
266use foo::<|>{
267 FooBar,
268};
269",
270 r"
271use foo::{<|>
272 FooBar,
273bar::baz};
274",
275 )
276 }
250} 277}
diff --git a/crates/ra_assists/src/handlers/reorder_fields.rs b/crates/ra_assists/src/handlers/reorder_fields.rs
index 692dd1315..5cbb98d73 100644
--- a/crates/ra_assists/src/handlers/reorder_fields.rs
+++ b/crates/ra_assists/src/handlers/reorder_fields.rs
@@ -1,20 +1,20 @@
1use std::collections::HashMap; 1use std::collections::HashMap;
2 2
3use itertools::Itertools;
4
5use hir::{Adt, ModuleDef, PathResolution, Semantics, Struct}; 3use hir::{Adt, ModuleDef, PathResolution, Semantics, Struct};
4use itertools::Itertools;
6use ra_ide_db::RootDatabase; 5use ra_ide_db::RootDatabase;
7use ra_syntax::{ 6use ra_syntax::{
8 algo, ast, 7 algo,
9 ast::{Name, Path, RecordLit, RecordPat}, 8 ast::{self, Path, RecordLit, RecordPat},
10 AstNode, SyntaxKind, SyntaxNode, 9 match_ast, AstNode, SyntaxKind,
10 SyntaxKind::*,
11 SyntaxNode,
11}; 12};
12 13
13use crate::{ 14use crate::{
14 assist_ctx::{Assist, AssistCtx}, 15 assist_ctx::{Assist, AssistCtx},
15 AssistId, 16 AssistId,
16}; 17};
17use ra_syntax::ast::{Expr, NameRef};
18 18
19// Assist: reorder_fields 19// Assist: reorder_fields
20// 20//
@@ -59,7 +59,6 @@ fn reorder<R: AstNode>(ctx: AssistCtx) -> Option<Assist> {
59} 59}
60 60
61fn get_fields_kind(node: &SyntaxNode) -> Vec<SyntaxKind> { 61fn get_fields_kind(node: &SyntaxNode) -> Vec<SyntaxKind> {
62 use SyntaxKind::*;
63 match node.kind() { 62 match node.kind() {
64 RECORD_LIT => vec![RECORD_FIELD], 63 RECORD_LIT => vec![RECORD_FIELD],
65 RECORD_PAT => vec![RECORD_FIELD_PAT, BIND_PAT], 64 RECORD_PAT => vec![RECORD_FIELD_PAT, BIND_PAT],
@@ -68,19 +67,14 @@ fn get_fields_kind(node: &SyntaxNode) -> Vec<SyntaxKind> {
68} 67}
69 68
70fn get_field_name(node: &SyntaxNode) -> String { 69fn get_field_name(node: &SyntaxNode) -> String {
71 use SyntaxKind::*; 70 let res = match_ast! {
72 match node.kind() { 71 match node {
73 RECORD_FIELD => { 72 ast::RecordField(field) => field.field_name().map(|it| it.to_string()),
74 if let Some(name) = node.children().find_map(NameRef::cast) { 73 ast::RecordFieldPat(field) => field.field_name().map(|it| it.to_string()),
75 return name.to_string(); 74 _ => None,
76 }
77 node.children().find_map(Expr::cast).map(|expr| expr.to_string()).unwrap_or_default()
78 }
79 BIND_PAT | RECORD_FIELD_PAT => {
80 node.children().find_map(Name::cast).map(|n| n.to_string()).unwrap_or_default()
81 } 75 }
82 _ => String::new(), 76 };
83 } 77 res.unwrap_or_default()
84} 78}
85 79
86fn get_fields(record: &SyntaxNode) -> Vec<SyntaxNode> { 80fn get_fields(record: &SyntaxNode) -> Vec<SyntaxNode> {
diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs
index c057dc8f2..79abe55ce 100644
--- a/crates/ra_hir_def/src/body/lower.rs
+++ b/crates/ra_hir_def/src/body/lower.rs
@@ -33,6 +33,7 @@ use crate::{
33}; 33};
34 34
35use super::{ExprSource, PatSource}; 35use super::{ExprSource, PatSource};
36use ast::AstChildren;
36 37
37pub(super) fn lower( 38pub(super) fn lower(
38 db: &dyn DefDatabase, 39 db: &dyn DefDatabase,
@@ -598,8 +599,8 @@ impl ExprCollector<'_> {
598 } 599 }
599 ast::Pat::TupleStructPat(p) => { 600 ast::Pat::TupleStructPat(p) => {
600 let path = p.path().and_then(|path| self.expander.parse_path(path)); 601 let path = p.path().and_then(|path| self.expander.parse_path(path));
601 let args = p.args().map(|p| self.collect_pat(p)).collect(); 602 let (args, ellipsis) = self.collect_tuple_pat(p.args());
602 Pat::TupleStruct { path, args } 603 Pat::TupleStruct { path, args, ellipsis }
603 } 604 }
604 ast::Pat::RefPat(p) => { 605 ast::Pat::RefPat(p) => {
605 let pat = self.collect_pat_opt(p.pat()); 606 let pat = self.collect_pat_opt(p.pat());
@@ -616,10 +617,10 @@ impl ExprCollector<'_> {
616 } 617 }
617 ast::Pat::ParenPat(p) => return self.collect_pat_opt(p.pat()), 618 ast::Pat::ParenPat(p) => return self.collect_pat_opt(p.pat()),
618 ast::Pat::TuplePat(p) => { 619 ast::Pat::TuplePat(p) => {
619 let args = p.args().map(|p| self.collect_pat(p)).collect(); 620 let (args, ellipsis) = self.collect_tuple_pat(p.args());
620 Pat::Tuple(args) 621 Pat::Tuple { args, ellipsis }
621 } 622 }
622 ast::Pat::PlaceholderPat(_) | ast::Pat::DotDotPat(_) => Pat::Wild, 623 ast::Pat::PlaceholderPat(_) => Pat::Wild,
623 ast::Pat::RecordPat(p) => { 624 ast::Pat::RecordPat(p) => {
624 let path = p.path().and_then(|path| self.expander.parse_path(path)); 625 let path = p.path().and_then(|path| self.expander.parse_path(path));
625 let record_field_pat_list = 626 let record_field_pat_list =
@@ -637,7 +638,7 @@ impl ExprCollector<'_> {
637 let iter = record_field_pat_list.record_field_pats().filter_map(|f| { 638 let iter = record_field_pat_list.record_field_pats().filter_map(|f| {
638 let ast_pat = f.pat()?; 639 let ast_pat = f.pat()?;
639 let pat = self.collect_pat(ast_pat); 640 let pat = self.collect_pat(ast_pat);
640 let name = f.name()?.as_name(); 641 let name = f.field_name()?.as_name();
641 Some(RecordFieldPat { name, pat }) 642 Some(RecordFieldPat { name, pat })
642 }); 643 });
643 fields.extend(iter); 644 fields.extend(iter);
@@ -665,6 +666,9 @@ impl ExprCollector<'_> {
665 Pat::Missing 666 Pat::Missing
666 } 667 }
667 } 668 }
669 ast::Pat::DotDotPat(_) => unreachable!(
670 "`DotDotPat` requires special handling and should not be mapped to a Pat."
671 ),
668 // FIXME: implement 672 // FIXME: implement
669 ast::Pat::BoxPat(_) | ast::Pat::RangePat(_) | ast::Pat::MacroPat(_) => Pat::Missing, 673 ast::Pat::BoxPat(_) | ast::Pat::RangePat(_) | ast::Pat::MacroPat(_) => Pat::Missing,
670 }; 674 };
@@ -679,6 +683,19 @@ impl ExprCollector<'_> {
679 self.missing_pat() 683 self.missing_pat()
680 } 684 }
681 } 685 }
686
687 fn collect_tuple_pat(&mut self, args: AstChildren<ast::Pat>) -> (Vec<PatId>, Option<usize>) {
688 // Find the location of the `..`, if there is one. Note that we do not
689 // consider the possiblity of there being multiple `..` here.
690 let ellipsis = args.clone().position(|p| matches!(p, ast::Pat::DotDotPat(_)));
691 // We want to skip the `..` pattern here, since we account for it above.
692 let args = args
693 .filter(|p| !matches!(p, ast::Pat::DotDotPat(_)))
694 .map(|p| self.collect_pat(p))
695 .collect();
696
697 (args, ellipsis)
698 }
682} 699}
683 700
684impl From<ast::BinOp> for BinaryOp { 701impl From<ast::BinOp> for BinaryOp {
diff --git a/crates/ra_hir_def/src/expr.rs b/crates/ra_hir_def/src/expr.rs
index e11bdf3ec..a0cdad529 100644
--- a/crates/ra_hir_def/src/expr.rs
+++ b/crates/ra_hir_def/src/expr.rs
@@ -374,7 +374,7 @@ pub struct RecordFieldPat {
374pub enum Pat { 374pub enum Pat {
375 Missing, 375 Missing,
376 Wild, 376 Wild,
377 Tuple(Vec<PatId>), 377 Tuple { args: Vec<PatId>, ellipsis: Option<usize> },
378 Or(Vec<PatId>), 378 Or(Vec<PatId>),
379 Record { path: Option<Path>, args: Vec<RecordFieldPat>, ellipsis: bool }, 379 Record { path: Option<Path>, args: Vec<RecordFieldPat>, ellipsis: bool },
380 Range { start: ExprId, end: ExprId }, 380 Range { start: ExprId, end: ExprId },
@@ -382,7 +382,7 @@ pub enum Pat {
382 Path(Path), 382 Path(Path),
383 Lit(ExprId), 383 Lit(ExprId),
384 Bind { mode: BindingAnnotation, name: Name, subpat: Option<PatId> }, 384 Bind { mode: BindingAnnotation, name: Name, subpat: Option<PatId> },
385 TupleStruct { path: Option<Path>, args: Vec<PatId> }, 385 TupleStruct { path: Option<Path>, args: Vec<PatId>, ellipsis: Option<usize> },
386 Ref { pat: PatId, mutability: Mutability }, 386 Ref { pat: PatId, mutability: Mutability },
387} 387}
388 388
@@ -393,7 +393,7 @@ impl Pat {
393 Pat::Bind { subpat, .. } => { 393 Pat::Bind { subpat, .. } => {
394 subpat.iter().copied().for_each(f); 394 subpat.iter().copied().for_each(f);
395 } 395 }
396 Pat::Or(args) | Pat::Tuple(args) | Pat::TupleStruct { args, .. } => { 396 Pat::Or(args) | Pat::Tuple { args, .. } | Pat::TupleStruct { args, .. } => {
397 args.iter().copied().for_each(f); 397 args.iter().copied().for_each(f);
398 } 398 }
399 Pat::Ref { pat, .. } => f(*pat), 399 Pat::Ref { pat, .. } => f(*pat),
diff --git a/crates/ra_hir_expand/src/name.rs b/crates/ra_hir_expand/src/name.rs
index 25cc1e9fc..fecce224e 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 {
83 } 83 }
84} 84}
85 85
86impl AsName for ast::NameOrNameRef {
87 fn as_name(&self) -> Name {
88 match self {
89 ast::NameOrNameRef::Name(it) => it.as_name(),
90 ast::NameOrNameRef::NameRef(it) => it.as_name(),
91 }
92 }
93}
94
86impl AsName for tt::Ident { 95impl AsName for tt::Ident {
87 fn as_name(&self) -> Name { 96 fn as_name(&self) -> Name {
88 Name::resolve(&self.text) 97 Name::resolve(&self.text)
diff --git a/crates/ra_hir_ty/src/_match.rs b/crates/ra_hir_ty/src/_match.rs
index 9e9a9d047..a64be9848 100644
--- a/crates/ra_hir_ty/src/_match.rs
+++ b/crates/ra_hir_ty/src/_match.rs
@@ -289,7 +289,7 @@ impl PatStack {
289 Self::from_slice(&self.0[1..]) 289 Self::from_slice(&self.0[1..])
290 } 290 }
291 291
292 fn replace_head_with(&self, pat_ids: &[PatId]) -> PatStack { 292 fn replace_head_with<T: Into<PatIdOrWild> + Copy>(&self, pat_ids: &[T]) -> PatStack {
293 let mut patterns: PatStackInner = smallvec![]; 293 let mut patterns: PatStackInner = smallvec![];
294 for pat in pat_ids { 294 for pat in pat_ids {
295 patterns.push((*pat).into()); 295 patterns.push((*pat).into());
@@ -320,12 +320,14 @@ impl PatStack {
320 constructor: &Constructor, 320 constructor: &Constructor,
321 ) -> MatchCheckResult<Option<PatStack>> { 321 ) -> MatchCheckResult<Option<PatStack>> {
322 let result = match (self.head().as_pat(cx), constructor) { 322 let result = match (self.head().as_pat(cx), constructor) {
323 (Pat::Tuple(ref pat_ids), Constructor::Tuple { arity }) => { 323 (Pat::Tuple { args: ref pat_ids, ellipsis }, Constructor::Tuple { arity: _ }) => {
324 debug_assert_eq!( 324 if ellipsis.is_some() {
325 pat_ids.len(), 325 // If there are ellipsis here, we should add the correct number of
326 *arity, 326 // Pat::Wild patterns to `pat_ids`. We should be able to use the
327 "we type check before calling this code, so we should never hit this case", 327 // constructors arity for this, but at the time of writing we aren't
328 ); 328 // correctly calculating this arity when ellipsis are present.
329 return Err(MatchCheckErr::NotImplemented);
330 }
329 331
330 Some(self.replace_head_with(pat_ids)) 332 Some(self.replace_head_with(pat_ids))
331 } 333 }
@@ -351,19 +353,47 @@ impl PatStack {
351 Some(self.to_tail()) 353 Some(self.to_tail())
352 } 354 }
353 } 355 }
354 (Pat::TupleStruct { args: ref pat_ids, .. }, Constructor::Enum(enum_constructor)) => { 356 (
357 Pat::TupleStruct { args: ref pat_ids, ellipsis, .. },
358 Constructor::Enum(enum_constructor),
359 ) => {
355 let pat_id = self.head().as_id().expect("we know this isn't a wild"); 360 let pat_id = self.head().as_id().expect("we know this isn't a wild");
356 if !enum_variant_matches(cx, pat_id, *enum_constructor) { 361 if !enum_variant_matches(cx, pat_id, *enum_constructor) {
357 None 362 None
358 } else { 363 } else {
359 // If the enum variant matches, then we need to confirm 364 let constructor_arity = constructor.arity(cx)?;
360 // that the number of patterns aligns with the expected 365 if let Some(ellipsis_position) = ellipsis {
361 // number of patterns for that enum variant. 366 // If there are ellipsis in the pattern, the ellipsis must take the place
362 if pat_ids.len() != constructor.arity(cx)? { 367 // of at least one sub-pattern, so `pat_ids` should be smaller than the
363 return Err(MatchCheckErr::MalformedMatchArm); 368 // constructor arity.
369 if pat_ids.len() < constructor_arity {
370 let mut new_patterns: Vec<PatIdOrWild> = vec![];
371
372 for pat_id in &pat_ids[0..ellipsis_position] {
373 new_patterns.push((*pat_id).into());
374 }
375
376 for _ in 0..(constructor_arity - pat_ids.len()) {
377 new_patterns.push(PatIdOrWild::Wild);
378 }
379
380 for pat_id in &pat_ids[ellipsis_position..pat_ids.len()] {
381 new_patterns.push((*pat_id).into());
382 }
383
384 Some(self.replace_head_with(&new_patterns))
385 } else {
386 return Err(MatchCheckErr::MalformedMatchArm);
387 }
388 } else {
389 // If there is no ellipsis in the tuple pattern, the number
390 // of patterns must equal the constructor arity.
391 if pat_ids.len() == constructor_arity {
392 Some(self.replace_head_with(pat_ids))
393 } else {
394 return Err(MatchCheckErr::MalformedMatchArm);
395 }
364 } 396 }
365
366 Some(self.replace_head_with(pat_ids))
367 } 397 }
368 } 398 }
369 (Pat::Or(_), _) => return Err(MatchCheckErr::NotImplemented), 399 (Pat::Or(_), _) => return Err(MatchCheckErr::NotImplemented),
@@ -644,7 +674,11 @@ impl Constructor {
644fn pat_constructor(cx: &MatchCheckCtx, pat: PatIdOrWild) -> MatchCheckResult<Option<Constructor>> { 674fn pat_constructor(cx: &MatchCheckCtx, pat: PatIdOrWild) -> MatchCheckResult<Option<Constructor>> {
645 let res = match pat.as_pat(cx) { 675 let res = match pat.as_pat(cx) {
646 Pat::Wild => None, 676 Pat::Wild => None,
647 Pat::Tuple(pats) => Some(Constructor::Tuple { arity: pats.len() }), 677 // FIXME somehow create the Tuple constructor with the proper arity. If there are
678 // ellipsis, the arity is not equal to the number of patterns.
679 Pat::Tuple { args: pats, ellipsis } if ellipsis.is_none() => {
680 Some(Constructor::Tuple { arity: pats.len() })
681 }
648 Pat::Lit(lit_expr) => match cx.body.exprs[lit_expr] { 682 Pat::Lit(lit_expr) => match cx.body.exprs[lit_expr] {
649 Expr::Literal(Literal::Bool(val)) => Some(Constructor::Bool(val)), 683 Expr::Literal(Literal::Bool(val)) => Some(Constructor::Bool(val)),
650 _ => return Err(MatchCheckErr::NotImplemented), 684 _ => return Err(MatchCheckErr::NotImplemented),
@@ -973,6 +1007,47 @@ mod tests {
973 } 1007 }
974 1008
975 #[test] 1009 #[test]
1010 fn tuple_of_bools_with_ellipsis_at_end_no_diagnostic() {
1011 let content = r"
1012 fn test_fn() {
1013 match (false, true, false) {
1014 (false, ..) => {},
1015 (true, ..) => {},
1016 }
1017 }
1018 ";
1019
1020 check_no_diagnostic(content);
1021 }
1022
1023 #[test]
1024 fn tuple_of_bools_with_ellipsis_at_beginning_no_diagnostic() {
1025 let content = r"
1026 fn test_fn() {
1027 match (false, true, false) {
1028 (.., false) => {},
1029 (.., true) => {},
1030 }
1031 }
1032 ";
1033
1034 check_no_diagnostic(content);
1035 }
1036
1037 #[test]
1038 fn tuple_of_bools_with_ellipsis_no_diagnostic() {
1039 let content = r"
1040 fn test_fn() {
1041 match (false, true, false) {
1042 (..) => {},
1043 }
1044 }
1045 ";
1046
1047 check_no_diagnostic(content);
1048 }
1049
1050 #[test]
976 fn tuple_of_tuple_and_bools_no_arms() { 1051 fn tuple_of_tuple_and_bools_no_arms() {
977 let content = r" 1052 let content = r"
978 fn test_fn() { 1053 fn test_fn() {
@@ -1315,8 +1390,9 @@ mod tests {
1315 } 1390 }
1316 "; 1391 ";
1317 1392
1318 // Match arms with the incorrect type are filtered out. 1393 // Match statements with arms that don't match the
1319 check_diagnostic(content); 1394 // expression pattern do not fire this diagnostic.
1395 check_no_diagnostic(content);
1320 } 1396 }
1321 1397
1322 #[test] 1398 #[test]
@@ -1330,8 +1406,9 @@ mod tests {
1330 } 1406 }
1331 "; 1407 ";
1332 1408
1333 // Match arms with the incorrect type are filtered out. 1409 // Match statements with arms that don't match the
1334 check_diagnostic(content); 1410 // expression pattern do not fire this diagnostic.
1411 check_no_diagnostic(content);
1335 } 1412 }
1336 1413
1337 #[test] 1414 #[test]
@@ -1344,8 +1421,9 @@ mod tests {
1344 } 1421 }
1345 "; 1422 ";
1346 1423
1347 // Match arms with the incorrect type are filtered out. 1424 // Match statements with arms that don't match the
1348 check_diagnostic(content); 1425 // expression pattern do not fire this diagnostic.
1426 check_no_diagnostic(content);
1349 } 1427 }
1350 1428
1351 #[test] 1429 #[test]
@@ -1383,6 +1461,163 @@ mod tests {
1383 // we don't create a diagnostic). 1461 // we don't create a diagnostic).
1384 check_no_diagnostic(content); 1462 check_no_diagnostic(content);
1385 } 1463 }
1464
1465 #[test]
1466 fn expr_diverges() {
1467 let content = r"
1468 enum Either {
1469 A,
1470 B,
1471 }
1472 fn test_fn() {
1473 match loop {} {
1474 Either::A => (),
1475 Either::B => (),
1476 }
1477 }
1478 ";
1479
1480 check_no_diagnostic(content);
1481 }
1482
1483 #[test]
1484 fn expr_loop_with_break() {
1485 let content = r"
1486 enum Either {
1487 A,
1488 B,
1489 }
1490 fn test_fn() {
1491 match loop { break Foo::A } {
1492 Either::A => (),
1493 Either::B => (),
1494 }
1495 }
1496 ";
1497
1498 check_no_diagnostic(content);
1499 }
1500
1501 #[test]
1502 fn expr_partially_diverges() {
1503 let content = r"
1504 enum Either<T> {
1505 A(T),
1506 B,
1507 }
1508 fn foo() -> Either<!> {
1509 Either::B
1510 }
1511 fn test_fn() -> u32 {
1512 match foo() {
1513 Either::A(val) => val,
1514 Either::B => 0,
1515 }
1516 }
1517 ";
1518
1519 check_no_diagnostic(content);
1520 }
1521
1522 #[test]
1523 fn enum_tuple_partial_ellipsis_no_diagnostic() {
1524 let content = r"
1525 enum Either {
1526 A(bool, bool, bool, bool),
1527 B,
1528 }
1529 fn test_fn() {
1530 match Either::B {
1531 Either::A(true, .., true) => {},
1532 Either::A(true, .., false) => {},
1533 Either::A(false, .., true) => {},
1534 Either::A(false, .., false) => {},
1535 Either::B => {},
1536 }
1537 }
1538 ";
1539
1540 check_no_diagnostic(content);
1541 }
1542
1543 #[test]
1544 fn enum_tuple_partial_ellipsis_2_no_diagnostic() {
1545 let content = r"
1546 enum Either {
1547 A(bool, bool, bool, bool),
1548 B,
1549 }
1550 fn test_fn() {
1551 match Either::B {
1552 Either::A(true, .., true) => {},
1553 Either::A(true, .., false) => {},
1554 Either::A(.., true) => {},
1555 Either::A(.., false) => {},
1556 Either::B => {},
1557 }
1558 }
1559 ";
1560
1561 check_no_diagnostic(content);
1562 }
1563
1564 #[test]
1565 fn enum_tuple_partial_ellipsis_missing_arm() {
1566 let content = r"
1567 enum Either {
1568 A(bool, bool, bool, bool),
1569 B,
1570 }
1571 fn test_fn() {
1572 match Either::B {
1573 Either::A(true, .., true) => {},
1574 Either::A(true, .., false) => {},
1575 Either::A(false, .., false) => {},
1576 Either::B => {},
1577 }
1578 }
1579 ";
1580
1581 check_diagnostic(content);
1582 }
1583
1584 #[test]
1585 fn enum_tuple_partial_ellipsis_2_missing_arm() {
1586 let content = r"
1587 enum Either {
1588 A(bool, bool, bool, bool),
1589 B,
1590 }
1591 fn test_fn() {
1592 match Either::B {
1593 Either::A(true, .., true) => {},
1594 Either::A(true, .., false) => {},
1595 Either::A(.., true) => {},
1596 Either::B => {},
1597 }
1598 }
1599 ";
1600
1601 check_diagnostic(content);
1602 }
1603
1604 #[test]
1605 fn enum_tuple_ellipsis_no_diagnostic() {
1606 let content = r"
1607 enum Either {
1608 A(bool, bool, bool, bool),
1609 B,
1610 }
1611 fn test_fn() {
1612 match Either::B {
1613 Either::A(..) => {},
1614 Either::B => {},
1615 }
1616 }
1617 ";
1618
1619 check_no_diagnostic(content);
1620 }
1386} 1621}
1387 1622
1388#[cfg(test)] 1623#[cfg(test)]
@@ -1452,4 +1687,75 @@ mod false_negatives {
1452 // We do not currently handle patterns with internal `or`s. 1687 // We do not currently handle patterns with internal `or`s.
1453 check_no_diagnostic(content); 1688 check_no_diagnostic(content);
1454 } 1689 }
1690
1691 #[test]
1692 fn expr_diverges_missing_arm() {
1693 let content = r"
1694 enum Either {
1695 A,
1696 B,
1697 }
1698 fn test_fn() {
1699 match loop {} {
1700 Either::A => (),
1701 }
1702 }
1703 ";
1704
1705 // This is a false negative.
1706 // Even though the match expression diverges, rustc fails
1707 // to compile here since `Either::B` is missing.
1708 check_no_diagnostic(content);
1709 }
1710
1711 #[test]
1712 fn expr_loop_missing_arm() {
1713 let content = r"
1714 enum Either {
1715 A,
1716 B,
1717 }
1718 fn test_fn() {
1719 match loop { break Foo::A } {
1720 Either::A => (),
1721 }
1722 }
1723 ";
1724
1725 // This is a false negative.
1726 // We currently infer the type of `loop { break Foo::A }` to `!`, which
1727 // causes us to skip the diagnostic since `Either::A` doesn't type check
1728 // with `!`.
1729 check_no_diagnostic(content);
1730 }
1731
1732 #[test]
1733 fn tuple_of_bools_with_ellipsis_at_end_missing_arm() {
1734 let content = r"
1735 fn test_fn() {
1736 match (false, true, false) {
1737 (false, ..) => {},
1738 }
1739 }
1740 ";
1741
1742 // This is a false negative.
1743 // We don't currently handle tuple patterns with ellipsis.
1744 check_no_diagnostic(content);
1745 }
1746
1747 #[test]
1748 fn tuple_of_bools_with_ellipsis_at_beginning_missing_arm() {
1749 let content = r"
1750 fn test_fn() {
1751 match (false, true, false) {
1752 (.., false) => {},
1753 }
1754 }
1755 ";
1756
1757 // This is a false negative.
1758 // We don't currently handle tuple patterns with ellipsis.
1759 check_no_diagnostic(content);
1760 }
1455} 1761}
diff --git a/crates/ra_hir_ty/src/expr.rs b/crates/ra_hir_ty/src/expr.rs
index 69b527f74..21abbcf1e 100644
--- a/crates/ra_hir_ty/src/expr.rs
+++ b/crates/ra_hir_ty/src/expr.rs
@@ -161,12 +161,6 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
161 161
162 let mut seen = Matrix::empty(); 162 let mut seen = Matrix::empty();
163 for pat in pats { 163 for pat in pats {
164 // We skip any patterns whose type we cannot resolve.
165 //
166 // This could lead to false positives in this diagnostic, so
167 // it might be better to skip the entire diagnostic if we either
168 // cannot resolve a match arm or determine that the match arm has
169 // the wrong type.
170 if let Some(pat_ty) = infer.type_of_pat.get(pat) { 164 if let Some(pat_ty) = infer.type_of_pat.get(pat) {
171 // We only include patterns whose type matches the type 165 // We only include patterns whose type matches the type
172 // of the match expression. If we had a InvalidMatchArmPattern 166 // of the match expression. If we had a InvalidMatchArmPattern
@@ -189,8 +183,15 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
189 // to the matrix here. 183 // to the matrix here.
190 let v = PatStack::from_pattern(pat); 184 let v = PatStack::from_pattern(pat);
191 seen.push(&cx, v); 185 seen.push(&cx, v);
186 continue;
192 } 187 }
193 } 188 }
189
190 // If we can't resolve the type of a pattern, or the pattern type doesn't
191 // fit the match expression, we skip this diagnostic. Skipping the entire
192 // diagnostic rather than just not including this match arm is preferred
193 // to avoid the chance of false positives.
194 return;
194 } 195 }
195 196
196 match is_useful(&cx, &seen, &PatStack::from_wild()) { 197 match is_useful(&cx, &seen, &PatStack::from_wild()) {
diff --git a/crates/ra_hir_ty/src/infer/pat.rs b/crates/ra_hir_ty/src/infer/pat.rs
index 078476f76..8ec4d4ace 100644
--- a/crates/ra_hir_ty/src/infer/pat.rs
+++ b/crates/ra_hir_ty/src/infer/pat.rs
@@ -85,7 +85,7 @@ impl<'a> InferenceContext<'a> {
85 let body = Arc::clone(&self.body); // avoid borrow checker problem 85 let body = Arc::clone(&self.body); // avoid borrow checker problem
86 86
87 let is_non_ref_pat = match &body[pat] { 87 let is_non_ref_pat = match &body[pat] {
88 Pat::Tuple(..) 88 Pat::Tuple { .. }
89 | Pat::Or(..) 89 | Pat::Or(..)
90 | Pat::TupleStruct { .. } 90 | Pat::TupleStruct { .. }
91 | Pat::Record { .. } 91 | Pat::Record { .. }
@@ -116,7 +116,7 @@ impl<'a> InferenceContext<'a> {
116 let expected = expected; 116 let expected = expected;
117 117
118 let ty = match &body[pat] { 118 let ty = match &body[pat] {
119 Pat::Tuple(ref args) => { 119 Pat::Tuple { ref args, .. } => {
120 let expectations = match expected.as_tuple() { 120 let expectations = match expected.as_tuple() {
121 Some(parameters) => &*parameters.0, 121 Some(parameters) => &*parameters.0,
122 _ => &[], 122 _ => &[],
@@ -155,7 +155,7 @@ impl<'a> InferenceContext<'a> {
155 let subty = self.infer_pat(*pat, expectation, default_bm); 155 let subty = self.infer_pat(*pat, expectation, default_bm);
156 Ty::apply_one(TypeCtor::Ref(*mutability), subty) 156 Ty::apply_one(TypeCtor::Ref(*mutability), subty)
157 } 157 }
158 Pat::TupleStruct { path: p, args: subpats } => { 158 Pat::TupleStruct { path: p, args: subpats, .. } => {
159 self.infer_tuple_struct_pat(p.as_ref(), subpats, expected, default_bm, pat) 159 self.infer_tuple_struct_pat(p.as_ref(), subpats, expected, default_bm, pat)
160 } 160 }
161 Pat::Record { path: p, args: fields, ellipsis: _ } => { 161 Pat::Record { path: p, args: fields, ellipsis: _ } => {
diff --git a/crates/ra_hir_ty/src/tests/patterns.rs b/crates/ra_hir_ty/src/tests/patterns.rs
index 6e5d2247c..07cbc521a 100644
--- a/crates/ra_hir_ty/src/tests/patterns.rs
+++ b/crates/ra_hir_ty/src/tests/patterns.rs
@@ -1,7 +1,8 @@
1use super::{infer, infer_with_mismatches};
2use insta::assert_snapshot; 1use insta::assert_snapshot;
3use test_utils::covers; 2use test_utils::covers;
4 3
4use super::{infer, infer_with_mismatches};
5
5#[test] 6#[test]
6fn infer_pattern() { 7fn infer_pattern() {
7 assert_snapshot!( 8 assert_snapshot!(
diff --git a/crates/ra_ide/src/call_info.rs b/crates/ra_ide/src/call_info.rs
index ca57eceff..f95b6baf3 100644
--- a/crates/ra_ide/src/call_info.rs
+++ b/crates/ra_ide/src/call_info.rs
@@ -208,9 +208,20 @@ mod tests {
208 } 208 }
209 } 209 }
210 210
211 fn call_info(text: &str) -> CallInfo { 211 fn call_info_helper(text: &str) -> Option<CallInfo> {
212 let (analysis, position) = single_file_with_position(text); 212 let (analysis, position) = single_file_with_position(text);
213 analysis.call_info(position).unwrap().unwrap() 213 analysis.call_info(position).unwrap()
214 }
215
216 fn call_info(text: &str) -> CallInfo {
217 let info = call_info_helper(text);
218 assert!(info.is_some());
219 info.unwrap()
220 }
221
222 fn no_call_info(text: &str) {
223 let info = call_info_helper(text);
224 assert!(info.is_none());
214 } 225 }
215 226
216 #[test] 227 #[test]
@@ -558,9 +569,8 @@ fn main() {
558 } 569 }
559 570
560 #[test] 571 #[test]
561 #[should_panic]
562 fn cant_call_named_structs() { 572 fn cant_call_named_structs() {
563 let _ = call_info( 573 no_call_info(
564 r#" 574 r#"
565struct TS { x: u32, y: i32 } 575struct TS { x: u32, y: i32 }
566fn main() { 576fn main() {
@@ -594,9 +604,8 @@ fn main() {
594 } 604 }
595 605
596 #[test] 606 #[test]
597 #[should_panic]
598 fn cant_call_enum_records() { 607 fn cant_call_enum_records() {
599 let _ = call_info( 608 no_call_info(
600 r#" 609 r#"
601enum E { 610enum E {
602 /// A Variant 611 /// A Variant
diff --git a/crates/ra_ide/src/completion/complete_pattern.rs b/crates/ra_ide/src/completion/complete_pattern.rs
index 1b7d3122f..a8b4ce114 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) {
7 if !ctx.is_pat_binding_or_const { 7 if !ctx.is_pat_binding_or_const {
8 return; 8 return;
9 } 9 }
10 if ctx.record_pat_syntax.is_some() {
11 return;
12 }
13
10 // FIXME: ideally, we should look at the type we are matching against and 14 // FIXME: ideally, we should look at the type we are matching against and
11 // suggest variants + auto-imports 15 // suggest variants + auto-imports
12 ctx.scope().process_all_names(&mut |name, res| { 16 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 f46bcee5c..83a553155 100644
--- a/crates/ra_ide/src/completion/complete_record.rs
+++ b/crates/ra_ide/src/completion/complete_record.rs
@@ -2,7 +2,7 @@
2use crate::completion::{CompletionContext, Completions}; 2use crate::completion::{CompletionContext, Completions};
3 3
4pub(super) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { 4pub(super) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
5 let missing_fields = match (ctx.record_lit_pat.as_ref(), ctx.record_lit_syntax.as_ref()) { 5 let missing_fields = match (ctx.record_pat_syntax.as_ref(), ctx.record_lit_syntax.as_ref()) {
6 (None, None) => return None, 6 (None, None) => return None,
7 (Some(_), Some(_)) => unreachable!("A record cannot be both a literal and a pattern"), 7 (Some(_), Some(_)) => unreachable!("A record cannot be both a literal and a pattern"),
8 (Some(record_pat), _) => ctx.sema.record_pattern_missing_fields(record_pat), 8 (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 0b0da6ee4..2d8e0776c 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 @@
3use crate::completion::{CompletionContext, Completions}; 3use crate::completion::{CompletionContext, Completions};
4 4
5pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { 5pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
6 if !(ctx.is_trivial_path && !ctx.is_pat_binding_or_const && !ctx.record_lit_syntax.is_some()) { 6 if !ctx.is_trivial_path {
7 return;
8 }
9
10 if ctx.is_pat_binding_or_const
11 || ctx.record_lit_syntax.is_some()
12 || ctx.record_pat_syntax.is_some()
13 {
7 return; 14 return;
8 } 15 }
9 16
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs
index eb8016dd1..da054f7a2 100644
--- a/crates/ra_ide/src/completion/completion_context.rs
+++ b/crates/ra_ide/src/completion/completion_context.rs
@@ -31,7 +31,7 @@ pub(crate) struct CompletionContext<'a> {
31 pub(super) function_syntax: Option<ast::FnDef>, 31 pub(super) function_syntax: Option<ast::FnDef>,
32 pub(super) use_item_syntax: Option<ast::UseItem>, 32 pub(super) use_item_syntax: Option<ast::UseItem>,
33 pub(super) record_lit_syntax: Option<ast::RecordLit>, 33 pub(super) record_lit_syntax: Option<ast::RecordLit>,
34 pub(super) record_lit_pat: Option<ast::RecordPat>, 34 pub(super) record_pat_syntax: Option<ast::RecordPat>,
35 pub(super) impl_def: Option<ast::ImplDef>, 35 pub(super) impl_def: Option<ast::ImplDef>,
36 pub(super) call_info: Option<CallInfo>, 36 pub(super) call_info: Option<CallInfo>,
37 pub(super) is_param: bool, 37 pub(super) is_param: bool,
@@ -97,7 +97,7 @@ impl<'a> CompletionContext<'a> {
97 call_info: None, 97 call_info: None,
98 use_item_syntax: None, 98 use_item_syntax: None,
99 record_lit_syntax: None, 99 record_lit_syntax: None,
100 record_lit_pat: None, 100 record_pat_syntax: None,
101 impl_def: None, 101 impl_def: None,
102 is_param: false, 102 is_param: false,
103 is_pat_binding_or_const: false, 103 is_pat_binding_or_const: false,
@@ -186,6 +186,11 @@ impl<'a> CompletionContext<'a> {
186 self.is_param = true; 186 self.is_param = true;
187 return; 187 return;
188 } 188 }
189 // FIXME: remove this (V) duplication and make the check more precise
190 if name_ref.syntax().ancestors().find_map(ast::RecordFieldPatList::cast).is_some() {
191 self.record_pat_syntax =
192 self.sema.find_node_at_offset_with_macros(&original_file, offset);
193 }
189 self.classify_name_ref(original_file, name_ref, offset); 194 self.classify_name_ref(original_file, name_ref, offset);
190 } 195 }
191 196
@@ -215,8 +220,9 @@ impl<'a> CompletionContext<'a> {
215 self.is_param = true; 220 self.is_param = true;
216 return; 221 return;
217 } 222 }
223 // FIXME: remove this (^) duplication and make the check more precise
218 if name.syntax().ancestors().find_map(ast::RecordFieldPatList::cast).is_some() { 224 if name.syntax().ancestors().find_map(ast::RecordFieldPatList::cast).is_some() {
219 self.record_lit_pat = 225 self.record_pat_syntax =
220 self.sema.find_node_at_offset_with_macros(&original_file, offset); 226 self.sema.find_node_at_offset_with_macros(&original_file, offset);
221 } 227 }
222 } 228 }
diff --git a/crates/ra_parser/src/grammar.rs b/crates/ra_parser/src/grammar.rs
index d0530955e..c2a6e82e9 100644
--- a/crates/ra_parser/src/grammar.rs
+++ b/crates/ra_parser/src/grammar.rs
@@ -282,13 +282,10 @@ fn name_ref(p: &mut Parser) {
282} 282}
283 283
284fn name_ref_or_index(p: &mut Parser) { 284fn name_ref_or_index(p: &mut Parser) {
285 if p.at(IDENT) || p.at(INT_NUMBER) { 285 assert!(p.at(IDENT) || p.at(INT_NUMBER));
286 let m = p.start(); 286 let m = p.start();
287 p.bump_any(); 287 p.bump_any();
288 m.complete(p, NAME_REF); 288 m.complete(p, NAME_REF);
289 } else {
290 p.err_and_bump("expected identifier");
291 }
292} 289}
293 290
294fn error_block(p: &mut Parser, message: &str) { 291fn error_block(p: &mut Parser, message: &str) {
diff --git a/crates/ra_parser/src/grammar/patterns.rs b/crates/ra_parser/src/grammar/patterns.rs
index 936d27575..68fb2fc73 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) {
192 match p.current() { 192 match p.current() {
193 // A trailing `..` is *not* treated as a DOT_DOT_PAT. 193 // A trailing `..` is *not* treated as a DOT_DOT_PAT.
194 T![.] if p.at(T![..]) => p.bump(T![..]), 194 T![.] if p.at(T![..]) => p.bump(T![..]),
195
196 IDENT | INT_NUMBER if p.nth(1) == T![:] => record_field_pat(p),
197 T!['{'] => error_block(p, "expected ident"), 195 T!['{'] => error_block(p, "expected ident"),
198 T![box] => { 196
199 box_pat(p); 197 c => {
200 } 198 let m = p.start();
201 _ => { 199 match c {
202 bind_pat(p, false); 200 // test record_field_pat
201 // fn foo() {
202 // let S { 0: 1 } = ();
203 // let S { x: 1 } = ();
204 // }
205 IDENT | INT_NUMBER if p.nth(1) == T![:] => {
206 name_ref_or_index(p);
207 p.bump(T![:]);
208 pattern(p);
209 }
210 T![box] => {
211 // FIXME: not all box patterns should be allowed
212 box_pat(p);
213 }
214 _ => {
215 bind_pat(p, false);
216 }
217 }
218 m.complete(p, RECORD_FIELD_PAT);
203 } 219 }
204 } 220 }
205 if !p.at(T!['}']) { 221 if !p.at(T!['}']) {
@@ -210,26 +226,6 @@ fn record_field_pat_list(p: &mut Parser) {
210 m.complete(p, RECORD_FIELD_PAT_LIST); 226 m.complete(p, RECORD_FIELD_PAT_LIST);
211} 227}
212 228
213// test record_field_pat
214// fn foo() {
215// let S { 0: 1 } = ();
216// let S { x: 1 } = ();
217// }
218fn record_field_pat(p: &mut Parser) {
219 assert!(p.at(IDENT) || p.at(INT_NUMBER));
220 assert!(p.nth(1) == T![:]);
221
222 let m = p.start();
223
224 if !p.eat(INT_NUMBER) {
225 name(p)
226 }
227
228 p.bump_any();
229 pattern(p);
230 m.complete(p, RECORD_FIELD_PAT);
231}
232
233// test placeholder_pat 229// test placeholder_pat
234// fn main() { let _ = (); } 230// fn main() { let _ = (); }
235fn placeholder_pat(p: &mut Parser) -> CompletedMarker { 231fn placeholder_pat(p: &mut Parser) -> CompletedMarker {
diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs
index 99c6b7219..7fca5661e 100644
--- a/crates/ra_syntax/src/ast.rs
+++ b/crates/ra_syntax/src/ast.rs
@@ -18,8 +18,8 @@ use crate::{
18pub use self::{ 18pub use self::{
19 expr_extensions::{ArrayExprKind, BinOp, ElseBranch, LiteralKind, PrefixOp, RangeOp}, 19 expr_extensions::{ArrayExprKind, BinOp, ElseBranch, LiteralKind, PrefixOp, RangeOp},
20 extensions::{ 20 extensions::{
21 AttrKind, FieldKind, PathSegmentKind, SelfParamKind, SlicePatComponents, StructKind, 21 AttrKind, FieldKind, NameOrNameRef, PathSegmentKind, SelfParamKind, SlicePatComponents,
22 TypeBoundKind, VisibilityKind, 22 StructKind, TypeBoundKind, VisibilityKind,
23 }, 23 },
24 generated::{nodes::*, tokens::*}, 24 generated::{nodes::*, tokens::*},
25 tokens::*, 25 tokens::*,
diff --git a/crates/ra_syntax/src/ast/extensions.rs b/crates/ra_syntax/src/ast/extensions.rs
index 63e272fbf..f2ea5088e 100644
--- a/crates/ra_syntax/src/ast/extensions.rs
+++ b/crates/ra_syntax/src/ast/extensions.rs
@@ -1,6 +1,8 @@
1//! Various extension methods to ast Nodes, which are hard to code-generate. 1//! Various extension methods to ast Nodes, which are hard to code-generate.
2//! Extensions for various expressions live in a sibling `expr_extensions` module. 2//! Extensions for various expressions live in a sibling `expr_extensions` module.
3 3
4use std::fmt;
5
4use itertools::Itertools; 6use itertools::Itertools;
5use ra_parser::SyntaxKind; 7use ra_parser::SyntaxKind;
6 8
@@ -217,6 +219,34 @@ impl ast::RecordField {
217 } 219 }
218} 220}
219 221
222pub enum NameOrNameRef {
223 Name(ast::Name),
224 NameRef(ast::NameRef),
225}
226
227impl fmt::Display for NameOrNameRef {
228 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
229 match self {
230 NameOrNameRef::Name(it) => fmt::Display::fmt(it, f),
231 NameOrNameRef::NameRef(it) => fmt::Display::fmt(it, f),
232 }
233 }
234}
235
236impl ast::RecordFieldPat {
237 /// Deals with field init shorthand
238 pub fn field_name(&self) -> Option<NameOrNameRef> {
239 if let Some(name_ref) = self.name_ref() {
240 return Some(NameOrNameRef::NameRef(name_ref));
241 }
242 if let Some(ast::Pat::BindPat(pat)) = self.pat() {
243 let name = pat.name()?;
244 return Some(NameOrNameRef::Name(name));
245 }
246 None
247 }
248}
249
220impl ast::EnumVariant { 250impl ast::EnumVariant {
221 pub fn parent_enum(&self) -> ast::EnumDef { 251 pub fn parent_enum(&self) -> ast::EnumDef {
222 self.syntax() 252 self.syntax()
diff --git a/crates/ra_syntax/src/ast/generated/nodes.rs b/crates/ra_syntax/src/ast/generated/nodes.rs
index f1098755b..188f0df96 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 {
1806 fn syntax(&self) -> &SyntaxNode { &self.syntax } 1806 fn syntax(&self) -> &SyntaxNode { &self.syntax }
1807} 1807}
1808impl ast::AttrsOwner for RecordFieldPat {} 1808impl ast::AttrsOwner for RecordFieldPat {}
1809impl ast::NameOwner for RecordFieldPat {}
1810impl RecordFieldPat { 1809impl RecordFieldPat {
1810 pub fn name_ref(&self) -> Option<NameRef> { support::child(&self.syntax) }
1811 pub fn colon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![:]) } 1811 pub fn colon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![:]) }
1812 pub fn pat(&self) -> Option<Pat> { support::child(&self.syntax) } 1812 pub fn pat(&self) -> Option<Pat> { support::child(&self.syntax) }
1813} 1813}
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 c2614543c..fcd099de9 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)
44 RECORD_FIELD_PAT_LIST@[40; 56) 44 RECORD_FIELD_PAT_LIST@[40; 56)
45 L_CURLY@[40; 41) "{" 45 L_CURLY@[40; 41) "{"
46 WHITESPACE@[41; 42) " " 46 WHITESPACE@[41; 42) " "
47 BIND_PAT@[42; 43) 47 RECORD_FIELD_PAT@[42; 43)
48 NAME@[42; 43) 48 BIND_PAT@[42; 43)
49 IDENT@[42; 43) "f" 49 NAME@[42; 43)
50 IDENT@[42; 43) "f"
50 COMMA@[43; 44) "," 51 COMMA@[43; 44) ","
51 WHITESPACE@[44; 45) " " 52 WHITESPACE@[44; 45) " "
52 BIND_PAT@[45; 54) 53 RECORD_FIELD_PAT@[45; 54)
53 REF_KW@[45; 48) "ref" 54 BIND_PAT@[45; 54)
54 WHITESPACE@[48; 49) " " 55 REF_KW@[45; 48) "ref"
55 MUT_KW@[49; 52) "mut" 56 WHITESPACE@[48; 49) " "
56 WHITESPACE@[52; 53) " " 57 MUT_KW@[49; 52) "mut"
57 NAME@[53; 54) 58 WHITESPACE@[52; 53) " "
58 IDENT@[53; 54) "g" 59 NAME@[53; 54)
60 IDENT@[53; 54) "g"
59 WHITESPACE@[54; 55) " " 61 WHITESPACE@[54; 55) " "
60 R_CURLY@[55; 56) "}" 62 R_CURLY@[55; 56) "}"
61 WHITESPACE@[56; 57) " " 63 WHITESPACE@[56; 57) " "
@@ -79,7 +81,7 @@ SOURCE_FILE@[0; 119)
79 L_CURLY@[73; 74) "{" 81 L_CURLY@[73; 74) "{"
80 WHITESPACE@[74; 75) " " 82 WHITESPACE@[74; 75) " "
81 RECORD_FIELD_PAT@[75; 79) 83 RECORD_FIELD_PAT@[75; 79)
82 NAME@[75; 76) 84 NAME_REF@[75; 76)
83 IDENT@[75; 76) "h" 85 IDENT@[75; 76) "h"
84 COLON@[76; 77) ":" 86 COLON@[76; 77) ":"
85 WHITESPACE@[77; 78) " " 87 WHITESPACE@[77; 78) " "
@@ -110,7 +112,7 @@ SOURCE_FILE@[0; 119)
110 L_CURLY@[101; 102) "{" 112 L_CURLY@[101; 102) "{"
111 WHITESPACE@[102; 103) " " 113 WHITESPACE@[102; 103) " "
112 RECORD_FIELD_PAT@[103; 107) 114 RECORD_FIELD_PAT@[103; 107)
113 NAME@[103; 104) 115 NAME_REF@[103; 104)
114 IDENT@[103; 104) "h" 116 IDENT@[103; 104) "h"
115 COLON@[104; 105) ":" 117 COLON@[104; 105) ":"
116 WHITESPACE@[105; 106) " " 118 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 f75673070..1d245f8f3 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)
44 RECORD_FIELD_PAT_LIST@[50; 81) 44 RECORD_FIELD_PAT_LIST@[50; 81)
45 L_CURLY@[50; 51) "{" 45 L_CURLY@[50; 51) "{"
46 WHITESPACE@[51; 52) " " 46 WHITESPACE@[51; 52) " "
47 BOX_PAT@[52; 57) 47 RECORD_FIELD_PAT@[52; 57)
48 BOX_KW@[52; 55) "box" 48 BOX_PAT@[52; 57)
49 WHITESPACE@[55; 56) " " 49 BOX_KW@[52; 55) "box"
50 BIND_PAT@[56; 57) 50 WHITESPACE@[55; 56) " "
51 NAME@[56; 57) 51 BIND_PAT@[56; 57)
52 IDENT@[56; 57) "i" 52 NAME@[56; 57)
53 IDENT@[56; 57) "i"
53 COMMA@[57; 58) "," 54 COMMA@[57; 58) ","
54 WHITESPACE@[58; 59) " " 55 WHITESPACE@[58; 59) " "
55 RECORD_FIELD_PAT@[59; 79) 56 RECORD_FIELD_PAT@[59; 79)
56 NAME@[59; 60) 57 NAME_REF@[59; 60)
57 IDENT@[59; 60) "j" 58 IDENT@[59; 60) "j"
58 COLON@[60; 61) ":" 59 COLON@[60; 61) ":"
59 WHITESPACE@[61; 62) " " 60 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 0d786f597..cac2ffdcf 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)
25 L_CURLY@[21; 22) "{" 25 L_CURLY@[21; 22) "{"
26 WHITESPACE@[22; 23) " " 26 WHITESPACE@[22; 23) " "
27 RECORD_FIELD_PAT@[23; 27) 27 RECORD_FIELD_PAT@[23; 27)
28 INT_NUMBER@[23; 24) "0" 28 NAME_REF@[23; 24)
29 INT_NUMBER@[23; 24) "0"
29 COLON@[24; 25) ":" 30 COLON@[24; 25) ":"
30 WHITESPACE@[25; 26) " " 31 WHITESPACE@[25; 26) " "
31 LITERAL_PAT@[26; 27) 32 LITERAL_PAT@[26; 27)
@@ -54,7 +55,7 @@ SOURCE_FILE@[0; 63)
54 L_CURLY@[46; 47) "{" 55 L_CURLY@[46; 47) "{"
55 WHITESPACE@[47; 48) " " 56 WHITESPACE@[47; 48) " "
56 RECORD_FIELD_PAT@[48; 52) 57 RECORD_FIELD_PAT@[48; 52)
57 NAME@[48; 49) 58 NAME_REF@[48; 49)
58 IDENT@[48; 49) "x" 59 IDENT@[48; 49) "x"
59 COLON@[49; 50) ":" 60 COLON@[49; 50) ":"
60 WHITESPACE@[50; 51) " " 61 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 9b5954ebd..d0623ba90 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)
68 RECORD_FIELD_PAT_LIST@[59; 67) 68 RECORD_FIELD_PAT_LIST@[59; 67)
69 L_CURLY@[59; 60) "{" 69 L_CURLY@[59; 60) "{"
70 WHITESPACE@[60; 61) " " 70 WHITESPACE@[60; 61) " "
71 BIND_PAT@[61; 62) 71 RECORD_FIELD_PAT@[61; 62)
72 NAME@[61; 62) 72 BIND_PAT@[61; 62)
73 IDENT@[61; 62) "a" 73 NAME@[61; 62)
74 IDENT@[61; 62) "a"
74 COMMA@[62; 63) "," 75 COMMA@[62; 63) ","
75 WHITESPACE@[63; 64) " " 76 WHITESPACE@[63; 64) " "
76 BIND_PAT@[64; 65) 77 RECORD_FIELD_PAT@[64; 65)
77 NAME@[64; 65) 78 BIND_PAT@[64; 65)
78 IDENT@[64; 65) "b" 79 NAME@[64; 65)
80 IDENT@[64; 65) "b"
79 WHITESPACE@[65; 66) " " 81 WHITESPACE@[65; 66) " "
80 R_CURLY@[66; 67) "}" 82 R_CURLY@[66; 67) "}"
81 COLON@[67; 68) ":" 83 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 b30030de3..5e96b695b 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)
71 RECORD_FIELD_PAT_LIST@[58; 66) 71 RECORD_FIELD_PAT_LIST@[58; 66)
72 L_CURLY@[58; 59) "{" 72 L_CURLY@[58; 59) "{"
73 WHITESPACE@[59; 60) " " 73 WHITESPACE@[59; 60) " "
74 BIND_PAT@[60; 61) 74 RECORD_FIELD_PAT@[60; 61)
75 NAME@[60; 61) 75 BIND_PAT@[60; 61)
76 IDENT@[60; 61) "a" 76 NAME@[60; 61)
77 IDENT@[60; 61) "a"
77 COMMA@[61; 62) "," 78 COMMA@[61; 62) ","
78 WHITESPACE@[62; 63) " " 79 WHITESPACE@[62; 63) " "
79 BIND_PAT@[63; 64) 80 RECORD_FIELD_PAT@[63; 64)
80 NAME@[63; 64) 81 BIND_PAT@[63; 64)
81 IDENT@[63; 64) "b" 82 NAME@[63; 64)
83 IDENT@[63; 64) "b"
82 WHITESPACE@[64; 65) " " 84 WHITESPACE@[64; 65) " "
83 R_CURLY@[65; 66) "}" 85 R_CURLY@[65; 66) "}"
84 COLON@[66; 67) ":" 86 COLON@[66; 67) ":"
diff --git a/xtask/src/ast_src.rs b/xtask/src/ast_src.rs
index 69cba9168..9c02f7c6f 100644
--- a/xtask/src/ast_src.rs
+++ b/xtask/src/ast_src.rs
@@ -511,7 +511,7 @@ pub(crate) const AST_SRC: AstSrc = AstSrc {
511 T![..], 511 T![..],
512 T!['}'] 512 T!['}']
513 } 513 }
514 struct RecordFieldPat: AttrsOwner, NameOwner { T![:], Pat } 514 struct RecordFieldPat: AttrsOwner { NameRef, T![:], Pat }
515 515
516 struct TupleStructPat { Path, T!['('], args: [Pat], T![')'] } 516 struct TupleStructPat { Path, T!['('], args: [Pat], T![')'] }
517 struct TuplePat { T!['('], args: [Pat], T![')'] } 517 struct TuplePat { T!['('], args: [Pat], T![')'] }