aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_hir/src/source_analyzer.rs36
-rw-r--r--crates/ra_hir_def/src/body.rs13
-rw-r--r--crates/ra_hir_def/src/body/lower.rs30
-rw-r--r--crates/ra_hir_def/src/path.rs5
-rw-r--r--crates/ra_hir_ty/src/expr.rs55
-rw-r--r--crates/ra_hir_ty/src/tests.rs4
-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.rs30
-rw-r--r--crates/ra_syntax/src/lib.rs5
-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/rust-analyzer/src/cli/analysis_stats.rs12
-rw-r--r--crates/test_utils/src/lib.rs2
17 files changed, 180 insertions, 121 deletions
diff --git a/crates/ra_hir/src/source_analyzer.rs b/crates/ra_hir/src/source_analyzer.rs
index 226fb4534..58ae6ce41 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)?;
@@ -319,8 +317,7 @@ fn scope_for_offset(
319 if source.file_id != offset.file_id { 317 if source.file_id != offset.file_id {
320 return None; 318 return None;
321 } 319 }
322 let syntax_node_ptr = 320 let syntax_node_ptr = source.value.syntax_node_ptr();
323 source.value.either(|it| it.syntax_node_ptr(), |it| it.syntax_node_ptr());
324 Some((syntax_node_ptr, scope)) 321 Some((syntax_node_ptr, scope))
325 }) 322 })
326 // find containing scope 323 // find containing scope
@@ -399,8 +396,7 @@ fn adjust(
399 if source.file_id != file_id { 396 if source.file_id != file_id {
400 return None; 397 return None;
401 } 398 }
402 let syntax_node_ptr = 399 let syntax_node_ptr = source.value.syntax_node_ptr();
403 source.value.either(|it| it.syntax_node_ptr(), |it| it.syntax_node_ptr());
404 Some((syntax_node_ptr, scope)) 400 Some((syntax_node_ptr, scope))
405 }) 401 })
406 .map(|(ptr, scope)| (ptr.range(), scope)) 402 .map(|(ptr, scope)| (ptr.range(), scope))
diff --git a/crates/ra_hir_def/src/body.rs b/crates/ra_hir_def/src/body.rs
index 7fac6ce66..eafaf48c1 100644
--- a/crates/ra_hir_def/src/body.rs
+++ b/crates/ra_hir_def/src/body.rs
@@ -9,6 +9,8 @@ use drop_bomb::DropBomb;
9use either::Either; 9use either::Either;
10use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, AstId, HirFileId, InFile, MacroDefId}; 10use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, AstId, HirFileId, InFile, MacroDefId};
11use ra_arena::{map::ArenaMap, Arena}; 11use ra_arena::{map::ArenaMap, Arena};
12use ra_cfg::CfgOptions;
13use ra_db::CrateId;
12use ra_prof::profile; 14use ra_prof::profile;
13use ra_syntax::{ast, AstNode, AstPtr}; 15use ra_syntax::{ast, AstNode, AstPtr};
14use rustc_hash::FxHashMap; 16use rustc_hash::FxHashMap;
@@ -24,8 +26,6 @@ use crate::{
24 src::HasSource, 26 src::HasSource,
25 AsMacroCall, DefWithBodyId, HasModule, Lookup, ModuleId, 27 AsMacroCall, DefWithBodyId, HasModule, Lookup, ModuleId,
26}; 28};
27use ra_cfg::CfgOptions;
28use ra_db::CrateId;
29 29
30/// A subser of Exander that only deals with cfg attributes. We only need it to 30/// A subser of Exander that only deals with cfg attributes. We only need it to
31/// avoid cyclic queries in crate def map during enum processing. 31/// avoid cyclic queries in crate def map during enum processing.
@@ -187,7 +187,7 @@ pub struct Body {
187 pub item_scope: ItemScope, 187 pub item_scope: ItemScope,
188} 188}
189 189
190pub type ExprPtr = Either<AstPtr<ast::Expr>, AstPtr<ast::RecordField>>; 190pub type ExprPtr = AstPtr<ast::Expr>;
191pub type ExprSource = InFile<ExprPtr>; 191pub type ExprSource = InFile<ExprPtr>;
192 192
193pub type PatPtr = Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>; 193pub type PatPtr = Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>;
@@ -285,7 +285,7 @@ impl BodySourceMap {
285 } 285 }
286 286
287 pub fn node_expr(&self, node: InFile<&ast::Expr>) -> Option<ExprId> { 287 pub fn node_expr(&self, node: InFile<&ast::Expr>) -> Option<ExprId> {
288 let src = node.map(|it| Either::Left(AstPtr::new(it))); 288 let src = node.map(|it| AstPtr::new(it));
289 self.expr_map.get(&src).cloned() 289 self.expr_map.get(&src).cloned()
290 } 290 }
291 291
@@ -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..cc2532e88 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,
@@ -102,7 +101,6 @@ impl ExprCollector<'_> {
102 } 101 }
103 102
104 fn alloc_expr(&mut self, expr: Expr, ptr: AstPtr<ast::Expr>) -> ExprId { 103 fn alloc_expr(&mut self, expr: Expr, ptr: AstPtr<ast::Expr>) -> ExprId {
105 let ptr = Either::Left(ptr);
106 let src = self.expander.to_source(ptr); 104 let src = self.expander.to_source(ptr);
107 let id = self.make_expr(expr, Ok(src.clone())); 105 let id = self.make_expr(expr, Ok(src.clone()));
108 self.source_map.expr_map.insert(src, id); 106 self.source_map.expr_map.insert(src, id);
@@ -113,13 +111,6 @@ impl ExprCollector<'_> {
113 fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId { 111 fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId {
114 self.make_expr(expr, Err(SyntheticSyntax)) 112 self.make_expr(expr, Err(SyntheticSyntax))
115 } 113 }
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 { 114 fn empty_block(&mut self) -> ExprId {
124 self.alloc_expr_desugared(Expr::Block { statements: Vec::new(), tail: None }) 115 self.alloc_expr_desugared(Expr::Block { statements: Vec::new(), tail: None })
125 } 116 }
@@ -289,7 +280,7 @@ impl ExprCollector<'_> {
289 ast::Expr::ParenExpr(e) => { 280 ast::Expr::ParenExpr(e) => {
290 let inner = self.collect_expr_opt(e.expr()); 281 let inner = self.collect_expr_opt(e.expr());
291 // make the paren expr point to the inner expression as well 282 // make the paren expr point to the inner expression as well
292 let src = self.expander.to_source(Either::Left(syntax_ptr)); 283 let src = self.expander.to_source(syntax_ptr);
293 self.source_map.expr_map.insert(src, inner); 284 self.source_map.expr_map.insert(src, inner);
294 inner 285 inner
295 } 286 }
@@ -309,22 +300,13 @@ impl ExprCollector<'_> {
309 if !self.expander.is_cfg_enabled(&attrs) { 300 if !self.expander.is_cfg_enabled(&attrs) {
310 return None; 301 return None;
311 } 302 }
303 let name = field.field_name()?.as_name();
312 304
313 Some(RecordLitField { 305 Some(RecordLitField {
314 name: field 306 name,
315 .name_ref() 307 expr: match field.expr() {
316 .map(|nr| nr.as_name()) 308 Some(e) => self.collect_expr(e),
317 .unwrap_or_else(Name::missing), 309 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 }, 310 },
329 }) 311 })
330 }) 312 })
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_hir_ty/src/expr.rs b/crates/ra_hir_ty/src/expr.rs
index 827b687de..69b527f74 100644
--- a/crates/ra_hir_ty/src/expr.rs
+++ b/crates/ra_hir_ty/src/expr.rs
@@ -89,21 +89,19 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
89 let (_, source_map) = db.body_with_source_map(self.func.into()); 89 let (_, source_map) = db.body_with_source_map(self.func.into());
90 90
91 if let Ok(source_ptr) = source_map.expr_syntax(id) { 91 if let Ok(source_ptr) = source_map.expr_syntax(id) {
92 if let Some(expr) = source_ptr.value.as_ref().left() { 92 let root = source_ptr.file_syntax(db.upcast());
93 let root = source_ptr.file_syntax(db.upcast()); 93 if let ast::Expr::RecordLit(record_lit) = &source_ptr.value.to_node(&root) {
94 if let ast::Expr::RecordLit(record_lit) = expr.to_node(&root) { 94 if let Some(field_list) = record_lit.record_field_list() {
95 if let Some(field_list) = record_lit.record_field_list() { 95 let variant_data = variant_data(db.upcast(), variant_def);
96 let variant_data = variant_data(db.upcast(), variant_def); 96 let missed_fields = missed_fields
97 let missed_fields = missed_fields 97 .into_iter()
98 .into_iter() 98 .map(|idx| variant_data.fields()[idx].name.clone())
99 .map(|idx| variant_data.fields()[idx].name.clone()) 99 .collect();
100 .collect(); 100 self.sink.push(MissingFields {
101 self.sink.push(MissingFields { 101 file: source_ptr.file_id,
102 file: source_ptr.file_id, 102 field_list: AstPtr::new(&field_list),
103 field_list: AstPtr::new(&field_list), 103 missed_fields,
104 missed_fields, 104 })
105 })
106 }
107 } 105 }
108 } 106 }
109 } 107 }
@@ -205,18 +203,16 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
205 } 203 }
206 204
207 if let Ok(source_ptr) = source_map.expr_syntax(id) { 205 if let Ok(source_ptr) = source_map.expr_syntax(id) {
208 if let Some(expr) = source_ptr.value.as_ref().left() { 206 let root = source_ptr.file_syntax(db.upcast());
209 let root = source_ptr.file_syntax(db.upcast()); 207 if let ast::Expr::MatchExpr(match_expr) = &source_ptr.value.to_node(&root) {
210 if let ast::Expr::MatchExpr(match_expr) = expr.to_node(&root) { 208 if let (Some(match_expr), Some(arms)) =
211 if let (Some(match_expr), Some(arms)) = 209 (match_expr.expr(), match_expr.match_arm_list())
212 (match_expr.expr(), match_expr.match_arm_list()) 210 {
213 { 211 self.sink.push(MissingMatchArms {
214 self.sink.push(MissingMatchArms { 212 file: source_ptr.file_id,
215 file: source_ptr.file_id, 213 match_expr: AstPtr::new(&match_expr),
216 match_expr: AstPtr::new(&match_expr), 214 arms: AstPtr::new(&arms),
217 arms: AstPtr::new(&arms), 215 })
218 })
219 }
220 } 216 }
221 } 217 }
222 } 218 }
@@ -247,9 +243,8 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
247 let (_, source_map) = db.body_with_source_map(self.func.into()); 243 let (_, source_map) = db.body_with_source_map(self.func.into());
248 244
249 if let Ok(source_ptr) = source_map.expr_syntax(id) { 245 if let Ok(source_ptr) = source_map.expr_syntax(id) {
250 if let Some(expr) = source_ptr.value.left() { 246 self.sink
251 self.sink.push(MissingOkInTailExpr { file: source_ptr.file_id, expr }); 247 .push(MissingOkInTailExpr { file: source_ptr.file_id, expr: source_ptr.value });
252 }
253 } 248 }
254 } 249 }
255 } 250 }
diff --git a/crates/ra_hir_ty/src/tests.rs b/crates/ra_hir_ty/src/tests.rs
index 54e31602f..81fc0f63a 100644
--- a/crates/ra_hir_ty/src/tests.rs
+++ b/crates/ra_hir_ty/src/tests.rs
@@ -82,9 +82,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
82 82
83 for (expr, ty) in inference_result.type_of_expr.iter() { 83 for (expr, ty) in inference_result.type_of_expr.iter() {
84 let syntax_ptr = match body_source_map.expr_syntax(expr) { 84 let syntax_ptr = match body_source_map.expr_syntax(expr) {
85 Ok(sp) => { 85 Ok(sp) => sp.map(|ast| ast.syntax_node_ptr()),
86 sp.map(|ast| ast.either(|it| it.syntax_node_ptr(), |it| it.syntax_node_ptr()))
87 }
88 Err(SyntheticSyntax) => continue, 86 Err(SyntheticSyntax) => continue,
89 }; 87 };
90 types.push((syntax_ptr.clone(), ty)); 88 types.push((syntax_ptr.clone(), ty));
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..63e272fbf 100644
--- a/crates/ra_syntax/src/ast/extensions.rs
+++ b/crates/ra_syntax/src/ast/extensions.rs
@@ -187,6 +187,36 @@ 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 let candidate =
193 field_name.syntax().parent().and_then(ast::RecordField::cast).or_else(|| {
194 field_name.syntax().ancestors().nth(4).and_then(ast::RecordField::cast)
195 })?;
196 if candidate.field_name().as_ref() == Some(field_name) {
197 Some(candidate)
198 } else {
199 None
200 }
201 }
202
203 /// Deals with field init shorthand
204 pub fn field_name(&self) -> Option<ast::NameRef> {
205 if let Some(name_ref) = self.name_ref() {
206 return Some(name_ref);
207 }
208 if let Some(ast::Expr::PathExpr(expr)) = self.expr() {
209 let path = expr.path()?;
210 let segment = path.segment()?;
211 let name_ref = segment.name_ref()?;
212 if path.qualifier().is_none() {
213 return Some(name_ref);
214 }
215 }
216 None
217 }
218}
219
190impl ast::EnumVariant { 220impl ast::EnumVariant {
191 pub fn parent_enum(&self) -> ast::EnumDef { 221 pub fn parent_enum(&self) -> ast::EnumDef {
192 self.syntax() 222 self.syntax()
diff --git a/crates/ra_syntax/src/lib.rs b/crates/ra_syntax/src/lib.rs
index f0e16dc2b..a796e78b1 100644
--- a/crates/ra_syntax/src/lib.rs
+++ b/crates/ra_syntax/src/lib.rs
@@ -19,6 +19,11 @@
19//! [RFC]: <https://github.com/rust-lang/rfcs/pull/2256> 19//! [RFC]: <https://github.com/rust-lang/rfcs/pull/2256>
20//! [Swift]: <https://github.com/apple/swift/blob/13d593df6f359d0cb2fc81cfaac273297c539455/lib/Syntax/README.md> 20//! [Swift]: <https://github.com/apple/swift/blob/13d593df6f359d0cb2fc81cfaac273297c539455/lib/Syntax/README.md>
21 21
22#[allow(unused)]
23macro_rules! eprintln {
24 ($($tt:tt)*) => { stdx::eprintln!($($tt)*) };
25}
26
22mod syntax_node; 27mod syntax_node;
23mod syntax_error; 28mod syntax_error;
24mod parsing; 29mod parsing;
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/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs
index 75cf2dae5..e9ee0b888 100644
--- a/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -163,10 +163,7 @@ pub fn analysis_stats(
163 if let Ok(src) = src { 163 if let Ok(src) = src {
164 let original_file = src.file_id.original_file(db); 164 let original_file = src.file_id.original_file(db);
165 let line_index = host.analysis().file_line_index(original_file).unwrap(); 165 let line_index = host.analysis().file_line_index(original_file).unwrap();
166 let text_range = src.value.either( 166 let text_range = src.value.syntax_node_ptr().range();
167 |it| it.syntax_node_ptr().range(),
168 |it| it.syntax_node_ptr().range(),
169 );
170 let (start, end) = ( 167 let (start, end) = (
171 line_index.line_col(text_range.start()), 168 line_index.line_col(text_range.start()),
172 line_index.line_col(text_range.end()), 169 line_index.line_col(text_range.end()),
@@ -192,12 +189,7 @@ pub fn analysis_stats(
192 // FIXME: it might be nice to have a function (on Analysis?) that goes from Source<T> -> (LineCol, LineCol) directly 189 // FIXME: it might be nice to have a function (on Analysis?) that goes from Source<T> -> (LineCol, LineCol) directly
193 // But also, we should just turn the type mismatches into diagnostics and provide these 190 // But also, we should just turn the type mismatches into diagnostics and provide these
194 let root = db.parse_or_expand(src.file_id).unwrap(); 191 let root = db.parse_or_expand(src.file_id).unwrap();
195 let node = src.map(|e| { 192 let node = src.map(|e| e.to_node(&root).syntax().clone());
196 e.either(
197 |p| p.to_node(&root).syntax().clone(),
198 |p| p.to_node(&root).syntax().clone(),
199 )
200 });
201 let original_range = original_range(db, node.as_ref()); 193 let original_range = original_range(db, node.as_ref());
202 let path = db.file_relative_path(original_range.file_id); 194 let path = db.file_relative_path(original_range.file_id);
203 let line_index = 195 let line_index =
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