Repository: rust-analyzer/rust-analyzer
Pull request #3951 (2020-04-11 17:25:55)

Simplify records grammar
bors r+
🤖


Patch:
diff --git a/crates/ra_hir/src/source_analyzer.rs b/crates/ra_hir/src/source_analyzer.rs
index 226fb45348..58ae6ce418 100644
--- a/crates/ra_hir/src/source_analyzer.rs
+++ b/crates/ra_hir/src/source_analyzer.rs
@@ -139,7 +139,7 @@ impl SourceAnalyzer {
         &self,
         db: &dyn HirDatabase,
         field: &ast::FieldExpr,
-    ) -> Option<crate::StructField> {
+    ) -> Option<StructField> {
         let expr_id = self.expr_id(db, &field.clone().into())?;
         self.infer.as_ref()?.field_resolution(expr_id).map(|it| it.into())
     }
@@ -148,21 +148,19 @@ impl SourceAnalyzer {
         &self,
         db: &dyn HirDatabase,
         field: &ast::RecordField,
-    ) -> Option<(crate::StructField, Option<Local>)> {
-        let (expr_id, local) = match field.expr() {
-            Some(it) => (self.expr_id(db, &it)?, None),
-            None => {
-                let src = InFile { file_id: self.file_id, value: field };
-                let expr_id = self.body_source_map.as_ref()?.field_init_shorthand_expr(src)?;
-                let local_name = field.name_ref()?.as_name();
-                let path = ModPath::from_segments(PathKind::Plain, once(local_name));
-                let local = match self.resolver.resolve_path_in_value_ns_fully(db.upcast(), &path) {
-                    Some(ValueNs::LocalBinding(pat_id)) => {
-                        Some(Local { pat_id, parent: self.resolver.body_owner()? })
-                    }
-                    _ => None,
-                };
-                (expr_id, local)
+    ) -> Option<(StructField, Option<Local>)> {
+        let expr = field.expr()?;
+        let expr_id = self.expr_id(db, &expr)?;
+        let local = if field.name_ref().is_some() {
+            None
+        } else {
+            let local_name = field.field_name()?.as_name();
+            let path = ModPath::from_segments(PathKind::Plain, once(local_name));
+            match self.resolver.resolve_path_in_value_ns_fully(db.upcast(), &path) {
+                Some(ValueNs::LocalBinding(pat_id)) => {
+                    Some(Local { pat_id, parent: self.resolver.body_owner()? })
+                }
+                _ => None,
             }
         };
         let struct_field = self.infer.as_ref()?.record_field_resolution(expr_id)?;
@@ -319,8 +317,7 @@ fn scope_for_offset(
             if source.file_id != offset.file_id {
                 return None;
             }
-            let syntax_node_ptr =
-                source.value.either(|it| it.syntax_node_ptr(), |it| it.syntax_node_ptr());
+            let syntax_node_ptr = source.value.syntax_node_ptr();
             Some((syntax_node_ptr, scope))
         })
         // find containing scope
@@ -399,8 +396,7 @@ fn adjust(
             if source.file_id != file_id {
                 return None;
             }
-            let syntax_node_ptr =
-                source.value.either(|it| it.syntax_node_ptr(), |it| it.syntax_node_ptr());
+            let syntax_node_ptr = source.value.syntax_node_ptr();
             Some((syntax_node_ptr, scope))
         })
         .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 7fac6ce66e..eafaf48c17 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;
 use either::Either;
 use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, AstId, HirFileId, InFile, MacroDefId};
 use ra_arena::{map::ArenaMap, Arena};
+use ra_cfg::CfgOptions;
+use ra_db::CrateId;
 use ra_prof::profile;
 use ra_syntax::{ast, AstNode, AstPtr};
 use rustc_hash::FxHashMap;
@@ -24,8 +26,6 @@ use crate::{
     src::HasSource,
     AsMacroCall, DefWithBodyId, HasModule, Lookup, ModuleId,
 };
-use ra_cfg::CfgOptions;
-use ra_db::CrateId;
 
 /// A subser of Exander that only deals with cfg attributes. We only need it to
 /// avoid cyclic queries in crate def map during enum processing.
