diff options
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 @@ | |||
1 | use std::iter::successors; | 1 | use std::iter::successors; |
2 | 2 | ||
3 | use ra_syntax::{ | 3 | use 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" | ||
265 | use foo::bar::baz; | ||
266 | use foo::<|>{ | ||
267 | FooBar, | ||
268 | }; | ||
269 | ", | ||
270 | r" | ||
271 | use foo::{<|> | ||
272 | FooBar, | ||
273 | bar::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 @@ | |||
1 | use std::collections::HashMap; | 1 | use std::collections::HashMap; |
2 | 2 | ||
3 | use itertools::Itertools; | ||
4 | |||
5 | use hir::{Adt, ModuleDef, PathResolution, Semantics, Struct}; | 3 | use hir::{Adt, ModuleDef, PathResolution, Semantics, Struct}; |
4 | use itertools::Itertools; | ||
6 | use ra_ide_db::RootDatabase; | 5 | use ra_ide_db::RootDatabase; |
7 | use ra_syntax::{ | 6 | use 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 | ||
13 | use crate::{ | 14 | use crate::{ |
14 | assist_ctx::{Assist, AssistCtx}, | 15 | assist_ctx::{Assist, AssistCtx}, |
15 | AssistId, | 16 | AssistId, |
16 | }; | 17 | }; |
17 | use 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 | ||
61 | fn get_fields_kind(node: &SyntaxNode) -> Vec<SyntaxKind> { | 61 | fn 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 | ||
70 | fn get_field_name(node: &SyntaxNode) -> String { | 69 | fn 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 | ||
86 | fn get_fields(record: &SyntaxNode) -> Vec<SyntaxNode> { | 80 | fn 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 | ||
35 | use super::{ExprSource, PatSource}; | 35 | use super::{ExprSource, PatSource}; |
36 | use ast::AstChildren; | ||
36 | 37 | ||
37 | pub(super) fn lower( | 38 | pub(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 | ||
684 | impl From<ast::BinOp> for BinaryOp { | 701 | impl 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 { | |||
374 | pub enum Pat { | 374 | pub 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 | ||
86 | impl 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 | |||
86 | impl AsName for tt::Ident { | 95 | impl 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 { | |||
644 | fn pat_constructor(cx: &MatchCheckCtx, pat: PatIdOrWild) -> MatchCheckResult<Option<Constructor>> { | 674 | fn 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 @@ | |||
1 | use super::{infer, infer_with_mismatches}; | ||
2 | use insta::assert_snapshot; | 1 | use insta::assert_snapshot; |
3 | use test_utils::covers; | 2 | use test_utils::covers; |
4 | 3 | ||
4 | use super::{infer, infer_with_mismatches}; | ||
5 | |||
5 | #[test] | 6 | #[test] |
6 | fn infer_pattern() { | 7 | fn 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#" |
565 | struct TS { x: u32, y: i32 } | 575 | struct TS { x: u32, y: i32 } |
566 | fn main() { | 576 | fn 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#" |
601 | enum E { | 610 | enum 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 @@ | |||
2 | use crate::completion::{CompletionContext, Completions}; | 2 | use crate::completion::{CompletionContext, Completions}; |
3 | 3 | ||
4 | pub(super) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { | 4 | pub(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 @@ | |||
3 | use crate::completion::{CompletionContext, Completions}; | 3 | use crate::completion::{CompletionContext, Completions}; |
4 | 4 | ||
5 | pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { | 5 | pub(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 | ||
284 | fn name_ref_or_index(p: &mut Parser) { | 284 | fn 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 | ||
294 | fn error_block(p: &mut Parser, message: &str) { | 291 | fn 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 | // } | ||
218 | fn 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 _ = (); } |
235 | fn placeholder_pat(p: &mut Parser) -> CompletedMarker { | 231 | fn 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::{ | |||
18 | pub use self::{ | 18 | pub 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 | ||
4 | use std::fmt; | ||
5 | |||
4 | use itertools::Itertools; | 6 | use itertools::Itertools; |
5 | use ra_parser::SyntaxKind; | 7 | use ra_parser::SyntaxKind; |
6 | 8 | ||
@@ -217,6 +219,34 @@ impl ast::RecordField { | |||
217 | } | 219 | } |
218 | } | 220 | } |
219 | 221 | ||
222 | pub enum NameOrNameRef { | ||
223 | Name(ast::Name), | ||
224 | NameRef(ast::NameRef), | ||
225 | } | ||
226 | |||
227 | impl 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 | |||
236 | impl 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 | |||
220 | impl ast::EnumVariant { | 250 | impl 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 | } |
1808 | impl ast::AttrsOwner for RecordFieldPat {} | 1808 | impl ast::AttrsOwner for RecordFieldPat {} |
1809 | impl ast::NameOwner for RecordFieldPat {} | ||
1810 | impl RecordFieldPat { | 1809 | impl 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![')'] } |