Repository: rust-analyzer/rust-analyzer
Pull request #3330 (2020-02-26 20:26:47)

cicd: add cargo audit
follow up of #3329 


Patch:
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 8ab47106d0..ff971dc088 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -8,6 +8,16 @@ on:
       - trying
 
 jobs:
+  rust-audit:
+    name: Audit Rust vulnerabilities
+    runs-on: ubuntu-latest
+    steps:
+        - name: Checkout repository
+          uses: actions/checkout@v1
+
+        - run: cargo install cargo-audit
+        - run: cargo audit
+
   rust:
     name: Rust
     runs-on: ${{ matrix.os }}
diff --git a/Cargo.lock b/Cargo.lock
index 118cf137e2..e5400f5ebd 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1067,6 +1067,7 @@ dependencies = [
  "rustc-hash",
  "superslice",
  "test_utils",
+ "unicase",
 ]
 
 [[package]]
@@ -1093,6 +1094,7 @@ dependencies = [
  "rustc-hash",
  "superslice",
  "test_utils",
+ "unicase",
 ]
 
 [[package]]
@@ -1603,6 +1605,15 @@ dependencies = [
  "num_cpus",
 ]
 
+[[package]]
+name = "unicase"
+version = "2.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
+dependencies = [
+ "version_check",
+]
+
 [[package]]
 name = "unicode-bidi"
 version = "0.3.4"
@@ -1645,6 +1656,12 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "version_check"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce"
+
 [[package]]
 name = "walkdir"
 version = "2.3.1"
diff --git a/crates/ra_assists/src/assist_ctx.rs b/crates/ra_assists/src/assist_ctx.rs
index c25d2e3239..5aab5fb8b6 100644
--- a/crates/ra_assists/src/assist_ctx.rs
+++ b/crates/ra_assists/src/assist_ctx.rs
@@ -1,6 +1,6 @@
 //! This module defines `AssistCtx` -- the API surface that is exposed to assists.
-use hir::Semantics;
-use ra_db::FileRange;
+use hir::{InFile, SourceAnalyzer, SourceBinder};
+use ra_db::{FileRange, SourceDatabase};
 use ra_fmt::{leading_indent, reindent};
 use ra_ide_db::RootDatabase;
 use ra_syntax::{
@@ -74,23 +74,29 @@ pub(crate) type AssistHandler = fn(AssistCtx) -> Option<Assist>;
 /// Note, however, that we don't actually use such two-phase logic at the
 /// moment, because the LSP API is pretty awkward in this place, and it's much
 /// easier to just compute the edit eagerly :-)
-#[derive(Clone)]
+#[derive(Debug)]
 pub(crate) struct AssistCtx<'a> {
-    pub(crate) sema: &'a Semantics<'a, RootDatabase>,
     pub(crate) db: &'a RootDatabase,
     pub(crate) frange: FileRange,
     source_file: SourceFile,
     should_compute_edit: bool,
 }
 