@@ -187,7 +187,7 @@ pub struct Body {
     pub item_scope: ItemScope,
 }
 
-pub type ExprPtr = Either<AstPtr<ast::Expr>, AstPtr<ast::RecordField>>;
+pub type ExprPtr = AstPtr<ast::Expr>;
 pub type ExprSource = InFile<ExprPtr>;
 
 pub type PatPtr = Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>;
@@ -285,7 +285,7 @@ impl BodySourceMap {
     }
 
     pub fn node_expr(&self, node: InFile<&ast::Expr>) -> Option<ExprId> {
-        let src = node.map(|it| Either::Left(AstPtr::new(it)));
+        let src = node.map(|it| AstPtr::new(it));
         self.expr_map.get(&src).cloned()
     }
 
@@ -294,11 +294,6 @@ impl BodySourceMap {
         self.expansions.get(&src).cloned()
     }
 
-    pub fn field_init_shorthand_expr(&self, node: InFile<&ast::RecordField>) -> Option<ExprId> {
-        let src = node.map(|it| Either::Right(AstPtr::new(it)));
-        self.expr_map.get(&src).cloned()
-    }
-
     pub fn pat_syntax(&self, pat: PatId) -> Result<PatSource, SyntheticSyntax> {
         self.pat_map_back[pat].clone()
     }
diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs
index c1d7eb826a..cc2532e88b 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::{
     },
     item_scope::BuiltinShadowMode,
     path::GenericArgs,
-    path::Path,
     type_ref::{Mutability, TypeRef},
     AdtId, ConstLoc, ContainerId, DefWithBodyId, EnumLoc, FunctionLoc, Intern, ModuleDefId,
     StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc,
@@ -102,7 +101,6 @@ impl ExprCollector<'_> {
     }
 
     fn alloc_expr(&mut self, expr: Expr, ptr: AstPtr<ast::Expr>) -> ExprId {
-        let ptr = Either::Left(ptr);
         let src = self.expander.to_source(ptr);
         let id = self.make_expr(expr, Ok(src.clone()));
         self.source_map.expr_map.insert(src, id);
@@ -113,13 +111,6 @@ impl ExprCollector<'_> {
     fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId {
         self.make_expr(expr, Err(SyntheticSyntax))
     }
-    fn alloc_expr_field_shorthand(&mut self, expr: Expr, ptr: AstPtr<ast::RecordField>) -> ExprId {
-        let ptr = Either::Right(ptr);
-        let src = self.expander.to_source(ptr);
-        let id = self.make_expr(expr, Ok(src.clone()));
-        self.source_map.expr_map.insert(src, id);
-        id
-    }
     fn empty_block(&mut self) -> ExprId {
         self.alloc_expr_desugared(Expr::Block { statements: Vec::new(), tail: None })
     }
@@ -289,7 +280,7 @@ impl ExprCollector<'_> {
             ast::Expr::ParenExpr(e) => {
                 let inner = self.collect_expr_opt(e.expr());
                 // make the paren expr point to the inner expression as well
-                let src = self.expander.to_source(Either::Left(syntax_ptr));
+                let src = self.expander.to_source(syntax_ptr);
                 self.source_map.expr_map.insert(src, inner);
                 inner
             }
@@ -309,22 +300,13 @@ impl ExprCollector<'_> {
                             if !self.expander.is_cfg_enabled(&attrs) {
                                 return None;
                             }
+                            let name = field.field_name()?.as_name();
 
                             Some(RecordLitField {
-                                name: field
-                                    .name_ref()
-                                    .map(|nr| nr.as_name())
-                                    .unwrap_or_else(Name::missing),
-                                expr: if let Some(e) = field.expr() {
-                                    self.collect_expr(e)
-                                } else if let Some(nr) = field.name_ref() {
-                                    // field shorthand
-                                    self.alloc_expr_field_shorthand(
-                                        Expr::Path(Path::from_name_ref(&nr)),
-                                        AstPtr::new(&field),
-                                    )
-                                } else {
-                                    self.missing_expr()
+                                name,
+                                expr: match field.expr() {
+                                    Some(e) => self.collect_expr(e),
+                                    None => self.missing_expr(),
                                 },
                             })
                         })
