Repository: rust-analyzer/rust-analyzer
Pull request #1853 (2019-09-16 11:27:21)

Introduce FromSource trait
The idea is to provide an ability to get HIR from AST in a more general way than it's possible using `source_binder`. 
It also could help with #1622 fixing.


Patch:
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs
index 706d24c325..28002f7400 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -313,7 +313,7 @@ pub struct StructField {
     pub(crate) id: StructFieldId,
 }
 
-#[derive(Debug)]
+#[derive(Debug, PartialEq, Eq)]
 pub enum FieldSource {
     Named(ast::RecordFieldDef),
     Pos(ast::TupleFieldDef),
diff --git a/crates/ra_hir/src/from_source.rs b/crates/ra_hir/src/from_source.rs
new file mode 100644
index 0000000000..c6ad85fc7c
--- /dev/null
+++ b/crates/ra_hir/src/from_source.rs
@@ -0,0 +1,211 @@
+use ra_db::{FileId, FilePosition};
+use ra_syntax::{
+    algo::find_node_at_offset,
+    ast::{self, AstNode, NameOwner},
+    SyntaxNode,
+};
+
+use crate::{
+    db::{AstDatabase, DefDatabase, HirDatabase},
+    ids::{AstItemDef, LocationCtx},
+    name::AsName,
+    Const, Crate, Enum, EnumVariant, FieldSource, Function, HasSource, ImplBlock, Module,
+    ModuleSource, Source, Static, Struct, StructField, Trait, TypeAlias, Union, VariantDef,
+};
+
+pub trait FromSource: Sized {
+    type Ast;
+    fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self>;
+}
+
+impl FromSource for Struct {
+    type Ast = ast::StructDef;
+    fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> {
+        let id = from_source(db, src)?;
+        Some(Struct { id })
+    }
+}
+impl FromSource for Union {
+    type Ast = ast::StructDef;
+    fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> {
+        let id = from_source(db, src)?;
+        Some(Union { id })
+    }
+}
+impl FromSource for Enum {
+    type Ast = ast::EnumDef;
+    fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> {
+        let id = from_source(db, src)?;
+        Some(Enum { id })
+    }
+}
+impl FromSource for Trait {
+    type Ast = ast::TraitDef;
+    fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> {
+        let id = from_source(db, src)?;
+        Some(Trait { id })
+    }
+}
+impl FromSource for Function {
+    type Ast = ast::FnDef;
+    fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> {
+        let id = from_source(db, src)?;
+        Some(Function { id })
+    }
+}
+impl FromSource for Const {
+    type Ast = ast::ConstDef;
+    fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> {
+        let id = from_source(db, src)?;
+        Some(Const { id })
+    }
+}
+impl FromSource for Static {
+    type Ast = ast::StaticDef;
+    fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> {
+        let id = from_source(db, src)?;
+        Some(Static { id })
+    }
+}
+impl FromSource for TypeAlias {
+    type Ast = ast::TypeAliasDef;
+    fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> {
+        let id = from_source(db, src)?;
+        Some(TypeAlias { id })
+    }
+}
+// FIXME: add impl FromSource for MacroDef
+
+impl FromSource for ImplBlock {
+    type Ast = ast::ImplBlock;
+    fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> {
+        let module_src = crate::ModuleSource::from_child_node(
+            db,
+            src.file_id.original_file(db),
+            &src.ast.syntax(),
+        );
+        let module = Module::from_definition(db, Source { file_id: src.file_id, ast: module_src })?;
+        let impls = module.impl_blocks(db);
+        impls.into_iter().find(|b| b.source(db) == src)
+    }
+}
+
+impl FromSource for EnumVariant {
+    type Ast = ast::EnumVariant;
+    fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> {
+        let parent_enum = src.ast.parent_enum();
+        let src_enum = Source { file_id: src.file_id, ast: parent_enum };
+        let variants = Enum::from_source(db, src_enum)?.variants(db);
+        variants.into_iter().find(|v| v.source(db) == src)
+    }
+}
+
+impl FromSource for StructField {
+    type Ast = FieldSource;
+    fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> {
+        let variant_def: VariantDef = match src.ast {
+            FieldSource::Named(ref field) => {
+                let ast = field.syntax().ancestors().find_map(ast::StructDef::cast)?;
+                let src = Source { file_id: src.file_id, ast };
+                let def = Struct::from_source(db, src)?;
+                VariantDef::from(def)
+            }
+            FieldSource::Pos(ref field) => {
+                let ast = field.syntax().ancestors().find_map(ast::EnumVariant::cast)?;
+                let src = Source { file_id: src.file_id, ast };
+                let def = EnumVariant::from_source(db, src)?;
+                VariantDef::from(def)
+            }
+        };
+        variant_def
+            .variant_data(db)
+            .fields()
+            .into_iter()
+            .flat_map(|it| it.iter())
+            .map(|(id, _)| StructField { parent: variant_def.clone(), id })
+            .find(|f| f.source(db) == src)
+    }
+}
+
+// FIXME: simplify it
+impl ModuleSource {
+    pub fn from_position(
+        db: &(impl DefDatabase + AstDatabase),
+        position: FilePosition,
+    ) -> ModuleSource {
+        let parse = db.parse(position.file_id);
+        match &find_node_at_offset::<ast::Module>(parse.tree().syntax(), position.offset) {
+            Some(m) if !m.has_semi() => ModuleSource::Module(m.clone()),
+            _ => {
+                let source_file = parse.tree().to_owned();
+                ModuleSource::SourceFile(source_file)
+            }
+        }
+    }
+
+    pub fn from_child_node(
+        db: &(impl DefDatabase + AstDatabase),
+        file_id: FileId,
+        child: &SyntaxNode,
+    ) -> ModuleSource {
+        if let Some(m) = child.ancestors().filter_map(ast::Module::cast).find(|it| !it.has_semi()) {
+            ModuleSource::Module(m.clone())
+        } else {
+            let source_file = db.parse(file_id).tree().to_owned();
+            ModuleSource::SourceFile(source_file)
+        }
+    }
+
+    pub fn from_file_id(db: &(impl DefDatabase + AstDatabase), file_id: FileId) -> ModuleSource {
+        let source_file = db.parse(file_id).tree().to_owned();
+        ModuleSource::SourceFile(source_file)
+    }
+}
+
+impl Module {
+    pub fn from_declaration(db: &impl HirDatabase, src: Source<ast::Module>) -> Option<Self> {
+        let src_parent = Source {
+            file_id: src.file_id,
+            ast: ModuleSource::new(db, Some(src.file_id.original_file(db)), None),
+        };
+        let parent_module = Module::from_definition(db, src_parent)?;
+        let child_name = src.ast.name()?;
+        parent_module.child(db, &child_name.as_name())
+    }
+
+    pub fn from_definition(
+        db: &(impl DefDatabase + AstDatabase),
+        src: Source<ModuleSource>,
+    ) -> Option<Self> {
+        let decl_id = match src.ast {
+            ModuleSource::Module(ref module) => {
+                assert!(!module.has_semi());
+                let ast_id_map = db.ast_id_map(src.file_id);
+                let item_id = ast_id_map.ast_id(module).with_file_id(src.file_id);
+                Some(item_id)
+            }
+            ModuleSource::SourceFile(_) => None,
+        };
+
+        let source_root_id = db.file_source_root(src.file_id.original_file(db));
+        db.source_root_crates(source_root_id).iter().map(|&crate_id| Crate { crate_id }).find_map(
+            |krate| {
+                let def_map = db.crate_def_map(krate);
+                let module_id = def_map.find_module_by_source(src.file_id, decl_id)?;
+                Some(Module { krate, module_id })
+            },
+        )
+    }
+}
+
+fn from_source<N, DEF>(db: &(impl DefDatabase + AstDatabase), src: Source<N>) -> Option<DEF>
+where
+    N: AstNode,
+    DEF: AstItemDef<N>,
+{
+    let module_src =
+        crate::ModuleSource::from_child_node(db, src.file_id.original_file(db), &src.ast.syntax());
+    let module = Module::from_definition(db, Source { file_id: src.file_id, ast: module_src })?;
+    let ctx = LocationCtx::new(db, module, src.file_id);
+    Some(DEF::from_ast(ctx, &src.ast))
+}
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs
index e7a576aa0a..a9de9fb6b1 100644
--- a/crates/ra_hir/src/lib.rs
+++ b/crates/ra_hir/src/lib.rs
@@ -53,6 +53,8 @@ pub mod diagnostics;
 
 mod code_model;
 
+pub mod from_source;
+
 #[cfg(test)]
 mod marks;
 
@@ -67,6 +69,7 @@ pub use self::{
     adt::VariantDef,
     either::Either,
     expr::ExprScopes,
+    from_source::FromSource,
     generics::{GenericParam, GenericParams, HasGenericParams},
     ids::{HirFileId, MacroCallId, MacroCallLoc, MacroDefId, MacroFile},
     impl_block::ImplBlock,
diff --git a/crates/ra_hir/src/mock.rs b/crates/ra_hir/src/mock.rs
index 8dcea50713..cb405091ee 100644
--- a/crates/ra_hir/src/mock.rs
+++ b/crates/ra_hir/src/mock.rs
@@ -93,7 +93,11 @@ impl MockDatabase {
         let mut files: Vec<FileId> = self.files.values().copied().collect();
         files.sort();
         for file in files {
-            let module = crate::source_binder::module_from_file_id(self, file).unwrap();
+            let src = crate::Source {
+                file_id: file.into(),
+                ast: crate::ModuleSource::new(self, Some(file), None),
+            };
+            let module = crate::Module::from_definition(self, src).unwrap();
             module.diagnostics(
                 self,
                 &mut DiagnosticSink::new(|d| {
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs
index 4d895f0a19..296acc3642 100644
--- a/crates/ra_hir/src/source_binder.rs
+++ b/crates/ra_hir/src/source_binder.rs
@@ -7,10 +7,9 @@
 /// purely for "IDE needs".
 use std::sync::Arc;
 
-use ra_db::{FileId, FilePosition};
+use ra_db::FileId;
 use ra_syntax::{
-    algo::find_node_at_offset,
-    ast::{self, AstNode, NameOwner},
+    ast::{self, AstNode},
     AstPtr,
     SyntaxKind::*,
     SyntaxNode, SyntaxNodePtr, TextRange, TextUnit,
@@ -28,119 +27,28 @@ use crate::{
     path::known,
     resolve::{ScopeDef, TypeNs, ValueNs},
     ty::method_resolution::implements_trait,
-    AsName, AstId, Const, Crate, DefWithBody, Either, Enum, Function, HasBody, HirFileId, MacroDef,
-    Module, Name, Path, Resolver, Static, Struct, Trait, Ty,
+    AsName, Const, DefWithBody, Either, Enum, FromSource, Function, HasBody, HirFileId, MacroDef,
+    Module, Name, Path, Resolver, Static, Struct, Ty,
 };
 
-/// Locates the module by `FileId`. Picks topmost module in the file.
-pub fn module_from_file_id(db: &impl HirDatabase, file_id: FileId) -> Option<Module> {
-    module_from_source(db, file_id.into(), None)
-}
-
-/// Locates the child module by `mod child;` declaration.
-pub fn module_from_declaration(
-    db: &impl HirDatabase,
-    file_id: FileId,
-    decl: ast::Module,
-) -> Option<Module> {
-    let parent_module = module_from_file_id(db, file_id);
-    let child_name = decl.name();
-    match (parent_module, child_name) {
-        (Some(parent_module), Some(child_name)) => parent_module.child(db, &child_name.as_name()),
-        _ => None,
-    }
-}
-
-/// Locates the module by position in the source code.
-pub fn module_from_position(db: &impl HirDatabase, position: FilePosition) -> Option<Module> {
-    let parse = db.parse(position.file_id);
-    match &find_node_at_offset::<ast::Module>(parse.tree().syntax(), position.offset) {
-        Some(m) if !m.has_semi() => module_from_inline(db, position.file_id, m.clone()),
-        _ => module_from_file_id(db, position.file_id),
-    }
-}
-
-fn module_from_inline(
-    db: &impl HirDatabase,
-    file_id: FileId,
-    module: ast::Module,
-) -> Option<Module> {
-    assert!(!module.has_semi());
-    let file_id = file_id.into();
-    let ast_id_map = db.ast_id_map(file_id);
-    let item_id = ast_id_map.ast_id(&module).with_file_id(file_id);
-    module_from_source(db, file_id, Some(item_id))
-}
-
-/// Locates the module by child syntax element within the module
-pub fn module_from_child_node(
-    db: &impl HirDatabase,
-    file_id: FileId,
-    child: &SyntaxNode,
-) -> Option<Module> {
-    if let Some(m) = child.ancestors().filter_map(ast::Module::cast).find(|it| !it.has_semi()) {
-        module_from_inline(db, file_id, m)
-    } else {
-        module_from_file_id(db, file_id)
-    }
-}
-
-fn module_from_source(
-    db: &impl HirDatabase,
-    file_id: HirFileId,
-    decl_id: Option<AstId<ast::Module>>,
-) -> Option<Module> {
-    let source_root_id = db.file_source_root(file_id.as_original_file());
-    db.source_root_crates(source_root_id).iter().map(|&crate_id| Crate { crate_id }).find_map(
-        |krate| {
-            let def_map = db.crate_def_map(krate);
-            let module_id = def_map.find_module_by_source(file_id, decl_id)?;
-            Some(Module { krate, module_id })
-        },
-    )
-}
-
-pub fn struct_from_module(
-    db: &impl HirDatabase,
-    module: Module,
-    struct_def: &ast::StructDef,
-) -> Struct {
-    let file_id = module.definition_source(db).file_id;
-    let ctx = LocationCtx::new(db, module, file_id);
-    Struct { id: ctx.to_def(struct_def) }
-}
-
-pub fn enum_from_module(db: &impl HirDatabase, module: Module, enum_def: &ast::EnumDef) -> Enum {
-    let file_id = module.definition_source(db).file_id;
-    let ctx = LocationCtx::new(db, module, file_id);
-    Enum { id: ctx.to_def(enum_def) }
-}
-
-pub fn trait_from_module(
-    db: &impl HirDatabase,
-    module: Module,
-    trait_def: &ast::TraitDef,
-) -> Trait {
-    let file_id = module.definition_source(db).file_id;
-    let ctx = LocationCtx::new(db, module, file_id);
-    Trait { id: ctx.to_def(trait_def) }
-}
-
 fn try_get_resolver_for_node(
     db: &impl HirDatabase,
     file_id: FileId,
     node: &SyntaxNode,
 ) -> Option<Resolver> {
     if let Some(module) = ast::Module::cast(node.clone()) {
-        Some(module_from_declaration(db, file_id, module)?.resolver(db))
-    } else if let Some(_) = ast::SourceFile::cast(node.clone()) {
-        Some(module_from_source(db, file_id.into(), None)?.resolver(db))
+        let src = crate::Source { file_id: file_id.into(), ast: module };
+        Some(crate::Module::from_declaration(db, src)?.resolver(db))
+    } else if let Some(file) = ast::SourceFile::cast(node.clone()) {
+        let src =
+            crate::Source { file_id: file_id.into(), ast: crate::ModuleSource::SourceFile(file) };
+        Some(crate::Module::from_definition(db, src)?.resolver(db))
     } else if let Some(s) = ast::StructDef::cast(node.clone()) {
-        let module = module_from_child_node(db, file_id, s.syntax())?;
-        Some(struct_from_module(db, module, &s).resolver(db))
+        let src = crate::Source { file_id: file_id.into(), ast: s };
+        Some(Struct::from_source(db, src)?.resolver(db))
     } else if let Some(e) = ast::EnumDef::cast(node.clone()) {
-        let module = module_from_child_node(db, file_id, e.syntax())?;
-        Some(enum_from_module(db, module, &e).resolver(db))
+        let src = crate::Source { file_id: file_id.into(), ast: e };
+        Some(Enum::from_source(db, src)?.resolver(db))
     } else if node.kind() == FN_DEF || node.kind() == CONST_DEF || node.kind() == STATIC_DEF {
         Some(def_with_body_from_child_node(db, file_id, node)?.resolver(db))
     } else {
@@ -154,8 +62,10 @@ fn def_with_body_from_child_node(
     file_id: FileId,
     node: &SyntaxNode,
 ) -> Option<DefWithBody> {
-    let module = module_from_child_node(db, file_id, node)?;
+    let src = crate::ModuleSource::from_child_node(db, file_id, node);
+    let module = Module::from_definition(db, crate::Source { file_id: file_id.into(), ast: src })?;
     let ctx = LocationCtx::new(db, module, file_id.into());
+
     node.ancestors().find_map(|node| {
         if let Some(def) = ast::FnDef::cast(node.clone()) {
             return Some(Function { id: ctx.to_def(&def) }.into());
diff --git a/crates/ra_ide_api/src/completion/completion_context.rs b/crates/ra_ide_api/src/completion/completion_context.rs
index 7139947b34..59bd3689b5 100644
--- a/crates/ra_ide_api/src/completion/completion_context.rs
+++ b/crates/ra_ide_api/src/completion/completion_context.rs
@@ -1,4 +1,3 @@
-use hir::source_binder;
 use ra_syntax::{
     algo::{find_covering_element, find_node_at_offset},
     ast, AstNode, Parse, SourceFile,
@@ -47,7 +46,11 @@ impl<'a> CompletionContext<'a> {
         original_parse: &'a Parse<ast::SourceFile>,
         position: FilePosition,
     ) -> Option<CompletionContext<'a>> {
-        let module = source_binder::module_from_position(db, position);
+        let src = hir::ModuleSource::from_position(db, position);
+        let module = hir::Module::from_definition(
+            db,
+            hir::Source { file_id: position.file_id.into(), ast: src },
+        );
         let token =
             original_parse.tree().syntax().token_at_offset(position.offset).left_biased()?;
         let analyzer =
diff --git a/crates/ra_ide_api/src/diagnostics.rs b/crates/ra_ide_api/src/diagnostics.rs
index c448aa8c5d..1ae152e5b1 100644
--- a/crates/ra_ide_api/src/diagnostics.rs
+++ b/crates/ra_ide_api/src/diagnostics.rs
@@ -1,9 +1,6 @@
 use std::cell::RefCell;
 
-use hir::{
-    diagnostics::{AstDiagnostic, Diagnostic as _, DiagnosticSink},
-    source_binder,
-};
+use hir::diagnostics::{AstDiagnostic, Diagnostic as _, DiagnosticSink};
 use itertools::Itertools;
 use ra_assists::ast_editor::{AstBuilder, AstEditor};
 use ra_db::SourceDatabase;
@@ -89,7 +86,10 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
             fix: Some(fix),
         })
     });
-    if let Some(m) = source_binder::module_from_file_id(db, file_id) {
+    let source_file = db.parse(file_id).tree().to_owned();
+    let src =
+        hir::Source { file_id: file_id.into(), ast: hir::ModuleSource::SourceFile(source_file) };
+    if let Some(m) = hir::Module::from_definition(db, src) {
         m.diagnostics(db, &mut sink);
     };
     drop(sink);
diff --git a/crates/ra_ide_api/src/folding_ranges.rs b/crates/ra_ide_api/src/folding_ranges.rs
index 79fadcc5db..3ab6c195e6 100644
--- a/crates/ra_ide_api/src/folding_ranges.rs
+++ b/crates/ra_ide_api/src/folding_ranges.rs
@@ -87,7 +87,6 @@ fn fold_kind(kind: SyntaxKind) -> Option<FoldKind> {
         | EXTERN_ITEM_LIST
         | USE_TREE_LIST
         | BLOCK
-        | MATCH_ARM_LIST
         | ENUM_VARIANT_LIST
         | TOKEN_TREE => Some(FoldKind::Block),
         _ => None,
@@ -359,18 +358,4 @@ macro_rules! foo <fold>{
         let folds = &[FoldKind::Block];
         do_check(text, folds);
     }
-
-    #[test]
-    fn test_fold_match_arms() {
-        let text = r#"
-fn main() <fold>{
-    match 0 <fold>{
-        0 => 0,
-        _ => 1,
-    }</fold>
-}</fold>"#;
-
-        let folds = &[FoldKind::Block, FoldKind::Block];
-        do_check(text, folds);
-    }
 }
diff --git a/crates/ra_ide_api/src/goto_definition.rs b/crates/ra_ide_api/src/goto_definition.rs
index 503dcacff4..bc8863dadb 100644
--- a/crates/ra_ide_api/src/goto_definition.rs
+++ b/crates/ra_ide_api/src/goto_definition.rs
@@ -96,9 +96,8 @@ pub(crate) fn name_definition(
 
     if let Some(module) = ast::Module::cast(parent.clone()) {
         if module.has_semi() {
-            if let Some(child_module) =
-                hir::source_binder::module_from_declaration(db, file_id, module)
-            {
+            let src = hir::Source { file_id: file_id.into(), ast: module };
+            if let Some(child_module) = hir::Module::from_declaration(db, src) {
                 let nav = NavigationTarget::from_module(db, child_module);
                 return Some(vec![nav]);
             }
diff --git a/crates/ra_ide_api/src/impls.rs b/crates/ra_ide_api/src/impls.rs
index c5620dd522..f57f9a21b6 100644
--- a/crates/ra_ide_api/src/impls.rs
+++ b/crates/ra_ide_api/src/impls.rs
@@ -1,4 +1,4 @@
-use hir::{db::HirDatabase, source_binder, ApplicationTy, Ty, TypeCtor};
+use hir::{db::HirDatabase, ApplicationTy, FromSource, Ty, TypeCtor};
 use ra_db::SourceDatabase;
 use ra_syntax::{algo::find_node_at_offset, ast, AstNode};
 
@@ -11,17 +11,21 @@ pub(crate) fn goto_implementation(
     let parse = db.parse(position.file_id);
     let syntax = parse.tree().syntax().clone();
 
-    let module = source_binder::module_from_position(db, position)?;
+    let src = hir::ModuleSource::from_position(db, position);
+    let module = hir::Module::from_definition(
+        db,
+        hir::Source { file_id: position.file_id.into(), ast: src },
+    )?;
 
     if let Some(nominal_def) = find_node_at_offset::<ast::NominalDef>(&syntax, position.offset) {
         return Some(RangeInfo::new(
             nominal_def.syntax().text_range(),
-            impls_for_def(db, &nominal_def, module)?,
+            impls_for_def(db, position, &nominal_def, module)?,
         ));
     } else if let Some(trait_def) = find_node_at_offset::<ast::TraitDef>(&syntax, position.offset) {
         return Some(RangeInfo::new(
             trait_def.syntax().text_range(),
-            impls_for_trait(db, &trait_def, module)?,
+            impls_for_trait(db, position, &trait_def, module)?,
         ));
     }
 
@@ -30,14 +34,19 @@ pub(crate) fn goto_implementation(
 
 fn impls_for_def(
     db: &RootDatabase,
+    position: FilePosition,
     node: &ast::NominalDef,
     module: hir::Module,
 ) -> Option<Vec<NavigationTarget>> {
     let ty = match node {
         ast::NominalDef::StructDef(def) => {
-            source_binder::struct_from_module(db, module, &def).ty(db)
+            let src = hir::Source { file_id: position.file_id.into(), ast: def.clone() };
+            hir::Struct::from_source(db, src)?.ty(db)
+        }
+        ast::NominalDef::EnumDef(def) => {
+            let src = hir::Source { file_id: position.file_id.into(), ast: def.clone() };
+            hir::Enum::from_source(db, src)?.ty(db)
         }
-        ast::NominalDef::EnumDef(def) => source_binder::enum_from_module(db, module, &def).ty(db),
     };
 
     let krate = module.krate(db)?;
@@ -54,10 +63,12 @@ fn impls_for_def(
 
 fn impls_for_trait(
     db: &RootDatabase,
+    position: FilePosition,
     node: &ast::TraitDef,
     module: hir::Module,
 ) -> Option<Vec<NavigationTarget>> {
-    let tr = source_binder::trait_from_module(db, module, node);
+    let src = hir::Source { file_id: position.file_id.into(), ast: node.clone() };
+    let tr = hir::Trait::from_source(db, src)?;
 
     let krate = module.krate(db)?;
     let impls = db.impls_in_crate(krate);
diff --git a/crates/ra_ide_api/src/parent_module.rs b/crates/ra_ide_api/src/parent_module.rs
index 7d5f9ea2c2..3668da8d74 100644
--- a/crates/ra_ide_api/src/parent_module.rs
+++ b/crates/ra_ide_api/src/parent_module.rs
@@ -5,7 +5,11 @@ use crate::{db::RootDatabase, NavigationTarget};
 /// This returns `Vec` because a module may be included from several places. We
 /// don't handle this case yet though, so the Vec has length at most one.
 pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<NavigationTarget> {
-    let module = match hir::source_binder::module_from_position(db, position) {
+    let src = hir::ModuleSource::from_position(db, position);
+    let module = match hir::Module::from_definition(
+        db,
+        hir::Source { file_id: position.file_id.into(), ast: src },
+    ) {
         None => return Vec::new(),
         Some(it) => it,
     };
@@ -15,10 +19,12 @@ pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<Na
 
 /// Returns `Vec` for the same reason as `parent_module`
 pub(crate) fn crate_for(db: &RootDatabase, file_id: FileId) -> Vec<CrateId> {
-    let module = match hir::source_binder::module_from_file_id(db, file_id) {
-        Some(it) => it,
-        None => return Vec::new(),
-    };
+    let src = hir::ModuleSource::from_file_id(db, file_id);
+    let module =
+        match hir::Module::from_definition(db, hir::Source { file_id: file_id.into(), ast: src }) {
+            Some(it) => it,
+            None => return Vec::new(),
+        };
     let krate = match module.krate(db) {
         Some(it) => it,
         None => return Vec::new(),
diff --git a/crates/ra_ide_api/src/references.rs b/crates/ra_ide_api/src/references.rs
index 5f1f0efc30..acca71f2a8 100644
--- a/crates/ra_ide_api/src/references.rs
+++ b/crates/ra_ide_api/src/references.rs
@@ -1,4 +1,4 @@
-use hir::{source_binder, Either, ModuleSource};
+use hir::{Either, ModuleSource};
 use ra_db::SourceDatabase;
 use ra_syntax::{algo::find_node_at_offset, ast, AstNode, SourceFile, SyntaxNode};
 use relative_path::{RelativePath, RelativePathBuf};
@@ -135,9 +135,8 @@ fn rename_mod(
 ) -> Option<SourceChange> {
     let mut source_file_edits = Vec::new();
     let mut file_system_edits = Vec::new();
-    if let Some(module) =
-        source_binder::module_from_declaration(db, position.file_id, ast_module.clone())
-    {
+    let module_src = hir::Source { file_id: position.file_id.into(), ast: ast_module.clone() };
+    if let Some(module) = hir::Module::from_declaration(db, module_src) {
         let src = module.definition_source(db);
         let file_id = src.file_id.as_original_file();
         match src.ast {
diff --git a/crates/ra_ide_api/src/runnables.rs b/crates/ra_ide_api/src/runnables.rs
index 9d84b3d084..095ca56c4c 100644
--- a/crates/ra_ide_api/src/runnables.rs
+++ b/crates/ra_ide_api/src/runnables.rs
@@ -63,7 +63,9 @@ fn runnable_mod(db: &RootDatabase, file_id: FileId, module: ast::Module) -> Opti
         return None;
     }
     let range = module.syntax().text_range();
-    let module = hir::source_binder::module_from_child_node(db, file_id, module.syntax())?;
+    let src = hir::ModuleSource::from_child_node(db, file_id, &module.syntax());
+    let module =
+        hir::Module::from_definition(db, hir::Source { file_id: file_id.into(), ast: src })?;
 
     let path = module.path_to_root(db).into_iter().rev().filter_map(|it| it.name(db)).join("::");
     Some(Runnable { range, kind: RunnableKind::TestMod { path } })
diff --git a/crates/ra_lsp_server/src/conv.rs b/crates/ra_lsp_server/src/conv.rs
index d78f779255..5fa52ec1b4 100644
--- a/crates/ra_lsp_server/src/conv.rs
+++ b/crates/ra_lsp_server/src/conv.rs
@@ -6,8 +6,8 @@ use lsp_types::{
 };
 use ra_ide_api::{
     translate_offset_with_edit, CompletionItem, CompletionItemKind, FileId, FilePosition,
-    FileRange, FileSystemEdit, Fold, FoldKind, InsertTextFormat, LineCol, LineIndex,
-    NavigationTarget, RangeInfo, Severity, SourceChange, SourceFileEdit,
+    FileRange, FileSystemEdit, InsertTextFormat, LineCol, LineIndex, NavigationTarget, RangeInfo,
+    Severity, SourceChange, SourceFileEdit,
 };
 use ra_syntax::{SyntaxKind, TextRange, TextUnit};
 use ra_text_edit::{AtomTextEdit, TextEdit};
@@ -225,26 +225,6 @@ impl ConvWith<(&LineIndex, LineEndings)> for &AtomTextEdit {
     }
 }
 
-impl ConvWith<&LineIndex> for Fold {
-    type Output = lsp_types::FoldingRange;
-
-    fn conv_with(self, line_index: &LineIndex) -> lsp_types::FoldingRange {
-        let range = self.range.conv_with(&line_index);
-        lsp_types::FoldingRange {
-            start_line: range.start.line,
-            start_character: Some(range.start.character),
-            end_line: range.end.line,
-            end_character: Some(range.end.character),
-            kind: match self.kind {
-                FoldKind::Comment => Some(lsp_types::FoldingRangeKind::Comment),
-                FoldKind::Imports => Some(lsp_types::FoldingRangeKind::Imports),
-                FoldKind::Mods => None,
-                FoldKind::Block => None,
-            },
-        }
-    }
-}
-
 impl<T: ConvWith<CTX>, CTX> ConvWith<CTX> for Option<T> {
     type Output = Option<T::Output>;
 
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs
index ae57e57e95..948d543ea2 100644
--- a/crates/ra_lsp_server/src/main_loop/handlers.rs
+++ b/crates/ra_lsp_server/src/main_loop/handlers.rs
@@ -3,11 +3,14 @@ use std::{fmt::Write as _, io::Write as _};
 use lsp_server::ErrorCode;
 use lsp_types::{
     CodeAction, CodeActionResponse, CodeLens, Command, CompletionItem, Diagnostic,
-    DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange, FoldingRangeParams,
-    Hover, HoverContents, Location, MarkupContent, MarkupKind, Position, PrepareRenameResponse,
-    Range, RenameParams, SymbolInformation, TextDocumentIdentifier, TextEdit, WorkspaceEdit,
+    DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange, FoldingRangeKind,
+    FoldingRangeParams, Hover, HoverContents, Location, MarkupContent, MarkupKind, Position,
+    PrepareRenameResponse, Range, RenameParams, SymbolInformation, TextDocumentIdentifier,
+    TextEdit, WorkspaceEdit,
+};
+use ra_ide_api::{
+    AssistId, FileId, FilePosition, FileRange, FoldKind, Query, Runnable, RunnableKind,
 };
-use ra_ide_api::{AssistId, FileId, FilePosition, FileRange, Query, Runnable, RunnableKind};
 use ra_prof::profile;
 use ra_syntax::{AstNode, SyntaxKind, TextRange, TextUnit};
 use rustc_hash::FxHashMap;
@@ -380,9 +383,32 @@ pub fn handle_folding_range(
     params: FoldingRangeParams,
 ) -> Result<Option<Vec<FoldingRange>>> {
     let file_id = params.text_document.try_conv_with(&world)?;
-    let folds = world.analysis().folding_ranges(file_id)?;
     let line_index = world.analysis().file_line_index(file_id)?;
-    let res = Some(folds.into_iter().map_conv_with(&*line_index).collect());
+
+    let res = Some(
+        world
+            .analysis()
+            .folding_ranges(file_id)?
+            .into_iter()
+            .map(|fold| {
+                let kind = match fold.kind {
+                    FoldKind::Comment => Some(FoldingRangeKind::Comment),
+                    FoldKind::Imports => Some(FoldingRangeKind::Imports),
+                    FoldKind::Mods => None,
+                    FoldKind::Block => None,
+                };
+                let range = fold.range.conv_with(&line_index);
+                FoldingRange {
+                    start_line: range.start.line,
+                    start_character: Some(range.start.character),
+                    end_line: range.end.line,
+                    end_character: Some(range.start.character),
+                    kind,
+                }
+            })
+            .collect(),
+    );
+
     Ok(res)
 }
 


Test patch:
diff --git a/crates/ra_hir/src/nameres/tests/incremental.rs b/crates/ra_hir/src/nameres/tests/incremental.rs
index bc721f6e0a..c41862a0bd 100644
--- a/crates/ra_hir/src/nameres/tests/incremental.rs
+++ b/crates/ra_hir/src/nameres/tests/incremental.rs
@@ -114,7 +114,11 @@ fn typing_inside_a_macro_should_not_invalidate_def_map() {
     );
     {
         let events = db.log_executed(|| {
-            let module = crate::source_binder::module_from_file_id(&db, pos.file_id).unwrap();
+            let src = crate::Source {
+                file_id: pos.file_id.into(),
+                ast: crate::ModuleSource::new(&db, Some(pos.file_id), None),
+            };
+            let module = crate::Module::from_definition(&db, src).unwrap();
             let decls = module.declarations(&db);
             assert_eq!(decls.len(), 18);
         });
@@ -124,7 +128,11 @@ fn typing_inside_a_macro_should_not_invalidate_def_map() {
 
     {
         let events = db.log_executed(|| {
-            let module = crate::source_binder::module_from_file_id(&db, pos.file_id).unwrap();
+            let src = crate::Source {
+                file_id: pos.file_id.into(),
+                ast: crate::ModuleSource::new(&db, Some(pos.file_id), None),
+            };
+            let module = crate::Module::from_definition(&db, src).unwrap();
             let decls = module.declarations(&db);
             assert_eq!(decls.len(), 18);
         });
diff --git a/crates/ra_mbe/src/tests.rs b/crates/ra_mbe/src/tests.rs
index f34ab52e19..d7482c63d0 100644
--- a/crates/ra_mbe/src/tests.rs
+++ b/crates/ra_mbe/src/tests.rs
@@ -1,4 +1,4 @@
-use ra_syntax::{ast, AstNode, NodeOrToken, WalkEvent};
+use ra_syntax::{ast, AstNode, NodeOrToken};
 use test_utils::assert_eq_text;
 
 use super::*;
@@ -78,8 +78,18 @@ macro_rules! impl_froms {
 impl_froms!(TokenTree: Leaf, Subtree);
 "#;
 
-    let rules = create_rules(macro_definition);
-    let expansion = expand(&rules, macro_invocation);
+    let source_file = ast::SourceFile::parse(macro_definition).ok().unwrap();
+    let macro_definition =
+        source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap();
+
+    let source_file = ast::SourceFile::parse(macro_invocation).ok().unwrap();
+    let macro_invocation =
+        source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap();
+
+    let (definition_tt, _) = ast_to_token_tree(&macro_definition.token_tree().unwrap()).unwrap();
+    let (invocation_tt, _) = ast_to_token_tree(&macro_invocation.token_tree().unwrap()).unwrap();
+    let rules = crate::MacroRules::parse(&definition_tt).unwrap();
+    let expansion = rules.expand(&invocation_tt).unwrap();
     assert_eq!(
         expansion.to_string(),
         "impl From <Leaf > for TokenTree {fn from (it : Leaf) -> TokenTree {TokenTree ::Leaf (it)}} \
@@ -87,48 +97,140 @@ impl_froms!(TokenTree: Leaf, Subtree);
     )
 }
 
-#[test]
-fn test_expr_order() {
-    let rules = create_rules(
-        r#"
-        macro_rules! foo {
-            ($ i:expr) => {
-                 fn bar() { $ i * 2; }
+pub(crate) fn create_rules(macro_definition: &str) -> MacroRules {
+    let source_file = ast::SourceFile::parse(macro_definition).ok().unwrap();
+    let macro_definition =
+        source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap();
+
+    let (definition_tt, _) = ast_to_token_tree(&macro_definition.token_tree().unwrap()).unwrap();
+    crate::MacroRules::parse(&definition_tt).unwrap()
+}
+
+pub(crate) fn expand(rules: &MacroRules, invocation: &str) -> tt::Subtree {
+    let source_file = ast::SourceFile::parse(invocation).ok().unwrap();
+    let macro_invocation =
+        source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap();
+
+    let (invocation_tt, _) = ast_to_token_tree(&macro_invocation.token_tree().unwrap()).unwrap();
+
+    rules.expand(&invocation_tt).unwrap()
+}
+
+pub(crate) fn expand_to_items(rules: &MacroRules, invocation: &str) -> ast::MacroItems {
+    let expanded = expand(rules, invocation);
+    token_tree_to_items(&expanded).unwrap().tree()
+}
+
+#[allow(unused)]
+pub(crate) fn expand_to_stmts(rules: &MacroRules, invocation: &str) -> ast::MacroStmts {
+    let expanded = expand(rules, invocation);
+    token_tree_to_macro_stmts(&expanded).unwrap().tree()
+}
+
+pub(crate) fn expand_to_expr(rules: &MacroRules, invocation: &str) -> ast::Expr {
+    let expanded = expand(rules, invocation);
+    token_tree_to_expr(&expanded).unwrap().tree()
+}
+
+pub(crate) fn text_to_tokentree(text: &str) -> tt::Subtree {
+    // wrap the given text to a macro call
+    let wrapped = format!("wrap_macro!( {} )", text);
+    let wrapped = ast::SourceFile::parse(&wrapped);
+    let wrapped = wrapped.tree().syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
+    let mut wrapped = ast_to_token_tree(&wrapped).unwrap().0;
+    wrapped.delimiter = tt::Delimiter::None;
+
+    wrapped
+}
+
+pub(crate) enum MacroKind {
+    Items,
+    Stmts,
+}
+
+use ra_syntax::WalkEvent;
+
+pub fn debug_dump_ignore_spaces(node: &ra_syntax::SyntaxNode) -> String {
+    use std::fmt::Write;
+
+    let mut level = 0;
+    let mut buf = String::new();
+    macro_rules! indent {
+        () => {
+            for _ in 0..level {
+                buf.push_str("  ");
+            }
+        };
+    }
+
+    for event in node.preorder_with_tokens() {
+        match event {
+            WalkEvent::Enter(element) => {
+                match element {
+                    NodeOrToken::Node(node) => {
+                        indent!();
+                        writeln!(buf, "{:?}", node.kind()).unwrap();
+                    }
+                    NodeOrToken::Token(token) => match token.kind() {
+                        ra_syntax::SyntaxKind::WHITESPACE => {}
+                        _ => {
+                            indent!();
+                            writeln!(buf, "{:?}", token.kind()).unwrap();
+                        }
+                    },
+                }
+                level += 1;
             }
+            WalkEvent::Leave(_) => level -= 1,
         }
-"#,
-    );
-    let expanded = expand(&rules, "foo! { 1 + 1}");
-    let tree = token_tree_to_items(&expanded).unwrap().tree();
+    }
 
-    let dump = format!("{:#?}", tree.syntax());
-    assert_eq_text!(
-        dump.trim(),
-        r#"MACRO_ITEMS@[0; 15)
-  FN_DEF@[0; 15)
-    FN_KW@[0; 2) "fn"
-    NAME@[2; 5)
-      IDENT@[2; 5) "bar"
-    PARAM_LIST@[5; 7)
-      L_PAREN@[5; 6) "("
-      R_PAREN@[6; 7) ")"
-    BLOCK_EXPR@[7; 15)
-      BLOCK@[7; 15)
-        L_CURLY@[7; 8) "{"
-        EXPR_STMT@[8; 14)
-          BIN_EXPR@[8; 13)
-            BIN_EXPR@[8; 11)
-              LITERAL@[8; 9)
-                INT_NUMBER@[8; 9) "1"
-              PLUS@[9; 10) "+"
-              LITERAL@[10; 11)
-                INT_NUMBER@[10; 11) "1"
-            STAR@[11; 12) "*"
-            LITERAL@[12; 13)
-              INT_NUMBER@[12; 13) "2"
-          SEMI@[13; 14) ";"
-        R_CURLY@[14; 15) "}""#,
+    buf
+}
+
+pub(crate) fn assert_expansion(
+    kind: MacroKind,
+    rules: &MacroRules,
+    invocation: &str,
+    expected: &str,
+) -> tt::Subtree {
+    let expanded = expand(rules, invocation);
+    assert_eq!(expanded.to_string(), expected);
+
+    let expected = expected.replace("$crate", "C_C__C");
+
+    // wrap the given text to a macro call
+    let expected = text_to_tokentree(&expected);
+    let (expanded_tree, expected_tree) = match kind {
+        MacroKind::Items => {
+            let expanded_tree = token_tree_to_items(&expanded).unwrap().tree();
+            let expected_tree = token_tree_to_items(&expected).unwrap().tree();
+
+            (
+                debug_dump_ignore_spaces(expanded_tree.syntax()).trim().to_string(),
+                debug_dump_ignore_spaces(expected_tree.syntax()).trim().to_string(),
+            )
+        }
+
+        MacroKind::Stmts => {
+            let expanded_tree = token_tree_to_macro_stmts(&expanded).unwrap().tree();
+            let expected_tree = token_tree_to_macro_stmts(&expected).unwrap().tree();
+
+            (
+                debug_dump_ignore_spaces(expanded_tree.syntax()).trim().to_string(),
+                debug_dump_ignore_spaces(expected_tree.syntax()).trim().to_string(),
+            )
+        }
+    };
+
+    let expected_tree = expected_tree.replace("C_C__C", "$crate");
+    assert_eq!(
+        expanded_tree, expected_tree,
+        "\nleft:\n{}\nright:\n{}",
+        expanded_tree, expected_tree,
     );
+
+    expanded
 }
 
 #[test]
@@ -602,6 +704,47 @@ fn test_expr() {
     );
 }
 
+#[test]
+fn test_expr_order() {
+    let rules = create_rules(
+        r#"
+        macro_rules! foo {
+            ($ i:expr) => {
+                 fn bar() { $ i * 2; }
+            }
+        }
+"#,
+    );
+    let dump = format!("{:#?}", expand_to_items(&rules, "foo! { 1 + 1  }").syntax());
+    assert_eq_text!(
+        dump.trim(),
+        r#"MACRO_ITEMS@[0; 15)
+  FN_DEF@[0; 15)
+    FN_KW@[0; 2) "fn"
+    NAME@[2; 5)
+      IDENT@[2; 5) "bar"
+    PARAM_LIST@[5; 7)
+      L_PAREN@[5; 6) "("
+      R_PAREN@[6; 7) ")"
+    BLOCK_EXPR@[7; 15)
+      BLOCK@[7; 15)
+        L_CURLY@[7; 8) "{"
+        EXPR_STMT@[8; 14)
+          BIN_EXPR@[8; 13)
+            BIN_EXPR@[8; 11)
+              LITERAL@[8; 9)
+                INT_NUMBER@[8; 9) "1"
+              PLUS@[9; 10) "+"
+              LITERAL@[10; 11)
+                INT_NUMBER@[10; 11) "1"
+            STAR@[11; 12) "*"
+            LITERAL@[12; 13)
+              INT_NUMBER@[12; 13) "2"
+          SEMI@[13; 14) ";"
+        R_CURLY@[14; 15) "}""#,
+    );
+}
+
 #[test]
 fn test_last_expr() {
     let rules = create_rules(
@@ -918,11 +1061,8 @@ fn test_vec() {
         r#"{let mut v = Vec :: new () ; v . push (1u32) ; v . push (2) ; v}"#,
     );
 
-    let expansion = expand(&rules, r#"vec![1u32,2];"#);
-    let tree = token_tree_to_expr(&expansion).unwrap().tree();
-
     assert_eq!(
-        format!("{:#?}", tree.syntax()).trim(),
+        format!("{:#?}", expand_to_expr(&rules, r#"vec![1u32,2];"#).syntax()).trim(),
         r#"BLOCK_EXPR@[0; 45)
   BLOCK@[0; 45)
     L_CURLY@[0; 1) "{"
@@ -1362,117 +1502,3 @@ macro_rules! delegate_impl {
 
     assert_expansion(MacroKind::Items, &rules, r#"delegate_impl ! {[G , & 'a mut G , deref] pub trait Data : GraphBase {@ section type type NodeWeight ;}}"#, "impl <> Data for & \'a mut G where G : Data {}");
 }
-
-pub(crate) fn create_rules(macro_definition: &str) -> MacroRules {
-    let source_file = ast::SourceFile::parse(macro_definition).ok().unwrap();
-    let macro_definition =
-        source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap();
-
-    let (definition_tt, _) = ast_to_token_tree(&macro_definition.token_tree().unwrap()).unwrap();
-    crate::MacroRules::parse(&definition_tt).unwrap()
-}
-
-pub(crate) fn expand(rules: &MacroRules, invocation: &str) -> tt::Subtree {
-    let source_file = ast::SourceFile::parse(invocation).ok().unwrap();
-    let macro_invocation =
-        source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap();
-
-    let (invocation_tt, _) = ast_to_token_tree(&macro_invocation.token_tree().unwrap()).unwrap();
-
-    rules.expand(&invocation_tt).unwrap()
-}
-
-pub(crate) enum MacroKind {
-    Items,
-    Stmts,
-}
-
-pub(crate) fn assert_expansion(
-    kind: MacroKind,
-    rules: &MacroRules,
-    invocation: &str,
-    expected: &str,
-) -> tt::Subtree {
-    let expanded = expand(rules, invocation);
-    assert_eq!(expanded.to_string(), expected);
-
-    let expected = expected.replace("$crate", "C_C__C");
-
-    // wrap the given text to a macro call
-    let expected = {
-        let wrapped = format!("wrap_macro!( {} )", expected);
-        let wrapped = ast::SourceFile::parse(&wrapped);
-        let wrapped = wrapped.tree().syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
-        let mut wrapped = ast_to_token_tree(&wrapped).unwrap().0;
-        wrapped.delimiter = tt::Delimiter::None;
-        wrapped
-    };
-    let (expanded_tree, expected_tree) = match kind {
-        MacroKind::Items => {
-            let expanded_tree = token_tree_to_items(&expanded).unwrap().tree();
-            let expected_tree = token_tree_to_items(&expected).unwrap().tree();
-
-            (
-                debug_dump_ignore_spaces(expanded_tree.syntax()).trim().to_string(),
-                debug_dump_ignore_spaces(expected_tree.syntax()).trim().to_string(),
-            )
-        }
-
-        MacroKind::Stmts => {
-            let expanded_tree = token_tree_to_macro_stmts(&expanded).unwrap().tree();
-            let expected_tree = token_tree_to_macro_stmts(&expected).unwrap().tree();
-
-            (
-                debug_dump_ignore_spaces(expanded_tree.syntax()).trim().to_string(),
-                debug_dump_ignore_spaces(expected_tree.syntax()).trim().to_string(),
-            )
-        }
-    };
-
-    let expected_tree = expected_tree.replace("C_C__C", "$crate");
-    assert_eq!(
-        expanded_tree, expected_tree,
-        "\nleft:\n{}\nright:\n{}",
-        expanded_tree, expected_tree,
-    );
-
-    expanded
-}
-
-pub fn debug_dump_ignore_spaces(node: &ra_syntax::SyntaxNode) -> String {
-    use std::fmt::Write;
-
-    let mut level = 0;
-    let mut buf = String::new();
-    macro_rules! indent {
-        () => {
-            for _ in 0..level {
-                buf.push_str("  ");
-            }
-        };
-    }
-
-    for event in node.preorder_with_tokens() {
-        match event {
-            WalkEvent::Enter(element) => {
-                match element {
-                    NodeOrToken::Node(node) => {
-                        indent!();
-                        writeln!(buf, "{:?}", node.kind()).unwrap();
-                    }
-                    NodeOrToken::Token(token) => match token.kind() {
-                        ra_syntax::SyntaxKind::WHITESPACE => {}
-                        _ => {
-                            indent!();
-                            writeln!(buf, "{:?}", token.kind()).unwrap();
-                        }
-                    },
-                }
-                level += 1;
-            }
-            WalkEvent::Leave(_) => level -= 1,
-        }
-    }
-
-    buf
-}