Repository: rust-analyzer/rust-analyzer
Pull request #4023 (2020-04-17 20:53:12)

Fix another crash from wrong binders
Basically, if we had something like `dyn Trait<T>` (where `T` is a type parameter) in an impl we lowered that to `dyn Trait<^0.0>`, when it should be `dyn Trait<^1.0>` because the `dyn` introduces a new binder. With one type parameter, that's just wrong, with two, it'll lead to crashes.


Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 3826ae1c6f..2a12d9f5b3 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -675,16 +675,6 @@ version = "2.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
 
-[[package]]
-name = "memmap"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b"
-dependencies = [
- "libc",
- "winapi 0.3.8",
-]
-
 [[package]]
 name = "memoffset"
 version = "0.5.4"
@@ -1122,7 +1112,6 @@ dependencies = [
  "difference",
  "goblin",
  "libloading",
- "memmap",
  "ra_mbe",
  "ra_proc_macro",
  "ra_tt",
@@ -1352,7 +1341,6 @@ dependencies = [
  "ra_hir_def",
  "ra_hir_ty",
  "ra_ide",
- "ra_proc_macro_srv",
  "ra_prof",
  "ra_project_model",
  "ra_syntax",
diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs
index 0b477f0e9e..2707e422d1 100644
--- a/crates/ra_hir/src/semantics.rs
+++ b/crates/ra_hir/src/semantics.rs
@@ -20,7 +20,6 @@ use rustc_hash::{FxHashMap, FxHashSet};
 
 use crate::{
     db::HirDatabase,
-    diagnostics::Diagnostic,
     semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
     source_analyzer::{resolve_hir_path, SourceAnalyzer},
     AssocItem, Function, HirFileId, ImplDef, InFile, Local, MacroDef, Module, ModuleDef, Name,
@@ -127,13 +126,6 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
         original_range(self.db, node.as_ref())
     }
 
-    pub fn diagnostics_range(&self, diagnostics: &dyn Diagnostic) -> FileRange {
-        let src = diagnostics.source();
-        let root = self.db.parse_or_expand(src.file_id).unwrap();
-        let node = src.value.to_node(&root);
-        original_range(self.db, src.with_value(&node))
-    }
-
     pub fn ancestors_with_macros(&self, node: SyntaxNode) -> impl Iterator<Item = SyntaxNode> + '_ {
         let node = self.find_file(node);
         node.ancestors_with_macros(self.db).map(|it| it.value)
diff --git a/crates/ra_hir/src/source_analyzer.rs b/crates/ra_hir/src/source_analyzer.rs
index 23af400b8b..58ae6ce418 100644
--- a/crates/ra_hir/src/source_analyzer.rs
+++ b/crates/ra_hir/src/source_analyzer.rs
@@ -23,7 +23,7 @@ use hir_ty::{
 };
 use ra_syntax::{
     ast::{self, AstNode},
-    SyntaxNode, TextRange, TextUnit,
+    SyntaxNode, SyntaxNodePtr, TextUnit,
 };
 
 use crate::{
@@ -56,7 +56,7 @@ impl SourceAnalyzer {
         let scopes = db.expr_scopes(def);
         let scope = match offset {
             None => scope_for(&scopes, &source_map, node),
-            Some(offset) => scope_for_offset(db, &scopes, &source_map, node.with_value(offset)),
+            Some(offset) => scope_for_offset(&scopes, &source_map, node.with_value(offset)),
         };
         let resolver = resolver_for_scope(db.upcast(), def, scope);
         SourceAnalyzer {
@@ -304,7 +304,6 @@ fn scope_for(
 }
 
 fn scope_for_offset(
-    db: &dyn HirDatabase,
     scopes: &ExprScopes,
     source_map: &BodySourceMap,
     offset: InFile<TextUnit>,
@@ -318,63 +317,21 @@ fn scope_for_offset(
             if source.file_id != offset.file_id {
                 return None;
             }
-            let root = source.file_syntax(db.upcast());
-            let node = source.value.to_node(&root);
-            Some((node.syntax().text_range(), scope))
+            let syntax_node_ptr = source.value.syntax_node_ptr();
+            Some((syntax_node_ptr, scope))
         })
         // find containing scope
-        .min_by_key(|(expr_range, _scope)| {
+        .min_by_key(|(ptr, _scope)| {
             (
-                !(expr_range.start() <= offset.value && offset.value <= expr_range.end()),
-                expr_range.len(),
+                !(ptr.range().start() <= offset.value && offset.value <= ptr.range().end()),
+                ptr.range().len(),
             )
         })
-        .map(|(expr_range, scope)| {
-            adjust(db, scopes, source_map, expr_range, offset.file_id, offset.value)
-                .unwrap_or(*scope)
+        .map(|(ptr, scope)| {
+            adjust(scopes, source_map, ptr, offset.file_id, offset.value).unwrap_or(*scope)
         })
 }
 
-// XXX: during completion, cursor might be outside of any particular
-// expression. Try to figure out the correct scope...
-fn adjust(
-    db: &dyn HirDatabase,
-    scopes: &ExprScopes,
-    source_map: &BodySourceMap,
-    expr_range: TextRange,
-    file_id: HirFileId,
-    offset: TextUnit,
-) -> Option<ScopeId> {
-    let child_scopes = scopes
-        .scope_by_expr()
-        .iter()
-        .filter_map(|(id, scope)| {
-            let source = source_map.expr_syntax(*id).ok()?;
-            // FIXME: correctly handle macro expansion
-            if source.file_id != file_id {
-                return None;
-            }
-            let root = source.file_syntax(db.upcast());
-            let node = source.value.to_node(&root);
-            Some((node.syntax().text_range(), scope))
-        })
-        .filter(|(range, _)| {
-            range.start() <= offset && range.is_subrange(&expr_range) && *range != expr_range
-        });
-
-    child_scopes
-        .max_by(|(r1, _), (r2, _)| {
-            if r2.is_subrange(&r1) {
-                std::cmp::Ordering::Greater
-            } else if r1.is_subrange(&r2) {
-                std::cmp::Ordering::Less
-            } else {
-                r1.start().cmp(&r2.start())
-            }
-        })
-        .map(|(_ptr, scope)| *scope)
-}
-
 pub(crate) fn resolve_hir_path(
     db: &dyn HirDatabase,
     resolver: &Resolver,
@@ -419,3 +376,41 @@ pub(crate) fn resolve_hir_path(
             .map(|def| PathResolution::Macro(def.into()))
     })
 }
+
+// XXX: during completion, cursor might be outside of any particular
+// expression. Try to figure out the correct scope...
+fn adjust(
+    scopes: &ExprScopes,
+    source_map: &BodySourceMap,
+    ptr: SyntaxNodePtr,
+    file_id: HirFileId,
+    offset: TextUnit,
+) -> Option<ScopeId> {
+    let r = ptr.range();
+    let child_scopes = scopes
+        .scope_by_expr()
+        .iter()
+        .filter_map(|(id, scope)| {
+            let source = source_map.expr_syntax(*id).ok()?;
+            // FIXME: correctly handle macro expansion
+            if source.file_id != file_id {
+                return None;
+            }
+            let syntax_node_ptr = source.value.syntax_node_ptr();
+            Some((syntax_node_ptr, scope))
+        })
+        .map(|(ptr, scope)| (ptr.range(), scope))
+        .filter(|(range, _)| range.start() <= offset && range.is_subrange(&r) && *range != r);
+
+    child_scopes
+        .max_by(|(r1, _), (r2, _)| {
+            if r2.is_subrange(&r1) {
+                std::cmp::Ordering::Greater
+            } else if r1.is_subrange(&r2) {
+                std::cmp::Ordering::Less
+            } else {
+                r1.start().cmp(&r2.start())
+            }
+        })
+        .map(|(_ptr, scope)| *scope)
+}
diff --git a/crates/ra_hir_def/src/body.rs b/crates/ra_hir_def/src/body.rs
index 3b169440ad..eafaf48c17 100644
--- a/crates/ra_hir_def/src/body.rs
+++ b/crates/ra_hir_def/src/body.rs
@@ -210,7 +210,7 @@ pub struct BodySourceMap {
     expr_map_back: ArenaMap<ExprId, Result<ExprSource, SyntheticSyntax>>,
     pat_map: FxHashMap<PatSource, PatId>,
     pat_map_back: ArenaMap<PatId, Result<PatSource, SyntheticSyntax>>,
-    field_map: FxHashMap<(ExprId, usize), InFile<AstPtr<ast::RecordField>>>,
+    field_map: FxHashMap<(ExprId, usize), AstPtr<ast::RecordField>>,
     expansions: FxHashMap<InFile<AstPtr<ast::MacroCall>>, HirFileId>,
 }
 
@@ -303,7 +303,7 @@ impl BodySourceMap {
         self.pat_map.get(&src).cloned()
     }
 
-    pub fn field_syntax(&self, expr: ExprId, field: usize) -> InFile<AstPtr<ast::RecordField>> {
+    pub fn field_syntax(&self, expr: ExprId, field: usize) -> AstPtr<ast::RecordField> {
         self.field_map[&(expr, field)].clone()
     }
 }
diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs
index 82a52804d5..79abe55ce6 100644
--- a/crates/ra_hir_def/src/body/lower.rs
+++ b/crates/ra_hir_def/src/body/lower.rs
@@ -320,8 +320,7 @@ impl ExprCollector<'_> {
 
                 let res = self.alloc_expr(record_lit, syntax_ptr);
                 for (i, ptr) in field_ptrs.into_iter().enumerate() {
-                    let src = self.expander.to_source(ptr);
-                    self.source_map.field_map.insert((res, i), src);
+                    self.source_map.field_map.insert((res, i), ptr);
                 }
                 res
             }
@@ -651,7 +650,6 @@ impl ExprCollector<'_> {
             ast::Pat::SlicePat(p) => {
                 let SlicePatComponents { prefix, slice, suffix } = p.components();
 
-                // FIXME properly handle `DotDotPat`
                 Pat::Slice {
                     prefix: prefix.into_iter().map(|p| self.collect_pat(p)).collect(),
                     slice: slice.map(|p| self.collect_pat(p)),
@@ -668,15 +666,9 @@ impl ExprCollector<'_> {
                     Pat::Missing
                 }
             }
-            ast::Pat::DotDotPat(_) => {
-                // `DotDotPat` requires special handling and should not be mapped
-                // to a Pat. Here we are using `Pat::Missing` as a fallback for
-                // when `DotDotPat` is mapped to `Pat`, which can easily happen
-                // when the source code being analyzed has a malformed pattern
-                // which includes `..` in a place where it isn't valid.
-
-                Pat::Missing
-            }
+            ast::Pat::DotDotPat(_) => unreachable!(
+                "`DotDotPat` requires special handling and should not be mapped to a Pat."
+            ),
             // FIXME: implement
             ast::Pat::BoxPat(_) | ast::Pat::RangePat(_) | ast::Pat::MacroPat(_) => Pat::Missing,
         };
diff --git a/crates/ra_hir_def/src/diagnostics.rs b/crates/ra_hir_def/src/diagnostics.rs
index 510c5e0648..cfa0f2f76f 100644
--- a/crates/ra_hir_def/src/diagnostics.rs
+++ b/crates/ra_hir_def/src/diagnostics.rs
@@ -20,7 +20,7 @@ impl Diagnostic for UnresolvedModule {
         "unresolved module".to_string()
     }
     fn source(&self) -> InFile<SyntaxNodePtr> {
-        InFile::new(self.file, self.decl.clone().into())
+        InFile { file_id: self.file, value: self.decl.clone().into() }
     }
     fn as_any(&self) -> &(dyn Any + Send + 'static) {
         self
diff --git a/crates/ra_hir_expand/src/diagnostics.rs b/crates/ra_hir_expand/src/diagnostics.rs
index 99209c6e8c..108c1e38c6 100644
--- a/crates/ra_hir_expand/src/diagnostics.rs
+++ b/crates/ra_hir_expand/src/diagnostics.rs
@@ -16,13 +16,16 @@
 
 use std::{any::Any, fmt};
 
-use ra_syntax::{SyntaxNode, SyntaxNodePtr};
+use ra_syntax::{SyntaxNode, SyntaxNodePtr, TextRange};
 
 use crate::{db::AstDatabase, InFile};
 
 pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static {
     fn message(&self) -> String;
     fn source(&self) -> InFile<SyntaxNodePtr>;
+    fn highlight_range(&self) -> TextRange {
+        self.source().value.range()
+    }
     fn as_any(&self) -> &(dyn Any + Send + 'static);
 }
 
diff --git a/crates/ra_hir_ty/src/diagnostics.rs b/crates/ra_hir_ty/src/diagnostics.rs
index c8fd548615..927896d6f7 100644
--- a/crates/ra_hir_ty/src/diagnostics.rs
+++ b/crates/ra_hir_ty/src/diagnostics.rs
@@ -21,7 +21,7 @@ impl Diagnostic for NoSuchField {
     }
 
     fn source(&self) -> InFile<SyntaxNodePtr> {
-        InFile::new(self.file, self.field.clone().into())
+        InFile { file_id: self.file, value: self.field.clone().into() }
     }
 
     fn as_any(&self) -> &(dyn Any + Send + 'static) {
diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs
index b6d9b3438e..246b0e9be2 100644
--- a/crates/ra_hir_ty/src/infer.rs
+++ b/crates/ra_hir_ty/src/infer.rs
@@ -682,10 +682,10 @@ mod diagnostics {
         ) {
             match self {
                 InferenceDiagnostic::NoSuchField { expr, field } => {
-                    let source = owner.lookup(db.upcast()).source(db.upcast());
+                    let file = owner.lookup(db.upcast()).source(db.upcast()).file_id;
                     let (_, source_map) = db.body_with_source_map(owner.into());
                     let field = source_map.field_syntax(*expr, *field);
-                    sink.push(NoSuchField { file: source.file_id, field: field.value })
+                    sink.push(NoSuchField { file, field })
                 }
             }
         }
diff --git a/crates/ra_hir_ty/src/infer/coerce.rs b/crates/ra_hir_ty/src/infer/coerce.rs
index 89200255a2..959b1e2124 100644
--- a/crates/ra_hir_ty/src/infer/coerce.rs
+++ b/crates/ra_hir_ty/src/infer/coerce.rs
@@ -51,7 +51,7 @@ impl<'a> InferenceContext<'a> {
             // Trivial cases, this should go after `never` check to
             // avoid infer result type to be never
             _ => {
-                if self.table.unify_inner_trivial(&from_ty, &to_ty, 0) {
+                if self.table.unify_inner_trivial(&from_ty, &to_ty) {
                     return true;
                 }
             }
@@ -175,7 +175,7 @@ impl<'a> InferenceContext<'a> {
                     return self.table.unify_substs(st1, st2, 0);
                 }
                 _ => {
-                    if self.table.unify_inner_trivial(&derefed_ty, &to_ty, 0) {
+                    if self.table.unify_inner_trivial(&derefed_ty, &to_ty) {
                         return true;
                     }
                 }
diff --git a/crates/ra_hir_ty/src/infer/unify.rs b/crates/ra_hir_ty/src/infer/unify.rs
index ab0bc8b70b..5f6cea8d31 100644
--- a/crates/ra_hir_ty/src/infer/unify.rs
+++ b/crates/ra_hir_ty/src/infer/unify.rs
@@ -8,8 +8,7 @@ use test_utils::tested_by;
 
 use super::{InferenceContext, Obligation};
 use crate::{
-    BoundVar, Canonical, DebruijnIndex, GenericPredicate, InEnvironment, InferTy, Substs, Ty,
-    TypeCtor, TypeWalk,
+    BoundVar, Canonical, DebruijnIndex, InEnvironment, InferTy, Substs, Ty, TypeCtor, TypeWalk,
 };
 
 impl<'a> InferenceContext<'a> {
@@ -227,26 +226,16 @@ impl InferenceTable {
             (Ty::Apply(a_ty1), Ty::Apply(a_ty2)) if a_ty1.ctor == a_ty2.ctor => {
                 self.unify_substs(&a_ty1.parameters, &a_ty2.parameters, depth + 1)
             }
-
-            _ => self.unify_inner_trivial(&ty1, &ty2, depth),
+            _ => self.unify_inner_trivial(&ty1, &ty2),
         }
     }
 
-    pub(super) fn unify_inner_trivial(&mut self, ty1: &Ty, ty2: &Ty, depth: usize) -> bool {
+    pub(super) fn unify_inner_trivial(&mut self, ty1: &Ty, ty2: &Ty) -> bool {
         match (ty1, ty2) {
             (Ty::Unknown, _) | (_, Ty::Unknown) => true,
 
             (Ty::Placeholder(p1), Ty::Placeholder(p2)) if *p1 == *p2 => true,
 
-            (Ty::Dyn(dyn1), Ty::Dyn(dyn2)) if dyn1.len() == dyn2.len() => {
-                for (pred1, pred2) in dyn1.iter().zip(dyn2.iter()) {
-                    if !self.unify_preds(pred1, pred2, depth + 1) {
-                        return false;
-                    }
-                }
-                true
-            }
-
             (Ty::Infer(InferTy::TypeVar(tv1)), Ty::Infer(InferTy::TypeVar(tv2)))
             | (Ty::Infer(InferTy::IntVar(tv1)), Ty::Infer(InferTy::IntVar(tv2)))
             | (Ty::Infer(InferTy::FloatVar(tv1)), Ty::Infer(InferTy::FloatVar(tv2)))
@@ -279,31 +268,6 @@ impl InferenceTable {
         }
     }
 
-    fn unify_preds(
-        &mut self,
-        pred1: &GenericPredicate,
-        pred2: &GenericPredicate,
-        depth: usize,
-    ) -> bool {
-        match (pred1, pred2) {
-            (GenericPredicate::Implemented(tr1), GenericPredicate::Implemented(tr2))
-                if tr1.trait_ == tr2.trait_ =>
-            {
-                self.unify_substs(&tr1.substs, &tr2.substs, depth + 1)
-            }
-            (GenericPredicate::Projection(proj1), GenericPredicate::Projection(proj2))
-                if proj1.projection_ty.associated_ty == proj2.projection_ty.associated_ty =>
-            {
-                self.unify_substs(
-                    &proj1.projection_ty.parameters,
-                    &proj2.projection_ty.parameters,
-                    depth + 1,
-                ) && self.unify_inner(&proj1.ty, &proj2.ty, depth + 1)
-            }
-            _ => false,
-        }
-    }
-
     /// If `ty` is a type variable with known type, returns that type;
     /// otherwise, return ty.
     pub fn resolve_ty_shallow<'b>(&mut self, ty: &'b Ty) -> Cow<'b, Ty> {
diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs
index 2677f3af2d..a4b8d66837 100644
--- a/crates/ra_hir_ty/src/lib.rs
+++ b/crates/ra_hir_ty/src/lib.rs
@@ -396,12 +396,12 @@ impl Substs {
     }
 
     /// Return Substs that replace each parameter by a bound variable.
-    pub(crate) fn bound_vars(generic_params: &Generics) -> Substs {
+    pub(crate) fn bound_vars(generic_params: &Generics, debruijn: DebruijnIndex) -> Substs {
         Substs(
             generic_params
                 .iter()
                 .enumerate()
-                .map(|(idx, _)| Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, idx)))
+                .map(|(idx, _)| Ty::Bound(BoundVar::new(debruijn, idx)))
                 .collect(),
         )
     }
diff --git a/crates/ra_hir_ty/src/lower.rs b/crates/ra_hir_ty/src/lower.rs
index cc1ac8e3e9..c2812e1783 100644
--- a/crates/ra_hir_ty/src/lower.rs
+++ b/crates/ra_hir_ty/src/lower.rs
@@ -39,6 +39,7 @@ use crate::{
 pub struct TyLoweringContext<'a> {
     pub db: &'a dyn HirDatabase,
     pub resolver: &'a Resolver,
+    in_binders: DebruijnIndex,
     /// Note: Conceptually, it's thinkable that we could be in a location where
     /// some type params should be represented as placeholders, and others
     /// should be converted to variables. I think in practice, this isn't
@@ -53,7 +54,27 @@ impl<'a> TyLoweringContext<'a> {
         let impl_trait_counter = std::cell::Cell::new(0);
         let impl_trait_mode = ImplTraitLoweringMode::Disallowed;
         let type_param_mode = TypeParamLoweringMode::Placeholder;
-        Self { db, resolver, impl_trait_mode, impl_trait_counter, type_param_mode }
+        let in_binders = DebruijnIndex::INNERMOST;
+        Self { db, resolver, in_binders, impl_trait_mode, impl_trait_counter, type_param_mode }
+    }
+
+    pub fn with_shifted_in<T>(
+        &self,
+        debruijn: DebruijnIndex,
+        f: impl FnOnce(&TyLoweringContext) -> T,
+    ) -> T {
+        let new_ctx = Self {
+            in_binders: self.in_binders.shifted_in_from(debruijn),
+            impl_trait_counter: std::cell::Cell::new(self.impl_trait_counter.get()),
+            ..*self
+        };
+        let result = f(&new_ctx);
+        self.impl_trait_counter.set(new_ctx.impl_trait_counter.get());
+        result
+    }
+
+    pub fn shifted_in(self, debruijn: DebruijnIndex) -> Self {
+        Self { in_binders: self.in_binders.shifted_in_from(debruijn), ..self }
     }
 
     pub fn with_impl_trait_mode(self, impl_trait_mode: ImplTraitLoweringMode) -> Self {
@@ -134,22 +155,26 @@ impl Ty {
             }
             TypeRef::DynTrait(bounds) => {
                 let self_ty = Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, 0));
-                let predicates = bounds
-                    .iter()
-                    .flat_map(|b| GenericPredicate::from_type_bound(ctx, b, self_ty.clone()))
-                    .collect();
+                let predicates = ctx.with_shifted_in(DebruijnIndex::ONE, |ctx| {
+                    bounds
+                        .iter()
+                        .flat_map(|b| GenericPredicate::from_type_bound(ctx, b, self_ty.clone()))
+                        .collect()
+                });
                 Ty::Dyn(predicates)
             }
             TypeRef::ImplTrait(bounds) => {
                 match ctx.impl_trait_mode {
                     ImplTraitLoweringMode::Opaque => {
                         let self_ty = Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, 0));
-                        let predicates = bounds
-                            .iter()
-                            .flat_map(|b| {
-                                GenericPredicate::from_type_bound(ctx, b, self_ty.clone())
-                            })
-                            .collect();
+                        let predicates = ctx.with_shifted_in(DebruijnIndex::ONE, |ctx| {
+                            bounds
+                                .iter()
+                                .flat_map(|b| {
+                                    GenericPredicate::from_type_bound(ctx, b, self_ty.clone())
+                                })
+                                .collect()
+                        });
                         Ty::Opaque(predicates)
                     }
                     ImplTraitLoweringMode::Param => {
@@ -180,7 +205,7 @@ impl Ty {
                                 (0, 0, 0, 0)
                             };
                         Ty::Bound(BoundVar::new(
-                            DebruijnIndex::INNERMOST,
+                            ctx.in_binders,
                             idx as usize + parent_params + self_params + list_params,
                         ))
                     }
@@ -293,7 +318,7 @@ impl Ty {
                     TypeParamLoweringMode::Placeholder => Ty::Placeholder(param_id),
                     TypeParamLoweringMode::Variable => {
                         let idx = generics.param_idx(param_id).expect("matching generics");
-                        Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, idx))
+                        Ty::Bound(BoundVar::new(ctx.in_binders, idx))
                     }
                 }
             }
@@ -303,7 +328,9 @@ impl Ty {
                     TypeParamLoweringMode::Placeholder => {
                         Substs::type_params_for_generics(&generics)
                     }
-                    TypeParamLoweringMode::Variable => Substs::bound_vars(&generics),
+                    TypeParamLoweringMode::Variable => {
+                        Substs::bound_vars(&generics, ctx.in_binders)
+                    }
                 };
                 ctx.db.impl_self_ty(impl_id).subst(&substs)
             }
@@ -313,7 +340,9 @@ impl Ty {
                     TypeParamLoweringMode::Placeholder => {
                         Substs::type_params_for_generics(&generics)
                     }
-                    TypeParamLoweringMode::Variable => Substs::bound_vars(&generics),
+                    TypeParamLoweringMode::Variable => {
+                        Substs::bound_vars(&generics, ctx.in_binders)
+                    }
                 };
                 ctx.db.ty(adt.into()).subst(&substs)
             }
@@ -797,7 +826,7 @@ fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig {
 /// function body.
 fn type_for_fn(db: &dyn HirDatabase, def: FunctionId) -> Binders<Ty> {
     let generics = generics(db.upcast(), def.into());
-    let substs = Substs::bound_vars(&generics);
+    let substs = Substs::bound_vars(&generics, DebruijnIndex::INNERMOST);
     Binders::new(substs.len(), Ty::apply(TypeCtor::FnDef(def.into()), substs))
 }
 
@@ -851,7 +880,7 @@ fn type_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> Binders<T
         return type_for_adt(db, def.into());
     }
     let generics = generics(db.upcast(), def.into());
-    let substs = Substs::bound_vars(&generics);
+    let substs = Substs::bound_vars(&generics, DebruijnIndex::INNERMOST);
     Binders::new(substs.len(), Ty::apply(TypeCtor::FnDef(def.into()), substs))
 }
 
@@ -876,13 +905,13 @@ fn type_for_enum_variant_constructor(db: &dyn HirDatabase, def: EnumVariantId) -
         return type_for_adt(db, def.parent.into());
     }
     let generics = generics(db.upcast(), def.parent.into());
-    let substs = Substs::bound_vars(&generics);
+    let substs = Substs::bound_vars(&generics, DebruijnIndex::INNERMOST);
     Binders::new(substs.len(), Ty::apply(TypeCtor::FnDef(def.into()), substs))
 }
 
 fn type_for_adt(db: &dyn HirDatabase, adt: AdtId) -> Binders<Ty> {
     let generics = generics(db.upcast(), adt.into());
-    let substs = Substs::bound_vars(&generics);
+    let substs = Substs::bound_vars(&generics, DebruijnIndex::INNERMOST);
     Binders::new(substs.len(), Ty::apply(TypeCtor::Adt(adt), substs))
 }
 
@@ -892,7 +921,7 @@ fn type_for_type_alias(db: &dyn HirDatabase, t: TypeAliasId) -> Binders<Ty> {
     let ctx =
         TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable);
     let type_ref = &db.type_alias_data(t).type_ref;
-    let substs = Substs::bound_vars(&generics);
+    let substs = Substs::bound_vars(&generics, DebruijnIndex::INNERMOST);
     let inner = Ty::from_hir(&ctx, type_ref.as_ref().unwrap_or(&TypeRef::Error));
     Binders::new(substs.len(), inner)
 }
diff --git a/crates/ra_hir_ty/src/traits/chalk.rs b/crates/ra_hir_ty/src/traits/chalk.rs
index 60d70d18e8..e00a82db2a 100644
--- a/crates/ra_hir_ty/src/traits/chalk.rs
+++ b/crates/ra_hir_ty/src/traits/chalk.rs
@@ -17,7 +17,7 @@ use ra_db::{
 use super::{builtin, AssocTyValue, Canonical, ChalkContext, Impl, Obligation};
 use crate::{
     db::HirDatabase, display::HirDisplay, method_resolution::TyFingerprint, utils::generics,
-    ApplicationTy, GenericPredicate, ProjectionTy, Substs, TraitRef, Ty, TypeCtor,
+    ApplicationTy, DebruijnIndex, GenericPredicate, ProjectionTy, Substs, TraitRef, Ty, TypeCtor,
 };
 
 pub(super) mod tls;
@@ -815,7 +815,7 @@ pub(crate) fn associated_ty_data_query(
     // Lower bounds -- we could/should maybe move this to a separate query in `lower`
     let type_alias_data = db.type_alias_data(type_alias);
     let generic_params = generics(db.upcast(), type_alias.into());
-    let bound_vars = Substs::bound_vars(&generic_params);
+    let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST);
     let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db.upcast());
     let ctx = crate::TyLoweringContext::new(db, &resolver)
         .with_type_param_mode(crate::lower::TypeParamLoweringMode::Variable);
@@ -849,7 +849,7 @@ pub(crate) fn trait_datum_query(
     let trait_data = db.trait_data(trait_);
     debug!("trait {:?} = {:?}", trait_id, trait_data.name);
     let generic_params = generics(db.upcast(), trait_.into());
-    let bound_vars = Substs::bound_vars(&generic_params);
+    let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST);
     let flags = chalk_rust_ir::TraitFlags {
         auto: trait_data.auto,
         upstream: trait_.lookup(db.upcast()).container.module(db.upcast()).krate != krate,
@@ -888,7 +888,7 @@ pub(crate) fn struct_datum_query(
         .as_generic_def()
         .map(|generic_def| {
             let generic_params = generics(db.upcast(), generic_def);
-            let bound_vars = Substs::bound_vars(&generic_params);
+            let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST);
             convert_where_clauses(db, generic_def, &bound_vars)
         })
         .unwrap_or_else(Vec::new);
@@ -934,7 +934,7 @@ fn impl_def_datum(
     let impl_data = db.impl_data(impl_id);
 
     let generic_params = generics(db.upcast(), impl_id.into());
-    let bound_vars = Substs::bound_vars(&generic_params);
+    let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST);
     let trait_ = trait_ref.trait_;
     let impl_type = if impl_id.lookup(db.upcast()).container.module(db.upcast()).krate == krate {
         chalk_rust_ir::ImplType::Local
diff --git a/crates/ra_ide/src/completion/complete_trait_impl.rs b/crates/ra_ide/src/completion/complete_trait_impl.rs
index 2ec0e7ce9d..fab02945c5 100644
--- a/crates/ra_ide/src/completion/complete_trait_impl.rs
+++ b/crates/ra_ide/src/completion/complete_trait_impl.rs
@@ -142,11 +142,11 @@ fn add_function_impl(
         CompletionItemKind::Function
     };
 
-    let snippet = format!("{} {{\n    $0\n}}", display);
+    let snippet = format!("{} {{}}", display);
 
     let range = TextRange::from_to(fn_def_node.text_range().start(), ctx.source_range().end());
 
-    builder.snippet_edit(TextEdit::replace(range, snippet)).kind(completion_kind).add_to(acc);
+    builder.text_edit(TextEdit::replace(range, snippet)).kind(completion_kind).add_to(acc);
 }
 
 fn add_type_alias_impl(
@@ -217,9 +217,8 @@ fn make_const_compl_syntax(const_: &ast::ConstDef) -> String {
 
 #[cfg(test)]
 mod tests {
-    use insta::assert_debug_snapshot;
-
     use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
+    use insta::assert_debug_snapshot;
 
     fn complete(code: &str) -> Vec<CompletionItem> {
         do_completion(code, CompletionKind::Magic)
@@ -256,7 +255,7 @@ mod tests {
                 label: "fn test()",
                 source_range: [209; 210),
                 delete: [209; 210),
-                insert: "fn test() {\n    $0\n}",
+                insert: "fn test() {}",
                 kind: Function,
                 lookup: "test",
             },
@@ -314,7 +313,7 @@ mod tests {
                 label: "fn test()",
                 source_range: [139; 140),
                 delete: [139; 140),
-                insert: "fn test() {\n    $0\n}",
+                insert: "fn test() {}",
                 kind: Function,
                 lookup: "test",
             },
@@ -343,7 +342,7 @@ mod tests {
                 label: "fn foo()",
                 source_range: [141; 142),
                 delete: [138; 142),
-                insert: "fn foo() {\n    $0\n}",
+                insert: "fn foo() {}",
                 kind: Function,
                 lookup: "foo",
             },
@@ -375,7 +374,7 @@ mod tests {
                 label: "fn foo_bar()",
                 source_range: [200; 201),
                 delete: [197; 201),
-                insert: "fn foo_bar() {\n    $0\n}",
+                insert: "fn foo_bar() {}",
                 kind: Function,
                 lookup: "foo_bar",
             },
@@ -426,7 +425,7 @@ mod tests {
                 label: "fn foo()",
                 source_range: [144; 145),
                 delete: [141; 145),
-                insert: "fn foo<T>() {\n    $0\n}",
+                insert: "fn foo<T>() {}",
                 kind: Function,
                 lookup: "foo",
             },
@@ -455,7 +454,7 @@ mod tests {
                 label: "fn foo()",
                 source_range: [166; 167),
                 delete: [163; 167),
-                insert: "fn foo<T>()\nwhere T: Into<String> {\n    $0\n}",
+                insert: "fn foo<T>()\nwhere T: Into<String> {}",
                 kind: Function,
                 lookup: "foo",
             },
diff --git a/crates/ra_ide/src/completion/complete_unqualified_path.rs b/crates/ra_ide/src/completion/complete_unqualified_path.rs
index ad5fdcc4ea..2d8e0776ca 100644
--- a/crates/ra_ide/src/completion/complete_unqualified_path.rs
+++ b/crates/ra_ide/src/completion/complete_unqualified_path.rs
@@ -1,10 +1,6 @@
 //! Completion of names from the current scope, e.g. locals and imported items.
 
-use hir::ScopeDef;
-use test_utils::tested_by;
-
 use crate::completion::{CompletionContext, Completions};
-use ra_syntax::AstNode;
 
 pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
     if !ctx.is_trivial_path {
@@ -18,23 +14,12 @@ pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
         return;
     }
 
-    ctx.scope().process_all_names(&mut |name, res| {
-        if ctx.use_item_syntax.is_some() {
-            if let (ScopeDef::Unknown, Some(name_ref)) = (&res, &ctx.name_ref_syntax) {
-                if name_ref.syntax().text() == name.to_string().as_str() {
-                    tested_by!(self_fulfilling_completion);
-                    return;
-                }
-            }
-        }
-        acc.add_resolution(ctx, name.to_string(), &res)
-    });
+    ctx.scope().process_all_names(&mut |name, res| acc.add_resolution(ctx, name.to_string(), &res));
 }
 
 #[cfg(test)]
 mod tests {
     use insta::assert_debug_snapshot;
-    use test_utils::covers;
 
     use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
 
@@ -42,29 +27,6 @@ mod tests {
         do_completion(ra_fixture, CompletionKind::Reference)
     }
 
-    #[test]
-    fn self_fulfilling_completion() {
-        covers!(self_fulfilling_completion);
-        assert_debug_snapshot!(
-            do_reference_completion(
-                r#"
-                use foo<|>
-                use std::collections;
-                "#,
-            ),
-            @r###"
-            [
-                CompletionItem {
-                    label: "collections",
-                    source_range: [21; 24),
-                    delete: [21; 24),
-                    insert: "collections",
-                },
-            ]
-            "###
-        );
-    }
-
     #[test]
     fn bind_pat_and_path_ignore_at() {
         assert_debug_snapshot!(
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs
index e7e2017098..901ad104c1 100644
--- a/crates/ra_ide/src/diagnostics.rs
+++ b/crates/ra_ide/src/diagnostics.rs
@@ -1,8 +1,4 @@
-//! Collects diagnostics & fixits  for a single file.
-//!
-//! The tricky bit here is that diagnostics are produced by hir in terms of
-//! macro-expanded files, but we need to present them to the users in terms of
-//! original files. So we need to map the ranges.
+//! FIXME: write short doc here
 
 use std::cell::RefCell;
 
@@ -50,7 +46,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
     let mut sink = DiagnosticSink::new(|d| {
         res.borrow_mut().push(Diagnostic {
             message: d.message(),
-            range: sema.diagnostics_range(d).range,
+            range: d.highlight_range(),
             severity: Severity::Error,
             fix: None,
         })
@@ -66,7 +62,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
         let create_file = FileSystemEdit::CreateFile { source_root, path };
         let fix = SourceChange::file_system_edit("create module", create_file);
         res.borrow_mut().push(Diagnostic {
-            range: sema.diagnostics_range(d).range,
+            range: d.highlight_range(),
             message: d.message(),
             severity: Severity::Error,
             fix: Some(fix),
@@ -99,7 +95,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
         };
 
         res.borrow_mut().push(Diagnostic {
-            range: sema.diagnostics_range(d).range,
+            range: d.highlight_range(),
             message: d.message(),
             severity: Severity::Error,
             fix,
@@ -107,7 +103,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
     })
     .on::<hir::diagnostics::MissingMatchArms, _>(|d| {
         res.borrow_mut().push(Diagnostic {
-            range: sema.diagnostics_range(d).range,
+            range: d.highlight_range(),
             message: d.message(),
             severity: Severity::Error,
             fix: None,
@@ -119,7 +115,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
         let edit = TextEdit::replace(node.syntax().text_range(), replacement);
         let fix = SourceChange::source_file_edit_from("wrap with ok", file_id, edit);
         res.borrow_mut().push(Diagnostic {
-            range: sema.diagnostics_range(d).range,
+            range: d.highlight_range(),
             message: d.message(),
             severity: Severity::Error,
             fix: Some(fix),
@@ -625,62 +621,6 @@ mod tests {
         "###);
     }
 
-    #[test]
-    fn range_mapping_out_of_macros() {
-        let (analysis, file_id) = single_file(
-            r"
-            fn some() {}
-            fn items() {}
-            fn here() {}
-
-            macro_rules! id {
-                ($($tt:tt)*) => { $($tt)*};
-            }
-
-            fn main() {
-                let _x = id![Foo { a: 42 }];
-            }
-
-            pub struct Foo {
-                pub a: i32,
-                pub b: i32,
-            }
-        ",
-        );
-        let diagnostics = analysis.diagnostics(file_id).unwrap();
-        assert_debug_snapshot!(diagnostics, @r###"
-        [
-            Diagnostic {
-                message: "Missing structure fields:\n- b",
-                range: [224; 233),
-                fix: Some(
-                    SourceChange {
-                        label: "fill struct fields",
-                        source_file_edits: [
-                            SourceFileEdit {
-                                file_id: FileId(
-                                    1,
-                                ),
-                                edit: TextEdit {
-                                    atoms: [
-                                        AtomTextEdit {
-                                            delete: [3; 9),
-                                            insert: "{a:42, b: ()}",
-                                        },
-                                    ],
-                                },
-                            },
-                        ],
-                        file_system_edits: [],
-                        cursor_position: None,
-                    },
-                ),
-                severity: Error,
-            },
-        ]
-        "###);
-    }
-
     #[test]
     fn test_check_unnecessary_braces_in_use_statement() {
         check_not_applicable(
diff --git a/crates/ra_ide/src/marks.rs b/crates/ra_ide/src/marks.rs
index eee44e8869..5e1f135c56 100644
--- a/crates/ra_ide/src/marks.rs
+++ b/crates/ra_ide/src/marks.rs
@@ -8,5 +8,4 @@ test_utils::marks!(
     test_resolve_parent_module_on_module_decl
     search_filters_by_range
     dont_insert_macro_call_parens_unncessary
-    self_fulfilling_completion
 );
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index 7b15b82bdb..83d161f45a 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -174,8 +174,7 @@ pub(crate) fn highlight(
     }
 
     assert_eq!(res.len(), 1, "after DFS traversal, the stack should only contain a single element");
-    let mut res = res.pop().unwrap();
-    res.sort_by_key(|range| range.range.start());
+    let res = res.pop().unwrap();
     // Check that ranges are sorted and disjoint
     assert!(res
         .iter()
diff --git a/crates/ra_proc_macro/src/lib.rs b/crates/ra_proc_macro/src/lib.rs
index b200fd1263..63da9f1b45 100644
--- a/crates/ra_proc_macro/src/lib.rs
+++ b/crates/ra_proc_macro/src/lib.rs
@@ -12,7 +12,6 @@ pub mod msg;
 use process::{ProcMacroProcessSrv, ProcMacroProcessThread};
 use ra_tt::{SmolStr, Subtree};
 use std::{
-    ffi::OsStr,
     path::{Path, PathBuf},
     sync::Arc,
 };
@@ -57,15 +56,8 @@ pub struct ProcMacroClient {
 }
 
 impl ProcMacroClient {
-    pub fn extern_process<I, S>(
-        process_path: &Path,
-        args: I,
-    ) -> Result<ProcMacroClient, std::io::Error>
-    where
-        I: IntoIterator<Item = S>,
-        S: AsRef<OsStr>,
-    {
-        let (thread, process) = ProcMacroProcessSrv::run(process_path, args)?;
+    pub fn extern_process(process_path: &Path) -> Result<ProcMacroClient, std::io::Error> {
+        let (thread, process) = ProcMacroProcessSrv::run(process_path)?;
         Ok(ProcMacroClient {
             kind: ProcMacroClientKind::Process { process: Arc::new(process), thread },
         })
diff --git a/crates/ra_proc_macro/src/process.rs b/crates/ra_proc_macro/src/process.rs
index f851570bca..e8c85be38a 100644
--- a/crates/ra_proc_macro/src/process.rs
+++ b/crates/ra_proc_macro/src/process.rs
@@ -9,7 +9,6 @@ use crate::rpc::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTas
 use io::{BufRead, BufReader};
 use std::{
     convert::{TryFrom, TryInto},
-    ffi::OsStr,
     io::{self, Write},
     path::{Path, PathBuf},
     process::{Child, Command, Stdio},
@@ -45,13 +44,8 @@ impl Drop for Process {
 }
 
 impl Process {
-    fn run<I, S>(process_path: &Path, args: I) -> Result<Process, io::Error>
-    where
-        I: IntoIterator<Item = S>,
-        S: AsRef<OsStr>,
-    {
+    fn run(process_path: &Path) -> Result<Process, io::Error> {
         let child = Command::new(process_path.clone())
-            .args(args)
             .stdin(Stdio::piped())
             .stdout(Stdio::piped())
             .stderr(Stdio::null())
@@ -80,15 +74,10 @@ impl Process {
 }
 
 impl ProcMacroProcessSrv {
-    pub fn run<I, S>(
+    pub fn run(
         process_path: &Path,
-        args: I,
-    ) -> Result<(ProcMacroProcessThread, ProcMacroProcessSrv), io::Error>
-    where
-        I: IntoIterator<Item = S>,
-        S: AsRef<OsStr>,
-    {
-        let process = Process::run(process_path, args)?;
+    ) -> Result<(ProcMacroProcessThread, ProcMacroProcessSrv), io::Error> {
+        let process = Process::run(process_path)?;
 
         let (task_tx, task_rx) = bounded(0);
         let handle = jod_thread::spawn(move || {
diff --git a/crates/ra_proc_macro_srv/Cargo.toml b/crates/ra_proc_macro_srv/Cargo.toml
index ac2d156dc2..1e0f503392 100644
--- a/crates/ra_proc_macro_srv/Cargo.toml
+++ b/crates/ra_proc_macro_srv/Cargo.toml
@@ -14,7 +14,6 @@ ra_mbe = { path = "../ra_mbe" }
 ra_proc_macro = { path = "../ra_proc_macro" }
 goblin = "0.2.1"
 libloading = "0.6.0"
-memmap = "0.7"
 test_utils = { path = "../test_utils" }
 
 [dev-dependencies]
diff --git a/crates/ra_proc_macro_srv/src/dylib.rs b/crates/ra_proc_macro_srv/src/dylib.rs
index 16bd7466e3..ec63d587bb 100644
--- a/crates/ra_proc_macro_srv/src/dylib.rs
+++ b/crates/ra_proc_macro_srv/src/dylib.rs
@@ -1,12 +1,10 @@
 //! Handles dynamic library loading for proc macro
 
 use crate::{proc_macro::bridge, rustc_server::TokenStream};
-use std::fs::File;
 use std::path::Path;
 
 use goblin::{mach::Mach, Object};
 use libloading::Library;
-use memmap::Mmap;
 use ra_proc_macro::ProcMacroKind;
 
 use std::io::Error as IoError;
@@ -18,54 +16,55 @@ fn invalid_data_err(e: impl Into<Box<dyn std::error::Error + Send + Sync>>) -> I
     IoError::new(IoErrorKind::InvalidData, e)
 }
 
-fn is_derive_registrar_symbol(symbol: &str) -> bool {
-    symbol.contains(NEW_REGISTRAR_SYMBOL)
-}
-
-fn find_registrar_symbol(file: &Path) -> Result<Option<String>, IoError> {
-    let file = File::open(file)?;
-    let buffer = unsafe { Mmap::map(&file)? };
+fn get_symbols_from_lib(file: &Path) -> Result<Vec<String>, IoError> {
+    let buffer = std::fs::read(file)?;
     let object = Object::parse(&buffer).map_err(invalid_data_err)?;
 
     match object {
         Object::Elf(elf) => {
             let symbols = elf.dynstrtab.to_vec().map_err(invalid_data_err)?;
-            let name =
-                symbols.iter().find(|s| is_derive_registrar_symbol(s)).map(|s| s.to_string());
-            Ok(name)
+            let names = symbols.iter().map(|s| s.to_string()).collect();
+            Ok(names)
         }
         Object::PE(pe) => {
-            let name = pe
-                .exports
-                .iter()
-                .flat_map(|s| s.name)
-                .find(|s| is_derive_registrar_symbol(s))
-                .map(|s| s.to_string());
-            Ok(name)
-        }
-        Object::Mach(Mach::Binary(binary)) => {
-            let exports = binary.exports().map_err(invalid_data_err)?;
-            let name = exports
-                .iter()
-                .map(|s| {
-                    // In macos doc:
-                    // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/dlsym.3.html
-                    // Unlike other dyld API's, the symbol name passed to dlsym() must NOT be
-                    // prepended with an underscore.
-                    if s.name.starts_with("_") {
-                        &s.name[1..]
-                    } else {
-                        &s.name
-                    }
-                })
-                .find(|s| is_derive_registrar_symbol(s))
-                .map(|s| s.to_string());
-            Ok(name)
+            let symbol_names =
+                pe.exports.iter().flat_map(|s| s.name).map(|n| n.to_string()).collect();
+            Ok(symbol_names)
         }
-        _ => Ok(None),
+        Object::Mach(mach) => match mach {
+            Mach::Binary(binary) => {
+                let exports = binary.exports().map_err(invalid_data_err)?;
+                let names = exports
+                    .into_iter()
+                    .map(|s| {
+                        // In macos doc:
+                        // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/dlsym.3.html
+                        // Unlike other dyld API's, the symbol name passed to dlsym() must NOT be
+                        // prepended with an underscore.
+                        if s.name.starts_with("_") {
+                            s.name[1..].to_string()
+                        } else {
+                            s.name
+                        }
+                    })
+                    .collect();
+                Ok(names)
+            }
+            Mach::Fat(_) => Ok(vec![]),
+        },
+        Object::Archive(_) | Object::Unknown(_) => Ok(vec![]),
     }
 }
 
+fn is_derive_registrar_symbol(symbol: &str) -> bool {
+    symbol.contains(NEW_REGISTRAR_SYMBOL)
+}
+
+fn find_registrar_symbol(file: &Path) -> Result<Option<String>, IoError> {
+    let symbols = get_symbols_from_lib(file)?;
+    Ok(symbols.into_iter().find(|s| is_derive_registrar_symbol(s)))
+}
+
 /// Loads dynamic library in platform dependent manner.
 ///
 /// For unix, you have to use RTLD_DEEPBIND flag to escape problems described
diff --git a/crates/ra_proc_macro_srv/src/lib.rs b/crates/ra_proc_macro_srv/src/lib.rs
index c62b0ed893..59716cbb3b 100644
--- a/crates/ra_proc_macro_srv/src/lib.rs
+++ b/crates/ra_proc_macro_srv/src/lib.rs
@@ -22,7 +22,7 @@ mod dylib;
 use proc_macro::bridge::client::TokenStream;
 use ra_proc_macro::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask};
 
-pub(crate) fn expand_task(task: &ExpansionTask) -> Result<ExpansionResult, String> {
+pub fn expand_task(task: &ExpansionTask) -> Result<ExpansionResult, String> {
     let expander = dylib::Expander::new(&task.lib)
         .expect(&format!("Cannot expand with provided libraries: ${:?}", &task.lib));
 
@@ -39,7 +39,7 @@ pub(crate) fn expand_task(task: &ExpansionTask) -> Result<ExpansionResult, Strin
     }
 }
 
-pub(crate) fn list_macros(task: &ListMacrosTask) -> Result<ListMacrosResult, String> {
+pub fn list_macros(task: &ListMacrosTask) -> Result<ListMacrosResult, String> {
     let expander = dylib::Expander::new(&task.lib)
         .expect(&format!("Cannot expand with provided libraries: ${:?}", &task.lib));
 
@@ -53,7 +53,5 @@ pub(crate) fn list_macros(task: &ListMacrosTask) -> Result<ListMacrosResult, Str
     }
 }
 
-pub mod cli;
-
 #[cfg(test)]
 mod tests;
diff --git a/crates/ra_proc_macro_srv/src/cli.rs b/crates/ra_proc_macro_srv/src/main.rs
similarity index 96%
rename from crates/ra_proc_macro_srv/src/cli.rs
rename to crates/ra_proc_macro_srv/src/main.rs
index c771f2b389..70743c1f40 100644
--- a/crates/ra_proc_macro_srv/src/cli.rs
+++ b/crates/ra_proc_macro_srv/src/main.rs
@@ -1,7 +1,7 @@
 //! Driver for proc macro server
 
-use crate::{expand_task, list_macros};
 use ra_proc_macro::msg::{self, Message};
+use ra_proc_macro_srv::{expand_task, list_macros};
 
 use std::io;
 
@@ -24,8 +24,7 @@ fn write_response(res: Result<msg::Response, String>) -> Result<(), io::Error> {
     let mut stdout = stdout.lock();
     msg.write(&mut stdout)
 }
-
-pub fn run() {
+fn main() {
     loop {
         let req = match read_request() {
             Err(err) => {
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs
index 03f2629dae..0ab64a1e05 100644
--- a/crates/ra_project_model/src/lib.rs
+++ b/crates/ra_project_model/src/lib.rs
@@ -5,8 +5,9 @@ mod json_project;
 mod sysroot;
 
 use std::{
+    error::Error,
     fs::{read_dir, File, ReadDir},
-    io::{self, BufReader},
+    io::BufReader,
     path::{Path, PathBuf},
     process::Command,
 };
@@ -24,6 +25,25 @@ pub use crate::{
 };
 pub use ra_proc_macro::ProcMacroClient;
 
+#[derive(Clone, PartialEq, Eq, Hash, Debug)]
+pub struct CargoTomlNotFoundError {
+    pub searched_at: PathBuf,
+    pub reason: String,
+}
+
+impl std::fmt::Display for CargoTomlNotFoundError {
+    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(
+            fmt,
+            "can't find Cargo.toml at {}, due to {}",
+            self.searched_at.display(),
+            self.reason
+        )
+    }
+}
+
+impl Error for CargoTomlNotFoundError {}
+
 #[derive(Debug, Clone)]
 pub enum ProjectWorkspace {
     /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`.
@@ -57,119 +77,31 @@ impl PackageRoot {
     }
 }
 
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub enum ProjectRoot {
-    ProjectJson(PathBuf),
-    CargoToml(PathBuf),
-}
-
-impl ProjectRoot {
-    pub fn from_manifest_file(path: PathBuf) -> Result<ProjectRoot> {
-        if path.ends_with("rust-project.json") {
-            return Ok(ProjectRoot::ProjectJson(path));
-        }
-        if path.ends_with("Cargo.toml") {
-            return Ok(ProjectRoot::CargoToml(path));
-        }
-        bail!("project root must point to Cargo.toml or rust-project.json: {}", path.display())
-    }
-
-    pub fn discover_single(path: &Path) -> Result<ProjectRoot> {
-        let mut candidates = ProjectRoot::discover(path)?;
-        let res = match candidates.pop() {
-            None => bail!("no projects"),
-            Some(it) => it,
-        };
-
-        if !candidates.is_empty() {
-            bail!("more than one project")
-        }
-        Ok(res)
-    }
-
-    pub fn discover(path: &Path) -> io::Result<Vec<ProjectRoot>> {
-        if let Some(project_json) = find_rust_project_json(path) {
-            return Ok(vec![ProjectRoot::ProjectJson(project_json)]);
-        }
-        return find_cargo_toml(path)
-            .map(|paths| paths.into_iter().map(ProjectRoot::CargoToml).collect());
-
-        fn find_rust_project_json(path: &Path) -> Option<PathBuf> {
-            if path.ends_with("rust-project.json") {
-                return Some(path.to_path_buf());
-            }
-
-            let mut curr = Some(path);
-            while let Some(path) = curr {
-                let candidate = path.join("rust-project.json");
-                if candidate.exists() {
-                    return Some(candidate);
-                }
-                curr = path.parent();
-            }
-
-            None
-        }
-
-        fn find_cargo_toml(path: &Path) -> io::Result<Vec<PathBuf>> {
-            if path.ends_with("Cargo.toml") {
-                return Ok(vec![path.to_path_buf()]);
-            }
-
-            if let Some(p) = find_cargo_toml_in_parent_dir(path) {
-                return Ok(vec![p]);
-            }
-
-            let entities = read_dir(path)?;
-            Ok(find_cargo_toml_in_child_dir(entities))
-        }
-
-        fn find_cargo_toml_in_parent_dir(path: &Path) -> Option<PathBuf> {
-            let mut curr = Some(path);
-            while let Some(path) = curr {
-                let candidate = path.join("Cargo.toml");
-                if candidate.exists() {
-                    return Some(candidate);
-                }
-                curr = path.parent();
-            }
-
-            None
-        }
-
-        fn find_cargo_toml_in_child_dir(entities: ReadDir) -> Vec<PathBuf> {
-            // Only one level down to avoid cycles the easy way and stop a runaway scan with large projects
-            let mut valid_canditates = vec![];
-            for entity in entities.filter_map(Result::ok) {
-                let candidate = entity.path().join("Cargo.toml");
-                if candidate.exists() {
-                    valid_canditates.push(candidate)
-                }
-            }
-            valid_canditates
-        }
+impl ProjectWorkspace {
+    pub fn discover(path: &Path, cargo_features: &CargoConfig) -> Result<ProjectWorkspace> {
+        ProjectWorkspace::discover_with_sysroot(path, true, cargo_features)
     }
-}
 
-impl ProjectWorkspace {
-    pub fn load(
-        root: ProjectRoot,
-        cargo_features: &CargoConfig,
+    pub fn discover_with_sysroot(
+        path: &Path,
         with_sysroot: bool,
+        cargo_features: &CargoConfig,
     ) -> Result<ProjectWorkspace> {
-        let res = match root {
-            ProjectRoot::ProjectJson(project_json) => {
-                let file = File::open(&project_json).with_context(|| {
-                    format!("Failed to open json file {}", project_json.display())
-                })?;
+        match find_rust_project_json(path) {
+            Some(json_path) => {
+                let file = File::open(&json_path)
+                    .with_context(|| format!("Failed to open json file {}", json_path.display()))?;
                 let reader = BufReader::new(file);
-                ProjectWorkspace::Json {
+                Ok(ProjectWorkspace::Json {
                     project: from_reader(reader).with_context(|| {
-                        format!("Failed to deserialize json file {}", project_json.display())
+                        format!("Failed to deserialize json file {}", json_path.display())
                     })?,
-                }
+                })
             }
-            ProjectRoot::CargoToml(cargo_toml) => {
+            None => {
+                let cargo_toml = find_cargo_toml(path).with_context(|| {
+                    format!("Failed to find Cargo.toml for path {}", path.display())
+                })?;
                 let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_features)
                     .with_context(|| {
                         format!(
@@ -187,11 +119,9 @@ impl ProjectWorkspace {
                 } else {
                     Sysroot::default()
                 };
-                ProjectWorkspace::Cargo { cargo, sysroot }
+                Ok(ProjectWorkspace::Cargo { cargo, sysroot })
             }
-        };
-
-        Ok(res)
+        }
     }
 
     /// Returns the roots for the current `ProjectWorkspace`
@@ -539,6 +469,87 @@ impl ProjectWorkspace {
     }
 }
 
+fn find_rust_project_json(path: &Path) -> Option<PathBuf> {
+    if path.ends_with("rust-project.json") {
+        return Some(path.to_path_buf());
+    }
+
+    let mut curr = Some(path);
+    while let Some(path) = curr {
+        let candidate = path.join("rust-project.json");
+        if candidate.exists() {
+            return Some(candidate);
+        }
+        curr = path.parent();
+    }
+
+    None
+}
+
+fn find_cargo_toml_in_parent_dir(path: &Path) -> Option<PathBuf> {
+    let mut curr = Some(path);
+    while let Some(path) = curr {
+        let candidate = path.join("Cargo.toml");
+        if candidate.exists() {
+            return Some(candidate);
+        }
+        curr = path.parent();
+    }
+
+    None
+}
+
+fn find_cargo_toml_in_child_dir(entities: ReadDir) -> Vec<PathBuf> {
+    // Only one level down to avoid cycles the easy way and stop a runaway scan with large projects
+    let mut valid_canditates = vec![];
+    for entity in entities.filter_map(Result::ok) {
+        let candidate = entity.path().join("Cargo.toml");
+        if candidate.exists() {
+            valid_canditates.push(candidate)
+        }
+    }
+    valid_canditates
+}
+
+fn find_cargo_toml(path: &Path) -> Result<PathBuf> {
+    if path.ends_with("Cargo.toml") {
+        return Ok(path.to_path_buf());
+    }
+
+    if let Some(p) = find_cargo_toml_in_parent_dir(path) {
+        return Ok(p);
+    }
+
+    let entities = match read_dir(path) {
+        Ok(entities) => entities,
+        Err(e) => {
+            return Err(CargoTomlNotFoundError {
+                searched_at: path.to_path_buf(),
+                reason: format!("file system error: {}", e),
+            }
+            .into());
+        }
+    };
+
+    let mut valid_canditates = find_cargo_toml_in_child_dir(entities);
+    match valid_canditates.len() {
+        1 => Ok(valid_canditates.remove(0)),
+        0 => Err(CargoTomlNotFoundError {
+            searched_at: path.to_path_buf(),
+            reason: "no Cargo.toml file found".to_string(),
+        }
+        .into()),
+        _ => Err(CargoTomlNotFoundError {
+            searched_at: path.to_path_buf(),
+            reason: format!(
+                "multiple equally valid Cargo.toml files found: {:?}",
+                valid_canditates
+            ),
+        }
+        .into()),
+    }
+}
+
 pub fn get_rustc_cfg_options() -> CfgOptions {
     let mut cfg_options = CfgOptions::default();
 
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index cee0248b62..f5f7734325 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -46,7 +46,7 @@ ra_db = { path = "../ra_db" }
 hir = { path = "../ra_hir", package = "ra_hir" }
 hir_def = { path = "../ra_hir_def", package = "ra_hir_def" }
 hir_ty = { path = "../ra_hir_ty", package = "ra_hir_ty" }
-ra_proc_macro_srv = { path = "../ra_proc_macro_srv" }
+
 
 [target.'cfg(windows)'.dependencies]
 winapi = "0.3.8"
diff --git a/crates/rust-analyzer/src/bin/args.rs b/crates/rust-analyzer/src/bin/args.rs
index 5e19253a65..f5981588ab 100644
--- a/crates/rust-analyzer/src/bin/args.rs
+++ b/crates/rust-analyzer/src/bin/args.rs
@@ -29,23 +29,19 @@ pub(crate) enum Command {
         with_deps: bool,
         path: PathBuf,
         load_output_dirs: bool,
-        with_proc_macro: bool,
     },
     Bench {
         path: PathBuf,
         what: BenchWhat,
         load_output_dirs: bool,
-        with_proc_macro: bool,
     },
     Diagnostics {
         path: PathBuf,
         load_output_dirs: bool,
-        with_proc_macro: bool,
         /// Include files which are not modules. In rust-analyzer
         /// this would include the parser test files.
         all: bool,
     },
-    ProcMacro,
     RunServer,
     Version,
 }
@@ -152,7 +148,6 @@ FLAGS:
     -h, --help              Prints help information
         --memory-usage
         --load-output-dirs  Load OUT_DIR values by running `cargo check` before analysis
-        --with-proc-macro    Use ra-proc-macro-srv for proc-macro expanding
     -v, --verbose
     -q, --quiet
 
@@ -170,7 +165,6 @@ ARGS:
                 let only: Option<String> = matches.opt_value_from_str(["-o", "--only"])?;
                 let with_deps: bool = matches.contains("--with-deps");
                 let load_output_dirs = matches.contains("--load-output-dirs");
-                let with_proc_macro = matches.contains("--with-proc-macro");
                 let path = {
                     let mut trailing = matches.free()?;
                     if trailing.len() != 1 {
@@ -179,15 +173,7 @@ ARGS:
                     trailing.pop().unwrap().into()
                 };
 
-                Command::Stats {
-                    randomize,
-                    memory_usage,
-                    only,
-                    with_deps,
-                    path,
-                    load_output_dirs,
-                    with_proc_macro,
-                }
+                Command::Stats { randomize, memory_usage, only, with_deps, path, load_output_dirs }
             }
             "analysis-bench" => {
                 if matches.contains(["-h", "--help"]) {
@@ -201,7 +187,6 @@ USAGE:
 FLAGS:
     -h, --help          Prints help information
     --load-output-dirs  Load OUT_DIR values by running `cargo check` before analysis
-    --with-proc-macro    Use ra-proc-macro-srv for proc-macro expanding
     -v, --verbose
 
 OPTIONS:
@@ -229,8 +214,7 @@ ARGS:
                     ),
                 };
                 let load_output_dirs = matches.contains("--load-output-dirs");
-                let with_proc_macro = matches.contains("--with-proc-macro");
-                Command::Bench { path, what, load_output_dirs, with_proc_macro }
+                Command::Bench { path, what, load_output_dirs }
             }
             "diagnostics" => {
                 if matches.contains(["-h", "--help"]) {
@@ -253,7 +237,6 @@ ARGS:
                 }
 
                 let load_output_dirs = matches.contains("--load-output-dirs");
-                let with_proc_macro = matches.contains("--with-proc-macro");
                 let all = matches.contains("--all");
                 let path = {
                     let mut trailing = matches.free()?;
@@ -263,9 +246,8 @@ ARGS:
                     trailing.pop().unwrap().into()
                 };
 
-                Command::Diagnostics { path, load_output_dirs, with_proc_macro, all }
+                Command::Diagnostics { path, load_output_dirs, all }
             }
-            "proc-macro" => Command::ProcMacro,
             _ => {
                 eprintln!(
                     "\
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs
index 28b67cfe2a..7cfc44f01f 100644
--- a/crates/rust-analyzer/src/bin/main.rs
+++ b/crates/rust-analyzer/src/bin/main.rs
@@ -25,7 +25,6 @@ fn main() -> Result<()> {
             with_deps,
             path,
             load_output_dirs,
-            with_proc_macro,
         } => cli::analysis_stats(
             args.verbosity,
             memory_usage,
@@ -34,24 +33,16 @@ fn main() -> Result<()> {
             with_deps,
             randomize,
             load_output_dirs,
-            with_proc_macro,
         )?,
 
-        args::Command::Bench { path, what, load_output_dirs, with_proc_macro } => {
-            cli::analysis_bench(
-                args.verbosity,
-                path.as_ref(),
-                what,
-                load_output_dirs,
-                with_proc_macro,
-            )?
+        args::Command::Bench { path, what, load_output_dirs } => {
+            cli::analysis_bench(args.verbosity, path.as_ref(), what, load_output_dirs)?
         }
 
-        args::Command::Diagnostics { path, load_output_dirs, with_proc_macro, all } => {
-            cli::diagnostics(path.as_ref(), load_output_dirs, with_proc_macro, all)?
+        args::Command::Diagnostics { path, load_output_dirs, all } => {
+            cli::diagnostics(path.as_ref(), load_output_dirs, all)?
         }
 
-        args::Command::ProcMacro => run_proc_macro_sv()?,
         args::Command::RunServer => run_server()?,
         args::Command::Version => println!("rust-analyzer {}", env!("REV")),
     }
@@ -65,11 +56,6 @@ fn setup_logging() -> Result<()> {
     Ok(())
 }
 
-fn run_proc_macro_sv() -> Result<()> {
-    ra_proc_macro_srv::cli::run();
-    Ok(())
-}
-
 fn run_server() -> Result<()> {
     log::info!("lifecycle: server started");
 
diff --git a/crates/rust-analyzer/src/cli/analysis_bench.rs b/crates/rust-analyzer/src/cli/analysis_bench.rs
index 6147ae2074..7667873d5f 100644
--- a/crates/rust-analyzer/src/cli/analysis_bench.rs
+++ b/crates/rust-analyzer/src/cli/analysis_bench.rs
@@ -47,13 +47,12 @@ pub fn analysis_bench(
     path: &Path,
     what: BenchWhat,
     load_output_dirs: bool,
-    with_proc_macro: bool,
 ) -> Result<()> {
     ra_prof::init();
 
     let start = Instant::now();
     eprint!("loading: ");
-    let (mut host, roots) = load_cargo(path, load_output_dirs, with_proc_macro)?;
+    let (mut host, roots) = load_cargo(path, load_output_dirs)?;
     let db = host.raw_database();
     eprintln!("{:?}\n", start.elapsed());
 
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs
index d442cbd63e..e9ee0b888f 100644
--- a/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -25,10 +25,9 @@ pub fn analysis_stats(
     with_deps: bool,
     randomize: bool,
     load_output_dirs: bool,
-    with_proc_macro: bool,
 ) -> Result<()> {
     let db_load_time = Instant::now();
-    let (mut host, roots) = load_cargo(path, load_output_dirs, with_proc_macro)?;
+    let (mut host, roots) = load_cargo(path, load_output_dirs)?;
     let db = host.raw_database();
     println!("Database loaded, {} roots, {:?}", roots.len(), db_load_time.elapsed());
     let analysis_time = Instant::now();
diff --git a/crates/rust-analyzer/src/cli/diagnostics.rs b/crates/rust-analyzer/src/cli/diagnostics.rs
index 60daefa3ef..92664b415b 100644
--- a/crates/rust-analyzer/src/cli/diagnostics.rs
+++ b/crates/rust-analyzer/src/cli/diagnostics.rs
@@ -9,13 +9,8 @@ use std::{collections::HashSet, path::Path};
 use crate::cli::{load_cargo::load_cargo, Result};
 use hir::Semantics;
 
-pub fn diagnostics(
-    path: &Path,
-    load_output_dirs: bool,
-    with_proc_macro: bool,
-    all: bool,
-) -> Result<()> {
-    let (host, roots) = load_cargo(path, load_output_dirs, with_proc_macro)?;
+pub fn diagnostics(path: &Path, load_output_dirs: bool, all: bool) -> Result<()> {
+    let (host, roots) = load_cargo(path, load_output_dirs)?;
     let db = host.raw_database();
     let analysis = host.analysis();
     let semantics = Semantics::new(db);
diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs
index 762f776fea..43062ea105 100644
--- a/crates/rust-analyzer/src/cli/load_cargo.rs
+++ b/crates/rust-analyzer/src/cli/load_cargo.rs
@@ -8,7 +8,7 @@ use crossbeam_channel::{unbounded, Receiver};
 use ra_db::{ExternSourceId, FileId, SourceRootId};
 use ra_ide::{AnalysisChange, AnalysisHost};
 use ra_project_model::{
-    get_rustc_cfg_options, CargoConfig, PackageRoot, ProcMacroClient, ProjectRoot, ProjectWorkspace,
+    get_rustc_cfg_options, CargoConfig, PackageRoot, ProcMacroClient, ProjectWorkspace,
 };
 use ra_vfs::{RootEntry, Vfs, VfsChange, VfsTask, Watch};
 use rustc_hash::{FxHashMap, FxHashSet};
@@ -25,14 +25,11 @@ fn vfs_root_to_id(r: ra_vfs::VfsRoot) -> SourceRootId {
 pub(crate) fn load_cargo(
     root: &Path,
     load_out_dirs_from_check: bool,
-    with_proc_macro: bool,
 ) -> Result<(AnalysisHost, FxHashMap<SourceRootId, PackageRoot>)> {
     let root = std::env::current_dir()?.join(root);
-    let root = ProjectRoot::discover_single(&root)?;
-    let ws = ProjectWorkspace::load(
-        root,
+    let ws = ProjectWorkspace::discover(
+        root.as_ref(),
         &CargoConfig { load_out_dirs_from_check, ..Default::default() },
-        true,
     )?;
 
     let mut extern_dirs = FxHashSet::default();
@@ -72,12 +69,7 @@ pub(crate) fn load_cargo(
         })
         .collect::<FxHashMap<_, _>>();
 
-    let proc_macro_client = if !with_proc_macro {
-        ProcMacroClient::dummy()
-    } else {
-        let path = std::env::current_exe()?;
-        ProcMacroClient::extern_process(&path, &["proc-macro"]).unwrap()
-    };
+    let proc_macro_client = ProcMacroClient::dummy();
     let host = load(&source_roots, ws, &mut vfs, receiver, extern_dirs, &proc_macro_client);
     Ok((host, source_roots))
 }
@@ -183,7 +175,7 @@ mod tests {
     #[test]
     fn test_loading_rust_analyzer() {
         let path = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap().parent().unwrap();
-        let (host, _roots) = load_cargo(path, false, false).unwrap();
+        let (host, _roots) = load_cargo(path, false).unwrap();
         let n_crates = Crate::all(host.raw_database()).len();
         // RA has quite a few crates, but the exact count doesn't matter
         assert!(n_crates > 20);
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 3597a14e36..4734df16ac 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -20,7 +20,7 @@ pub struct Config {
     pub with_sysroot: bool,
     pub publish_diagnostics: bool,
     pub lru_capacity: Option<usize>,
-    pub proc_macro_srv: Option<(String, Vec<String>)>,
+    pub proc_macro_srv: Option<String>,
     pub files: FilesConfig,
     pub notifications: NotificationsConfig,
 
@@ -131,16 +131,6 @@ impl Config {
         set(value, "/cargo/allFeatures", &mut self.cargo.all_features);
         set(value, "/cargo/features", &mut self.cargo.features);
         set(value, "/cargo/loadOutDirsFromCheck", &mut self.cargo.load_out_dirs_from_check);
-
-        match get::<bool>(value, "/procMacro/enabled") {
-            Some(true) => {
-                if let Ok(path) = std::env::current_exe() {
-                    self.proc_macro_srv = Some((path.to_string_lossy().to_string(), vec!["proc-macro".to_string()]));
-                }
-            }
-            _ => self.proc_macro_srv = None,
-        }
-
         match get::<Vec<String>>(value, "/rustfmt/overrideCommand") {
             Some(mut args) if !args.is_empty() => {
                 let command = args.remove(0);
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index fc4c77f8aa..8d14291962 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -15,7 +15,6 @@ use std::{
 };
 
 use crossbeam_channel::{never, select, unbounded, RecvError, Sender};
-use itertools::Itertools;
 use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response};
 use lsp_types::{
     NumberOrString, WorkDoneProgress, WorkDoneProgressBegin, WorkDoneProgressCreateParams,
@@ -89,46 +88,37 @@ pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection)
 
     let mut loop_state = LoopState::default();
     let mut world_state = {
+        // FIXME: support dynamic workspace loading.
         let workspaces = {
-            // FIXME: support dynamic workspace loading.
-            let mut visited = FxHashSet::default();
-            let project_roots = ws_roots
-                .iter()
-                .filter_map(|it| ra_project_model::ProjectRoot::discover(it).ok())
-                .flatten()
-                .filter(|it| visited.insert(it.clone()))
-                .collect::<Vec<_>>();
-
-            if project_roots.is_empty() && config.notifications.cargo_toml_not_found {
-                show_message(
-                        req::MessageType::Error,
-                        format!(
-                            "rust-analyzer failed to discover workspace, no Cargo.toml found, dirs searched: {}",
-                            ws_roots.iter().format_with(", ", |it, f| f(&it.display()))
-                        ),
-                        &connection.sender,
-                    );
-            };
+            let mut loaded_workspaces = Vec::new();
+            for ws_root in &ws_roots {
+                let workspace = ra_project_model::ProjectWorkspace::discover_with_sysroot(
+                    ws_root.as_path(),
+                    config.with_sysroot,
+                    &config.cargo,
+                );
+                match workspace {
+                    Ok(workspace) => loaded_workspaces.push(workspace),
+                    Err(e) => {
+                        log::error!("loading workspace failed: {:?}", e);
+
+                        if let Some(ra_project_model::CargoTomlNotFoundError { .. }) =
+                            e.downcast_ref()
+                        {
+                            if !config.notifications.cargo_toml_not_found {
+                                continue;
+                            }
+                        }
 
-            project_roots
-                .into_iter()
-                .filter_map(|root| {
-                    ra_project_model::ProjectWorkspace::load(
-                        root,
-                        &config.cargo,
-                        config.with_sysroot,
-                    )
-                    .map_err(|err| {
-                        log::error!("failed to load workspace: {:#}", err);
                         show_message(
                             req::MessageType::Error,
-                            format!("rust-analyzer failed to load workspace: {:#}", err),
+                            format!("rust-analyzer failed to load workspace: {:?}", e),
                             &connection.sender,
                         );
-                    })
-                    .ok()
-                })
-                .collect::<Vec<_>>()
+                    }
+                }
+            }
+            loaded_workspaces
         };
 
         let globs = config
diff --git a/crates/rust-analyzer/src/world.rs b/crates/rust-analyzer/src/world.rs
index f2ad453faf..6c42e1d760 100644
--- a/crates/rust-analyzer/src/world.rs
+++ b/crates/rust-analyzer/src/world.rs
@@ -64,7 +64,6 @@ pub struct WorldState {
     pub latest_requests: Arc<RwLock<LatestRequests>>,
     pub flycheck: Option<Flycheck>,
     pub diagnostics: DiagnosticCollection,
-    pub proc_macro_client: ProcMacroClient,
 }
 
 /// An immutable snapshot of the world's state at a point in time.
@@ -148,9 +147,9 @@ impl WorldState {
 
         let proc_macro_client = match &config.proc_macro_srv {
             None => ProcMacroClient::dummy(),
-            Some((path, args)) => {
-                let path = std::path::Path::new(path);
-                match ProcMacroClient::extern_process(path, args) {
+            Some(srv) => {
+                let path = Path::new(&srv);
+                match ProcMacroClient::extern_process(path) {
                     Ok(it) => it,
                     Err(err) => {
                         log::error!(
@@ -193,7 +192,6 @@ impl WorldState {
             latest_requests: Default::default(),
             flycheck,
             diagnostics: Default::default(),
-            proc_macro_client,
         }
     }
 
diff --git a/editors/code/package.json b/editors/code/package.json
index 5ce59e54a9..5f73c8d838 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -388,11 +388,6 @@
                     "description": "Enable logging of VS Code extensions itself",
                     "type": "boolean",
                     "default": false
-                },
-                "rust-analyzer.procMacro.enabled": {
-                    "description": "Enable Proc macro support, cargo.loadOutDirsFromCheck must be enabled.",
-                    "type": "boolean",
-                    "default": false
                 }
             }
         },
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index 3b2eec8baa..35a05131c8 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -12,7 +12,6 @@ export class Config {
     private readonly requiresReloadOpts = [
         "serverPath",
         "cargo",
-        "procMacro",
         "files",
         "highlighting",
         "updates.channel",


Test patch:
diff --git a/crates/ra_hir_ty/src/tests/regression.rs b/crates/ra_hir_ty/src/tests/regression.rs
index 61284d6722..d69115a2f5 100644
--- a/crates/ra_hir_ty/src/tests/regression.rs
+++ b/crates/ra_hir_ty/src/tests/regression.rs
@@ -484,52 +484,3 @@ fn main() {
 
     assert_eq!("()", super::type_at_pos(&db, pos));
 }
-
-#[test]
-fn issue_3999_slice() {
-    assert_snapshot!(
-        infer(r#"
-fn foo(params: &[usize]) {
-    match params {
-        [ps @ .., _] => {}
-    }
-}
-"#),
-        @r###"
-    [8; 14) 'params': &[usize]
-    [26; 81) '{     ...   } }': ()
-    [32; 79) 'match ...     }': ()
-    [38; 44) 'params': &[usize]
-    [55; 67) '[ps @ .., _]': [usize]
-    [65; 66) '_': usize
-    [71; 73) '{}': ()
-    "###
-    );
-}
-
-#[test]
-fn issue_3999_struct() {
-    // rust-analyzer should not panic on seeing this malformed
-    // record pattern.
-    assert_snapshot!(
-        infer(r#"
-struct Bar {
-    a: bool,
-}
-fn foo(b: Bar) {
-    match b {
-        Bar { a: .. } => {},
-    }
-}
-"#),
-        @r###"
-    [36; 37) 'b': Bar
-    [44; 96) '{     ...   } }': ()
-    [50; 94) 'match ...     }': ()
-    [56; 57) 'b': Bar
-    [68; 81) 'Bar { a: .. }': Bar
-    [77; 79) '..': bool
-    [85; 87) '{}': ()
-    "###
-    );
-}
diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs
index f6e3e07cde..36f53b264d 100644
--- a/crates/ra_hir_ty/src/tests/traits.rs
+++ b/crates/ra_hir_ty/src/tests/traits.rs
@@ -1210,6 +1210,42 @@ fn test(x: dyn Trait<u64>, y: &dyn Trait<u64>) {
     );
 }
 
+#[test]
+fn dyn_trait_in_impl() {
+    assert_snapshot!(
+        infer(r#"
+trait Trait<T, U> {
+    fn foo(&self) -> (T, U);
+}
+struct S<T, U> {}
+impl<T, U> S<T, U> {
+    fn bar(&self) -> &dyn Trait<T, U> { loop {} }
+}
+trait Trait2<T, U> {
+    fn baz(&self) -> (T, U);
+}
+impl<T, U> Trait2<T, U> for dyn Trait<T, U> { }
+
+fn test(s: S<u32, i32>) {
+    s.bar().baz();
+}
+"#),
+        @r###"
+    [33; 37) 'self': &Self
+    [103; 107) 'self': &S<T, U>
+    [129; 140) '{ loop {} }': &dyn Trait<T, U>
+    [131; 138) 'loop {}': !
+    [136; 138) '{}': ()
+    [176; 180) 'self': &Self
+    [252; 253) 's': S<u32, i32>
+    [268; 290) '{     ...z(); }': ()
+    [274; 275) 's': S<u32, i32>
+    [274; 281) 's.bar()': &dyn Trait<u32, i32>
+    [274; 287) 's.bar().baz()': (u32, i32)
+    "###
+    );
+}
+
 #[test]
 fn dyn_trait_bare() {
     assert_snapshot!(
@@ -2204,201 +2240,3 @@ fn test(x: Box<dyn Trait>) {
     );
     assert_eq!(t, "()");
 }
-
-#[test]
-fn string_to_owned() {
-    let t = type_at(
-        r#"
-//- /main.rs
-struct String {}
-pub trait ToOwned {
-    type Owned;
-    fn to_owned(&self) -> Self::Owned;
-}
-impl ToOwned for str {
-    type Owned = String;
-}
-fn test() {
-    "foo".to_owned()<|>;
-}
-"#,
-    );
-    assert_eq!(t, "String");
-}
-
-#[test]
-fn iterator_chain() {
-    assert_snapshot!(
-        infer(r#"
-//- /main.rs
-#[lang = "fn_once"]
-trait FnOnce<Args> {
-    type Output;
-}
-#[lang = "fn_mut"]
-trait FnMut<Args>: FnOnce<Args> { }
-
-enum Option<T> { Some(T), None }
-use Option::*;
-
-pub trait Iterator {
-    type Item;
-
-    fn filter_map<B, F>(self, f: F) -> FilterMap<Self, F>
-    where
-        F: FnMut(Self::Item) -> Option<B>,
-    { loop {} }
-
-    fn for_each<F>(self, f: F)
-    where
-        F: FnMut(Self::Item),
-    { loop {} }
-}
-
-pub trait IntoIterator {
-    type Item;
-    type IntoIter: Iterator<Item = Self::Item>;
-    fn into_iter(self) -> Self::IntoIter;
-}
-
-pub struct FilterMap<I, F> { }
-impl<B, I: Iterator, F> Iterator for FilterMap<I, F>
-where
-    F: FnMut(I::Item) -> Option<B>,
-{
-    type Item = B;
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<I: Iterator> IntoIterator for I {
-    type Item = I::Item;
-    type IntoIter = I;
-
-    fn into_iter(self) -> I {
-        self
-    }
-}
-
-struct Vec<T> {}
-impl<T> Vec<T> {
-    fn new() -> Self { loop {} }
-}
-
-impl<T> IntoIterator for Vec<T> {
-    type Item = T;
-    type IntoIter = IntoIter<T>;
-}
-
-pub struct IntoIter<T> { }
-impl<T> Iterator for IntoIter<T> {
-    type Item = T;
-}
-
-fn main() {
-    Vec::<i32>::new().into_iter()
-      .filter_map(|x| if x > 0 { Some(x as u32) } else { None })
-      .for_each(|y| { y; });
-}
-"#),
-        @r###"
-    [240; 244) 'self': Self
-    [246; 247) 'f': F
-    [331; 342) '{ loop {} }': FilterMap<Self, F>
-    [333; 340) 'loop {}': !
-    [338; 340) '{}': ()
-    [363; 367) 'self': Self
-    [369; 370) 'f': F
-    [419; 430) '{ loop {} }': ()
-    [421; 428) 'loop {}': !
-    [426; 428) '{}': ()
-    [539; 543) 'self': Self
-    [868; 872) 'self': I
-    [879; 899) '{     ...     }': I
-    [889; 893) 'self': I
-    [958; 969) '{ loop {} }': Vec<T>
-    [960; 967) 'loop {}': !
-    [965; 967) '{}': ()
-    [1156; 1287) '{     ... }); }': ()
-    [1162; 1177) 'Vec::<i32>::new': fn new<i32>() -> Vec<i32>
-    [1162; 1179) 'Vec::<...:new()': Vec<i32>
-    [1162; 1191) 'Vec::<...iter()': IntoIter<i32>
-    [1162; 1256) 'Vec::<...one })': FilterMap<IntoIter<i32>, |i32| -> Option<u32>>
-    [1162; 1284) 'Vec::<... y; })': ()
-    [1210; 1255) '|x| if...None }': |i32| -> Option<u32>
-    [1211; 1212) 'x': i32
-    [1214; 1255) 'if x >...None }': Option<u32>
-    [1217; 1218) 'x': i32
-    [1217; 1222) 'x > 0': bool
-    [1221; 1222) '0': i32
-    [1223; 1241) '{ Some...u32) }': Option<u32>
-    [1225; 1229) 'Some': Some<u32>(u32) -> Option<u32>
-    [1225; 1239) 'Some(x as u32)': Option<u32>
-    [1230; 1231) 'x': i32
-    [1230; 1238) 'x as u32': u32
-    [1247; 1255) '{ None }': Option<u32>
-    [1249; 1253) 'None': Option<u32>
-    [1273; 1283) '|y| { y; }': |u32| -> ()
-    [1274; 1275) 'y': u32
-    [1277; 1283) '{ y; }': ()
-    [1279; 1280) 'y': u32
-    "###
-    );
-}
-
-#[test]
-fn nested_assoc() {
-    let t = type_at(
-        r#"
-//- /main.rs
-struct Bar;
-struct Foo;
-
-trait A {
-    type OutputA;
-}
-
-impl A for Bar {
-    type OutputA = Foo;
-}
-
-trait B {
-    type Output;
-    fn foo() -> Self::Output;
-}
-
-impl<T:A> B for T {
-    type Output = T::OutputA;
-    fn foo() -> Self::Output { loop {} }
-}
-
-fn main() {
-    Bar::foo()<|>;
-}
-"#,
-    );
-    assert_eq!(t, "Foo");
-}
-
-#[test]
-fn trait_object_no_coercion() {
-    assert_snapshot!(
-        infer_with_mismatches(r#"
-trait Foo {}
-
-fn foo(x: &dyn Foo) {}
-
-fn test(x: &dyn Foo) {
-    foo(x);
-}
-"#, true),
-        @r###"
-    [22; 23) 'x': &dyn Foo
-    [35; 37) '{}': ()
-    [47; 48) 'x': &dyn Foo
-    [60; 75) '{     foo(x); }': ()
-    [66; 69) 'foo': fn foo(&dyn Foo)
-    [66; 72) 'foo(x)': ()
-    [70; 71) 'x': &dyn Foo
-    "###
-    );
-}
diff --git a/crates/ra_ide/src/syntax_highlighting/tests.rs b/crates/ra_ide/src/syntax_highlighting/tests.rs
index 73611e23a5..110887c2ac 100644
--- a/crates/ra_ide/src/syntax_highlighting/tests.rs
+++ b/crates/ra_ide/src/syntax_highlighting/tests.rs
@@ -156,15 +156,3 @@ fn main() {
     fs::write(dst_file, &actual_html).unwrap();
     assert_eq_text!(expected_html, actual_html);
 }
-
-#[test]
-fn ranges_sorted() {
-    let (analysis, file_id) = single_file(
-        r#"
-#[foo(bar = "bar")]
-macro_rules! test {}
-}"#
-        .trim(),
-    );
-    let _ = analysis.highlight(file_id).unwrap();
-}
diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs
index 1dd2676b6e..6388133112 100644
--- a/crates/rust-analyzer/tests/heavy_tests/main.rs
+++ b/crates/rust-analyzer/tests/heavy_tests/main.rs
@@ -9,7 +9,7 @@ use lsp_types::{
 };
 use rust_analyzer::req::{
     CodeActionParams, CodeActionRequest, Completion, CompletionParams, DidOpenTextDocument,
-    Formatting, GotoDefinition, HoverRequest, OnEnter, Runnables, RunnablesParams,
+    Formatting, GotoDefinition, OnEnter, Runnables, RunnablesParams,
 };
 use serde_json::json;
 use tempfile::TempDir;
@@ -625,92 +625,3 @@ fn main() { message(); }
     ));
     assert!(format!("{}", res).contains("hello.rs"));
 }
-
-#[test]
-fn resolve_proc_macro() {
-    if skip_slow_tests() {
-        return;
-    }
-    let server = Project::with_fixture(
-        r###"
-//- foo/Cargo.toml
-[package]
-name = "foo"
-version = "0.0.0"
-edition = "2018"
-[dependencies]
-bar = {path = "../bar"}
-
-//- foo/src/main.rs
-use bar::Bar;
-trait Bar {
-  fn bar();
-}
-#[derive(Bar)]
-struct Foo {}
-fn main() {
-  Foo::bar();
-}
-
-//- bar/Cargo.toml
-[package]
-name = "bar"
-version = "0.0.0"
-edition = "2018"
-
-[lib]
-proc-macro = true
-
-//- bar/src/lib.rs
-extern crate proc_macro;
-use proc_macro::{Delimiter, Group, Ident, Span, TokenStream, TokenTree};
-macro_rules! t {
-    ($n:literal) => {
-        TokenTree::from(Ident::new($n, Span::call_site()))
-    };
-    ({}) => {
-        TokenTree::from(Group::new(Delimiter::Brace, TokenStream::new()))
-    };
-    (()) => {
-        TokenTree::from(Group::new(Delimiter::Parenthesis, TokenStream::new()))
-    };
-}
-#[proc_macro_derive(Bar)]
-pub fn foo(_input: TokenStream) -> TokenStream {
-    // We hard code the output here for preventing to use any deps
-    let mut res = TokenStream::new();
-
-    // impl Bar for Foo { fn bar() {} }
-    let mut tokens = vec![t!("impl"), t!("Bar"), t!("for"), t!("Foo")];
-    let mut fn_stream = TokenStream::new();
-    fn_stream.extend(vec![t!("fn"), t!("bar"), t!(()), t!({})]);
-    tokens.push(Group::new(Delimiter::Brace, fn_stream).into());
-    res.extend(tokens);
-    res
-}
-
-"###,
-    )
-    .with_config(|config| {
-        // FIXME: Use env!("CARGO_BIN_EXE_ra-analyzer") instead after
-        // https://github.com/rust-lang/cargo/pull/7697 landed
-        let macro_srv_path = std::path::Path::new(std::env!("CARGO_MANIFEST_DIR"))
-            .join("../../target/debug/rust-analyzer")
-            .to_string_lossy()
-            .to_string();
-
-        config.cargo.load_out_dirs_from_check = true;
-        config.proc_macro_srv = Some((macro_srv_path, vec!["proc-macro".to_string()]));
-    })
-    .root("foo")
-    .root("bar")
-    .server();
-    server.wait_until_workspace_is_loaded();
-    let res = server.send_request::<HoverRequest>(TextDocumentPositionParams::new(
-        server.doc_id("foo/src/main.rs"),
-        Position::new(7, 9),
-    ));
-
-    let value = res.get("contents").unwrap().get("value").unwrap().to_string();
-    assert_eq!(value, r#""```rust\nfoo::Bar\nfn bar()\n```""#)
-}
diff --git a/xtask/tests/tidy-tests/main.rs b/xtask/tests/tidy-tests/main.rs
index ead642acc6..101ae19bd1 100644
--- a/xtask/tests/tidy-tests/main.rs
+++ b/xtask/tests/tidy-tests/main.rs
@@ -35,7 +35,7 @@ fn check_todo(path: &Path, text: &str) {
     }
     if text.contains("TODO") || text.contains("TOOD") || text.contains("todo!") {
         panic!(
-            "\nTODO markers or todo! macros should not be committed to the master branch,\n\
+            "\nTODO markers should not be committed to the master branch,\n\
              use FIXME instead\n\
              {}\n",
             path.display(),
@@ -47,9 +47,9 @@ fn check_trailing_ws(path: &Path, text: &str) {
     if is_exclude_dir(path, &["test_data"]) {
         return;
     }
-    for (line_number, line) in text.lines().enumerate() {
+    for line in text.lines() {
         if line.chars().last().map(char::is_whitespace) == Some(true) {
-            panic!("Trailing whitespace in {} at line {}", path.display(), line_number)
+            panic!("Trailing whitespace in {}", path.display())
         }
     }
 }