Repository: rust-analyzer/rust-analyzer
Pull request #2169 (2019-11-03 18:06:37)

MBE: Mapping spans for goto definition
Currently, go to definition gives the wrong span in MBE.  This PR implement a mapping mechanism to fix it and it could be used for future MBE hygiene implementation.

The basic idea of the mapping is:
1. When expanding the macro, generated 2 `TokenMap` which maps the macro args and macro defs between tokens and input text-ranges.
2. Before converting generated `TokenTree` to `SyntaxNode`, generated a `ExpandedRangeMap` which is a mapping between token and output text-ranges.
3. Using these 3 mappings to construct an `ExpansionInfo`  which can map between input text ranges and output text ranges.


edwin0cheng: I did want to implement it in June, however there are some situations happened in my hometown (Hong Kong) which used all my spared time. :(
matklad: @edwin0cheng super excited to have you back! 

I will try to review this tomorrow! At the high-level, this looks about right. I am not sure about using a global atomic for map_id, I think we might want a more local device to track various expansions (though not sure how that would work exactly). 
matklad: Ok, so I think I know how we can sort out token identity without global counter. The problem originates in this code, I think:

```
    let loc = db.lookup_intern_macro(id);
    let macro_arg = db.macro_arg(id).ok_or("Fail to args in to tt::TokenTree")?;

    let macro_rules = db.macro_def(loc.def).ok_or("Fail to find macro definition")?;
    let tt = macro_rules.0.expand(&macro_arg.0).map_err(|err| format!("{:?}", err))?;
```

Here, both `macro_arg` and `macro_rules` contain tokens using some local numeration. That is, token with id 92 in `arg` might be `foo`, and token with the same id in `macro_rules` might be `bar`. The resulting token stream `tt` however contains a mixture of tokens from both sources, so you might get two different tokens with the same id!

What we can do here is to re-number the tokens during expansion. Specifically:

* when we create `macro_rules`, we note the highest id of the token we have as `shift`
* when we expand macro rules, if we need to output a token from definition, we just re-use its id
* if we need to output a token from the argument, we increase its id by `shift` (so it's guaranteed to not to collide with anything from the definition)
* finally, when we have a `HirFileId` of the expansion, we can look up the original value of `shift` and classify node to the arg/def by comparing it's id with `shift`.

This is very similar to how [de Bruijn indexes](https://en.wikipedia.org/wiki/De_Bruijn_index) work, except that we don't have equivalent of closures, so we can shift all arg tokens without checking if they are free or bound. 

@edwin0cheng let's add `shift` in a separate PR, as it seems to be an isolatable chunk of work?  That is:

* add `shift` field to MacroRules
* use `shift` when expanding macros
edwin0cheng: I think your method is reasonable and much better than a global counter solution. However, I am a little bit concerned about this step:

> if we need to output a token from the argument, we increase its id by shift (so it's guaranteed to not to collide with anything from the definition)

May I just clone the whole argument token tree and `shift` the TokenId inside `mbe_expander::expand` instead? It is because for "outputting" tokens, we  just "copy" the TokenTree, and this kind of copy exists in all over the place in mbe_expander. 

> @edwin0cheng let's add `shift` in a separate PR, as it seems to be an isolatable chunk of work? 
Sure, I will submit another PR for that after this one is merged.

matklad: >May I just clone the whole argument token tree and shift the TokenId inside mbe_expander::expand instead? It is because for "outputting" tokens, we just "copy" the TokenTree, and this kind of copy exists in all over the place in mbe_expander.

Yeah, I think such *eager* shifting might be better!
matklad: >Sure, I will submit another PR for that after this one is merged.

Sorry, I meant that we can *first* add `shift` (as it is an independent change), and than rebase this PR on top, just to keep steps smaller. 
edwin0cheng: @matklad #2173 is submitted for the "shift" :) 

Patch:
diff --git a/Cargo.lock b/Cargo.lock
index b67637fe8b..3879204d9e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -8,6 +8,14 @@ dependencies = [
  "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "arrayvec"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "arrayvec"
 version = "0.5.1"
@@ -43,7 +51,7 @@ name = "backtrace-sys"
 version = "0.1.32"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cc 1.0.46 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
@@ -107,7 +115,7 @@ dependencies = [
 
 [[package]]
 name = "cc"
-version = "1.0.47"
+version = "1.0.46"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
@@ -212,7 +220,7 @@ dependencies = [
 
 [[package]]
 name = "console"
-version = "0.9.1"
+version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "clicolors-control 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -227,44 +235,44 @@ dependencies = [
 
 [[package]]
 name = "crossbeam"
-version = "0.7.3"
+version = "0.7.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
- "crossbeam-channel 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "crossbeam-epoch 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "crossbeam-queue 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-channel 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "crossbeam-channel"
-version = "0.4.0"
+version = "0.3.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "crossbeam-deque"
-version = "0.7.2"
+version = "0.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "crossbeam-epoch 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "crossbeam-epoch"
-version = "0.8.0"
+version = "0.7.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)",
  "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
- "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "memoffset 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "memoffset 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
@@ -276,14 +284,6 @@ dependencies = [
  "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
-[[package]]
-name = "crossbeam-queue"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
 [[package]]
 name = "crossbeam-utils"
 version = "0.6.6"
@@ -293,16 +293,6 @@ dependencies = [
  "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
-[[package]]
-name = "crossbeam-utils"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
- "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
- "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
 [[package]]
 name = "derive-new"
 version = "0.5.8"
@@ -364,7 +354,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "flexi_logger"
-version = "0.14.5"
+version = "0.14.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -483,7 +473,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "unicode-normalization 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-normalization 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -496,10 +486,10 @@ dependencies = [
 
 [[package]]
 name = "indicatif"
-version = "0.13.0"
+version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "console 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "console 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "number_prefix 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -528,7 +518,7 @@ name = "insta"
 version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "console 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "console 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -573,7 +563,7 @@ name = "jemalloc-sys"
 version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cc 1.0.46 (registry+https://github.com/rust-lang/crates.io-index)",
  "fs_extra 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
@@ -649,10 +639,10 @@ dependencies = [
 
 [[package]]
 name = "lsp-server"
-version = "0.3.0"
+version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "crossbeam-channel 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-channel 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -675,11 +665,6 @@ name = "matches"
 version = "0.1.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
-[[package]]
-name = "maybe-uninit"
-version = "2.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
 [[package]]
 name = "memchr"
 version = "2.2.1"
@@ -687,7 +672,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "memoffset"
-version = "0.5.3"
+version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -742,6 +727,11 @@ dependencies = [
  "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "nodrop"
+version = "0.1.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
 [[package]]
 name = "notify"
 version = "4.0.14"
@@ -821,7 +811,7 @@ dependencies = [
  "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
  "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
+ "smallvec 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
  "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
@@ -906,10 +896,10 @@ dependencies = [
 
 [[package]]
 name = "psm"
-version = "0.1.6"
+version = "0.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cc 1.0.46 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -949,13 +939,13 @@ dependencies = [
 name = "ra_batch"
 version = "0.1.0"
 dependencies = [
- "crossbeam-channel 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-channel 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "ra_db 0.1.0",
  "ra_hir 0.1.0",
  "ra_ide_api 0.1.0",
  "ra_project_model 0.1.0",
- "ra_vfs 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "ra_vfs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "ra_vfs_glob 0.1.0",
  "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
@@ -974,8 +964,8 @@ dependencies = [
 name = "ra_cli"
 version = "0.1.0"
 dependencies = [
- "flexi_logger 0.14.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "indicatif 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "flexi_logger 0.14.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "indicatif 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "pico-args 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "ra_batch 0.1.0",
  "ra_db 0.1.0",
@@ -1097,11 +1087,11 @@ dependencies = [
 name = "ra_lsp_server"
 version = "0.1.0"
 dependencies = [
- "crossbeam-channel 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "flexi_logger 0.14.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-channel 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "flexi_logger 0.14.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "jod-thread 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "lsp-server 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lsp-server 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "lsp-types 0.61.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "ra_ide_api 0.1.0",
@@ -1109,7 +1099,7 @@ dependencies = [
  "ra_project_model 0.1.0",
  "ra_syntax 0.1.0",
  "ra_text_edit 0.1.0",
- "ra_vfs 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "ra_vfs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "ra_vfs_glob 0.1.0",
  "relative-path 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1129,7 +1119,7 @@ dependencies = [
  "ra_syntax 0.1.0",
  "ra_tt 0.1.0",
  "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
+ "smallvec 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
  "test_utils 0.1.0",
 ]
 
@@ -1200,10 +1190,10 @@ dependencies = [
 
 [[package]]
 name = "ra_vfs"
-version = "0.5.1"
+version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "crossbeam-channel 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-channel 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
  "jod-thread 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "notify 4.0.14 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1218,7 +1208,7 @@ name = "ra_vfs_glob"
 version = "0.1.0"
 dependencies = [
  "globset 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "ra_vfs 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "ra_vfs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -1368,7 +1358,7 @@ name = "rayon"
 version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "rayon-core 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
@@ -1378,7 +1368,7 @@ name = "rayon-core"
 version = "1.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1486,7 +1476,7 @@ name = "salsa"
 version = "0.13.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "crossbeam 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "derive-new 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "indexmap 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1494,7 +1484,7 @@ dependencies = [
  "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "salsa-macros 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
+ "smallvec 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -1591,11 +1581,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "smallvec"
-version = "0.6.13"
+version = "0.6.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
-]
 
 [[package]]
 name = "smol_str"
@@ -1610,10 +1597,10 @@ name = "stacker"
 version = "0.1.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cc 1.0.46 (registry+https://github.com/rust-lang/crates.io-index)",
  "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
- "psm 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "psm 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
@@ -1711,10 +1698,10 @@ dependencies = [
 
 [[package]]
 name = "unicode-normalization"
-version = "0.1.9"
+version = "0.1.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
+ "smallvec 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -1845,6 +1832,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [metadata]
 "checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d"
+"checksum arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9"
 "checksum arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8"
 "checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90"
 "checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2"
@@ -1858,7 +1846,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 "checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5"
 "checksum c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb"
 "checksum cargo_metadata 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8d2d1617e838936c0d2323a65cc151e03ae19a7678dd24f72bccf27119b90a5d"
-"checksum cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)" = "aa87058dce70a3ff5621797f1506cb837edd02ac4c0ae642b4542dce802908b8"
+"checksum cc 1.0.46 (registry+https://github.com/rust-lang/crates.io-index)" = "0213d356d3c4ea2c18c40b037c3be23cd639825c18f25ee670ac7813beeef99c"
 "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
 "checksum chalk-derive 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05)" = "<none>"
 "checksum chalk-engine 0.9.0 (git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05)" = "<none>"
@@ -1869,15 +1857,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 "checksum chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e8493056968583b0193c1bb04d6f7684586f3726992d6c573261941a895dbd68"
 "checksum clicolors-control 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90082ee5dcdd64dc4e9e0d37fbf3ee325419e39c0092191e0393df65518f741e"
 "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
-"checksum console 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f5d540c2d34ac9dd0deb5f3b5f54c36c79efa78f6b3ad19106a554d07a7b5d9f"
-"checksum crossbeam 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "69323bff1fb41c635347b8ead484a5ca6c3f11914d784170b158d8449ab07f8e"
-"checksum crossbeam-channel 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "acec9a3b0b3559f15aee4f90746c4e5e293b701c0f7d3925d24e01645267b68c"
-"checksum crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3aa945d63861bfe624b55d153a39684da1e8c0bc8fba932f7ee3a3c16cea3ca"
-"checksum crossbeam-epoch 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5064ebdbf05ce3cb95e45c8b086f72263f4166b29b97f6baff7ef7fe047b55ac"
+"checksum console 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "62828f51cfa18f8c31d3d55a43c6ce6af3f87f754cba9fbba7ff38089b9f5612"
+"checksum crossbeam 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2d818a4990769aac0c7ff1360e233ef3a41adcb009ebb2036bf6915eb0f6b23c"
+"checksum crossbeam-channel 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c8ec7fcd21571dc78f96cc96243cab8d8f035247c3efd16c687be154c3fa9efa"
+"checksum crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b18cd2e169ad86297e6bc0ad9aa679aee9daa4f19e8163860faf7c164e4f5a71"
+"checksum crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fedcd6772e37f3da2a9af9bf12ebe046c0dfe657992377b4df982a2b54cd37a9"
 "checksum crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b"
-"checksum crossbeam-queue 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dfd6515864a82d2f877b42813d4553292c6659498c9a2aa31bab5a15243c2700"
 "checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6"
-"checksum crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce446db02cdc3165b94ae73111e570793400d0794e46125cc4056c81cbb039f4"
 "checksum derive-new 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)" = "71f31892cd5c62e414316f2963c5689242c43d8e7bbcaaeca97e5e28c95d91d9"
 "checksum difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
 "checksum drop_bomb 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "69b26e475fd29098530e709294e94e661974c851aed42512793f120fed4e199f"
@@ -1887,7 +1873,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 "checksum encode_unicode 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
 "checksum filetime 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6bd7380b54ced79dda72ecc35cc4fbbd1da6bba54afaa37e96fd1c2a308cd469"
 "checksum fixedbitset 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "86d4de0081402f5e88cdac65c8dcdcc73118c1a7a465e2a05f0da05843a8ea33"
-"checksum flexi_logger 0.14.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a13ea6b8a4debecf47bf3966d56db0e21366bc3a3649ba159e1a9e6fdd36a4f4"
+"checksum flexi_logger 0.14.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d20d127f22dd09495f5a5de83dc9f51cf90932735b642910062c577082dd3b36"
 "checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3"
 "checksum format-buf 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f7aea5a5909a74969507051a3b17adc84737e31a5f910559892aedce026f4d53"
 "checksum fs_extra 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5f2a4a2034423744d2cc7ca2068453168dcdb82c438419e639a26bd87839c674"
@@ -1904,7 +1890,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 "checksum hermit-abi 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "307c3c9f937f38e3534b1d6447ecf090cafcc9744e4a6360e8b037b2cf5af120"
 "checksum idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9"
 "checksum indexmap 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712d7b3ea5827fcb9d4fda14bf4da5f136f0db2ae9c8f4bd4e2d1c6fde4e6db2"
-"checksum indicatif 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8572bccfb0665e70b7faf44ee28841b8e0823450cd4ad562a76b5a3c4bf48487"
+"checksum indicatif 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a8d596a9576eaa1446996092642d72bfef35cf47243129b7ab883baf5faec31e"
 "checksum inotify 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "40b54539f3910d6f84fbf9a643efd6e3aa6e4f001426c0329576128255994718"
 "checksum inotify-sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e74a1aa87c59aeff6ef2cc2fa62d41bc43f54952f55652656b18a02fd5e356c0"
 "checksum insta 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0d499dc062e841590a67230d853bce62d0abeb91304927871670b7c55c461349"
@@ -1924,16 +1910,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 "checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83"
 "checksum lock_api 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f8912e782533a93a167888781b836336a6ca5da6175c05944c86cf28c31104dc"
 "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
-"checksum lsp-server 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0ba36405bd742139ab79c246ca5adb7fde2fe1a0f495e2c8e2f607b607dedb12"
+"checksum lsp-server 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "148cfb1c0b3295c23d9fb4a20fd1b242f5e6f46c525fdcc7f5c0a65710362012"
 "checksum lsp-types 0.61.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fa3268fbe8beb2795c2fb327bf44f4f3d24f5fe9ebc18d7e2980afd444d72bcf"
 "checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
-"checksum maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
 "checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e"
-"checksum memoffset 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "75189eb85871ea5c2e2c15abbdd541185f63b408415e5051f5cac122d8c774b9"
+"checksum memoffset 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a85c1a8c329f11437034d7313dca647c79096523533a1c79e86f1d0f657c7cc"
 "checksum mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)" = "83f51996a3ed004ef184e16818edc51fadffe8e7ca68be67f9dee67d84d0ff23"
 "checksum mio-extras 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "46e73a04c2fa6250b8d802134d56d554a9ec2922bf977777c805ea5def61ce40"
 "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
 "checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88"
+"checksum nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
 "checksum notify 4.0.14 (registry+https://github.com/rust-lang/crates.io-index)" = "199628fc33b21bc767baa057490b00b382ecbae030803a7b36292422d15b778b"
 "checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09"
 "checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32"
@@ -1952,10 +1938,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 "checksum proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)" = "ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5"
 "checksum proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9c9e470a8dc4aeae2dee2f335e8f533e2d4b347e1434e5671afc49b054592f27"
 "checksum proptest 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cf147e022eacf0c8a054ab864914a7602618adba841d800a9a9868a5237a529f"
-"checksum psm 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b14fc68b454f875abc8354c2555e1d56596f74833ddc0f77f87f4871ed6a30e0"
+"checksum psm 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "54639efee26275dfadc49644039c31c73673edde0ef2815acf25a8db526ee193"
 "checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0"
 "checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe"
-"checksum ra_vfs 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a95d3d1edc70a98d9e42f145bc92c2071dfdc532571591c9eac407c0e4feb89"
+"checksum ra_vfs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc8f508bc7a9871b81b6ee75e15d0bbc9f81a96698364a090a8d10b0f8b970b7"
 "checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca"
 "checksum rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3ae1b169243eaf61759b8475a998f0a385e42042370f3a7dbaf35246eacc8412"
 "checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef"
@@ -1998,7 +1984,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 "checksum serde_repr 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "cd02c7587ec314570041b2754829f84d873ced14a96d1fd1823531e11db40573"
 "checksum serde_yaml 0.8.11 (registry+https://github.com/rust-lang/crates.io-index)" = "691b17f19fc1ec9d94ec0b5864859290dff279dbd7b03f017afda54eb36c3c35"
 "checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
-"checksum smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6"
+"checksum smallvec 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "533e29e15d0748f28afbaf4ff7cab44d73e483a8e50b38c40bd13b7f3d48f542"
 "checksum smol_str 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "34836c9a295c62c2ce3514471117c5cb269891e8421b2aafdd910050576c4d8b"
 "checksum stacker 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "d96fc4f13a0ac088e9a3cd9af1cc8c5cc1ab5deb2145cef661267dfc9c542f8a"
 "checksum superslice 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ab16ced94dbd8a46c82fd81e3ed9a8727dac2977ea869d217bcc4ea1f122e81f"
@@ -2011,7 +1997,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 "checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f"
 "checksum unicase 2.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2e2e6bd1e59e56598518beb94fd6db628ded570326f0a98c679a304bd9f00150"
 "checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
-"checksum unicode-normalization 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "09c8070a9942f5e7cfccd93f490fdebd230ee3c3c9f107cb25bad5351ef671cf"
+"checksum unicode-normalization 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "141339a08b982d942be2ca06ff8b076563cbe223d1befd5450716790d44e2426"
 "checksum unicode-segmentation 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49f5526225fd8b77342d5986ab5f6055552e9c0776193b5b63fd53b46debfad7"
 "checksum unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7007dbd421b92cc6e28410fe7362e2e0a2503394908f417b68ec8d1c364c4e20"
 "checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index 39c1c283f4..38599d4f17 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -9,8 +9,6 @@ mod assist_ctx;
 mod marks;
 #[cfg(test)]
 mod doc_tests;
-#[cfg(test)]
-mod test_db;
 
 use hir::db::HirDatabase;
 use ra_db::FileRange;
@@ -148,19 +146,20 @@ mod assists {
 
 #[cfg(test)]
 mod helpers {
-    use ra_db::{fixture::WithFixture, FileRange};
+    use hir::mock::MockDatabase;
+    use ra_db::FileRange;
     use ra_syntax::TextRange;
     use test_utils::{add_cursor, assert_eq_text, extract_offset, extract_range};
 
-    use crate::{test_db::TestDB, Assist, AssistCtx};
+    use crate::{Assist, AssistCtx};
 
     pub(crate) fn check_assist(
-        assist: fn(AssistCtx<TestDB>) -> Option<Assist>,
+        assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
         before: &str,
         after: &str,
     ) {
         let (before_cursor_pos, before) = extract_offset(before);
-        let (db, file_id) = TestDB::with_single_file(&before);
+        let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
         let frange =
             FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
         let assist =
@@ -183,12 +182,12 @@ mod helpers {
     }
 
     pub(crate) fn check_assist_range(
-        assist: fn(AssistCtx<TestDB>) -> Option<Assist>,
+        assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
         before: &str,
         after: &str,
     ) {
         let (range, before) = extract_range(before);
-        let (db, file_id) = TestDB::with_single_file(&before);
+        let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
         let frange = FileRange { file_id, range };
         let assist =
             AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable");
@@ -205,12 +204,12 @@ mod helpers {
     }
 
     pub(crate) fn check_assist_target(
-        assist: fn(AssistCtx<TestDB>) -> Option<Assist>,
+        assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
         before: &str,
         target: &str,
     ) {
         let (before_cursor_pos, before) = extract_offset(before);
-        let (db, file_id) = TestDB::with_single_file(&before);
+        let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
         let frange =
             FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
         let assist =
@@ -225,12 +224,12 @@ mod helpers {
     }
 
     pub(crate) fn check_assist_range_target(
-        assist: fn(AssistCtx<TestDB>) -> Option<Assist>,
+        assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
         before: &str,
         target: &str,
     ) {
         let (range, before) = extract_range(before);
-        let (db, file_id) = TestDB::with_single_file(&before);
+        let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
         let frange = FileRange { file_id, range };
         let assist =
             AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable");
@@ -244,11 +243,11 @@ mod helpers {
     }
 
     pub(crate) fn check_assist_not_applicable(
-        assist: fn(AssistCtx<TestDB>) -> Option<Assist>,
+        assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
         before: &str,
     ) {
         let (before_cursor_pos, before) = extract_offset(before);
-        let (db, file_id) = TestDB::with_single_file(&before);
+        let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
         let frange =
             FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
         let assist = AssistCtx::with_ctx(&db, frange, true, assist);
@@ -256,11 +255,11 @@ mod helpers {
     }
 
     pub(crate) fn check_assist_range_not_applicable(
-        assist: fn(AssistCtx<TestDB>) -> Option<Assist>,
+        assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
         before: &str,
     ) {
         let (range, before) = extract_range(before);
-        let (db, file_id) = TestDB::with_single_file(&before);
+        let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
         let frange = FileRange { file_id, range };
         let assist = AssistCtx::with_ctx(&db, frange, true, assist);
         assert!(assist.is_none());
@@ -269,17 +268,16 @@ mod helpers {
 
 #[cfg(test)]
 mod tests {
-    use ra_db::{fixture::WithFixture, FileRange};
+    use hir::mock::MockDatabase;
+    use ra_db::FileRange;
     use ra_syntax::TextRange;
     use test_utils::{extract_offset, extract_range};
 
-    use crate::test_db::TestDB;
-
     #[test]
     fn assist_order_field_struct() {
         let before = "struct Foo { <|>bar: u32 }";
         let (before_cursor_pos, before) = extract_offset(before);
-        let (db, file_id) = TestDB::with_single_file(&before);
+        let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
         let frange =
             FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
         let assists = super::assists(&db, frange);
@@ -300,7 +298,7 @@ mod tests {
             }
         }";
         let (range, before) = extract_range(before);
-        let (db, file_id) = TestDB::with_single_file(&before);
+        let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
         let frange = FileRange { file_id, range };
         let assists = super::assists(&db, frange);
         let mut assists = assists.iter();
diff --git a/crates/ra_batch/Cargo.toml b/crates/ra_batch/Cargo.toml
index d08c5ceac3..c85da7a2ce 100644
--- a/crates/ra_batch/Cargo.toml
+++ b/crates/ra_batch/Cargo.toml
@@ -7,7 +7,7 @@ authors = ["rust-analyzer developers"]
 [dependencies]
 log = "0.4.5"
 rustc-hash = "1.0"
-crossbeam-channel = "0.4.0"
+crossbeam-channel = "0.3.5"
 
 ra_vfs = "0.5.0"
 ra_vfs_glob = { path = "../ra_vfs_glob" }
diff --git a/crates/ra_cli/Cargo.toml b/crates/ra_cli/Cargo.toml
index c9d3bdb776..fcd102e8b0 100644
--- a/crates/ra_cli/Cargo.toml
+++ b/crates/ra_cli/Cargo.toml
@@ -8,7 +8,7 @@ publish = false
 [dependencies]
 pico-args = "0.3.0"
 flexi_logger = "0.14.0"
-indicatif = "0.13.0"
+indicatif = "0.12.0"
 
 ra_syntax = { path = "../ra_syntax" }
 ra_ide_api = { path = "../ra_ide_api" }
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs
index 9d0db80249..181c5d47af 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -2,7 +2,6 @@
 
 pub(crate) mod src;
 pub(crate) mod docs;
-pub(crate) mod attrs;
 
 use std::sync::Arc;
 
@@ -31,8 +30,8 @@ use crate::{
     impl_block::ImplBlock,
     resolve::{Resolver, Scope, TypeNs},
     traits::TraitData,
-    ty::{InferenceResult, Namespace, TraitRef},
-    Either, HasSource, ImportId, Name, ScopeDef, Ty,
+    ty::{InferenceResult, TraitRef},
+    Either, HasSource, Name, ScopeDef, Ty, {ImportId, Namespace},
 };
 
 /// hir::Crate describes a single crate. It's the main interface with which
diff --git a/crates/ra_hir/src/code_model/attrs.rs b/crates/ra_hir/src/code_model/attrs.rs
deleted file mode 100644
index f7db36b668..0000000000
--- a/crates/ra_hir/src/code_model/attrs.rs
+++ /dev/null
@@ -1,92 +0,0 @@
-//! FIXME: write short doc here
-
-use crate::{
-    db::{AstDatabase, DefDatabase, HirDatabase},
-    Adt, Const, Enum, EnumVariant, FieldSource, Function, HasSource, MacroDef, Module, Static,
-    Struct, StructField, Trait, TypeAlias, Union,
-};
-use hir_def::attr::Attr;
-use hir_expand::hygiene::Hygiene;
-use ra_syntax::ast;
-use std::sync::Arc;
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-pub enum AttrDef {
-    Module(Module),
-    StructField(StructField),
-    Adt(Adt),
-    Function(Function),
-    EnumVariant(EnumVariant),
-    Static(Static),
-    Const(Const),
-    Trait(Trait),
-    TypeAlias(TypeAlias),
-    MacroDef(MacroDef),
-}
-
-impl_froms!(
-    AttrDef: Module,
-    StructField,
-    Adt(Struct, Enum, Union),
-    EnumVariant,
-    Static,
-    Const,
-    Function,
-    Trait,
-    TypeAlias,
-    MacroDef
-);
-
-pub trait Attrs {
-    fn attrs(&self, db: &impl HirDatabase) -> Option<Arc<[Attr]>>;
-}
-
-pub(crate) fn attributes_query(
-    db: &(impl DefDatabase + AstDatabase),
-    def: AttrDef,
-) -> Option<Arc<[Attr]>> {
-    match def {
-        AttrDef::Module(it) => {
-            let src = it.declaration_source(db)?;
-            let hygiene = Hygiene::new(db, src.file_id);
-            Attr::from_attrs_owner(&src.ast, &hygiene)
-        }
-        AttrDef::StructField(it) => match it.source(db).ast {
-            FieldSource::Named(named) => {
-                let src = it.source(db);
-                let hygiene = Hygiene::new(db, src.file_id);
-                Attr::from_attrs_owner(&named, &hygiene)
-            }
-            FieldSource::Pos(..) => None,
-        },
-        AttrDef::Adt(it) => match it {
-            Adt::Struct(it) => attrs_from_ast(it, db),
-            Adt::Enum(it) => attrs_from_ast(it, db),
-            Adt::Union(it) => attrs_from_ast(it, db),
-        },
-        AttrDef::EnumVariant(it) => attrs_from_ast(it, db),
-        AttrDef::Static(it) => attrs_from_ast(it, db),
-        AttrDef::Const(it) => attrs_from_ast(it, db),
-        AttrDef::Function(it) => attrs_from_ast(it, db),
-        AttrDef::Trait(it) => attrs_from_ast(it, db),
-        AttrDef::TypeAlias(it) => attrs_from_ast(it, db),
-        AttrDef::MacroDef(it) => attrs_from_ast(it, db),
-    }
-}
-
-fn attrs_from_ast<T, D>(node: T, db: &D) -> Option<Arc<[Attr]>>
-where
-    T: HasSource,
-    T::Ast: ast::AttrsOwner,
-    D: DefDatabase + AstDatabase,
-{
-    let src = node.source(db);
-    let hygiene = Hygiene::new(db, src.file_id);
-    Attr::from_attrs_owner(&src.ast, &hygiene)
-}
-
-impl<T: Into<AttrDef> + Copy> Attrs for T {
-    fn attrs(&self, db: &impl HirDatabase) -> Option<Arc<[Attr]>> {
-        db.attrs((*self).into())
-    }
-}
diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs
index 75c322c999..eb66325f7f 100644
--- a/crates/ra_hir/src/db.rs
+++ b/crates/ra_hir/src/db.rs
@@ -2,7 +2,6 @@
 
 use std::sync::Arc;
 
-use hir_def::attr::Attr;
 use ra_db::salsa;
 use ra_syntax::SmolStr;
 
@@ -15,10 +14,10 @@ use crate::{
     traits::TraitData,
     ty::{
         method_resolution::CrateImplBlocks, traits::Impl, CallableDef, FnSig, GenericPredicate,
-        InferenceResult, Namespace, Substs, Ty, TypableDef, TypeCtor,
+        InferenceResult, Substs, Ty, TypableDef, TypeCtor,
     },
     type_alias::TypeAliasData,
-    Const, ConstData, Crate, DefWithBody, ExprScopes, FnData, Function, Module, Static,
+    Const, ConstData, Crate, DefWithBody, ExprScopes, FnData, Function, Module, Namespace, Static,
     StructField, Trait, TypeAlias,
 };
 
@@ -76,9 +75,6 @@ pub trait DefDatabase: HirDebugDatabase + DefDatabase2 {
 
     #[salsa::invoke(crate::code_model::docs::documentation_query)]
     fn documentation(&self, def: crate::DocDef) -> Option<crate::Documentation>;
-
-    #[salsa::invoke(crate::code_model::attrs::attributes_query)]
-    fn attrs(&self, def: crate::AttrDef) -> Option<Arc<[Attr]>>;
 }
 
 #[salsa::query_group(HirDatabaseStorage)]
diff --git a/crates/ra_hir/src/expr/scope.rs b/crates/ra_hir/src/expr/scope.rs
index c14c2ab663..5496822e72 100644
--- a/crates/ra_hir/src/expr/scope.rs
+++ b/crates/ra_hir/src/expr/scope.rs
@@ -174,11 +174,11 @@ fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope
 
 #[cfg(test)]
 mod tests {
-    use ra_db::{fixture::WithFixture, SourceDatabase};
+    use ra_db::SourceDatabase;
     use ra_syntax::{algo::find_node_at_offset, ast, AstNode};
     use test_utils::{assert_eq_text, extract_offset};
 
-    use crate::{source_binder::SourceAnalyzer, test_db::TestDB};
+    use crate::{mock::MockDatabase, source_binder::SourceAnalyzer};
 
     fn do_check(code: &str, expected: &[&str]) {
         let (off, code) = extract_offset(code);
@@ -191,7 +191,7 @@ mod tests {
             buf
         };
 
-        let (db, file_id) = TestDB::with_single_file(&code);
+        let (db, _source_root, file_id) = MockDatabase::with_single_file(&code);
         let file = db.parse(file_id).ok().unwrap();
         let marker: ast::PathExpr = find_node_at_offset(file.syntax(), off).unwrap();
         let analyzer = SourceAnalyzer::new(&db, file_id, marker.syntax(), None);
@@ -288,7 +288,7 @@ mod tests {
     fn do_check_local_name(code: &str, expected_offset: u32) {
         let (off, code) = extract_offset(code);
 
-        let (db, file_id) = TestDB::with_single_file(&code);
+        let (db, _source_root, file_id) = MockDatabase::with_single_file(&code);
         let file = db.parse(file_id).ok().unwrap();
         let expected_name = find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into())
             .expect("failed to find a name at the target offset");
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs
index 131f6c797a..3ba99d92d7 100644
--- a/crates/ra_hir/src/lib.rs
+++ b/crates/ra_hir/src/lib.rs
@@ -29,6 +29,8 @@ macro_rules! impl_froms {
 pub mod debug;
 
 pub mod db;
+#[macro_use]
+pub mod mock;
 pub mod source_binder;
 
 mod ids;
@@ -49,8 +51,6 @@ mod code_model;
 
 pub mod from_source;
 
-#[cfg(test)]
-mod test_db;
 #[cfg(test)]
 mod marks;
 
@@ -61,7 +61,6 @@ use crate::{ids::MacroFileKind, resolve::Resolver};
 pub use crate::{
     adt::VariantDef,
     code_model::{
-        attrs::{AttrDef, Attrs},
         docs::{DocDef, Docs, Documentation},
         src::{HasBodySource, HasSource},
         Adt, AssocItem, Const, ConstData, Container, Crate, CrateDependency, DefWithBody, Enum,
@@ -82,7 +81,10 @@ pub use crate::{
 
 pub use hir_def::{
     builtin_type::BuiltinType,
-    nameres::{per_ns::PerNs, raw::ImportId},
+    nameres::{
+        per_ns::{Namespace, PerNs},
+        raw::ImportId,
+    },
     path::{Path, PathKind},
     type_ref::Mutability,
 };
diff --git a/crates/ra_hir/src/mock.rs b/crates/ra_hir/src/mock.rs
new file mode 100644
index 0000000000..ab97a09b91
--- /dev/null
+++ b/crates/ra_hir/src/mock.rs
@@ -0,0 +1,262 @@
+//! FIXME: write short doc here
+
+use std::{panic, sync::Arc};
+
+use hir_expand::diagnostics::DiagnosticSink;
+use parking_lot::Mutex;
+use ra_cfg::CfgOptions;
+use ra_db::{
+    salsa, CrateGraph, CrateId, Edition, FileId, FileLoader, FileLoaderDelegate, FilePosition,
+    RelativePath, RelativePathBuf, SourceDatabase, SourceDatabaseExt, SourceRoot, SourceRootId,
+};
+use rustc_hash::FxHashMap;
+use test_utils::{extract_offset, parse_fixture, CURSOR_MARKER};
+
+use crate::{db, debug::HirDebugHelper};
+
+pub const WORKSPACE: SourceRootId = SourceRootId(0);
+
+#[salsa::database(
+    ra_db::SourceDatabaseExtStorage,
+    ra_db::SourceDatabaseStorage,
+    db::InternDatabaseStorage,
+    db::AstDatabaseStorage,
+    db::DefDatabaseStorage,
+    db::DefDatabase2Storage,
+    db::HirDatabaseStorage
+)]
+#[derive(Debug)]
+pub struct MockDatabase {
+    events: Mutex<Option<Vec<salsa::Event<MockDatabase>>>>,
+    runtime: salsa::Runtime<MockDatabase>,
+    files: FxHashMap<String, FileId>,
+    crate_names: Arc<FxHashMap<CrateId, String>>,
+    file_paths: Arc<FxHashMap<FileId, String>>,
+}
+
+impl panic::RefUnwindSafe for MockDatabase {}
+
+impl FileLoader for MockDatabase {
+    fn file_text(&self, file_id: FileId) -> Arc<String> {
+        FileLoaderDelegate(self).file_text(file_id)
+    }
+    fn resolve_relative_path(
+        &self,
+        anchor: FileId,
+        relative_path: &RelativePath,
+    ) -> Option<FileId> {
+        FileLoaderDelegate(self).resolve_relative_path(anchor, relative_path)
+    }
+    fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> {
+        FileLoaderDelegate(self).relevant_crates(file_id)
+    }
+}
+
+impl HirDebugHelper for MockDatabase {
+    fn crate_name(&self, krate: CrateId) -> Option<String> {
+        self.crate_names.get(&krate).cloned()
+    }
+
+    fn file_path(&self, file_id: FileId) -> Option<String> {
+        self.file_paths.get(&file_id).cloned()
+    }
+}
+
+impl MockDatabase {
+    pub fn with_files(fixture: &str) -> MockDatabase {
+        let (db, position) = MockDatabase::from_fixture(fixture);
+        assert!(position.is_none());
+        db
+    }
+
+    pub fn with_single_file(text: &str) -> (MockDatabase, SourceRoot, FileId) {
+        let mut db = MockDatabase::default();
+        let mut source_root = SourceRoot::default();
+        let file_id = db.add_file(WORKSPACE, "/", &mut source_root, "/main.rs", text);
+        db.set_source_root(WORKSPACE, Arc::new(source_root.clone()));
+        (db, source_root, file_id)
+    }
+
+    pub fn file_id_of(&self, path: &str) -> FileId {
+        match self.files.get(path) {
+            Some(it) => *it,
+            None => panic!("unknown file: {:?}\nexisting files:\n{:#?}", path, self.files),
+        }
+    }
+
+    pub fn diagnostics(&self) -> String {
+        let mut buf = String::new();
+        let mut files: Vec<FileId> = self.files.values().copied().collect();
+        files.sort();
+        for file in files {
+            let src = crate::Source {
+                file_id: file.into(),
+                ast: crate::ModuleSource::new(self, Some(file), None),
+            };
+            let module = crate::Module::from_definition(self, src).unwrap();
+            module.diagnostics(
+                self,
+                &mut DiagnosticSink::new(|d| {
+                    buf += &format!("{:?}: {}\n", d.syntax_node(self).text(), d.message());
+                }),
+            )
+        }
+        buf
+    }
+
+    fn from_fixture(fixture: &str) -> (MockDatabase, Option<FilePosition>) {
+        let mut db = MockDatabase::default();
+
+        let pos = db.add_fixture(fixture);
+
+        (db, pos)
+    }
+
+    fn add_fixture(&mut self, fixture: &str) -> Option<FilePosition> {
+        let mut position = None;
+        let mut source_root = SourceRoot::default();
+        let mut source_root_id = WORKSPACE;
+        let mut source_root_prefix = "/".to_string();
+        for entry in parse_fixture(fixture) {
+            if entry.meta.starts_with("root") {
+                self.set_source_root(source_root_id, Arc::new(source_root));
+                source_root = SourceRoot::default();
+
+                source_root_id = SourceRootId(source_root_id.0 + 1);
+                source_root_prefix = entry.meta["root".len()..].trim().to_string();
+                continue;
+            }
+            if entry.text.contains(CURSOR_MARKER) {
+                assert!(position.is_none(), "only one marker (<|>) per fixture is allowed");
+                position = Some(self.add_file_with_position(
+                    source_root_id,
+                    &source_root_prefix,
+                    &mut source_root,
+                    &entry.meta,
+                    &entry.text,
+                ));
+            } else {
+                self.add_file(
+                    source_root_id,
+                    &source_root_prefix,
+                    &mut source_root,
+                    &entry.meta,
+                    &entry.text,
+                );
+            }
+        }
+        self.set_source_root(source_root_id, Arc::new(source_root));
+        position
+    }
+
+    fn add_file(
+        &mut self,
+        source_root_id: SourceRootId,
+        source_root_prefix: &str,
+        source_root: &mut SourceRoot,
+        path: &str,
+        text: &str,
+    ) -> FileId {
+        assert!(source_root_prefix.starts_with('/'));
+        assert!(source_root_prefix.ends_with('/'));
+        assert!(path.starts_with(source_root_prefix));
+        let rel_path = RelativePathBuf::from_path(&path[source_root_prefix.len()..]).unwrap();
+
+        let is_crate_root = rel_path == "lib.rs" || rel_path == "/main.rs";
+
+        let file_id = FileId(self.files.len() as u32);
+
+        let prev = self.files.insert(path.to_string(), file_id);
+        assert!(prev.is_none(), "duplicate files in the text fixture");
+        Arc::make_mut(&mut self.file_paths).insert(file_id, path.to_string());
+
+        let text = Arc::new(text.to_string());
+        self.set_file_text(file_id, text);
+        self.set_file_relative_path(file_id, rel_path.clone());
+        self.set_file_source_root(file_id, source_root_id);
+        source_root.insert_file(rel_path, file_id);
+
+        if is_crate_root {
+            let mut crate_graph = CrateGraph::default();
+            crate_graph.add_crate_root(file_id, Edition::Edition2018, CfgOptions::default());
+            self.set_crate_graph(Arc::new(crate_graph));
+        }
+        file_id
+    }
+
+    fn add_file_with_position(
+        &mut self,
+        source_root_id: SourceRootId,
+        source_root_prefix: &str,
+        source_root: &mut SourceRoot,
+        path: &str,
+        text: &str,
+    ) -> FilePosition {
+        let (offset, text) = extract_offset(text);
+        let file_id = self.add_file(source_root_id, source_root_prefix, source_root, path, &text);
+        FilePosition { file_id, offset }
+    }
+}
+
+impl salsa::Database for MockDatabase {
+    fn salsa_runtime(&self) -> &salsa::Runtime<MockDatabase> {
+        &self.runtime
+    }
+
+    fn salsa_event(&self, event: impl Fn() -> salsa::Event<MockDatabase>) {
+        let mut events = self.events.lock();
+        if let Some(events) = &mut *events {
+            events.push(event());
+        }
+    }
+}
+
+impl Default for MockDatabase {
+    fn default() -> MockDatabase {
+        let mut db = MockDatabase {
+            events: Default::default(),
+            runtime: salsa::Runtime::default(),
+            files: FxHashMap::default(),
+            crate_names: Default::default(),
+            file_paths: Default::default(),
+        };
+        db.set_crate_graph(Default::default());
+        db
+    }
+}
+
+impl salsa::ParallelDatabase for MockDatabase {
+    fn snapshot(&self) -> salsa::Snapshot<MockDatabase> {
+        salsa::Snapshot::new(MockDatabase {
+            events: Default::default(),
+            runtime: self.runtime.snapshot(self),
+            // only the root database can be used to get file_id by path.
+            files: FxHashMap::default(),
+            file_paths: Arc::clone(&self.file_paths),
+            crate_names: Arc::clone(&self.crate_names),
+        })
+    }
+}
+
+impl MockDatabase {
+    pub fn log(&self, f: impl FnOnce()) -> Vec<salsa::Event<MockDatabase>> {
+        *self.events.lock() = Some(Vec::new());
+        f();
+        self.events.lock().take().unwrap()
+    }
+
+    pub fn log_executed(&self, f: impl FnOnce()) -> Vec<String> {
+        let events = self.log(f);
+        events
+            .into_iter()
+            .filter_map(|e| match e.kind {
+                // This pretty horrible, but `Debug` is the only way to inspect
+                // QueryDescriptor at the moment.
+                salsa::EventKind::WillExecute { database_key } => {
+                    Some(format!("{:?}", database_key))
+                }
+                _ => None,
+            })
+            .collect()
+    }
+}
diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs
index cd2ac0e8bd..d1a9d74111 100644
--- a/crates/ra_hir/src/ty.rs
+++ b/crates/ra_hir/src/ty.rs
@@ -27,7 +27,7 @@ pub(crate) use infer::{infer_query, InferTy, InferenceResult};
 pub use lower::CallableDef;
 pub(crate) use lower::{
     callable_item_sig, generic_defaults_query, generic_predicates_for_param_query,
-    generic_predicates_query, type_for_def, type_for_field, Namespace, TypableDef,
+    generic_predicates_query, type_for_def, type_for_field, TypableDef,
 };
 pub(crate) use traits::{InEnvironment, Obligation, ProjectionPredicate, TraitEnvironment};
 
diff --git a/crates/ra_hir/src/ty/infer/expr.rs b/crates/ra_hir/src/ty/infer/expr.rs
index 4af1d65ee4..a09ef5c5d3 100644
--- a/crates/ra_hir/src/ty/infer/expr.rs
+++ b/crates/ra_hir/src/ty/infer/expr.rs
@@ -12,10 +12,10 @@ use crate::{
     expr::{self, Array, BinaryOp, Expr, ExprId, Literal, Statement, UnaryOp},
     generics::{GenericParams, HasGenericParams},
     ty::{
-        autoderef, method_resolution, op, primitive, CallableDef, InferTy, Mutability, Namespace,
-        Obligation, ProjectionPredicate, ProjectionTy, Substs, TraitRef, Ty, TypeCtor, TypeWalk,
+        autoderef, method_resolution, op, primitive, CallableDef, InferTy, Mutability, Obligation,
+        ProjectionPredicate, ProjectionTy, Substs, TraitRef, Ty, TypeCtor, TypeWalk,
     },
-    Adt, Name,
+    Adt, Name, Namespace,
 };
 
 impl<'a, D: HirDatabase> InferenceContext<'a, D> {
diff --git a/crates/ra_hir/src/ty/infer/path.rs b/crates/ra_hir/src/ty/infer/path.rs
index 865ced5a13..59b7f7eb64 100644
--- a/crates/ra_hir/src/ty/infer/path.rs
+++ b/crates/ra_hir/src/ty/infer/path.rs
@@ -6,8 +6,8 @@ use super::{ExprOrPatId, InferenceContext, TraitRef};
 use crate::{
     db::HirDatabase,
     resolve::{ResolveValueResult, Resolver, TypeNs, ValueNs},
-    ty::{method_resolution, Namespace, Substs, Ty, TypableDef, TypeWalk},
-    AssocItem, Container, HasGenericParams, Name, Path,
+    ty::{method_resolution, Substs, Ty, TypableDef, TypeWalk},
+    AssocItem, Container, HasGenericParams, Name, Namespace, Path,
 };
 
 impl<'a, D: HirDatabase> InferenceContext<'a, D> {
diff --git a/crates/ra_hir/src/ty/lower.rs b/crates/ra_hir/src/ty/lower.rs
index d26b16cb2f..e29ab8492a 100644
--- a/crates/ra_hir/src/ty/lower.rs
+++ b/crates/ra_hir/src/ty/lower.rs
@@ -29,21 +29,10 @@ use crate::{
         Adt,
     },
     util::make_mut_slice,
-    Const, Enum, EnumVariant, Function, ModuleDef, Path, Static, Struct, StructField, Trait,
-    TypeAlias, Union,
+    Const, Enum, EnumVariant, Function, ModuleDef, Namespace, Path, Static, Struct, StructField,
+    Trait, TypeAlias, Union,
 };
 
-// FIXME: this is only really used in `type_for_def`, which contains a bunch of
-// impossible cases. Perhaps we should recombine `TypeableDef` and `Namespace`
-// into a `AsTypeDef`, `AsValueDef` enums?
-#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub enum Namespace {
-    Types,
-    Values,
-    // Note that only type inference uses this enum, and it doesn't care about macros.
-    // Macro,
-}
-
 impl Ty {
     pub(crate) fn from_hir(db: &impl HirDatabase, resolver: &Resolver, type_ref: &TypeRef) -> Self {
         match type_ref {
diff --git a/crates/ra_hir/src/ty/traits/chalk.rs b/crates/ra_hir/src/ty/traits/chalk.rs
index c694952f3c..39ef921827 100644
--- a/crates/ra_hir/src/ty/traits/chalk.rs
+++ b/crates/ra_hir/src/ty/traits/chalk.rs
@@ -18,10 +18,7 @@ use crate::{
     db::HirDatabase,
     generics::GenericDef,
     ty::display::HirDisplay,
-    ty::{
-        ApplicationTy, GenericPredicate, Namespace, ProjectionTy, Substs, TraitRef, Ty, TypeCtor,
-        TypeWalk,
-    },
+    ty::{ApplicationTy, GenericPredicate, ProjectionTy, Substs, TraitRef, Ty, TypeCtor, TypeWalk},
     AssocItem, Crate, HasGenericParams, ImplBlock, Trait, TypeAlias,
 };
 
@@ -655,7 +652,7 @@ fn impl_block_datum(
         })
         .filter_map(|t| {
             let assoc_ty = trait_.associated_type_by_name(db, &t.name(db))?;
-            let ty = db.type_for_def(t.into(), Namespace::Types).subst(&bound_vars);
+            let ty = db.type_for_def(t.into(), crate::Namespace::Types).subst(&bound_vars);
             Some(chalk_rust_ir::AssociatedTyValue {
                 impl_id,
                 associated_ty_id: assoc_ty.to_chalk(db),
diff --git a/crates/ra_hir_def/src/nameres.rs b/crates/ra_hir_def/src/nameres.rs
index d3ecabb9be..433bdde48f 100644
--- a/crates/ra_hir_def/src/nameres.rs
+++ b/crates/ra_hir_def/src/nameres.rs
@@ -14,10 +14,10 @@
 //!
 //! ## Collecting RawItems
 //!
-//! This happens in the `raw` module, which parses a single source file into a
-//! set of top-level items. Nested imports are desugared to flat imports in this
-//! phase. Macro calls are represented as a triple of (Path, Option<Name>,
-//! TokenTree).
+//!  This happens in the `raw` module, which parses a single source file into a
+//!  set of top-level items. Nested imports are desugared to flat imports in
+//!  this phase. Macro calls are represented as a triple of (Path, Option<Name>,
+//!  TokenTree).
 //!
 //! ## Collecting Modules
 //!
@@ -44,14 +44,14 @@
 //! Macros from other crates (including proc-macros) can be used with
 //! `foo::bar!` syntax. We handle them similarly to imports. There's a list of
 //! unexpanded macros. On every iteration, we try to resolve each macro call
-//! path and, upon success, we run macro expansion and "collect module" phase on
-//! the result
+//! path and, upon success, we run macro expansion and "collect module" phase
+//! on the result
 
+// FIXME: review privacy of submodules
 pub mod raw;
 pub mod per_ns;
-mod collector;
-mod mod_resolution;
-mod path_resolution;
+pub mod collector;
+pub mod mod_resolution;
 
 #[cfg(test)]
 mod tests;
@@ -65,15 +65,14 @@ use ra_db::{CrateId, Edition, FileId};
 use ra_prof::profile;
 use ra_syntax::ast;
 use rustc_hash::{FxHashMap, FxHashSet};
+use test_utils::tested_by;
 
 use crate::{
     builtin_type::BuiltinType,
     db::DefDatabase2,
-    nameres::{
-        diagnostics::DefDiagnostic, path_resolution::ResolveMode, per_ns::PerNs, raw::ImportId,
-    },
-    path::Path,
-    AstId, CrateModuleId, ModuleDefId, ModuleId, TraitId,
+    nameres::{diagnostics::DefDiagnostic, per_ns::PerNs, raw::ImportId},
+    path::{Path, PathKind},
+    AdtId, AstId, CrateModuleId, EnumVariantId, ModuleDefId, ModuleId, TraitId,
 };
 
 /// Contains all top-level defs from a macro-expanded crate
@@ -196,6 +195,45 @@ pub struct Resolution {
     pub import: Option<ImportId>,
 }
 
+impl Resolution {
+    pub(crate) fn from_macro(macro_: MacroDefId) -> Self {
+        Resolution { def: PerNs::macros(macro_), import: None }
+    }
+}
+
+#[derive(Debug, Clone)]
+struct ResolvePathResult {
+    resolved_def: PerNs,
+    segment_index: Option<usize>,
+    reached_fixedpoint: ReachedFixedPoint,
+}
+
+impl ResolvePathResult {
+    fn empty(reached_fixedpoint: ReachedFixedPoint) -> ResolvePathResult {
+        ResolvePathResult::with(PerNs::none(), reached_fixedpoint, None)
+    }
+
+    fn with(
+        resolved_def: PerNs,
+        reached_fixedpoint: ReachedFixedPoint,
+        segment_index: Option<usize>,
+    ) -> ResolvePathResult {
+        ResolvePathResult { resolved_def, reached_fixedpoint, segment_index }
+    }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+enum ResolveMode {
+    Import,
+    Other,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+enum ReachedFixedPoint {
+    Yes,
+    No,
+}
+
 impl CrateDefMap {
     pub(crate) fn crate_def_map_query(
         // Note that this doesn't have `+ AstDatabase`!
@@ -258,6 +296,210 @@ impl CrateDefMap {
         let res = self.resolve_path_fp_with_macro(db, ResolveMode::Other, original_module, path);
         (res.resolved_def, res.segment_index)
     }
+
+    // Returns Yes if we are sure that additions to `ItemMap` wouldn't change
+    // the result.
+    fn resolve_path_fp_with_macro(
+        &self,
+        db: &impl DefDatabase2,
+        mode: ResolveMode,
+        original_module: CrateModuleId,
+        path: &Path,
+    ) -> ResolvePathResult {
+        let mut segments = path.segments.iter().enumerate();
+        let mut curr_per_ns: PerNs = match path.kind {
+            PathKind::DollarCrate(krate) => {
+                if krate == self.krate {
+                    tested_by!(macro_dollar_crate_self);
+                    PerNs::types(ModuleId { krate: self.krate, module_id: self.root }.into())
+                } else {
+                    let def_map = db.crate_def_map(krate);
+                    let module = ModuleId { krate, module_id: def_map.root };
+                    tested_by!(macro_dollar_crate_other);
+                    PerNs::types(module.into())
+                }
+            }
+            PathKind::Crate => {
+                PerNs::types(ModuleId { krate: self.krate, module_id: self.root }.into())
+            }
+            PathKind::Self_ => {
+                PerNs::types(ModuleId { krate: self.krate, module_id: original_module }.into())
+            }
+            // plain import or absolute path in 2015: crate-relative with
+            // fallback to extern prelude (with the simplification in
+            // rust-lang/rust#57745)
+            // FIXME there must be a nicer way to write this condition
+            PathKind::Plain | PathKind::Abs
+                if self.edition == Edition::Edition2015
+                    && (path.kind == PathKind::Abs || mode == ResolveMode::Import) =>
+            {
+                let segment = match segments.next() {
+                    Some((_, segment)) => segment,
+                    None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
+                };
+                log::debug!("resolving {:?} in crate root (+ extern prelude)", segment);
+                self.resolve_name_in_crate_root_or_extern_prelude(&segment.name)
+            }
+            PathKind::Plain => {
+                let segment = match segments.next() {
+                    Some((_, segment)) => segment,
+                    None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
+                };
+                log::debug!("resolving {:?} in module", segment);
+                self.resolve_name_in_module(db, original_module, &segment.name)
+            }
+            PathKind::Super => {
+                if let Some(p) = self.modules[original_module].parent {
+                    PerNs::types(ModuleId { krate: self.krate, module_id: p }.into())
+                } else {
+                    log::debug!("super path in root module");
+                    return ResolvePathResult::empty(ReachedFixedPoint::Yes);
+                }
+            }
+            PathKind::Abs => {
+                // 2018-style absolute path -- only extern prelude
+                let segment = match segments.next() {
+                    Some((_, segment)) => segment,
+                    None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
+                };
+                if let Some(def) = self.extern_prelude.get(&segment.name) {
+                    log::debug!("absolute path {:?} resolved to crate {:?}", path, def);
+                    PerNs::types(*def)
+                } else {
+                    return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude
+                }
+            }
+            PathKind::Type(_) => {
+                // This is handled in `infer::infer_path_expr`
+                // The result returned here does not matter
+                return ResolvePathResult::empty(ReachedFixedPoint::Yes);
+            }
+        };
+
+        for (i, segment) in segments {
+            let curr = match curr_per_ns.take_types() {
+                Some(r) => r,
+                None => {
+                    // we still have path segments left, but the path so far
+                    // didn't resolve in the types namespace => no resolution
+                    // (don't break here because `curr_per_ns` might contain
+                    // something in the value namespace, and it would be wrong
+                    // to return that)
+                    return ResolvePathResult::empty(ReachedFixedPoint::No);
+                }
+            };
+            // resolve segment in curr
+
+            curr_per_ns = match curr {
+                ModuleDefId::ModuleId(module) => {
+                    if module.krate != self.krate {
+                        let path =
+                            Path { segments: path.segments[i..].to_vec(), kind: PathKind::Self_ };
+                        log::debug!("resolving {:?} in other crate", path);
+                        let defp_map = db.crate_def_map(module.krate);
+                        let (def, s) = defp_map.resolve_path(db, module.module_id, &path);
+                        return ResolvePathResult::with(
+                            def,
+                            ReachedFixedPoint::Yes,
+                            s.map(|s| s + i),
+                        );
+                    }
+
+                    // Since it is a qualified path here, it should not contains legacy macros
+                    match self[module.module_id].scope.get(&segment.name) {
+                        Some(res) => res.def,
+                        _ => {
+                            log::debug!("path segment {:?} not found", segment.name);
+                            return ResolvePathResult::empty(ReachedFixedPoint::No);
+                        }
+                    }
+                }
+                ModuleDefId::AdtId(AdtId::EnumId(e)) => {
+                    // enum variant
+                    tested_by!(can_import_enum_variant);
+                    let enum_data = db.enum_data(e);
+                    match enum_data.variant(&segment.name) {
+                        Some(local_id) => {
+                            let variant = EnumVariantId { parent: e, local_id };
+                            PerNs::both(variant.into(), variant.into())
+                        }
+                        None => {
+                            return ResolvePathResult::with(
+                                PerNs::types(e.into()),
+                                ReachedFixedPoint::Yes,
+                                Some(i),
+                            );
+                        }
+                    }
+                }
+                s => {
+                    // could be an inherent method call in UFCS form
+                    // (`Struct::method`), or some other kind of associated item
+                    log::debug!(
+                        "path segment {:?} resolved to non-module {:?}, but is not last",
+                        segment.name,
+                        curr,
+                    );
+
+                    return ResolvePathResult::with(
+                        PerNs::types(s),
+                        ReachedFixedPoint::Yes,
+                        Some(i),
+                    );
+                }
+            };
+        }
+        ResolvePathResult::with(curr_per_ns, ReachedFixedPoint::Yes, None)
+    }
+
+    fn resolve_name_in_crate_root_or_extern_prelude(&self, name: &Name) -> PerNs {
+        let from_crate_root =
+            self[self.root].scope.get(name).map_or_else(PerNs::none, |res| res.def);
+        let from_extern_prelude = self.resolve_name_in_extern_prelude(name);
+
+        from_crate_root.or(from_extern_prelude)
+    }
+
+    pub(crate) fn resolve_name_in_module(
+        &self,
+        db: &impl DefDatabase2,
+        module: CrateModuleId,
+        name: &Name,
+    ) -> PerNs {
+        // Resolve in:
+        //  - legacy scope of macro
+        //  - current module / scope
+        //  - extern prelude
+        //  - std prelude
+        let from_legacy_macro =
+            self[module].scope.get_legacy_macro(name).map_or_else(PerNs::none, PerNs::macros);
+        let from_scope = self[module].scope.get(name).map_or_else(PerNs::none, |res| res.def);
+        let from_extern_prelude =
+            self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it));
+        let from_prelude = self.resolve_in_prelude(db, name);
+
+        from_legacy_macro.or(from_scope).or(from_extern_prelude).or(from_prelude)
+    }
+
+    fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs {
+        self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it))
+    }
+
+    fn resolve_in_prelude(&self, db: &impl DefDatabase2, name: &Name) -> PerNs {
+        if let Some(prelude) = self.prelude {
+            let keep;
+            let def_map = if prelude.krate == self.krate {
+                self
+            } else {
+                // Extend lifetime
+                keep = db.crate_def_map(prelude.krate);
+                &keep
+            };
+            def_map[prelude.module_id].scope.get(name).map_or_else(PerNs::none, |res| res.def)
+        } else {
+            PerNs::none()
+        }
+    }
 }
 
 mod diagnostics {
diff --git a/crates/ra_hir_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs
index aacd50df88..3b61d9895b 100644
--- a/crates/ra_hir_def/src/nameres/collector.rs
+++ b/crates/ra_hir_def/src/nameres/collector.rs
@@ -14,8 +14,8 @@ use crate::{
     attr::Attr,
     db::DefDatabase2,
     nameres::{
-        diagnostics::DefDiagnostic, mod_resolution::ModDir, path_resolution::ReachedFixedPoint,
-        per_ns::PerNs, raw, CrateDefMap, ModuleData, Resolution, ResolveMode,
+        diagnostics::DefDiagnostic, mod_resolution::ModDir, per_ns::PerNs, raw, CrateDefMap,
+        ModuleData, ReachedFixedPoint, Resolution, ResolveMode,
     },
     path::{Path, PathKind},
     AdtId, AstId, AstItemDef, ConstId, CrateModuleId, EnumId, EnumVariantId, FunctionId,
@@ -182,11 +182,7 @@ where
         // In Rust, `#[macro_export]` macros are unconditionally visible at the
         // crate root, even if the parent modules is **not** visible.
         if export {
-            self.update(
-                self.def_map.root,
-                None,
-                &[(name, Resolution { def: PerNs::macros(macro_), import: None })],
-            );
+            self.update(self.def_map.root, None, &[(name, Resolution::from_macro(macro_))]);
         }
     }
 
diff --git a/crates/ra_hir_def/src/nameres/mod_resolution.rs b/crates/ra_hir_def/src/nameres/mod_resolution.rs
index b3b1379d02..f6b0b8fb1e 100644
--- a/crates/ra_hir_def/src/nameres/mod_resolution.rs
+++ b/crates/ra_hir_def/src/nameres/mod_resolution.rs
@@ -6,7 +6,7 @@ use ra_syntax::SmolStr;
 use crate::{db::DefDatabase2, HirFileId};
 
 #[derive(Clone, Debug)]
-pub(super) struct ModDir {
+pub struct ModDir {
     /// `.` for `mod.rs`, `lib.rs`
     /// `./foo` for `foo.rs`
     /// `./foo/bar` for `mod bar { mod x; }` nested in `foo.rs`
@@ -16,15 +16,11 @@ pub(super) struct ModDir {
 }
 
 impl ModDir {
-    pub(super) fn root() -> ModDir {
+    pub fn root() -> ModDir {
         ModDir { path: RelativePathBuf::default(), root_non_dir_owner: false }
     }
 
-    pub(super) fn descend_into_definition(
-        &self,
-        name: &Name,
-        attr_path: Option<&SmolStr>,
-    ) -> ModDir {
+    pub fn descend_into_definition(&self, name: &Name, attr_path: Option<&SmolStr>) -> ModDir {
         let mut path = self.path.clone();
         match attr_to_path(attr_path) {
             None => path.push(&name.to_string()),
@@ -38,7 +34,7 @@ impl ModDir {
         ModDir { path, root_non_dir_owner: false }
     }
 
-    pub(super) fn resolve_declaration(
+    pub fn resolve_declaration(
         &self,
         db: &impl DefDatabase2,
         file_id: HirFileId,
diff --git a/crates/ra_hir_def/src/nameres/path_resolution.rs b/crates/ra_hir_def/src/nameres/path_resolution.rs
deleted file mode 100644
index 95692f8261..0000000000
--- a/crates/ra_hir_def/src/nameres/path_resolution.rs
+++ /dev/null
@@ -1,261 +0,0 @@
-//! This modules implements a function to resolve a path `foo::bar::baz` to a
-//! def, which is used within the name resolution.
-//!
-//! When name resolution is finished, the result of resolving a path is either
-//! `Some(def)` or `None`. However, when we are in process of resolving imports
-//! or macros, there's a third possibility:
-//!
-//!   I can't resolve this path right now, but I might be resolve this path
-//!   later, when more macros are expanded.
-//!
-//! `ReachedFixedPoint` signals about this.
-
-use hir_expand::name::Name;
-use ra_db::Edition;
-use test_utils::tested_by;
-
-use crate::{
-    db::DefDatabase2,
-    nameres::{per_ns::PerNs, CrateDefMap},
-    path::{Path, PathKind},
-    AdtId, CrateModuleId, EnumVariantId, ModuleDefId, ModuleId,
-};
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub(super) enum ResolveMode {
-    Import,
-    Other,
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub(super) enum ReachedFixedPoint {
-    Yes,
-    No,
-}
-
-#[derive(Debug, Clone)]
-pub(super) struct ResolvePathResult {
-    pub(super) resolved_def: PerNs,
-    pub(super) segment_index: Option<usize>,
-    pub(super) reached_fixedpoint: ReachedFixedPoint,
-}
-
-impl ResolvePathResult {
-    fn empty(reached_fixedpoint: ReachedFixedPoint) -> ResolvePathResult {
-        ResolvePathResult::with(PerNs::none(), reached_fixedpoint, None)
-    }
-
-    fn with(
-        resolved_def: PerNs,
-        reached_fixedpoint: ReachedFixedPoint,
-        segment_index: Option<usize>,
-    ) -> ResolvePathResult {
-        ResolvePathResult { resolved_def, reached_fixedpoint, segment_index }
-    }
-}
-
-impl CrateDefMap {
-    pub(super) fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs {
-        self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it))
-    }
-
-    // Returns Yes if we are sure that additions to `ItemMap` wouldn't change
-    // the result.
-    pub(super) fn resolve_path_fp_with_macro(
-        &self,
-        db: &impl DefDatabase2,
-        mode: ResolveMode,
-        original_module: CrateModuleId,
-        path: &Path,
-    ) -> ResolvePathResult {
-        let mut segments = path.segments.iter().enumerate();
-        let mut curr_per_ns: PerNs = match path.kind {
-            PathKind::DollarCrate(krate) => {
-                if krate == self.krate {
-                    tested_by!(macro_dollar_crate_self);
-                    PerNs::types(ModuleId { krate: self.krate, module_id: self.root }.into())
-                } else {
-                    let def_map = db.crate_def_map(krate);
-                    let module = ModuleId { krate, module_id: def_map.root };
-                    tested_by!(macro_dollar_crate_other);
-                    PerNs::types(module.into())
-                }
-            }
-            PathKind::Crate => {
-                PerNs::types(ModuleId { krate: self.krate, module_id: self.root }.into())
-            }
-            PathKind::Self_ => {
-                PerNs::types(ModuleId { krate: self.krate, module_id: original_module }.into())
-            }
-            // plain import or absolute path in 2015: crate-relative with
-            // fallback to extern prelude (with the simplification in
-            // rust-lang/rust#57745)
-            // FIXME there must be a nicer way to write this condition
-            PathKind::Plain | PathKind::Abs
-                if self.edition == Edition::Edition2015
-                    && (path.kind == PathKind::Abs || mode == ResolveMode::Import) =>
-            {
-                let segment = match segments.next() {
-                    Some((_, segment)) => segment,
-                    None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
-                };
-                log::debug!("resolving {:?} in crate root (+ extern prelude)", segment);
-                self.resolve_name_in_crate_root_or_extern_prelude(&segment.name)
-            }
-            PathKind::Plain => {
-                let segment = match segments.next() {
-                    Some((_, segment)) => segment,
-                    None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
-                };
-                log::debug!("resolving {:?} in module", segment);
-                self.resolve_name_in_module(db, original_module, &segment.name)
-            }
-            PathKind::Super => {
-                if let Some(p) = self.modules[original_module].parent {
-                    PerNs::types(ModuleId { krate: self.krate, module_id: p }.into())
-                } else {
-                    log::debug!("super path in root module");
-                    return ResolvePathResult::empty(ReachedFixedPoint::Yes);
-                }
-            }
-            PathKind::Abs => {
-                // 2018-style absolute path -- only extern prelude
-                let segment = match segments.next() {
-                    Some((_, segment)) => segment,
-                    None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
-                };
-                if let Some(def) = self.extern_prelude.get(&segment.name) {
-                    log::debug!("absolute path {:?} resolved to crate {:?}", path, def);
-                    PerNs::types(*def)
-                } else {
-                    return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude
-                }
-            }
-            PathKind::Type(_) => {
-                // This is handled in `infer::infer_path_expr`
-                // The result returned here does not matter
-                return ResolvePathResult::empty(ReachedFixedPoint::Yes);
-            }
-        };
-
-        for (i, segment) in segments {
-            let curr = match curr_per_ns.take_types() {
-                Some(r) => r,
-                None => {
-                    // we still have path segments left, but the path so far
-                    // didn't resolve in the types namespace => no resolution
-                    // (don't break here because `curr_per_ns` might contain
-                    // something in the value namespace, and it would be wrong
-                    // to return that)
-                    return ResolvePathResult::empty(ReachedFixedPoint::No);
-                }
-            };
-            // resolve segment in curr
-
-            curr_per_ns = match curr {
-                ModuleDefId::ModuleId(module) => {
-                    if module.krate != self.krate {
-                        let path =
-                            Path { segments: path.segments[i..].to_vec(), kind: PathKind::Self_ };
-                        log::debug!("resolving {:?} in other crate", path);
-                        let defp_map = db.crate_def_map(module.krate);
-                        let (def, s) = defp_map.resolve_path(db, module.module_id, &path);
-                        return ResolvePathResult::with(
-                            def,
-                            ReachedFixedPoint::Yes,
-                            s.map(|s| s + i),
-                        );
-                    }
-
-                    // Since it is a qualified path here, it should not contains legacy macros
-                    match self[module.module_id].scope.get(&segment.name) {
-                        Some(res) => res.def,
-                        _ => {
-                            log::debug!("path segment {:?} not found", segment.name);
-                            return ResolvePathResult::empty(ReachedFixedPoint::No);
-                        }
-                    }
-                }
-                ModuleDefId::AdtId(AdtId::EnumId(e)) => {
-                    // enum variant
-                    tested_by!(can_import_enum_variant);
-                    let enum_data = db.enum_data(e);
-                    match enum_data.variant(&segment.name) {
-                        Some(local_id) => {
-                            let variant = EnumVariantId { parent: e, local_id };
-                            PerNs::both(variant.into(), variant.into())
-                        }
-                        None => {
-                            return ResolvePathResult::with(
-                                PerNs::types(e.into()),
-                                ReachedFixedPoint::Yes,
-                                Some(i),
-                            );
-                        }
-                    }
-                }
-                s => {
-                    // could be an inherent method call in UFCS form
-                    // (`Struct::method`), or some other kind of associated item
-                    log::debug!(
-                        "path segment {:?} resolved to non-module {:?}, but is not last",
-                        segment.name,
-                        curr,
-                    );
-
-                    return ResolvePathResult::with(
-                        PerNs::types(s),
-                        ReachedFixedPoint::Yes,
-                        Some(i),
-                    );
-                }
-            };
-        }
-        ResolvePathResult::with(curr_per_ns, ReachedFixedPoint::Yes, None)
-    }
-
-    fn resolve_name_in_module(
-        &self,
-        db: &impl DefDatabase2,
-        module: CrateModuleId,
-        name: &Name,
-    ) -> PerNs {
-        // Resolve in:
-        //  - legacy scope of macro
-        //  - current module / scope
-        //  - extern prelude
-        //  - std prelude
-        let from_legacy_macro =
-            self[module].scope.get_legacy_macro(name).map_or_else(PerNs::none, PerNs::macros);
-        let from_scope = self[module].scope.get(name).map_or_else(PerNs::none, |res| res.def);
-        let from_extern_prelude =
-            self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it));
-        let from_prelude = self.resolve_in_prelude(db, name);
-
-        from_legacy_macro.or(from_scope).or(from_extern_prelude).or(from_prelude)
-    }
-
-    fn resolve_name_in_crate_root_or_extern_prelude(&self, name: &Name) -> PerNs {
-        let from_crate_root =
-            self[self.root].scope.get(name).map_or_else(PerNs::none, |res| res.def);
-        let from_extern_prelude = self.resolve_name_in_extern_prelude(name);
-
-        from_crate_root.or(from_extern_prelude)
-    }
-
-    fn resolve_in_prelude(&self, db: &impl DefDatabase2, name: &Name) -> PerNs {
-        if let Some(prelude) = self.prelude {
-            let keep;
-            let def_map = if prelude.krate == self.krate {
-                self
-            } else {
-                // Extend lifetime
-                keep = db.crate_def_map(prelude.krate);
-                &keep
-            };
-            def_map[prelude.module_id].scope.get(name).map_or_else(PerNs::none, |res| res.def)
-        } else {
-            PerNs::none()
-        }
-    }
-}
diff --git a/crates/ra_hir_def/src/nameres/per_ns.rs b/crates/ra_hir_def/src/nameres/per_ns.rs
index 717ed1ef90..298b0b0c72 100644
--- a/crates/ra_hir_def/src/nameres/per_ns.rs
+++ b/crates/ra_hir_def/src/nameres/per_ns.rs
@@ -4,6 +4,14 @@ use hir_expand::MacroDefId;
 
 use crate::ModuleDefId;
 
+#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub enum Namespace {
+    Types,
+    Values,
+    // Note that only type inference uses this enum, and it doesn't care about macros.
+    // Macro,
+}
+
 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
 pub struct PerNs {
     pub types: Option<ModuleDefId>,
diff --git a/crates/ra_hir_def/src/nameres/raw.rs b/crates/ra_hir_def/src/nameres/raw.rs
index 369376f309..cb47fa317d 100644
--- a/crates/ra_hir_def/src/nameres/raw.rs
+++ b/crates/ra_hir_def/src/nameres/raw.rs
@@ -88,7 +88,7 @@ impl RawItems {
         (Arc::new(collector.raw_items), Arc::new(collector.source_map))
     }
 
-    pub(super) fn items(&self) -> &[RawItem] {
+    pub fn items(&self) -> &[RawItem] {
         &self.items
     }
 }
@@ -125,19 +125,19 @@ impl Index<Macro> for RawItems {
 type Attrs = Option<Arc<[Attr]>>;
 
 #[derive(Debug, PartialEq, Eq, Clone)]
-pub(super) struct RawItem {
+pub struct RawItem {
     attrs: Attrs,
-    pub(super) kind: RawItemKind,
+    pub kind: RawItemKind,
 }
 
 impl RawItem {
-    pub(super) fn attrs(&self) -> &[Attr] {
+    pub fn attrs(&self) -> &[Attr] {
         self.attrs.as_ref().map_or(&[], |it| &*it)
     }
 }
 
 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
-pub(super) enum RawItemKind {
+pub enum RawItemKind {
     Module(Module),
     Import(ImportId),
     Def(Def),
@@ -145,11 +145,11 @@ pub(super) enum RawItemKind {
 }
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub(super) struct Module(RawId);
+pub struct Module(RawId);
 impl_arena_id!(Module);
 
 #[derive(Debug, PartialEq, Eq)]
-pub(super) enum ModuleData {
+pub enum ModuleData {
     Declaration { name: Name, ast_id: FileAstId<ast::Module> },
     Definition { name: Name, ast_id: FileAstId<ast::Module>, items: Vec<RawItem> },
 }
@@ -160,26 +160,26 @@ impl_arena_id!(ImportId);
 
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub struct ImportData {
-    pub(super) path: Path,
-    pub(super) alias: Option<Name>,
-    pub(super) is_glob: bool,
-    pub(super) is_prelude: bool,
-    pub(super) is_extern_crate: bool,
-    pub(super) is_macro_use: bool,
+    pub path: Path,
+    pub alias: Option<Name>,
+    pub is_glob: bool,
+    pub is_prelude: bool,
+    pub is_extern_crate: bool,
+    pub is_macro_use: bool,
 }
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub(super) struct Def(RawId);
+pub struct Def(RawId);
 impl_arena_id!(Def);
 
 #[derive(Debug, PartialEq, Eq)]
-pub(super) struct DefData {
-    pub(super) name: Name,
-    pub(super) kind: DefKind,
+pub struct DefData {
+    pub name: Name,
+    pub kind: DefKind,
 }
 
 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
-pub(super) enum DefKind {
+pub enum DefKind {
     Function(FileAstId<ast::FnDef>),
     Struct(FileAstId<ast::StructDef>),
     Union(FileAstId<ast::StructDef>),
@@ -191,15 +191,15 @@ pub(super) enum DefKind {
 }
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub(super) struct Macro(RawId);
+pub struct Macro(RawId);
 impl_arena_id!(Macro);
 
 #[derive(Debug, PartialEq, Eq)]
-pub(super) struct MacroData {
-    pub(super) ast_id: FileAstId<ast::MacroCall>,
-    pub(super) path: Path,
-    pub(super) name: Option<Name>,
-    pub(super) export: bool,
+pub struct MacroData {
+    pub ast_id: FileAstId<ast::MacroCall>,
+    pub path: Path,
+    pub name: Option<Name>,
+    pub export: bool,
 }
 
 struct RawItemsCollector {
diff --git a/crates/ra_hir_expand/src/db.rs b/crates/ra_hir_expand/src/db.rs
index a4ee9a529a..b789c6e7b3 100644
--- a/crates/ra_hir_expand/src/db.rs
+++ b/crates/ra_hir_expand/src/db.rs
@@ -22,9 +22,12 @@ pub trait AstDatabase: SourceDatabase {
 
     #[salsa::interned]
     fn intern_macro(&self, macro_call: MacroCallLoc) -> MacroCallId;
-    fn macro_arg(&self, id: MacroCallId) -> Option<Arc<tt::Subtree>>;
-    fn macro_def(&self, id: MacroDefId) -> Option<Arc<mbe::MacroRules>>;
-    fn parse_macro(&self, macro_file: MacroFile) -> Option<Parse<SyntaxNode>>;
+    fn macro_arg(&self, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>>;
+    fn macro_def(&self, id: MacroDefId) -> Option<Arc<(mbe::MacroRules, mbe::TokenMap)>>;
+    fn parse_macro(
+        &self,
+        macro_file: MacroFile,
+    ) -> Option<(Parse<SyntaxNode>, Arc<mbe::RevTokenMap>)>;
     fn macro_expand(&self, macro_call: MacroCallId) -> Result<Arc<tt::Subtree>, String>;
 }
 
@@ -34,10 +37,13 @@ pub(crate) fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<AstIdM
     Arc::new(map)
 }
 
-pub(crate) fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option<Arc<MacroRules>> {
+pub(crate) fn macro_def(
+    db: &dyn AstDatabase,
+    id: MacroDefId,
+) -> Option<Arc<(mbe::MacroRules, mbe::TokenMap)>> {
     let macro_call = id.ast_id.to_node(db);
     let arg = macro_call.token_tree()?;
-    let (tt, _) = mbe::ast_to_token_tree(&arg).or_else(|| {
+    let (tt, tmap) = mbe::ast_to_token_tree(&arg).or_else(|| {
         log::warn!("fail on macro_def to token tree: {:#?}", arg);
         None
     })?;
@@ -45,15 +51,18 @@ pub(crate) fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option<Arc<Macr
         log::warn!("fail on macro_def parse: {:#?}", tt);
         None
     })?;
-    Some(Arc::new(rules))
+    Some(Arc::new((rules, tmap)))
 }
 
-pub(crate) fn macro_arg(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<tt::Subtree>> {
+pub(crate) fn macro_arg(
+    db: &dyn AstDatabase,
+    id: MacroCallId,
+) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>> {
     let loc = db.lookup_intern_macro(id);
     let macro_call = loc.ast_id.to_node(db);
     let arg = macro_call.token_tree()?;
-    let (tt, _) = mbe::ast_to_token_tree(&arg)?;
-    Some(Arc::new(tt))
+    let (tt, tmap) = mbe::ast_to_token_tree(&arg)?;
+    Some(Arc::new((tt, tmap)))
 }
 
 pub(crate) fn macro_expand(
@@ -64,7 +73,7 @@ pub(crate) fn macro_expand(
     let macro_arg = db.macro_arg(id).ok_or("Fail to args in to tt::TokenTree")?;
 
     let macro_rules = db.macro_def(loc.def).ok_or("Fail to find macro definition")?;
-    let tt = macro_rules.expand(&macro_arg).map_err(|err| format!("{:?}", err))?;
+    let tt = macro_rules.0.expand(&macro_arg.0).map_err(|err| format!("{:?}", err))?;
     // Set a hard limit for the expanded tt
     let count = tt.count();
     if count > 65536 {
@@ -77,7 +86,7 @@ pub(crate) fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Optio
     match file_id.0 {
         HirFileIdRepr::FileId(file_id) => Some(db.parse(file_id).tree().syntax().clone()),
         HirFileIdRepr::MacroFile(macro_file) => {
-            db.parse_macro(macro_file).map(|it| it.syntax_node())
+            db.parse_macro(macro_file).map(|(it, _)| it.syntax_node())
         }
     }
 }
@@ -85,8 +94,9 @@ pub(crate) fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Optio
 pub(crate) fn parse_macro(
     db: &dyn AstDatabase,
     macro_file: MacroFile,
-) -> Option<Parse<SyntaxNode>> {
+) -> Option<(Parse<SyntaxNode>, Arc<mbe::RevTokenMap>)> {
     let _p = profile("parse_macro_query");
+
     let macro_call_id = macro_file.macro_call_id;
     let tt = db
         .macro_expand(macro_call_id)
@@ -97,8 +107,13 @@ pub(crate) fn parse_macro(
             log::warn!("fail on macro_parse: (reason: {})", err,);
         })
         .ok()?;
+
     match macro_file.macro_file_kind {
-        MacroFileKind::Items => mbe::token_tree_to_items(&tt).ok().map(Parse::to_syntax),
-        MacroFileKind::Expr => mbe::token_tree_to_expr(&tt).ok().map(Parse::to_syntax),
+        MacroFileKind::Items => {
+            mbe::token_tree_to_items(&tt).ok().map(|(p, map)| (p.to_syntax(), Arc::new(map)))
+        }
+        MacroFileKind::Expr => {
+            mbe::token_tree_to_expr(&tt).ok().map(|(p, map)| (p.to_syntax(), Arc::new(map)))
+        }
     }
 }
diff --git a/crates/ra_hir_expand/src/lib.rs b/crates/ra_hir_expand/src/lib.rs
index dd07a16b4c..b219b8fbf8 100644
--- a/crates/ra_hir_expand/src/lib.rs
+++ b/crates/ra_hir_expand/src/lib.rs
@@ -12,11 +12,12 @@ pub mod hygiene;
 pub mod diagnostics;
 
 use std::hash::{Hash, Hasher};
+use std::sync::Arc;
 
 use ra_db::{salsa, CrateId, FileId};
 use ra_syntax::{
     ast::{self, AstNode},
-    SyntaxNode,
+    SyntaxNode, TextRange, TextUnit,
 };
 
 use crate::ast_id_map::FileAstId;
@@ -66,6 +67,30 @@ impl HirFileId {
             }
         }
     }
+
+    /// Return expansion information if it is a macro-expansion file
+    pub fn expansion_info(self, db: &dyn db::AstDatabase) -> Option<ExpansionInfo> {
+        match self.0 {
+            HirFileIdRepr::FileId(_) => None,
+            HirFileIdRepr::MacroFile(macro_file) => {
+                let loc: MacroCallLoc = db.lookup_intern_macro(macro_file.macro_call_id);
+
+                let arg_start = loc.ast_id.to_node(db).token_tree()?.syntax().text_range().start();
+                let def_start =
+                    loc.def.ast_id.to_node(db).token_tree()?.syntax().text_range().start();
+
+                let macro_def = db.macro_def(loc.def)?;
+                let shift = macro_def.0.shift();
+                let exp_map = db.parse_macro(macro_file)?.1;
+                let macro_arg = db.macro_arg(macro_file.macro_call_id)?;
+
+                let arg_start = (loc.ast_id.file_id, arg_start);
+                let def_start = (loc.def.ast_id.file_id, def_start);
+
+                Some(ExpansionInfo { arg_start, def_start, macro_arg, macro_def, exp_map, shift })
+            }
+        }
+    }
 }
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -112,6 +137,36 @@ impl MacroCallId {
     }
 }
 
+#[derive(Debug, Clone, PartialEq, Eq)]
+/// ExpansionInfo mainly describes how to map text range between src and expanded macro
+pub struct ExpansionInfo {
+    pub(crate) arg_start: (HirFileId, TextUnit),
+    pub(crate) def_start: (HirFileId, TextUnit),
+    pub(crate) shift: u32,
+
+    pub(crate) macro_def: Arc<(mbe::MacroRules, mbe::TokenMap)>,
+    pub(crate) macro_arg: Arc<(tt::Subtree, mbe::TokenMap)>,
+    pub(crate) exp_map: Arc<mbe::RevTokenMap>,
+}
+
+impl ExpansionInfo {
+    pub fn find_range(&self, from: TextRange) -> Option<(HirFileId, TextRange)> {
+        fn look_in_rev_map(exp_map: &mbe::RevTokenMap, from: TextRange) -> Option<tt::TokenId> {
+            exp_map.ranges.iter().find(|&it| it.0.is_subrange(&from)).map(|it| it.1)
+        }
+
+        let token_id = look_in_rev_map(&self.exp_map, from)?;
+        let (token_map, file_offset, token_id) = if token_id.0 >= self.shift {
+            (&self.macro_arg.1, self.arg_start, tt::TokenId(token_id.0 - self.shift).into())
+        } else {
+            (&self.macro_def.1, self.def_start, token_id)
+        };
+
+        let range = token_map.relative_range_of(token_id)?;
+        Some((file_offset.0, TextRange::offset_len(range.start() + file_offset.1, range.len())))
+    }
+}
+
 /// `AstId` points to an AST node in any file.
 ///
 /// It is stable across reparses, and can be used as salsa key/value.
diff --git a/crates/ra_ide_api/src/completion/complete_postfix.rs b/crates/ra_ide_api/src/completion/complete_postfix.rs
index 4f95654419..60ed3518b8 100644
--- a/crates/ra_ide_api/src/completion/complete_postfix.rs
+++ b/crates/ra_ide_api/src/completion/complete_postfix.rs
@@ -1,9 +1,5 @@
 //! FIXME: write short doc here
 
-use hir::{Ty, TypeCtor};
-use ra_syntax::{ast::AstNode, TextRange, TextUnit};
-use ra_text_edit::TextEdit;
-
 use crate::{
     completion::{
         completion_context::CompletionContext,
@@ -11,53 +7,9 @@ use crate::{
     },
     CompletionItem,
 };
-
-pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
-    let dot_receiver = match &ctx.dot_receiver {
-        Some(it) => it,
-        None => return,
-    };
-
-    let receiver_text = if ctx.dot_receiver_is_ambiguous_float_literal {
-        let text = dot_receiver.syntax().text();
-        let without_dot = ..text.len() - TextUnit::of_char('.');
-        text.slice(without_dot).to_string()
-    } else {
-        dot_receiver.syntax().text().to_string()
-    };
-
-    let receiver_ty = ctx.analyzer.type_of(ctx.db, &dot_receiver);
-
-    if is_bool_or_unknown(receiver_ty) {
-        postfix_snippet(ctx, "if", "if expr {}", &format!("if {} {{$0}}", receiver_text))
-            .add_to(acc);
-        postfix_snippet(
-            ctx,
-            "while",
-            "while expr {}",
-            &format!("while {} {{\n$0\n}}", receiver_text),
-        )
-        .add_to(acc);
-    }
-
-    postfix_snippet(ctx, "not", "!expr", &format!("!{}", receiver_text)).add_to(acc);
-
-    postfix_snippet(ctx, "ref", "&expr", &format!("&{}", receiver_text)).add_to(acc);
-    postfix_snippet(ctx, "refm", "&mut expr", &format!("&mut {}", receiver_text)).add_to(acc);
-
-    postfix_snippet(
-        ctx,
-        "match",
-        "match expr {}",
-        &format!("match {} {{\n    ${{1:_}} => {{$0\\}},\n}}", receiver_text),
-    )
-    .add_to(acc);
-
-    postfix_snippet(ctx, "dbg", "dbg!(expr)", &format!("dbg!({})", receiver_text)).add_to(acc);
-
-    postfix_snippet(ctx, "box", "Box::new(expr)", &format!("Box::new({})", receiver_text))
-        .add_to(acc);
-}
+use hir::{Ty, TypeCtor};
+use ra_syntax::{ast::AstNode, TextRange, TextUnit};
+use ra_text_edit::TextEdit;
 
 fn postfix_snippet(ctx: &CompletionContext, label: &str, detail: &str, snippet: &str) -> Builder {
     let edit = {
@@ -72,18 +24,61 @@ fn postfix_snippet(ctx: &CompletionContext, label: &str, detail: &str, snippet:
 }
 
 fn is_bool_or_unknown(ty: Option<Ty>) -> bool {
-    match &ty {
-        Some(Ty::Apply(app)) if app.ctor == TypeCtor::Bool => true,
-        Some(Ty::Unknown) | None => true,
-        Some(_) => false,
+    if let Some(ty) = ty {
+        match ty {
+            Ty::Apply(at) => match at.ctor {
+                TypeCtor::Bool => true,
+                _ => false,
+            },
+            Ty::Unknown => true,
+            _ => false,
+        }
+    } else {
+        true
+    }
+}
+
+pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
+    if let Some(dot_receiver) = &ctx.dot_receiver {
+        let receiver_text = if ctx.dot_receiver_is_ambiguous_float_literal {
+            let text = dot_receiver.syntax().text();
+            let without_dot = ..text.len() - TextUnit::of_char('.');
+            text.slice(without_dot).to_string()
+        } else {
+            dot_receiver.syntax().text().to_string()
+        };
+        let receiver_ty = ctx.analyzer.type_of(ctx.db, &dot_receiver);
+        if is_bool_or_unknown(receiver_ty) {
+            postfix_snippet(ctx, "if", "if expr {}", &format!("if {} {{$0}}", receiver_text))
+                .add_to(acc);
+            postfix_snippet(
+                ctx,
+                "while",
+                "while expr {}",
+                &format!("while {} {{\n$0\n}}", receiver_text),
+            )
+            .add_to(acc);
+        }
+        postfix_snippet(ctx, "not", "!expr", &format!("!{}", receiver_text)).add_to(acc);
+        postfix_snippet(ctx, "ref", "&expr", &format!("&{}", receiver_text)).add_to(acc);
+        postfix_snippet(ctx, "refm", "&mut expr", &format!("&mut {}", receiver_text)).add_to(acc);
+        postfix_snippet(
+            ctx,
+            "match",
+            "match expr {}",
+            &format!("match {} {{\n    ${{1:_}} => {{$0\\}},\n}}", receiver_text),
+        )
+        .add_to(acc);
+        postfix_snippet(ctx, "dbg", "dbg!(expr)", &format!("dbg!({})", receiver_text)).add_to(acc);
+        postfix_snippet(ctx, "box", "Box::new(expr)", &format!("Box::new({})", receiver_text))
+            .add_to(acc);
     }
 }
 
 #[cfg(test)]
 mod tests {
-    use insta::assert_debug_snapshot;
-
     use crate::completion::{do_completion, CompletionItem, CompletionKind};
+    use insta::assert_debug_snapshot;
 
     fn do_postfix_completion(code: &str) -> Vec<CompletionItem> {
         do_completion(code, CompletionKind::Postfix)
diff --git a/crates/ra_ide_api/src/completion/presentation.rs b/crates/ra_ide_api/src/completion/presentation.rs
index d861303b75..cb55d18751 100644
--- a/crates/ra_ide_api/src/completion/presentation.rs
+++ b/crates/ra_ide_api/src/completion/presentation.rs
@@ -1,8 +1,8 @@
 //! This modules takes care of rendering various definitions as completion items.
 
-use hir::{db::HirDatabase, Attrs, Docs, HasSource, HirDisplay, ScopeDef, Ty, TypeWalk};
+use hir::{db::HirDatabase, Docs, HasSource, HirDisplay, ScopeDef, Ty, TypeWalk};
 use join_to_string::join;
-use ra_syntax::ast::NameOwner;
+use ra_syntax::ast::{AttrsOwner, NameOwner};
 use test_utils::tested_by;
 
 use crate::completion::{
@@ -18,7 +18,11 @@ impl Completions {
         field: hir::StructField,
         substs: &hir::Substs,
     ) {
-        let is_deprecated = is_deprecated(field, ctx.db);
+        let ast_node = field.source(ctx.db).ast;
+        let is_deprecated = match ast_node {
+            hir::FieldSource::Named(m) => is_deprecated(m),
+            hir::FieldSource::Pos(m) => is_deprecated(m),
+        };
         CompletionItem::new(
             CompletionKind::Reference,
             ctx.source_range(),
@@ -181,7 +185,7 @@ impl Completions {
             CompletionItem::new(CompletionKind::Reference, ctx.source_range(), &macro_declaration)
                 .kind(CompletionItemKind::Macro)
                 .set_documentation(docs.clone())
-                .set_deprecated(is_deprecated(macro_, ctx.db))
+                .set_deprecated(is_deprecated(ast_node))
                 .detail(detail);
 
         builder = if ctx.use_item_syntax.is_some() {
@@ -214,7 +218,7 @@ impl Completions {
                     CompletionItemKind::Function
                 })
                 .set_documentation(func.docs(ctx.db))
-                .set_deprecated(is_deprecated(func, ctx.db))
+                .set_deprecated(is_deprecated(ast_node))
                 .detail(detail);
 
         // Add `<>` for generic types
@@ -246,7 +250,7 @@ impl Completions {
         CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.text().to_string())
             .kind(CompletionItemKind::Const)
             .set_documentation(constant.docs(ctx.db))
-            .set_deprecated(is_deprecated(constant, ctx.db))
+            .set_deprecated(is_deprecated(ast_node))
             .detail(detail)
             .add_to(self);
     }
@@ -262,13 +266,13 @@ impl Completions {
         CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.text().to_string())
             .kind(CompletionItemKind::TypeAlias)
             .set_documentation(type_alias.docs(ctx.db))
-            .set_deprecated(is_deprecated(type_alias, ctx.db))
+            .set_deprecated(is_deprecated(type_def))
             .detail(detail)
             .add_to(self);
     }
 
     pub(crate) fn add_enum_variant(&mut self, ctx: &CompletionContext, variant: hir::EnumVariant) {
-        let is_deprecated = is_deprecated(variant, ctx.db);
+        let is_deprecated = is_deprecated(variant.source(ctx.db).ast);
         let name = match variant.name(ctx.db) {
             Some(it) => it,
             None => return,
@@ -287,11 +291,8 @@ impl Completions {
     }
 }
 
-fn is_deprecated(node: impl Attrs, db: &impl HirDatabase) -> bool {
-    match node.attrs(db) {
-        None => false,
-        Some(attrs) => attrs.iter().any(|x| x.is_simple_atom("deprecated")),
-    }
+fn is_deprecated(node: impl AttrsOwner) -> bool {
+    node.attrs().filter_map(|x| x.simple_name()).any(|x| x == "deprecated")
 }
 
 fn has_non_default_type_params(def: hir::GenericDef, db: &db::RootDatabase) -> bool {
diff --git a/crates/ra_ide_api/src/display/navigation_target.rs b/crates/ra_ide_api/src/display/navigation_target.rs
index 5cb67fb95b..1bf81e7d51 100644
--- a/crates/ra_ide_api/src/display/navigation_target.rs
+++ b/crates/ra_ide_api/src/display/navigation_target.rs
@@ -29,6 +29,21 @@ pub struct NavigationTarget {
     docs: Option<String>,
 }
 
+fn find_range_from_node(
+    db: &RootDatabase,
+    src: hir::HirFileId,
+    node: &SyntaxNode,
+) -> (FileId, TextRange) {
+    let text_range = node.text_range();
+    let (file_id, text_range) = src
+        .expansion_info(db)
+        .and_then(|expansion_info| expansion_info.find_range(text_range))
+        .unwrap_or((src, text_range));
+
+    // FIXME: handle recursive macro generated macro
+    (file_id.original_file(db), text_range)
+}
+
 impl NavigationTarget {
     /// When `focus_range` is specified, returns it. otherwise
     /// returns `full_range`
@@ -72,8 +87,12 @@ impl NavigationTarget {
         self.focus_range
     }
 
-    pub(crate) fn from_bind_pat(file_id: FileId, pat: &ast::BindPat) -> NavigationTarget {
-        NavigationTarget::from_named(file_id, pat, None, None)
+    pub(crate) fn from_bind_pat(
+        db: &RootDatabase,
+        file_id: FileId,
+        pat: &ast::BindPat,
+    ) -> NavigationTarget {
+        NavigationTarget::from_named(db, file_id.into(), pat, None, None)
     }
 
     pub(crate) fn from_symbol(db: &RootDatabase, symbol: FileSymbol) -> NavigationTarget {
@@ -96,7 +115,7 @@ impl NavigationTarget {
     ) -> NavigationTarget {
         let parse = db.parse(file_id);
         let pat = pat.to_node(parse.tree().syntax());
-        NavigationTarget::from_bind_pat(file_id, &pat)
+        NavigationTarget::from_bind_pat(db, file_id, &pat)
     }
 
     pub(crate) fn from_self_param(
@@ -119,31 +138,46 @@ impl NavigationTarget {
 
     pub(crate) fn from_module(db: &RootDatabase, module: hir::Module) -> NavigationTarget {
         let src = module.definition_source(db);
-        let file_id = src.file_id.original_file(db);
         let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default();
         match src.ast {
             ModuleSource::SourceFile(node) => {
-                NavigationTarget::from_syntax(file_id, name, None, node.syntax(), None, None)
+                let (file_id, text_range) = find_range_from_node(db, src.file_id, node.syntax());
+
+                NavigationTarget::from_syntax(
+                    file_id,
+                    name,
+                    None,
+                    text_range,
+                    node.syntax(),
+                    None,
+                    None,
+                )
+            }
+            ModuleSource::Module(node) => {
+                let (file_id, text_range) = find_range_from_node(db, src.file_id, node.syntax());
+
+                NavigationTarget::from_syntax(
+                    file_id,
+                    name,
+                    None,
+                    text_range,
+                    node.syntax(),
+                    node.doc_comment_text(),
+                    node.short_label(),
+                )
             }
-            ModuleSource::Module(node) => NavigationTarget::from_syntax(
-                file_id,
-                name,
-                None,
-                node.syntax(),
-                node.doc_comment_text(),
-                node.short_label(),
-            ),
         }
     }
 
     pub(crate) fn from_module_to_decl(db: &RootDatabase, module: hir::Module) -> NavigationTarget {
         let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default();
         if let Some(src) = module.declaration_source(db) {
-            let file_id = src.file_id.original_file(db);
+            let (file_id, text_range) = find_range_from_node(db, src.file_id, src.ast.syntax());
             return NavigationTarget::from_syntax(
                 file_id,
                 name,
                 None,
+                text_range,
                 src.ast.syntax(),
                 src.ast.doc_comment_text(),
                 src.ast.short_label(),
@@ -154,13 +188,25 @@ impl NavigationTarget {
 
     pub(crate) fn from_field(db: &RootDatabase, field: hir::StructField) -> NavigationTarget {
         let src = field.source(db);
-        let file_id = src.file_id.original_file(db);
         match src.ast {
-            FieldSource::Named(it) => {
-                NavigationTarget::from_named(file_id, &it, it.doc_comment_text(), it.short_label())
-            }
+            FieldSource::Named(it) => NavigationTarget::from_named(
+                db,
+                src.file_id,
+                &it,
+                it.doc_comment_text(),
+                it.short_label(),
+            ),
             FieldSource::Pos(it) => {
-                NavigationTarget::from_syntax(file_id, "".into(), None, it.syntax(), None, None)
+                let (file_id, text_range) = find_range_from_node(db, src.file_id, it.syntax());
+                NavigationTarget::from_syntax(
+                    file_id,
+                    "".into(),
+                    None,
+                    text_range,
+                    it.syntax(),
+                    None,
+                    None,
+                )
             }
         }
     }
@@ -172,7 +218,8 @@ impl NavigationTarget {
     {
         let src = def.source(db);
         NavigationTarget::from_named(
-            src.file_id.original_file(db),
+            db,
+            src.file_id,
             &src.ast,
             src.ast.doc_comment_text(),
             src.ast.short_label(),
@@ -212,10 +259,13 @@ impl NavigationTarget {
         impl_block: hir::ImplBlock,
     ) -> NavigationTarget {
         let src = impl_block.source(db);
+        let (file_id, text_range) = find_range_from_node(db, src.file_id, src.ast.syntax());
+
         NavigationTarget::from_syntax(
-            src.file_id.original_file(db),
+            file_id,
             "impl".into(),
             None,
+            text_range,
             src.ast.syntax(),
             None,
             None,
@@ -236,12 +286,7 @@ impl NavigationTarget {
     pub(crate) fn from_macro_def(db: &RootDatabase, macro_call: hir::MacroDef) -> NavigationTarget {
         let src = macro_call.source(db);
         log::debug!("nav target {:#?}", src.ast.syntax());
-        NavigationTarget::from_named(
-            src.file_id.original_file(db),
-            &src.ast,
-            src.ast.doc_comment_text(),
-            None,
-        )
+        NavigationTarget::from_named(db, src.file_id, &src.ast, src.ast.doc_comment_text(), None)
     }
 
     #[cfg(test)]
@@ -270,21 +315,33 @@ impl NavigationTarget {
 
     /// Allows `NavigationTarget` to be created from a `NameOwner`
     pub(crate) fn from_named(
-        file_id: FileId,
+        db: &RootDatabase,
+        file_id: hir::HirFileId,
         node: &impl ast::NameOwner,
         docs: Option<String>,
         description: Option<String>,
     ) -> NavigationTarget {
         //FIXME: use `_` instead of empty string
         let name = node.name().map(|it| it.text().clone()).unwrap_or_default();
-        let focus_range = node.name().map(|it| it.syntax().text_range());
-        NavigationTarget::from_syntax(file_id, name, focus_range, node.syntax(), docs, description)
+        let focus_range = node.name().map(|it| find_range_from_node(db, file_id, it.syntax()).1);
+        let (file_id, full_range) = find_range_from_node(db, file_id, node.syntax());
+
+        NavigationTarget::from_syntax(
+            file_id,
+            name,
+            focus_range,
+            full_range,
+            node.syntax(),
+            docs,
+            description,
+        )
     }
 
     fn from_syntax(
         file_id: FileId,
         name: SmolStr,
         focus_range: Option<TextRange>,
+        full_range: TextRange,
         node: &SyntaxNode,
         docs: Option<String>,
         description: Option<String>,
@@ -293,9 +350,8 @@ impl NavigationTarget {
             file_id,
             name,
             kind: node.kind(),
-            full_range: node.text_range(),
+            full_range,
             focus_range,
-            // ptr: Some(LocalSyntaxPtr::new(node)),
             container_name: None,
             description,
             docs,
diff --git a/crates/ra_ide_api/src/goto_definition.rs b/crates/ra_ide_api/src/goto_definition.rs
index c1ce54bea3..afa59cbe3a 100644
--- a/crates/ra_ide_api/src/goto_definition.rs
+++ b/crates/ra_ide_api/src/goto_definition.rs
@@ -101,19 +101,20 @@ pub(crate) fn name_definition(
         }
     }
 
-    if let Some(nav) = named_target(file_id, &parent) {
+    if let Some(nav) = named_target(db, file_id, &parent) {
         return Some(vec![nav]);
     }
 
     None
 }
 
-fn named_target(file_id: FileId, node: &SyntaxNode) -> Option<NavigationTarget> {
+fn named_target(db: &RootDatabase, file_id: FileId, node: &SyntaxNode) -> Option<NavigationTarget> {
     match_ast! {
         match node {
             ast::StructDef(it) => {
                 Some(NavigationTarget::from_named(
-                    file_id,
+                    db,
+                    file_id.into(),
                     &it,
                     it.doc_comment_text(),
                     it.short_label(),
@@ -121,7 +122,8 @@ fn named_target(file_id: FileId, node: &SyntaxNode) -> Option<NavigationTarget>
             },
             ast::EnumDef(it) => {
                 Some(NavigationTarget::from_named(
-                    file_id,
+                    db,
+                    file_id.into(),
                     &it,
                     it.doc_comment_text(),
                     it.short_label(),
@@ -129,7 +131,8 @@ fn named_target(file_id: FileId, node: &SyntaxNode) -> Option<NavigationTarget>
             },
             ast::EnumVariant(it) => {
                 Some(NavigationTarget::from_named(
-                    file_id,
+                    db,
+                    file_id.into(),
                     &it,
                     it.doc_comment_text(),
                     it.short_label(),
@@ -137,7 +140,8 @@ fn named_target(file_id: FileId, node: &SyntaxNode) -> Option<NavigationTarget>
             },
             ast::FnDef(it) => {
                 Some(NavigationTarget::from_named(
-                    file_id,
+                    db,
+                    file_id.into(),
                     &it,
                     it.doc_comment_text(),
                     it.short_label(),
@@ -145,7 +149,8 @@ fn named_target(file_id: FileId, node: &SyntaxNode) -> Option<NavigationTarget>
             },
             ast::TypeAliasDef(it) => {
                 Some(NavigationTarget::from_named(
-                    file_id,
+                    db,
+                    file_id.into(),
                     &it,
                     it.doc_comment_text(),
                     it.short_label(),
@@ -153,7 +158,8 @@ fn named_target(file_id: FileId, node: &SyntaxNode) -> Option<NavigationTarget>
             },
             ast::ConstDef(it) => {
                 Some(NavigationTarget::from_named(
-                    file_id,
+                    db,
+                    file_id.into(),
                     &it,
                     it.doc_comment_text(),
                     it.short_label(),
@@ -161,7 +167,8 @@ fn named_target(file_id: FileId, node: &SyntaxNode) -> Option<NavigationTarget>
             },
             ast::StaticDef(it) => {
                 Some(NavigationTarget::from_named(
-                    file_id,
+                    db,
+                    file_id.into(),
                     &it,
                     it.doc_comment_text(),
                     it.short_label(),
@@ -169,7 +176,8 @@ fn named_target(file_id: FileId, node: &SyntaxNode) -> Option<NavigationTarget>
             },
             ast::TraitDef(it) => {
                 Some(NavigationTarget::from_named(
-                    file_id,
+                    db,
+                    file_id.into(),
                     &it,
                     it.doc_comment_text(),
                     it.short_label(),
@@ -177,7 +185,8 @@ fn named_target(file_id: FileId, node: &SyntaxNode) -> Option<NavigationTarget>
             },
             ast::RecordFieldDef(it) => {
                 Some(NavigationTarget::from_named(
-                    file_id,
+                    db,
+                    file_id.into(),
                     &it,
                     it.doc_comment_text(),
                     it.short_label(),
@@ -185,7 +194,8 @@ fn named_target(file_id: FileId, node: &SyntaxNode) -> Option<NavigationTarget>
             },
             ast::Module(it) => {
                 Some(NavigationTarget::from_named(
-                    file_id,
+                    db,
+                    file_id.into(),
                     &it,
                     it.doc_comment_text(),
                     it.short_label(),
@@ -193,7 +203,8 @@ fn named_target(file_id: FileId, node: &SyntaxNode) -> Option<NavigationTarget>
             },
             ast::MacroCall(it) => {
                 Some(NavigationTarget::from_named(
-                    file_id,
+                    db,
+                    file_id.into(),
                     &it,
                     it.doc_comment_text(),
                     None,
@@ -334,6 +345,46 @@ mod tests {
         );
     }
 
+    #[test]
+    fn goto_definition_works_for_macro_defined_fn_with_arg() {
+        check_goto(
+            "
+            //- /lib.rs
+            macro_rules! define_fn {
+                ($name:ident) => (fn $name() {})
+            }
+
+            define_fn!(
+                foo
+            )
+
+            fn bar() {
+               <|>foo();
+            }
+            ",
+            "foo FN_DEF FileId(1) [80; 83) [80; 83)",
+        );
+    }
+
+    #[test]
+    fn goto_definition_works_for_macro_defined_fn_no_arg() {
+        check_goto(
+            "
+            //- /lib.rs
+            macro_rules! define_fn {
+                () => (fn foo() {})
+            }
+
+            define_fn!();
+
+            fn bar() {
+               <|>foo();
+            }
+            ",
+            "foo FN_DEF FileId(1) [39; 42) [39; 42)",
+        );
+    }
+
     #[test]
     fn goto_definition_works_for_methods() {
         covers!(goto_definition_works_for_methods);
diff --git a/crates/ra_ide_api/src/status.rs b/crates/ra_ide_api/src/status.rs
index f91f16c8e4..1bb27eb851 100644
--- a/crates/ra_ide_api/src/status.rs
+++ b/crates/ra_ide_api/src/status.rs
@@ -94,10 +94,10 @@ impl FromIterator<TableEntry<FileId, Parse<ast::SourceFile>>> for SyntaxTreeStat
     }
 }
 
-impl FromIterator<TableEntry<MacroFile, Option<Parse<SyntaxNode>>>> for SyntaxTreeStats {
+impl<M> FromIterator<TableEntry<MacroFile, Option<(Parse<SyntaxNode>, M)>>> for SyntaxTreeStats {
     fn from_iter<T>(iter: T) -> SyntaxTreeStats
     where
-        T: IntoIterator<Item = TableEntry<MacroFile, Option<Parse<SyntaxNode>>>>,
+        T: IntoIterator<Item = TableEntry<MacroFile, Option<(Parse<SyntaxNode>, M)>>>,
     {
         let mut res = SyntaxTreeStats::default();
         for entry in iter {
diff --git a/crates/ra_lsp_server/Cargo.toml b/crates/ra_lsp_server/Cargo.toml
index 743f6a60a1..dd4d3bf9ab 100644
--- a/crates/ra_lsp_server/Cargo.toml
+++ b/crates/ra_lsp_server/Cargo.toml
@@ -9,7 +9,7 @@ threadpool = "1.7.1"
 relative-path = "1.0.0"
 serde_json = "1.0.34"
 serde = { version = "1.0.83", features = ["derive"] }
-crossbeam-channel = "0.4"
+crossbeam-channel = "0.3.5"
 flexi_logger = "0.14.0"
 log = "0.4.3"
 lsp-types = { version = "0.61.0", features = ["proposed"] }
@@ -20,7 +20,7 @@ ra_vfs = "0.5.0"
 ra_syntax = { path = "../ra_syntax" }
 ra_text_edit = { path = "../ra_text_edit" }
 ra_ide_api = { path = "../ra_ide_api" }
-lsp-server = "0.3.0"
+lsp-server = "0.2.0"
 ra_project_model = { path = "../ra_project_model" }
 ra_prof = { path = "../ra_prof" }
 ra_vfs_glob = { path = "../ra_vfs_glob" }
diff --git a/crates/ra_mbe/src/lib.rs b/crates/ra_mbe/src/lib.rs
index 15f0001755..70a289f099 100644
--- a/crates/ra_mbe/src/lib.rs
+++ b/crates/ra_mbe/src/lib.rs
@@ -32,7 +32,7 @@ pub enum ExpandError {
 
 pub use crate::syntax_bridge::{
     ast_to_token_tree, syntax_node_to_token_tree, token_tree_to_expr, token_tree_to_items,
-    token_tree_to_macro_stmts, token_tree_to_pat, token_tree_to_ty,
+    token_tree_to_macro_stmts, token_tree_to_pat, token_tree_to_ty, RevTokenMap, TokenMap,
 };
 
 /// This struct contains AST for a single `macro_rules` definition. What might
@@ -118,6 +118,10 @@ impl MacroRules {
         shift_subtree(&mut tt, self.shift);
         mbe_expander::expand(self, &tt)
     }
+
+    pub fn shift(&self) -> u32 {
+        self.shift
+    }
 }
 
 impl Rule {
diff --git a/crates/ra_mbe/src/syntax_bridge.rs b/crates/ra_mbe/src/syntax_bridge.rs
index 592fcf527b..9653f7fef7 100644
--- a/crates/ra_mbe/src/syntax_bridge.rs
+++ b/crates/ra_mbe/src/syntax_bridge.rs
@@ -14,12 +14,18 @@ use crate::subtree_source::SubtreeTokenSource;
 use crate::ExpandError;
 
 /// Maps `tt::TokenId` to the relative range of the original token.
-#[derive(Default)]
+#[derive(Debug, PartialEq, Eq, Default)]
 pub struct TokenMap {
     /// Maps `tt::TokenId` to the *relative* source range.
     tokens: Vec<TextRange>,
 }
 
+/// Maps relative range of the expanded syntax node to `tt::TokenId`
+#[derive(Debug, PartialEq, Eq, Default)]
+pub struct RevTokenMap {
+    pub ranges: Vec<(TextRange, tt::TokenId)>,
+}
+
 /// Convert the syntax tree (what user has written) to a `TokenTree` (what macro
 /// will consume).
 pub fn ast_to_token_tree(ast: &ast::TokenTree) -> Option<(tt::Subtree, TokenMap)> {
@@ -52,7 +58,7 @@ pub fn syntax_node_to_token_tree(node: &SyntaxNode) -> Option<(tt::Subtree, Toke
 fn fragment_to_syntax_node(
     tt: &tt::Subtree,
     fragment_kind: FragmentKind,
-) -> Result<Parse<SyntaxNode>, ExpandError> {
+) -> Result<(Parse<SyntaxNode>, RevTokenMap), ExpandError> {
     let tmp;
     let tokens = match tt {
         tt::Subtree { delimiter: tt::Delimiter::None, token_trees } => token_trees.as_slice(),
@@ -69,38 +75,33 @@ fn fragment_to_syntax_node(
         return Err(ExpandError::ConversionError);
     }
     //FIXME: would be cool to report errors
-    let parse = tree_sink.inner.finish();
-    Ok(parse)
+    let (parse, range_map) = tree_sink.finish();
+    Ok((parse, range_map))
 }
 
-/// Parses the token tree (result of macro expansion) to an expression
-pub fn token_tree_to_expr(tt: &tt::Subtree) -> Result<Parse<ast::Expr>, ExpandError> {
-    let parse = fragment_to_syntax_node(tt, Expr)?;
-    parse.cast().ok_or_else(|| crate::ExpandError::ConversionError)
-}
-
-/// Parses the token tree (result of macro expansion) to a Pattern
-pub fn token_tree_to_pat(tt: &tt::Subtree) -> Result<Parse<ast::Pat>, ExpandError> {
-    let parse = fragment_to_syntax_node(tt, Pattern)?;
-    parse.cast().ok_or_else(|| crate::ExpandError::ConversionError)
-}
-
-/// Parses the token tree (result of macro expansion) to a Type
-pub fn token_tree_to_ty(tt: &tt::Subtree) -> Result<Parse<ast::TypeRef>, ExpandError> {
-    let parse = fragment_to_syntax_node(tt, Type)?;
-    parse.cast().ok_or_else(|| crate::ExpandError::ConversionError)
-}
-
-/// Parses the token tree (result of macro expansion) as a sequence of stmts
-pub fn token_tree_to_macro_stmts(tt: &tt::Subtree) -> Result<Parse<ast::MacroStmts>, ExpandError> {
-    let parse = fragment_to_syntax_node(tt, Statements)?;
-    parse.cast().ok_or_else(|| crate::ExpandError::ConversionError)
+macro_rules! impl_token_tree_conversions {
+    ($($(#[$attr:meta])* $name:ident => ($kind:ident, $t:ty) ),*) => {
+        $(
+            $(#[$attr])*
+            pub fn $name(tt: &tt::Subtree) -> Result<(Parse<$t>, RevTokenMap), ExpandError> {
+                let (parse, map) = fragment_to_syntax_node(tt, $kind)?;
+                parse.cast().ok_or_else(|| crate::ExpandError::ConversionError).map(|p| (p, map))
+            }
+        )*
+    }
 }
 
-/// Parses the token tree (result of macro expansion) as a sequence of items
-pub fn token_tree_to_items(tt: &tt::Subtree) -> Result<Parse<ast::MacroItems>, ExpandError> {
-    let parse = fragment_to_syntax_node(tt, Items)?;
-    parse.cast().ok_or_else(|| crate::ExpandError::ConversionError)
+impl_token_tree_conversions! {
+    /// Parses the token tree (result of macro expansion) to an expression
+    token_tree_to_expr => (Expr, ast::Expr),
+    /// Parses the token tree (result of macro expansion) to a Pattern
+    token_tree_to_pat => (Pattern, ast::Pat),
+    /// Parses the token tree (result of macro expansion) to a Type
+    token_tree_to_ty => (Type, ast::TypeRef),
+    /// Parses the token tree (result of macro expansion) as a sequence of stmts
+    token_tree_to_macro_stmts => (Statements, ast::MacroStmts),
+    /// Parses the token tree (result of macro expansion) as a sequence of items
+    token_tree_to_items => (Items, ast::MacroItems)
 }
 
 impl TokenMap {
@@ -116,6 +117,12 @@ impl TokenMap {
     }
 }
 
+impl RevTokenMap {
+    fn add(&mut self, relative_range: TextRange, token_id: tt::TokenId) {
+        self.ranges.push((relative_range, token_id.clone()))
+    }
+}
+
 /// Returns the textual content of a doc comment block as a quoted string
 /// That is, strips leading `///` (or `/**`, etc)
 /// and strips the ending `*/`
@@ -262,6 +269,7 @@ struct TtTreeSink<'a> {
     cursor: Cursor<'a>,
     text_pos: TextUnit,
     inner: SyntaxTreeBuilder,
+    range_map: RevTokenMap,
 
     // Number of roots
     // Use for detect ill-form tree which is not single root
@@ -276,8 +284,13 @@ impl<'a> TtTreeSink<'a> {
             text_pos: 0.into(),
             inner: SyntaxTreeBuilder::default(),
             roots: smallvec::SmallVec::new(),
+            range_map: RevTokenMap::default(),
         }
     }
+
+    fn finish(self) -> (Parse<SyntaxNode>, RevTokenMap) {
+        (self.inner.finish(), self.range_map)
+    }
 }
 
 fn delim_to_str(d: tt::Delimiter, closing: bool) -> SmolStr {
@@ -307,6 +320,15 @@ impl<'a> TreeSink for TtTreeSink<'a> {
 
             match self.cursor.token_tree() {
                 Some(tt::TokenTree::Leaf(leaf)) => {
+                    // Mark the range if needed
+                    if let tt::Leaf::Ident(ident) = leaf {
+                        if kind == IDENT {
+                            let range =
+                                TextRange::offset_len(self.text_pos, TextUnit::of_str(&ident.text));
+                            self.range_map.add(range, ident.id);
+                        }
+                    }
+
                     self.cursor = self.cursor.bump();
                     self.buf += &format!("{}", leaf);
                 }
@@ -337,6 +359,7 @@ impl<'a> TreeSink for TtTreeSink<'a> {
         {
             if curr.spacing == tt::Spacing::Alone {
                 self.inner.token(WHITESPACE, " ".into());
+                self.text_pos += TextUnit::of_char(' ');
             }
         }
     }
diff --git a/crates/ra_parser/src/grammar/types.rs b/crates/ra_parser/src/grammar/types.rs
index 9b2e440fb9..d4ca94fca5 100644
--- a/crates/ra_parser/src/grammar/types.rs
+++ b/crates/ra_parser/src/grammar/types.rs
@@ -28,7 +28,7 @@ fn type_with_bounds_cond(p: &mut Parser, allow_bounds: bool) {
         T![fn] | T![unsafe] | T![extern] => fn_pointer_type(p),
         T![for] => for_type(p),
         T![impl] => impl_trait_type(p),
-        T![dyn] => dyn_trait_type(p),
+        T![dyn ] => dyn_trait_type(p),
         // Some path types are not allowed to have bounds (no plus)
         T![<] => path_type_(p, allow_bounds),
         _ if paths::is_use_path_start(p) => path_or_macro_type_(p, allow_bounds),
@@ -234,9 +234,9 @@ fn impl_trait_type(p: &mut Parser) {
 // test dyn_trait_type
 // type A = dyn Iterator<Item=Foo<'a>> + 'a;
 fn dyn_trait_type(p: &mut Parser) {
-    assert!(p.at(T![dyn]));
+    assert!(p.at(T![dyn ]));
     let m = p.start();
-    p.bump(T![dyn]);
+    p.bump(T![dyn ]);
     type_params::bounds_without_colon(p);
     m.complete(p, DYN_TRAIT_TYPE);
 }
diff --git a/xtask/src/codegen.rs b/xtask/src/codegen.rs
index 770b55a9ac..4ec8ab75af 100644
--- a/xtask/src/codegen.rs
+++ b/xtask/src/codegen.rs
@@ -46,7 +46,7 @@ pub enum Mode {
 /// With verify = false,
 fn update(path: &Path, contents: &str, mode: Mode) -> Result<()> {
     match fs::read_to_string(path) {
-        Ok(ref old_contents) if normalize(old_contents) == normalize(contents) => {
+        Ok(ref old_contents) if old_contents == contents => {
             return Ok(());
         }
         _ => (),
@@ -56,11 +56,7 @@ fn update(path: &Path, contents: &str, mode: Mode) -> Result<()> {
     }
     eprintln!("updating {}", path.display());
     fs::write(path, contents)?;
-    return Ok(());
-
-    fn normalize(s: &str) -> String {
-        s.replace("\r\n", "\n")
-    }
+    Ok(())
 }
 
 fn reformat(text: impl std::fmt::Display) -> Result<String> {


Test patch:
diff --git a/crates/ra_assists/src/doc_tests.rs b/crates/ra_assists/src/doc_tests.rs
index a8f8446cb6..6e1e3de843 100644
--- a/crates/ra_assists/src/doc_tests.rs
+++ b/crates/ra_assists/src/doc_tests.rs
@@ -5,14 +5,13 @@
 
 mod generated;
 
-use ra_db::{fixture::WithFixture, FileRange};
+use hir::mock::MockDatabase;
+use ra_db::FileRange;
 use test_utils::{assert_eq_text, extract_range_or_offset};
 
-use crate::test_db::TestDB;
-
 fn check(assist_id: &str, before: &str, after: &str) {
     let (selection, before) = extract_range_or_offset(before);
-    let (db, file_id) = TestDB::with_single_file(&before);
+    let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
     let frange = FileRange { file_id, range: selection.into() };
 
     let (_assist_id, action) = crate::assists(&db, frange)
diff --git a/crates/ra_assists/src/test_db.rs b/crates/ra_assists/src/test_db.rs
deleted file mode 100644
index 5be7383edc..0000000000
--- a/crates/ra_assists/src/test_db.rs
+++ /dev/null
@@ -1,45 +0,0 @@
-//! Database used for testing `ra_assists`.
-
-use std::sync::Arc;
-
-use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, RelativePath};
-
-#[salsa::database(
-    ra_db::SourceDatabaseExtStorage,
-    ra_db::SourceDatabaseStorage,
-    hir::db::InternDatabaseStorage,
-    hir::db::AstDatabaseStorage,
-    hir::db::DefDatabaseStorage,
-    hir::db::DefDatabase2Storage,
-    hir::db::HirDatabaseStorage
-)]
-#[derive(Debug, Default)]
-pub struct TestDB {
-    runtime: salsa::Runtime<TestDB>,
-}
-
-impl salsa::Database for TestDB {
-    fn salsa_runtime(&self) -> &salsa::Runtime<Self> {
-        &self.runtime
-    }
-}
-
-impl std::panic::RefUnwindSafe for TestDB {}
-
-impl FileLoader for TestDB {
-    fn file_text(&self, file_id: FileId) -> Arc<String> {
-        FileLoaderDelegate(self).file_text(file_id)
-    }
-    fn resolve_relative_path(
-        &self,
-        anchor: FileId,
-        relative_path: &RelativePath,
-    ) -> Option<FileId> {
-        FileLoaderDelegate(self).resolve_relative_path(anchor, relative_path)
-    }
-    fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> {
-        FileLoaderDelegate(self).relevant_crates(file_id)
-    }
-}
-
-impl hir::debug::HirDebugHelper for TestDB {}
diff --git a/crates/ra_hir/src/test_db.rs b/crates/ra_hir/src/test_db.rs
deleted file mode 100644
index 5237b303af..0000000000
--- a/crates/ra_hir/src/test_db.rs
+++ /dev/null
@@ -1,120 +0,0 @@
-//! Database used for testing `hir`.
-
-use std::{panic, sync::Arc};
-
-use hir_def::{db::DefDatabase2, ModuleId};
-use hir_expand::diagnostics::DiagnosticSink;
-use parking_lot::Mutex;
-use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, RelativePath, SourceDatabase};
-
-use crate::{db, debug::HirDebugHelper};
-
-#[salsa::database(
-    ra_db::SourceDatabaseExtStorage,
-    ra_db::SourceDatabaseStorage,
-    db::InternDatabaseStorage,
-    db::AstDatabaseStorage,
-    db::DefDatabaseStorage,
-    db::DefDatabase2Storage,
-    db::HirDatabaseStorage
-)]
-#[derive(Debug, Default)]
-pub struct TestDB {
-    events: Mutex<Option<Vec<salsa::Event<TestDB>>>>,
-    runtime: salsa::Runtime<TestDB>,
-}
-
-impl salsa::Database for TestDB {
-    fn salsa_runtime(&self) -> &salsa::Runtime<TestDB> {
-        &self.runtime
-    }
-
-    fn salsa_event(&self, event: impl Fn() -> salsa::Event<TestDB>) {
-        let mut events = self.events.lock();
-        if let Some(events) = &mut *events {
-            events.push(event());
-        }
-    }
-}
-
-impl salsa::ParallelDatabase for TestDB {
-    fn snapshot(&self) -> salsa::Snapshot<TestDB> {
-        salsa::Snapshot::new(TestDB {
-            events: Default::default(),
-            runtime: self.runtime.snapshot(self),
-        })
-    }
-}
-
-impl panic::RefUnwindSafe for TestDB {}
-
-impl FileLoader for TestDB {
-    fn file_text(&self, file_id: FileId) -> Arc<String> {
-        FileLoaderDelegate(self).file_text(file_id)
-    }
-    fn resolve_relative_path(
-        &self,
-        anchor: FileId,
-        relative_path: &RelativePath,
-    ) -> Option<FileId> {
-        FileLoaderDelegate(self).resolve_relative_path(anchor, relative_path)
-    }
-    fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> {
-        FileLoaderDelegate(self).relevant_crates(file_id)
-    }
-}
-
-// FIXME: improve `WithFixture` to bring useful hir debugging back
-impl HirDebugHelper for TestDB {
-    fn crate_name(&self, _krate: CrateId) -> Option<String> {
-        None
-    }
-
-    fn file_path(&self, _file_id: FileId) -> Option<String> {
-        None
-    }
-}
-
-impl TestDB {
-    pub fn diagnostics(&self) -> String {
-        let mut buf = String::new();
-        let crate_graph = self.crate_graph();
-        for krate in crate_graph.iter().next() {
-            let crate_def_map = self.crate_def_map(krate);
-            for (module_id, _) in crate_def_map.modules.iter() {
-                let module_id = ModuleId { krate, module_id };
-                let module = crate::Module::from(module_id);
-                module.diagnostics(
-                    self,
-                    &mut DiagnosticSink::new(|d| {
-                        buf += &format!("{:?}: {}\n", d.syntax_node(self).text(), d.message());
-                    }),
-                )
-            }
-        }
-        buf
-    }
-}
-
-impl TestDB {
-    pub fn log(&self, f: impl FnOnce()) -> Vec<salsa::Event<TestDB>> {
-        *self.events.lock() = Some(Vec::new());
-        f();
-        self.events.lock().take().unwrap()
-    }
-
-    pub fn log_executed(&self, f: impl FnOnce()) -> Vec<String> {
-        let events = self.log(f);
-        events
-            .into_iter()
-            .filter_map(|e| match e.kind {
-                // This pretty horrible, but `Debug` is the only way to inspect
-                // QueryDescriptor at the moment.
-                salsa::EventKind::WillExecute { database_key } => {
-                    Some(format!("{:?}", database_key))
-                }
-                _ => None,
-            })
-            .collect()
-    }
-}
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs
index e56b9356e9..4b7e34878a 100644
--- a/crates/ra_hir/src/ty/tests.rs
+++ b/crates/ra_hir/src/ty/tests.rs
@@ -11,7 +11,7 @@ use ra_syntax::{
 use test_utils::covers;
 
 use crate::{
-    expr::BodySourceMap, test_db::TestDB, ty::display::HirDisplay, ty::InferenceResult,
+    expr::BodySourceMap, mock::MockDatabase, ty::display::HirDisplay, ty::InferenceResult,
     SourceAnalyzer,
 };
 
@@ -24,7 +24,7 @@ mod coercion;
 
 #[test]
 fn cfg_impl_block() {
-    let (db, pos) = TestDB::with_position(
+    let (db, pos) = MockDatabase::with_position(
         r#"
 //- /main.rs crate:main deps:foo cfg:test
 use foo::S as T;
@@ -64,7 +64,7 @@ impl S {
 
 #[test]
 fn infer_await() {
-    let (db, pos) = TestDB::with_position(
+    let (db, pos) = MockDatabase::with_position(
         r#"
 //- /main.rs crate:main deps:std
 
@@ -95,7 +95,7 @@ mod future {
 
 #[test]
 fn infer_box() {
-    let (db, pos) = TestDB::with_position(
+    let (db, pos) = MockDatabase::with_position(
         r#"
 //- /main.rs crate:main deps:std
 
@@ -122,7 +122,7 @@ mod boxed {
 
 #[test]
 fn infer_adt_self() {
-    let (db, pos) = TestDB::with_position(
+    let (db, pos) = MockDatabase::with_position(
         r#"
 //- /main.rs
 enum Nat { Succ(Self), Demo(Nat), Zero }
@@ -141,7 +141,7 @@ fn test() {
 
 #[test]
 fn infer_try() {
-    let (db, pos) = TestDB::with_position(
+    let (db, pos) = MockDatabase::with_position(
         r#"
 //- /main.rs crate:main deps:std
 
@@ -181,7 +181,7 @@ mod result {
 
 #[test]
 fn infer_for_loop() {
-    let (db, pos) = TestDB::with_position(
+    let (db, pos) = MockDatabase::with_position(
         r#"
 //- /main.rs crate:main deps:std
 
@@ -223,7 +223,7 @@ mod collections {
 #[test]
 fn infer_while_let() {
     covers!(infer_while_let);
-    let (db, pos) = TestDB::with_position(
+    let (db, pos) = MockDatabase::with_position(
         r#"
 //- /main.rs
 enum Option<T> { Some(T), None }
@@ -2484,7 +2484,7 @@ pub fn main_loop() {
 
 #[test]
 fn cross_crate_associated_method_call() {
-    let (db, pos) = TestDB::with_position(
+    let (db, pos) = MockDatabase::with_position(
         r#"
 //- /main.rs crate:main deps:other_crate
 fn test() {
@@ -3378,7 +3378,7 @@ fn test() { S.foo()<|>; }
 
 #[test]
 fn infer_macro_with_dollar_crate_is_correct_in_expr() {
-    let (db, pos) = TestDB::with_position(
+    let (db, pos) = MockDatabase::with_position(
         r#"
 //- /main.rs crate:main deps:foo
 fn test() {
@@ -3482,7 +3482,7 @@ fn test() { (&S).foo()<|>; }
 
 #[test]
 fn method_resolution_trait_from_prelude() {
-    let (db, pos) = TestDB::with_position(
+    let (db, pos) = MockDatabase::with_position(
         r#"
 //- /main.rs crate:main deps:other_crate
 struct S;
@@ -4651,7 +4651,7 @@ fn test<T, U>() where T: Trait<U::Item>, U: Trait<T::Item> {
     assert_eq!(t, "{unknown}");
 }
 
-fn type_at_pos(db: &TestDB, pos: FilePosition) -> String {
+fn type_at_pos(db: &MockDatabase, pos: FilePosition) -> String {
     let file = db.parse(pos.file_id).ok().unwrap();
     let expr = algo::find_node_at_offset::<ast::Expr>(file.syntax(), pos.offset).unwrap();
     let analyzer = SourceAnalyzer::new(db, pos.file_id, expr.syntax(), Some(pos.offset));
@@ -4660,12 +4660,12 @@ fn type_at_pos(db: &TestDB, pos: FilePosition) -> String {
 }
 
 fn type_at(content: &str) -> String {
-    let (db, file_pos) = TestDB::with_position(content);
+    let (db, file_pos) = MockDatabase::with_position(content);
     type_at_pos(&db, file_pos)
 }
 
 fn infer(content: &str) -> String {
-    let (db, file_id) = TestDB::with_single_file(content);
+    let (db, _, file_id) = MockDatabase::with_single_file(content);
     let source_file = db.parse(file_id).ok().unwrap();
 
     let mut acc = String::new();
@@ -4748,7 +4748,7 @@ fn ellipsize(mut text: String, max_len: usize) -> String {
 
 #[test]
 fn typing_whitespace_inside_a_function_should_not_invalidate_types() {
-    let (mut db, pos) = TestDB::with_position(
+    let (mut db, pos) = MockDatabase::with_position(
         "
         //- /lib.rs
         fn foo() -> i32 {
@@ -4788,7 +4788,7 @@ fn typing_whitespace_inside_a_function_should_not_invalidate_types() {
 
 #[test]
 fn no_such_field_diagnostics() {
-    let diagnostics = TestDB::with_files(
+    let diagnostics = MockDatabase::with_files(
         r"
         //- /lib.rs
         struct S { foo: i32, bar: () }
diff --git a/crates/ra_mbe/src/tests.rs b/crates/ra_mbe/src/tests.rs
index a23e3afe3e..a848ea3347 100644
--- a/crates/ra_mbe/src/tests.rs
+++ b/crates/ra_mbe/src/tests.rs
@@ -126,7 +126,7 @@ fn test_expr_order() {
 "#,
     );
     let expanded = expand(&rules, "foo! { 1 + 1}");
-    let tree = token_tree_to_items(&expanded).unwrap().tree();
+    let tree = token_tree_to_items(&expanded).unwrap().0.tree();
 
     let dump = format!("{:#?}", tree.syntax());
     assert_eq_text!(
@@ -383,7 +383,7 @@ fn test_expand_to_item_list() {
             ",
     );
     let expansion = expand(&rules, "structs!(Foo, Bar);");
-    let tree = token_tree_to_items(&expansion).unwrap().tree();
+    let tree = token_tree_to_items(&expansion).unwrap().0.tree();
     assert_eq!(
         format!("{:#?}", tree.syntax()).trim(),
         r#"
@@ -501,7 +501,7 @@ fn test_tt_to_stmts() {
     );
 
     let expanded = expand(&rules, "foo!{}");
-    let stmts = token_tree_to_macro_stmts(&expanded).unwrap().tree();
+    let stmts = token_tree_to_macro_stmts(&expanded).unwrap().0.tree();
 
     assert_eq!(
         format!("{:#?}", stmts.syntax()).trim(),
@@ -946,7 +946,7 @@ fn test_vec() {
     );
 
     let expansion = expand(&rules, r#"vec![1u32,2];"#);
-    let tree = token_tree_to_expr(&expansion).unwrap().tree();
+    let tree = token_tree_to_expr(&expansion).unwrap().0.tree();
 
     assert_eq!(
         format!("{:#?}", tree.syntax()).trim(),
@@ -1436,8 +1436,8 @@ pub(crate) fn assert_expansion(
     };
     let (expanded_tree, expected_tree) = match kind {
         MacroKind::Items => {
-            let expanded_tree = token_tree_to_items(&expanded).unwrap().tree();
-            let expected_tree = token_tree_to_items(&expected).unwrap().tree();
+            let expanded_tree = token_tree_to_items(&expanded).unwrap().0.tree();
+            let expected_tree = token_tree_to_items(&expected).unwrap().0.tree();
 
             (
                 debug_dump_ignore_spaces(expanded_tree.syntax()).trim().to_string(),
@@ -1446,8 +1446,8 @@ pub(crate) fn assert_expansion(
         }
 
         MacroKind::Stmts => {
-            let expanded_tree = token_tree_to_macro_stmts(&expanded).unwrap().tree();
-            let expected_tree = token_tree_to_macro_stmts(&expected).unwrap().tree();
+            let expanded_tree = token_tree_to_macro_stmts(&expanded).unwrap().0.tree();
+            let expected_tree = token_tree_to_macro_stmts(&expected).unwrap().0.tree();
 
             (
                 debug_dump_ignore_spaces(expanded_tree.syntax()).trim().to_string(),