diff --git a/crates/ra_hir_def/src/path.rs b/crates/ra_hir_def/src/path.rs
index 904080341a..91c7b3e094 100644
--- a/crates/ra_hir_def/src/path.rs
+++ b/crates/ra_hir_def/src/path.rs
@@ -134,11 +134,6 @@ impl Path {
         lower::lower_path(path, hygiene)
     }
 
-    /// Converts an `ast::NameRef` into a single-identifier `Path`.
-    pub(crate) fn from_name_ref(name_ref: &ast::NameRef) -> Path {
-        Path { type_anchor: None, mod_path: name_ref.as_name().into(), generic_args: vec![None] }
-    }
-
     /// Converts a known mod path to `Path`.
     pub(crate) fn from_known_path(
         path: ModPath,
diff --git a/crates/ra_hir_ty/src/db.rs b/crates/ra_hir_ty/src/db.rs
index 33da16b487..1462b053f0 100644
--- a/crates/ra_hir_ty/src/db.rs
+++ b/crates/ra_hir_ty/src/db.rs
@@ -11,7 +11,7 @@ use ra_db::{impl_intern_key, salsa, CrateId, Upcast};
 use ra_prof::profile;
 
 use crate::{
-    method_resolution::{CrateImplDefs, TyFingerprint},
+    method_resolution::CrateImplDefs,
     traits::{chalk, AssocTyValue, Impl},
     Binders, CallableDef, GenericPredicate, InferenceResult, PolyFnSig, Substs, TraitRef, Ty,
     TyDefId, TypeCtor, ValueTyDefId,
@@ -65,12 +65,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
     fn impls_in_crate(&self, krate: CrateId) -> Arc<CrateImplDefs>;
 
     #[salsa::invoke(crate::traits::impls_for_trait_query)]
-    fn impls_for_trait(
-        &self,
-        krate: CrateId,
-        trait_: TraitId,
-        self_ty_fp: Option<TyFingerprint>,
-    ) -> Arc<[ImplId]>;
+    fn impls_for_trait(&self, krate: CrateId, trait_: TraitId) -> Arc<[ImplId]>;
 
     // Interned IDs for Chalk integration
     #[salsa::interned]
diff --git a/crates/ra_hir_ty/src/expr.rs b/crates/ra_hir_ty/src/expr.rs
index 827b687de1..69b527f744 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> {
         let (_, source_map) = db.body_with_source_map(self.func.into());
 
         if let Ok(source_ptr) = source_map.expr_syntax(id) {
-            if let Some(expr) = source_ptr.value.as_ref().left() {
-                let root = source_ptr.file_syntax(db.upcast());
-                if let ast::Expr::RecordLit(record_lit) = expr.to_node(&root) {
-                    if let Some(field_list) = record_lit.record_field_list() {
-                        let variant_data = variant_data(db.upcast(), variant_def);
-                        let missed_fields = missed_fields
-                            .into_iter()
-                            .map(|idx| variant_data.fields()[idx].name.clone())
-                            .collect();
-                        self.sink.push(MissingFields {
-                            file: source_ptr.file_id,
-                            field_list: AstPtr::new(&field_list),
-                            missed_fields,
-                        })
-                    }
+            let root = source_ptr.file_syntax(db.upcast());
+            if let ast::Expr::RecordLit(record_lit) = &source_ptr.value.to_node(&root) {
+                if let Some(field_list) = record_lit.record_field_list() {
+                    let variant_data = variant_data(db.upcast(), variant_def);
+                    let missed_fields = missed_fields
+                        .into_iter()
+                        .map(|idx| variant_data.fields()[idx].name.clone())
+                        .collect();
+                    self.sink.push(MissingFields {
+                        file: source_ptr.file_id,
+                        field_list: AstPtr::new(&field_list),
+                        missed_fields,
+                    })
                 }
             }
         }
@@ -205,18 +203,16 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
         }
 
         if let Ok(source_ptr) = source_map.expr_syntax(id) {
-            if let Some(expr) = source_ptr.value.as_ref().left() {
-                let root = source_ptr.file_syntax(db.upcast());
-                if let ast::Expr::MatchExpr(match_expr) = expr.to_node(&root) {
-                    if let (Some(match_expr), Some(arms)) =
-                        (match_expr.expr(), match_expr.match_arm_list())
-                    {
-                        self.sink.push(MissingMatchArms {
-                            file: source_ptr.file_id,
-                            match_expr: AstPtr::new(&match_expr),
-                            arms: AstPtr::new(&arms),
-                        })
-                    }
+            let root = source_ptr.file_syntax(db.upcast());
+            if let ast::Expr::MatchExpr(match_expr) = &source_ptr.value.to_node(&root) {
+                if let (Some(match_expr), Some(arms)) =
+                    (match_expr.expr(), match_expr.match_arm_list())
+                {
+                    self.sink.push(MissingMatchArms {
+                        file: source_ptr.file_id,
+                        match_expr: AstPtr::new(&match_expr),
+                        arms: AstPtr::new(&arms),
+                    })
                 }
             }
         }
@@ -247,9 +243,8 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
             let (_, source_map) = db.body_with_source_map(self.func.into());
 
             if let Ok(source_ptr) = source_map.expr_syntax(id) {
-                if let Some(expr) = source_ptr.value.left() {
-                    self.sink.push(MissingOkInTailExpr { file: source_ptr.file_id, expr });
-                }
+                self.sink
+                    .push(MissingOkInTailExpr { file: source_ptr.file_id, expr: source_ptr.value });
             }
         }
     }
diff --git a/crates/ra_hir_ty/src/method_resolution.rs b/crates/ra_hir_ty/src/method_resolution.rs
index 657284fd01..74a0bc7db8 100644
--- a/crates/ra_hir_ty/src/method_resolution.rs
+++ b/crates/ra_hir_ty/src/method_resolution.rs
@@ -34,7 +34,7 @@ impl TyFingerprint {
     /// Creates a TyFingerprint for looking up an impl. Only certain types can
     /// have impls: if we have some `struct S`, we can have an `impl S`, but not
     /// `impl &S`. Hence, this will return `None` for reference types and such.
-    pub(crate) fn for_impl(ty: &Ty) -> Option<TyFingerprint> {
+    fn for_impl(ty: &Ty) -> Option<TyFingerprint> {
         match ty {
             Ty::Apply(a_ty) => Some(TyFingerprint::Apply(a_ty.ctor)),
             _ => None,
@@ -45,7 +45,7 @@ impl TyFingerprint {
 #[derive(Debug, PartialEq, Eq)]
 pub struct CrateImplDefs {
     impls: FxHashMap<TyFingerprint, Vec<ImplId>>,
-    impls_by_trait: FxHashMap<TraitId, FxHashMap<Option<TyFingerprint>, Vec<ImplId>>>,
+    impls_by_trait: FxHashMap<TraitId, Vec<ImplId>>,
 }
 
 impl CrateImplDefs {
@@ -59,14 +59,7 @@ impl CrateImplDefs {
             for impl_id in module_data.scope.impls() {
                 match db.impl_trait(impl_id) {
                     Some(tr) => {
-                        let self_ty = db.impl_self_ty(impl_id);
-                        let self_ty_fp = TyFingerprint::for_impl(&self_ty.value);
-                        res.impls_by_trait
-                            .entry(tr.value.trait_)
-                            .or_default()
-                            .entry(self_ty_fp)
-                            .or_default()
-                            .push(impl_id);
+                        res.impls_by_trait.entry(tr.value.trait_).or_default().push(impl_id);
                     }
                     None => {
                         let self_ty = db.impl_self_ty(impl_id);
@@ -86,39 +79,11 @@ impl CrateImplDefs {
     }
 
     pub fn lookup_impl_defs_for_trait(&self, tr: TraitId) -> impl Iterator<Item = ImplId> + '_ {
-        self.impls_by_trait
-            .get(&tr)
-            .into_iter()
-            .flat_map(|m| m.values().flat_map(|v| v.iter().copied()))
-    }
-
-    pub fn lookup_impl_defs_for_trait_and_ty(
-        &self,
-        tr: TraitId,
-        fp: TyFingerprint,
-    ) -> impl Iterator<Item = ImplId> + '_ {
-        self.impls_by_trait
-            .get(&tr)
-            .and_then(|m| m.get(&Some(fp)))
-            .into_iter()
-            .flatten()
-            .copied()
-            .chain(
-                self.impls_by_trait
-                    .get(&tr)
-                    .and_then(|m| m.get(&None))
-                    .into_iter()
-                    .flatten()
-                    .copied(),
-            )
+        self.impls_by_trait.get(&tr).into_iter().flatten().copied()
     }
 
     pub fn all_impls<'a>(&'a self) -> impl Iterator<Item = ImplId> + 'a {
-        self.impls
-            .values()
-            .chain(self.impls_by_trait.values().flat_map(|m| m.values()))
-            .flatten()
-            .copied()
+        self.impls.values().chain(self.impls_by_trait.values()).flatten().copied()
     }
 }
 
diff --git a/crates/ra_hir_ty/src/traits.rs b/crates/ra_hir_ty/src/traits.rs
index 43d8d1e802..21e2333799 100644
--- a/crates/ra_hir_ty/src/traits.rs
+++ b/crates/ra_hir_ty/src/traits.rs
@@ -7,7 +7,7 @@ use ra_db::{impl_intern_key, salsa, CrateId};
 use ra_prof::profile;
 use rustc_hash::FxHashSet;
 
-use crate::{db::HirDatabase, method_resolution::TyFingerprint, DebruijnIndex};
+use crate::{db::HirDatabase, DebruijnIndex};
 
 use super::{Canonical, GenericPredicate, HirDisplay, ProjectionTy, TraitRef, Ty, TypeWalk};
 
@@ -40,12 +40,7 @@ pub(crate) fn impls_for_trait_query(
     db: &dyn HirDatabase,
     krate: CrateId,
     trait_: TraitId,
-    self_ty_fp: Option<TyFingerprint>,
 ) -> Arc<[ImplId]> {
-    // FIXME: We could be a lot smarter here - because of the orphan rules and
-    // the fact that the trait and the self type need to be in the dependency
-    // tree of a crate somewhere for an impl to exist, we could skip looking in
-    // a lot of crates completely
     let mut impls = FxHashSet::default();
     // We call the query recursively here. On the one hand, this means we can
     // reuse results from queries for different crates; on the other hand, this
@@ -53,13 +48,10 @@ pub(crate) fn impls_for_trait_query(
     // ones the user is editing), so this may actually be a waste of memory. I'm
     // doing it like this mainly for simplicity for now.
     for dep in &db.crate_graph()[krate].dependencies {
-        impls.extend(db.impls_for_trait(dep.crate_id, trait_, self_ty_fp).iter());
+        impls.extend(db.impls_for_trait(dep.crate_id, trait_).iter());
     }
     let crate_impl_defs = db.impls_in_crate(krate);
-    match self_ty_fp {
-        Some(fp) => impls.extend(crate_impl_defs.lookup_impl_defs_for_trait_and_ty(trait_, fp)),
-        None => impls.extend(crate_impl_defs.lookup_impl_defs_for_trait(trait_)),
-    }
+    impls.extend(crate_impl_defs.lookup_impl_defs_for_trait(trait_));
     impls.into_iter().collect()
 }
 
diff --git a/crates/ra_hir_ty/src/traits/chalk.rs b/crates/ra_hir_ty/src/traits/chalk.rs
index e05fea8430..c5f1b52324 100644
--- a/crates/ra_hir_ty/src/traits/chalk.rs
+++ b/crates/ra_hir_ty/src/traits/chalk.rs
@@ -16,8 +16,8 @@ use ra_db::{
 
 use super::{builtin, AssocTyValue, Canonical, ChalkContext, Impl, Obligation};
 use crate::{
-    db::HirDatabase, display::HirDisplay, method_resolution::TyFingerprint, utils::generics,
-    ApplicationTy, GenericPredicate, ProjectionTy, Substs, TraitRef, Ty, TypeCtor,
+    db::HirDatabase, display::HirDisplay, utils::generics, ApplicationTy, GenericPredicate,
+    ProjectionTy, Substs, TraitRef, Ty, TypeCtor,
 };
 
 pub(super) mod tls;
@@ -647,22 +647,19 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
         debug!("impls_for_trait {:?}", trait_id);
         let trait_: hir_def::TraitId = from_chalk(self.db, trait_id);
 
-        let ty: Ty = from_chalk(self.db, parameters[0].assert_ty_ref(&Interner).clone());
-
-        let self_ty_fp = TyFingerprint::for_impl(&ty);
-
         // Note: Since we're using impls_for_trait, only impls where the trait
         // can be resolved should ever reach Chalk. `impl_datum` relies on that
         // and will panic if the trait can't be resolved.
         let mut result: Vec<_> = self
             .db
-            .impls_for_trait(self.krate, trait_, self_ty_fp)
+            .impls_for_trait(self.krate, trait_)
             .iter()
             .copied()
             .map(Impl::ImplDef)
             .map(|impl_| impl_.to_chalk(self.db))
             .collect();
 
+        let ty: Ty = from_chalk(self.db, parameters[0].assert_ty_ref(&Interner).clone());
         let arg: Option<Ty> =
             parameters.get(1).map(|p| from_chalk(self.db, p.assert_ty_ref(&Interner).clone()));
 
diff --git a/crates/ra_ide/src/completion/complete_unqualified_path.rs b/crates/ra_ide/src/completion/complete_unqualified_path.rs
index efde9bf731..0b0da6ee49 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 @@
 use crate::completion::{CompletionContext, Completions};
 
 pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
-    if !(ctx.is_trivial_path && !ctx.is_pat_binding_or_const) {
+    if !(ctx.is_trivial_path && !ctx.is_pat_binding_or_const && !ctx.record_lit_syntax.is_some()) {
         return;
     }
 
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs
index 6637afaf70..14a4a14d7b 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> {
         self.name_ref_syntax =
             find_node_at_offset(&original_file, name_ref.syntax().text_range().start());
         let name_range = name_ref.syntax().text_range();
-        if name_ref.syntax().parent().and_then(ast::RecordField::cast).is_some() {
+        if ast::RecordField::for_field_name(&name_ref).is_some() {
             self.record_lit_syntax =
                 self.sema.find_node_at_offset_with_macros(&original_file, offset);
         }
diff --git a/crates/ra_ide_db/src/defs.rs b/crates/ra_ide_db/src/defs.rs
index e9934844e3..49a8c74fba 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(
         }
     }
 
-    if let Some(record_field) = ast::RecordField::cast(parent.clone()) {
+    if let Some(record_field) = ast::RecordField::for_field_name(name_ref) {
         tested_by!(goto_def_for_record_fields; force);
         tested_by!(goto_def_for_field_init_shorthand; force);
         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 a1bd530638..cb30b25a89 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) {
     let m = p.start();
     p.bump(T!['{']);
     while !p.at(EOF) && !p.at(T!['}']) {
+        let m = p.start();
+        // test record_literal_field_with_attr
+        // fn main() {
+        //     S { #[cfg(test)] field: 1 }
+        // }
+        attributes::outer_attributes(p);
+
         match p.current() {
-            // test record_literal_field_with_attr
-            // fn main() {
-            //     S { #[cfg(test)] field: 1 }
-            // }
-            IDENT | INT_NUMBER | T![#] => {
-                let m = p.start();
-                attributes::outer_attributes(p);
-                name_ref_or_index(p);
-                if p.eat(T![:]) {
-                    expr(p);
+            IDENT | INT_NUMBER => {
+                // test_err record_literal_before_ellipsis_recovery
+                // fn main() {
+                //     S { field ..S::default() }
+                // }
+                if p.nth_at(1, T![:]) || p.nth_at(1, T![..]) {
+                    name_ref_or_index(p);
+                    p.expect(T![:]);
                 }
+                expr(p);
                 m.complete(p, RECORD_FIELD);
             }
             T![.] if p.at(T![..]) => {
+                m.abandon(p);
                 p.bump(T![..]);
                 expr(p);
             }
-            T!['{'] => error_block(p, "expected a field"),
-            _ => p.err_and_bump("expected identifier"),
+            T!['{'] => {
+                error_block(p, "expected a field");
+                m.abandon(p);
+            }
+            _ => {
+                p.err_and_bump("expected identifier");
+                m.abandon(p);
+            }
         }
         if !p.at(T!['}']) {
             p.expect(T![,]);
diff --git a/crates/ra_syntax/src/ast/extensions.rs b/crates/ra_syntax/src/ast/extensions.rs
index 76b7655e68..63e272fbfa 100644
--- a/crates/ra_syntax/src/ast/extensions.rs
+++ b/crates/ra_syntax/src/ast/extensions.rs
@@ -187,6 +187,36 @@ impl ast::StructDef {
     }
 }
 
+impl ast::RecordField {
+    pub fn for_field_name(field_name: &ast::NameRef) -> Option<ast::RecordField> {
+        let candidate =
+            field_name.syntax().parent().and_then(ast::RecordField::cast).or_else(|| {
+                field_name.syntax().ancestors().nth(4).and_then(ast::RecordField::cast)
+            })?;
+        if candidate.field_name().as_ref() == Some(field_name) {
+            Some(candidate)
+        } else {
+            None
+        }
+    }
+
+    /// Deals with field init shorthand
+    pub fn field_name(&self) -> Option<ast::NameRef> {
+        if let Some(name_ref) = self.name_ref() {
+            return Some(name_ref);
+        }
+        if let Some(ast::Expr::PathExpr(expr)) = self.expr() {
+            let path = expr.path()?;
+            let segment = path.segment()?;
+            let name_ref = segment.name_ref()?;
+            if path.qualifier().is_none() {
+                return Some(name_ref);
+            }
+        }
+        None
+    }
+}
+
 impl ast::EnumVariant {
     pub fn parent_enum(&self) -> ast::EnumDef {
         self.syntax()
diff --git a/crates/ra_syntax/src/lib.rs b/crates/ra_syntax/src/lib.rs
index f0e16dc2bf..a796e78b12 100644
--- a/crates/ra_syntax/src/lib.rs
+++ b/crates/ra_syntax/src/lib.rs
@@ -19,6 +19,11 @@
 //! [RFC]: <https://github.com/rust-lang/rfcs/pull/2256>
 //! [Swift]: <https://github.com/apple/swift/blob/13d593df6f359d0cb2fc81cfaac273297c539455/lib/Syntax/README.md>
 
+#[allow(unused)]
+macro_rules! eprintln {
+    ($($tt:tt)*) => { stdx::eprintln!($($tt)*) };
+}
+
 mod syntax_node;
 mod syntax_error;
 mod parsing;
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs
index 75cf2dae50..e9ee0b888f 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(
                 if let Ok(src) = src {
                     let original_file = src.file_id.original_file(db);
                     let line_index = host.analysis().file_line_index(original_file).unwrap();
-                    let text_range = src.value.either(
-                        |it| it.syntax_node_ptr().range(),
-                        |it| it.syntax_node_ptr().range(),
-                    );
+                    let text_range = src.value.syntax_node_ptr().range();
                     let (start, end) = (
                         line_index.line_col(text_range.start()),
                         line_index.line_col(text_range.end()),
@@ -192,12 +189,7 @@ pub fn analysis_stats(
                         // FIXME: it might be nice to have a function (on Analysis?) that goes from Source<T> -> (LineCol, LineCol) directly
                         // But also, we should just turn the type mismatches into diagnostics and provide these
                         let root = db.parse_or_expand(src.file_id).unwrap();
-                        let node = src.map(|e| {
-                            e.either(
-                                |p| p.to_node(&root).syntax().clone(),
-                                |p| p.to_node(&root).syntax().clone(),
-                            )
-                        });
+                        let node = src.map(|e| e.to_node(&root).syntax().clone());
                         let original_range = original_range(db, node.as_ref());
                         let path = db.file_relative_path(original_range.file_id);
                         let line_index =


Test patch:
diff --git a/crates/ra_hir_ty/src/tests.rs b/crates/ra_hir_ty/src/tests.rs
index 54e31602fb..81fc0f63a8 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 {
 
         for (expr, ty) in inference_result.type_of_expr.iter() {
             let syntax_ptr = match body_source_map.expr_syntax(expr) {
-                Ok(sp) => {
-                    sp.map(|ast| ast.either(|it| it.syntax_node_ptr(), |it| it.syntax_node_ptr()))
-                }
+                Ok(sp) => sp.map(|ast| ast.syntax_node_ptr()),
                 Err(SyntheticSyntax) => continue,
             };
             types.push((syntax_ptr.clone(), ty));
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 0000000000..75043c9c04
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/inline/err/0014_record_literal_before_ellipsis_recovery.rast
@@ -0,0 +1,49 @@
+SOURCE_FILE@[0; 45)
+  FN_DEF@[0; 44)
+    FN_KW@[0; 2) "fn"
+    WHITESPACE@[2; 3) " "
+    NAME@[3; 7)
+      IDENT@[3; 7) "main"
+    PARAM_LIST@[7; 9)
+      L_PAREN@[7; 8) "("
+      R_PAREN@[8; 9) ")"
+    WHITESPACE@[9; 10) " "
+    BLOCK_EXPR@[10; 44)
+      BLOCK@[10; 44)
+        L_CURLY@[10; 11) "{"
+        WHITESPACE@[11; 16) "\n    "
+        RECORD_LIT@[16; 42)
+          PATH@[16; 17)
+            PATH_SEGMENT@[16; 17)
+              NAME_REF@[16; 17)
+                IDENT@[16; 17) "S"
+          WHITESPACE@[17; 18) " "
+          RECORD_FIELD_LIST@[18; 42)
+            L_CURLY@[18; 19) "{"
+            WHITESPACE@[19; 20) " "
+            RECORD_FIELD@[20; 40)
+              NAME_REF@[20; 25)
+                IDENT@[20; 25) "field"
+              WHITESPACE@[25; 26) " "
+              RANGE_EXPR@[26; 40)
+                DOT2@[26; 28) ".."
+                CALL_EXPR@[28; 40)
+                  PATH_EXPR@[28; 38)
+                    PATH@[28; 38)
+                      PATH@[28; 29)
+                        PATH_SEGMENT@[28; 29)
+                          NAME_REF@[28; 29)
+                            IDENT@[28; 29) "S"
+                      COLON2@[29; 31) "::"
+                      PATH_SEGMENT@[31; 38)
+                        NAME_REF@[31; 38)
+                          IDENT@[31; 38) "default"
+                  ARG_LIST@[38; 40)
+                    L_PAREN@[38; 39) "("
+                    R_PAREN@[39; 40) ")"
+            WHITESPACE@[40; 41) " "
+            R_CURLY@[41; 42) "}"
+        WHITESPACE@[42; 43) "\n"
+        R_CURLY@[43; 44) "}"
+  WHITESPACE@[44; 45) "\n"
+error [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 0000000000..a4e5b2f693
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/inline/err/0014_record_literal_before_ellipsis_recovery.rs
@@ -0,0 +1,3 @@
+fn main() {
+    S { field ..S::default() }
+}
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 f4206858bf..89a6117996 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)
               L_CURLY@[27; 28) "{"
               WHITESPACE@[28; 29) " "
               RECORD_FIELD@[29; 30)
-                NAME_REF@[29; 30)
-                  IDENT@[29; 30) "x"
+                PATH_EXPR@[29; 30)
+                  PATH@[29; 30)
+                    PATH_SEGMENT@[29; 30)
+                      NAME_REF@[29; 30)
+                        IDENT@[29; 30) "x"
               COMMA@[30; 31) ","
               WHITESPACE@[31; 32) " "
               RECORD_FIELD@[32; 37)
@@ -62,8 +65,11 @@ SOURCE_FILE@[0; 112)
               L_CURLY@[48; 49) "{"
               WHITESPACE@[49; 50) " "
               RECORD_FIELD@[50; 51)
-                NAME_REF@[50; 51)
-                  IDENT@[50; 51) "x"
+                PATH_EXPR@[50; 51)
+                  PATH@[50; 51)
+                    PATH_SEGMENT@[50; 51)
+                      NAME_REF@[50; 51)
+                        IDENT@[50; 51) "x"
               COMMA@[51; 52) ","
               WHITESPACE@[52; 53) " "
               RECORD_FIELD@[53; 58)
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs
index 3b60c55f3f..4164bfd5ed 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 {
     should_skip
 }
 
-const REWRITE: bool = true;
+const REWRITE: bool = false;
 
 /// Asserts that `expected` and `actual` strings are equal. If they differ only
 /// in trailing or leading whitespace the test won't fail and