+impl Clone for AssistCtx<'_> {
+    fn clone(&self) -> Self {
+        AssistCtx {
+            db: self.db,
+            frange: self.frange,
+            source_file: self.source_file.clone(),
+            should_compute_edit: self.should_compute_edit,
+        }
+    }
+}
+
 impl<'a> AssistCtx<'a> {
-    pub fn new(
-        sema: &'a Semantics<'a, RootDatabase>,
-        frange: FileRange,
-        should_compute_edit: bool,
-    ) -> AssistCtx<'a> {
-        let source_file = sema.parse(frange.file_id);
-        AssistCtx { sema, db: sema.db, frange, source_file, should_compute_edit }
+    pub fn new(db: &RootDatabase, frange: FileRange, should_compute_edit: bool) -> AssistCtx {
+        let parse = db.parse(frange.file_id);
+        AssistCtx { db, frange, source_file: parse.tree(), should_compute_edit }
     }
 
     pub(crate) fn add_assist(
@@ -132,6 +138,18 @@ impl<'a> AssistCtx<'a> {
     pub(crate) fn covering_element(&self) -> SyntaxElement {
         find_covering_element(self.source_file.syntax(), self.frange.range)
     }
+    pub(crate) fn source_binder(&self) -> SourceBinder<'a, RootDatabase> {
+        SourceBinder::new(self.db)
+    }
+    pub(crate) fn source_analyzer(
+        &self,
+        node: &SyntaxNode,
+        offset: Option<TextUnit>,
+    ) -> SourceAnalyzer {
+        let src = InFile::new(self.frange.file_id.into(), node);
+        self.source_binder().analyze(src, offset)
+    }
+
     pub(crate) fn covering_node_for_range(&self, range: TextRange) -> SyntaxElement {
         find_covering_element(self.source_file.syntax(), range)
     }
diff --git a/crates/ra_assists/src/ast_transform.rs b/crates/ra_assists/src/ast_transform.rs
index 7846e97981..c6d15af5fc 100644
--- a/crates/ra_assists/src/ast_transform.rs
+++ b/crates/ra_assists/src/ast_transform.rs
@@ -1,12 +1,15 @@
 //! `AstTransformer`s are functions that replace nodes in an AST and can be easily combined.
 use rustc_hash::FxHashMap;
 
-use hir::{PathResolution, SemanticsScope};
+use hir::{InFile, PathResolution};
 use ra_ide_db::RootDatabase;
 use ra_syntax::ast::{self, AstNode};
 
 pub trait AstTransform<'a> {
-    fn get_substitution(&self, node: &ra_syntax::SyntaxNode) -> Option<ra_syntax::SyntaxNode>;
+    fn get_substitution(
+        &self,
+        node: InFile<&ra_syntax::SyntaxNode>,
+    ) -> Option<ra_syntax::SyntaxNode>;
 
     fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a>;
     fn or<T: AstTransform<'a> + 'a>(self, other: T) -> Box<dyn AstTransform<'a> + 'a>
@@ -20,7 +23,10 @@ pub trait AstTransform<'a> {
 struct NullTransformer;
 
 impl<'a> AstTransform<'a> for NullTransformer {
-    fn get_substitution(&self, _node: &ra_syntax::SyntaxNode) -> Option<ra_syntax::SyntaxNode> {
+    fn get_substitution(
+        &self,
+        _node: InFile<&ra_syntax::SyntaxNode>,
+    ) -> Option<ra_syntax::SyntaxNode> {
         None
     }
     fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a> {
@@ -29,16 +35,14 @@ impl<'a> AstTransform<'a> for NullTransformer {
 }
 
 pub struct SubstituteTypeParams<'a> {
-    source_scope: &'a SemanticsScope<'a, RootDatabase>,
+    db: &'a RootDatabase,
     substs: FxHashMap<hir::TypeParam, ast::TypeRef>,
     previous: Box<dyn AstTransform<'a> + 'a>,
 }
 
 impl<'a> SubstituteTypeParams<'a> {
     pub fn for_trait_impl(
-        source_scope: &'a SemanticsScope<'a, RootDatabase>,
         db: &'a RootDatabase,
-        // FIXME: there's implicit invariant that `trait_` and  `source_scope` match...
         trait_: hir::Trait,
         impl_block: ast::ImplBlock,
     ) -> SubstituteTypeParams<'a> {
@@ -52,7 +56,7 @@ impl<'a> SubstituteTypeParams<'a> {
             .zip(substs.into_iter())
             .collect();
         return SubstituteTypeParams {
-            source_scope,
+            db,
             substs: substs_by_param,
             previous: Box::new(NullTransformer),
         };
@@ -76,15 +80,15 @@ impl<'a> SubstituteTypeParams<'a> {
     }
     fn get_substitution_inner(
         &self,
-        node: &ra_syntax::SyntaxNode,
+        node: InFile<&ra_syntax::SyntaxNode>,
     ) -> Option<ra_syntax::SyntaxNode> {
-        let type_ref = ast::TypeRef::cast(node.clone())?;
+        let type_ref = ast::TypeRef::cast(node.value.clone())?;
         let path = match &type_ref {
             ast::TypeRef::PathType(path_type) => path_type.path()?,
             _ => return None,
         };
-        let path = hir::Path::from_ast(path)?;
-        let resolution = self.source_scope.resolve_hir_path(&path)?;
+        let analyzer = hir::SourceAnalyzer::new(self.db, node, None);
+        let resolution = analyzer.resolve_path(self.db, &path)?;
         match resolution {
             hir::PathResolution::TypeParam(tp) => Some(self.substs.get(&tp)?.syntax().clone()),
             _ => None,
@@ -93,7 +97,10 @@ impl<'a> SubstituteTypeParams<'a> {
 }
 
 impl<'a> AstTransform<'a> for SubstituteTypeParams<'a> {
-    fn get_substitution(&self, node: &ra_syntax::SyntaxNode) -> Option<ra_syntax::SyntaxNode> {
+    fn get_substitution(
+        &self,
+        node: InFile<&ra_syntax::SyntaxNode>,
+    ) -> Option<ra_syntax::SyntaxNode> {
         self.get_substitution_inner(node).or_else(|| self.previous.get_substitution(node))
     }
     fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a> {
@@ -102,34 +109,29 @@ impl<'a> AstTransform<'a> for SubstituteTypeParams<'a> {
 }
 
 pub struct QualifyPaths<'a> {
-    target_scope: &'a SemanticsScope<'a, RootDatabase>,
-    source_scope: &'a SemanticsScope<'a, RootDatabase>,
     db: &'a RootDatabase,
+    from: Option<hir::Module>,
     previous: Box<dyn AstTransform<'a> + 'a>,
 }
 
 impl<'a> QualifyPaths<'a> {
-    pub fn new(
-        target_scope: &'a SemanticsScope<'a, RootDatabase>,
-        source_scope: &'a SemanticsScope<'a, RootDatabase>,
-        db: &'a RootDatabase,
-    ) -> Self {
-        Self { target_scope, source_scope, db, previous: Box::new(NullTransformer) }
+    pub fn new(db: &'a RootDatabase, from: Option<hir::Module>) -> Self {
+        Self { db, from, previous: Box::new(NullTransformer) }
     }
 
     fn get_substitution_inner(
         &self,
-        node: &ra_syntax::SyntaxNode,
+        node: InFile<&ra_syntax::SyntaxNode>,
     ) -> Option<ra_syntax::SyntaxNode> {
         // FIXME handle value ns?
-        let from = self.target_scope.module()?;
-        let p = ast::Path::cast(node.clone())?;
+        let from = self.from?;
+        let p = ast::Path::cast(node.value.clone())?;
         if p.segment().and_then(|s| s.param_list()).is_some() {
             // don't try to qualify `Fn(Foo) -> Bar` paths, they are in prelude anyway
             return None;
         }
-        let hir_path = hir::Path::from_ast(p.clone());
-        let resolution = self.source_scope.resolve_hir_path(&hir_path?)?;
+        let analyzer = hir::SourceAnalyzer::new(self.db, node, None);
+        let resolution = analyzer.resolve_path(self.db, &p)?;
         match resolution {
             PathResolution::Def(def) => {
                 let found_path = from.find_use_path(self.db, def)?;
@@ -138,7 +140,7 @@ impl<'a> QualifyPaths<'a> {
                 let type_args = p
                     .segment()
                     .and_then(|s| s.type_arg_list())
-                    .map(|arg_list| apply(self, arg_list));
+                    .map(|arg_list| apply(self, node.with_value(arg_list)));
                 if let Some(type_args) = type_args {
                     let last_segment = path.segment().unwrap();
                     path = path.with_segment(last_segment.with_type_args(type_args))
@@ -155,11 +157,11 @@ impl<'a> QualifyPaths<'a> {
     }
 }
 
-pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: N) -> N {
-    let syntax = node.syntax();
+pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: InFile<N>) -> N {
+    let syntax = node.value.syntax();
     let result = ra_syntax::algo::replace_descendants(syntax, &|element| match element {
         ra_syntax::SyntaxElement::Node(n) => {
-            let replacement = transformer.get_substitution(&n)?;
+            let replacement = transformer.get_substitution(node.with_value(&n))?;
             Some(replacement.into())
         }
         _ => None,
@@ -168,7 +170,10 @@ pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: N) -> N {
 }
 
 impl<'a> AstTransform<'a> for QualifyPaths<'a> {
-    fn get_substitution(&self, node: &ra_syntax::SyntaxNode) -> Option<ra_syntax::SyntaxNode> {
+    fn get_substitution(
+        &self,
+        node: InFile<&ra_syntax::SyntaxNode>,
+    ) -> Option<ra_syntax::SyntaxNode> {
         self.get_substitution_inner(node).or_else(|| self.previous.get_substitution(node))
     }
     fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a> {
diff --git a/crates/ra_assists/src/handlers/add_explicit_type.rs b/crates/ra_assists/src/handlers/add_explicit_type.rs
index a63ef48b1d..2cb9d2f48e 100644
--- a/crates/ra_assists/src/handlers/add_explicit_type.rs
+++ b/crates/ra_assists/src/handlers/add_explicit_type.rs
@@ -51,13 +51,14 @@ pub(crate) fn add_explicit_type(ctx: AssistCtx) -> Option<Assist> {
         }
     }
     // Infer type
-    let ty = ctx.sema.type_of_expr(&expr)?;
+    let db = ctx.db;
+    let analyzer = ctx.source_analyzer(stmt.syntax(), None);
+    let ty = analyzer.type_of(db, &expr)?;
     // Assist not applicable if the type is unknown
     if ty.contains_unknown() {
         return None;
     }
 
-    let db = ctx.db;
     ctx.add_assist(
         AssistId("add_explicit_type"),
         format!("Insert explicit type '{}'", ty.display(db)),
diff --git a/crates/ra_assists/src/handlers/add_missing_impl_members.rs b/crates/ra_assists/src/handlers/add_missing_impl_members.rs
index 4005014bdf..ab21388c8d 100644
--- a/crates/ra_assists/src/handlers/add_missing_impl_members.rs
+++ b/crates/ra_assists/src/handlers/add_missing_impl_members.rs
@@ -1,4 +1,4 @@
-use hir::HasSource;
+use hir::{HasSource, InFile};
 use ra_syntax::{
     ast::{self, edit, make, AstNode, NameOwner},
     SmolStr,
@@ -104,7 +104,9 @@ fn add_missing_impl_members_inner(
     let impl_node = ctx.find_node_at_offset::<ast::ImplBlock>()?;
     let impl_item_list = impl_node.item_list()?;
 
-    let trait_ = resolve_target_trait(&ctx.sema, &impl_node)?;
+    let analyzer = ctx.source_analyzer(impl_node.syntax(), None);
+
+    let trait_ = resolve_target_trait(ctx.db, &analyzer, &impl_node)?;
 
     let def_name = |item: &ast::ImplItem| -> Option<SmolStr> {
         match item {
@@ -115,7 +117,7 @@ fn add_missing_impl_members_inner(
         .map(|it| it.text().clone())
     };
 
-    let missing_items = get_missing_impl_items(&ctx.sema, &impl_node)
+    let missing_items = get_missing_impl_items(ctx.db, &analyzer, &impl_node)
         .iter()
         .map(|i| match i {
             hir::AssocItem::Function(i) => ast::ImplItem::FnDef(i.source(ctx.db).value),
@@ -136,17 +138,23 @@ fn add_missing_impl_members_inner(
         return None;
     }
 
-    let sema = ctx.sema;
+    let db = ctx.db;
+    let file_id = ctx.frange.file_id;
+    let trait_file_id = trait_.source(db).file_id;
 
     ctx.add_assist(AssistId(assist_id), label, |edit| {
         let n_existing_items = impl_item_list.impl_items().count();
-        let source_scope = sema.scope_for_def(trait_);
-        let target_scope = sema.scope(impl_item_list.syntax());
-        let ast_transform = QualifyPaths::new(&target_scope, &source_scope, sema.db)
-            .or(SubstituteTypeParams::for_trait_impl(&source_scope, sema.db, trait_, impl_node));
+        let module = hir::SourceAnalyzer::new(
+            db,
+            hir::InFile::new(file_id.into(), impl_node.syntax()),
+            None,
+        )
+        .module();
+        let ast_transform = QualifyPaths::new(db, module)
+            .or(SubstituteTypeParams::for_trait_impl(db, trait_, impl_node));
         let items = missing_items
             .into_iter()
-            .map(|it| ast_transform::apply(&*ast_transform, it))
+            .map(|it| ast_transform::apply(&*ast_transform, InFile::new(trait_file_id, it)))
             .map(|it| match it {
                 ast::ImplItem::FnDef(def) => ast::ImplItem::FnDef(add_body(def)),
                 _ => it,
@@ -173,9 +181,8 @@ fn add_body(fn_def: ast::FnDef) -> ast::FnDef {
 
 #[cfg(test)]
 mod tests {
-    use crate::helpers::{check_assist, check_assist_not_applicable};
-
     use super::*;
+    use crate::helpers::{check_assist, check_assist_not_applicable};
 
     #[test]
     fn test_add_missing_impl_members() {
diff --git a/crates/ra_assists/src/handlers/add_new.rs b/crates/ra_assists/src/handlers/add_new.rs
index 166e907fb4..dd070e8ec0 100644
--- a/crates/ra_assists/src/handlers/add_new.rs
+++ b/crates/ra_assists/src/handlers/add_new.rs
@@ -1,5 +1,5 @@
 use format_buf::format;
-use hir::Adt;
+use hir::{Adt, InFile};
 use join_to_string::join;
 use ra_syntax::{
     ast::{
@@ -133,11 +133,16 @@ fn find_struct_impl(ctx: &AssistCtx, strukt: &ast::StructDef) -> Option<Option<a
     let module = strukt.syntax().ancestors().find(|node| {
         ast::Module::can_cast(node.kind()) || ast::SourceFile::can_cast(node.kind())
     })?;
+    let mut sb = ctx.source_binder();
 
-    let struct_def = ctx.sema.to_def(strukt)?;
+    let struct_def = {
+        let src = InFile { file_id: ctx.frange.file_id.into(), value: strukt.clone() };
+        sb.to_def(src)?
+    };
 
     let block = module.descendants().filter_map(ast::ImplBlock::cast).find_map(|impl_blk| {
-        let blk = ctx.sema.to_def(&impl_blk)?;
+        let src = InFile { file_id: ctx.frange.file_id.into(), value: impl_blk.clone() };
+        let blk = sb.to_def(src)?;
 
         // FIXME: handle e.g. `struct S<T>; impl<U> S<U> {}`
         // (we currently use the wrong type parameter)
diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs
index edf0cf6d0e..c4aea2a067 100644
--- a/crates/ra_assists/src/handlers/auto_import.rs
+++ b/crates/ra_assists/src/handlers/auto_import.rs
@@ -3,8 +3,8 @@ use crate::{
     insert_use_statement, AssistId,
 };
 use hir::{
-    AsAssocItem, AssocItemContainer, ModPath, Module, ModuleDef, PathResolution, Semantics, Trait,
-    Type,
+    db::HirDatabase, AsAssocItem, AssocItemContainer, ModPath, Module, ModuleDef, PathResolution,
+    SourceAnalyzer, Trait, Type,
 };
 use ra_ide_db::{imports_locator::ImportsLocator, RootDatabase};
 use ra_prof::profile;
@@ -78,9 +78,14 @@ impl AutoImportAssets {
 
     fn for_method_call(method_call: ast::MethodCallExpr, ctx: &AssistCtx) -> Option<Self> {
         let syntax_under_caret = method_call.syntax().to_owned();
-        let module_with_name_to_import = ctx.sema.scope(&syntax_under_caret).module()?;
+        let source_analyzer = ctx.source_analyzer(&syntax_under_caret, None);
+        let module_with_name_to_import = source_analyzer.module()?;
         Some(Self {
-            import_candidate: ImportCandidate::for_method_call(&ctx.sema, &method_call)?,
+            import_candidate: ImportCandidate::for_method_call(
+                &method_call,
+                &source_analyzer,
+                ctx.db,
+            )?,
             module_with_name_to_import,
             syntax_under_caret,
         })
@@ -92,9 +97,14 @@ impl AutoImportAssets {
             return None;
         }
 
-        let module_with_name_to_import = ctx.sema.scope(&syntax_under_caret).module()?;
+        let source_analyzer = ctx.source_analyzer(&syntax_under_caret, None);
+        let module_with_name_to_import = source_analyzer.module()?;
         Some(Self {
-            import_candidate: ImportCandidate::for_regular_path(&ctx.sema, &path_under_caret)?,
+            import_candidate: ImportCandidate::for_regular_path(
+                &path_under_caret,
+                &source_analyzer,
+                ctx.db,
+            )?,
             module_with_name_to_import,
             syntax_under_caret,
         })
@@ -219,23 +229,25 @@ enum ImportCandidate {
 
 impl ImportCandidate {
     fn for_method_call(
-        sema: &Semantics<RootDatabase>,
         method_call: &ast::MethodCallExpr,
+        source_analyzer: &SourceAnalyzer,
+        db: &impl HirDatabase,
     ) -> Option<Self> {
-        if sema.resolve_method_call(method_call).is_some() {
+        if source_analyzer.resolve_method_call(method_call).is_some() {
             return None;
         }
         Some(Self::TraitMethod(
-            sema.type_of_expr(&method_call.expr()?)?,
+            source_analyzer.type_of(db, &method_call.expr()?)?,
             method_call.name_ref()?.syntax().to_string(),
         ))
     }
 
     fn for_regular_path(
-        sema: &Semantics<RootDatabase>,
         path_under_caret: &ast::Path,
+        source_analyzer: &SourceAnalyzer,
+        db: &impl HirDatabase,
     ) -> Option<Self> {
-        if sema.resolve_path(path_under_caret).is_some() {
+        if source_analyzer.resolve_path(db, path_under_caret).is_some() {
             return None;
         }
 
@@ -244,15 +256,17 @@ impl ImportCandidate {
             let qualifier_start = qualifier.syntax().descendants().find_map(ast::NameRef::cast)?;
             let qualifier_start_path =
                 qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?;
-            if let Some(qualifier_start_resolution) = sema.resolve_path(&qualifier_start_path) {
+            if let Some(qualifier_start_resolution) =
+                source_analyzer.resolve_path(db, &qualifier_start_path)
+            {
                 let qualifier_resolution = if qualifier_start_path == qualifier {
                     qualifier_start_resolution
                 } else {
-                    sema.resolve_path(&qualifier)?
+                    source_analyzer.resolve_path(db, &qualifier)?
                 };
                 if let PathResolution::Def(ModuleDef::Adt(assoc_item_path)) = qualifier_resolution {
                     Some(ImportCandidate::TraitAssocItem(
-                        assoc_item_path.ty(sema.db),
+                        assoc_item_path.ty(db),
                         segment.syntax().to_string(),
                     ))
                 } else {
diff --git a/crates/ra_assists/src/handlers/fill_match_arms.rs b/crates/ra_assists/src/handlers/fill_match_arms.rs
index e5d8c639d1..ae2437ed32 100644
--- a/crates/ra_assists/src/handlers/fill_match_arms.rs
+++ b/crates/ra_assists/src/handlers/fill_match_arms.rs
@@ -2,11 +2,10 @@
 
 use std::iter;
 
-use hir::{db::HirDatabase, Adt, HasSource, Semantics};
+use hir::{db::HirDatabase, Adt, HasSource};
 use ra_syntax::ast::{self, edit::IndentLevel, make, AstNode, NameOwner};
 
 use crate::{Assist, AssistCtx, AssistId};
-use ra_ide_db::RootDatabase;
 
 // Assist: fill_match_arms
 //
@@ -47,9 +46,10 @@ pub(crate) fn fill_match_arms(ctx: AssistCtx) -> Option<Assist> {
     };
 
     let expr = match_expr.expr()?;
-    let enum_def = resolve_enum_def(&ctx.sema, &expr)?;
-    let module = ctx.sema.scope(expr.syntax()).module()?;
-
+    let (enum_def, module) = {
+        let analyzer = ctx.source_analyzer(expr.syntax(), None);
+        (resolve_enum_def(ctx.db, &analyzer, &expr)?, analyzer.module()?)
+    };
     let variants = enum_def.variants(ctx.db);
     if variants.is_empty() {
         return None;
@@ -81,11 +81,18 @@ fn is_trivial(arm: &ast::MatchArm) -> bool {
     }
 }
 
-fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<hir::Enum> {
-    sema.type_of_expr(&expr)?.autoderef(sema.db).find_map(|ty| match ty.as_adt() {
+fn resolve_enum_def(
+    db: &impl HirDatabase,
+    analyzer: &hir::SourceAnalyzer,
+    expr: &ast::Expr,
+) -> Option<hir::Enum> {
+    let expr_ty = analyzer.type_of(db, &expr)?;
+
+    let result = expr_ty.autoderef(db).find_map(|ty| match ty.as_adt() {
         Some(Adt::Enum(e)) => Some(e),
         _ => None,
-    })
+    });
+    result
 }
 
 fn build_pat(
diff --git a/crates/ra_assists/src/handlers/inline_local_variable.rs b/crates/ra_assists/src/handlers/inline_local_variable.rs
index 53a72309b1..91b588243e 100644
--- a/crates/ra_assists/src/handlers/inline_local_variable.rs
+++ b/crates/ra_assists/src/handlers/inline_local_variable.rs
@@ -44,7 +44,8 @@ pub(crate) fn inline_local_variable(ctx: AssistCtx) -> Option<Assist> {
     } else {
         let_stmt.syntax().text_range()
     };
-    let refs = ctx.sema.find_all_refs(&bind_pat);
+    let analyzer = ctx.source_analyzer(bind_pat.syntax(), None);
+    let refs = analyzer.find_all_refs(&bind_pat);
     if refs.is_empty() {
         return None;
     };
diff --git a/crates/ra_assists/src/handlers/introduce_variable.rs b/crates/ra_assists/src/handlers/introduce_variable.rs
index b453c51fbc..7312ce6871 100644
--- a/crates/ra_assists/src/handlers/introduce_variable.rs
+++ b/crates/ra_assists/src/handlers/introduce_variable.rs
@@ -136,13 +136,15 @@ fn anchor_stmt(expr: ast::Expr) -> Option<(SyntaxNode, bool)> {
 mod tests {
     use test_utils::covers;
 
-    use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_target};
+    use crate::helpers::{
+        check_assist_range, check_assist_range_not_applicable, check_assist_range_target,
+    };
 
     use super::*;
 
     #[test]
     fn test_introduce_var_simple() {
-        check_assist(
+        check_assist_range(
             introduce_variable,
             "
 fn foo() {
@@ -159,13 +161,16 @@ fn foo() {
     #[test]
     fn introduce_var_in_comment_is_not_applicable() {
         covers!(introduce_var_in_comment_is_not_applicable);
-        check_assist_not_applicable(introduce_variable, "fn main() { 1 + /* <|>comment<|> */ 1; }");
+        check_assist_range_not_applicable(
+            introduce_variable,
+            "fn main() { 1 + /* <|>comment<|> */ 1; }",
+        );
     }
 
     #[test]
     fn test_introduce_var_expr_stmt() {
         covers!(test_introduce_var_expr_stmt);
-        check_assist(
+        check_assist_range(
             introduce_variable,
             "
 fn foo() {
@@ -176,7 +181,7 @@ fn foo() {
     let <|>var_name = 1 + 1;
 }",
         );
-        check_assist(
+        check_assist_range(
             introduce_variable,
             "
 fn foo() {
@@ -193,7 +198,7 @@ fn foo() {
 
     #[test]
     fn test_introduce_var_part_of_expr_stmt() {
-        check_assist(
+        check_assist_range(
             introduce_variable,
             "
 fn foo() {
@@ -210,7 +215,7 @@ fn foo() {
     #[test]
     fn test_introduce_var_last_expr() {
         covers!(test_introduce_var_last_expr);
-        check_assist(
+        check_assist_range(
             introduce_variable,
             "
 fn foo() {
@@ -222,7 +227,7 @@ fn foo() {
     bar(var_name)
 }",
         );
-        check_assist(
+        check_assist_range(
             introduce_variable,
             "
 fn foo() {
@@ -238,7 +243,7 @@ fn foo() {
 
     #[test]
     fn test_introduce_var_in_match_arm_no_block() {
-        check_assist(
+        check_assist_range(
             introduce_variable,
             "
 fn main() {
@@ -263,7 +268,7 @@ fn main() {
 
     #[test]
     fn test_introduce_var_in_match_arm_with_block() {
-        check_assist(
+        check_assist_range(
             introduce_variable,
             "
 fn main() {
@@ -295,7 +300,7 @@ fn main() {
 
     #[test]
     fn test_introduce_var_in_closure_no_block() {
-        check_assist(
+        check_assist_range(
             introduce_variable,
             "
 fn main() {
@@ -312,7 +317,7 @@ fn main() {
 
     #[test]
     fn test_introduce_var_in_closure_with_block() {
-        check_assist(
+        check_assist_range(
             introduce_variable,
             "
 fn main() {
@@ -329,7 +334,7 @@ fn main() {
 
     #[test]
     fn test_introduce_var_path_simple() {
-        check_assist(
+        check_assist_range(
             introduce_variable,
             "
 fn main() {
@@ -347,7 +352,7 @@ fn main() {
 
     #[test]
     fn test_introduce_var_path_method() {
-        check_assist(
+        check_assist_range(
             introduce_variable,
             "
 fn main() {
@@ -365,7 +370,7 @@ fn main() {
 
     #[test]
     fn test_introduce_var_return() {
-        check_assist(
+        check_assist_range(
             introduce_variable,
             "
 fn foo() -> u32 {
@@ -383,7 +388,7 @@ fn foo() -> u32 {
 
     #[test]
     fn test_introduce_var_does_not_add_extra_whitespace() {
-        check_assist(
+        check_assist_range(
             introduce_variable,
             "
 fn foo() -> u32 {
@@ -402,7 +407,7 @@ fn foo() -> u32 {
 ",
         );
 
-        check_assist(
+        check_assist_range(
             introduce_variable,
             "
 fn foo() -> u32 {
@@ -419,7 +424,7 @@ fn foo() -> u32 {
 ",
         );
 
-        check_assist(
+        check_assist_range(
             introduce_variable,
             "
 fn foo() -> u32 {
@@ -447,7 +452,7 @@ fn foo() -> u32 {
 
     #[test]
     fn test_introduce_var_break() {
-        check_assist(
+        check_assist_range(
             introduce_variable,
             "
 fn main() {
@@ -469,7 +474,7 @@ fn main() {
 
     #[test]
     fn test_introduce_var_for_cast() {
-        check_assist(
+        check_assist_range(
             introduce_variable,
             "
 fn main() {
@@ -487,20 +492,27 @@ fn main() {
 
     #[test]
     fn test_introduce_var_for_return_not_applicable() {
-        check_assist_not_applicable(introduce_variable, "fn foo() { <|>return<|>; } ");
+        check_assist_range_not_applicable(introduce_variable, "fn foo() { <|>return<|>; } ");
     }
 
     #[test]
     fn test_introduce_var_for_break_not_applicable() {
-        check_assist_not_applicable(introduce_variable, "fn main() { loop { <|>break<|>; }; }");
+        check_assist_range_not_applicable(
+            introduce_variable,
+            "fn main() { loop { <|>break<|>; }; }",
+        );
     }
 
     // FIXME: This is not quite correct, but good enough(tm) for the sorting heuristic
     #[test]
     fn introduce_var_target() {
-        check_assist_target(introduce_variable, "fn foo() -> u32 { <|>return 2 + 2<|>; }", "2 + 2");
+        check_assist_range_target(
+            introduce_variable,
+            "fn foo() -> u32 { <|>return 2 + 2<|>; }",
+            "2 + 2",
+        );
 
-        check_assist_target(
+        check_assist_range_target(
             introduce_variable,
             "
 fn main() {
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index c28a9b92b8..d7998b0d1a 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -19,7 +19,6 @@ use ra_text_edit::TextEdit;
 
 pub(crate) use crate::assist_ctx::{Assist, AssistCtx, AssistHandler};
 pub use crate::handlers::replace_qualified_name_with_use::insert_use_statement;
-use hir::Semantics;
 
 /// Unique identifier of the assist, should not be shown to the user
 /// directly.
@@ -64,8 +63,7 @@ pub struct ResolvedAssist {
 /// Assists are returned in the "unresolved" state, that is only labels are
 /// returned, without actual edits.
 pub fn unresolved_assists(db: &RootDatabase, range: FileRange) -> Vec<AssistLabel> {
-    let sema = Semantics::new(db);
-    let ctx = AssistCtx::new(&sema, range, false);
+    let ctx = AssistCtx::new(db, range, false);
     handlers::all()
         .iter()
         .filter_map(|f| f(ctx.clone()))
@@ -79,8 +77,7 @@ pub fn unresolved_assists(db: &RootDatabase, range: FileRange) -> Vec<AssistLabe
 /// Assists are returned in the "resolved" state, that is with edit fully
 /// computed.
 pub fn resolved_assists(db: &RootDatabase, range: FileRange) -> Vec<ResolvedAssist> {
-    let sema = Semantics::new(db);
-    let ctx = AssistCtx::new(&sema, range, true);
+    let ctx = AssistCtx::new(db, range, true);
     let mut a = handlers::all()
         .iter()
         .filter_map(|f| f(ctx.clone()))
@@ -165,10 +162,9 @@ mod helpers {
     use ra_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt};
     use ra_ide_db::{symbol_index::SymbolsDatabase, RootDatabase};
     use ra_syntax::TextRange;
-    use test_utils::{add_cursor, assert_eq_text, extract_range_or_offset, RangeOrOffset};
+    use test_utils::{add_cursor, assert_eq_text, extract_offset, extract_range};
 
     use crate::{AssistCtx, AssistHandler};
-    use hir::Semantics;
 
     pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) {
         let (mut db, file_id) = RootDatabase::with_single_file(text);
@@ -180,66 +176,81 @@ mod helpers {
     }
 
     pub(crate) fn check_assist(assist: AssistHandler, before: &str, after: &str) {
-        check(assist, before, ExpectedResult::After(after));
+        let (before_cursor_pos, before) = extract_offset(before);
+        let (db, file_id) = with_single_file(&before);
+        let frange =
+            FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
+        let assist =
+            assist(AssistCtx::new(&db, frange, true)).expect("code action is not applicable");
+        let action = assist.0[0].action.clone().unwrap();
+
+        let actual = action.edit.apply(&before);
+        let actual_cursor_pos = match action.cursor_position {
+            None => action
+                .edit
+                .apply_to_offset(before_cursor_pos)
+                .expect("cursor position is affected by the edit"),
+            Some(off) => off,
+        };
+        let actual = add_cursor(&actual, actual_cursor_pos);
+        assert_eq_text!(after, &actual);
     }
 
-    // FIXME: instead of having a separate function here, maybe use
-    // `extract_ranges` and mark the target as `<target> </target>` in the
-    // fixuture?
-    pub(crate) fn check_assist_target(assist: AssistHandler, before: &str, target: &str) {
-        check(assist, before, ExpectedResult::Target(target));
+    pub(crate) fn check_assist_range(assist: AssistHandler, before: &str, after: &str) {
+        let (range, before) = extract_range(before);
+        let (db, file_id) = with_single_file(&before);
+        let frange = FileRange { file_id, range };
+        let assist =
+            assist(AssistCtx::new(&db, frange, true)).expect("code action is not applicable");
+        let action = assist.0[0].action.clone().unwrap();
+
+        let mut actual = action.edit.apply(&before);
+        if let Some(pos) = action.cursor_position {
+            actual = add_cursor(&actual, pos);
+        }
+        assert_eq_text!(after, &actual);
     }
 
-    pub(crate) fn check_assist_not_applicable(assist: AssistHandler, before: &str) {
-        check(assist, before, ExpectedResult::NotApplicable);
+    pub(crate) fn check_assist_target(assist: AssistHandler, before: &str, target: &str) {
+        let (before_cursor_pos, before) = extract_offset(before);
+        let (db, file_id) = with_single_file(&before);
+        let frange =
+            FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
+        let assist =
+            assist(AssistCtx::new(&db, frange, true)).expect("code action is not applicable");
+        let action = assist.0[0].action.clone().unwrap();
+
+        let range = action.target.expect("expected target on action");
+        assert_eq_text!(&before[range.start().to_usize()..range.end().to_usize()], target);
     }
 
-    enum ExpectedResult<'a> {
-        NotApplicable,
-        After(&'a str),
-        Target(&'a str),
+    pub(crate) fn check_assist_range_target(assist: AssistHandler, before: &str, target: &str) {
+        let (range, before) = extract_range(before);
+        let (db, file_id) = with_single_file(&before);
+        let frange = FileRange { file_id, range };
+        let assist =
+            assist(AssistCtx::new(&db, frange, true)).expect("code action is not applicable");
+        let action = assist.0[0].action.clone().unwrap();
+
+        let range = action.target.expect("expected target on action");
+        assert_eq_text!(&before[range.start().to_usize()..range.end().to_usize()], target);
     }
 
-    fn check(assist: AssistHandler, before: &str, expected: ExpectedResult) {
-        let (range_or_offset, before) = extract_range_or_offset(before);
-        let range: TextRange = range_or_offset.into();
+    pub(crate) fn check_assist_not_applicable(assist: AssistHandler, before: &str) {
+        let (before_cursor_pos, before) = extract_offset(before);
+        let (db, file_id) = with_single_file(&before);
+        let frange =
+            FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
+        let assist = assist(AssistCtx::new(&db, frange, true));
+        assert!(assist.is_none());
+    }
 
+    pub(crate) fn check_assist_range_not_applicable(assist: AssistHandler, before: &str) {
+        let (range, before) = extract_range(before);
         let (db, file_id) = with_single_file(&before);
         let frange = FileRange { file_id, range };
-        let sema = Semantics::new(&db);
-        let assist_ctx = AssistCtx::new(&sema, frange, true);
-
-        match (assist(assist_ctx), expected) {
-            (Some(assist), ExpectedResult::After(after)) => {
-                let action = assist.0[0].action.clone().unwrap();
-
-                let mut actual = action.edit.apply(&before);
-                match action.cursor_position {
-                    None => {
-                        if let RangeOrOffset::Offset(before_cursor_pos) = range_or_offset {
-                            let off = action
-                                .edit
-                                .apply_to_offset(before_cursor_pos)
-                                .expect("cursor position is affected by the edit");
-                            actual = add_cursor(&actual, off)
-                        }
-                    }
-                    Some(off) => actual = add_cursor(&actual, off),
-                };
-
-                assert_eq_text!(after, &actual);
-            }
-            (Some(assist), ExpectedResult::Target(target)) => {
-                let action = assist.0[0].action.clone().unwrap();
-                let range = action.target.expect("expected target on action");
-                assert_eq_text!(&before[range.start().to_usize()..range.end().to_usize()], target);
-            }
-            (Some(_), ExpectedResult::NotApplicable) => panic!("assist should not be applicable!"),
-            (None, ExpectedResult::After(_)) | (None, ExpectedResult::Target(_)) => {
-                panic!("code action is not applicable")
-            }
-            (None, ExpectedResult::NotApplicable) => (),
-        };
+        let assist = assist(AssistCtx::new(&db, frange, true));
+        assert!(assist.is_none());
     }
 }
 
diff --git a/crates/ra_assists/src/utils.rs b/crates/ra_assists/src/utils.rs
index 92d3ed4714..6ff44c95cb 100644
--- a/crates/ra_assists/src/utils.rs
+++ b/crates/ra_assists/src/utils.rs
@@ -1,15 +1,16 @@
 //! Assorted functions shared by several assists.
 
-use hir::Semantics;
-use ra_ide_db::RootDatabase;
 use ra_syntax::{
     ast::{self, make, NameOwner},
     AstNode, T,
 };
+
+use hir::db::HirDatabase;
 use rustc_hash::FxHashSet;
 
 pub fn get_missing_impl_items(
-    sema: &Semantics<RootDatabase>,
+    db: &impl HirDatabase,
+    analyzer: &hir::SourceAnalyzer,
     impl_block: &ast::ImplBlock,
 ) -> Vec<hir::AssocItem> {
     // Names must be unique between constants and functions. However, type aliases
@@ -41,17 +42,15 @@ pub fn get_missing_impl_items(
         }
     }
 
-    resolve_target_trait(sema, impl_block).map_or(vec![], |target_trait| {
+    resolve_target_trait(db, analyzer, impl_block).map_or(vec![], |target_trait| {
         target_trait
-            .items(sema.db)
+            .items(db)
             .iter()
             .filter(|i| match i {
-                hir::AssocItem::Function(f) => {
-                    !impl_fns_consts.contains(&f.name(sema.db).to_string())
-                }
-                hir::AssocItem::TypeAlias(t) => !impl_type.contains(&t.name(sema.db).to_string()),
+                hir::AssocItem::Function(f) => !impl_fns_consts.contains(&f.name(db).to_string()),
+                hir::AssocItem::TypeAlias(t) => !impl_type.contains(&t.name(db).to_string()),
                 hir::AssocItem::Const(c) => c
-                    .name(sema.db)
+                    .name(db)
                     .map(|n| !impl_fns_consts.contains(&n.to_string()))
                     .unwrap_or_default(),
             })
@@ -61,7 +60,8 @@ pub fn get_missing_impl_items(
 }
 
 pub(crate) fn resolve_target_trait(
-    sema: &Semantics<RootDatabase>,
+    db: &impl HirDatabase,
+    analyzer: &hir::SourceAnalyzer,
     impl_block: &ast::ImplBlock,
 ) -> Option<hir::Trait> {
     let ast_path = impl_block
@@ -70,7 +70,7 @@ pub(crate) fn resolve_target_trait(
         .and_then(ast::PathType::cast)?
         .path()?;
 
-    match sema.resolve_path(&ast_path) {
+    match analyzer.resolve_path(db, &ast_path) {
         Some(hir::PathResolution::Def(hir::ModuleDef::Trait(def))) => Some(def),
         _ => None,
     }
diff --git a/crates/ra_hir/src/from_id.rs b/crates/ra_hir/src/from_id.rs
index 3aa7c48704..c16c17072b 100644
--- a/crates/ra_hir/src/from_id.rs
+++ b/crates/ra_hir/src/from_id.rs
@@ -40,7 +40,6 @@ from_id![
     (hir_def::ConstId, crate::Const),
     (hir_def::FunctionId, crate::Function),
     (hir_def::ImplId, crate::ImplBlock),
-    (hir_def::TypeParamId, crate::TypeParam),
     (hir_expand::MacroDefId, crate::MacroDef)
 ];
 
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs
index 4a85e7e36d..7a9745ebeb 100644
--- a/crates/ra_hir/src/lib.rs
+++ b/crates/ra_hir/src/lib.rs
@@ -26,7 +26,6 @@ macro_rules! impl_froms {
     }
 }
 
-mod semantics;
 pub mod db;
 pub mod source_analyzer;
 pub mod source_binder;
@@ -46,8 +45,8 @@ pub use crate::{
         StructField, Trait, Type, TypeAlias, TypeParam, Union, VariantDef,
     },
     has_source::HasSource,
-    semantics::{original_range, Semantics, SemanticsScope},
-    source_analyzer::PathResolution,
+    source_analyzer::{PathResolution, ScopeEntryWithSyntax, SourceAnalyzer},
+    source_binder::SourceBinder,
 };
 
 pub use hir_def::{
diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs
deleted file mode 100644
index 5b0b94e34d..0000000000
--- a/crates/ra_hir/src/semantics.rs
+++ /dev/null
@@ -1,419 +0,0 @@
-//! See `Semantics`.
-
-use std::{cell::RefCell, fmt, iter::successors};
-
-use hir_def::{
-    resolver::{self, HasResolver, Resolver},
-    DefWithBodyId, TraitId,
-};
-use ra_db::{FileId, FileRange};
-use ra_syntax::{
-    algo::{find_covering_element, skip_trivia_token},
-    ast, match_ast, AstNode, Direction, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxToken,
-    TextRange, TextUnit,
-};
-use rustc_hash::{FxHashMap, FxHashSet};
-
-use crate::{
-    db::HirDatabase,
-    source_analyzer::{resolve_hir_path, ReferenceDescriptor, SourceAnalyzer},
-    source_binder::{ChildContainer, SourceBinder},
-    Function, HirFileId, InFile, Local, MacroDef, Module, Name, Origin, Path, PathResolution,
-    ScopeDef, StructField, Trait, Type, TypeParam, VariantDef,
-};
-use ra_prof::profile;
-
-/// Primary API to get semantic information, like types, from syntax trees.
-pub struct Semantics<'db, DB> {
-    pub db: &'db DB,
-    pub(crate) sb: RefCell<SourceBinder>,
-    cache: RefCell<FxHashMap<SyntaxNode, HirFileId>>,
-}
-
-impl<DB> fmt::Debug for Semantics<'_, DB> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "Semantics {{ ... }}")
-    }
-}
-
-impl<'db, DB: HirDatabase> Semantics<'db, DB> {
-    pub fn new(db: &DB) -> Semantics<DB> {
-        let sb = RefCell::new(SourceBinder::new());
-        Semantics { db, sb, cache: RefCell::default() }
-    }
-
-    pub fn parse(&self, file_id: FileId) -> ast::SourceFile {
-        let tree = self.db.parse(file_id).tree();
-        self.cache(tree.syntax().clone(), file_id.into());
-        tree
-    }
-
-    pub fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> {
-        let macro_call = self.find_file(macro_call.syntax().clone()).with_value(macro_call);
-        let sa = self.analyze2(macro_call.map(|it| it.syntax()), None);
-        let file_id = sa.expand(self.db, macro_call)?;
-        let node = self.db.parse_or_expand(file_id)?;
-        self.cache(node.clone(), file_id);
-        Some(node)
-    }
-
-    pub fn descend_into_macros(&self, token: SyntaxToken) -> SyntaxToken {
-        let parent = token.parent();
-        let parent = self.find_file(parent);
-        let sa = self.analyze2(parent.as_ref(), None);
-
-        let token = successors(Some(parent.with_value(token)), |token| {
-            let macro_call = token.value.ancestors().find_map(ast::MacroCall::cast)?;
-            let tt = macro_call.token_tree()?;
-            if !token.value.text_range().is_subrange(&tt.syntax().text_range()) {
-                return None;
-            }
-            let file_id = sa.expand(self.db, token.with_value(&macro_call))?;
-            let token = file_id.expansion_info(self.db)?.map_token_down(token.as_ref())?;
-
-            self.cache(find_root(&token.value.parent()), token.file_id);
-
-            Some(token)
-        })
-        .last()
-        .unwrap();
-
-        token.value
-    }
-
-    pub fn original_range(&self, node: &SyntaxNode) -> FileRange {
-        let node = self.find_file(node.clone());
-        original_range(self.db, node.as_ref())
-    }
-
-    pub fn ancestors_with_macros(&self, node: SyntaxNode) -> impl Iterator<Item = SyntaxNode> + '_ {
-        let node = self.find_file(node);
-        node.ancestors_with_macros(self.db).map(|it| it.value)
-    }
-
-    pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> {
-        self.analyze(expr.syntax()).type_of(self.db, &expr)
-    }
-
-    pub fn type_of_pat(&self, pat: &ast::Pat) -> Option<Type> {
-        self.analyze(pat.syntax()).type_of_pat(self.db, &pat)
-    }
-
-    pub fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<Function> {
-        self.analyze(call.syntax()).resolve_method_call(call)
-    }
-
-    pub fn resolve_field(&self, field: &ast::FieldExpr) -> Option<StructField> {
-        self.analyze(field.syntax()).resolve_field(field)
-    }
-
-    pub fn resolve_record_field(&self, field: &ast::RecordField) -> Option<StructField> {
-        self.analyze(field.syntax()).resolve_record_field(field)
-    }
-
-    pub fn resolve_record_literal(&self, record_lit: &ast::RecordLit) -> Option<VariantDef> {
-        self.analyze(record_lit.syntax()).resolve_record_literal(record_lit)
-    }
-
-    pub fn resolve_record_pattern(&self, record_pat: &ast::RecordPat) -> Option<VariantDef> {
-        self.analyze(record_pat.syntax()).resolve_record_pattern(record_pat)
-    }
-
-    pub fn resolve_macro_call(&self, macro_call: &ast::MacroCall) -> Option<MacroDef> {
-        let sa = self.analyze(macro_call.syntax());
-        let macro_call = self.find_file(macro_call.syntax().clone()).with_value(macro_call);
-        sa.resolve_macro_call(self.db, macro_call)
-    }
-
-    pub fn resolve_path(&self, path: &ast::Path) -> Option<PathResolution> {
-        self.analyze(path.syntax()).resolve_path(self.db, path)
-    }
-
-    // FIXME: use this instead?
-    // pub fn resolve_name_ref(&self, name_ref: &ast::NameRef) -> Option<???>;
-
-    pub fn to_def<T: ToDef + Clone>(&self, src: &T) -> Option<T::Def> {
-        T::to_def(self, src)
-    }
-
-    pub fn to_module_def(&self, file: FileId) -> Option<Module> {
-        let mut sb = self.sb.borrow_mut();
-        sb.to_module_def(self.db, file)
-    }
-
-    pub fn scope(&self, node: &SyntaxNode) -> SemanticsScope<'db, DB> {
-        let node = self.find_file(node.clone());
-        let resolver = self.analyze2(node.as_ref(), None).resolver;
-        SemanticsScope { db: self.db, resolver }
-    }
-
-    pub fn scope_at_offset(&self, node: &SyntaxNode, offset: TextUnit) -> SemanticsScope<'db, DB> {
-        let node = self.find_file(node.clone());
-        let resolver = self.analyze2(node.as_ref(), Some(offset)).resolver;
-        SemanticsScope { db: self.db, resolver }
-    }
-
-    pub fn scope_for_def(&self, def: Trait) -> SemanticsScope<'db, DB> {
-        let resolver = def.id.resolver(self.db);
-        SemanticsScope { db: self.db, resolver }
-    }
-
-    // FIXME: we only use this in `inline_local_variable` assist, ideally, we
-    // should switch to general reference search infra there.
-    pub fn find_all_refs(&self, pat: &ast::BindPat) -> Vec<ReferenceDescriptor> {
-        self.analyze(pat.syntax()).find_all_refs(pat)
-    }
-
-    fn analyze(&self, node: &SyntaxNode) -> SourceAnalyzer {
-        let src = self.find_file(node.clone());
-        self.analyze2(src.as_ref(), None)
-    }
-
-    fn analyze2(&self, src: InFile<&SyntaxNode>, offset: Option<TextUnit>) -> SourceAnalyzer {
-        let _p = profile("Semantics::analyze2");
-
-        let container = match self.sb.borrow_mut().find_container(self.db, src) {
-            Some(it) => it,
-            None => return SourceAnalyzer::new_for_resolver(Resolver::default(), src),
-        };
-
-        let resolver = match container {
-            ChildContainer::DefWithBodyId(def) => {
-                return SourceAnalyzer::new_for_body(self.db, def, src, offset)
-            }
-            ChildContainer::TraitId(it) => it.resolver(self.db),
-            ChildContainer::ImplId(it) => it.resolver(self.db),
-            ChildContainer::ModuleId(it) => it.resolver(self.db),
-            ChildContainer::EnumId(it) => it.resolver(self.db),
-            ChildContainer::VariantId(it) => it.resolver(self.db),
-            ChildContainer::GenericDefId(it) => it.resolver(self.db),
-        };
-        SourceAnalyzer::new_for_resolver(resolver, src)
-    }
-
-    fn cache(&self, root_node: SyntaxNode, file_id: HirFileId) {
-        assert!(root_node.parent().is_none());
-        let mut cache = self.cache.borrow_mut();
-        let prev = cache.insert(root_node, file_id);
-        assert!(prev == None || prev == Some(file_id))
-    }
-
-    pub fn assert_contains_node(&self, node: &SyntaxNode) {
-        self.find_file(node.clone());
-    }
-
-    fn lookup(&self, root_node: &SyntaxNode) -> Option<HirFileId> {
-        let cache = self.cache.borrow();
-        cache.get(root_node).copied()
-    }
-
-    fn find_file(&self, node: SyntaxNode) -> InFile<SyntaxNode> {
-        let root_node = find_root(&node);
-        let file_id = self.lookup(&root_node).unwrap_or_else(|| {
-            panic!(
-                "\n\nFailed to lookup {:?} in this Semantics.\n\
-                 Make sure to use only query nodes, derived from this instance of Semantics.\n\
-                 root node:   {:?}\n\
-                 known nodes: {}\n\n",
-                node,
-                root_node,
-                self.cache
-                    .borrow()
-                    .keys()
-                    .map(|it| format!("{:?}", it))
-                    .collect::<Vec<_>>()
-                    .join(", ")
-            )
-        });
-        InFile::new(file_id, node)
-    }
-}
-
-pub trait ToDef: Sized + AstNode + 'static {
-    type Def;
-    fn to_def<DB: HirDatabase>(sema: &Semantics<DB>, src: &Self) -> Option<Self::Def>;
-}
-
-macro_rules! to_def_impls {
-    ($(($def:path, $ast:path)),* ,) => {$(
-        impl ToDef for $ast {
-            type Def = $def;
-            fn to_def<DB: HirDatabase>(sema: &Semantics<DB>, src: &Self)
-                -> Option<Self::Def>
-            {
-                let src = sema.find_file(src.syntax().clone()).with_value(src);
-                sema.sb.borrow_mut().to_id(sema.db, src.cloned()).map(Into::into)
-            }
-        }
-    )*}
-}
-
-to_def_impls![
-    (crate::Module, ast::Module),
-    (crate::Struct, ast::StructDef),
-    (crate::Enum, ast::EnumDef),
-    (crate::Union, ast::UnionDef),
-    (crate::Trait, ast::TraitDef),
-    (crate::ImplBlock, ast::ImplBlock),
-    (crate::TypeAlias, ast::TypeAliasDef),
-    (crate::Const, ast::ConstDef),
-    (crate::Static, ast::StaticDef),
-    (crate::Function, ast::FnDef),
-    (crate::StructField, ast::RecordFieldDef),
-    (crate::EnumVariant, ast::EnumVariant),
-    (crate::TypeParam, ast::TypeParam),
-    (crate::MacroDef, ast::MacroCall), // this one is dubious, not all calls are macros
-];
-
-impl ToDef for ast::BindPat {
-    type Def = Local;
-
-    fn to_def<DB: HirDatabase>(sema: &Semantics<DB>, src: &Self) -> Option<Local> {
-        let src = sema.find_file(src.syntax().clone()).with_value(src);
-        let file_id = src.file_id;
-        let mut sb = sema.sb.borrow_mut();
-        let db = sema.db;
-        let parent: DefWithBodyId = src.value.syntax().ancestors().find_map(|it| {
-            let res = match_ast! {
-                match it {
-                    ast::ConstDef(value) => { sb.to_id(db, InFile { value, file_id})?.into() },
-                    ast::StaticDef(value) => { sb.to_id(db, InFile { value, file_id})?.into() },
-                    ast::FnDef(value) => { sb.to_id(db, InFile { value, file_id})?.into() },
-                    _ => return None,
-                }
-            };
-            Some(res)
-        })?;
-        let (_body, source_map) = db.body_with_source_map(parent);
-        let src = src.cloned().map(ast::Pat::from);
-        let pat_id = source_map.node_pat(src.as_ref())?;
-        Some(Local { parent: parent.into(), pat_id })
-    }
-}
-
-fn find_root(node: &SyntaxNode) -> SyntaxNode {
-    node.ancestors().last().unwrap()
-}
-
-pub struct SemanticsScope<'a, DB> {
-    pub db: &'a DB,
-    resolver: Resolver,
-}
-
-impl<'a, DB: HirDatabase> SemanticsScope<'a, DB> {
-    pub fn module(&self) -> Option<Module> {
-        Some(Module { id: self.resolver.module()? })
-    }
-
-    /// Note: `FxHashSet<TraitId>` should be treated as an opaque type, passed into `Type
-    // FIXME: rename to visible_traits to not repeat scope?
-    pub fn traits_in_scope(&self) -> FxHashSet<TraitId> {
-        let resolver = &self.resolver;
-        resolver.traits_in_scope(self.db)
-    }
-
-    pub fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef)) {
-        let resolver = &self.resolver;
-
-        resolver.process_all_names(self.db, &mut |name, def| {
-            let def = match def {
-                resolver::ScopeDef::PerNs(it) => it.into(),
-                resolver::ScopeDef::ImplSelfType(it) => ScopeDef::ImplSelfType(it.into()),
-                resolver::ScopeDef::AdtSelfType(it) => ScopeDef::AdtSelfType(it.into()),
-                resolver::ScopeDef::GenericParam(id) => ScopeDef::GenericParam(TypeParam { id }),
-                resolver::ScopeDef::Local(pat_id) => {
-                    let parent = resolver.body_owner().unwrap().into();
-                    ScopeDef::Local(Local { parent, pat_id })
-                }
-            };
-            f(name, def)
-        })
-    }
-
-    pub fn resolve_hir_path(&self, path: &Path) -> Option<PathResolution> {
-        resolve_hir_path(self.db, &self.resolver, path)
-    }
-}
-
-// FIXME: Change `HasSource` trait to work with `Semantics` and remove this?
-pub fn original_range(db: &impl HirDatabase, node: InFile<&SyntaxNode>) -> FileRange {
-    let mut elem: InFile<SyntaxElement> = node.map(|n| n.clone().into());
-
-    while let Some((range, Origin::Call)) = original_range_and_origin(db, elem.as_ref()) {
-        let original_file = range.file_id.original_file(db);
-
-        if range.file_id == original_file.into() {
-            return FileRange { file_id: original_file, range: range.value };
-        }
-
-        if range.file_id != elem.file_id {
-            if let Some(root) = db.parse_or_expand(range.file_id) {
-                elem = range.with_value(find_covering_element(&root, range.value));
-                continue;
-            }
-        }
-
-        log::error!("Fail to mapping up more for {:?}", range);
-        return FileRange { file_id: range.file_id.original_file(db), range: range.value };
-    }
-
-    // Fall back to whole macro call
-    if let Some(expansion) = node.file_id.expansion_info(db) {
-        if let Some(call_node) = expansion.call_node() {
-            return FileRange {
-                file_id: call_node.file_id.original_file(db),
-                range: call_node.value.text_range(),
-            };
-        }
-    }
-
-    FileRange { file_id: node.file_id.original_file(db), range: node.value.text_range() }
-}
-
-fn original_range_and_origin(
-    db: &impl HirDatabase,
-    elem: InFile<&SyntaxElement>,
-) -> Option<(InFile<TextRange>, Origin)> {
-    let expansion = elem.file_id.expansion_info(db)?;
-
-    let node = match elem.as_ref().value {
-        NodeOrToken::Node(it) => elem.with_value(it),
-        NodeOrToken::Token(it) => {
-            let (tt, origin) = expansion.map_token_up(elem.with_value(it))?;
-            return Some((tt.map(|it| it.text_range()), origin));
-        }
-    };
-
-    // the input node has only one token ?
-    let single = skip_trivia_token(node.value.first_token()?, Direction::Next)?
-        == skip_trivia_token(node.value.last_token()?, Direction::Prev)?;
-
-    return Some(node.value.descendants().find_map(|it| {
-        let first = skip_trivia_token(it.first_token()?, Direction::Next)?;
-        let last = skip_trivia_token(it.last_token()?, Direction::Prev)?;
-
-        if !single && first == last {
-            return None;
-        }
-
-        // Try to map first and last tokens of node, and, if success, return the union range of mapped tokens
-        let (first, first_origin) = expansion.map_token_up(node.with_value(&first))?;
-        let (last, last_origin) = expansion.map_token_up(node.with_value(&last))?;
-
-        if first.file_id != last.file_id || first_origin != last_origin {
-            return None;
-        }
-
-        // FIXME: Add union method in TextRange
-        Some((
-            first.with_value(union_range(first.value.text_range(), last.value.text_range())),
-            first_origin,
-        ))
-    })?);
-
-    fn union_range(a: TextRange, b: TextRange) -> TextRange {
-        let start = a.start().min(b.start());
-        let end = a.end().max(b.end());
-        TextRange::from_to(start, end)
-    }
-}
diff --git a/crates/ra_hir/src/source_analyzer.rs b/crates/ra_hir/src/source_analyzer.rs
index b655e2c320..efa3f8a79c 100644
--- a/crates/ra_hir/src/source_analyzer.rs
+++ b/crates/ra_hir/src/source_analyzer.rs
@@ -14,27 +14,29 @@ use hir_def::{
         BodySourceMap,
     },
     expr::{ExprId, PatId},
-    resolver::{resolver_for_scope, Resolver, TypeNs, ValueNs},
-    AsMacroCall, DefWithBodyId,
+    resolver::{self, resolver_for_scope, Resolver, TypeNs, ValueNs},
+    AsMacroCall, DefWithBodyId, TraitId,
 };
-use hir_expand::{hygiene::Hygiene, name::AsName, HirFileId, InFile};
+use hir_expand::{hygiene::Hygiene, name::AsName, HirFileId, InFile, MacroCallId};
 use hir_ty::{InEnvironment, InferenceResult, TraitEnvironment};
 use ra_syntax::{
     ast::{self, AstNode},
-    AstPtr, SyntaxNode, SyntaxNodePtr, TextRange, TextUnit,
+    AstPtr, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange, TextUnit,
 };
+use rustc_hash::FxHashSet;
 
 use crate::{
-    db::HirDatabase, Adt, Const, EnumVariant, Function, Local, MacroDef, Path, Static, Struct,
-    Trait, Type, TypeAlias, TypeParam,
+    db::HirDatabase, Adt, Const, DefWithBody, EnumVariant, Function, Local, MacroDef, Name, Path,
+    ScopeDef, Static, Struct, Trait, Type, TypeAlias, TypeParam,
 };
 
 /// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of
 /// original source files. It should not be used inside the HIR itself.
 #[derive(Debug)]
-pub(crate) struct SourceAnalyzer {
+pub struct SourceAnalyzer {
     file_id: HirFileId,
-    pub(crate) resolver: Resolver,
+    resolver: Resolver,
+    body_owner: Option<DefWithBody>,
     body_source_map: Option<Arc<BodySourceMap>>,
     infer: Option<Arc<InferenceResult>>,
     scopes: Option<Arc<ExprScopes>>,
@@ -53,13 +55,57 @@ pub enum PathResolution {
     AssocItem(crate::AssocItem),
 }
 
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct ScopeEntryWithSyntax {
+    pub(crate) name: Name,
+    pub(crate) ptr: Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>,
+}
+
+impl ScopeEntryWithSyntax {
+    pub fn name(&self) -> &Name {
+        &self.name
+    }
+
+    pub fn ptr(&self) -> Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>> {
+        self.ptr
+    }
+}
+
 #[derive(Debug)]
 pub struct ReferenceDescriptor {
     pub range: TextRange,
     pub name: String,
 }
 
+#[derive(Debug)]
+pub struct Expansion {
+    macro_call_id: MacroCallId,
+}
+
+impl Expansion {
+    pub fn map_token_down(
+        &self,
+        db: &impl HirDatabase,
+        token: InFile<&SyntaxToken>,
+    ) -> Option<InFile<SyntaxToken>> {
+        let exp_info = self.file_id().expansion_info(db)?;
+        exp_info.map_token_down(token)
+    }
+
+    pub fn file_id(&self) -> HirFileId {
+        self.macro_call_id.as_file()
+    }
+}
+
 impl SourceAnalyzer {
+    pub fn new(
+        db: &impl HirDatabase,
+        node: InFile<&SyntaxNode>,
+        offset: Option<TextUnit>,
+    ) -> SourceAnalyzer {
+        crate::source_binder::SourceBinder::new(db).analyze(node, offset)
+    }
+
     pub(crate) fn new_for_body(
         db: &impl HirDatabase,
         def: DefWithBodyId,
@@ -75,6 +121,7 @@ impl SourceAnalyzer {
         let resolver = resolver_for_scope(db, def, scope);
         SourceAnalyzer {
             resolver,
+            body_owner: Some(def.into()),
             body_source_map: Some(source_map),
             infer: Some(db.infer(def)),
             scopes: Some(scopes),
@@ -88,6 +135,7 @@ impl SourceAnalyzer {
     ) -> SourceAnalyzer {
         SourceAnalyzer {
             resolver,
+            body_owner: None,
             body_source_map: None,
             infer: None,
             scopes: None,
@@ -95,6 +143,10 @@ impl SourceAnalyzer {
         }
     }
 
+    pub fn module(&self) -> Option<crate::code_model::Module> {
+        Some(crate::code_model::Module { id: self.resolver.module()? })
+    }
+
     fn expr_id(&self, expr: &ast::Expr) -> Option<ExprId> {
         let src = InFile { file_id: self.file_id, value: expr };
         self.body_source_map.as_ref()?.node_expr(src)
@@ -128,7 +180,7 @@ impl SourceAnalyzer {
         TraitEnvironment::lower(db, &self.resolver)
     }
 
-    pub(crate) fn type_of(&self, db: &impl HirDatabase, expr: &ast::Expr) -> Option<Type> {
+    pub fn type_of(&self, db: &impl HirDatabase, expr: &ast::Expr) -> Option<Type> {
         let expr_id = if let Some(expr) = self.expand_expr(db, InFile::new(self.file_id, expr)) {
             self.body_source_map.as_ref()?.node_expr(expr.as_ref())?
         } else {
@@ -140,27 +192,24 @@ impl SourceAnalyzer {
         Some(Type { krate: self.resolver.krate()?, ty: InEnvironment { value: ty, environment } })
     }
 
-    pub(crate) fn type_of_pat(&self, db: &impl HirDatabase, pat: &ast::Pat) -> Option<Type> {
+    pub fn type_of_pat(&self, db: &impl HirDatabase, pat: &ast::Pat) -> Option<Type> {
         let pat_id = self.pat_id(pat)?;
         let ty = self.infer.as_ref()?[pat_id].clone();
         let environment = self.trait_env(db);
         Some(Type { krate: self.resolver.krate()?, ty: InEnvironment { value: ty, environment } })
     }
 
-    pub(crate) fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<Function> {
+    pub fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<Function> {
         let expr_id = self.expr_id(&call.clone().into())?;
         self.infer.as_ref()?.method_resolution(expr_id).map(Function::from)
     }
 
-    pub(crate) fn resolve_field(&self, field: &ast::FieldExpr) -> Option<crate::StructField> {
+    pub fn resolve_field(&self, field: &ast::FieldExpr) -> Option<crate::StructField> {
         let expr_id = self.expr_id(&field.clone().into())?;
         self.infer.as_ref()?.field_resolution(expr_id).map(|it| it.into())
     }
 
-    pub(crate) fn resolve_record_field(
-        &self,
-        field: &ast::RecordField,
-    ) -> Option<crate::StructField> {
+    pub fn resolve_record_field(&self, field: &ast::RecordField) -> Option<crate::StructField> {
         let expr_id = match field.expr() {
             Some(it) => self.expr_id(&it)?,
             None => {
@@ -171,23 +220,17 @@ impl SourceAnalyzer {
         self.infer.as_ref()?.record_field_resolution(expr_id).map(|it| it.into())
     }
 
-    pub(crate) fn resolve_record_literal(
-        &self,
-        record_lit: &ast::RecordLit,
-    ) -> Option<crate::VariantDef> {
+    pub fn resolve_record_literal(&self, record_lit: &ast::RecordLit) -> Option<crate::VariantDef> {
         let expr_id = self.expr_id(&record_lit.clone().into())?;
         self.infer.as_ref()?.variant_resolution_for_expr(expr_id).map(|it| it.into())
     }
 
-    pub(crate) fn resolve_record_pattern(
-        &self,
-        record_pat: &ast::RecordPat,
-    ) -> Option<crate::VariantDef> {
+    pub fn resolve_record_pattern(&self, record_pat: &ast::RecordPat) -> Option<crate::VariantDef> {
         let pat_id = self.pat_id(&record_pat.clone().into())?;
         self.infer.as_ref()?.variant_resolution_for_pat(pat_id).map(|it| it.into())
     }
 
-    pub(crate) fn resolve_macro_call(
+    pub fn resolve_macro_call(
         &self,
         db: &impl HirDatabase,
         macro_call: InFile<&ast::MacroCall>,
@@ -197,11 +240,52 @@ impl SourceAnalyzer {
         self.resolver.resolve_path_as_macro(db, path.mod_path()).map(|it| it.into())
     }
 
-    pub(crate) fn resolve_path(
+    pub fn resolve_hir_path(
         &self,
         db: &impl HirDatabase,
-        path: &ast::Path,
+        path: &crate::Path,
     ) -> Option<PathResolution> {
+        let types =
+            self.resolver.resolve_path_in_type_ns_fully(db, path.mod_path()).map(|ty| match ty {
+                TypeNs::SelfType(it) => PathResolution::SelfType(it.into()),
+                TypeNs::GenericParam(id) => PathResolution::TypeParam(TypeParam { id }),
+                TypeNs::AdtSelfType(it) | TypeNs::AdtId(it) => {
+                    PathResolution::Def(Adt::from(it).into())
+                }
+                TypeNs::EnumVariantId(it) => PathResolution::Def(EnumVariant::from(it).into()),
+                TypeNs::TypeAliasId(it) => PathResolution::Def(TypeAlias::from(it).into()),
+                TypeNs::BuiltinType(it) => PathResolution::Def(it.into()),
+                TypeNs::TraitId(it) => PathResolution::Def(Trait::from(it).into()),
+            });
+        let values =
+            self.resolver.resolve_path_in_value_ns_fully(db, path.mod_path()).and_then(|val| {
+                let res = match val {
+                    ValueNs::LocalBinding(pat_id) => {
+                        let var = Local { parent: self.body_owner?, pat_id };
+                        PathResolution::Local(var)
+                    }
+                    ValueNs::FunctionId(it) => PathResolution::Def(Function::from(it).into()),
+                    ValueNs::ConstId(it) => PathResolution::Def(Const::from(it).into()),
+                    ValueNs::StaticId(it) => PathResolution::Def(Static::from(it).into()),
+                    ValueNs::StructId(it) => PathResolution::Def(Struct::from(it).into()),
+                    ValueNs::EnumVariantId(it) => PathResolution::Def(EnumVariant::from(it).into()),
+                };
+                Some(res)
+            });
+
+        let items = self
+            .resolver
+            .resolve_module_path_in_items(db, path.mod_path())
+            .take_types()
+            .map(|it| PathResolution::Def(it.into()));
+        types.or(values).or(items).or_else(|| {
+            self.resolver
+                .resolve_path_as_macro(db, path.mod_path())
+                .map(|def| PathResolution::Macro(def.into()))
+        })
+    }
+
+    pub fn resolve_path(&self, db: &impl HirDatabase, path: &ast::Path) -> Option<PathResolution> {
         if let Some(path_expr) = path.syntax().parent().and_then(ast::PathExpr::cast) {
             let expr_id = self.expr_id(&path_expr.into())?;
             if let Some(assoc) = self.infer.as_ref()?.assoc_resolutions_for_expr(expr_id) {
@@ -216,24 +300,40 @@ impl SourceAnalyzer {
         }
         // This must be a normal source file rather than macro file.
         let hir_path = crate::Path::from_ast(path.clone())?;
-        resolve_hir_path(db, &self.resolver, &hir_path)
+        self.resolve_hir_path(db, &hir_path)
     }
 
-    fn resolve_local_name(
-        &self,
-        name_ref: &ast::NameRef,
-    ) -> Option<Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>> {
+    fn resolve_local_name(&self, name_ref: &ast::NameRef) -> Option<ScopeEntryWithSyntax> {
         let name = name_ref.as_name();
         let source_map = self.body_source_map.as_ref()?;
         let scopes = self.scopes.as_ref()?;
         let scope = scope_for(scopes, source_map, InFile::new(self.file_id, name_ref.syntax()))?;
         let entry = scopes.resolve_name_in_scope(scope, &name)?;
-        Some(source_map.pat_syntax(entry.pat())?.value)
+        Some(ScopeEntryWithSyntax {
+            name: entry.name().clone(),
+            ptr: source_map.pat_syntax(entry.pat())?.value,
+        })
+    }
+
+    pub fn process_all_names(&self, db: &impl HirDatabase, f: &mut dyn FnMut(Name, ScopeDef)) {
+        self.resolver.process_all_names(db, &mut |name, def| {
+            let def = match def {
+                resolver::ScopeDef::PerNs(it) => it.into(),
+                resolver::ScopeDef::ImplSelfType(it) => ScopeDef::ImplSelfType(it.into()),
+                resolver::ScopeDef::AdtSelfType(it) => ScopeDef::AdtSelfType(it.into()),
+                resolver::ScopeDef::GenericParam(id) => ScopeDef::GenericParam(TypeParam { id }),
+                resolver::ScopeDef::Local(pat_id) => {
+                    let parent = self.resolver.body_owner().unwrap().into();
+                    ScopeDef::Local(Local { parent, pat_id })
+                }
+            };
+            f(name, def)
+        })
     }
 
     // FIXME: we only use this in `inline_local_variable` assist, ideally, we
     // should switch to general reference search infra there.
-    pub(crate) fn find_all_refs(&self, pat: &ast::BindPat) -> Vec<ReferenceDescriptor> {
+    pub fn find_all_refs(&self, pat: &ast::BindPat) -> Vec<ReferenceDescriptor> {
         let fn_def = pat.syntax().ancestors().find_map(ast::FnDef::cast).unwrap();
         let ptr = Either::Left(AstPtr::new(&ast::Pat::from(pat.clone())));
         fn_def
@@ -242,7 +342,7 @@ impl SourceAnalyzer {
             .filter_map(ast::NameRef::cast)
             .filter(|name_ref| match self.resolve_local_name(&name_ref) {
                 None => false,
-                Some(d_ptr) => d_ptr == ptr,
+                Some(entry) => entry.ptr() == ptr,
             })
             .map(|name_ref| ReferenceDescriptor {
                 name: name_ref.text().to_string(),
@@ -251,14 +351,19 @@ impl SourceAnalyzer {
             .collect()
     }
 
-    pub(crate) fn expand(
+    /// Note: `FxHashSet<TraitId>` should be treated as an opaque type, passed into `Type
+    pub fn traits_in_scope(&self, db: &impl HirDatabase) -> FxHashSet<TraitId> {
+        self.resolver.traits_in_scope(db)
+    }
+
+    pub fn expand(
         &self,
         db: &impl HirDatabase,
         macro_call: InFile<&ast::MacroCall>,
-    ) -> Option<HirFileId> {
+    ) -> Option<Expansion> {
         let macro_call_id =
             macro_call.as_call_id(db, |path| self.resolver.resolve_path_as_macro(db, &path))?;
-        Some(macro_call_id.as_file())
+        Some(Expansion { macro_call_id })
     }
 }
 
@@ -304,47 +409,6 @@ fn scope_for_offset(
         })
 }
 
-pub(crate) fn resolve_hir_path(
-    db: &impl HirDatabase,
-    resolver: &Resolver,
-    path: &crate::Path,
-) -> Option<PathResolution> {
-    let types = resolver.resolve_path_in_type_ns_fully(db, path.mod_path()).map(|ty| match ty {
-        TypeNs::SelfType(it) => PathResolution::SelfType(it.into()),
-        TypeNs::GenericParam(id) => PathResolution::TypeParam(TypeParam { id }),
-        TypeNs::AdtSelfType(it) | TypeNs::AdtId(it) => PathResolution::Def(Adt::from(it).into()),
-        TypeNs::EnumVariantId(it) => PathResolution::Def(EnumVariant::from(it).into()),
-        TypeNs::TypeAliasId(it) => PathResolution::Def(TypeAlias::from(it).into()),
-        TypeNs::BuiltinType(it) => PathResolution::Def(it.into()),
-        TypeNs::TraitId(it) => PathResolution::Def(Trait::from(it).into()),
-    });
-    let body_owner = resolver.body_owner();
-    let values = resolver.resolve_path_in_value_ns_fully(db, path.mod_path()).and_then(|val| {
-        let res = match val {
-            ValueNs::LocalBinding(pat_id) => {
-                let var = Local { parent: body_owner?.into(), pat_id };
-                PathResolution::Local(var)
-            }
-            ValueNs::FunctionId(it) => PathResolution::Def(Function::from(it).into()),
-            ValueNs::ConstId(it) => PathResolution::Def(Const::from(it).into()),
-            ValueNs::StaticId(it) => PathResolution::Def(Static::from(it).into()),
-            ValueNs::StructId(it) => PathResolution::Def(Struct::from(it).into()),
-            ValueNs::EnumVariantId(it) => PathResolution::Def(EnumVariant::from(it).into()),
-        };
-        Some(res)
-    });
-
-    let items = resolver
-        .resolve_module_path_in_items(db, path.mod_path())
-        .take_types()
-        .map(|it| PathResolution::Def(it.into()));
-    types.or(values).or(items).or_else(|| {
-        resolver
-            .resolve_path_as_macro(db, path.mod_path())
-            .map(|def| PathResolution::Macro(def.into()))
-    })
-}
-
 // XXX: during completion, cursor might be outside of any particular
 // expression. Try to figure out the correct scope...
 fn adjust(
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs
index 4353e25ac8..f3150f5784 100644
--- a/crates/ra_hir/src/source_binder.rs
+++ b/crates/ra_hir/src/source_binder.rs
@@ -5,89 +5,112 @@ use hir_def::{
     child_by_source::ChildBySource,
     dyn_map::DynMap,
     keys::{self, Key},
+    resolver::{HasResolver, Resolver},
     ConstId, DefWithBodyId, EnumId, EnumVariantId, FunctionId, GenericDefId, ImplId, ModuleId,
-    StaticId, StructFieldId, StructId, TraitId, TypeAliasId, TypeParamId, UnionId, VariantId,
+    StaticId, StructFieldId, StructId, TraitId, TypeAliasId, UnionId, VariantId,
 };
 use hir_expand::{name::AsName, AstId, InFile, MacroDefId, MacroDefKind};
-use ra_db::FileId;
 use ra_prof::profile;
 use ra_syntax::{
     ast::{self, NameOwner},
-    match_ast, AstNode, SyntaxNode,
+    match_ast, AstNode, SyntaxNode, TextUnit,
 };
 use rustc_hash::FxHashMap;
 
-use crate::{db::HirDatabase, Module};
+use crate::{db::HirDatabase, Local, Module, SourceAnalyzer, TypeParam};
+use ra_db::FileId;
 
-pub(crate) struct SourceBinder {
+pub struct SourceBinder<'a, DB> {
+    pub db: &'a DB,
     child_by_source_cache: FxHashMap<ChildContainer, DynMap>,
 }
 
-impl SourceBinder {
-    pub(crate) fn new() -> SourceBinder {
-        SourceBinder { child_by_source_cache: FxHashMap::default() }
+impl<DB: HirDatabase> SourceBinder<'_, DB> {
+    pub fn new(db: &DB) -> SourceBinder<DB> {
+        SourceBinder { db, child_by_source_cache: FxHashMap::default() }
+    }
+
+    pub fn analyze(
+        &mut self,
+        src: InFile<&SyntaxNode>,
+        offset: Option<TextUnit>,
+    ) -> SourceAnalyzer {
+        let _p = profile("SourceBinder::analyzer");
+        let container = match self.find_container(src) {
+            Some(it) => it,
+            None => return SourceAnalyzer::new_for_resolver(Resolver::default(), src),
+        };
+
+        let resolver = match container {
+            ChildContainer::DefWithBodyId(def) => {
+                return SourceAnalyzer::new_for_body(self.db, def, src, offset)
+            }
+            ChildContainer::TraitId(it) => it.resolver(self.db),
+            ChildContainer::ImplId(it) => it.resolver(self.db),
+            ChildContainer::ModuleId(it) => it.resolver(self.db),
+            ChildContainer::EnumId(it) => it.resolver(self.db),
+            ChildContainer::VariantId(it) => it.resolver(self.db),
+            ChildContainer::GenericDefId(it) => it.resolver(self.db),
+        };
+        SourceAnalyzer::new_for_resolver(resolver, src)
+    }
+
+    pub fn to_def<T: ToDef>(&mut self, src: InFile<T>) -> Option<T::Def> {
+        T::to_def(self, src)
     }
 
-    pub(crate) fn to_module_def(&mut self, db: &impl HirDatabase, file: FileId) -> Option<Module> {
+    pub fn to_module_def(&mut self, file: FileId) -> Option<Module> {
         let _p = profile("SourceBinder::to_module_def");
-        let (krate, local_id) = db.relevant_crates(file).iter().find_map(|&crate_id| {
-            let crate_def_map = db.crate_def_map(crate_id);
+        let (krate, local_id) = self.db.relevant_crates(file).iter().find_map(|&crate_id| {
+            let crate_def_map = self.db.crate_def_map(crate_id);
             let local_id = crate_def_map.modules_for_file(file).next()?;
             Some((crate_id, local_id))
         })?;
         Some(Module { id: ModuleId { krate, local_id } })
     }
 
-    pub(crate) fn to_id<T: ToId>(
-        &mut self,
-        db: &impl HirDatabase,
-        src: InFile<T>,
-    ) -> Option<T::ID> {
-        T::to_id(db, self, src)
+    fn to_id<T: ToId>(&mut self, src: InFile<T>) -> Option<T::ID> {
+        T::to_id(self, src)
     }
 
-    pub(crate) fn find_container(
-        &mut self,
-        db: &impl HirDatabase,
-        src: InFile<&SyntaxNode>,
-    ) -> Option<ChildContainer> {
-        for container in src.cloned().ancestors_with_macros(db).skip(1) {
+    fn find_container(&mut self, src: InFile<&SyntaxNode>) -> Option<ChildContainer> {
+        for container in src.cloned().ancestors_with_macros(self.db).skip(1) {
             let res: ChildContainer = match_ast! {
                 match (container.value) {
                     ast::TraitDef(it) => {
-                        let def: TraitId = self.to_id(db, container.with_value(it))?;
+                        let def: TraitId = self.to_id(container.with_value(it))?;
                         def.into()
                     },
                     ast::ImplBlock(it) => {
-                        let def: ImplId = self.to_id(db, container.with_value(it))?;
+                        let def: ImplId = self.to_id(container.with_value(it))?;
                         def.into()
                     },
                     ast::FnDef(it) => {
-                        let def: FunctionId = self.to_id(db, container.with_value(it))?;
+                        let def: FunctionId = self.to_id(container.with_value(it))?;
                         DefWithBodyId::from(def).into()
                     },
                     ast::StaticDef(it) => {
-                        let def: StaticId = self.to_id(db, container.with_value(it))?;
+                        let def: StaticId = self.to_id(container.with_value(it))?;
                         DefWithBodyId::from(def).into()
                     },
                     ast::ConstDef(it) => {
-                        let def: ConstId = self.to_id(db, container.with_value(it))?;
+                        let def: ConstId = self.to_id(container.with_value(it))?;
                         DefWithBodyId::from(def).into()
                     },
                     ast::EnumDef(it) => {
-                        let def: EnumId = self.to_id(db, container.with_value(it))?;
+                        let def: EnumId = self.to_id(container.with_value(it))?;
                         def.into()
                     },
                     ast::StructDef(it) => {
-                        let def: StructId = self.to_id(db, container.with_value(it))?;
+                        let def: StructId = self.to_id(container.with_value(it))?;
                         VariantId::from(def).into()
                     },
                     ast::UnionDef(it) => {
-                        let def: UnionId = self.to_id(db, container.with_value(it))?;
+                        let def: UnionId = self.to_id(container.with_value(it))?;
                         VariantId::from(def).into()
                     },
                     ast::Module(it) => {
-                        let def: ModuleId = self.to_id(db, container.with_value(it))?;
+                        let def: ModuleId = self.to_id(container.with_value(it))?;
                         def.into()
                     },
                     _ => { continue },
@@ -96,11 +119,12 @@ impl SourceBinder {
             return Some(res);
         }
 
-        let c = self.to_module_def(db, src.file_id.original_file(db))?;
+        let c = self.to_module_def(src.file_id.original_file(self.db))?;
         Some(c.id.into())
     }
 
-    fn child_by_source(&mut self, db: &impl HirDatabase, container: ChildContainer) -> &DynMap {
+    fn child_by_source(&mut self, container: ChildContainer) -> &DynMap {
+        let db = self.db;
         self.child_by_source_cache.entry(container).or_insert_with(|| match container {
             ChildContainer::DefWithBodyId(it) => it.child_by_source(db),
             ChildContainer::ModuleId(it) => it.child_by_source(db),
@@ -113,17 +137,49 @@ impl SourceBinder {
     }
 }
 
-pub(crate) trait ToId: Sized {
+pub trait ToId: Sized {
     type ID: Sized + Copy + 'static;
-    fn to_id<DB: HirDatabase>(
-        db: &DB,
-        sb: &mut SourceBinder,
+    fn to_id<DB: HirDatabase>(sb: &mut SourceBinder<'_, DB>, src: InFile<Self>)
+        -> Option<Self::ID>;
+}
+
+pub trait ToDef: Sized + AstNode + 'static {
+    type Def;
+    fn to_def<DB: HirDatabase>(
+        sb: &mut SourceBinder<'_, DB>,
         src: InFile<Self>,
-    ) -> Option<Self::ID>;
+    ) -> Option<Self::Def>;
+}
+
+macro_rules! to_def_impls {
+    ($(($def:path, $ast:path)),* ,) => {$(
+        impl ToDef for $ast {
+            type Def = $def;
+            fn to_def<DB: HirDatabase>(sb: &mut SourceBinder<'_, DB>, src: InFile<Self>)
+                -> Option<Self::Def>
+            { sb.to_id(src).map(Into::into) }
+        }
+    )*}
 }
 
+to_def_impls![
+    (crate::Module, ast::Module),
+    (crate::Struct, ast::StructDef),
+    (crate::Enum, ast::EnumDef),
+    (crate::Union, ast::UnionDef),
+    (crate::Trait, ast::TraitDef),
+    (crate::ImplBlock, ast::ImplBlock),
+    (crate::TypeAlias, ast::TypeAliasDef),
+    (crate::Const, ast::ConstDef),
+    (crate::Static, ast::StaticDef),
+    (crate::Function, ast::FnDef),
+    (crate::StructField, ast::RecordFieldDef),
+    (crate::EnumVariant, ast::EnumVariant),
+    (crate::MacroDef, ast::MacroCall), // this one is dubious, not all calls are macros
+];
+
 #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
-pub(crate) enum ChildContainer {
+enum ChildContainer {
     DefWithBodyId(DefWithBodyId),
     ModuleId(ModuleId),
     TraitId(TraitId),
@@ -145,7 +201,7 @@ impl_froms! {
     GenericDefId
 }
 
-pub(crate) trait ToIdByKey: Sized + AstNode + 'static {
+pub trait ToIdByKey: Sized + AstNode + 'static {
     type ID: Sized + Copy + 'static;
     const KEY: Key<Self, Self::ID>;
 }
@@ -153,11 +209,11 @@ pub(crate) trait ToIdByKey: Sized + AstNode + 'static {
 impl<T: ToIdByKey> ToId for T {
     type ID = <T as ToIdByKey>::ID;
     fn to_id<DB: HirDatabase>(
-        db: &DB,
-        sb: &mut SourceBinder,
+        sb: &mut SourceBinder<'_, DB>,
         src: InFile<Self>,
     ) -> Option<Self::ID> {
-        let container = sb.find_container(db, src.as_ref().map(|it| it.syntax()))?;
+        let container = sb.find_container(src.as_ref().map(|it| it.syntax()))?;
+        let db = sb.db;
         let dyn_map =
             &*sb.child_by_source_cache.entry(container).or_insert_with(|| match container {
                 ChildContainer::DefWithBodyId(it) => it.child_by_source(db),
@@ -199,44 +255,68 @@ to_id_key_impls![
 impl ToId for ast::MacroCall {
     type ID = MacroDefId;
     fn to_id<DB: HirDatabase>(
-        db: &DB,
-        sb: &mut SourceBinder,
+        sb: &mut SourceBinder<'_, DB>,
         src: InFile<Self>,
     ) -> Option<Self::ID> {
         let kind = MacroDefKind::Declarative;
 
-        let krate = sb.to_module_def(db, src.file_id.original_file(db))?.id.krate;
+        let krate = sb.to_module_def(src.file_id.original_file(sb.db))?.id.krate;
 
-        let ast_id = Some(AstId::new(src.file_id, db.ast_id_map(src.file_id).ast_id(&src.value)));
+        let ast_id =
+            Some(AstId::new(src.file_id, sb.db.ast_id_map(src.file_id).ast_id(&src.value)));
 
         Some(MacroDefId { krate: Some(krate), ast_id, kind })
     }
 }
 
-impl ToId for ast::TypeParam {
-    type ID = TypeParamId;
+impl ToDef for ast::BindPat {
+    type Def = Local;
 
-    fn to_id<DB: HirDatabase>(
-        db: &DB,
-        sb: &mut SourceBinder,
-        src: InFile<Self>,
-    ) -> Option<Self::ID> {
+    fn to_def<DB: HirDatabase>(sb: &mut SourceBinder<'_, DB>, src: InFile<Self>) -> Option<Local> {
+        let file_id = src.file_id;
+        let parent: DefWithBodyId = src.value.syntax().ancestors().find_map(|it| {
+            let res = match_ast! {
+                match it {
+                    ast::ConstDef(value) => { sb.to_id(InFile { value, file_id})?.into() },
+                    ast::StaticDef(value) => { sb.to_id(InFile { value, file_id})?.into() },
+                    ast::FnDef(value) => { sb.to_id(InFile { value, file_id})?.into() },
+                    _ => return None,
+                }
+            };
+            Some(res)
+        })?;
+        let (_body, source_map) = sb.db.body_with_source_map(parent);
+        let src = src.map(ast::Pat::from);
+        let pat_id = source_map.node_pat(src.as_ref())?;
+        Some(Local { parent: parent.into(), pat_id })
+    }
+}
+
+impl ToDef for ast::TypeParam {
+    type Def = TypeParam;
+
+    fn to_def<DB: HirDatabase>(
+        sb: &mut SourceBinder<'_, DB>,
+        src: InFile<ast::TypeParam>,
+    ) -> Option<TypeParam> {
+        let mut sb = SourceBinder::new(sb.db);
         let file_id = src.file_id;
         let parent: GenericDefId = src.value.syntax().ancestors().find_map(|it| {
             let res = match_ast! {
                 match it {
-                    ast::FnDef(value) => { sb.to_id(db, InFile { value, file_id})?.into() },
-                    ast::StructDef(value) => { sb.to_id(db, InFile { value, file_id})?.into() },
-                    ast::EnumDef(value) => { sb.to_id(db, InFile { value, file_id})?.into() },
-                    ast::TraitDef(value) => { sb.to_id(db, InFile { value, file_id})?.into() },
-                    ast::TypeAliasDef(value) => { sb.to_id(db, InFile { value, file_id})?.into() },
-                    ast::ImplBlock(value) => { sb.to_id(db, InFile { value, file_id})?.into() },
+                    ast::FnDef(value) => { sb.to_id(InFile { value, file_id})?.into() },
+                    ast::StructDef(value) => { sb.to_id(InFile { value, file_id})?.into() },
+                    ast::EnumDef(value) => { sb.to_id(InFile { value, file_id})?.into() },
+                    ast::TraitDef(value) => { sb.to_id(InFile { value, file_id})?.into() },
+                    ast::TypeAliasDef(value) => { sb.to_id(InFile { value, file_id})?.into() },
+                    ast::ImplBlock(value) => { sb.to_id(InFile { value, file_id})?.into() },
                     _ => return None,
                 }
             };
             Some(res)
         })?;
-        sb.child_by_source(db, parent.into())[keys::TYPE_PARAM].get(&src).copied()
+        let &id = sb.child_by_source(parent.into())[keys::TYPE_PARAM].get(&src)?;
+        Some(TypeParam { id })
     }
 }
 
@@ -244,8 +324,7 @@ impl ToId for ast::Module {
     type ID = ModuleId;
 
     fn to_id<DB: HirDatabase>(
-        db: &DB,
-        sb: &mut SourceBinder,
+        sb: &mut SourceBinder<'_, DB>,
         src: InFile<ast::Module>,
     ) -> Option<ModuleId> {
         {
@@ -254,7 +333,7 @@ impl ToId for ast::Module {
                 .as_ref()
                 .map(|it| it.syntax())
                 .cloned()
-                .ancestors_with_macros(db)
+                .ancestors_with_macros(sb.db)
                 .skip(1)
                 .find_map(|it| {
                     let m = ast::Module::cast(it.value.clone())?;
@@ -262,15 +341,15 @@ impl ToId for ast::Module {
                 });
 
             let parent_module = match parent_declaration {
-                Some(parent_declaration) => sb.to_id(db, parent_declaration)?,
+                Some(parent_declaration) => sb.to_id(parent_declaration)?,
                 None => {
-                    let file_id = src.file_id.original_file(db);
-                    sb.to_module_def(db, file_id)?.id
+                    let file_id = src.file_id.original_file(sb.db);
+                    sb.to_module_def(file_id)?.id
                 }
             };
 
             let child_name = src.value.name()?.as_name();
-            let def_map = db.crate_def_map(parent_module.krate);
+            let def_map = sb.db.crate_def_map(parent_module.krate);
             let child_id = *def_map[parent_module.local_id].children.get(&child_name)?;
             Some(ModuleId { krate: parent_module.krate, local_id: child_id })
         }
diff --git a/crates/ra_ide/Cargo.toml b/crates/ra_ide/Cargo.toml
index 6a3112f9c6..3407d25989 100644
--- a/crates/ra_ide/Cargo.toml
+++ b/crates/ra_ide/Cargo.toml
@@ -20,6 +20,7 @@ log = "0.4.8"
 rayon = "1.3.0"
 fst = { version = "0.3.5", default-features = false }
 rustc-hash = "1.1.0"
+unicase = "2.6.0"
 superslice = "1.0.0"
 rand = { version = "0.7.3", features = ["small_rng"] }
 once_cell = "1.3.1"
diff --git a/crates/ra_ide/src/call_hierarchy.rs b/crates/ra_ide/src/call_hierarchy.rs
index b00b6d4310..51ac59a718 100644
--- a/crates/ra_ide/src/call_hierarchy.rs
+++ b/crates/ra_ide/src/call_hierarchy.rs
@@ -2,13 +2,13 @@
 
 use indexmap::IndexMap;
 
-use hir::Semantics;
+use hir::db::AstDatabase;
 use ra_ide_db::RootDatabase;
 use ra_syntax::{ast, match_ast, AstNode, TextRange};
 
 use crate::{
-    call_info::FnCallNode, display::ToNav, goto_definition, references, FilePosition,
-    NavigationTarget, RangeInfo,
+    call_info::FnCallNode, display::ToNav, expand::descend_into_macros, goto_definition,
+    references, FilePosition, NavigationTarget, RangeInfo,
 };
 
 #[derive(Debug, Clone)]
@@ -38,31 +38,30 @@ pub(crate) fn call_hierarchy(
 }
 
 pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Option<Vec<CallItem>> {
-    let sema = Semantics::new(db);
     // 1. Find all refs
     // 2. Loop through refs and determine unique fndef. This will become our `from: CallHierarchyItem,` in the reply.
     // 3. Add ranges relative to the start of the fndef.
     let refs = references::find_all_refs(db, position, None)?;
 
     let mut calls = CallLocations::default();
+    let mut sb = hir::SourceBinder::new(db);
 
     for reference in refs.info.references() {
         let file_id = reference.file_range.file_id;
-        let file = sema.parse(file_id);
-        let file = file.syntax();
+        let file = db.parse_or_expand(file_id.into())?;
         let token = file.token_at_offset(reference.file_range.range.start()).next()?;
-        let token = sema.descend_into_macros(token);
-        let syntax = token.parent();
+        let token = descend_into_macros(db, file_id, token);
+        let syntax = token.value.parent();
 
         // This target is the containing function
         if let Some(nav) = syntax.ancestors().find_map(|node| {
             match_ast! {
                 match node {
                     ast::FnDef(it) => {
-                        let def = sema.to_def(&it)?;
-                        Some(def.to_nav(sema.db))
+                        let def = sb.to_def(token.with_value(it))?;
+                        Some(def.to_nav(sb.db))
                     },
-                    _ => None,
+                    _ => { None },
                 }
             }
         }) {
@@ -75,13 +74,11 @@ pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Optio
 }
 
 pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Option<Vec<CallItem>> {
-    let sema = Semantics::new(db);
     let file_id = position.file_id;
-    let file = sema.parse(file_id);
-    let file = file.syntax();
+    let file = db.parse_or_expand(file_id.into())?;
     let token = file.token_at_offset(position.offset).next()?;
-    let token = sema.descend_into_macros(token);
-    let syntax = token.parent();
+    let token = descend_into_macros(db, file_id, token);
+    let syntax = token.value.parent();
 
     let mut calls = CallLocations::default();
 
@@ -90,11 +87,14 @@ pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Optio
         .filter_map(|node| FnCallNode::with_node_exact(&node))
         .filter_map(|call_node| {
             let name_ref = call_node.name_ref()?;
+            let name_ref = token.with_value(name_ref.syntax());
+
+            let analyzer = hir::SourceAnalyzer::new(db, name_ref, None);
 
             if let Some(func_target) = match &call_node {
                 FnCallNode::CallExpr(expr) => {
                     //FIXME: Type::as_callable is broken
-                    let callable_def = sema.type_of_expr(&expr.expr()?)?.as_callable()?;
+                    let callable_def = analyzer.type_of(db, &expr.expr()?)?.as_callable()?;
                     match callable_def {
                         hir::CallableDef::FunctionId(it) => {
                             let fn_def: hir::Function = it.into();
@@ -105,15 +105,15 @@ pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Optio
                     }
                 }
                 FnCallNode::MethodCallExpr(expr) => {
-                    let function = sema.resolve_method_call(&expr)?;
+                    let function = analyzer.resolve_method_call(&expr)?;
                     Some(function.to_nav(db))
                 }
-                FnCallNode::MacroCallExpr(macro_call) => {
-                    let macro_def = sema.resolve_macro_call(&macro_call)?;
+                FnCallNode::MacroCallExpr(expr) => {
+                    let macro_def = analyzer.resolve_macro_call(db, name_ref.with_value(&expr))?;
                     Some(macro_def.to_nav(db))
                 }
             } {
-                Some((func_target, name_ref.syntax().text_range()))
+                Some((func_target, name_ref.value.text_range()))
             } else {
                 None
             }
diff --git a/crates/ra_ide/src/call_info.rs b/crates/ra_ide/src/call_info.rs
index 9a1fc0d35b..7c6322cb48 100644
--- a/crates/ra_ide/src/call_info.rs
+++ b/crates/ra_ide/src/call_info.rs
@@ -1,5 +1,5 @@
 //! FIXME: write short doc here
-use hir::Semantics;
+use hir::db::AstDatabase;
 use ra_ide_db::RootDatabase;
 use ra_syntax::{
     ast::{self, ArgListOwner},
@@ -7,23 +7,24 @@ use ra_syntax::{
 };
 use test_utils::tested_by;
 
-use crate::{CallInfo, FilePosition, FunctionSignature};
+use crate::{expand::descend_into_macros, CallInfo, FilePosition, FunctionSignature};
 
 /// Computes parameter information for the given call expression.
 pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<CallInfo> {
-    let sema = Semantics::new(db);
-    let file = sema.parse(position.file_id);
-    let file = file.syntax();
+    let file = db.parse_or_expand(position.file_id.into())?;
     let token = file.token_at_offset(position.offset).next()?;
-    let token = sema.descend_into_macros(token);
+    let token = descend_into_macros(db, position.file_id, token);
 
     // Find the calling expression and it's NameRef
-    let calling_node = FnCallNode::with_node(&token.parent())?;
+    let calling_node = FnCallNode::with_node(&token.value.parent())?;
+    let name_ref = calling_node.name_ref()?;
+    let name_ref = token.with_value(name_ref.syntax());
 
+    let analyzer = hir::SourceAnalyzer::new(db, name_ref, None);
     let (mut call_info, has_self) = match &calling_node {
-        FnCallNode::CallExpr(call) => {
+        FnCallNode::CallExpr(expr) => {
             //FIXME: Type::as_callable is broken
-            let callable_def = sema.type_of_expr(&call.expr()?)?.as_callable()?;
+            let callable_def = analyzer.type_of(db, &expr.expr()?)?.as_callable()?;
             match callable_def {
                 hir::CallableDef::FunctionId(it) => {
                     let fn_def = it.into();
@@ -35,12 +36,12 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<Cal
                 }
             }
         }
-        FnCallNode::MethodCallExpr(method_call) => {
-            let function = sema.resolve_method_call(&method_call)?;
+        FnCallNode::MethodCallExpr(expr) => {
+            let function = analyzer.resolve_method_call(&expr)?;
             (CallInfo::with_fn(db, function), function.has_self_param(db))
         }
-        FnCallNode::MacroCallExpr(macro_call) => {
-            let macro_def = sema.resolve_macro_call(&macro_call)?;
+        FnCallNode::MacroCallExpr(expr) => {
+            let macro_def = analyzer.resolve_macro_call(db, name_ref.with_value(&expr))?;
             (CallInfo::with_macro(db, macro_def)?, false)
         }
     };
diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs
index c378c2c627..4bdc6ba232 100644
--- a/crates/ra_ide/src/completion.rs
+++ b/crates/ra_ide/src/completion.rs
@@ -17,6 +17,7 @@ mod complete_postfix;
 mod complete_macro_in_item_position;
 mod complete_trait_impl;
 
+use ra_db::SourceDatabase;
 use ra_ide_db::RootDatabase;
 
 #[cfg(test)]
@@ -56,7 +57,8 @@ pub use crate::completion::completion_item::{
 /// identifier prefix/fuzzy match should be done higher in the stack, together
 /// with ordering of completions (currently this is done by the client).
 pub(crate) fn completions(db: &RootDatabase, position: FilePosition) -> Option<Completions> {
-    let ctx = CompletionContext::new(db, position)?;
+    let original_parse = db.parse(position.file_id);
+    let ctx = CompletionContext::new(db, &original_parse, position)?;
 
     let mut acc = Completions::default();
 
diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs
index a6e0158b2a..2ca78c927f 100644
--- a/crates/ra_ide/src/completion/complete_dot.rs
+++ b/crates/ra_ide/src/completion/complete_dot.rs
@@ -16,7 +16,7 @@ pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
         _ => return,
     };
 
-    let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) {
+    let receiver_ty = match ctx.analyzer.type_of(ctx.db, &dot_receiver) {
         Some(ty) => ty,
         _ => return,
     };
@@ -55,7 +55,7 @@ fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Ty
 fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) {
     if let Some(krate) = ctx.module.map(|it| it.krate()) {
         let mut seen_methods = FxHashSet::default();
-        let traits_in_scope = ctx.scope().traits_in_scope();
+        let traits_in_scope = ctx.analyzer.traits_in_scope(ctx.db);
         receiver.iterate_method_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, func| {
             if func.has_self_param(ctx.db) && seen_methods.insert(func.name(ctx.db)) {
                 acc.add_function(ctx, func);
diff --git a/crates/ra_ide/src/completion/complete_macro_in_item_position.rs b/crates/ra_ide/src/completion/complete_macro_in_item_position.rs
index 1866d9e6c6..faadd1e3f4 100644
--- a/crates/ra_ide/src/completion/complete_macro_in_item_position.rs
+++ b/crates/ra_ide/src/completion/complete_macro_in_item_position.rs
@@ -5,7 +5,7 @@ use crate::completion::{CompletionContext, Completions};
 pub(super) fn complete_macro_in_item_position(acc: &mut Completions, ctx: &CompletionContext) {
     // Show only macros in top level.
     if ctx.is_new_item {
-        ctx.scope().process_all_names(&mut |name, res| {
+        ctx.analyzer.process_all_names(ctx.db, &mut |name, res| {
             if let hir::ScopeDef::MacroDef(mac) = res {
                 acc.add_macro(ctx, Some(name.to_string()), mac);
             }
diff --git a/crates/ra_ide/src/completion/complete_path.rs b/crates/ra_ide/src/completion/complete_path.rs
index c626e90cc8..2d7f09a6c0 100644
--- a/crates/ra_ide/src/completion/complete_path.rs
+++ b/crates/ra_ide/src/completion/complete_path.rs
@@ -11,7 +11,7 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) {
         Some(path) => path.clone(),
         _ => return,
     };
-    let def = match ctx.scope().resolve_hir_path(&path) {
+    let def = match ctx.analyzer.resolve_hir_path(ctx.db, &path) {
         Some(PathResolution::Def(def)) => def,
         _ => return,
     };
@@ -49,7 +49,7 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) {
             // FIXME: complete T::AssocType
             let krate = ctx.module.map(|m| m.krate());
             if let Some(krate) = krate {
-                let traits_in_scope = ctx.scope().traits_in_scope();
+                let traits_in_scope = ctx.analyzer.traits_in_scope(ctx.db);
                 ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| {
                     match item {
                         hir::AssocItem::Function(func) => {
diff --git a/crates/ra_ide/src/completion/complete_pattern.rs b/crates/ra_ide/src/completion/complete_pattern.rs
index c2c6ca002c..fd03b1c405 100644
--- a/crates/ra_ide/src/completion/complete_pattern.rs
+++ b/crates/ra_ide/src/completion/complete_pattern.rs
@@ -9,7 +9,7 @@ pub(super) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
     }
     // FIXME: ideally, we should look at the type we are matching against and
     // suggest variants + auto-imports
-    ctx.scope().process_all_names(&mut |name, res| {
+    ctx.analyzer.process_all_names(ctx.db, &mut |name, res| {
         let def = match &res {
             hir::ScopeDef::ModuleDef(def) => def,
             _ => return,
diff --git a/crates/ra_ide/src/completion/complete_postfix.rs b/crates/ra_ide/src/completion/complete_postfix.rs
index 8a74f993ab..5470dc291f 100644
--- a/crates/ra_ide/src/completion/complete_postfix.rs
+++ b/crates/ra_ide/src/completion/complete_postfix.rs
@@ -29,7 +29,7 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
         dot_receiver.syntax().text().to_string()
     };
 
-    let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) {
+    let receiver_ty = match ctx.analyzer.type_of(ctx.db, &dot_receiver) {
         Some(it) => it,
         None => return,
     };
diff --git a/crates/ra_ide/src/completion/complete_record_literal.rs b/crates/ra_ide/src/completion/complete_record_literal.rs
index f98353d769..577c394d22 100644
--- a/crates/ra_ide/src/completion/complete_record_literal.rs
+++ b/crates/ra_ide/src/completion/complete_record_literal.rs
@@ -5,7 +5,10 @@ use crate::completion::{CompletionContext, Completions};
 /// Complete fields in fields literals.
 pub(super) fn complete_record_literal(acc: &mut Completions, ctx: &CompletionContext) {
     let (ty, variant) = match ctx.record_lit_syntax.as_ref().and_then(|it| {
-        Some((ctx.sema.type_of_expr(&it.clone().into())?, ctx.sema.resolve_record_literal(it)?))
+        Some((
+            ctx.analyzer.type_of(ctx.db, &it.clone().into())?,
+            ctx.analyzer.resolve_record_literal(it)?,
+        ))
     }) {
         Some(it) => it,
         _ => return,
diff --git a/crates/ra_ide/src/completion/complete_record_pattern.rs b/crates/ra_ide/src/completion/complete_record_pattern.rs
index 9bdeae49f9..a56c7e3a1f 100644
--- a/crates/ra_ide/src/completion/complete_record_pattern.rs
+++ b/crates/ra_ide/src/completion/complete_record_pattern.rs
@@ -4,7 +4,10 @@ use crate::completion::{CompletionContext, Completions};
 
 pub(super) fn complete_record_pattern(acc: &mut Completions, ctx: &CompletionContext) {
     let (ty, variant) = match ctx.record_lit_pat.as_ref().and_then(|it| {
-        Some((ctx.sema.type_of_pat(&it.clone().into())?, ctx.sema.resolve_record_pattern(it)?))
+        Some((
+            ctx.analyzer.type_of_pat(ctx.db, &it.clone().into())?,
+            ctx.analyzer.resolve_record_pattern(it)?,
+        ))
     }) {
         Some(it) => it,
         _ => return,
diff --git a/crates/ra_ide/src/completion/complete_scope.rs b/crates/ra_ide/src/completion/complete_scope.rs
index aad016d4ab..e2ee86dd15 100644
--- a/crates/ra_ide/src/completion/complete_scope.rs
+++ b/crates/ra_ide/src/completion/complete_scope.rs
@@ -7,7 +7,9 @@ pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) {
         return;
     }
 
-    ctx.scope().process_all_names(&mut |name, res| acc.add_resolution(ctx, name.to_string(), &res));
+    ctx.analyzer.process_all_names(ctx.db, &mut |name, res| {
+        acc.add_resolution(ctx, name.to_string(), &res)
+    });
 }
 
 #[cfg(test)]
diff --git a/crates/ra_ide/src/completion/complete_trait_impl.rs b/crates/ra_ide/src/completion/complete_trait_impl.rs
index 9a27c164b7..83628e35c2 100644
--- a/crates/ra_ide/src/completion/complete_trait_impl.rs
+++ b/crates/ra_ide/src/completion/complete_trait_impl.rs
@@ -64,12 +64,11 @@ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext
     if let (Some(trigger), Some(impl_block)) = (trigger, impl_block) {
         match trigger.kind() {
             SyntaxKind::FN_DEF => {
-                for missing_fn in
-                    get_missing_impl_items(&ctx.sema, &impl_block).iter().filter_map(|item| {
-                        match item {
-                            hir::AssocItem::Function(fn_item) => Some(fn_item),
-                            _ => None,
-                        }
+                for missing_fn in get_missing_impl_items(ctx.db, &ctx.analyzer, &impl_block)
+                    .iter()
+                    .filter_map(|item| match item {
+                        hir::AssocItem::Function(fn_item) => Some(fn_item),
+                        _ => None,
                     })
                 {
                     add_function_impl(&trigger, acc, ctx, &missing_fn);
@@ -77,12 +76,11 @@ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext
             }
 
             SyntaxKind::TYPE_ALIAS_DEF => {
-                for missing_fn in
-                    get_missing_impl_items(&ctx.sema, &impl_block).iter().filter_map(|item| {
-                        match item {
-                            hir::AssocItem::TypeAlias(type_item) => Some(type_item),
-                            _ => None,
-                        }
+                for missing_fn in get_missing_impl_items(ctx.db, &ctx.analyzer, &impl_block)
+                    .iter()
+                    .filter_map(|item| match item {
+                        hir::AssocItem::TypeAlias(type_item) => Some(type_item),
+                        _ => None,
                     })
                 {
                     add_type_alias_impl(&trigger, acc, ctx, &missing_fn);
@@ -90,12 +88,11 @@ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext
             }
 
             SyntaxKind::CONST_DEF => {
-                for missing_fn in
-                    get_missing_impl_items(&ctx.sema, &impl_block).iter().filter_map(|item| {
-                        match item {
-                            hir::AssocItem::Const(const_item) => Some(const_item),
-                            _ => None,
-                        }
+                for missing_fn in get_missing_impl_items(ctx.db, &ctx.analyzer, &impl_block)
+                    .iter()
+                    .filter_map(|item| match item {
+                        hir::AssocItem::Const(const_item) => Some(const_item),
+                        _ => None,
                     })
                 {
                     add_const_impl(&trigger, acc, ctx, &missing_fn);
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs
index 81321a897f..8678a3234f 100644
--- a/crates/ra_ide/src/completion/completion_context.rs
+++ b/crates/ra_ide/src/completion/completion_context.rs
@@ -1,11 +1,9 @@
 //! FIXME: write short doc here
 
-use hir::{Semantics, SemanticsScope};
-use ra_db::SourceDatabase;
 use ra_ide_db::RootDatabase;
 use ra_syntax::{
     algo::{find_covering_element, find_node_at_offset},
-    ast, AstNode, SourceFile,
+    ast, AstNode, Parse, SourceFile,
     SyntaxKind::*,
     SyntaxNode, SyntaxToken, TextRange, TextUnit,
 };
@@ -17,8 +15,8 @@ use crate::FilePosition;
 /// exactly is the cursor, syntax-wise.
 #[derive(Debug)]
 pub(crate) struct CompletionContext<'a> {
-    pub(super) sema: Semantics<'a, RootDatabase>,
     pub(super) db: &'a RootDatabase,
+    pub(super) analyzer: hir::SourceAnalyzer,
     pub(super) offset: TextUnit,
     pub(super) token: SyntaxToken,
     pub(super) module: Option<hir::Module>,
@@ -53,26 +51,20 @@ pub(crate) struct CompletionContext<'a> {
 impl<'a> CompletionContext<'a> {
     pub(super) fn new(
         db: &'a RootDatabase,
+        original_parse: &'a Parse<ast::SourceFile>,
         position: FilePosition,
     ) -> Option<CompletionContext<'a>> {
-        let sema = Semantics::new(db);
-
-        let original_file = sema.parse(position.file_id);
-
-        // Insert a fake ident to get a valid parse tree. We will use this file
-        // to determine context, though the original_file will be used for
-        // actual completion.
-        let file_with_fake_ident = {
-            let parse = db.parse(position.file_id);
-            let edit = AtomTextEdit::insert(position.offset, "intellijRulezz".to_string());
-            parse.reparse(&edit).tree()
-        };
-
-        let module = sema.to_module_def(position.file_id);
-        let token = original_file.syntax().token_at_offset(position.offset).left_biased()?;
+        let mut sb = hir::SourceBinder::new(db);
+        let module = sb.to_module_def(position.file_id);
+        let token =
+            original_parse.tree().syntax().token_at_offset(position.offset).left_biased()?;
+        let analyzer = sb.analyze(
+            hir::InFile::new(position.file_id.into(), &token.parent()),
+            Some(position.offset),
+        );
         let mut ctx = CompletionContext {
-            sema,
             db,
+            analyzer,
             token,
             offset: position.offset,
             module,
@@ -95,7 +87,7 @@ impl<'a> CompletionContext<'a> {
             has_type_args: false,
             dot_receiver_is_ambiguous_float_literal: false,
         };
-        ctx.fill(&original_file, file_with_fake_ident, position.offset);
+        ctx.fill(&original_parse, position.offset);
         Some(ctx)
     }
 
@@ -108,33 +100,29 @@ impl<'a> CompletionContext<'a> {
         }
     }
 
-    pub(crate) fn scope(&self) -> SemanticsScope<'_, RootDatabase> {
-        self.sema.scope_at_offset(&self.token.parent(), self.offset)
-    }
+    fn fill(&mut self, original_parse: &'a Parse<ast::SourceFile>, offset: TextUnit) {
+        // Insert a fake ident to get a valid parse tree. We will use this file
+        // to determine context, though the original_file will be used for
+        // actual completion.
+        let file = {
+            let edit = AtomTextEdit::insert(offset, "intellijRulezz".to_string());
+            original_parse.reparse(&edit).tree()
+        };
 
-    fn fill(
-        &mut self,
-        original_file: &ast::SourceFile,
-        file_with_fake_ident: ast::SourceFile,
-        offset: TextUnit,
-    ) {
         // First, let's try to complete a reference to some declaration.
-        if let Some(name_ref) =
-            find_node_at_offset::<ast::NameRef>(file_with_fake_ident.syntax(), offset)
-        {
+        if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(file.syntax(), offset) {
             // Special case, `trait T { fn foo(i_am_a_name_ref) {} }`.
             // See RFC#1685.
             if is_node::<ast::Param>(name_ref.syntax()) {
                 self.is_param = true;
                 return;
             }
-            self.classify_name_ref(original_file, name_ref);
+            self.classify_name_ref(original_parse.tree(), name_ref);
         }
 
         // Otherwise, see if this is a declaration. We can use heuristics to
         // suggest declaration names, see `CompletionKind::Magic`.
-        if let Some(name) = find_node_at_offset::<ast::Name>(file_with_fake_ident.syntax(), offset)
-        {
+        if let Some(name) = find_node_at_offset::<ast::Name>(file.syntax(), offset) {
             if let Some(bind_pat) = name.syntax().ancestors().find_map(ast::BindPat::cast) {
                 let parent = bind_pat.syntax().parent();
                 if parent.clone().and_then(ast::MatchArm::cast).is_some()
@@ -148,12 +136,13 @@ impl<'a> CompletionContext<'a> {
                 return;
             }
             if name.syntax().ancestors().find_map(ast::RecordFieldPatList::cast).is_some() {
-                self.record_lit_pat = find_node_at_offset(original_file.syntax(), self.offset);
+                self.record_lit_pat =
+                    find_node_at_offset(original_parse.tree().syntax(), self.offset);
             }
         }
     }
 
-    fn classify_name_ref(&mut self, original_file: &SourceFile, name_ref: ast::NameRef) {
+    fn classify_name_ref(&mut self, original_file: SourceFile, name_ref: ast::NameRef) {
         self.name_ref_syntax =
             find_node_at_offset(original_file.syntax(), name_ref.syntax().text_range().start());
         let name_range = name_ref.syntax().text_range();
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs
index a52f7fdd9f..9cf86b26d8 100644
--- a/crates/ra_ide/src/diagnostics.rs
+++ b/crates/ra_ide/src/diagnostics.rs
@@ -2,10 +2,7 @@
 
 use std::cell::RefCell;
 
-use hir::{
-    diagnostics::{AstDiagnostic, Diagnostic as _, DiagnosticSink},
-    Semantics,
-};
+use hir::diagnostics::{AstDiagnostic, Diagnostic as _, DiagnosticSink};
 use itertools::Itertools;
 use ra_db::{RelativePath, SourceDatabase, SourceDatabaseExt};
 use ra_ide_db::RootDatabase;
@@ -27,7 +24,7 @@ pub enum Severity {
 
 pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic> {
     let _p = profile("diagnostics");
-    let sema = Semantics::new(db);
+    let mut sb = hir::SourceBinder::new(db);
     let parse = db.parse(file_id);
     let mut res = Vec::new();
 
@@ -113,7 +110,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
             fix: Some(fix),
         })
     });
-    if let Some(m) = sema.to_module_def(file_id) {
+    if let Some(m) = sb.to_module_def(file_id) {
         m.diagnostics(db, &mut sink);
     };
     drop(sink);
diff --git a/crates/ra_ide/src/display/navigation_target.rs b/crates/ra_ide/src/display/navigation_target.rs
index 5afb23764e..c9d0058a62 100644
--- a/crates/ra_ide/src/display/navigation_target.rs
+++ b/crates/ra_ide/src/display/navigation_target.rs
@@ -1,7 +1,7 @@
 //! FIXME: write short doc here
 
 use either::Either;
-use hir::{original_range, AssocItem, FieldSource, HasSource, InFile, ModuleSource};
+use hir::{AssocItem, FieldSource, HasSource, InFile, ModuleSource};
 use ra_db::{FileId, SourceDatabase};
 use ra_ide_db::RootDatabase;
 use ra_syntax::{
@@ -11,11 +11,7 @@ use ra_syntax::{
     TextRange,
 };
 
-use crate::{
-    // expand::original_range,
-    references::NameDefinition,
-    FileSymbol,
-};
+use crate::{expand::original_range, references::NameDefinition, FileSymbol};
 
 use super::short_label::ShortLabel;
 
diff --git a/crates/ra_ide/src/expand.rs b/crates/ra_ide/src/expand.rs
new file mode 100644
index 0000000000..9f3aaa3a3f
--- /dev/null
+++ b/crates/ra_ide/src/expand.rs
@@ -0,0 +1,102 @@
+//! Utilities to work with files, produced by macros.
+use std::iter::successors;
+
+use hir::{InFile, Origin};
+use ra_db::FileId;
+use ra_ide_db::RootDatabase;
+use ra_syntax::{ast, AstNode, SyntaxNode, SyntaxToken, TextRange};
+
+use crate::FileRange;
+
+pub(crate) fn original_range(db: &RootDatabase, node: InFile<&SyntaxNode>) -> FileRange {
+    if let Some((range, Origin::Call)) = original_range_and_origin(db, node) {
+        return range;
+    }
+
+    if let Some(expansion) = node.file_id.expansion_info(db) {
+        if let Some(call_node) = expansion.call_node() {
+            return FileRange {
+                file_id: call_node.file_id.original_file(db),
+                range: call_node.value.text_range(),
+            };
+        }
+    }
+
+    FileRange { file_id: node.file_id.original_file(db), range: node.value.text_range() }
+}
+
+fn original_range_and_origin(
+    db: &RootDatabase,
+    node: InFile<&SyntaxNode>,
+) -> Option<(FileRange, Origin)> {
+    let expansion = node.file_id.expansion_info(db)?;
+
+    // the input node has only one token ?
+    let single = node.value.first_token()? == node.value.last_token()?;
+
+    // FIXME: We should handle recurside macro expansions
+    let (range, origin) = node.value.descendants().find_map(|it| {
+        let first = it.first_token()?;
+        let last = it.last_token()?;
+
+        if !single && first == last {
+            return None;
+        }
+
+        // Try to map first and last tokens of node, and, if success, return the union range of mapped tokens
+        let (first, first_origin) = expansion.map_token_up(node.with_value(&first))?;
+        let (last, last_origin) = expansion.map_token_up(node.with_value(&last))?;
+
+        if first.file_id != last.file_id || first_origin != last_origin {
+            return None;
+        }
+
+        // FIXME: Add union method in TextRange
+        Some((
+            first.with_value(union_range(first.value.text_range(), last.value.text_range())),
+            first_origin,
+        ))
+    })?;
+
+    return Some((
+        FileRange { file_id: range.file_id.original_file(db), range: range.value },
+        origin,
+    ));
+
+    fn union_range(a: TextRange, b: TextRange) -> TextRange {
+        let start = a.start().min(b.start());
+        let end = a.end().max(b.end());
+        TextRange::from_to(start, end)
+    }
+}
+
+pub(crate) fn descend_into_macros(
+    db: &RootDatabase,
+    file_id: FileId,
+    token: SyntaxToken,
+) -> InFile<SyntaxToken> {
+    let src = InFile::new(file_id.into(), token);
+
+    let source_analyzer =
+        hir::SourceAnalyzer::new(db, src.with_value(src.value.parent()).as_ref(), None);
+
+    descend_into_macros_with_analyzer(db, &source_analyzer, src)
+}
+
+pub(crate) fn descend_into_macros_with_analyzer(
+    db: &RootDatabase,
+    source_analyzer: &hir::SourceAnalyzer,
+    src: InFile<SyntaxToken>,
+) -> InFile<SyntaxToken> {
+    successors(Some(src), |token| {
+        let macro_call = token.value.ancestors().find_map(ast::MacroCall::cast)?;
+        let tt = macro_call.token_tree()?;
+        if !token.value.text_range().is_subrange(&tt.syntax().text_range()) {
+            return None;
+        }
+        let exp = source_analyzer.expand(db, token.with_value(&macro_call))?;
+        exp.map_token_down(db, token.as_ref())
+    })
+    .last()
+    .unwrap()
+}
diff --git a/crates/ra_ide/src/expand_macro.rs b/crates/ra_ide/src/expand_macro.rs
index f2814e6842..af2783befb 100644
--- a/crates/ra_ide/src/expand_macro.rs
+++ b/crates/ra_ide/src/expand_macro.rs
@@ -1,6 +1,7 @@
 //! This modules implements "expand macro" functionality in the IDE
 
-use hir::Semantics;
+use hir::db::AstDatabase;
+use ra_db::SourceDatabase;
 use ra_ide_db::RootDatabase;
 use ra_syntax::{
     algo::{find_node_at_offset, replace_descendants},
@@ -16,12 +17,13 @@ pub struct ExpandedMacro {
 }
 
 pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<ExpandedMacro> {
-    let sema = Semantics::new(db);
-    let file = sema.parse(position.file_id);
+    let parse = db.parse(position.file_id);
+    let file = parse.tree();
     let name_ref = find_node_at_offset::<ast::NameRef>(file.syntax(), position.offset)?;
     let mac = name_ref.syntax().ancestors().find_map(ast::MacroCall::cast)?;
 
-    let expanded = expand_macro_recur(&sema, &mac)?;
+    let source = hir::InFile::new(position.file_id.into(), mac.syntax());
+    let expanded = expand_macro_recur(db, source, source.with_value(&mac))?;
 
     // FIXME:
     // macro expansion may lose all white space information
@@ -31,16 +33,21 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<
 }
 
 fn expand_macro_recur(
-    sema: &Semantics<RootDatabase>,
-    macro_call: &ast::MacroCall,
+    db: &RootDatabase,
+    source: hir::InFile<&SyntaxNode>,
+    macro_call: hir::InFile<&ast::MacroCall>,
 ) -> Option<SyntaxNode> {
-    let mut expanded = sema.expand(macro_call)?;
+    let analyzer = hir::SourceAnalyzer::new(db, source, None);
+    let expansion = analyzer.expand(db, macro_call)?;
+    let macro_file_id = expansion.file_id();
+    let mut expanded: SyntaxNode = db.parse_or_expand(macro_file_id)?;
 
     let children = expanded.descendants().filter_map(ast::MacroCall::cast);
     let mut replaces: FxHashMap<SyntaxElement, SyntaxElement> = FxHashMap::default();
 
     for child in children.into_iter() {
-        if let Some(new_node) = expand_macro_recur(sema, &child) {
+        let node = hir::InFile::new(macro_file_id, &child);
+        if let Some(new_node) = expand_macro_recur(db, source, node) {
             // Replace the whole node if it is root
             // `replace_descendants` will not replace the parent node
             // but `SyntaxNode::descendants include itself
@@ -113,11 +120,9 @@ fn insert_whitespaces(syn: SyntaxNode) -> String {
 
 #[cfg(test)]
 mod tests {
-    use insta::assert_snapshot;
-
-    use crate::mock_analysis::analysis_and_position;
-
     use super::*;
+    use crate::mock_analysis::analysis_and_position;
+    use insta::assert_snapshot;
 
     fn check_expand_macro(fixture: &str) -> ExpandedMacro {
         let (analysis, pos) = analysis_and_position(fixture);
diff --git a/crates/ra_ide/src/extend_selection.rs b/crates/ra_ide/src/extend_selection.rs
index 2e09bd1ec3..1e7d0621a6 100644
--- a/crates/ra_ide/src/extend_selection.rs
+++ b/crates/ra_ide/src/extend_selection.rs
@@ -2,26 +2,26 @@
 
 use std::iter::successors;
 
-use hir::Semantics;
+use hir::db::AstDatabase;
+use ra_db::SourceDatabase;
 use ra_ide_db::RootDatabase;
 use ra_syntax::{
-    algo::{self, find_covering_element, skip_trivia_token},
+    algo::find_covering_element,
     ast::{self, AstNode, AstToken},
-    Direction, NodeOrToken,
+    Direction, NodeOrToken, SyntaxElement,
     SyntaxKind::{self, *},
     SyntaxNode, SyntaxToken, TextRange, TextUnit, TokenAtOffset, T,
 };
 
-use crate::FileRange;
+use crate::{expand::descend_into_macros, FileId, FileRange};
 
 pub(crate) fn extend_selection(db: &RootDatabase, frange: FileRange) -> TextRange {
-    let sema = Semantics::new(db);
-    let src = sema.parse(frange.file_id);
-    try_extend_selection(&sema, src.syntax(), frange).unwrap_or(frange.range)
+    let src = db.parse(frange.file_id).tree();
+    try_extend_selection(db, src.syntax(), frange).unwrap_or(frange.range)
 }
 
 fn try_extend_selection(
-    sema: &Semantics<RootDatabase>,
+    db: &RootDatabase,
     root: &SyntaxNode,
     frange: FileRange,
 ) -> Option<TextRange> {
@@ -86,7 +86,7 @@ fn try_extend_selection(
     // if we are in single token_tree, we maybe live in macro or attr
     if node.kind() == TOKEN_TREE {
         if let Some(macro_call) = node.ancestors().find_map(ast::MacroCall::cast) {
-            if let Some(range) = extend_tokens_from_range(sema, macro_call, range) {
+            if let Some(range) = extend_tokens_from_range(db, frange.file_id, macro_call, range) {
                 return Some(range);
             }
         }
@@ -96,7 +96,7 @@ fn try_extend_selection(
         return Some(node.text_range());
     }
 
-    let node = shallowest_node(&node.into());
+    let node = shallowest_node(&node.into()).unwrap();
 
     if node.parent().map(|n| list_kinds.contains(&n.kind())) == Some(true) {
         if let Some(range) = extend_list_item(&node) {
@@ -108,7 +108,8 @@ fn try_extend_selection(
 }
 
 fn extend_tokens_from_range(
-    sema: &Semantics<RootDatabase>,
+    db: &RootDatabase,
+    file_id: FileId,
     macro_call: ast::MacroCall,
     original_range: TextRange,
 ) -> Option<TextRange> {
@@ -118,45 +119,49 @@ fn extend_tokens_from_range(
         NodeOrToken::Token(it) => (it.clone(), it),
     };
 
-    let mut first_token = skip_trivia_token(first_token, Direction::Next)?;
-    let mut last_token = skip_trivia_token(last_token, Direction::Prev)?;
+    let mut first_token = skip_whitespace(first_token, Direction::Next)?;
+    let mut last_token = skip_whitespace(last_token, Direction::Prev)?;
 
     while !first_token.text_range().is_subrange(&original_range) {
-        first_token = skip_trivia_token(first_token.next_token()?, Direction::Next)?;
+        first_token = skip_whitespace(first_token.next_token()?, Direction::Next)?;
     }
     while !last_token.text_range().is_subrange(&original_range) {
-        last_token = skip_trivia_token(last_token.prev_token()?, Direction::Prev)?;
+        last_token = skip_whitespace(last_token.prev_token()?, Direction::Prev)?;
     }
 
     // compute original mapped token range
-    let extended = {
-        let fst_expanded = sema.descend_into_macros(first_token.clone());
-        let lst_expanded = sema.descend_into_macros(last_token.clone());
-        let mut lca = algo::least_common_ancestor(&fst_expanded.parent(), &lst_expanded.parent())?;
-        lca = shallowest_node(&lca);
-        if lca.first_token() == Some(fst_expanded) && lca.last_token() == Some(lst_expanded) {
-            lca = lca.parent()?;
+    let expanded = {
+        let first_node = descend_into_macros(db, file_id, first_token.clone());
+        let first_node = first_node.map(|it| it.text_range());
+
+        let last_node = descend_into_macros(db, file_id, last_token.clone());
+        if last_node.file_id == file_id.into() || first_node.file_id != last_node.file_id {
+            return None;
         }
-        lca
+        first_node.map(|it| union_range(it, last_node.value.text_range()))
     };
 
     // Compute parent node range
+    let src = db.parse_or_expand(expanded.file_id)?;
+    let parent = shallowest_node(&find_covering_element(&src, expanded.value))?.parent()?;
+
     let validate = |token: &SyntaxToken| {
-        let expanded = sema.descend_into_macros(token.clone());
-        algo::least_common_ancestor(&extended, &expanded.parent()).as_ref() == Some(&extended)
+        let node = descend_into_macros(db, file_id, token.clone());
+        node.file_id == expanded.file_id
+            && node.value.text_range().is_subrange(&parent.text_range())
     };
 
     // Find the first and last text range under expanded parent
     let first = successors(Some(first_token), |token| {
         let token = token.prev_token()?;
-        skip_trivia_token(token, Direction::Prev)
+        skip_whitespace(token, Direction::Prev)
     })
     .take_while(validate)
     .last()?;
 
     let last = successors(Some(last_token), |token| {
         let token = token.next_token()?;
-        skip_trivia_token(token, Direction::Next)
+        skip_whitespace(token, Direction::Next)
     })
     .take_while(validate)
     .last()?;
@@ -169,6 +174,16 @@ fn extend_tokens_from_range(
     }
 }
 
+fn skip_whitespace(mut token: SyntaxToken, direction: Direction) -> Option<SyntaxToken> {
+    while token.kind() == WHITESPACE {
+        token = match direction {
+            Direction::Next => token.next_token()?,
+            Direction::Prev => token.prev_token()?,
+        }
+    }
+    Some(token)
+}
+
 fn union_range(range: TextRange, r: TextRange) -> TextRange {
     let start = range.start().min(r.start());
     let end = range.end().max(r.end());
@@ -176,8 +191,8 @@ fn union_range(range: TextRange, r: TextRange) -> TextRange {
 }
 
 /// Find the shallowest node with same range, which allows us to traverse siblings.
-fn shallowest_node(node: &SyntaxNode) -> SyntaxNode {
-    node.ancestors().take_while(|n| n.text_range() == node.text_range()).last().unwrap()
+fn shallowest_node(node: &SyntaxElement) -> Option<SyntaxNode> {
+    node.ancestors().take_while(|n| n.text_range() == node.text_range()).last()
 }
 
 fn extend_single_word_in_comment_or_string(
diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs
index 6053c1bb6a..feff1ec3f6 100644
--- a/crates/ra_ide/src/goto_definition.rs
+++ b/crates/ra_ide/src/goto_definition.rs
@@ -1,7 +1,7 @@
 //! FIXME: write short doc here
 
-use hir::Semantics;
-use ra_ide_db::{defs::classify_name, symbol_index, RootDatabase};
+use hir::{db::AstDatabase, InFile, SourceBinder};
+use ra_ide_db::{symbol_index, RootDatabase};
 use ra_syntax::{
     ast::{self},
     match_ast, AstNode,
@@ -11,7 +11,8 @@ use ra_syntax::{
 
 use crate::{
     display::{ToNav, TryToNav},
-    references::classify_name_ref,
+    expand::descend_into_macros,
+    references::{classify_name, classify_name_ref},
     FilePosition, NavigationTarget, RangeInfo,
 };
 
@@ -19,18 +20,18 @@ pub(crate) fn goto_definition(
     db: &RootDatabase,
     position: FilePosition,
 ) -> Option<RangeInfo<Vec<NavigationTarget>>> {
-    let sema = Semantics::new(db);
-    let file = sema.parse(position.file_id).syntax().clone();
+    let file = db.parse_or_expand(position.file_id.into())?;
     let original_token = pick_best(file.token_at_offset(position.offset))?;
-    let token = sema.descend_into_macros(original_token.clone());
+    let token = descend_into_macros(db, position.file_id, original_token.clone());
 
+    let mut sb = SourceBinder::new(db);
     let nav_targets = match_ast! {
-        match (token.parent()) {
+        match (token.value.parent()) {
             ast::NameRef(name_ref) => {
-                reference_definition(&sema, &name_ref).to_vec()
+                reference_definition(&mut sb, token.with_value(&name_ref)).to_vec()
             },
             ast::Name(name) => {
-                name_definition(&sema, &name)?
+                name_definition(&mut sb, token.with_value(&name))?
             },
             _ => return None,
         }
@@ -67,33 +68,33 @@ impl ReferenceResult {
 }
 
 pub(crate) fn reference_definition(
-    sema: &Semantics<RootDatabase>,
-    name_ref: &ast::NameRef,
+    sb: &mut SourceBinder<RootDatabase>,
+    name_ref: InFile<&ast::NameRef>,
 ) -> ReferenceResult {
     use self::ReferenceResult::*;
 
-    let name_kind = classify_name_ref(sema, name_ref);
+    let name_kind = classify_name_ref(sb, name_ref);
     if let Some(def) = name_kind {
-        return match def.try_to_nav(sema.db) {
+        return match def.try_to_nav(sb.db) {
             Some(nav) => ReferenceResult::Exact(nav),
             None => ReferenceResult::Approximate(Vec::new()),
         };
     }
 
     // Fallback index based approach:
-    let navs = symbol_index::index_resolve(sema.db, name_ref)
+    let navs = symbol_index::index_resolve(sb.db, name_ref.value)
         .into_iter()
-        .map(|s| s.to_nav(sema.db))
+        .map(|s| s.to_nav(sb.db))
         .collect();
     Approximate(navs)
 }
 
 fn name_definition(
-    sema: &Semantics<RootDatabase>,
-    name: &ast::Name,
+    sb: &mut SourceBinder<RootDatabase>,
+    name: InFile<&ast::Name>,
 ) -> Option<Vec<NavigationTarget>> {
-    let def = classify_name(sema, name)?;
-    let nav = def.try_to_nav(sema.db)?;
+    let def = classify_name(sb, name)?;
+    let nav = def.try_to_nav(sb.db)?;
     Some(vec![nav])
 }
 
diff --git a/crates/ra_ide/src/goto_type_definition.rs b/crates/ra_ide/src/goto_type_definition.rs
index 869a4708b7..69940fc36a 100644
--- a/crates/ra_ide/src/goto_type_definition.rs
+++ b/crates/ra_ide/src/goto_type_definition.rs
@@ -1,31 +1,31 @@
 //! FIXME: write short doc here
 
+use hir::db::AstDatabase;
 use ra_ide_db::RootDatabase;
-use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset};
+use ra_syntax::{ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset};
 
-use crate::{display::ToNav, FilePosition, NavigationTarget, RangeInfo};
+use crate::{
+    display::ToNav, expand::descend_into_macros, FilePosition, NavigationTarget, RangeInfo,
+};
 
 pub(crate) fn goto_type_definition(
     db: &RootDatabase,
     position: FilePosition,
 ) -> Option<RangeInfo<Vec<NavigationTarget>>> {
-    let sema = hir::Semantics::new(db);
-
-    let file: ast::SourceFile = sema.parse(position.file_id);
-    let token: SyntaxToken = pick_best(file.syntax().token_at_offset(position.offset))?;
-    let token: SyntaxToken = sema.descend_into_macros(token);
-
-    let (ty, node) = sema.ancestors_with_macros(token.parent()).find_map(|node| {
-        let ty = match_ast! {
-            match node {
-                ast::Expr(expr) => { sema.type_of_expr(&expr)? },
-                ast::Pat(pat) => { sema.type_of_pat(&pat)? },
-                _ => { return None },
-            }
-        };
+    let file = db.parse_or_expand(position.file_id.into())?;
+    let token = pick_best(file.token_at_offset(position.offset))?;
+    let token = descend_into_macros(db, position.file_id, token);
+
+    let node = token
+        .value
+        .ancestors()
+        .find(|n| ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some())?;
+
+    let analyzer = hir::SourceAnalyzer::new(db, token.with_value(&node), None);
 
-        Some((ty, node))
-    })?;
+    let ty: hir::Type = ast::Expr::cast(node.clone())
+        .and_then(|e| analyzer.type_of(db, &e))
+        .or_else(|| ast::Pat::cast(node.clone()).and_then(|p| analyzer.type_of_pat(db, &p)))?;
 
     let adt_def = ty.autoderef(db).find_map(|ty| ty.as_adt())?;
 
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs
index 177038e206..1c6ca36df1 100644
--- a/crates/ra_ide/src/hover.rs
+++ b/crates/ra_ide/src/hover.rs
@@ -1,10 +1,8 @@
 //! FIXME: write short doc here
 
-use hir::{Adt, HasSource, HirDisplay, Semantics};
-use ra_ide_db::{
-    defs::{classify_name, NameDefinition},
-    RootDatabase,
-};
+use hir::{db::AstDatabase, Adt, HasSource, HirDisplay, SourceBinder};
+use ra_db::SourceDatabase;
+use ra_ide_db::{defs::NameDefinition, RootDatabase};
 use ra_syntax::{
     algo::find_covering_element,
     ast::{self, DocCommentsOwner},
@@ -15,7 +13,8 @@ use ra_syntax::{
 
 use crate::{
     display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel},
-    references::classify_name_ref,
+    expand::{descend_into_macros, original_range},
+    references::{classify_name, classify_name_ref},
     FilePosition, FileRange, RangeInfo,
 };
 
@@ -144,25 +143,25 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: NameDefinition) -> Option<S
 }
 
 pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<HoverResult>> {
-    let sema = Semantics::new(db);
-    let file = sema.parse(position.file_id).syntax().clone();
+    let file = db.parse_or_expand(position.file_id.into())?;
     let token = pick_best(file.token_at_offset(position.offset))?;
-    let token = sema.descend_into_macros(token);
+    let token = descend_into_macros(db, position.file_id, token);
 
     let mut res = HoverResult::new();
 
+    let mut sb = SourceBinder::new(db);
     if let Some((node, name_kind)) = match_ast! {
-        match (token.parent()) {
+        match (token.value.parent()) {
             ast::NameRef(name_ref) => {
-                classify_name_ref(&sema, &name_ref).map(|d| (name_ref.syntax().clone(), d))
+                classify_name_ref(&mut sb, token.with_value(&name_ref)).map(|d| (name_ref.syntax().clone(), d))
             },
             ast::Name(name) => {
-                classify_name(&sema, &name).map(|d| (name.syntax().clone(), d))
+                classify_name(&mut sb, token.with_value(&name)).map(|d| (name.syntax().clone(), d))
             },
             _ => None,
         }
     } {
-        let range = sema.original_range(&node).range;
+        let range = original_range(db, token.with_value(&node)).range;
         res.extend(hover_text_from_name_kind(db, name_kind));
 
         if !res.is_empty() {
@@ -171,14 +170,11 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
     }
 
     let node = token
+        .value
         .ancestors()
         .find(|n| ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some())?;
 
-    // FIXME: Currently `hover::typeof` do not work inside
-    // macro expansion such that if the hover range is pointing to
-    // a string literal, the following type_of will return None.
-    // See also `test_hover_through_literal_string_in_macro`
-    let frange = sema.original_range(&node);
+    let frange = original_range(db, token.with_value(&node));
     res.extend(type_of(db, frange).map(rust_code_markup));
     if res.is_empty() {
         return None;
@@ -201,17 +197,19 @@ fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
 }
 
 pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Option<String> {
-    let sema = Semantics::new(db);
-    let source_file = sema.parse(frange.file_id);
-    let leaf_node = find_covering_element(source_file.syntax(), frange.range);
+    let parse = db.parse(frange.file_id);
+    let leaf_node = find_covering_element(parse.tree().syntax(), frange.range);
     // if we picked identifier, expand to pattern/expression
     let node = leaf_node
         .ancestors()
         .take_while(|it| it.text_range() == leaf_node.text_range())
         .find(|it| ast::Expr::cast(it.clone()).is_some() || ast::Pat::cast(it.clone()).is_some())?;
-    let ty = if let Some(ty) = ast::Expr::cast(node.clone()).and_then(|e| sema.type_of_expr(&e)) {
+    let analyzer =
+        hir::SourceAnalyzer::new(db, hir::InFile::new(frange.file_id.into(), &node), None);
+    let ty = if let Some(ty) = ast::Expr::cast(node.clone()).and_then(|e| analyzer.type_of(db, &e))
+    {
         ty
-    } else if let Some(ty) = ast::Pat::cast(node).and_then(|p| sema.type_of_pat(&p)) {
+    } else if let Some(ty) = ast::Pat::cast(node).and_then(|p| analyzer.type_of_pat(db, &p)) {
         ty
     } else {
         return None;
@@ -221,12 +219,11 @@ pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Option<String> {
 
 #[cfg(test)]
 mod tests {
-    use ra_db::FileLoader;
-    use ra_syntax::TextRange;
-
     use crate::mock_analysis::{
         analysis_and_position, single_file_with_position, single_file_with_range,
     };
+    use ra_db::FileLoader;
+    use ra_syntax::TextRange;
 
     fn trim_markup(s: &str) -> &str {
         s.trim_start_matches("```rust\n").trim_end_matches("\n```")
@@ -254,11 +251,6 @@ mod tests {
         content[hover.range].to_string()
     }
 
-    fn check_hover_no_result(fixture: &str) {
-        let (analysis, position) = analysis_and_position(fixture);
-        assert!(analysis.hover(position).unwrap().is_none());
-    }
-
     #[test]
     fn hover_shows_type_of_an_expression() {
         let (analysis, position) = single_file_with_position(
@@ -762,45 +754,6 @@ fn func(foo: i32) { if true { <|>foo; }; }
         assert_eq!(hover_on, "bar")
     }
 
-    #[test]
-    fn test_hover_through_expr_in_macro_recursive() {
-        let hover_on = check_hover_result(
-            "
-            //- /lib.rs
-            macro_rules! id_deep {
-                ($($tt:tt)*) => { $($tt)* }
-            }
-            macro_rules! id {
-                ($($tt:tt)*) => { id_deep!($($tt)*) }
-            }
-            fn foo(bar:u32) {
-                let a = id!(ba<|>r);
-            }
-            ",
-            &["u32"],
-        );
-
-        assert_eq!(hover_on, "bar")
-    }
-
-    #[test]
-    fn test_hover_through_literal_string_in_macro() {
-        // FIXME: Currently `hover::type_of` do not work inside
-        // macro expansion
-        check_hover_no_result(
-            r#"
-            //- /lib.rs
-            macro_rules! arr {
-                ($($tt:tt)*) => { [$($tt)*)] }
-            }
-            fn foo() {
-                let mastered_for_itunes = "";
-                let _ = arr!("Tr<|>acks", &mastered_for_itunes);
-            }
-            "#,
-        );
-    }
-
     #[test]
     fn test_hover_non_ascii_space_doc() {
         check_hover_result(
diff --git a/crates/ra_ide/src/impls.rs b/crates/ra_ide/src/impls.rs
index bf82b2a168..64a2dadc85 100644
--- a/crates/ra_ide/src/impls.rs
+++ b/crates/ra_ide/src/impls.rs
@@ -1,6 +1,7 @@
 //! FIXME: write short doc here
 
-use hir::{Crate, ImplBlock, Semantics};
+use hir::{Crate, ImplBlock, SourceBinder};
+use ra_db::SourceDatabase;
 use ra_ide_db::RootDatabase;
 use ra_syntax::{algo::find_node_at_offset, ast, AstNode};
 
@@ -10,21 +11,21 @@ pub(crate) fn goto_implementation(
     db: &RootDatabase,
     position: FilePosition,
 ) -> Option<RangeInfo<Vec<NavigationTarget>>> {
-    let sema = Semantics::new(db);
-    let source_file = sema.parse(position.file_id);
-    let syntax = source_file.syntax().clone();
+    let parse = db.parse(position.file_id);
+    let syntax = parse.tree().syntax().clone();
+    let mut sb = SourceBinder::new(db);
 
-    let krate = sema.to_module_def(position.file_id)?.krate();
+    let krate = sb.to_module_def(position.file_id)?.krate();
 
     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(&sema, &nominal_def, krate)?,
+            impls_for_def(&mut sb, position, &nominal_def, krate)?,
         ));
     } 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(&sema, &trait_def, krate)?,
+            impls_for_trait(&mut sb, position, &trait_def, krate)?,
         ));
     }
 
@@ -32,37 +33,49 @@ pub(crate) fn goto_implementation(
 }
 
 fn impls_for_def(
-    sema: &Semantics<RootDatabase>,
+    sb: &mut SourceBinder<RootDatabase>,
+    position: FilePosition,
     node: &ast::NominalDef,
     krate: Crate,
 ) -> Option<Vec<NavigationTarget>> {
     let ty = match node {
-        ast::NominalDef::StructDef(def) => sema.to_def(def)?.ty(sema.db),
-        ast::NominalDef::EnumDef(def) => sema.to_def(def)?.ty(sema.db),
-        ast::NominalDef::UnionDef(def) => sema.to_def(def)?.ty(sema.db),
+        ast::NominalDef::StructDef(def) => {
+            let src = hir::InFile { file_id: position.file_id.into(), value: def.clone() };
+            sb.to_def(src)?.ty(sb.db)
+        }
+        ast::NominalDef::EnumDef(def) => {
+            let src = hir::InFile { file_id: position.file_id.into(), value: def.clone() };
+            sb.to_def(src)?.ty(sb.db)
+        }
+        ast::NominalDef::UnionDef(def) => {
+            let src = hir::InFile { file_id: position.file_id.into(), value: def.clone() };
+            sb.to_def(src)?.ty(sb.db)
+        }
     };
 
-    let impls = ImplBlock::all_in_crate(sema.db, krate);
+    let impls = ImplBlock::all_in_crate(sb.db, krate);
 
     Some(
         impls
             .into_iter()
-            .filter(|impl_block| ty.is_equal_for_find_impls(&impl_block.target_ty(sema.db)))
-            .map(|imp| imp.to_nav(sema.db))
+            .filter(|impl_block| ty.is_equal_for_find_impls(&impl_block.target_ty(sb.db)))
+            .map(|imp| imp.to_nav(sb.db))
             .collect(),
     )
 }
 
 fn impls_for_trait(
-    sema: &Semantics<RootDatabase>,
+    sb: &mut SourceBinder<RootDatabase>,
+    position: FilePosition,
     node: &ast::TraitDef,
     krate: Crate,
 ) -> Option<Vec<NavigationTarget>> {
-    let tr = sema.to_def(node)?;
+    let src = hir::InFile { file_id: position.file_id.into(), value: node.clone() };
+    let tr = sb.to_def(src)?;
 
-    let impls = ImplBlock::for_trait(sema.db, krate, tr);
+    let impls = ImplBlock::for_trait(sb.db, krate, tr);
 
-    Some(impls.into_iter().map(|imp| imp.to_nav(sema.db)).collect())
+    Some(impls.into_iter().map(|imp| imp.to_nav(sb.db)).collect())
 }
 
 #[cfg(test)]
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs
index 35e3f782d1..b42aa15235 100644
--- a/crates/ra_ide/src/inlay_hints.rs
+++ b/crates/ra_ide/src/inlay_hints.rs
@@ -1,11 +1,12 @@
 //! FIXME: write short doc here
 
-use hir::{Adt, HirDisplay, Semantics, Type};
+use hir::{Adt, HirDisplay, SourceAnalyzer, SourceBinder, Type};
+use once_cell::unsync::Lazy;
 use ra_ide_db::RootDatabase;
 use ra_prof::profile;
 use ra_syntax::{
     ast::{self, ArgListOwner, AstNode, TypeAscriptionOwner},
-    match_ast, SmolStr, SyntaxNode, TextRange,
+    match_ast, SmolStr, SourceFile, SyntaxNode, TextRange,
 };
 
 use crate::{FileId, FunctionSignature};
@@ -26,36 +27,38 @@ pub struct InlayHint {
 pub(crate) fn inlay_hints(
     db: &RootDatabase,
     file_id: FileId,
+    file: &SourceFile,
     max_inlay_hint_length: Option<usize>,
 ) -> Vec<InlayHint> {
-    let sema = Semantics::new(db);
-    let file = sema.parse(file_id);
+    let mut sb = SourceBinder::new(db);
     let mut res = Vec::new();
     for node in file.syntax().descendants() {
-        get_inlay_hints(&mut res, &sema, &node, max_inlay_hint_length);
+        get_inlay_hints(&mut res, &mut sb, file_id, &node, max_inlay_hint_length);
     }
     res
 }
 
 fn get_inlay_hints(
     acc: &mut Vec<InlayHint>,
-    sema: &Semantics<RootDatabase>,
+    sb: &mut SourceBinder<RootDatabase>,
+    file_id: FileId,
     node: &SyntaxNode,
     max_inlay_hint_length: Option<usize>,
 ) -> Option<()> {
     let _p = profile("get_inlay_hints");
-    let db = sema.db;
+    let db = sb.db;
+    let analyzer = Lazy::new(move || sb.analyze(hir::InFile::new(file_id.into(), node), None));
     match_ast! {
         match node {
             ast::CallExpr(it) => {
-                get_param_name_hints(acc, sema, ast::Expr::from(it));
+                get_param_name_hints(acc, db, &analyzer, ast::Expr::from(it));
             },
             ast::MethodCallExpr(it) => {
-                get_param_name_hints(acc, sema, ast::Expr::from(it));
+                get_param_name_hints(acc, db, &analyzer, ast::Expr::from(it));
             },
             ast::BindPat(it) => {
                 let pat = ast::Pat::from(it.clone());
-                let ty = sema.type_of_pat(&pat)?;
+                let ty = analyzer.type_of_pat(db, &pat)?;
 
                 if should_not_display_type_hint(db, &it, &ty) {
                     return None;
@@ -122,7 +125,8 @@ fn should_not_display_type_hint(db: &RootDatabase, bind_pat: &ast::BindPat, pat_
 
 fn get_param_name_hints(
     acc: &mut Vec<InlayHint>,
-    sema: &Semantics<RootDatabase>,
+    db: &RootDatabase,
+    analyzer: &SourceAnalyzer,
     expr: ast::Expr,
 ) -> Option<()> {
     let args = match &expr {
@@ -134,7 +138,7 @@ fn get_param_name_hints(
     // we need args len to determine whether to skip or not the &self parameter
     .collect::<Vec<_>>();
 
-    let fn_signature = get_fn_signature(sema, &expr)?;
+    let fn_signature = get_fn_signature(db, analyzer, &expr)?;
     let n_params_to_skip =
         if fn_signature.has_self_param && fn_signature.parameter_names.len() > args.len() {
             1
@@ -180,26 +184,28 @@ fn should_show_param_hint(
     true
 }
 
-fn get_fn_signature(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<FunctionSignature> {
+fn get_fn_signature(
+    db: &RootDatabase,
+    analyzer: &SourceAnalyzer,
+    expr: &ast::Expr,
+) -> Option<FunctionSignature> {
     match expr {
         ast::Expr::CallExpr(expr) => {
             // FIXME: Type::as_callable is broken for closures
-            let callable_def = sema.type_of_expr(&expr.expr()?)?.as_callable()?;
+            let callable_def = analyzer.type_of(db, &expr.expr()?)?.as_callable()?;
             match callable_def {
                 hir::CallableDef::FunctionId(it) => {
-                    Some(FunctionSignature::from_hir(sema.db, it.into()))
-                }
-                hir::CallableDef::StructId(it) => {
-                    FunctionSignature::from_struct(sema.db, it.into())
+                    Some(FunctionSignature::from_hir(db, it.into()))
                 }
+                hir::CallableDef::StructId(it) => FunctionSignature::from_struct(db, it.into()),
                 hir::CallableDef::EnumVariantId(it) => {
-                    FunctionSignature::from_enum_variant(sema.db, it.into())
+                    FunctionSignature::from_enum_variant(db, it.into())
                 }
             }
         }
         ast::Expr::MethodCallExpr(expr) => {
-            let fn_def = sema.resolve_method_call(&expr)?;
-            Some(FunctionSignature::from_hir(sema.db, fn_def))
+            let fn_def = analyzer.resolve_method_call(&expr)?;
+            Some(FunctionSignature::from_hir(db, fn_def))
         }
         _ => None,
     }
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs
index d74d324534..82e10bc7e6 100644
--- a/crates/ra_ide/src/lib.rs
+++ b/crates/ra_ide/src/lib.rs
@@ -35,6 +35,7 @@ mod typing;
 mod matching_brace;
 mod display;
 mod inlay_hints;
+mod expand;
 mod expand_macro;
 mod ssr;
 
@@ -74,7 +75,7 @@ pub use crate::{
     runnables::{Runnable, RunnableKind, TestId},
     source_change::{FileSystemEdit, SourceChange, SourceFileEdit},
     ssr::SsrError,
-    syntax_highlighting::{HighlightTag, HighlightedRange},
+    syntax_highlighting::{tags, HighlightedRange},
 };
 
 pub use hir::Documentation;
@@ -318,7 +319,9 @@ impl Analysis {
         file_id: FileId,
         max_inlay_hint_length: Option<usize>,
     ) -> Cancelable<Vec<InlayHint>> {
-        self.with_db(|db| inlay_hints::inlay_hints(db, file_id, max_inlay_hint_length))
+        self.with_db(|db| {
+            inlay_hints::inlay_hints(db, file_id, &db.parse(file_id).tree(), max_inlay_hint_length)
+        })
     }
 
     /// Returns the set of folding ranges.
@@ -422,14 +425,9 @@ impl Analysis {
         self.with_db(|db| runnables::runnables(db, file_id))
     }
 
-    /// Computes syntax highlighting for the given file
+    /// Computes syntax highlighting for the given file.
     pub fn highlight(&self, file_id: FileId) -> Cancelable<Vec<HighlightedRange>> {
-        self.with_db(|db| syntax_highlighting::highlight(db, file_id, None))
-    }
-
-    /// Computes syntax highlighting for the given file range.
-    pub fn highlight_range(&self, frange: FileRange) -> Cancelable<Vec<HighlightedRange>> {
-        self.with_db(|db| syntax_highlighting::highlight(db, frange.file_id, Some(frange.range)))
+        self.with_db(|db| syntax_highlighting::highlight(db, file_id))
     }
 
     /// Computes syntax highlighting for the given file.
diff --git a/crates/ra_ide/src/marks.rs b/crates/ra_ide/src/marks.rs
index 7b8b727b4a..bcb67e3737 100644
--- a/crates/ra_ide/src/marks.rs
+++ b/crates/ra_ide/src/marks.rs
@@ -11,5 +11,4 @@ test_utils::marks!(
     call_info_bad_offset
     dont_complete_current_use
     test_resolve_parent_module_on_module_decl
-    search_filters_by_range
 );
diff --git a/crates/ra_ide/src/parent_module.rs b/crates/ra_ide/src/parent_module.rs
index 2c4bdb0393..af14d6ab3a 100644
--- a/crates/ra_ide/src/parent_module.rs
+++ b/crates/ra_ide/src/parent_module.rs
@@ -1,7 +1,6 @@
 //! FIXME: write short doc here
 
-use hir::Semantics;
-use ra_db::{CrateId, FileId, FilePosition};
+use ra_db::{CrateId, FileId, FilePosition, SourceDatabase};
 use ra_ide_db::RootDatabase;
 use ra_syntax::{
     algo::find_node_at_offset,
@@ -14,10 +13,10 @@ use crate::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 sema = Semantics::new(db);
-    let source_file = sema.parse(position.file_id);
+    let mut sb = hir::SourceBinder::new(db);
+    let parse = db.parse(position.file_id);
 
-    let mut module = find_node_at_offset::<ast::Module>(source_file.syntax(), position.offset);
+    let mut module = find_node_at_offset::<ast::Module>(parse.tree().syntax(), position.offset);
 
     // If cursor is literally on `mod foo`, go to the grandpa.
     if let Some(m) = &module {
@@ -31,8 +30,8 @@ pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<Na
     }
 
     let module = match module {
-        Some(module) => sema.to_def(&module),
-        None => sema.to_module_def(position.file_id),
+        Some(module) => sb.to_def(hir::InFile::new(position.file_id.into(), module)),
+        None => sb.to_module_def(position.file_id),
     };
     let module = match module {
         None => return Vec::new(),
@@ -44,8 +43,8 @@ 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 sema = Semantics::new(db);
-    let module = match sema.to_module_def(file_id) {
+    let mut sb = hir::SourceBinder::new(db);
+    let module = match sb.to_module_def(file_id) {
         Some(it) => it,
         None => return Vec::new(),
     };
diff --git a/crates/ra_ide/src/references.rs b/crates/ra_ide/src/references.rs
index baa8a4d291..aadc2dbcb0 100644
--- a/crates/ra_ide/src/references.rs
+++ b/crates/ra_ide/src/references.rs
@@ -13,22 +13,25 @@ mod classify;
 mod rename;
 mod search_scope;
 
-use hir::Semantics;
+use crate::expand::descend_into_macros_with_analyzer;
+use hir::{InFile, SourceBinder};
 use once_cell::unsync::Lazy;
-use ra_db::SourceDatabaseExt;
+use ra_db::{SourceDatabase, SourceDatabaseExt};
 use ra_ide_db::RootDatabase;
 use ra_prof::profile;
 use ra_syntax::{
     algo::find_node_at_offset,
     ast::{self, NameOwner},
-    match_ast, AstNode, SyntaxKind, SyntaxNode, TextRange, TextUnit, TokenAtOffset,
+    match_ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, TextRange, TextUnit, TokenAtOffset,
 };
-use test_utils::tested_by;
 
 use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeInfo};
 
-pub(crate) use self::{classify::classify_name_ref, rename::rename};
-pub(crate) use ra_ide_db::defs::{classify_name, NameDefinition};
+pub(crate) use self::{
+    classify::{classify_name, classify_name_ref},
+    rename::rename,
+};
+pub(crate) use ra_ide_db::defs::NameDefinition;
 
 pub use self::search_scope::SearchScope;
 
@@ -111,8 +114,8 @@ pub(crate) fn find_all_refs(
     position: FilePosition,
     search_scope: Option<SearchScope>,
 ) -> Option<RangeInfo<ReferenceSearchResult>> {
-    let sema = Semantics::new(db);
-    let syntax = sema.parse(position.file_id).syntax().clone();
+    let parse = db.parse(position.file_id);
+    let syntax = parse.tree().syntax().clone();
 
     let (opt_name, search_kind) =
         if let Some(name) = get_struct_def_name_for_struc_litetal_search(&syntax, position) {
@@ -121,7 +124,7 @@ pub(crate) fn find_all_refs(
             (find_node_at_offset::<ast::Name>(&syntax, position.offset), ReferenceKind::Other)
         };
 
-    let RangeInfo { range, info: (name, def) } = find_name(&sema, &syntax, position, opt_name)?;
+    let RangeInfo { range, info: (name, def) } = find_name(db, &syntax, position, opt_name)?;
     let declaration = def.try_to_nav(db)?;
 
     let search_scope = {
@@ -149,18 +152,19 @@ pub(crate) fn find_all_refs(
 }
 
 fn find_name(
-    sema: &Semantics<RootDatabase>,
+    db: &RootDatabase,
     syntax: &SyntaxNode,
     position: FilePosition,
     opt_name: Option<ast::Name>,
 ) -> Option<RangeInfo<(String, NameDefinition)>> {
+    let mut sb = SourceBinder::new(db);
     if let Some(name) = opt_name {
-        let def = classify_name(sema, &name)?;
+        let def = classify_name(&mut sb, InFile::new(position.file_id.into(), &name))?;
         let range = name.syntax().text_range();
         return Some(RangeInfo::new(range, (name.text().to_string(), def)));
     }
     let name_ref = find_node_at_offset::<ast::NameRef>(&syntax, position.offset)?;
-    let def = classify_name_ref(sema, &name_ref)?;
+    let def = classify_name_ref(&mut sb, InFile::new(position.file_id.into(), &name_ref))?;
     let range = name_ref.syntax().text_range();
     Some(RangeInfo::new(range, (name_ref.text().to_string(), def)))
 }
@@ -178,53 +182,64 @@ fn process_definition(
 
     for (file_id, search_range) in scope {
         let text = db.file_text(file_id);
-        let search_range =
-            search_range.unwrap_or(TextRange::offset_len(0.into(), TextUnit::of_str(&text)));
 
-        let sema = Semantics::new(db);
-        let tree = Lazy::new(|| sema.parse(file_id).syntax().clone());
+        let parse = Lazy::new(|| SourceFile::parse(&text));
+        let mut sb = Lazy::new(|| SourceBinder::new(db));
+        let mut analyzer = None;
 
         for (idx, _) in text.match_indices(pat) {
             let offset = TextUnit::from_usize(idx);
-            if !search_range.contains_inclusive(offset) {
-                tested_by!(search_filters_by_range);
-                continue;
-            }
 
-            let name_ref =
-                if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&tree, offset) {
-                    name_ref
-                } else {
-                    // Handle macro token cases
-                    let token = match tree.token_at_offset(offset) {
-                        TokenAtOffset::None => continue,
-                        TokenAtOffset::Single(t) => t,
-                        TokenAtOffset::Between(_, t) => t,
-                    };
-                    let expanded = sema.descend_into_macros(token);
-                    match ast::NameRef::cast(expanded.parent()) {
-                        Some(name_ref) => name_ref,
-                        _ => continue,
-                    }
+            let (name_ref, range) = if let Some(name_ref) =
+                find_node_at_offset::<ast::NameRef>(parse.tree().syntax(), offset)
+            {
+                let range = name_ref.syntax().text_range();
+                (InFile::new(file_id.into(), name_ref), range)
+            } else {
+                // Handle macro token cases
+                let t = match parse.tree().syntax().token_at_offset(offset) {
+                    TokenAtOffset::None => continue,
+                    TokenAtOffset::Single(t) => t,
+                    TokenAtOffset::Between(_, t) => t,
                 };
+                let range = t.text_range();
+                let analyzer = analyzer.get_or_insert_with(|| {
+                    sb.analyze(InFile::new(file_id.into(), parse.tree().syntax()), None)
+                });
+                let expanded = descend_into_macros_with_analyzer(
+                    db,
+                    &analyzer,
+                    InFile::new(file_id.into(), t),
+                );
+                if let Some(token) = ast::NameRef::cast(expanded.value.parent()) {
+                    (expanded.with_value(token), range)
+                } else {
+                    continue;
+                }
+            };
 
+            if let Some(search_range) = search_range {
+                if !range.is_subrange(&search_range) {
+                    continue;
+                }
+            }
             // FIXME: reuse sb
             // See https://github.com/rust-lang/rust/pull/68198#issuecomment-574269098
 
-            if let Some(d) = classify_name_ref(&sema, &name_ref) {
+            if let Some(d) = classify_name_ref(&mut sb, name_ref.as_ref()) {
                 if d == def {
-                    let kind =
-                        if is_record_lit_name_ref(&name_ref) || is_call_expr_name_ref(&name_ref) {
-                            ReferenceKind::StructLiteral
-                        } else {
-                            ReferenceKind::Other
-                        };
-
-                    let file_range = sema.original_range(name_ref.syntax());
+                    let kind = if is_record_lit_name_ref(&name_ref.value)
+                        || is_call_expr_name_ref(&name_ref.value)
+                    {
+                        ReferenceKind::StructLiteral
+                    } else {
+                        ReferenceKind::Other
+                    };
+
                     refs.push(Reference {
-                        file_range,
+                        file_range: FileRange { file_id, range },
                         kind,
-                        access: reference_access(&d, &name_ref),
+                        access: reference_access(&d, &name_ref.value),
                     });
                 }
             }
@@ -333,8 +348,6 @@ fn is_call_expr_name_ref(name_ref: &ast::NameRef) -> bool {
 
 #[cfg(test)]
 mod tests {
-    use test_utils::covers;
-
     use crate::{
         mock_analysis::{analysis_and_position, single_file_with_position, MockAnalysis},
         Declaration, Reference, ReferenceSearchResult, SearchScope,
@@ -442,27 +455,6 @@ mod tests {
         );
     }
 
-    #[test]
-    fn search_filters_by_range() {
-        covers!(search_filters_by_range);
-        let code = r#"
-            fn foo() {
-                let spam<|> = 92;
-                spam + spam
-            }
-            fn bar() {
-                let spam = 92;
-                spam + spam
-            }
-        "#;
-        let refs = get_all_refs(code);
-        check_result(
-            refs,
-            "spam BIND_PAT FileId(1) [44; 48) Other Write",
-            &["FileId(1) [71; 75) Other Read", "FileId(1) [78; 82) Other Read"],
-        );
-    }
-
     #[test]
     fn test_find_all_refs_for_param_inside() {
         let code = r#"
diff --git a/crates/ra_ide/src/references/classify.rs b/crates/ra_ide/src/references/classify.rs
index 91b21429a6..478e188712 100644
--- a/crates/ra_ide/src/references/classify.rs
+++ b/crates/ra_ide/src/references/classify.rs
@@ -1,32 +1,34 @@
 //! Functions that are used to classify an element from its definition or reference.
 
-use hir::{PathResolution, Semantics};
-use ra_ide_db::defs::NameDefinition;
-use ra_ide_db::RootDatabase;
+use hir::{InFile, PathResolution, SourceBinder};
 use ra_prof::profile;
 use ra_syntax::{ast, AstNode};
 use test_utils::tested_by;
 
-pub use ra_ide_db::defs::{from_module_def, from_struct_field};
+use super::NameDefinition;
+use ra_ide_db::RootDatabase;
+
+pub use ra_ide_db::defs::{classify_name, from_module_def, from_struct_field};
 
 pub(crate) fn classify_name_ref(
-    sema: &Semantics<RootDatabase>,
-    name_ref: &ast::NameRef,
+    sb: &mut SourceBinder<RootDatabase>,
+    name_ref: InFile<&ast::NameRef>,
 ) -> Option<NameDefinition> {
     let _p = profile("classify_name_ref");
 
-    let parent = name_ref.syntax().parent()?;
+    let parent = name_ref.value.syntax().parent()?;
+    let analyzer = sb.analyze(name_ref.map(|it| it.syntax()), None);
 
     if let Some(method_call) = ast::MethodCallExpr::cast(parent.clone()) {
         tested_by!(goto_def_for_methods);
-        if let Some(func) = sema.resolve_method_call(&method_call) {
+        if let Some(func) = analyzer.resolve_method_call(&method_call) {
             return Some(from_module_def(func.into()));
         }
     }
 
     if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) {
         tested_by!(goto_def_for_fields);
-        if let Some(field) = sema.resolve_field(&field_expr) {
+        if let Some(field) = analyzer.resolve_field(&field_expr) {
             return Some(from_struct_field(field));
         }
     }
@@ -34,20 +36,22 @@ pub(crate) fn classify_name_ref(
     if let Some(record_field) = ast::RecordField::cast(parent.clone()) {
         tested_by!(goto_def_for_record_fields);
         tested_by!(goto_def_for_field_init_shorthand);
-        if let Some(field_def) = sema.resolve_record_field(&record_field) {
+        if let Some(field_def) = analyzer.resolve_record_field(&record_field) {
             return Some(from_struct_field(field_def));
         }
     }
 
     if let Some(macro_call) = parent.ancestors().find_map(ast::MacroCall::cast) {
         tested_by!(goto_def_for_macros);
-        if let Some(macro_def) = sema.resolve_macro_call(&macro_call) {
+        if let Some(macro_def) =
+            analyzer.resolve_macro_call(sb.db, name_ref.with_value(&macro_call))
+        {
             return Some(NameDefinition::Macro(macro_def));
         }
     }
 
-    let path = name_ref.syntax().ancestors().find_map(ast::Path::cast)?;
-    let resolved = sema.resolve_path(&path)?;
+    let path = name_ref.value.syntax().ancestors().find_map(ast::Path::cast)?;
+    let resolved = analyzer.resolve_path(sb.db, &path)?;
     let res = match resolved {
         PathResolution::Def(def) => from_module_def(def),
         PathResolution::AssocItem(item) => {
diff --git a/crates/ra_ide/src/references/rename.rs b/crates/ra_ide/src/references/rename.rs
index 5b4bcf434e..bdb90020bb 100644
--- a/crates/ra_ide/src/references/rename.rs
+++ b/crates/ra_ide/src/references/rename.rs
@@ -1,7 +1,7 @@
 //! FIXME: write short doc here
 
-use hir::{ModuleSource, Semantics};
-use ra_db::{RelativePath, RelativePathBuf, SourceDatabaseExt};
+use hir::ModuleSource;
+use ra_db::{RelativePath, RelativePathBuf, SourceDatabase, SourceDatabaseExt};
 use ra_ide_db::RootDatabase;
 use ra_syntax::{
     algo::find_node_at_offset, ast, lex_single_valid_syntax_kind, AstNode, SyntaxKind, SyntaxNode,
@@ -24,16 +24,15 @@ pub(crate) fn rename(
         _ => return None,
     }
 
-    let sema = Semantics::new(db);
-    let source_file = sema.parse(position.file_id);
+    let parse = db.parse(position.file_id);
     if let Some((ast_name, ast_module)) =
-        find_name_and_module_at_offset(source_file.syntax(), position)
+        find_name_and_module_at_offset(parse.tree().syntax(), position)
     {
         let range = ast_name.syntax().text_range();
-        rename_mod(&sema, &ast_name, &ast_module, position, new_name)
+        rename_mod(db, &ast_name, &ast_module, position, new_name)
             .map(|info| RangeInfo::new(range, info))
     } else {
-        rename_reference(sema.db, position, new_name)
+        rename_reference(db, position, new_name)
     }
 }
 
@@ -55,7 +54,7 @@ fn source_edit_from_file_id_range(
 }
 
 fn rename_mod(
-    sema: &Semantics<RootDatabase>,
+    db: &RootDatabase,
     ast_name: &ast::Name,
     ast_module: &ast::Module,
     position: FilePosition,
@@ -63,12 +62,13 @@ fn rename_mod(
 ) -> Option<SourceChange> {
     let mut source_file_edits = Vec::new();
     let mut file_system_edits = Vec::new();
-    if let Some(module) = sema.to_def(ast_module) {
-        let src = module.definition_source(sema.db);
-        let file_id = src.file_id.original_file(sema.db);
+    let module_src = hir::InFile { file_id: position.file_id.into(), value: ast_module.clone() };
+    if let Some(module) = hir::SourceBinder::new(db).to_def(module_src) {
+        let src = module.definition_source(db);
+        let file_id = src.file_id.original_file(db);
         match src.value {
             ModuleSource::SourceFile(..) => {
-                let mod_path: RelativePathBuf = sema.db.file_relative_path(file_id);
+                let mod_path: RelativePathBuf = db.file_relative_path(file_id);
                 // mod is defined in path/to/dir/mod.rs
                 let dst_path = if mod_path.file_stem() == Some("mod") {
                     mod_path
@@ -82,7 +82,7 @@ fn rename_mod(
                 if let Some(path) = dst_path {
                     let move_file = FileSystemEdit::MoveFile {
                         src: file_id,
-                        dst_source_root: sema.db.file_source_root(position.file_id),
+                        dst_source_root: db.file_source_root(position.file_id),
                         dst_path: path,
                     };
                     file_system_edits.push(move_file);
@@ -98,7 +98,7 @@ fn rename_mod(
     };
     source_file_edits.push(edit);
 
-    if let Some(RangeInfo { range: _, info: refs }) = find_all_refs(sema.db, position, None) {
+    if let Some(RangeInfo { range: _, info: refs }) = find_all_refs(db, position, None) {
         let ref_edits = refs.references.into_iter().map(|reference| {
             source_edit_from_file_id_range(
                 reference.file_range.file_id,
diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs
index 74877e90fe..be2a67d0ae 100644
--- a/crates/ra_ide/src/runnables.rs
+++ b/crates/ra_ide/src/runnables.rs
@@ -1,7 +1,8 @@
 //! FIXME: write short doc here
 
-use hir::Semantics;
+use hir::{InFile, SourceBinder};
 use itertools::Itertools;
+use ra_db::SourceDatabase;
 use ra_ide_db::RootDatabase;
 use ra_syntax::{
     ast::{self, AstNode, AttrsOwner, ModuleItemOwner, NameOwner},
@@ -41,33 +42,46 @@ pub enum RunnableKind {
 }
 
 pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
-    let sema = Semantics::new(db);
-    let source_file = sema.parse(file_id);
-    source_file.syntax().descendants().filter_map(|i| runnable(&sema, i)).collect()
+    let parse = db.parse(file_id);
+    let mut sb = SourceBinder::new(db);
+    parse.tree().syntax().descendants().filter_map(|i| runnable(db, &mut sb, file_id, i)).collect()
 }
 
-fn runnable(sema: &Semantics<RootDatabase>, item: SyntaxNode) -> Option<Runnable> {
+fn runnable(
+    db: &RootDatabase,
+    source_binder: &mut SourceBinder<RootDatabase>,
+    file_id: FileId,
+    item: SyntaxNode,
+) -> Option<Runnable> {
     match_ast! {
         match item {
-            ast::FnDef(it) => { runnable_fn(sema, it) },
-            ast::Module(it) => { runnable_mod(sema, it) },
-            _ => None,
+            ast::FnDef(it) => { runnable_fn(db, source_binder, file_id, it) },
+            ast::Module(it) => { runnable_mod(db, source_binder, file_id, it) },
+            _ => { None },
         }
     }
 }
 
-fn runnable_fn(sema: &Semantics<RootDatabase>, fn_def: ast::FnDef) -> Option<Runnable> {
+fn runnable_fn(
+    db: &RootDatabase,
+    source_binder: &mut SourceBinder<RootDatabase>,
+    file_id: FileId,
+    fn_def: ast::FnDef,
+) -> Option<Runnable> {
     let name_string = fn_def.name()?.text().to_string();
 
     let kind = if name_string == "main" {
         RunnableKind::Bin
     } else {
-        let test_id = if let Some(module) = sema.to_def(&fn_def).map(|def| def.module(sema.db)) {
+        let test_id = if let Some(module) = source_binder
+            .to_def(InFile::new(file_id.into(), fn_def.clone()))
+            .map(|def| def.module(db))
+        {
             let path = module
-                .path_to_root(sema.db)
+                .path_to_root(db)
                 .into_iter()
                 .rev()
-                .filter_map(|it| it.name(sema.db))
+                .filter_map(|it| it.name(db))
                 .map(|name| name.to_string())
                 .chain(std::iter::once(name_string))
                 .join("::");
@@ -101,7 +115,12 @@ fn has_test_related_attribute(fn_def: &ast::FnDef) -> bool {
         .any(|attribute_text| attribute_text.contains("test"))
 }
 
-fn runnable_mod(sema: &Semantics<RootDatabase>, module: ast::Module) -> Option<Runnable> {
+fn runnable_mod(
+    db: &RootDatabase,
+    source_binder: &mut SourceBinder<RootDatabase>,
+    file_id: FileId,
+    module: ast::Module,
+) -> Option<Runnable> {
     let has_test_function = module
         .item_list()?
         .items()
@@ -114,10 +133,9 @@ fn runnable_mod(sema: &Semantics<RootDatabase>, module: ast::Module) -> Option<R
         return None;
     }
     let range = module.syntax().text_range();
-    let module = sema.to_def(&module)?;
+    let module = source_binder.to_def(InFile::new(file_id.into(), module))?;
 
-    let path =
-        module.path_to_root(sema.db).into_iter().rev().filter_map(|it| it.name(sema.db)).join("::");
+    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_ide/src/snapshots/rainbow_highlighting.html b/crates/ra_ide/src/snapshots/rainbow_highlighting.html
index d6a7da9532..95f038f000 100644
--- a/crates/ra_ide/src/snapshots/rainbow_highlighting.html
+++ b/crates/ra_ide/src/snapshots/rainbow_highlighting.html
@@ -25,14 +25,14 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .keyword\.control  { color: #F0DFAF; font-weight: bold; }
 </style>
 <pre><code><span class="keyword">fn</span> <span class="function">main</span>() {
-    <span class="keyword">let</span> <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(261,57%,61%);">hello</span> = <span class="string">"hello"</span>;
-    <span class="keyword">let</span> <span class="variable" data-binding-hash="2705725358298919760" style="color: hsl(17,51%,74%);">x</span> = <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(261,57%,61%);">hello</span>.to_string();
-    <span class="keyword">let</span> <span class="variable" data-binding-hash="3365759661443752373" style="color: hsl(127,76%,66%);">y</span> = <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(261,57%,61%);">hello</span>.to_string();
+    <span class="keyword">let</span> <span class="variable" data-binding-hash="2217585909179791122" style="color: hsl(280,74%,48%);">hello</span> = <span class="string">"hello"</span>;
+    <span class="keyword">let</span> <span class="variable" data-binding-hash="4303609361109701698" style="color: hsl(242,75%,88%);">x</span> = <span class="variable" data-binding-hash="2217585909179791122" style="color: hsl(280,74%,48%);">hello</span>.to_string();
+    <span class="keyword">let</span> <span class="variable" data-binding-hash="13865792086344377029" style="color: hsl(340,64%,86%);">y</span> = <span class="variable" data-binding-hash="2217585909179791122" style="color: hsl(280,74%,48%);">hello</span>.to_string();
 
-    <span class="keyword">let</span> <span class="variable" data-binding-hash="794745962933817518" style="color: hsl(19,74%,76%);">x</span> = <span class="string">"other color please!"</span>;
-    <span class="keyword">let</span> <span class="variable" data-binding-hash="6717528807933952652" style="color: hsl(85,49%,84%);">y</span> = <span class="variable" data-binding-hash="794745962933817518" style="color: hsl(19,74%,76%);">x</span>.to_string();
+    <span class="keyword">let</span> <span class="variable" data-binding-hash="7011301204224269512" style="color: hsl(198,45%,40%);">x</span> = <span class="string">"other color please!"</span>;
+    <span class="keyword">let</span> <span class="variable" data-binding-hash="12461245066629867975" style="color: hsl(132,91%,68%);">y</span> = <span class="variable" data-binding-hash="7011301204224269512" style="color: hsl(198,45%,40%);">x</span>.to_string();
 }
 
 <span class="keyword">fn</span> <span class="function">bar</span>() {
-    <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable.mut" data-binding-hash="8121853618659664005" style="color: hsl(261,57%,61%);">hello</span> = <span class="string">"hello"</span>;
+    <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable.mut" data-binding-hash="2217585909179791122" style="color: hsl(280,74%,48%);">hello</span> = <span class="string">"hello"</span>;
 }</code></pre>
\ No newline at end of file
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index d422930bfa..812229b4e0 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -1,28 +1,54 @@
 //! FIXME: write short doc here
 
-mod highlight_tag;
-
-use hir::{Name, Semantics};
+use hir::{HirFileId, InFile, Name, SourceAnalyzer, SourceBinder};
 use ra_db::SourceDatabase;
-use ra_ide_db::{
-    defs::{classify_name, NameDefinition},
-    RootDatabase,
-};
+use ra_ide_db::{defs::NameDefinition, RootDatabase};
 use ra_prof::profile;
 use ra_syntax::{
-    ast, AstNode, Direction, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxKind::*, SyntaxToken,
-    TextRange, WalkEvent, T,
+    ast, AstNode, Direction, SyntaxElement, SyntaxKind, SyntaxKind::*, SyntaxToken, TextRange,
+    WalkEvent, T,
 };
 use rustc_hash::FxHashMap;
 
-use crate::{references::classify_name_ref, FileId};
+use crate::{
+    expand::descend_into_macros_with_analyzer,
+    references::{classify_name, classify_name_ref},
+    FileId,
+};
 
-pub use highlight_tag::HighlightTag;
+pub mod tags {
+    pub const FIELD: &str = "field";
+    pub const FUNCTION: &str = "function";
+    pub const MODULE: &str = "module";
+    pub const CONSTANT: &str = "constant";
+    pub const MACRO: &str = "macro";
+
+    pub const VARIABLE: &str = "variable";
+    pub const VARIABLE_MUT: &str = "variable.mut";
+
+    pub const TYPE: &str = "type";
+    pub const TYPE_BUILTIN: &str = "type.builtin";
+    pub const TYPE_SELF: &str = "type.self";
+    pub const TYPE_PARAM: &str = "type.param";
+    pub const TYPE_LIFETIME: &str = "type.lifetime";
+
+    pub const LITERAL_BYTE: &str = "literal.byte";
+    pub const LITERAL_NUMERIC: &str = "literal.numeric";
+    pub const LITERAL_CHAR: &str = "literal.char";
+
+    pub const LITERAL_COMMENT: &str = "comment";
+    pub const LITERAL_STRING: &str = "string";
+    pub const LITERAL_ATTRIBUTE: &str = "attribute";
+
+    pub const KEYWORD: &str = "keyword";
+    pub const KEYWORD_UNSAFE: &str = "keyword.unsafe";
+    pub const KEYWORD_CONTROL: &str = "keyword.control";
+}
 
 #[derive(Debug)]
 pub struct HighlightedRange {
     pub range: TextRange,
-    pub tag: HighlightTag,
+    pub tag: &'static str,
     pub binding_hash: Option<u64>,
 }
 
@@ -41,68 +67,35 @@ fn is_control_keyword(kind: SyntaxKind) -> bool {
     }
 }
 
-pub(crate) fn highlight(
-    db: &RootDatabase,
-    file_id: FileId,
-    range: Option<TextRange>,
-) -> Vec<HighlightedRange> {
+pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRange> {
     let _p = profile("highlight");
-    let sema = Semantics::new(db);
-    let root = sema.parse(file_id).syntax().clone();
+    let parse = db.parse(file_id);
+    let root = parse.tree().syntax().clone();
 
+    let mut sb = SourceBinder::new(db);
     let mut bindings_shadow_count: FxHashMap<Name, u32> = FxHashMap::default();
     let mut res = Vec::new();
+    let analyzer = sb.analyze(InFile::new(file_id.into(), &root), None);
 
     let mut in_macro_call = None;
 
-    // Determine the root based on the given range.
-    let (root, highlight_range) = if let Some(range) = range {
-        let root = match root.covering_element(range) {
-            NodeOrToken::Node(node) => node,
-            NodeOrToken::Token(token) => token.parent(),
-        };
-        (root, range)
-    } else {
-        (root.clone(), root.text_range())
-    };
-
     for event in root.preorder_with_tokens() {
         match event {
-            WalkEvent::Enter(node) => {
-                if node.text_range().intersection(&highlight_range).is_none() {
-                    continue;
-                }
-
-                match node.kind() {
-                    MACRO_CALL => {
-                        in_macro_call = Some(node.clone());
-                        if let Some(range) = highlight_macro(node) {
-                            res.push(HighlightedRange {
-                                range,
-                                tag: HighlightTag::MACRO,
-                                binding_hash: None,
-                            });
-                        }
+            WalkEvent::Enter(node) => match node.kind() {
+                MACRO_CALL => {
+                    in_macro_call = Some(node.clone());
+                    if let Some(range) = highlight_macro(InFile::new(file_id.into(), node)) {
+                        res.push(HighlightedRange { range, tag: tags::MACRO, binding_hash: None });
                     }
-                    _ if in_macro_call.is_some() => {
-                        if let Some(token) = node.as_token() {
-                            if let Some((tag, binding_hash)) = highlight_token_tree(
-                                &sema,
-                                &mut bindings_shadow_count,
-                                token.clone(),
-                            ) {
-                                res.push(HighlightedRange {
-                                    range: node.text_range(),
-                                    tag,
-                                    binding_hash,
-                                });
-                            }
-                        }
-                    }
-                    _ => {
-                        if let Some((tag, binding_hash)) =
-                            highlight_node(&sema, &mut bindings_shadow_count, node.clone())
-                        {
+                }
+                _ if in_macro_call.is_some() => {
+                    if let Some(token) = node.as_token() {
+                        if let Some((tag, binding_hash)) = highlight_token_tree(
+                            &mut sb,
+                            &analyzer,
+                            &mut bindings_shadow_count,
+                            InFile::new(file_id.into(), token.clone()),
+                        ) {
                             res.push(HighlightedRange {
                                 range: node.text_range(),
                                 tag,
@@ -111,12 +104,17 @@ pub(crate) fn highlight(
                         }
                     }
                 }
-            }
-            WalkEvent::Leave(node) => {
-                if node.text_range().intersection(&highlight_range).is_none() {
-                    continue;
+                _ => {
+                    if let Some((tag, binding_hash)) = highlight_node(
+                        &mut sb,
+                        &mut bindings_shadow_count,
+                        InFile::new(file_id.into(), node.clone()),
+                    ) {
+                        res.push(HighlightedRange { range: node.text_range(), tag, binding_hash });
+                    }
                 }
-
+            },
+            WalkEvent::Leave(node) => {
                 if let Some(m) = in_macro_call.as_ref() {
                     if *m == node {
                         in_macro_call = None;
@@ -129,8 +127,8 @@ pub(crate) fn highlight(
     res
 }
 
-fn highlight_macro(node: SyntaxElement) -> Option<TextRange> {
-    let macro_call = ast::MacroCall::cast(node.as_node()?.clone())?;
+fn highlight_macro(node: InFile<SyntaxElement>) -> Option<TextRange> {
+    let macro_call = ast::MacroCall::cast(node.value.as_node()?.clone())?;
     let path = macro_call.path()?;
     let name_ref = path.segment()?.name_ref()?;
 
@@ -147,54 +145,56 @@ fn highlight_macro(node: SyntaxElement) -> Option<TextRange> {
 }
 
 fn highlight_token_tree(
-    sema: &Semantics<RootDatabase>,
+    sb: &mut SourceBinder<RootDatabase>,
+    analyzer: &SourceAnalyzer,
     bindings_shadow_count: &mut FxHashMap<Name, u32>,
-    token: SyntaxToken,
-) -> Option<(HighlightTag, Option<u64>)> {
-    if token.parent().kind() != TOKEN_TREE {
+    token: InFile<SyntaxToken>,
+) -> Option<(&'static str, Option<u64>)> {
+    if token.value.parent().kind() != TOKEN_TREE {
         return None;
     }
-    let token = sema.descend_into_macros(token.clone());
+    let token = descend_into_macros_with_analyzer(sb.db, analyzer, token);
     let expanded = {
-        let parent = token.parent();
+        let parent = token.value.parent();
         // We only care Name and Name_ref
-        match (token.kind(), parent.kind()) {
-            (IDENT, NAME) | (IDENT, NAME_REF) => parent.into(),
-            _ => token.into(),
+        match (token.value.kind(), parent.kind()) {
+            (IDENT, NAME) | (IDENT, NAME_REF) => token.with_value(parent.into()),
+            _ => token.map(|it| it.into()),
         }
     };
 
-    highlight_node(sema, bindings_shadow_count, expanded)
+    highlight_node(sb, bindings_shadow_count, expanded)
 }
 
 fn highlight_node(
-    sema: &Semantics<RootDatabase>,
+    sb: &mut SourceBinder<RootDatabase>,
     bindings_shadow_count: &mut FxHashMap<Name, u32>,
-    node: SyntaxElement,
-) -> Option<(HighlightTag, Option<u64>)> {
-    let db = sema.db;
+    node: InFile<SyntaxElement>,
+) -> Option<(&'static str, Option<u64>)> {
+    let db = sb.db;
     let mut binding_hash = None;
-    let tag = match node.kind() {
+    let tag = match node.value.kind() {
         FN_DEF => {
             bindings_shadow_count.clear();
             return None;
         }
-        COMMENT => HighlightTag::LITERAL_COMMENT,
-        STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => HighlightTag::LITERAL_STRING,
-        ATTR => HighlightTag::LITERAL_ATTRIBUTE,
+        COMMENT => tags::LITERAL_COMMENT,
+        STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => tags::LITERAL_STRING,
+        ATTR => tags::LITERAL_ATTRIBUTE,
         // Special-case field init shorthand
-        NAME_REF if node.parent().and_then(ast::RecordField::cast).is_some() => HighlightTag::FIELD,
-        NAME_REF if node.ancestors().any(|it| it.kind() == ATTR) => return None,
+        NAME_REF if node.value.parent().and_then(ast::RecordField::cast).is_some() => tags::FIELD,
+        NAME_REF if node.value.ancestors().any(|it| it.kind() == ATTR) => return None,
         NAME_REF => {
-            let name_ref = node.as_node().cloned().and_then(ast::NameRef::cast).unwrap();
-            let name_kind = classify_name_ref(sema, &name_ref);
+            let name_ref = node.value.as_node().cloned().and_then(ast::NameRef::cast).unwrap();
+            let name_kind = classify_name_ref(sb, node.with_value(&name_ref));
             match name_kind {
                 Some(name_kind) => {
                     if let NameDefinition::Local(local) = &name_kind {
                         if let Some(name) = local.name(db) {
                             let shadow_count =
                                 bindings_shadow_count.entry(name.clone()).or_default();
-                            binding_hash = Some(calc_binding_hash(&name, *shadow_count))
+                            binding_hash =
+                                Some(calc_binding_hash(node.file_id, &name, *shadow_count))
                         }
                     };
 
@@ -204,41 +204,41 @@ fn highlight_node(
             }
         }
         NAME => {
-            let name = node.as_node().cloned().and_then(ast::Name::cast).unwrap();
-            let name_kind = classify_name(sema, &name);
+            let name = node.value.as_node().cloned().and_then(ast::Name::cast).unwrap();
+            let name_kind = classify_name(sb, node.with_value(&name));
 
             if let Some(NameDefinition::Local(local)) = &name_kind {
                 if let Some(name) = local.name(db) {
                     let shadow_count = bindings_shadow_count.entry(name.clone()).or_default();
                     *shadow_count += 1;
-                    binding_hash = Some(calc_binding_hash(&name, *shadow_count))
+                    binding_hash = Some(calc_binding_hash(node.file_id, &name, *shadow_count))
                 }
             };
 
             match name_kind {
                 Some(name_kind) => highlight_name(db, name_kind),
-                None => name.syntax().parent().map_or(HighlightTag::FUNCTION, |x| match x.kind() {
-                    STRUCT_DEF | ENUM_DEF | TRAIT_DEF | TYPE_ALIAS_DEF => HighlightTag::TYPE,
-                    TYPE_PARAM => HighlightTag::TYPE_PARAM,
-                    RECORD_FIELD_DEF => HighlightTag::FIELD,
-                    _ => HighlightTag::FUNCTION,
+                None => name.syntax().parent().map_or(tags::FUNCTION, |x| match x.kind() {
+                    STRUCT_DEF | ENUM_DEF | TRAIT_DEF | TYPE_ALIAS_DEF => tags::TYPE,
+                    TYPE_PARAM => tags::TYPE_PARAM,
+                    RECORD_FIELD_DEF => tags::FIELD,
+                    _ => tags::FUNCTION,
                 }),
             }
         }
-        INT_NUMBER | FLOAT_NUMBER => HighlightTag::LITERAL_NUMERIC,
-        BYTE => HighlightTag::LITERAL_BYTE,
-        CHAR => HighlightTag::LITERAL_CHAR,
-        LIFETIME => HighlightTag::TYPE_LIFETIME,
-        T![unsafe] => HighlightTag::KEYWORD_UNSAFE,
-        k if is_control_keyword(k) => HighlightTag::KEYWORD_CONTROL,
-        k if k.is_keyword() => HighlightTag::KEYWORD,
+        INT_NUMBER | FLOAT_NUMBER => tags::LITERAL_NUMERIC,
+        BYTE => tags::LITERAL_BYTE,
+        CHAR => tags::LITERAL_CHAR,
+        LIFETIME => tags::TYPE_LIFETIME,
+        T![unsafe] => tags::KEYWORD_UNSAFE,
+        k if is_control_keyword(k) => tags::KEYWORD_CONTROL,
+        k if k.is_keyword() => tags::KEYWORD,
 
         _ => return None,
     };
 
     return Some((tag, binding_hash));
 
-    fn calc_binding_hash(name: &Name, shadow_count: u32) -> u64 {
+    fn calc_binding_hash(file_id: HirFileId, name: &Name, shadow_count: u32) -> u64 {
         fn hash<T: std::hash::Hash + std::fmt::Debug>(x: T) -> u64 {
             use std::{collections::hash_map::DefaultHasher, hash::Hasher};
 
@@ -247,7 +247,7 @@ fn highlight_node(
             hasher.finish()
         }
 
-        hash((name, shadow_count))
+        hash((file_id, name, shadow_count))
     }
 }
 
@@ -265,7 +265,7 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: boo
         )
     }
 
-    let mut ranges = highlight(db, file_id, None);
+    let mut ranges = highlight(db, file_id);
     ranges.sort_by_key(|it| it.range.start());
     // quick non-optimal heuristic to intersect token ranges and highlighted ranges
     let mut frontier = 0;
@@ -293,7 +293,7 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: boo
         if ranges.is_empty() {
             buf.push_str(&text);
         } else {
-            let classes = ranges.iter().map(|x| x.tag.to_string()).collect::<Vec<_>>().join(" ");
+            let classes = ranges.iter().map(|x| x.tag).collect::<Vec<_>>().join(" ");
             let binding_hash = ranges.first().and_then(|x| x.binding_hash);
             let color = match (rainbow, binding_hash) {
                 (true, Some(hash)) => format!(
@@ -310,26 +310,26 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: boo
     buf
 }
 
-fn highlight_name(db: &RootDatabase, def: NameDefinition) -> HighlightTag {
+fn highlight_name(db: &RootDatabase, def: NameDefinition) -> &'static str {
     match def {
-        NameDefinition::Macro(_) => HighlightTag::MACRO,
-        NameDefinition::StructField(_) => HighlightTag::FIELD,
-        NameDefinition::ModuleDef(hir::ModuleDef::Module(_)) => HighlightTag::MODULE,
-        NameDefinition::ModuleDef(hir::ModuleDef::Function(_)) => HighlightTag::FUNCTION,
-        NameDefinition::ModuleDef(hir::ModuleDef::Adt(_)) => HighlightTag::TYPE,
-        NameDefinition::ModuleDef(hir::ModuleDef::EnumVariant(_)) => HighlightTag::CONSTANT,
-        NameDefinition::ModuleDef(hir::ModuleDef::Const(_)) => HighlightTag::CONSTANT,
-        NameDefinition::ModuleDef(hir::ModuleDef::Static(_)) => HighlightTag::CONSTANT,
-        NameDefinition::ModuleDef(hir::ModuleDef::Trait(_)) => HighlightTag::TYPE,
-        NameDefinition::ModuleDef(hir::ModuleDef::TypeAlias(_)) => HighlightTag::TYPE,
-        NameDefinition::ModuleDef(hir::ModuleDef::BuiltinType(_)) => HighlightTag::TYPE_BUILTIN,
-        NameDefinition::SelfType(_) => HighlightTag::TYPE_SELF,
-        NameDefinition::TypeParam(_) => HighlightTag::TYPE_PARAM,
+        NameDefinition::Macro(_) => tags::MACRO,
+        NameDefinition::StructField(_) => tags::FIELD,
+        NameDefinition::ModuleDef(hir::ModuleDef::Module(_)) => tags::MODULE,
+        NameDefinition::ModuleDef(hir::ModuleDef::Function(_)) => tags::FUNCTION,
+        NameDefinition::ModuleDef(hir::ModuleDef::Adt(_)) => tags::TYPE,
+        NameDefinition::ModuleDef(hir::ModuleDef::EnumVariant(_)) => tags::CONSTANT,
+        NameDefinition::ModuleDef(hir::ModuleDef::Const(_)) => tags::CONSTANT,
+        NameDefinition::ModuleDef(hir::ModuleDef::Static(_)) => tags::CONSTANT,
+        NameDefinition::ModuleDef(hir::ModuleDef::Trait(_)) => tags::TYPE,
+        NameDefinition::ModuleDef(hir::ModuleDef::TypeAlias(_)) => tags::TYPE,
+        NameDefinition::ModuleDef(hir::ModuleDef::BuiltinType(_)) => tags::TYPE_BUILTIN,
+        NameDefinition::SelfType(_) => tags::TYPE_SELF,
+        NameDefinition::TypeParam(_) => tags::TYPE_PARAM,
         NameDefinition::Local(local) => {
             if local.is_mut(db) || local.ty(db).is_mutable_reference() {
-                HighlightTag::VARIABLE_MUT
+                tags::VARIABLE_MUT
             } else {
-                HighlightTag::VARIABLE
+                tags::VARIABLE
             }
         }
     }
@@ -374,10 +374,7 @@ mod tests {
 
     use test_utils::{assert_eq_text, project_dir, read_text};
 
-    use crate::{
-        mock_analysis::{single_file, MockAnalysis},
-        FileRange, TextRange,
-    };
+    use crate::mock_analysis::{single_file, MockAnalysis};
 
     #[test]
     fn test_highlighting() {
@@ -478,26 +475,4 @@ fn bar() {
         let _ = host.analysis().highlight(file_id).unwrap();
         // eprintln!("elapsed: {:?}", t.elapsed());
     }
-
-    #[test]
-    fn test_ranges() {
-        let (analysis, file_id) = single_file(
-            r#"
-            #[derive(Clone, Debug)]
-            struct Foo {
-                pub x: i32,
-                pub y: i32,
-            }"#,
-        );
-
-        // The "x"
-        let highlights = &analysis
-            .highlight_range(FileRange {
-                file_id,
-                range: TextRange::offset_len(82.into(), 1.into()),
-            })
-            .unwrap();
-
-        assert_eq!(&highlights[0].tag.to_string(), "field");
-    }
 }
diff --git a/crates/ra_ide/src/syntax_highlighting/highlight_tag.rs b/crates/ra_ide/src/syntax_highlighting/highlight_tag.rs
deleted file mode 100644
index af1ac07b3d..0000000000
--- a/crates/ra_ide/src/syntax_highlighting/highlight_tag.rs
+++ /dev/null
@@ -1,43 +0,0 @@
-//! Defines token tags we use for syntax highlighting.
-//! A tag is not unlike a CSS class.
-
-use std::fmt;
-
-#[derive(Debug, PartialEq, Eq, Clone, Copy)]
-pub struct HighlightTag(&'static str);
-
-impl fmt::Display for HighlightTag {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        fmt::Display::fmt(self.0, f)
-    }
-}
-
-#[rustfmt::skip]
-impl HighlightTag {
-    pub const FIELD: HighlightTag              = HighlightTag("field");
-    pub const FUNCTION: HighlightTag           = HighlightTag("function");
-    pub const MODULE: HighlightTag             = HighlightTag("module");
-    pub const CONSTANT: HighlightTag           = HighlightTag("constant");
-    pub const MACRO: HighlightTag              = HighlightTag("macro");
-
-    pub const VARIABLE: HighlightTag           = HighlightTag("variable");
-    pub const VARIABLE_MUT: HighlightTag       = HighlightTag("variable.mut");
-
-    pub const TYPE: HighlightTag               = HighlightTag("type");
-    pub const TYPE_BUILTIN: HighlightTag       = HighlightTag("type.builtin");
-    pub const TYPE_SELF: HighlightTag          = HighlightTag("type.self");
-    pub const TYPE_PARAM: HighlightTag         = HighlightTag("type.param");
-    pub const TYPE_LIFETIME: HighlightTag      = HighlightTag("type.lifetime");
-
-    pub const LITERAL_BYTE: HighlightTag       = HighlightTag("literal.byte");
-    pub const LITERAL_NUMERIC: HighlightTag    = HighlightTag("literal.numeric");
-    pub const LITERAL_CHAR: HighlightTag       = HighlightTag("literal.char");
-
-    pub const LITERAL_COMMENT: HighlightTag    = HighlightTag("comment");
-    pub const LITERAL_STRING: HighlightTag     = HighlightTag("string");
-    pub const LITERAL_ATTRIBUTE: HighlightTag  = HighlightTag("attribute");
-
-    pub const KEYWORD: HighlightTag            = HighlightTag("keyword");
-    pub const KEYWORD_UNSAFE: HighlightTag     = HighlightTag("keyword.unsafe");
-    pub const KEYWORD_CONTROL: HighlightTag    = HighlightTag("keyword.control");
-}
diff --git a/crates/ra_ide_db/Cargo.toml b/crates/ra_ide_db/Cargo.toml
index 2f5b74b929..ee409e34e9 100644
--- a/crates/ra_ide_db/Cargo.toml
+++ b/crates/ra_ide_db/Cargo.toml
@@ -20,6 +20,7 @@ log = "0.4.8"
 rayon = "1.3.0"
 fst = { version = "0.3.5", default-features = false }
 rustc-hash = "1.1.0"
+unicase = "2.6.0"
 superslice = "1.0.0"
 once_cell = "1.3.1"
 
diff --git a/crates/ra_ide_db/src/defs.rs b/crates/ra_ide_db/src/defs.rs
index e10e72f711..04c214624b 100644
--- a/crates/ra_ide_db/src/defs.rs
+++ b/crates/ra_ide_db/src/defs.rs
@@ -6,8 +6,8 @@
 // FIXME: this badly needs rename/rewrite (matklad, 2020-02-06).
 
 use hir::{
-    Adt, FieldSource, HasSource, ImplBlock, Local, MacroDef, Module, ModuleDef, Semantics,
-    StructField, TypeParam,
+    Adt, FieldSource, HasSource, ImplBlock, InFile, Local, MacroDef, Module, ModuleDef,
+    SourceBinder, StructField, TypeParam,
 };
 use ra_prof::profile;
 use ra_syntax::{
@@ -68,62 +68,78 @@ impl NameDefinition {
     }
 }
 
-pub fn classify_name(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Option<NameDefinition> {
+pub fn classify_name(
+    sb: &mut SourceBinder<RootDatabase>,
+    name: InFile<&ast::Name>,
+) -> Option<NameDefinition> {
     let _p = profile("classify_name");
-    let parent = name.syntax().parent()?;
+    let parent = name.value.syntax().parent()?;
 
     match_ast! {
         match parent {
             ast::BindPat(it) => {
-                let local = sema.to_def(&it)?;
+                let src = name.with_value(it);
+                let local = sb.to_def(src)?;
                 Some(NameDefinition::Local(local))
             },
             ast::RecordFieldDef(it) => {
-                let field: hir::StructField = sema.to_def(&it)?;
+                let src = name.with_value(it);
+                let field: hir::StructField = sb.to_def(src)?;
                 Some(from_struct_field(field))
             },
             ast::Module(it) => {
-                let def = sema.to_def(&it)?;
+                let def = sb.to_def(name.with_value(it))?;
                 Some(from_module_def(def.into()))
             },
             ast::StructDef(it) => {
-                let def: hir::Struct = sema.to_def(&it)?;
+                let src = name.with_value(it);
+                let def: hir::Struct = sb.to_def(src)?;
                 Some(from_module_def(def.into()))
             },
             ast::EnumDef(it) => {
-                let def: hir::Enum = sema.to_def(&it)?;
+                let src = name.with_value(it);
+                let def: hir::Enum = sb.to_def(src)?;
                 Some(from_module_def(def.into()))
             },
             ast::TraitDef(it) => {
-                let def: hir::Trait = sema.to_def(&it)?;
+                let src = name.with_value(it);
+                let def: hir::Trait = sb.to_def(src)?;
                 Some(from_module_def(def.into()))
             },
             ast::StaticDef(it) => {
-                let def: hir::Static = sema.to_def(&it)?;
+                let src = name.with_value(it);
+                let def: hir::Static = sb.to_def(src)?;
                 Some(from_module_def(def.into()))
             },
             ast::EnumVariant(it) => {
-                let def: hir::EnumVariant = sema.to_def(&it)?;
+                let src = name.with_value(it);
+                let def: hir::EnumVariant = sb.to_def(src)?;
                 Some(from_module_def(def.into()))
             },
             ast::FnDef(it) => {
-                let def: hir::Function = sema.to_def(&it)?;
+                let src = name.with_value(it);
+                let def: hir::Function = sb.to_def(src)?;
                 Some(from_module_def(def.into()))
             },
             ast::ConstDef(it) => {
-                let def: hir::Const = sema.to_def(&it)?;
+                let src = name.with_value(it);
+                let def: hir::Const = sb.to_def(src)?;
                 Some(from_module_def(def.into()))
             },
             ast::TypeAliasDef(it) => {
-                let def: hir::TypeAlias = sema.to_def(&it)?;
+                let src = name.with_value(it);
+                let def: hir::TypeAlias = sb.to_def(src)?;
                 Some(from_module_def(def.into()))
             },
             ast::MacroCall(it) => {
-                let def = sema.to_def(&it)?;
+                let src = name.with_value(it);
+                let def = sb.to_def(src.clone())?;
+
                 Some(NameDefinition::Macro(def))
             },
             ast::TypeParam(it) => {
-                let def = sema.to_def(&it)?;
+                let src = name.with_value(it);
+                let def = sb.to_def(src)?;
                 Some(NameDefinition::TypeParam(def))
             },
             _ => None,
diff --git a/crates/ra_ide_db/src/imports_locator.rs b/crates/ra_ide_db/src/imports_locator.rs
index e590d2a5c3..b8dd358a97 100644
--- a/crates/ra_ide_db/src/imports_locator.rs
+++ b/crates/ra_ide_db/src/imports_locator.rs
@@ -1,7 +1,7 @@
 //! This module contains an import search funcionality that is provided to the ra_assists module.
 //! Later, this should be moved away to a separate crate that is accessible from the ra_assists module.
 
-use hir::{ModuleDef, Semantics};
+use hir::{db::HirDatabase, ModuleDef, SourceBinder};
 use ra_prof::profile;
 use ra_syntax::{ast, AstNode, SyntaxKind::NAME};
 
@@ -12,17 +12,17 @@ use crate::{
 };
 
 pub struct ImportsLocator<'a> {
-    sema: Semantics<'a, RootDatabase>,
+    source_binder: SourceBinder<'a, RootDatabase>,
 }
 
 impl<'a> ImportsLocator<'a> {
     pub fn new(db: &'a RootDatabase) -> Self {
-        Self { sema: Semantics::new(db) }
+        Self { source_binder: SourceBinder::new(db) }
     }
 
     pub fn find_imports(&mut self, name_to_import: &str) -> Vec<ModuleDef> {
         let _p = profile("search_for_imports");
-        let db = self.sema.db;
+        let db = self.source_binder.db;
 
         let project_results = {
             let mut query = Query::new(name_to_import.to_string());
@@ -41,7 +41,7 @@ impl<'a> ImportsLocator<'a> {
         project_results
             .into_iter()
             .chain(lib_results.into_iter())
-            .filter_map(|import_candidate| self.get_name_definition(&import_candidate))
+            .filter_map(|import_candidate| self.get_name_definition(db, &import_candidate))
             .filter_map(|name_definition_to_import| match name_definition_to_import {
                 NameDefinition::ModuleDef(module_def) => Some(module_def),
                 _ => None,
@@ -49,16 +49,22 @@ impl<'a> ImportsLocator<'a> {
             .collect()
     }
 
-    fn get_name_definition(&mut self, import_candidate: &FileSymbol) -> Option<NameDefinition> {
+    fn get_name_definition(
+        &mut self,
+        db: &impl HirDatabase,
+        import_candidate: &FileSymbol,
+    ) -> Option<NameDefinition> {
         let _p = profile("get_name_definition");
-        let file_id = import_candidate.file_id;
-
-        let candidate_node = import_candidate.ptr.to_node(self.sema.parse(file_id).syntax());
+        let file_id = import_candidate.file_id.into();
+        let candidate_node = import_candidate.ptr.to_node(&db.parse_or_expand(file_id)?);
         let candidate_name_node = if candidate_node.kind() != NAME {
             candidate_node.children().find(|it| it.kind() == NAME)?
         } else {
             candidate_node
         };
-        classify_name(&self.sema, &ast::Name::cast(candidate_name_node)?)
+        classify_name(
+            &mut self.source_binder,
+            hir::InFile { file_id, value: &ast::Name::cast(candidate_name_node)? },
+        )
     }
 }
diff --git a/crates/ra_syntax/src/algo.rs b/crates/ra_syntax/src/algo.rs
index ebf59288a3..21fca99a6f 100644
--- a/crates/ra_syntax/src/algo.rs
+++ b/crates/ra_syntax/src/algo.rs
@@ -4,11 +4,10 @@ use std::ops::RangeInclusive;
 
 use itertools::Itertools;
 use ra_text_edit::TextEditBuilder;
-use rustc_hash::{FxHashMap, FxHashSet};
+use rustc_hash::FxHashMap;
 
 use crate::{
-    AstNode, Direction, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxNodePtr, SyntaxToken,
-    TextRange, TextUnit,
+    AstNode, Direction, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxNodePtr, TextRange, TextUnit,
 };
 
 /// Returns ancestors of the node at the offset, sorted by length. This should
@@ -38,17 +37,6 @@ pub fn find_node_at_offset<N: AstNode>(syntax: &SyntaxNode, offset: TextUnit) ->
     ancestors_at_offset(syntax, offset).find_map(N::cast)
 }
 
-/// Skip to next non `trivia` token
-pub fn skip_trivia_token(mut token: SyntaxToken, direction: Direction) -> Option<SyntaxToken> {
-    while token.kind().is_trivia() {
-        token = match direction {
-            Direction::Next => token.next_token()?,
-            Direction::Prev => token.prev_token()?,
-        }
-    }
-    Some(token)
-}
-
 /// Finds the first sibling in the given direction which is not `trivia`
 pub fn non_trivia_sibling(element: SyntaxElement, direction: Direction) -> Option<SyntaxElement> {
     return match element {
@@ -68,11 +56,6 @@ pub fn find_covering_element(root: &SyntaxNode, range: TextRange) -> SyntaxEleme
     root.covering_element(range)
 }
 
-pub fn least_common_ancestor(u: &SyntaxNode, v: &SyntaxNode) -> Option<SyntaxNode> {
-    let u_ancestors = u.ancestors().collect::<FxHashSet<SyntaxNode>>();
-    v.ancestors().find(|it| u_ancestors.contains(it))
-}
-
 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
 pub enum InsertPosition<T> {
     First,
diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs
index db82eeb1ca..638987ee81 100644
--- a/crates/rust-analyzer/src/caps.rs
+++ b/crates/rust-analyzer/src/caps.rs
@@ -7,9 +7,9 @@ use lsp_types::{
     CompletionOptions, DocumentOnTypeFormattingOptions, FoldingRangeProviderCapability,
     ImplementationProviderCapability, RenameOptions, RenameProviderCapability, SaveOptions,
     SelectionRangeProviderCapability, SemanticTokensDocumentProvider, SemanticTokensLegend,
-    SemanticTokensOptions, ServerCapabilities, SignatureHelpOptions, TextDocumentSyncCapability,
-    TextDocumentSyncKind, TextDocumentSyncOptions, TypeDefinitionProviderCapability,
-    WorkDoneProgressOptions,
+    SemanticTokensOptions, SemanticTokensServerCapabilities, ServerCapabilities,
+    SignatureHelpOptions, TextDocumentSyncCapability, TextDocumentSyncKind,
+    TextDocumentSyncOptions, TypeDefinitionProviderCapability, WorkDoneProgressOptions,
 };
 
 pub fn server_capabilities() -> ServerCapabilities {
@@ -60,7 +60,7 @@ pub fn server_capabilities() -> ServerCapabilities {
         execute_command_provider: None,
         workspace: None,
         call_hierarchy_provider: Some(CallHierarchyServerCapability::Simple(true)),
-        semantic_tokens_provider: Some(
+        semantic_tokens_provider: Some(SemanticTokensServerCapabilities::SemanticTokensOptions(
             SemanticTokensOptions {
                 legend: SemanticTokensLegend {
                     token_types: semantic_tokens::supported_token_types().iter().cloned().collect(),
@@ -71,11 +71,9 @@ pub fn server_capabilities() -> ServerCapabilities {
                 },
 
                 document_provider: Some(SemanticTokensDocumentProvider::Bool(true)),
-                range_provider: Some(true),
-                work_done_progress_options: Default::default(),
-            }
-            .into(),
-        ),
+                ..SemanticTokensOptions::default()
+            },
+        )),
         experimental: Default::default(),
     }
 }
diff --git a/crates/rust-analyzer/src/conv.rs b/crates/rust-analyzer/src/conv.rs
index 220e0572a6..5fcb46b617 100644
--- a/crates/rust-analyzer/src/conv.rs
+++ b/crates/rust-analyzer/src/conv.rs
@@ -9,8 +9,8 @@ use lsp_types::{
     WorkspaceEdit,
 };
 use ra_ide::{
-    translate_offset_with_edit, CompletionItem, CompletionItemKind, FileId, FilePosition,
-    FileRange, FileSystemEdit, Fold, FoldKind, HighlightTag, InsertTextFormat, LineCol, LineIndex,
+    tags, translate_offset_with_edit, CompletionItem, CompletionItemKind, FileId, FilePosition,
+    FileRange, FileSystemEdit, Fold, FoldKind, InsertTextFormat, LineCol, LineIndex,
     NavigationTarget, RangeInfo, ReferenceAccess, Severity, SourceChange, SourceFileEdit,
 };
 use ra_syntax::{SyntaxKind, TextRange, TextUnit};
@@ -303,51 +303,51 @@ impl ConvWith<&FoldConvCtx<'_>> for Fold {
     }
 }
 
-impl Conv for HighlightTag {
+impl Conv for &'static str {
     type Output = (SemanticTokenType, Vec<SemanticTokenModifier>);
 
     fn conv(self) -> (SemanticTokenType, Vec<SemanticTokenModifier>) {
         let token_type: SemanticTokenType = match self {
-            HighlightTag::FIELD => SemanticTokenType::MEMBER,
-            HighlightTag::FUNCTION => SemanticTokenType::FUNCTION,
-            HighlightTag::MODULE => SemanticTokenType::NAMESPACE,
-            HighlightTag::CONSTANT => {
+            tags::FIELD => SemanticTokenType::MEMBER,
+            tags::FUNCTION => SemanticTokenType::FUNCTION,
+            tags::MODULE => SemanticTokenType::NAMESPACE,
+            tags::CONSTANT => {
                 return (
                     SemanticTokenType::VARIABLE,
                     vec![SemanticTokenModifier::STATIC, SemanticTokenModifier::READONLY],
                 )
             }
-            HighlightTag::MACRO => SemanticTokenType::MACRO,
+            tags::MACRO => SemanticTokenType::MACRO,
 
-            HighlightTag::VARIABLE => {
+            tags::VARIABLE => {
                 return (SemanticTokenType::VARIABLE, vec![SemanticTokenModifier::READONLY])
             }
-            HighlightTag::VARIABLE_MUT => SemanticTokenType::VARIABLE,
+            tags::VARIABLE_MUT => SemanticTokenType::VARIABLE,
 
-            HighlightTag::TYPE => SemanticTokenType::TYPE,
-            HighlightTag::TYPE_BUILTIN => SemanticTokenType::TYPE,
-            HighlightTag::TYPE_SELF => {
+            tags::TYPE => SemanticTokenType::TYPE,
+            tags::TYPE_BUILTIN => SemanticTokenType::TYPE,
+            tags::TYPE_SELF => {
                 return (SemanticTokenType::TYPE, vec![SemanticTokenModifier::REFERENCE])
             }
-            HighlightTag::TYPE_PARAM => SemanticTokenType::TYPE_PARAMETER,
-            HighlightTag::TYPE_LIFETIME => {
+            tags::TYPE_PARAM => SemanticTokenType::TYPE_PARAMETER,
+            tags::TYPE_LIFETIME => {
                 return (SemanticTokenType::LABEL, vec![SemanticTokenModifier::REFERENCE])
             }
 
-            HighlightTag::LITERAL_BYTE => SemanticTokenType::NUMBER,
-            HighlightTag::LITERAL_NUMERIC => SemanticTokenType::NUMBER,
-            HighlightTag::LITERAL_CHAR => SemanticTokenType::NUMBER,
+            tags::LITERAL_BYTE => SemanticTokenType::NUMBER,
+            tags::LITERAL_NUMERIC => SemanticTokenType::NUMBER,
+            tags::LITERAL_CHAR => SemanticTokenType::NUMBER,
 
-            HighlightTag::LITERAL_COMMENT => {
+            tags::LITERAL_COMMENT => {
                 return (SemanticTokenType::COMMENT, vec![SemanticTokenModifier::DOCUMENTATION])
             }
 
-            HighlightTag::LITERAL_STRING => SemanticTokenType::STRING,
-            HighlightTag::LITERAL_ATTRIBUTE => "attribute".into(),
+            tags::LITERAL_STRING => SemanticTokenType::STRING,
+            tags::LITERAL_ATTRIBUTE => SemanticTokenType::KEYWORD,
 
-            HighlightTag::KEYWORD => SemanticTokenType::KEYWORD,
-            HighlightTag::KEYWORD_UNSAFE => SemanticTokenType::KEYWORD,
-            HighlightTag::KEYWORD_CONTROL => SemanticTokenType::KEYWORD,
+            tags::KEYWORD => SemanticTokenType::KEYWORD,
+            tags::KEYWORD_UNSAFE => SemanticTokenType::KEYWORD,
+            tags::KEYWORD_CONTROL => SemanticTokenType::KEYWORD,
             unknown => panic!("Unknown semantic token: {}", unknown),
         };
 
@@ -363,11 +363,10 @@ impl Conv for (SemanticTokenType, Vec<SemanticTokenModifier>) {
             semantic_tokens::supported_token_types().iter().position(|it| *it == self.0).unwrap();
         let mut token_modifier_bitset = 0;
         for modifier in self.1.iter() {
-            let modifier_index = semantic_tokens::supported_token_modifiers()
+            token_modifier_bitset |= semantic_tokens::supported_token_modifiers()
                 .iter()
                 .position(|it| it == modifier)
                 .unwrap();
-            token_modifier_bitset |= 1 << modifier_index;
         }
 
         (token_index as u32, token_modifier_bitset as u32)
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 2b25f54436..6e9e604a65 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -527,9 +527,8 @@ fn on_request(
         .on::<req::CallHierarchyPrepare>(handlers::handle_call_hierarchy_prepare)?
         .on::<req::CallHierarchyIncomingCalls>(handlers::handle_call_hierarchy_incoming)?
         .on::<req::CallHierarchyOutgoingCalls>(handlers::handle_call_hierarchy_outgoing)?
-        .on::<req::SemanticTokensRequest>(handlers::handle_semantic_tokens)?
-        .on::<req::SemanticTokensRangeRequest>(handlers::handle_semantic_tokens_range)?
         .on::<req::Ssr>(handlers::handle_ssr)?
+        .on::<req::SemanticTokensRequest>(handlers::handle_semantic_tokens)?
         .finish();
     Ok(())
 }
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs
index e9f1c4f4b5..e13e7c95a2 100644
--- a/crates/rust-analyzer/src/main_loop/handlers.rs
+++ b/crates/rust-analyzer/src/main_loop/handlers.rs
@@ -17,8 +17,8 @@ use lsp_types::{
     Diagnostic, DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange,
     FoldingRangeParams, Hover, HoverContents, Location, MarkupContent, MarkupKind, Position,
     PrepareRenameResponse, Range, RenameParams, SemanticTokenModifier, SemanticTokenType,
-    SemanticTokens, SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult,
-    SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, TextEdit, WorkspaceEdit,
+    SemanticTokens, SemanticTokensParams, SemanticTokensResult, SymbolInformation,
+    TextDocumentIdentifier, TextEdit, WorkspaceEdit,
 };
 use ra_ide::{
     AssistId, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind,
@@ -954,7 +954,7 @@ fn highlight(world: &WorldSnapshot, file_id: FileId) -> Result<Vec<Decoration>>
         .into_iter()
         .map(|h| Decoration {
             range: h.range.conv_with(&line_index),
-            tag: h.tag.to_string(),
+            tag: h.tag,
             binding_hash: h.binding_hash.map(|x| x.to_string()),
         })
         .collect();
@@ -1092,25 +1092,3 @@ pub fn handle_semantic_tokens(
 
     Ok(Some(tokens.into()))
 }
-
-pub fn handle_semantic_tokens_range(
-    world: WorldSnapshot,
-    params: SemanticTokensRangeParams,
-) -> Result<Option<SemanticTokensRangeResult>> {
-    let _p = profile("handle_semantic_tokens_range");
-
-    let frange = (&params.text_document, params.range).try_conv_with(&world)?;
-    let line_index = world.analysis().file_line_index(frange.file_id)?;
-
-    let mut builder = SemanticTokensBuilder::default();
-
-    for h in world.analysis().highlight_range(frange)?.into_iter() {
-        let type_and_modifiers: (SemanticTokenType, Vec<SemanticTokenModifier>) = h.tag.conv();
-        let (token_type, token_modifiers) = type_and_modifiers.conv();
-        builder.push(h.range.conv_with(&line_index), token_type, token_modifiers);
-    }
-
-    let tokens = SemanticTokens { data: builder.build(), ..Default::default() };
-
-    Ok(Some(tokens.into()))
-}
diff --git a/crates/rust-analyzer/src/req.rs b/crates/rust-analyzer/src/req.rs
index fd6aef5971..3734899bc0 100644
--- a/crates/rust-analyzer/src/req.rs
+++ b/crates/rust-analyzer/src/req.rs
@@ -12,10 +12,9 @@ pub use lsp_types::{
     DocumentSymbolResponse, FileSystemWatcher, Hover, InitializeResult, MessageType,
     PartialResultParams, ProgressParams, ProgressParamsValue, ProgressToken,
     PublishDiagnosticsParams, ReferenceParams, Registration, RegistrationParams, SelectionRange,
-    SelectionRangeParams, SemanticTokensParams, SemanticTokensRangeParams,
-    SemanticTokensRangeResult, SemanticTokensResult, ServerCapabilities, ShowMessageParams,
-    SignatureHelp, SymbolKind, TextDocumentEdit, TextDocumentPositionParams, TextEdit,
-    WorkDoneProgressParams, WorkspaceEdit, WorkspaceSymbolParams,
+    SelectionRangeParams, SemanticTokensParams, SemanticTokensResult, ServerCapabilities,
+    ShowMessageParams, SignatureHelp, SymbolKind, TextDocumentEdit, TextDocumentPositionParams,
+    TextEdit, WorkDoneProgressParams, WorkspaceEdit, WorkspaceSymbolParams,
 };
 
 pub enum AnalyzerStatus {}
@@ -112,7 +111,7 @@ pub struct PublishDecorationsParams {
 #[serde(rename_all = "camelCase")]
 pub struct Decoration {
     pub range: Range,
-    pub tag: String,
+    pub tag: &'static str,
     pub binding_hash: Option<String>,
 }
 
diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json
index 6901363fc5..0288a468e0 100644
--- a/editors/code/package-lock.json
+++ b/editors/code/package-lock.json
@@ -112,6 +112,12 @@
                 "@types/node": "*"
             }
         },
+        "@types/throttle-debounce": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/@types/throttle-debounce/-/throttle-debounce-2.1.0.tgz",
+            "integrity": "sha512-5eQEtSCoESnh2FsiLTxE121IiE60hnMqcb435fShf4bpLRjEu1Eoekht23y6zXS9Ts3l+Szu3TARnTsA0GkOkQ==",
+            "dev": true
+        },
         "@types/vscode": {
             "version": "1.42.0",
             "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.42.0.tgz",
@@ -1511,6 +1517,11 @@
             "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
             "dev": true
         },
+        "throttle-debounce": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-2.1.0.tgz",
+            "integrity": "sha512-AOvyNahXQuU7NN+VVvOOX+uW6FPaWdAOdRP5HfwYxAfCzXTFKRMoIMk+n+po318+ktcChx+F1Dd91G3YHeMKyg=="
+        },
         "through": {
             "version": "2.3.8",
             "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
diff --git a/editors/code/package.json b/editors/code/package.json
index 2a89987e84..dff535fcd1 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -18,7 +18,6 @@
     "engines": {
         "vscode": "^1.42.0"
     },
-    "enableProposedApi": true,
     "scripts": {
         "vscode:prepublish": "tsc && rollup -c",
         "package": "vsce package -o rust-analyzer.vsix",
@@ -28,6 +27,7 @@
     "dependencies": {
         "jsonc-parser": "^2.1.0",
         "node-fetch": "^2.6.0",
+        "throttle-debounce": "^2.1.0",
         "vscode-languageclient": "^6.1.1"
     },
     "devDependencies": {
@@ -35,6 +35,7 @@
         "@rollup/plugin-node-resolve": "^7.1.1",
         "@types/node": "^12.12.27",
         "@types/node-fetch": "^2.5.4",
+        "@types/throttle-debounce": "^2.1.0",
         "@types/vscode": "^1.42.0",
         "@typescript-eslint/eslint-plugin": "^2.20.0",
         "@typescript-eslint/parser": "^2.20.0",
@@ -168,11 +169,6 @@
             "type": "object",
             "title": "Rust Analyzer",
             "properties": {
-                "rust-analyzer.highlighting.semanticTokens": {
-                    "type": "boolean",
-                    "default": false,
-                    "description": "Use proposed semantic tokens API for syntax highlighting"
-                },
                 "rust-analyzer.highlightingOn": {
                     "type": "boolean",
                     "default": false,
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts
index 44bd04c21b..aaf2ef40e3 100644
--- a/editors/code/src/client.ts
+++ b/editors/code/src/client.ts
@@ -3,7 +3,6 @@ import * as vscode from 'vscode';
 
 import { Config } from './config';
 import { CallHierarchyFeature } from 'vscode-languageclient/lib/callHierarchy.proposed';
-import { SemanticTokensFeature } from 'vscode-languageclient/lib/semanticTokens.proposed';
 
 export async function createClient(config: Config, serverPath: string): Promise<lc.LanguageClient> {
     // '.' Is the fallback if no folder is open
@@ -27,7 +26,7 @@ export async function createClient(config: Config, serverPath: string): Promise<
     const clientOptions: lc.LanguageClientOptions = {
         documentSelector: [{ scheme: 'file', language: 'rust' }],
         initializationOptions: {
-            publishDecorations: !config.highlightingSemanticTokens,
+            publishDecorations: true,
             lruCapacity: config.lruCapacity,
             maxInlayHintLength: config.maxInlayHintLength,
             cargoWatchEnable: cargoWatchOpts.enable,
@@ -84,10 +83,5 @@ export async function createClient(config: Config, serverPath: string): Promise<
     // Here we want to just enable CallHierarchyFeature since it is available on stable.
     // Note that while the CallHierarchyFeature is stable the LSP protocol is not.
     res.registerFeature(new CallHierarchyFeature(res));
-
-    if (config.highlightingSemanticTokens) {
-        res.registerFeature(new SemanticTokensFeature(res));
-    }
-
     return res;
 }
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index bf915102c8..47e8cd45d0 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -22,7 +22,6 @@ export class Config {
     private static readonly requiresReloadOpts = [
         "cargoFeatures",
         "cargo-watch",
-        "highlighting.semanticTokens"
     ]
         .map(opt => `${Config.rootSection}.${opt}`);
 
@@ -144,7 +143,6 @@ export class Config {
     // We don't do runtime config validation here for simplicity. More on stackoverflow:
     // https://stackoverflow.com/questions/60135780/what-is-the-best-way-to-type-check-the-configuration-for-vscode-extension
 
-    get highlightingSemanticTokens() { return this.cfg.get("highlighting.semanticTokens") as boolean; }
     get highlightingOn() { return this.cfg.get("highlightingOn") as boolean; }
     get rainbowHighlightingOn() { return this.cfg.get("rainbowHighlightingOn") as boolean; }
     get lruCapacity() { return this.cfg.get("lruCapacity") as null | number; }
diff --git a/editors/code/src/installation/download_artifact.ts b/editors/code/src/installation/download_artifact.ts
index 8ed99bf0a0..356723abaf 100644
--- a/editors/code/src/installation/download_artifact.ts
+++ b/editors/code/src/installation/download_artifact.ts
@@ -5,6 +5,7 @@ import { strict as assert } from "assert";
 
 import { ArtifactReleaseInfo } from "./interfaces";
 import { downloadFile } from "./download_file";
+import { throttle } from "throttle-debounce";
 
 /**
  * Downloads artifact from given `downloadUrl`.
@@ -37,15 +38,19 @@ export async function downloadArtifact(
         async (progress, _cancellationToken) => {
             let lastPrecentage = 0;
             const filePermissions = 0o755; // (rwx, r_x, r_x)
-            await downloadFile(downloadUrl, installationPath, filePermissions, (readBytes, totalBytes) => {
-                const newPercentage = (readBytes / totalBytes) * 100;
-                progress.report({
-                    message: newPercentage.toFixed(0) + "%",
-                    increment: newPercentage - lastPrecentage
-                });
+            await downloadFile(downloadUrl, installationPath, filePermissions, throttle(
+                200,
+                /* noTrailing: */ true,
+                (readBytes, totalBytes) => {
+                    const newPercentage = (readBytes / totalBytes) * 100;
+                    progress.report({
+                        message: newPercentage.toFixed(0) + "%",
+                        increment: newPercentage - lastPrecentage
+                    });
 
-                lastPrecentage = newPercentage;
-            });
+                    lastPrecentage = newPercentage;
+                })
+            );
         }
     );
 }
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index ecf53cf775..424ff1ac3d 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -89,9 +89,7 @@ export async function activate(context: vscode.ExtensionContext) {
 
     activateStatusDisplay(ctx);
 
-    if (!ctx.config.highlightingSemanticTokens) {
-        activateHighlighting(ctx);
-    }
+    activateHighlighting(ctx);
     activateInlayHints(ctx);
 }
 


Test patch:
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs
index 69deddcb5b..e6e8d71103 100644
--- a/crates/test_utils/src/lib.rs
+++ b/crates/test_utils/src/lib.rs
@@ -83,7 +83,6 @@ fn try_extract_range(text: &str) -> Option<(TextRange, String)> {
     Some((TextRange::from_to(start, end), text))
 }
 
-#[derive(Clone, Copy)]
 pub enum RangeOrOffset {
     Range(TextRange),
     Offset(TextUnit),