aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_hir/src/source_analyzer.rs30
-rw-r--r--crates/ra_hir_def/src/body.rs5
-rw-r--r--crates/ra_hir_def/src/body/lower.rs27
-rw-r--r--crates/ra_hir_def/src/path.rs5
-rw-r--r--crates/ra_ide/src/completion/complete_unqualified_path.rs2
-rw-r--r--crates/ra_ide/src/completion/completion_context.rs2
-rw-r--r--crates/ra_ide_db/src/defs.rs2
-rw-r--r--crates/ra_parser/src/grammar/expressions.rs37
-rw-r--r--crates/ra_syntax/src/ast/extensions.rs32
-rw-r--r--crates/ra_syntax/test_data/parser/inline/err/0014_record_literal_before_ellipsis_recovery.rast49
-rw-r--r--crates/ra_syntax/test_data/parser/inline/err/0014_record_literal_before_ellipsis_recovery.rs3
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0061_record_lit.rast14
-rw-r--r--crates/test_utils/src/lib.rs2
13 files changed, 142 insertions, 68 deletions
diff --git a/crates/ra_hir/src/source_analyzer.rs b/crates/ra_hir/src/source_analyzer.rs
index 226fb4534..05147901e 100644
--- a/crates/ra_hir/src/source_analyzer.rs
+++ b/crates/ra_hir/src/source_analyzer.rs
@@ -139,7 +139,7 @@ impl SourceAnalyzer {
139 &self, 139 &self,
140 db: &dyn HirDatabase, 140 db: &dyn HirDatabase,
141 field: &ast::FieldExpr, 141 field: &ast::FieldExpr,
142 ) -> Option<crate::StructField> { 142 ) -> Option<StructField> {
143 let expr_id = self.expr_id(db, &field.clone().into())?; 143 let expr_id = self.expr_id(db, &field.clone().into())?;
144 self.infer.as_ref()?.field_resolution(expr_id).map(|it| it.into()) 144 self.infer.as_ref()?.field_resolution(expr_id).map(|it| it.into())
145 } 145 }
@@ -148,21 +148,19 @@ impl SourceAnalyzer {
148 &self, 148 &self,
149 db: &dyn HirDatabase, 149 db: &dyn HirDatabase,
150 field: &ast::RecordField, 150 field: &ast::RecordField,
151 ) -> Option<(crate::StructField, Option<Local>)> { 151 ) -> Option<(StructField, Option<Local>)> {
152 let (expr_id, local) = match field.expr() { 152 let expr = field.expr()?;
153 Some(it) => (self.expr_id(db, &it)?, None), 153 let expr_id = self.expr_id(db, &expr)?;
154 None => { 154 let local = if field.name_ref().is_some() {
155 let src = InFile { file_id: self.file_id, value: field }; 155 None
156 let expr_id = self.body_source_map.as_ref()?.field_init_shorthand_expr(src)?; 156 } else {
157 let local_name = field.name_ref()?.as_name(); 157 let local_name = field.field_name()?.as_name();
158 let path = ModPath::from_segments(PathKind::Plain, once(local_name)); 158 let path = ModPath::from_segments(PathKind::Plain, once(local_name));
159 let local = match self.resolver.resolve_path_in_value_ns_fully(db.upcast(), &path) { 159 match self.resolver.resolve_path_in_value_ns_fully(db.upcast(), &path) {
160 Some(ValueNs::LocalBinding(pat_id)) => { 160 Some(ValueNs::LocalBinding(pat_id)) => {
161 Some(Local { pat_id, parent: self.resolver.body_owner()? }) 161 Some(Local { pat_id, parent: self.resolver.body_owner()? })
162 } 162 }
163 _ => None, 163 _ => None,
164 };
165 (expr_id, local)
166 } 164 }
167 }; 165 };
168 let struct_field = self.infer.as_ref()?.record_field_resolution(expr_id)?; 166 let struct_field = self.infer.as_ref()?.record_field_resolution(expr_id)?;
diff --git a/crates/ra_hir_def/src/body.rs b/crates/ra_hir_def/src/body.rs
index 7fac6ce66..67d623714 100644
--- a/crates/ra_hir_def/src/body.rs
+++ b/crates/ra_hir_def/src/body.rs
@@ -294,11 +294,6 @@ impl BodySourceMap {
294 self.expansions.get(&src).cloned() 294 self.expansions.get(&src).cloned()
295 } 295 }
296 296
297 pub fn field_init_shorthand_expr(&self, node: InFile<&ast::RecordField>) -> Option<ExprId> {
298 let src = node.map(|it| Either::Right(AstPtr::new(it)));
299 self.expr_map.get(&src).cloned()
300 }
301
302 pub fn pat_syntax(&self, pat: PatId) -> Result<PatSource, SyntheticSyntax> { 297 pub fn pat_syntax(&self, pat: PatId) -> Result<PatSource, SyntheticSyntax> {
303 self.pat_map_back[pat].clone() 298 self.pat_map_back[pat].clone()
304 } 299 }
diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs
index c1d7eb826..990761661 100644
--- a/crates/ra_hir_def/src/body/lower.rs
+++ b/crates/ra_hir_def/src/body/lower.rs
@@ -27,7 +27,6 @@ use crate::{
27 }, 27 },
28 item_scope::BuiltinShadowMode, 28 item_scope::BuiltinShadowMode,
29 path::GenericArgs, 29 path::GenericArgs,
30 path::Path,
31 type_ref::{Mutability, TypeRef}, 30 type_ref::{Mutability, TypeRef},
32 AdtId, ConstLoc, ContainerId, DefWithBodyId, EnumLoc, FunctionLoc, Intern, ModuleDefId, 31 AdtId, ConstLoc, ContainerId, DefWithBodyId, EnumLoc, FunctionLoc, Intern, ModuleDefId,
33 StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc, 32 StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc,
@@ -113,13 +112,6 @@ impl ExprCollector<'_> {
113 fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId { 112 fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId {
114 self.make_expr(expr, Err(SyntheticSyntax)) 113 self.make_expr(expr, Err(SyntheticSyntax))
115 } 114 }
116 fn alloc_expr_field_shorthand(&mut self, expr: Expr, ptr: AstPtr<ast::RecordField>) -> ExprId {
117 let ptr = Either::Right(ptr);
118 let src = self.expander.to_source(ptr);
119 let id = self.make_expr(expr, Ok(src.clone()));
120 self.source_map.expr_map.insert(src, id);
121 id
122 }
123 fn empty_block(&mut self) -> ExprId { 115 fn empty_block(&mut self) -> ExprId {
124 self.alloc_expr_desugared(Expr::Block { statements: Vec::new(), tail: None }) 116 self.alloc_expr_desugared(Expr::Block { statements: Vec::new(), tail: None })
125 } 117 }
@@ -309,22 +301,13 @@ impl ExprCollector<'_> {
309 if !self.expander.is_cfg_enabled(&attrs) { 301 if !self.expander.is_cfg_enabled(&attrs) {
310 return None; 302 return None;
311 } 303 }
304 let name = field.field_name()?.as_name();
312 305
313 Some(RecordLitField { 306 Some(RecordLitField {
314 name: field 307 name,
315 .name_ref() 308 expr: match field.expr() {
316 .map(|nr| nr.as_name()) 309 Some(e) => self.collect_expr(e),
317 .unwrap_or_else(Name::missing), 310 None => self.missing_expr(),
318 expr: if let Some(e) = field.expr() {
319 self.collect_expr(e)
320 } else if let Some(nr) = field.name_ref() {
321 // field shorthand
322 self.alloc_expr_field_shorthand(
323 Expr::Path(Path::from_name_ref(&nr)),
324 AstPtr::new(&field),
325 )
326 } else {
327 self.missing_expr()
328 }, 311 },
329 }) 312 })
330 }) 313 })
diff --git a/crates/ra_hir_def/src/path.rs b/crates/ra_hir_def/src/path.rs
index 904080341..91c7b3e09 100644
--- a/crates/ra_hir_def/src/path.rs
+++ b/crates/ra_hir_def/src/path.rs
@@ -134,11 +134,6 @@ impl Path {
134 lower::lower_path(path, hygiene) 134 lower::lower_path(path, hygiene)
135 } 135 }
136 136
137 /// Converts an `ast::NameRef` into a single-identifier `Path`.
138 pub(crate) fn from_name_ref(name_ref: &ast::NameRef) -> Path {
139 Path { type_anchor: None, mod_path: name_ref.as_name().into(), generic_args: vec![None] }
140 }
141
142 /// Converts a known mod path to `Path`. 137 /// Converts a known mod path to `Path`.
143 pub(crate) fn from_known_path( 138 pub(crate) fn from_known_path(
144 path: ModPath, 139 path: ModPath,
diff --git a/crates/ra_ide/src/completion/complete_unqualified_path.rs b/crates/ra_ide/src/completion/complete_unqualified_path.rs
index efde9bf73..0b0da6ee4 100644
--- a/crates/ra_ide/src/completion/complete_unqualified_path.rs
+++ b/crates/ra_ide/src/completion/complete_unqualified_path.rs
@@ -3,7 +3,7 @@
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) { 6 if !(ctx.is_trivial_path && !ctx.is_pat_binding_or_const && !ctx.record_lit_syntax.is_some()) {
7 return; 7 return;
8 } 8 }
9 9
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs
index 6637afaf7..14a4a14d7 100644
--- a/crates/ra_ide/src/completion/completion_context.rs
+++ b/crates/ra_ide/src/completion/completion_context.rs
@@ -227,7 +227,7 @@ impl<'a> CompletionContext<'a> {
227 self.name_ref_syntax = 227 self.name_ref_syntax =
228 find_node_at_offset(&original_file, name_ref.syntax().text_range().start()); 228 find_node_at_offset(&original_file, name_ref.syntax().text_range().start());
229 let name_range = name_ref.syntax().text_range(); 229 let name_range = name_ref.syntax().text_range();
230 if name_ref.syntax().parent().and_then(ast::RecordField::cast).is_some() { 230 if ast::RecordField::for_field_name(&name_ref).is_some() {
231 self.record_lit_syntax = 231 self.record_lit_syntax =
232 self.sema.find_node_at_offset_with_macros(&original_file, offset); 232 self.sema.find_node_at_offset_with_macros(&original_file, offset);
233 } 233 }
diff --git a/crates/ra_ide_db/src/defs.rs b/crates/ra_ide_db/src/defs.rs
index e9934844e..49a8c74fb 100644
--- a/crates/ra_ide_db/src/defs.rs
+++ b/crates/ra_ide_db/src/defs.rs
@@ -216,7 +216,7 @@ pub fn classify_name_ref(
216 } 216 }
217 } 217 }
218 218
219 if let Some(record_field) = ast::RecordField::cast(parent.clone()) { 219 if let Some(record_field) = ast::RecordField::for_field_name(name_ref) {
220 tested_by!(goto_def_for_record_fields; force); 220 tested_by!(goto_def_for_record_fields; force);
221 tested_by!(goto_def_for_field_init_shorthand; force); 221 tested_by!(goto_def_for_field_init_shorthand; force);
222 if let Some((field, local)) = sema.resolve_record_field(&record_field) { 222 if let Some((field, local)) = sema.resolve_record_field(&record_field) {
diff --git a/crates/ra_parser/src/grammar/expressions.rs b/crates/ra_parser/src/grammar/expressions.rs
index a1bd53063..cb30b25a8 100644
--- a/crates/ra_parser/src/grammar/expressions.rs
+++ b/crates/ra_parser/src/grammar/expressions.rs
@@ -619,26 +619,39 @@ pub(crate) fn record_field_list(p: &mut Parser) {
619 let m = p.start(); 619 let m = p.start();
620 p.bump(T!['{']); 620 p.bump(T!['{']);
621 while !p.at(EOF) && !p.at(T!['}']) { 621 while !p.at(EOF) && !p.at(T!['}']) {
622 let m = p.start();
623 // test record_literal_field_with_attr
624 // fn main() {
625 // S { #[cfg(test)] field: 1 }
626 // }
627 attributes::outer_attributes(p);
628
622 match p.current() { 629 match p.current() {
623 // test record_literal_field_with_attr 630 IDENT | INT_NUMBER => {
624 // fn main() { 631 // test_err record_literal_before_ellipsis_recovery
625 // S { #[cfg(test)] field: 1 } 632 // fn main() {
626 // } 633 // S { field ..S::default() }
627 IDENT | INT_NUMBER | T![#] => { 634 // }
628 let m = p.start(); 635 if p.nth_at(1, T![:]) || p.nth_at(1, T![..]) {
629 attributes::outer_attributes(p); 636 name_ref_or_index(p);
630 name_ref_or_index(p); 637 p.expect(T![:]);
631 if p.eat(T![:]) {
632 expr(p);
633 } 638 }
639 expr(p);
634 m.complete(p, RECORD_FIELD); 640 m.complete(p, RECORD_FIELD);
635 } 641 }
636 T![.] if p.at(T![..]) => { 642 T![.] if p.at(T![..]) => {
643 m.abandon(p);
637 p.bump(T![..]); 644 p.bump(T![..]);
638 expr(p); 645 expr(p);
639 } 646 }
640 T!['{'] => error_block(p, "expected a field"), 647 T!['{'] => {
641 _ => p.err_and_bump("expected identifier"), 648 error_block(p, "expected a field");
649 m.abandon(p);
650 }
651 _ => {
652 p.err_and_bump("expected identifier");
653 m.abandon(p);
654 }
642 } 655 }
643 if !p.at(T!['}']) { 656 if !p.at(T!['}']) {
644 p.expect(T![,]); 657 p.expect(T![,]);
diff --git a/crates/ra_syntax/src/ast/extensions.rs b/crates/ra_syntax/src/ast/extensions.rs
index 76b7655e6..7dc024991 100644
--- a/crates/ra_syntax/src/ast/extensions.rs
+++ b/crates/ra_syntax/src/ast/extensions.rs
@@ -187,6 +187,38 @@ impl ast::StructDef {
187 } 187 }
188} 188}
189 189
190impl ast::RecordField {
191 pub fn for_field_name(field_name: &ast::NameRef) -> Option<ast::RecordField> {
192 eprintln!("field_name = {}", field_name);
193 dbg!(field_name.syntax().ancestors().nth(6));
194 let candidate =
195 field_name.syntax().parent().and_then(ast::RecordField::cast).or_else(|| {
196 field_name.syntax().ancestors().nth(4).and_then(ast::RecordField::cast)
197 })?;
198 if candidate.field_name().as_ref() == Some(field_name) {
199 Some(candidate)
200 } else {
201 None
202 }
203 }
204
205 /// Deals with field init shorthand
206 pub fn field_name(&self) -> Option<ast::NameRef> {
207 if let Some(name_ref) = self.name_ref() {
208 return Some(name_ref);
209 }
210 if let Some(ast::Expr::PathExpr(expr)) = self.expr() {
211 let path = expr.path()?;
212 let segment = path.segment()?;
213 let name_ref = segment.name_ref()?;
214 if path.qualifier().is_none() {
215 return Some(name_ref);
216 }
217 }
218 None
219 }
220}
221
190impl ast::EnumVariant { 222impl ast::EnumVariant {
191 pub fn parent_enum(&self) -> ast::EnumDef { 223 pub fn parent_enum(&self) -> ast::EnumDef {
192 self.syntax() 224 self.syntax()
diff --git a/crates/ra_syntax/test_data/parser/inline/err/0014_record_literal_before_ellipsis_recovery.rast b/crates/ra_syntax/test_data/parser/inline/err/0014_record_literal_before_ellipsis_recovery.rast
new file mode 100644
index 000000000..75043c9c0
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/inline/err/0014_record_literal_before_ellipsis_recovery.rast
@@ -0,0 +1,49 @@
1SOURCE_FILE@[0; 45)
2 FN_DEF@[0; 44)
3 FN_KW@[0; 2) "fn"
4 WHITESPACE@[2; 3) " "
5 NAME@[3; 7)
6 IDENT@[3; 7) "main"
7 PARAM_LIST@[7; 9)
8 L_PAREN@[7; 8) "("
9 R_PAREN@[8; 9) ")"
10 WHITESPACE@[9; 10) " "
11 BLOCK_EXPR@[10; 44)
12 BLOCK@[10; 44)
13 L_CURLY@[10; 11) "{"
14 WHITESPACE@[11; 16) "\n "
15 RECORD_LIT@[16; 42)
16 PATH@[16; 17)
17 PATH_SEGMENT@[16; 17)
18 NAME_REF@[16; 17)
19 IDENT@[16; 17) "S"
20 WHITESPACE@[17; 18) " "
21 RECORD_FIELD_LIST@[18; 42)
22 L_CURLY@[18; 19) "{"
23 WHITESPACE@[19; 20) " "
24 RECORD_FIELD@[20; 40)
25 NAME_REF@[20; 25)
26 IDENT@[20; 25) "field"
27 WHITESPACE@[25; 26) " "
28 RANGE_EXPR@[26; 40)
29 DOT2@[26; 28) ".."
30 CALL_EXPR@[28; 40)
31 PATH_EXPR@[28; 38)
32 PATH@[28; 38)
33 PATH@[28; 29)
34 PATH_SEGMENT@[28; 29)
35 NAME_REF@[28; 29)
36 IDENT@[28; 29) "S"
37 COLON2@[29; 31) "::"
38 PATH_SEGMENT@[31; 38)
39 NAME_REF@[31; 38)
40 IDENT@[31; 38) "default"
41 ARG_LIST@[38; 40)
42 L_PAREN@[38; 39) "("
43 R_PAREN@[39; 40) ")"
44 WHITESPACE@[40; 41) " "
45 R_CURLY@[41; 42) "}"
46 WHITESPACE@[42; 43) "\n"
47 R_CURLY@[43; 44) "}"
48 WHITESPACE@[44; 45) "\n"
49error [25; 25): expected COLON
diff --git a/crates/ra_syntax/test_data/parser/inline/err/0014_record_literal_before_ellipsis_recovery.rs b/crates/ra_syntax/test_data/parser/inline/err/0014_record_literal_before_ellipsis_recovery.rs
new file mode 100644
index 000000000..a4e5b2f69
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/inline/err/0014_record_literal_before_ellipsis_recovery.rs
@@ -0,0 +1,3 @@
1fn main() {
2 S { field ..S::default() }
3}
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0061_record_lit.rast b/crates/ra_syntax/test_data/parser/inline/ok/0061_record_lit.rast
index f4206858b..89a611799 100644
--- a/crates/ra_syntax/test_data/parser/inline/ok/0061_record_lit.rast
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0061_record_lit.rast
@@ -35,8 +35,11 @@ SOURCE_FILE@[0; 112)
35 L_CURLY@[27; 28) "{" 35 L_CURLY@[27; 28) "{"
36 WHITESPACE@[28; 29) " " 36 WHITESPACE@[28; 29) " "
37 RECORD_FIELD@[29; 30) 37 RECORD_FIELD@[29; 30)
38 NAME_REF@[29; 30) 38 PATH_EXPR@[29; 30)
39 IDENT@[29; 30) "x" 39 PATH@[29; 30)
40 PATH_SEGMENT@[29; 30)
41 NAME_REF@[29; 30)
42 IDENT@[29; 30) "x"
40 COMMA@[30; 31) "," 43 COMMA@[30; 31) ","
41 WHITESPACE@[31; 32) " " 44 WHITESPACE@[31; 32) " "
42 RECORD_FIELD@[32; 37) 45 RECORD_FIELD@[32; 37)
@@ -62,8 +65,11 @@ SOURCE_FILE@[0; 112)
62 L_CURLY@[48; 49) "{" 65 L_CURLY@[48; 49) "{"
63 WHITESPACE@[49; 50) " " 66 WHITESPACE@[49; 50) " "
64 RECORD_FIELD@[50; 51) 67 RECORD_FIELD@[50; 51)
65 NAME_REF@[50; 51) 68 PATH_EXPR@[50; 51)
66 IDENT@[50; 51) "x" 69 PATH@[50; 51)
70 PATH_SEGMENT@[50; 51)
71 NAME_REF@[50; 51)
72 IDENT@[50; 51) "x"
67 COMMA@[51; 52) "," 73 COMMA@[51; 52) ","
68 WHITESPACE@[52; 53) " " 74 WHITESPACE@[52; 53) " "
69 RECORD_FIELD@[53; 58) 75 RECORD_FIELD@[53; 58)
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs
index 3b60c55f3..4164bfd5e 100644
--- a/crates/test_utils/src/lib.rs
+++ b/crates/test_utils/src/lib.rs
@@ -395,7 +395,7 @@ pub fn skip_slow_tests() -> bool {
395 should_skip 395 should_skip
396} 396}
397 397
398const REWRITE: bool = true; 398const REWRITE: bool = false;
399 399
400/// Asserts that `expected` and `actual` strings are equal. If they differ only 400/// Asserts that `expected` and `actual` strings are equal. If they differ only
401/// in trailing or leading whitespace the test won't fail and 401/// in trailing or leading whitespace the test won't fail and