Repository: rust-analyzer/rust-analyzer
Pull request #4157 (2020-04-26 11:55:34)

Use x86_64-unknown-linux-gnu for releases
The reasoning here is that we don't call much C code, and `libstd` should already be compatible with older Linux distributions.

Tested by running `rust-analyzer analysis-stats` on Ubuntu 14.04 with a binary compiled on Arch Linux.

This should make for faster binaries and avoid some problems with `musl` `dlopen()` not working (thus breaking procedural macro support).

bors[bot]: :v: lnicola can now approve this pull request. To approve and merge a pull request, simply reply with `bors r+`. More detailed instructions are available [here](https://bors.tech/documentation/getting-started/#reviewing-pull-requests).

Patch:
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index 2c1192f072..3f52f31f80 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -39,7 +39,6 @@ jobs:
       with:
         toolchain: stable
         profile: minimal
-        target: x86_64-unknown-linux-musl
         override: true
 
     - name: Install Nodejs
diff --git a/Cargo.lock b/Cargo.lock
index 367ff3f828..20e6cd5403 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1139,7 +1139,6 @@ dependencies = [
  "jemalloc-ctl",
  "jemallocator",
  "once_cell",
- "ra_arena",
 ]
 
 [[package]]
diff --git a/crates/ra_arena/src/lib.rs b/crates/ra_arena/src/lib.rs
index 441fbb3cbe..ea98d54441 100644
--- a/crates/ra_arena/src/lib.rs
+++ b/crates/ra_arena/src/lib.rs
@@ -96,9 +96,6 @@ impl<T> Arena<T> {
     pub const fn new() -> Arena<T> {
         Arena { data: Vec::new() }
     }
-    pub fn clear(&mut self) {
-        self.data.clear();
-    }
 
     pub fn len(&self) -> usize {
         self.data.len()
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs
index fb788736d8..3f645a1ddf 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -1136,13 +1136,6 @@ impl Type {
         matches!(&self.ty.value, Ty::Apply(ApplicationTy { ctor: TypeCtor::Closure { .. }, .. }))
     }
 
-    pub fn is_fn(&self) -> bool {
-        matches!(&self.ty.value,
-            Ty::Apply(ApplicationTy { ctor: TypeCtor::FnDef(..), .. }) |
-            Ty::Apply(ApplicationTy { ctor: TypeCtor::FnPtr { .. }, .. })
-        )
-    }
-
     pub fn contains_unknown(&self) -> bool {
         return go(&self.ty.value);
 
diff --git a/crates/ra_hir_def/src/body.rs b/crates/ra_hir_def/src/body.rs
index 890cefcafb..3b169440ad 100644
--- a/crates/ra_hir_def/src/body.rs
+++ b/crates/ra_hir_def/src/body.rs
@@ -27,7 +27,7 @@ use crate::{
     AsMacroCall, DefWithBodyId, HasModule, Lookup, ModuleId,
 };
 
-/// A subset of Exander that only deals with cfg attributes. We only need it to
+/// A subser of Exander that only deals with cfg attributes. We only need it to
 /// avoid cyclic queries in crate def map during enum processing.
 pub(crate) struct CfgExpander {
     cfg_options: CfgOptions,
diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs
index 5716038543..0caedd8d85 100644
--- a/crates/ra_hir_def/src/body/lower.rs
+++ b/crates/ra_hir_def/src/body/lower.rs
@@ -141,10 +141,6 @@ impl ExprCollector<'_> {
 
     fn collect_expr(&mut self, expr: ast::Expr) -> ExprId {
         let syntax_ptr = AstPtr::new(&expr);
-        let attrs = self.expander.parse_attrs(&expr);
-        if !self.expander.is_cfg_enabled(&attrs) {
-            return self.missing_expr();
-        }
         match expr {
             ast::Expr::IfExpr(e) => {
                 let then_branch = self.collect_block_opt(e.then_branch());
diff --git a/crates/ra_hir_expand/src/builtin_derive.rs b/crates/ra_hir_expand/src/builtin_derive.rs
index e60f879a39..bb45b0f1da 100644
--- a/crates/ra_hir_expand/src/builtin_derive.rs
+++ b/crates/ra_hir_expand/src/builtin_derive.rs
@@ -9,7 +9,7 @@ use ra_syntax::{
 };
 
 use crate::db::AstDatabase;
-use crate::{name, quote, LazyMacroId, MacroCallId, MacroDefId, MacroDefKind};
+use crate::{name, quote, LazyMacroId, MacroDefId, MacroDefKind};
 
 macro_rules! register_builtin {
     ( $($trait:ident => $expand:ident),* ) => {
@@ -153,113 +153,76 @@ fn expand_simple_derive(
     Ok(expanded)
 }
 
-fn find_builtin_crate(db: &dyn AstDatabase, id: LazyMacroId) -> tt::TokenTree {
-    // FIXME: make hygiene works for builtin derive macro
-    // such that $crate can be used here.
-
-    let m: MacroCallId = id.into();
-    let file_id = m.as_file().original_file(db);
-    let cg = db.crate_graph();
-    let krates = db.relevant_crates(file_id);
-    let krate = match krates.get(0) {
-        Some(krate) => krate,
-        None => {
-            let tt = quote! { core };
-            return tt.token_trees[0].clone();
-        }
-    };
-
-    // XXX
-    //  All crates except core itself should have a dependency on core,
-    //  We detect `core` by seeing whether it doesn't have such a dependency.
-    let tt = if cg[*krate].dependencies.iter().any(|dep| dep.name == "core") {
-        quote! { core }
-    } else {
-        quote! { crate }
-    };
-
-    tt.token_trees[0].clone()
-}
-
 fn copy_expand(
-    db: &dyn AstDatabase,
-    id: LazyMacroId,
+    _db: &dyn AstDatabase,
+    _id: LazyMacroId,
     tt: &tt::Subtree,
 ) -> Result<tt::Subtree, mbe::ExpandError> {
-    let krate = find_builtin_crate(db, id);
-    expand_simple_derive(tt, quote! { #krate::marker::Copy })
+    expand_simple_derive(tt, quote! { std::marker::Copy })
 }
 
 fn clone_expand(
-    db: &dyn AstDatabase,
-    id: LazyMacroId,
+    _db: &dyn AstDatabase,
+    _id: LazyMacroId,
     tt: &tt::Subtree,
 ) -> Result<tt::Subtree, mbe::ExpandError> {
-    let krate = find_builtin_crate(db, id);
-    expand_simple_derive(tt, quote! { #krate::clone::Clone })
+    expand_simple_derive(tt, quote! { std::clone::Clone })
 }
 
 fn default_expand(
-    db: &dyn AstDatabase,
-    id: LazyMacroId,
+    _db: &dyn AstDatabase,
+    _id: LazyMacroId,
     tt: &tt::Subtree,
 ) -> Result<tt::Subtree, mbe::ExpandError> {
-    let krate = find_builtin_crate(db, id);
-    expand_simple_derive(tt, quote! { #krate::default::Default })
+    expand_simple_derive(tt, quote! { std::default::Default })
 }
 
 fn debug_expand(
-    db: &dyn AstDatabase,
-    id: LazyMacroId,
+    _db: &dyn AstDatabase,
+    _id: LazyMacroId,
     tt: &tt::Subtree,
 ) -> Result<tt::Subtree, mbe::ExpandError> {
-    let krate = find_builtin_crate(db, id);
-    expand_simple_derive(tt, quote! { #krate::fmt::Debug })
+    expand_simple_derive(tt, quote! { std::fmt::Debug })
 }
 
 fn hash_expand(
-    db: &dyn AstDatabase,
-    id: LazyMacroId,
+    _db: &dyn AstDatabase,
+    _id: LazyMacroId,
     tt: &tt::Subtree,
 ) -> Result<tt::Subtree, mbe::ExpandError> {
-    let krate = find_builtin_crate(db, id);
-    expand_simple_derive(tt, quote! { #krate::hash::Hash })
+    expand_simple_derive(tt, quote! { std::hash::Hash })
 }
 
 fn eq_expand(
-    db: &dyn AstDatabase,
-    id: LazyMacroId,
+    _db: &dyn AstDatabase,
+    _id: LazyMacroId,
     tt: &tt::Subtree,
 ) -> Result<tt::Subtree, mbe::ExpandError> {
-    let krate = find_builtin_crate(db, id);
-    expand_simple_derive(tt, quote! { #krate::cmp::Eq })
+    expand_simple_derive(tt, quote! { std::cmp::Eq })
 }
 
 fn partial_eq_expand(
-    db: &dyn AstDatabase,
-    id: LazyMacroId,
+    _db: &dyn AstDatabase,
+    _id: LazyMacroId,
     tt: &tt::Subtree,
 ) -> Result<tt::Subtree, mbe::ExpandError> {
-    let krate = find_builtin_crate(db, id);
-    expand_simple_derive(tt, quote! { #krate::cmp::PartialEq })
+    expand_simple_derive(tt, quote! { std::cmp::PartialEq })
 }
 
 fn ord_expand(
-    db: &dyn AstDatabase,
-    id: LazyMacroId,
+    _db: &dyn AstDatabase,
+    _id: LazyMacroId,
     tt: &tt::Subtree,
 ) -> Result<tt::Subtree, mbe::ExpandError> {
-    let krate = find_builtin_crate(db, id);
-    expand_simple_derive(tt, quote! { #krate::cmp::Ord })
+    expand_simple_derive(tt, quote! { std::cmp::Ord })
 }
 
 fn partial_ord_expand(
-    db: &dyn AstDatabase,
-    id: LazyMacroId,
+    _db: &dyn AstDatabase,
+    _id: LazyMacroId,
     tt: &tt::Subtree,
 ) -> Result<tt::Subtree, mbe::ExpandError> {
-    let krate = find_builtin_crate(db, id);
-    expand_simple_derive(tt, quote! { #krate::cmp::PartialOrd })
+    expand_simple_derive(tt, quote! { std::cmp::PartialOrd })
 }
 
 #[cfg(test)]
@@ -271,18 +234,8 @@ mod tests {
 
     fn expand_builtin_derive(s: &str, name: Name) -> String {
         let def = find_builtin_derive(&name).unwrap();
-        let fixture = format!(
-            r#"//- /main.rs crate:main deps:core
-<|>
-{}
-//- /lib.rs crate:core
-// empty
-"#,
-            s
-        );
 
-        let (db, file_pos) = TestDB::with_position(&fixture);
-        let file_id = file_pos.file_id;
+        let (db, file_id) = TestDB::with_single_file(&s);
         let parsed = db.parse(file_id);
         let items: Vec<_> =
             parsed.syntax_node().descendants().filter_map(ast::ModuleItem::cast).collect();
@@ -311,7 +264,7 @@ mod tests {
             known::Copy,
         );
 
-        assert_eq!(expanded, "impl< >core::marker::CopyforFoo< >{}");
+        assert_eq!(expanded, "impl< >std::marker::CopyforFoo< >{}");
     }
 
     #[test]
@@ -326,7 +279,7 @@ mod tests {
 
         assert_eq!(
             expanded,
-            "impl<T0:core::marker::Copy,T1:core::marker::Copy>core::marker::CopyforFoo<T0,T1>{}"
+            "impl<T0:std::marker::Copy,T1:std::marker::Copy>std::marker::CopyforFoo<T0,T1>{}"
         );
     }
 
@@ -344,7 +297,7 @@ mod tests {
 
         assert_eq!(
             expanded,
-            "impl<T0:core::marker::Copy,T1:core::marker::Copy>core::marker::CopyforFoo<T0,T1>{}"
+            "impl<T0:std::marker::Copy,T1:std::marker::Copy>std::marker::CopyforFoo<T0,T1>{}"
         );
     }
 
@@ -360,7 +313,7 @@ mod tests {
 
         assert_eq!(
             expanded,
-            "impl<T0:core::clone::Clone,T1:core::clone::Clone>core::clone::CloneforFoo<T0,T1>{}"
+            "impl<T0:std::clone::Clone,T1:std::clone::Clone>std::clone::CloneforFoo<T0,T1>{}"
         );
     }
 }
diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs
index bd4ef69a07..6a53be621d 100644
--- a/crates/ra_hir_ty/src/infer.rs
+++ b/crates/ra_hir_ty/src/infer.rs
@@ -667,7 +667,7 @@ impl Expectation {
 }
 
 mod diagnostics {
-    use hir_def::{expr::ExprId, FunctionId};
+    use hir_def::{expr::ExprId, src::HasSource, FunctionId, Lookup};
     use hir_expand::diagnostics::DiagnosticSink;
 
     use crate::{db::HirDatabase, diagnostics::NoSuchField};
@@ -686,9 +686,10 @@ mod diagnostics {
         ) {
             match self {
                 InferenceDiagnostic::NoSuchField { expr, field } => {
+                    let source = owner.lookup(db.upcast()).source(db.upcast());
                     let (_, source_map) = db.body_with_source_map(owner.into());
                     let field = source_map.field_syntax(*expr, *field);
-                    sink.push(NoSuchField { file: field.file_id, field: field.value })
+                    sink.push(NoSuchField { file: source.file_id, field: field.value })
                 }
             }
         }
diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs
index a8ef32ec59..279c06d65d 100644
--- a/crates/ra_hir_ty/src/lib.rs
+++ b/crates/ra_hir_ty/src/lib.rs
@@ -487,18 +487,6 @@ impl<T> Binders<T> {
     pub fn new(num_binders: usize, value: T) -> Self {
         Self { num_binders, value }
     }
-
-    pub fn as_ref(&self) -> Binders<&T> {
-        Binders { num_binders: self.num_binders, value: &self.value }
-    }
-
-    pub fn map<U>(self, f: impl FnOnce(T) -> U) -> Binders<U> {
-        Binders { num_binders: self.num_binders, value: f(self.value) }
-    }
-
-    pub fn filter_map<U>(self, f: impl FnOnce(T) -> Option<U>) -> Option<Binders<U>> {
-        Some(Binders { num_binders: self.num_binders, value: f(self.value)? })
-    }
 }
 
 impl<T: Clone> Binders<&T> {
diff --git a/crates/ra_hir_ty/src/lower.rs b/crates/ra_hir_ty/src/lower.rs
index a6f893037f..b572142966 100644
--- a/crates/ra_hir_ty/src/lower.rs
+++ b/crates/ra_hir_ty/src/lower.rs
@@ -28,11 +28,11 @@ use crate::{
     db::HirDatabase,
     primitive::{FloatTy, IntTy},
     utils::{
-        all_super_trait_refs, associated_type_by_name_including_super_traits, generics,
-        make_mut_slice, variant_data,
+        all_super_traits, associated_type_by_name_including_super_traits, generics, make_mut_slice,
+        variant_data,
     },
     Binders, BoundVar, DebruijnIndex, FnSig, GenericPredicate, PolyFnSig, ProjectionPredicate,
-    ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TypeCtor, TypeWalk,
+    ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TypeCtor,
 };
 
 #[derive(Debug)]
@@ -256,7 +256,7 @@ impl Ty {
         if remaining_segments.len() == 1 {
             // resolve unselected assoc types
             let segment = remaining_segments.first().unwrap();
-            (Ty::select_associated_type(ctx, res, segment), None)
+            (Ty::select_associated_type(ctx, ty, res, segment), None)
         } else if remaining_segments.len() > 1 {
             // FIXME report error (ambiguous associated type)
             (Ty::Unknown, None)
@@ -380,20 +380,21 @@ impl Ty {
 
     fn select_associated_type(
         ctx: &TyLoweringContext<'_>,
+        self_ty: Ty,
         res: Option<TypeNs>,
         segment: PathSegment<'_>,
     ) -> Ty {
         let traits_from_env: Vec<_> = match res {
             Some(TypeNs::SelfType(impl_id)) => match ctx.db.impl_trait(impl_id) {
                 None => return Ty::Unknown,
-                Some(trait_ref) => vec![trait_ref.value],
+                Some(trait_ref) => vec![trait_ref.value.trait_],
             },
             Some(TypeNs::GenericParam(param_id)) => {
                 let predicates = ctx.db.generic_predicates_for_param(param_id);
                 let mut traits_: Vec<_> = predicates
                     .iter()
                     .filter_map(|pred| match &pred.value {
-                        GenericPredicate::Implemented(tr) => Some(tr.clone()),
+                        GenericPredicate::Implemented(tr) => Some(tr.trait_),
                         _ => None,
                     })
                     .collect();
@@ -403,37 +404,20 @@ impl Ty {
                     if generics.params.types[param_id.local_id].provenance
                         == TypeParamProvenance::TraitSelf
                     {
-                        let trait_ref = TraitRef {
-                            trait_: trait_id,
-                            substs: Substs::bound_vars(&generics, DebruijnIndex::INNERMOST),
-                        };
-                        traits_.push(trait_ref);
+                        traits_.push(trait_id);
                     }
                 }
                 traits_
             }
             _ => return Ty::Unknown,
         };
-        let traits = traits_from_env.into_iter().flat_map(|t| all_super_trait_refs(ctx.db, t));
+        let traits = traits_from_env.into_iter().flat_map(|t| all_super_traits(ctx.db.upcast(), t));
         for t in traits {
-            if let Some(associated_ty) =
-                ctx.db.trait_data(t.trait_).associated_type_by_name(&segment.name)
+            if let Some(associated_ty) = ctx.db.trait_data(t).associated_type_by_name(&segment.name)
             {
-                let substs = match ctx.type_param_mode {
-                    TypeParamLoweringMode::Placeholder => {
-                        // if we're lowering to placeholders, we have to put
-                        // them in now
-                        let s = Substs::type_params(
-                            ctx.db,
-                            ctx.resolver
-                                .generic_def()
-                                .expect("there should be generics if there's a generic param"),
-                        );
-                        t.substs.subst_bound_vars(&s)
-                    }
-                    TypeParamLoweringMode::Variable => t.substs,
-                };
-                // FIXME handle (forbid) type parameters on the segment
+                let substs =
+                    Substs::build_for_def(ctx.db, t).push(self_ty).fill_with_unknown().build();
+                // FIXME handle type parameters on the segment
                 return Ty::Projection(ProjectionTy { associated_ty, parameters: substs });
             }
         }
diff --git a/crates/ra_hir_ty/src/utils.rs b/crates/ra_hir_ty/src/utils.rs
index f98350bf92..1e5022fa4c 100644
--- a/crates/ra_hir_ty/src/utils.rs
+++ b/crates/ra_hir_ty/src/utils.rs
@@ -14,8 +14,6 @@ use hir_def::{
 };
 use hir_expand::name::{name, Name};
 
-use crate::{db::HirDatabase, GenericPredicate, TraitRef};
-
 fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> {
     let resolver = trait_.resolver(db);
     // returning the iterator directly doesn't easily work because of
@@ -43,28 +41,6 @@ fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> {
         .collect()
 }
 
-fn direct_super_trait_refs(db: &dyn HirDatabase, trait_ref: &TraitRef) -> Vec<TraitRef> {
-    // returning the iterator directly doesn't easily work because of
-    // lifetime problems, but since there usually shouldn't be more than a
-    // few direct traits this should be fine (we could even use some kind of
-    // SmallVec if performance is a concern)
-    let generic_params = db.generic_params(trait_ref.trait_.into());
-    let trait_self = match generic_params.find_trait_self_param() {
-        Some(p) => TypeParamId { parent: trait_ref.trait_.into(), local_id: p },
-        None => return Vec::new(),
-    };
-    db.generic_predicates_for_param(trait_self)
-        .iter()
-        .filter_map(|pred| {
-            pred.as_ref().filter_map(|pred| match pred {
-                GenericPredicate::Implemented(tr) => Some(tr.clone()),
-                _ => None,
-            })
-        })
-        .map(|pred| pred.subst(&trait_ref.substs))
-        .collect()
-}
-
 /// Returns an iterator over the whole super trait hierarchy (including the
 /// trait itself).
 pub(super) fn all_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> {
@@ -86,30 +62,6 @@ pub(super) fn all_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<Tra
     result
 }
 
-/// Given a trait ref (`Self: Trait`), builds all the implied trait refs for
-/// super traits. The original trait ref will be included. So the difference to
-/// `all_super_traits` is that we keep track of type parameters; for example if
-/// we have `Self: Trait<u32, i32>` and `Trait<T, U>: OtherTrait<U>` we'll get
-/// `Self: OtherTrait<i32>`.
-pub(super) fn all_super_trait_refs(db: &dyn HirDatabase, trait_ref: TraitRef) -> Vec<TraitRef> {
-    // we need to take care a bit here to avoid infinite loops in case of cycles
-    // (i.e. if we have `trait A: B; trait B: A;`)
-    let mut result = vec![trait_ref];
-    let mut i = 0;
-    while i < result.len() {
-        let t = &result[i];
-        // yeah this is quadratic, but trait hierarchies should be flat
-        // enough that this doesn't matter
-        for tt in direct_super_trait_refs(db, t) {
-            if !result.iter().any(|tr| tr.trait_ == tt.trait_) {
-                result.push(tt);
-            }
-        }
-        i += 1;
-    }
-    result
-}
-
 /// Finds a path from a trait to one of its super traits. Returns an empty
 /// vector if there is no path.
 pub(super) fn find_super_trait_path(
diff --git a/crates/ra_ide/src/completion/complete_unqualified_path.rs b/crates/ra_ide/src/completion/complete_unqualified_path.rs
index f559f2b970..56cd086c68 100644
--- a/crates/ra_ide/src/completion/complete_unqualified_path.rs
+++ b/crates/ra_ide/src/completion/complete_unqualified_path.rs
@@ -4,7 +4,7 @@ use hir::ScopeDef;
 use test_utils::tested_by;
 
 use crate::completion::{CompletionContext, Completions};
-use hir::{Adt, ModuleDef, Type};
+use hir::{Adt, ModuleDef};
 use ra_syntax::AstNode;
 
 pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
@@ -15,9 +15,7 @@ pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
         return;
     }
 
-    if let Some(ty) = &ctx.expected_type {
-        complete_enum_variants(acc, ctx, ty);
-    }
+    complete_enum_variants(acc, ctx);
 
     if ctx.is_pat_binding_or_const {
         return;
@@ -36,24 +34,26 @@ pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
     });
 }
 
-fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &Type) {
-    if let Some(Adt::Enum(enum_data)) = ty.as_adt() {
-        let variants = enum_data.variants(ctx.db);
-
-        let module = if let Some(module) = ctx.scope().module() {
-            // Compute path from the completion site if available.
-            module
-        } else {
-            // Otherwise fall back to the enum's definition site.
-            enum_data.module(ctx.db)
-        };
-
-        for variant in variants {
-            if let Some(path) = module.find_use_path(ctx.db, ModuleDef::from(variant)) {
-                // Variants with trivial paths are already added by the existing completion logic,
-                // so we should avoid adding these twice
-                if path.segments.len() > 1 {
-                    acc.add_enum_variant(ctx, variant, Some(path.to_string()));
+fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext) {
+    if let Some(ty) = ctx.expected_type_of(&ctx.token.parent()) {
+        if let Some(Adt::Enum(enum_data)) = ty.as_adt() {
+            let variants = enum_data.variants(ctx.db);
+
+            let module = if let Some(module) = ctx.scope().module() {
+                // Compute path from the completion site if available.
+                module
+            } else {
+                // Otherwise fall back to the enum's definition site.
+                enum_data.module(ctx.db)
+            };
+
+            for variant in variants {
+                if let Some(path) = module.find_use_path(ctx.db, ModuleDef::from(variant)) {
+                    // Variants with trivial paths are already added by the existing completion logic,
+                    // so we should avoid adding these twice
+                    if path.segments.len() > 1 {
+                        acc.add_enum_variant(ctx, variant, Some(path.to_string()));
+                    }
                 }
             }
         }
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs
index 118fceb2e7..5f2797e418 100644
--- a/crates/ra_ide/src/completion/completion_context.rs
+++ b/crates/ra_ide/src/completion/completion_context.rs
@@ -5,7 +5,7 @@ use ra_db::SourceDatabase;
 use ra_ide_db::RootDatabase;
 use ra_syntax::{
     algo::{find_covering_element, find_node_at_offset},
-    ast, match_ast, AstNode,
+    ast, AstNode,
     SyntaxKind::*,
     SyntaxNode, SyntaxToken, TextRange, TextSize,
 };
@@ -26,7 +26,6 @@ pub(crate) struct CompletionContext<'a> {
     /// The token before the cursor, in the macro-expanded file.
     pub(super) token: SyntaxToken,
     pub(super) krate: Option<hir::Crate>,
-    pub(super) expected_type: Option<Type>,
     pub(super) name_ref_syntax: Option<ast::NameRef>,
     pub(super) function_syntax: Option<ast::FnDef>,
     pub(super) use_item_syntax: Option<ast::UseItem>,
@@ -94,7 +93,6 @@ impl<'a> CompletionContext<'a> {
             token,
             offset: position.offset,
             krate,
-            expected_type: None,
             name_ref_syntax: None,
             function_syntax: None,
             use_item_syntax: None,
@@ -177,30 +175,23 @@ impl<'a> CompletionContext<'a> {
         self.sema.scope_at_offset(&self.token.parent(), self.offset)
     }
 
+    pub(crate) fn expected_type_of(&self, node: &SyntaxNode) -> Option<Type> {
+        for ancestor in node.ancestors() {
+            if let Some(pat) = ast::Pat::cast(ancestor.clone()) {
+                return self.sema.type_of_pat(&pat);
+            } else if let Some(expr) = ast::Expr::cast(ancestor) {
+                return self.sema.type_of_expr(&expr);
+            }
+        }
+        None
+    }
+
     fn fill(
         &mut self,
         original_file: &SyntaxNode,
         file_with_fake_ident: SyntaxNode,
         offset: TextSize,
     ) {
-        // FIXME: this is wrong in at least two cases:
-        //  * when there's no token `foo(<|>)`
-        //  * when there is a token, but it happens to have type of it's own
-        self.expected_type = self
-            .token
-            .ancestors()
-            .find_map(|node| {
-                let ty = match_ast! {
-                    match node {
-                        ast::Pat(it) => self.sema.type_of_pat(&it),
-                        ast::Expr(it) => self.sema.type_of_expr(&it),
-                        _ => return None,
-                    }
-                };
-                Some(ty)
-            })
-            .flatten();
-
         // First, let's try to complete a reference to some declaration.
         if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&file_with_fake_ident, offset) {
             // Special case, `trait T { fn foo(i_am_a_name_ref) {} }`.
diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs
index 77d3543760..7633cd7fd6 100644
--- a/crates/ra_ide/src/completion/presentation.rs
+++ b/crates/ra_ide/src/completion/presentation.rs
@@ -349,14 +349,6 @@ impl Builder {
         if ctx.use_item_syntax.is_some() || ctx.is_call {
             return self;
         }
-
-        // Don't add parentheses if the expected type is some function reference.
-        if let Some(ty) = &ctx.expected_type {
-            if ty.is_fn() {
-                return self;
-            }
-        }
-
         let cap = match ctx.config.snippet_cap {
             Some(it) => it,
             None => return self,
@@ -756,54 +748,6 @@ mod tests {
         );
     }
 
-    #[test]
-    fn no_call_parens_if_fn_ptr_needed() {
-        assert_debug_snapshot!(
-            do_reference_completion(
-                r"
-                fn somefn(with: u8, a: u8, lot: u8, of: u8, args: u8) {}
-
-                struct ManualVtable {
-                    method: fn(u8, u8, u8, u8, u8),
-                }
-
-                fn main() -> ManualVtable {
-                    ManualVtable {
-                        method: some<|>
-                    }
-                }
-                "
-            ),
-            @r###"
-        [
-            CompletionItem {
-                label: "ManualVtable",
-                source_range: 295..299,
-                delete: 295..299,
-                insert: "ManualVtable",
-                kind: Struct,
-            },
-            CompletionItem {
-                label: "main",
-                source_range: 295..299,
-                delete: 295..299,
-                insert: "main",
-                kind: Function,
-                detail: "fn main() -> ManualVtable",
-            },
-            CompletionItem {
-                label: "somefn",
-                source_range: 295..299,
-                delete: 295..299,
-                insert: "somefn",
-                kind: Function,
-                detail: "fn somefn(with: u8, a: u8, lot: u8, of: u8, args: u8)",
-            },
-        ]
-        "###
-        );
-    }
-
     #[test]
     fn arg_snippets_for_method_call() {
         assert_debug_snapshot!(
@@ -1235,7 +1179,7 @@ mod tests {
 
     #[test]
     fn test_struct_field_completion_in_record_lit() {
-        covers!(test_struct_field_completion_in_record_lit);
+        covers!(test_struct_field_completion_in_func_call);
         assert_debug_snapshot!(
         do_reference_completion(
                 r"
diff --git a/crates/ra_ide/src/snapshots/highlight_injection.html b/crates/ra_ide/src/snapshots/highlight_injection.html
index ea026d7a04..6ec13bd80f 100644
--- a/crates/ra_ide/src/snapshots/highlight_injection.html
+++ b/crates/ra_ide/src/snapshots/highlight_injection.html
@@ -20,7 +20,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .macro              { color: #94BFF3; }
 .module             { color: #AFD8AF; }
 .variable           { color: #DCDCCC; }
-.format_specifier   { color: #CC696B; }
 .mutable            { text-decoration: underline; }
 
 .keyword            { color: #F0DFAF; font-weight: bold; }
diff --git a/crates/ra_ide/src/snapshots/highlight_strings.html b/crates/ra_ide/src/snapshots/highlight_strings.html
index de06daf72b..433f2e0c5e 100644
--- a/crates/ra_ide/src/snapshots/highlight_strings.html
+++ b/crates/ra_ide/src/snapshots/highlight_strings.html
@@ -20,7 +20,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .macro              { color: #94BFF3; }
 .module             { color: #AFD8AF; }
 .variable           { color: #DCDCCC; }
-.format_specifier   { color: #CC696B; }
 .mutable            { text-decoration: underline; }
 
 .keyword            { color: #F0DFAF; font-weight: bold; }
@@ -41,43 +40,43 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 <span class="keyword">fn</span> <span class="function declaration">main</span>() {
     <span class="comment">// from https://doc.rust-lang.org/std/fmt/index.html</span>
     <span class="macro">println!</span>(<span class="string_literal">"Hello"</span>);                 <span class="comment">// =&gt; "Hello"</span>
-    <span class="macro">println!</span>(<span class="string_literal">"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"world"</span>);   <span class="comment">// =&gt; "Hello, world!"</span>
-    <span class="macro">println!</span>(<span class="string_literal">"The number is </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>);   <span class="comment">// =&gt; "The number is 1"</span>
-    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal">"</span>, (<span class="numeric_literal">3</span>, <span class="numeric_literal">4</span>));          <span class="comment">// =&gt; "(3, 4)"</span>
-    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">value</span><span class="format_specifier">}</span><span class="string_literal">"</span>, value=<span class="numeric_literal">4</span>);      <span class="comment">// =&gt; "4"</span>
-    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>, <span class="numeric_literal">2</span>);           <span class="comment">// =&gt; "1 2"</span>
-    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">4</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="numeric_literal">42</span>);             <span class="comment">// =&gt; "0042" with leading zerosV</span>
-    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>, <span class="numeric_literal">2</span>);   <span class="comment">// =&gt; "2 1 1 2"</span>
-    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">argument</span><span class="format_specifier">}</span><span class="string_literal">"</span>, argument = <span class="string_literal">"test"</span>);   <span class="comment">// =&gt; "test"</span>
-    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>, name = <span class="numeric_literal">2</span>);          <span class="comment">// =&gt; "2 1"</span>
-    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">a</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="variable">c</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="variable">b</span><span class="format_specifier">}</span><span class="string_literal">"</span>, a=<span class="string_literal">"a"</span>, b=<span class="char_literal">'b'</span>, c=<span class="numeric_literal">3</span>);  <span class="comment">// =&gt; "a 3 b"</span>
-    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>);
-    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>);
-    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>, <span class="string_literal">"x"</span>);
-    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">width</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>, width = <span class="numeric_literal">5</span>);
-    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">&lt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>);
-    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">-</span><span class="format_specifier">&lt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>);
-    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">^</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>);
-    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">&gt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>);
-    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">+</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>);
-    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="variable">x</span><span class="string_literal">}!"</span>, <span class="numeric_literal">27</span>);
-    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>);
-    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, -<span class="numeric_literal">5</span>);
-    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="numeric_literal">0</span><span class="numeric_literal">10</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">27</span>);
-    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">0.01</span>);
-    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="numeric_literal">5</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">0.01</span>);
-    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>, <span class="numeric_literal">0.01</span>);
-    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">"</span>,    <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>, <span class="numeric_literal">0.01</span>);
-    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">"</span>,   <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>, <span class="numeric_literal">0.01</span>);
-    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="variable">number</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="variable">prec</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="string_literal">"x"</span>, prec = <span class="numeric_literal">5</span>, number = <span class="numeric_literal">0.01</span>);
-    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">` has 3 fractional digits"</span>, <span class="string_literal">"Hello"</span>, <span class="numeric_literal">3</span>, name=<span class="numeric_literal">1234.56</span>);
-    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">` has 3 characters"</span>, <span class="string_literal">"Hello"</span>, <span class="numeric_literal">3</span>, name=<span class="string_literal">"1234.56"</span>);
-    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">&gt;</span><span class="numeric_literal">8</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">` has 3 right-aligned characters"</span>, <span class="string_literal">"Hello"</span>, <span class="numeric_literal">3</span>, name=<span class="string_literal">"1234.56"</span>);
+    <span class="macro">println!</span>(<span class="string_literal">"Hello, </span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="string_literal">"world"</span>);   <span class="comment">// =&gt; "Hello, world!"</span>
+    <span class="macro">println!</span>(<span class="string_literal">"The number is </span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>);   <span class="comment">// =&gt; "The number is 1"</span>
+    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="attribute">:</span><span class="attribute">?</span><span class="attribute">}</span><span class="string_literal">"</span>, (<span class="numeric_literal">3</span>, <span class="numeric_literal">4</span>));          <span class="comment">// =&gt; "(3, 4)"</span>
+    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="variable">value</span><span class="attribute">}</span><span class="string_literal">"</span>, value=<span class="numeric_literal">4</span>);      <span class="comment">// =&gt; "4"</span>
+    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal"> </span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>, <span class="numeric_literal">2</span>);           <span class="comment">// =&gt; "1 2"</span>
+    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="attribute">:</span><span class="numeric_literal">0</span><span class="numeric_literal">4</span><span class="attribute">}</span><span class="string_literal">"</span>, <span class="numeric_literal">42</span>);             <span class="comment">// =&gt; "0042" with leading zerosV</span>
+    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="numeric_literal">1</span><span class="attribute">}</span><span class="string_literal"> </span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal"> </span><span class="attribute">{</span><span class="numeric_literal">0</span><span class="attribute">}</span><span class="string_literal"> </span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>, <span class="numeric_literal">2</span>);   <span class="comment">// =&gt; "2 1 1 2"</span>
+    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="variable">argument</span><span class="attribute">}</span><span class="string_literal">"</span>, argument = <span class="string_literal">"test"</span>);   <span class="comment">// =&gt; "test"</span>
+    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="variable">name</span><span class="attribute">}</span><span class="string_literal"> </span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>, name = <span class="numeric_literal">2</span>);          <span class="comment">// =&gt; "2 1"</span>
+    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="variable">a</span><span class="attribute">}</span><span class="string_literal"> </span><span class="attribute">{</span><span class="variable">c</span><span class="attribute">}</span><span class="string_literal"> </span><span class="attribute">{</span><span class="variable">b</span><span class="attribute">}</span><span class="string_literal">"</span>, a=<span class="string_literal">"a"</span>, b=<span class="char_literal">'b'</span>, c=<span class="numeric_literal">3</span>);  <span class="comment">// =&gt; "a 3 b"</span>
+    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">:</span><span class="numeric_literal">5</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>);
+    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">:</span><span class="numeric_literal">1</span><span class="attribute">$</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>);
+    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="numeric_literal">1</span><span class="attribute">:</span><span class="numeric_literal">0</span><span class="attribute">$</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>, <span class="string_literal">"x"</span>);
+    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">:</span><span class="variable">width</span><span class="attribute">$</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>, width = <span class="numeric_literal">5</span>);
+    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">:</span><span class="attribute">&lt;</span><span class="numeric_literal">5</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>);
+    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">:</span><span class="attribute">-</span><span class="attribute">&lt;</span><span class="numeric_literal">5</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>);
+    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">:</span><span class="attribute">^</span><span class="numeric_literal">5</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>);
+    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">:</span><span class="attribute">&gt;</span><span class="numeric_literal">5</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>);
+    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">:</span><span class="attribute">+</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>);
+    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="attribute">:</span><span class="attribute">#</span><span class="variable">x</span><span class="string_literal">}!"</span>, <span class="numeric_literal">27</span>);
+    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>);
+    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="attribute">}</span><span class="string_literal">!"</span>, -<span class="numeric_literal">5</span>);
+    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="attribute">:</span><span class="attribute">#</span><span class="numeric_literal">0</span><span class="numeric_literal">10</span><span class="variable">x</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">27</span>);
+    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="numeric_literal">0</span><span class="attribute">}</span><span class="string_literal"> is </span><span class="attribute">{</span><span class="numeric_literal">1</span><span class="attribute">:</span><span class="attribute">.</span><span class="numeric_literal">5</span><span class="attribute">}</span><span class="string_literal">"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">0.01</span>);
+    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="numeric_literal">1</span><span class="attribute">}</span><span class="string_literal"> is </span><span class="attribute">{</span><span class="numeric_literal">2</span><span class="attribute">:</span><span class="attribute">.</span><span class="numeric_literal">0</span><span class="attribute">$</span><span class="attribute">}</span><span class="string_literal">"</span>, <span class="numeric_literal">5</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">0.01</span>);
+    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="numeric_literal">0</span><span class="attribute">}</span><span class="string_literal"> is </span><span class="attribute">{</span><span class="numeric_literal">2</span><span class="attribute">:</span><span class="attribute">.</span><span class="numeric_literal">1</span><span class="attribute">$</span><span class="attribute">}</span><span class="string_literal">"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>, <span class="numeric_literal">0.01</span>);
+    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal"> is </span><span class="attribute">{</span><span class="attribute">:</span><span class="attribute">.</span><span class="attribute">*</span><span class="attribute">}</span><span class="string_literal">"</span>,    <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>, <span class="numeric_literal">0.01</span>);
+    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal"> is </span><span class="attribute">{</span><span class="numeric_literal">2</span><span class="attribute">:</span><span class="attribute">.</span><span class="attribute">*</span><span class="attribute">}</span><span class="string_literal">"</span>,   <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>, <span class="numeric_literal">0.01</span>);
+    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal"> is </span><span class="attribute">{</span><span class="variable">number</span><span class="attribute">:</span><span class="attribute">.</span><span class="variable">prec</span><span class="attribute">$</span><span class="attribute">}</span><span class="string_literal">"</span>, <span class="string_literal">"x"</span>, prec = <span class="numeric_literal">5</span>, number = <span class="numeric_literal">0.01</span>);
+    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal">, `</span><span class="attribute">{</span><span class="variable">name</span><span class="attribute">:</span><span class="attribute">.</span><span class="attribute">*</span><span class="attribute">}</span><span class="string_literal">` has 3 fractional digits"</span>, <span class="string_literal">"Hello"</span>, <span class="numeric_literal">3</span>, name=<span class="numeric_literal">1234.56</span>);
+    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal">, `</span><span class="attribute">{</span><span class="variable">name</span><span class="attribute">:</span><span class="attribute">.</span><span class="attribute">*</span><span class="attribute">}</span><span class="string_literal">` has 3 characters"</span>, <span class="string_literal">"Hello"</span>, <span class="numeric_literal">3</span>, name=<span class="string_literal">"1234.56"</span>);
+    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal">, `</span><span class="attribute">{</span><span class="variable">name</span><span class="attribute">:</span><span class="attribute">&gt;</span><span class="numeric_literal">8</span><span class="attribute">.</span><span class="attribute">*</span><span class="attribute">}</span><span class="string_literal">` has 3 right-aligned characters"</span>, <span class="string_literal">"Hello"</span>, <span class="numeric_literal">3</span>, name=<span class="string_literal">"1234.56"</span>);
     <span class="macro">println!</span>(<span class="string_literal">"Hello {{}}"</span>);
     <span class="macro">println!</span>(<span class="string_literal">"{{ Hello"</span>);
 
-    <span class="macro">println!</span>(<span class="string_literal">r"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"world"</span>);
+    <span class="macro">println!</span>(<span class="string_literal">r"Hello, </span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="string_literal">"world"</span>);
 
-    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">\x41</span><span class="format_specifier">}</span><span class="string_literal">"</span>, A = <span class="numeric_literal">92</span>);
-    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">ничоси</span><span class="format_specifier">}</span><span class="string_literal">"</span>, ничоси = <span class="numeric_literal">92</span>);
+    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="variable">\x41</span><span class="attribute">}</span><span class="string_literal">"</span>, A = <span class="numeric_literal">92</span>);
+    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="variable">ничоси</span><span class="attribute">}</span><span class="string_literal">"</span>, ничоси = <span class="numeric_literal">92</span>);
 }</code></pre>
\ No newline at end of file
diff --git a/crates/ra_ide/src/snapshots/highlighting.html b/crates/ra_ide/src/snapshots/highlighting.html
index 4b12fe8238..ccb1fc7516 100644
--- a/crates/ra_ide/src/snapshots/highlighting.html
+++ b/crates/ra_ide/src/snapshots/highlighting.html
@@ -20,7 +20,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .macro              { color: #94BFF3; }
 .module             { color: #AFD8AF; }
 .variable           { color: #DCDCCC; }
-.format_specifier   { color: #CC696B; }
 .mutable            { text-decoration: underline; }
 
 .keyword            { color: #F0DFAF; font-weight: bold; }
diff --git a/crates/ra_ide/src/snapshots/rainbow_highlighting.html b/crates/ra_ide/src/snapshots/rainbow_highlighting.html
index 11e1f3e44e..3df82c45fb 100644
--- a/crates/ra_ide/src/snapshots/rainbow_highlighting.html
+++ b/crates/ra_ide/src/snapshots/rainbow_highlighting.html
@@ -20,7 +20,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .macro              { color: #94BFF3; }
 .module             { color: #AFD8AF; }
 .variable           { color: #DCDCCC; }
-.format_specifier   { color: #CC696B; }
 .mutable            { text-decoration: underline; }
 
 .keyword            { color: #F0DFAF; font-weight: bold; }
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index 6658c7bb27..be0f8c827a 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -290,7 +290,7 @@ fn highlight_format_specifier(kind: FormatSpecifier) -> Option<HighlightTag> {
         | FormatSpecifier::DollarSign
         | FormatSpecifier::Dot
         | FormatSpecifier::Asterisk
-        | FormatSpecifier::QuestionMark => HighlightTag::FormatSpecifier,
+        | FormatSpecifier::QuestionMark => HighlightTag::Attribute,
         FormatSpecifier::Integer | FormatSpecifier::Zero => HighlightTag::NumericLiteral,
         FormatSpecifier::Identifier => HighlightTag::Local,
     })
diff --git a/crates/ra_ide/src/syntax_highlighting/html.rs b/crates/ra_ide/src/syntax_highlighting/html.rs
index ff0eeeb52c..010db40175 100644
--- a/crates/ra_ide/src/syntax_highlighting/html.rs
+++ b/crates/ra_ide/src/syntax_highlighting/html.rs
@@ -79,7 +79,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .macro              { color: #94BFF3; }
 .module             { color: #AFD8AF; }
 .variable           { color: #DCDCCC; }
-.format_specifier   { color: #CC696B; }
 .mutable            { text-decoration: underline; }
 
 .keyword            { color: #F0DFAF; font-weight: bold; }
diff --git a/crates/ra_ide/src/syntax_highlighting/tags.rs b/crates/ra_ide/src/syntax_highlighting/tags.rs
index be1a0f12b2..f2c4216545 100644
--- a/crates/ra_ide/src/syntax_highlighting/tags.rs
+++ b/crates/ra_ide/src/syntax_highlighting/tags.rs
@@ -39,7 +39,6 @@ pub enum HighlightTag {
     Union,
     Local,
     UnresolvedReference,
-    FormatSpecifier,
 }
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
@@ -82,7 +81,6 @@ impl HighlightTag {
             HighlightTag::Union => "union",
             HighlightTag::Local => "variable",
             HighlightTag::UnresolvedReference => "unresolved_reference",
-            HighlightTag::FormatSpecifier => "format_specifier",
         }
     }
 }
diff --git a/crates/ra_prof/Cargo.toml b/crates/ra_prof/Cargo.toml
index c33b5121ae..d15b089925 100644
--- a/crates/ra_prof/Cargo.toml
+++ b/crates/ra_prof/Cargo.toml
@@ -9,7 +9,6 @@ publish = false
 doctest = false
 
 [dependencies]
-ra_arena = { path = "../ra_arena" }
 once_cell = "1.3.1"
 backtrace = { version = "0.3.44", optional = true }
 
diff --git a/crates/ra_prof/src/hprof.rs b/crates/ra_prof/src/hprof.rs
deleted file mode 100644
index 2b8a903636..0000000000
--- a/crates/ra_prof/src/hprof.rs
+++ /dev/null
@@ -1,246 +0,0 @@
-//! Simple hierarchical profiler
-use once_cell::sync::Lazy;
-use std::{
-    cell::RefCell,
-    collections::{BTreeMap, HashSet},
-    io::{stderr, Write},
-    sync::{
-        atomic::{AtomicBool, Ordering},
-        RwLock,
-    },
-    time::{Duration, Instant},
-};
-
-use crate::tree::{Idx, Tree};
-
-/// Filtering syntax
-/// env RA_PROFILE=*             // dump everything
-/// env RA_PROFILE=foo|bar|baz   // enabled only selected entries
-/// env RA_PROFILE=*@3>10        // dump everything, up to depth 3, if it takes more than 10 ms
-pub fn init() {
-    let spec = std::env::var("RA_PROFILE").unwrap_or_default();
-    init_from(&spec);
-}
-
-pub fn init_from(spec: &str) {
-    let filter = if spec.is_empty() { Filter::disabled() } else { Filter::from_spec(spec) };
-    filter.install();
-}
-
-pub type Label = &'static str;
-
-/// This function starts a profiling scope in the current execution stack with a given description.
-/// It returns a Profile structure and measure elapsed time between this method invocation and Profile structure drop.
-/// It supports nested profiling scopes in case when this function invoked multiple times at the execution stack. In this case the profiling information will be nested at the output.
-/// Profiling information is being printed in the stderr.
-///
-/// # Example
-/// ```
-/// use ra_prof::{profile, set_filter, Filter};
-///
-/// let f = Filter::from_spec("profile1|profile2@2");
-/// set_filter(f);
-/// profiling_function1();
-///
-/// fn profiling_function1() {
-///     let _p = profile("profile1");
-///     profiling_function2();
-/// }
-///
-/// fn profiling_function2() {
-///     let _p = profile("profile2");
-/// }
-/// ```
-/// This will print in the stderr the following:
-/// ```text
-///  0ms - profile
-///      0ms - profile2
-/// ```
-pub fn profile(label: Label) -> Profiler {
-    assert!(!label.is_empty());
-    let enabled = PROFILING_ENABLED.load(Ordering::Relaxed)
-        && PROFILE_STACK.with(|stack| stack.borrow_mut().push(label));
-    let label = if enabled { Some(label) } else { None };
-    Profiler { label, detail: None }
-}
-
-pub struct Profiler {
-    label: Option<Label>,
-    detail: Option<String>,
-}
-
-impl Profiler {
-    pub fn detail(mut self, detail: impl FnOnce() -> String) -> Profiler {
-        if self.label.is_some() {
-            self.detail = Some(detail())
-        }
-        self
-    }
-}
-
-impl Drop for Profiler {
-    fn drop(&mut self) {
-        match self {
-            Profiler { label: Some(label), detail } => {
-                PROFILE_STACK.with(|stack| {
-                    stack.borrow_mut().pop(label, detail.take());
-                });
-            }
-            Profiler { label: None, .. } => (),
-        }
-    }
-}
-
-static PROFILING_ENABLED: AtomicBool = AtomicBool::new(false);
-static FILTER: Lazy<RwLock<Filter>> = Lazy::new(Default::default);
-thread_local!(static PROFILE_STACK: RefCell<ProfileStack> = RefCell::new(ProfileStack::new()));
-
-#[derive(Default, Clone, Debug)]
-struct Filter {
-    depth: usize,
-    allowed: HashSet<String>,
-    longer_than: Duration,
-    version: usize,
-}
-
-impl Filter {
-    fn disabled() -> Filter {
-        Filter::default()
-    }
-
-    fn from_spec(mut spec: &str) -> Filter {
-        let longer_than = if let Some(idx) = spec.rfind('>') {
-            let longer_than = spec[idx + 1..].parse().expect("invalid profile longer_than");
-            spec = &spec[..idx];
-            Duration::from_millis(longer_than)
-        } else {
-            Duration::new(0, 0)
-        };
-
-        let depth = if let Some(idx) = spec.rfind('@') {
-            let depth: usize = spec[idx + 1..].parse().expect("invalid profile depth");
-            spec = &spec[..idx];
-            depth
-        } else {
-            999
-        };
-        let allowed =
-            if spec == "*" { HashSet::new() } else { spec.split('|').map(String::from).collect() };
-        Filter { depth, allowed, longer_than, version: 0 }
-    }
-
-    fn install(mut self) {
-        PROFILING_ENABLED.store(self.depth > 0, Ordering::SeqCst);
-        let mut old = FILTER.write().unwrap();
-        self.version = old.version + 1;
-        *old = self;
-    }
-}
-
-struct ProfileStack {
-    starts: Vec<Instant>,
-    filter: Filter,
-    messages: Tree<Message>,
-}
-
-#[derive(Default)]
-struct Message {
-    duration: Duration,
-    label: Label,
-    detail: Option<String>,
-}
-
-impl ProfileStack {
-    fn new() -> ProfileStack {
-        ProfileStack { starts: Vec::new(), messages: Tree::default(), filter: Default::default() }
-    }
-
-    fn push(&mut self, label: Label) -> bool {
-        if self.starts.is_empty() {
-            if let Ok(f) = FILTER.try_read() {
-                if f.version > self.filter.version {
-                    self.filter = f.clone();
-                }
-            };
-        }
-        if self.starts.len() > self.filter.depth {
-            return false;
-        }
-        let allowed = &self.filter.allowed;
-        if self.starts.is_empty() && !allowed.is_empty() && !allowed.contains(label) {
-            return false;
-        }
-
-        self.starts.push(Instant::now());
-        self.messages.start();
-        true
-    }
-
-    pub fn pop(&mut self, label: Label, detail: Option<String>) {
-        let start = self.starts.pop().unwrap();
-        let duration = start.elapsed();
-        let level = self.starts.len();
-        self.messages.finish(Message { duration, label, detail });
-        if level == 0 {
-            let longer_than = self.filter.longer_than;
-            // Convert to millis for comparison to avoid problems with rounding
-            // (otherwise we could print `0ms` despite user's `>0` filter when
-            // `duration` is just a few nanos).
-            if duration.as_millis() > longer_than.as_millis() {
-                let stderr = stderr();
-                if let Some(root) = self.messages.root() {
-                    print(&self.messages, root, 0, longer_than, &mut stderr.lock());
-                }
-            }
-            self.messages.clear();
-            assert!(self.starts.is_empty())
-        }
-    }
-}
-
-fn print(
-    tree: &Tree<Message>,
-    curr: Idx<Message>,
-    level: u32,
-    longer_than: Duration,
-    out: &mut impl Write,
-) {
-    let current_indent = "    ".repeat(level as usize);
-    let detail = tree[curr].detail.as_ref().map(|it| format!(" @ {}", it)).unwrap_or_default();
-    writeln!(
-        out,
-        "{}{:5}ms - {}{}",
-        current_indent,
-        tree[curr].duration.as_millis(),
-        tree[curr].label,
-        detail,
-    )
-    .expect("printing profiling info");
-
-    let mut accounted_for = Duration::default();
-    let mut short_children = BTreeMap::new(); // Use `BTreeMap` to get deterministic output.
-    for child in tree.children(curr) {
-        accounted_for += tree[child].duration;
-
-        if tree[child].duration.as_millis() > longer_than.as_millis() {
-            print(tree, child, level + 1, longer_than, out)
-        } else {
-            let (total_duration, cnt) =
-                short_children.entry(tree[child].label).or_insert((Duration::default(), 0));
-            *total_duration += tree[child].duration;
-            *cnt += 1;
-        }
-    }
-
-    for (child_msg, (duration, count)) in short_children.iter() {
-        let millis = duration.as_millis();
-        writeln!(out, "    {}{:5}ms - {} ({} calls)", current_indent, millis, child_msg, count)
-            .expect("printing profiling info");
-    }
-
-    let unaccounted = tree[curr].duration - accounted_for;
-    if tree.children(curr).next().is_some() && unaccounted > longer_than {
-        writeln!(out, "    {}{:5}ms - ???", current_indent, unaccounted.as_millis())
-            .expect("printing profiling info");
-    }
-}
diff --git a/crates/ra_prof/src/lib.rs b/crates/ra_prof/src/lib.rs
index 89df7f04b7..2d4f68f5e0 100644
--- a/crates/ra_prof/src/lib.rs
+++ b/crates/ra_prof/src/lib.rs
@@ -1,24 +1,323 @@
-//! A collection of tools for profiling rust-analyzer.
+//! FIXME: write short doc here
 
 mod memory_usage;
 #[cfg(feature = "cpu_profiler")]
 mod google_cpu_profiler;
-mod hprof;
-mod tree;
 
-use std::cell::RefCell;
-
-pub use crate::{
-    hprof::{init, init_from, profile},
-    memory_usage::{Bytes, MemoryUsage},
+use std::{
+    cell::RefCell,
+    collections::BTreeMap,
+    collections::HashSet,
+    io::{stderr, Write},
+    sync::{
+        atomic::{AtomicBool, Ordering},
+        RwLock,
+    },
+    time::{Duration, Instant},
 };
 
+use once_cell::sync::Lazy;
+
+pub use crate::memory_usage::{Bytes, MemoryUsage};
+
 // We use jemalloc mainly to get heap usage statistics, actual performance
 // difference is not measures.
 #[cfg(all(feature = "jemalloc", not(target_env = "msvc")))]
 #[global_allocator]
 static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
 
+pub fn init() {
+    set_filter(match std::env::var("RA_PROFILE") {
+        Ok(spec) => Filter::from_spec(&spec),
+        Err(_) => Filter::disabled(),
+    });
+}
+
+/// Set profiling filter. It specifies descriptions allowed to profile.
+/// This is helpful when call stack has too many nested profiling scopes.
+/// Additionally filter can specify maximum depth of profiling scopes nesting.
+///
+/// #Example
+/// ```
+/// use ra_prof::{set_filter, Filter};
+/// let f = Filter::from_spec("profile1|profile2@2");
+/// set_filter(f);
+/// ```
+pub fn set_filter(f: Filter) {
+    PROFILING_ENABLED.store(f.depth > 0, Ordering::SeqCst);
+    let set: HashSet<_> = f.allowed.iter().cloned().collect();
+    let mut old = FILTER.write().unwrap();
+    let filter_data = FilterData {
+        depth: f.depth,
+        allowed: set,
+        longer_than: f.longer_than,
+        version: old.version + 1,
+    };
+    *old = filter_data;
+}
+
+pub type Label = &'static str;
+
+/// This function starts a profiling scope in the current execution stack with a given description.
+/// It returns a Profile structure and measure elapsed time between this method invocation and Profile structure drop.
+/// It supports nested profiling scopes in case when this function invoked multiple times at the execution stack. In this case the profiling information will be nested at the output.
+/// Profiling information is being printed in the stderr.
+///
+/// # Example
+/// ```
+/// use ra_prof::{profile, set_filter, Filter};
+///
+/// let f = Filter::from_spec("profile1|profile2@2");
+/// set_filter(f);
+/// profiling_function1();
+///
+/// fn profiling_function1() {
+///     let _p = profile("profile1");
+///     profiling_function2();
+/// }
+///
+/// fn profiling_function2() {
+///     let _p = profile("profile2");
+/// }
+/// ```
+/// This will print in the stderr the following:
+/// ```text
+///  0ms - profile
+///      0ms - profile2
+/// ```
+pub fn profile(label: Label) -> Profiler {
+    assert!(!label.is_empty());
+    if !PROFILING_ENABLED.load(Ordering::Relaxed) {
+        return Profiler { label: None, detail: None };
+    }
+
+    PROFILE_STACK.with(|stack| {
+        let mut stack = stack.borrow_mut();
+        if stack.starts.is_empty() {
+            if let Ok(f) = FILTER.try_read() {
+                if f.version > stack.filter_data.version {
+                    stack.filter_data = f.clone();
+                }
+            };
+        }
+        if stack.starts.len() > stack.filter_data.depth {
+            return Profiler { label: None, detail: None };
+        }
+        let allowed = &stack.filter_data.allowed;
+        if stack.starts.is_empty() && !allowed.is_empty() && !allowed.contains(label) {
+            return Profiler { label: None, detail: None };
+        }
+
+        stack.starts.push(Instant::now());
+        Profiler { label: Some(label), detail: None }
+    })
+}
+
+pub struct Profiler {
+    label: Option<Label>,
+    detail: Option<String>,
+}
+
+impl Profiler {
+    pub fn detail(mut self, detail: impl FnOnce() -> String) -> Profiler {
+        if self.label.is_some() {
+            self.detail = Some(detail())
+        }
+        self
+    }
+}
+
+pub struct Filter {
+    depth: usize,
+    allowed: Vec<String>,
+    longer_than: Duration,
+}
+
+impl Filter {
+    // Filtering syntax
+    // env RA_PROFILE=*             // dump everything
+    // env RA_PROFILE=foo|bar|baz   // enabled only selected entries
+    // env RA_PROFILE=*@3>10        // dump everything, up to depth 3, if it takes more than 10 ms
+    pub fn from_spec(mut spec: &str) -> Filter {
+        let longer_than = if let Some(idx) = spec.rfind('>') {
+            let longer_than = spec[idx + 1..].parse().expect("invalid profile longer_than");
+            spec = &spec[..idx];
+            Duration::from_millis(longer_than)
+        } else {
+            Duration::new(0, 0)
+        };
+
+        let depth = if let Some(idx) = spec.rfind('@') {
+            let depth: usize = spec[idx + 1..].parse().expect("invalid profile depth");
+            spec = &spec[..idx];
+            depth
+        } else {
+            999
+        };
+        let allowed =
+            if spec == "*" { Vec::new() } else { spec.split('|').map(String::from).collect() };
+        Filter::new(depth, allowed, longer_than)
+    }
+
+    pub fn disabled() -> Filter {
+        Filter::new(0, Vec::new(), Duration::new(0, 0))
+    }
+
+    pub fn new(depth: usize, allowed: Vec<String>, longer_than: Duration) -> Filter {
+        Filter { depth, allowed, longer_than }
+    }
+}
+
+struct ProfileStack {
+    starts: Vec<Instant>,
+    messages: Vec<Message>,
+    filter_data: FilterData,
+}
+
+struct Message {
+    level: usize,
+    duration: Duration,
+    label: Label,
+    detail: Option<String>,
+}
+
+impl ProfileStack {
+    fn new() -> ProfileStack {
+        ProfileStack { starts: Vec::new(), messages: Vec::new(), filter_data: Default::default() }
+    }
+}
+
+#[derive(Default, Clone)]
+struct FilterData {
+    depth: usize,
+    version: usize,
+    allowed: HashSet<String>,
+    longer_than: Duration,
+}
+
+static PROFILING_ENABLED: AtomicBool = AtomicBool::new(false);
+
+static FILTER: Lazy<RwLock<FilterData>> = Lazy::new(Default::default);
+
+thread_local!(static PROFILE_STACK: RefCell<ProfileStack> = RefCell::new(ProfileStack::new()));
+
+impl Drop for Profiler {
+    fn drop(&mut self) {
+        match self {
+            Profiler { label: Some(label), detail } => {
+                PROFILE_STACK.with(|stack| {
+                    let mut stack = stack.borrow_mut();
+                    let start = stack.starts.pop().unwrap();
+                    let duration = start.elapsed();
+                    let level = stack.starts.len();
+                    stack.messages.push(Message { level, duration, label, detail: detail.take() });
+                    if level == 0 {
+                        let stdout = stderr();
+                        let longer_than = stack.filter_data.longer_than;
+                        // Convert to millis for comparison to avoid problems with rounding
+                        // (otherwise we could print `0ms` despite user's `>0` filter when
+                        // `duration` is just a few nanos).
+                        if duration.as_millis() > longer_than.as_millis() {
+                            print(&stack.messages, longer_than, &mut stdout.lock());
+                        }
+                        stack.messages.clear();
+                    }
+                });
+            }
+            Profiler { label: None, .. } => (),
+        }
+    }
+}
+
+fn print(msgs: &[Message], longer_than: Duration, out: &mut impl Write) {
+    if msgs.is_empty() {
+        return;
+    }
+    let children_map = idx_to_children(msgs);
+    let root_idx = msgs.len() - 1;
+    print_for_idx(root_idx, &children_map, msgs, longer_than, out);
+}
+
+fn print_for_idx(
+    current_idx: usize,
+    children_map: &[Vec<usize>],
+    msgs: &[Message],
+    longer_than: Duration,
+    out: &mut impl Write,
+) {
+    let current = &msgs[current_idx];
+    let current_indent = "    ".repeat(current.level);
+    let detail = current.detail.as_ref().map(|it| format!(" @ {}", it)).unwrap_or_default();
+    writeln!(
+        out,
+        "{}{:5}ms - {}{}",
+        current_indent,
+        current.duration.as_millis(),
+        current.label,
+        detail,
+    )
+    .expect("printing profiling info");
+
+    let longer_than_millis = longer_than.as_millis();
+    let children_indices = &children_map[current_idx];
+    let mut accounted_for = Duration::default();
+    let mut short_children = BTreeMap::new(); // Use `BTreeMap` to get deterministic output.
+
+    for child_idx in children_indices.iter() {
+        let child = &msgs[*child_idx];
+        if child.duration.as_millis() > longer_than_millis {
+            print_for_idx(*child_idx, children_map, msgs, longer_than, out);
+        } else {
+            let pair = short_children.entry(child.label).or_insert((Duration::default(), 0));
+            pair.0 += child.duration;
+            pair.1 += 1;
+        }
+        accounted_for += child.duration;
+    }
+
+    for (child_msg, (duration, count)) in short_children.iter() {
+        let millis = duration.as_millis();
+        writeln!(out, "    {}{:5}ms - {} ({} calls)", current_indent, millis, child_msg, count)
+            .expect("printing profiling info");
+    }
+
+    let unaccounted_millis = (current.duration - accounted_for).as_millis();
+    if !children_indices.is_empty()
+        && unaccounted_millis > 0
+        && unaccounted_millis > longer_than_millis
+    {
+        writeln!(out, "    {}{:5}ms - ???", current_indent, unaccounted_millis)
+            .expect("printing profiling info");
+    }
+}
+
+/// Returns a mapping from an index in the `msgs` to the vector with the indices of its children.
+///
+/// This assumes that the entries in `msgs` are in the order of when the calls to `profile` finish.
+/// In other words, a postorder of the call graph. In particular, the root is the last element of
+/// `msgs`.
+fn idx_to_children(msgs: &[Message]) -> Vec<Vec<usize>> {
+    // Initialize with the index of the root; `msgs` and `ancestors` should be never empty.
+    assert!(!msgs.is_empty());
+    let mut ancestors = vec![msgs.len() - 1];
+    let mut result: Vec<Vec<usize>> = vec![vec![]; msgs.len()];
+    for (idx, msg) in msgs[..msgs.len() - 1].iter().enumerate().rev() {
+        // We need to find the parent of the current message, i.e., the last ancestor that has a
+        // level lower than the current message.
+        while msgs[*ancestors.last().unwrap()].level >= msg.level {
+            ancestors.pop();
+        }
+        result[*ancestors.last().unwrap()].push(idx);
+        ancestors.push(idx);
+    }
+    // Note that above we visited all children from the last to the first one. Let's reverse vectors
+    // to get the more natural order where the first element is the first child.
+    for vec in result.iter_mut() {
+        vec.reverse();
+    }
+    result
+}
+
 /// Prints backtrace to stderr, useful for debugging.
 #[cfg(feature = "backtrace")]
 pub fn print_backtrace() {
@@ -104,3 +403,86 @@ impl Drop for CpuProfiler {
 pub fn memory_usage() -> MemoryUsage {
     MemoryUsage::current()
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_basic_profile() {
+        let s = vec!["profile1".to_string(), "profile2".to_string()];
+        let f = Filter::new(2, s, Duration::new(0, 0));
+        set_filter(f);
+        profiling_function1();
+    }
+
+    fn profiling_function1() {
+        let _p = profile("profile1");
+        profiling_function2();
+    }
+
+    fn profiling_function2() {
+        let _p = profile("profile2");
+    }
+
+    #[test]
+    fn test_longer_than() {
+        let mut result = vec![];
+        let msgs = vec![
+            Message { level: 1, duration: Duration::from_nanos(3), label: "bar", detail: None },
+            Message { level: 1, duration: Duration::from_nanos(2), label: "bar", detail: None },
+            Message { level: 0, duration: Duration::from_millis(1), label: "foo", detail: None },
+        ];
+        print(&msgs, Duration::from_millis(0), &mut result);
+        // The calls to `bar` are so short that they'll be rounded to 0ms and should get collapsed
+        // when printing.
+        assert_eq!(
+            std::str::from_utf8(&result).unwrap(),
+            "    1ms - foo\n        0ms - bar (2 calls)\n"
+        );
+    }
+
+    #[test]
+    fn test_unaccounted_for_topmost() {
+        let mut result = vec![];
+        let msgs = vec![
+            Message { level: 1, duration: Duration::from_millis(2), label: "bar", detail: None },
+            Message { level: 0, duration: Duration::from_millis(5), label: "foo", detail: None },
+        ];
+        print(&msgs, Duration::from_millis(0), &mut result);
+        assert_eq!(
+            std::str::from_utf8(&result).unwrap().lines().collect::<Vec<_>>(),
+            vec![
+                "    5ms - foo",
+                "        2ms - bar",
+                "        3ms - ???",
+                // Dummy comment to improve formatting
+            ]
+        );
+    }
+
+    #[test]
+    fn test_unaccounted_for_multiple_levels() {
+        let mut result = vec![];
+        let msgs = vec![
+            Message { level: 2, duration: Duration::from_millis(3), label: "baz", detail: None },
+            Message { level: 1, duration: Duration::from_millis(5), label: "bar", detail: None },
+            Message { level: 2, duration: Duration::from_millis(2), label: "baz", detail: None },
+            Message { level: 1, duration: Duration::from_millis(4), label: "bar", detail: None },
+            Message { level: 0, duration: Duration::from_millis(9), label: "foo", detail: None },
+        ];
+        print(&msgs, Duration::from_millis(0), &mut result);
+        assert_eq!(
+            std::str::from_utf8(&result).unwrap().lines().collect::<Vec<_>>(),
+            vec![
+                "    9ms - foo",
+                "        5ms - bar",
+                "            3ms - baz",
+                "            2ms - ???",
+                "        4ms - bar",
+                "            2ms - baz",
+                "            2ms - ???",
+            ]
+        );
+    }
+}
diff --git a/crates/ra_prof/src/tree.rs b/crates/ra_prof/src/tree.rs
deleted file mode 100644
index 9ea5b5db8e..0000000000
--- a/crates/ra_prof/src/tree.rs
+++ /dev/null
@@ -1,84 +0,0 @@
-//! A simple tree implementation which tries to not allocate all over the place.
-use std::ops;
-
-use ra_arena::Arena;
-
-#[derive(Default)]
-pub struct Tree<T> {
-    nodes: Arena<Node<T>>,
-    current_path: Vec<(Idx<T>, Option<Idx<T>>)>,
-}
-
-pub type Idx<T> = ra_arena::Idx<Node<T>>;
-
-impl<T> Tree<T> {
-    pub fn start(&mut self)
-    where
-        T: Default,
-    {
-        let me = self.nodes.alloc(Node::new(T::default()));
-        if let Some((parent, last_child)) = self.current_path.last_mut() {
-            let slot = match *last_child {
-                Some(last_child) => &mut self.nodes[last_child].next_sibling,
-                None => &mut self.nodes[*parent].first_child,
-            };
-            let prev = slot.replace(me);
-            assert!(prev.is_none());
-            *last_child = Some(me);
-        }
-
-        self.current_path.push((me, None));
-    }
-
-    pub fn finish(&mut self, data: T) {
-        let (me, _last_child) = self.current_path.pop().unwrap();
-        self.nodes[me].data = data;
-    }
-
-    pub fn root(&self) -> Option<Idx<T>> {
-        self.nodes.iter().next().map(|(idx, _)| idx)
-    }
-
-    pub fn children(&self, idx: Idx<T>) -> impl Iterator<Item = Idx<T>> + '_ {
-        NodeIter { nodes: &self.nodes, next: self.nodes[idx].first_child }
-    }
-    pub fn clear(&mut self) {
-        self.nodes.clear();
-        self.current_path.clear();
-    }
-}
-
-impl<T> ops::Index<Idx<T>> for Tree<T> {
-    type Output = T;
-    fn index(&self, index: Idx<T>) -> &T {
-        &self.nodes[index].data
-    }
-}
-
-pub struct Node<T> {
-    data: T,
-    first_child: Option<Idx<T>>,
-    next_sibling: Option<Idx<T>>,
-}
-
-impl<T> Node<T> {
-    fn new(data: T) -> Node<T> {
-        Node { data, first_child: None, next_sibling: None }
-    }
-}
-
-struct NodeIter<'a, T> {
-    nodes: &'a Arena<Node<T>>,
-    next: Option<Idx<T>>,
-}
-
-impl<'a, T> Iterator for NodeIter<'a, T> {
-    type Item = Idx<T>;
-
-    fn next(&mut self) -> Option<Idx<T>> {
-        self.next.map(|next| {
-            self.next = self.nodes[next].next_sibling;
-            next
-        })
-    }
-}
diff --git a/crates/rust-analyzer/src/cargo_target_spec.rs b/crates/rust-analyzer/src/cargo_target_spec.rs
index 10c25666a0..c2ece49f42 100644
--- a/crates/rust-analyzer/src/cargo_target_spec.rs
+++ b/crates/rust-analyzer/src/cargo_target_spec.rs
@@ -9,7 +9,6 @@ use crate::{world::WorldSnapshot, Result};
 ///
 /// We use it to cook up the set of cli args we need to pass to Cargo to
 /// build/test/run the target.
-#[derive(Clone)]
 pub(crate) struct CargoTargetSpec {
     pub(crate) package: String,
     pub(crate) target: String,
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 74a63e32a3..715eddadb6 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -69,7 +69,6 @@ pub enum RustfmtConfig {
 pub struct ClientCapsConfig {
     pub location_link: bool,
     pub line_folding_only: bool,
-    pub hierarchical_symbols: bool,
 }
 
 impl Default for Config {
@@ -216,11 +215,6 @@ impl Config {
         if let Some(value) = caps.folding_range.as_ref().and_then(|it| it.line_folding_only) {
             self.client_caps.line_folding_only = value
         }
-        if let Some(value) =
-            caps.document_symbol.as_ref().and_then(|it| it.hierarchical_document_symbol_support)
-        {
-            self.client_caps.hierarchical_symbols = value
-        }
         self.completion.allow_snippets(false);
         if let Some(completion) = &caps.completion {
             if let Some(completion_item) = &completion.completion_item {
diff --git a/crates/rust-analyzer/src/conv.rs b/crates/rust-analyzer/src/conv.rs
index ffe3ea84d7..b0f911f713 100644
--- a/crates/rust-analyzer/src/conv.rs
+++ b/crates/rust-analyzer/src/conv.rs
@@ -25,8 +25,7 @@ use crate::{
     Result,
 };
 use semantic_tokens::{
-    ATTRIBUTE, BUILTIN_TYPE, ENUM_MEMBER, FORMAT_SPECIFIER, LIFETIME, TYPE_ALIAS, UNION,
-    UNRESOLVED_REFERENCE,
+    ATTRIBUTE, BUILTIN_TYPE, ENUM_MEMBER, LIFETIME, TYPE_ALIAS, UNION, UNRESOLVED_REFERENCE,
 };
 
 pub trait Conv {
@@ -382,7 +381,6 @@ impl Conv for Highlight {
             HighlightTag::Attribute => ATTRIBUTE,
             HighlightTag::Keyword => SemanticTokenType::KEYWORD,
             HighlightTag::UnresolvedReference => UNRESOLVED_REFERENCE,
-            HighlightTag::FormatSpecifier => FORMAT_SPECIFIER,
         };
 
         for modifier in self.modifiers.iter() {
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs
index 6caaf5f88d..c2c1a23cdf 100644
--- a/crates/rust-analyzer/src/main_loop/handlers.rs
+++ b/crates/rust-analyzer/src/main_loop/handlers.rs
@@ -16,7 +16,7 @@ use lsp_types::{
     Hover, HoverContents, Location, MarkupContent, MarkupKind, Position, PrepareRenameResponse,
     Range, RenameParams, SemanticTokensParams, SemanticTokensRangeParams,
     SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, TextDocumentIdentifier,
-    TextEdit, Url, WorkspaceEdit,
+    TextEdit, WorkspaceEdit,
 };
 use ra_ide::{
     Assist, AssistId, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind,
@@ -219,7 +219,6 @@ pub fn handle_document_symbol(
     let _p = profile("handle_document_symbol");
     let file_id = params.text_document.try_conv_with(&world)?;
     let line_index = world.analysis().file_line_index(file_id)?;
-    let url = file_id.try_conv_with(&world)?;
 
     let mut parents: Vec<(DocumentSymbol, Option<usize>)> = Vec::new();
 
@@ -235,10 +234,10 @@ pub fn handle_document_symbol(
         };
         parents.push((doc_symbol, symbol.parent));
     }
-    let mut document_symbols = Vec::new();
+    let mut res = Vec::new();
     while let Some((node, parent)) = parents.pop() {
         match parent {
-            None => document_symbols.push(node),
+            None => res.push(node),
             Some(i) => {
                 let children = &mut parents[i].0.children;
                 if children.is_none() {
@@ -249,35 +248,7 @@ pub fn handle_document_symbol(
         }
     }
 
-    if world.config.client_caps.hierarchical_symbols {
-        Ok(Some(document_symbols.into()))
-    } else {
-        let mut symbol_information = Vec::<SymbolInformation>::new();
-        for symbol in document_symbols {
-            flatten_document_symbol(&symbol, None, &url, &mut symbol_information);
-        }
-
-        Ok(Some(symbol_information.into()))
-    }
-}
-
-fn flatten_document_symbol(
-    symbol: &DocumentSymbol,
-    container_name: Option<String>,
-    url: &Url,
-    res: &mut Vec<SymbolInformation>,
-) {
-    res.push(SymbolInformation {
-        name: symbol.name.clone(),
-        kind: symbol.kind,
-        deprecated: symbol.deprecated,
-        location: Location::new(url.clone(), symbol.range),
-        container_name: container_name,
-    });
-
-    for child in symbol.children.iter().flatten() {
-        flatten_document_symbol(child, Some(symbol.name.clone()), url, res);
-    }
+    Ok(Some(res.into()))
 }
 
 pub fn handle_workspace_symbol(
@@ -393,37 +364,28 @@ pub fn handle_runnables(
         }
         res.push(to_lsp_runnable(&world, file_id, runnable)?);
     }
-    // Add `cargo check` and `cargo test` for the whole package
+    let mut check_args = vec!["check".to_string()];
+    let label;
     match CargoTargetSpec::for_file(&world, file_id)? {
         Some(spec) => {
-            for &cmd in ["check", "test"].iter() {
-                res.push(req::Runnable {
-                    range: Default::default(),
-                    label: format!("cargo {} -p {}", cmd, spec.package),
-                    bin: "cargo".to_string(),
-                    args: {
-                        let mut args = vec![cmd.to_string()];
-                        spec.clone().push_to(&mut args);
-                        args
-                    },
-                    extra_args: Vec::new(),
-                    env: FxHashMap::default(),
-                    cwd: workspace_root.map(|root| root.to_owned()),
-                })
-            }
+            label = format!("cargo check -p {}", spec.package);
+            spec.push_to(&mut check_args);
         }
         None => {
-            res.push(req::Runnable {
-                range: Default::default(),
-                label: "cargo check --workspace".to_string(),
-                bin: "cargo".to_string(),
-                args: vec!["check".to_string(), "--workspace".to_string()],
-                extra_args: Vec::new(),
-                env: FxHashMap::default(),
-                cwd: workspace_root.map(|root| root.to_owned()),
-            });
+            label = "cargo check --all".to_string();
+            check_args.push("--all".to_string())
         }
     }
+    // Always add `cargo check`.
+    res.push(req::Runnable {
+        range: Default::default(),
+        label,
+        bin: "cargo".to_string(),
+        args: check_args,
+        extra_args: Vec::new(),
+        env: FxHashMap::default(),
+        cwd: workspace_root.map(|root| root.to_owned()),
+    });
     Ok(res)
 }
 
diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs
index 71f4f58a32..10fe696f6f 100644
--- a/crates/rust-analyzer/src/semantic_tokens.rs
+++ b/crates/rust-analyzer/src/semantic_tokens.rs
@@ -12,7 +12,6 @@ pub(crate) const TYPE_ALIAS: SemanticTokenType = SemanticTokenType::new("typeAli
 pub(crate) const UNION: SemanticTokenType = SemanticTokenType::new("union");
 pub(crate) const UNRESOLVED_REFERENCE: SemanticTokenType =
     SemanticTokenType::new("unresolvedReference");
-pub(crate) const FORMAT_SPECIFIER: SemanticTokenType = SemanticTokenType::new("formatSpecifier");
 
 pub(crate) const CONSTANT: SemanticTokenModifier = SemanticTokenModifier::new("constant");
 pub(crate) const CONTROL_FLOW: SemanticTokenModifier = SemanticTokenModifier::new("controlFlow");
@@ -47,7 +46,6 @@ pub(crate) const SUPPORTED_TYPES: &[SemanticTokenType] = &[
     TYPE_ALIAS,
     UNION,
     UNRESOLVED_REFERENCE,
-    FORMAT_SPECIFIER,
 ];
 
 pub(crate) const SUPPORTED_MODIFIERS: &[SemanticTokenModifier] = &[
diff --git a/docs/dev/architecture.md b/docs/dev/architecture.md
index 3a337c5746..0343b6c81f 100644
--- a/docs/dev/architecture.md
+++ b/docs/dev/architecture.md
@@ -56,7 +56,7 @@ In particular, `cargo xtask codegen` generates:
 2. [`ast/generated`](https://github.com/rust-analyzer/rust-analyzer/blob/a0be39296d2925972cacd9fbf8b5fb258fad6947/crates/ra_syntax/src/ast/generated.rs)
   -- AST data structure.
 
-3. [`doc_tests/generated`](https://github.com/rust-analyzer/rust-analyzer/blob/a0be39296d2925972cacd9fbf8b5fb258fad6947/crates/ra_assists/src/doc_tests/generated.rs),
+.3 [`doc_tests/generated`](https://github.com/rust-analyzer/rust-analyzer/blob/a0be39296d2925972cacd9fbf8b5fb258fad6947/crates/ra_assists/src/doc_tests/generated.rs),
   [`test_data/parser/inline`](https://github.com/rust-analyzer/rust-analyzer/tree/a0be39296d2925972cacd9fbf8b5fb258fad6947/crates/ra_syntax/test_data/parser/inline)
   -- tests for assists and the parser.
 
diff --git a/docs/user/readme.adoc b/docs/user/readme.adoc
index 76d065d35b..ce57048366 100644
--- a/docs/user/readme.adoc
+++ b/docs/user/readme.adoc
@@ -23,7 +23,7 @@ https://github.com/rust-analyzer/rust-analyzer/blob/master/docs/user/readme.adoc
 
 == Installation
 
-In theory, one should be able to just install the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>> and have it automatically work with any editor.
+In theory, one should be able to just install the server binary and have it automatically work with any editor.
 We are not there yet, so some editor specific setup is required.
 
 Additionally, rust-analyzer needs the sources of the standard library.
@@ -108,26 +108,15 @@ Here are some useful self-diagnostic commands:
 * To log all LSP requests, add `"rust-analyzer.trace.server": "verbose"` to the settings and look for `Server Trace` in the panel.
 * To enable client-side logging, add `"rust-analyzer.trace.extension": true` to the settings and open the `Console` tab of VS Code developer tools.
 
-=== rust-analyzer Language Server Binary
+=== Language Server Binary
 
 Other editors generally require the `rust-analyzer` binary to be in `$PATH`.
 You can download the pre-built binary from the https://github.com/rust-analyzer/rust-analyzer/releases[releases] page. Typically, you then need to rename the binary for your platform, e.g. `rust-analyzer-mac` if you're on Mac OS, to `rust-analzyer` and make it executable in addition to moving it into a directory in your `$PATH`.
 
-On Linux to install the `rust-analyzer` binary into `~/.local/bin`, this commands could be used
-
-[source,bash]
-----
-$ curl -L https://github.com/rust-analyzer/rust-analyzer/releases/latest/download/rust-analyzer-linux -o ~/.local/bin/rust-analyzer
-$ chmod +x ~/.local/bin/rust-analyzer
-----
-
-Ensure `~/.local/bin` is listed in the `$PATH` variable.
-
 Alternatively, you can install it from source using the following command:
 
 [source,bash]
 ----
-$ git clone https://github.com/rust-analyzer/rust-analyzer.git && cd rust-analyzer
 $ cargo xtask install --server
 ----
 
@@ -150,19 +139,15 @@ $ yay -S rust-analyzer-bin
 
 === Emacs
 
-Prerequisites: You have installed the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>>.
+Emacs support is maintained https://github.com/emacs-lsp/lsp-mode/blob/master/lsp-rust.el[upstream].
 
-Emacs support is maintained as part of the https://github.com/emacs-lsp/lsp-mode[Emacs-LSP] package in https://github.com/emacs-lsp/lsp-mode/blob/master/lsp-rust.el[lsp-rust.el].
-
-1. Install the most recent version of `emacs-lsp` package by following the https://github.com/emacs-lsp/lsp-mode[Emacs-LSP instructions].
+1. Install the most recent version of `emacs-lsp` package by following the instructions https://github.com/emacs-lsp/lsp-mode[here].
 2. Set `lsp-rust-server` to `'rust-analyzer`.
 3. Run `lsp` in a Rust buffer.
 4. (Optionally) bind commands like `lsp-rust-analyzer-join-lines`, `lsp-extend-selection` and `lsp-rust-analyzer-expand-macro` to keys.
 
 === Vim
 
-Prerequisites: You have installed the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>>.
-
 The are several LSP client implementations for vim:
 
 ==== coc-rust-analyzer
@@ -220,7 +205,7 @@ Once `neovim/nvim-lsp` is installed, use `+lua require'nvim_lsp'.rust_analyzer.s
 
 === Sublime Text 3
 
-Prerequisites: You have installed the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>>.
+Prerequisites: You have installed the <<language-server-binary,`rust-analyzer` binary>>.
 
 You also need the `LSP` package. To install it:
 
@@ -233,7 +218,7 @@ Finally, with your Rust project open, in the command palette, run `LSP: Enable L
 
 If it worked, you should see "rust-analzyer, Line X, Column Y" on the left side of the bottom bar, and after waiting a bit, functionality like tooltips on hovering over variables should become available.
 
-If you get an error saying `No such file or directory: 'rust-analyzer'`, see the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>> section on installing the language server binary.
+If you get an error saying `No such file or directory: 'rust-analyzer'`, see the <<language-server-binary,section on installing the language server binary>>.
 
 == Usage
 
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts
index cffdcf11ac..0ad4b63aeb 100644
--- a/editors/code/src/client.ts
+++ b/editors/code/src/client.ts
@@ -4,7 +4,7 @@ import * as vscode from 'vscode';
 import { CallHierarchyFeature } from 'vscode-languageclient/lib/callHierarchy.proposed';
 import { SemanticTokensFeature, DocumentSemanticsTokensSignature } from 'vscode-languageclient/lib/semanticTokens.proposed';
 
-export function createClient(serverPath: string, cwd: string): lc.LanguageClient {
+export async function createClient(serverPath: string, cwd: string): Promise<lc.LanguageClient> {
     // '.' Is the fallback if no folder is open
     // TODO?: Workspace folders support Uri's (eg: file://test.txt).
     // It might be a good idea to test if the uri points to a file.
@@ -42,6 +42,35 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient
         clientOptions,
     );
 
+    // HACK: This is an awful way of filtering out the decorations notifications
+    // However, pending proper support, this is the most effecitve approach
+    // Proper support for this would entail a change to vscode-languageclient to allow not notifying on certain messages
+    // Or the ability to disable the serverside component of highlighting (but this means that to do tracing we need to disable hihlighting)
+    // This also requires considering our settings strategy, which is work which needs doing
+    // @ts-ignore The tracer is private to vscode-languageclient, but we need access to it to not log publishDecorations requests
+    res._tracer = {
+        log: (messageOrDataObject: string | unknown, data?: string) => {
+            if (typeof messageOrDataObject === 'string') {
+                if (
+                    messageOrDataObject.includes(
+                        'rust-analyzer/publishDecorations',
+                    ) ||
+                    messageOrDataObject.includes(
+                        'rust-analyzer/decorationsRequest',
+                    )
+                ) {
+                    // Don't log publish decorations requests
+                } else {
+                    // @ts-ignore This is just a utility function
+                    res.logTrace(messageOrDataObject, data);
+                }
+            } else {
+                // @ts-ignore
+                res.logObjectTrace(messageOrDataObject);
+            }
+        },
+    };
+
     // To turn on all proposed features use: res.registerProposedFeatures();
     // Here we want to enable CallHierarchyFeature and SemanticTokensFeature
     // since they are available on stable.
diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts
index 41df119910..f7ed62d035 100644
--- a/editors/code/src/ctx.ts
+++ b/editors/code/src/ctx.ts
@@ -21,7 +21,7 @@ export class Ctx {
         serverPath: string,
         cwd: string,
     ): Promise<Ctx> {
-        const client = createClient(serverPath, cwd);
+        const client = await createClient(serverPath, cwd);
         const res = new Ctx(config, extCtx, client, serverPath);
         res.pushCleanup(client.start());
         await client.onReady();
diff --git a/xtask/src/dist.rs b/xtask/src/dist.rs
index a56eeef8d2..aef68089e1 100644
--- a/xtask/src/dist.rs
+++ b/xtask/src/dist.rs
@@ -50,21 +50,19 @@ fn dist_server(nightly: bool) -> Result<()> {
     if cfg!(target_os = "linux") {
         std::env::set_var("CC", "clang");
         run!(
-            "cargo build --manifest-path ./crates/rust-analyzer/Cargo.toml --bin rust-analyzer --release
-             --target x86_64-unknown-linux-musl
-            "
+            "cargo build --manifest-path ./crates/rust-analyzer/Cargo.toml --bin rust-analyzer --release"
             // We'd want to add, but that requires setting the right linker somehow
             // --features=jemalloc
         )?;
         if !nightly {
-            run!("strip ./target/x86_64-unknown-linux-musl/release/rust-analyzer")?;
+            run!("strip ./target/release/rust-analyzer")?;
         }
     } else {
         run!("cargo build --manifest-path ./crates/rust-analyzer/Cargo.toml --bin rust-analyzer --release")?;
     }
 
     let (src, dst) = if cfg!(target_os = "linux") {
-        ("./target/x86_64-unknown-linux-musl/release/rust-analyzer", "./dist/rust-analyzer-linux")
+        ("./target/release/rust-analyzer", "./dist/rust-analyzer-linux")
     } else if cfg!(target_os = "windows") {
         ("./target/release/rust-analyzer.exe", "./dist/rust-analyzer-windows.exe")
     } else if cfg!(target_os = "macos") {


Test patch:
diff --git a/crates/ra_hir_ty/src/tests.rs b/crates/ra_hir_ty/src/tests.rs
index 588d812820..b6a96bb5ca 100644
--- a/crates/ra_hir_ty/src/tests.rs
+++ b/crates/ra_hir_ty/src/tests.rs
@@ -390,38 +390,6 @@ fn no_such_field_with_feature_flag_diagnostics_on_struct_lit() {
     assert_snapshot!(diagnostics, @r###""###);
 }
 
-#[test]
-fn no_such_field_with_feature_flag_diagnostics_on_block_expr() {
-    let diagnostics = TestDB::with_files(
-        r#"
-        //- /lib.rs crate:foo cfg:feature=foo
-        struct S {
-            #[cfg(feature = "foo")]
-            foo: u32,
-            #[cfg(not(feature = "foo"))]
-            bar: u32,
-        }
-
-        impl S {
-            fn new(bar: u32) -> Self {
-                #[cfg(feature = "foo")]
-                {
-                Self { foo: bar }
-                }
-                #[cfg(not(feature = "foo"))]
-                {
-                Self { bar }
-                }
-            }
-        }
-        "#,
-    )
-    .diagnostics()
-    .0;
-
-    assert_snapshot!(diagnostics, @r###""###);
-}
-
 #[test]
 fn no_such_field_with_feature_flag_diagnostics_on_struct_fields() {
     let diagnostics = TestDB::with_files(
diff --git a/crates/ra_hir_ty/src/tests/macros.rs b/crates/ra_hir_ty/src/tests/macros.rs
index 5ddecbdc68..6b52672325 100644
--- a/crates/ra_hir_ty/src/tests/macros.rs
+++ b/crates/ra_hir_ty/src/tests/macros.rs
@@ -622,14 +622,14 @@ fn main() {
 fn infer_derive_clone_simple() {
     let (db, pos) = TestDB::with_position(
         r#"
-//- /main.rs crate:main deps:core
+//- /main.rs crate:main deps:std
 #[derive(Clone)]
 struct S;
 fn test() {
     S.clone()<|>;
 }
 
-//- /lib.rs crate:core
+//- /lib.rs crate:std
 #[prelude_import]
 use clone::*;
 mod clone {
@@ -642,36 +642,11 @@ mod clone {
     assert_eq!("S", type_at_pos(&db, pos));
 }
 
-#[test]
-fn infer_derive_clone_in_core() {
-    let (db, pos) = TestDB::with_position(
-        r#"
-//- /lib.rs crate:core
-#[prelude_import]
-use clone::*;
-mod clone {
-    trait Clone {
-        fn clone(&self) -> Self;
-    }
-}
-#[derive(Clone)]
-pub struct S;
-
-//- /main.rs crate:main deps:core
-use core::S;
-fn test() {
-    S.clone()<|>;
-}
-"#,
-    );
-    assert_eq!("S", type_at_pos(&db, pos));
-}
-
 #[test]
 fn infer_derive_clone_with_params() {
     let (db, pos) = TestDB::with_position(
         r#"
-//- /main.rs crate:main deps:core
+//- /main.rs crate:main deps:std
 #[derive(Clone)]
 struct S;
 #[derive(Clone)]
@@ -681,7 +656,7 @@ fn test() {
     (Wrapper(S).clone(), Wrapper(NonClone).clone())<|>;
 }
 
-//- /lib.rs crate:core
+//- /lib.rs crate:std
 #[prelude_import]
 use clone::*;
 mod clone {
diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs
index e555c879a0..f51cdd4964 100644
--- a/crates/ra_hir_ty/src/tests/traits.rs
+++ b/crates/ra_hir_ty/src/tests/traits.rs
@@ -1897,36 +1897,6 @@ fn test() {
     assert_eq!(t, "u32");
 }
 
-#[test]
-fn unselected_projection_chalk_fold() {
-    let t = type_at(
-        r#"
-//- /main.rs
-trait Interner {}
-trait Fold<I: Interner, TI = I> {
-    type Result;
-}
-
-struct Ty<I: Interner> {}
-impl<I: Interner, TI: Interner> Fold<I, TI> for Ty<I> {
-    type Result = Ty<TI>;
-}
-
-fn fold<I: Interner, T>(interner: &I, t: T) -> T::Result
-where
-    T: Fold<I, I>,
-{
-    loop {}
-}
-
-fn foo<I: Interner>(interner: &I, t: Ty<I>) {
-    fold(interner, t)<|>;
-}
-"#,
-    );
-    assert_eq!(t, "Ty<I>");
-}
-
 #[test]
 fn trait_impl_self_ty() {
     let t = type_at(
diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs
index f6245ddd47..b31533e5ef 100644
--- a/crates/rust-analyzer/tests/heavy_tests/main.rs
+++ b/crates/rust-analyzer/tests/heavy_tests/main.rs
@@ -87,15 +87,24 @@ fn foo() {
             }
           },
           {
-            "args": ["check", "--workspace"],
+            "args": [
+              "check",
+              "--all"
+            ],
             "extraArgs": [],
             "bin": "cargo",
             "env": {},
             "cwd": null,
-            "label": "cargo check --workspace",
+            "label": "cargo check --all",
             "range": {
-              "end": { "character": 0, "line": 0 },
-              "start": { "character": 0, "line": 0 }
+              "end": {
+                "character": 0,
+                "line": 0
+              },
+              "start": {
+                "character": 0,
+                "line": 0
+              }
             }
           }
         ]),
@@ -136,42 +145,42 @@ fn main() {}
     server.request::<Runnables>(
         RunnablesParams { text_document: server.doc_id("foo/tests/spam.rs"), position: None },
         json!([
-            {
-              "args": [ "test", "--package", "foo", "--test", "spam" ],
-              "extraArgs": [ "test_eggs", "--exact", "--nocapture" ],
-              "bin": "cargo",
-              "env": { "RUST_BACKTRACE": "short" },
-              "label": "test test_eggs",
-              "range": {
-                "end": { "character": 17, "line": 1 },
-                "start": { "character": 0, "line": 0 }
-              },
-              "cwd": server.path().join("foo")
-            },
-            {
-              "args": [ "check", "--package", "foo", "--test", "spam" ],
-              "extraArgs": [],
-              "bin": "cargo",
-              "env": {},
-              "label": "cargo check -p foo",
-              "range": {
-                "end": { "character": 0, "line": 0 },
-                "start": { "character": 0, "line": 0 }
-              },
-              "cwd": server.path().join("foo")
+          {
+            "args": [ "test", "--package", "foo", "--test", "spam" ],
+            "extraArgs": [ "test_eggs", "--exact", "--nocapture" ],
+            "bin": "cargo",
+            "env": { "RUST_BACKTRACE": "short" },
+            "label": "test test_eggs",
+            "range": {
+              "end": { "character": 17, "line": 1 },
+              "start": { "character": 0, "line": 0 }
             },
-            {
-              "args": [ "test", "--package", "foo", "--test", "spam" ],
-              "extraArgs": [],
-              "bin": "cargo",
-              "env": {},
-              "label": "cargo test -p foo",
-              "range": {
-                "end": { "character": 0, "line": 0 },
-                "start": { "character": 0, "line": 0 }
+            "cwd": server.path().join("foo")
+          },
+          {
+            "args": [
+              "check",
+              "--package",
+              "foo",
+              "--test",
+              "spam"
+            ],
+            "extraArgs": [],
+            "bin": "cargo",
+            "env": {},
+            "cwd": server.path().join("foo"),
+            "label": "cargo check -p foo",
+            "range": {
+              "end": {
+                "character": 0,
+                "line": 0
               },
-              "cwd": server.path().join("foo")
+              "start": {
+                "character": 0,
+                "line": 0
+              }
             }
+          }
         ]),
     );
 }
diff --git a/crates/rust-analyzer/tests/heavy_tests/support.rs b/crates/rust-analyzer/tests/heavy_tests/support.rs
index e4fe3411aa..7eebedff7a 100644
--- a/crates/rust-analyzer/tests/heavy_tests/support.rs
+++ b/crates/rust-analyzer/tests/heavy_tests/support.rs
@@ -62,7 +62,11 @@ impl<'a> Project<'a> {
         static INIT: Once = Once::new();
         INIT.call_once(|| {
             env_logger::builder().is_test(true).try_init().unwrap();
-            ra_prof::init_from(crate::PROFILE);
+            ra_prof::set_filter(if crate::PROFILE.is_empty() {
+                ra_prof::Filter::disabled()
+            } else {
+                ra_prof::Filter::from_spec(&crate::PROFILE)
+            });
         });
 
         let mut paths = vec![];