Repository: rust-analyzer/rust-analyzer
Pull request #4166 (2020-04-26 22:14:54)

Defining a default target to support cross-compilation targets 
Related to #4163 


Patch:
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_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_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/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs
index 362ee30fe0..59f46a2a05 100644
--- a/crates/ra_project_model/src/cargo_workspace.rs
+++ b/crates/ra_project_model/src/cargo_workspace.rs
@@ -56,6 +56,9 @@ pub struct CargoConfig {
 
     /// Runs cargo check on launch to figure out the correct values of OUT_DIR
     pub load_out_dirs_from_check: bool,
+
+    /// rustc target
+    pub target: Option<String>,
 }
 
 impl Default for CargoConfig {
@@ -65,6 +68,7 @@ impl Default for CargoConfig {
             all_features: true,
             features: Vec::new(),
             load_out_dirs_from_check: false,
+            target: None,
         }
     }
 }
@@ -160,6 +164,9 @@ impl CargoWorkspace {
         if let Some(parent) = cargo_toml.parent() {
             meta.current_dir(parent);
         }
+        if let Some(target) = cargo_features.target.as_ref() {
+            meta.other_options(&[String::from("--filter-platform"), target.clone()]);
+        }
         let meta = meta.exec().with_context(|| {
             format!("Failed to run `cargo metadata --manifest-path {}`", cargo_toml.display())
         })?;
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs
index 731cbd2918..c2b33c1dca 100644
--- a/crates/ra_project_model/src/lib.rs
+++ b/crates/ra_project_model/src/lib.rs
@@ -543,7 +543,7 @@ impl ProjectWorkspace {
     }
 }
 
-pub fn get_rustc_cfg_options() -> CfgOptions {
+pub fn get_rustc_cfg_options(target: Option<&String>) -> CfgOptions {
     let mut cfg_options = CfgOptions::default();
 
     // Some nightly-only cfgs, which are required for stdlib
@@ -558,10 +558,12 @@ pub fn get_rustc_cfg_options() -> CfgOptions {
 
     match (|| -> Result<String> {
         // `cfg(test)` and `cfg(debug_assertion)` are handled outside, so we suppress them here.
-        let output = Command::new("rustc")
-            .args(&["--print", "cfg", "-O"])
-            .output()
-            .context("Failed to get output from rustc --print cfg -O")?;
+        let mut cmd = Command::new("rustc");
+        cmd.args(&["--print", "cfg", "-O"]);
+        if let Some(target) = target {
+            cmd.args(&["--target", target.as_str()]);
+        }
+        let output = cmd.output().context("Failed to get output from rustc --print cfg -O")?;
         if !output.status.success() {
             bail!(
                 "rustc --print cfg -O exited with exit code ({})",
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/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs
index d0a71120aa..023ced6cfb 100644
--- a/crates/rust-analyzer/src/cli/load_cargo.rs
+++ b/crates/rust-analyzer/src/cli/load_cargo.rs
@@ -149,7 +149,7 @@ pub(crate) fn load(
 
     // FIXME: cfg options?
     let default_cfg_options = {
-        let mut opts = get_rustc_cfg_options();
+        let mut opts = get_rustc_cfg_options(None);
         opts.insert_atom("test".into());
         opts.insert_atom("debug_assertion".into());
         opts
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 74a63e32a3..4b2570d4db 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 {
@@ -135,6 +134,7 @@ impl Config {
         set(value, "/cargo/allFeatures", &mut self.cargo.all_features);
         set(value, "/cargo/features", &mut self.cargo.features);
         set(value, "/cargo/loadOutDirsFromCheck", &mut self.cargo.load_out_dirs_from_check);
+        set(value, "/cargo/target", &mut self.cargo.target);
 
         match get(value, "/procMacro/enable") {
             Some(true) => {
@@ -216,11 +216,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/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/world.rs b/crates/rust-analyzer/src/world.rs
index 34941931b7..16020648df 100644
--- a/crates/rust-analyzer/src/world.rs
+++ b/crates/rust-analyzer/src/world.rs
@@ -131,7 +131,7 @@ impl WorldState {
 
         // FIXME: Read default cfgs from config
         let default_cfg_options = {
-            let mut opts = get_rustc_cfg_options();
+            let mut opts = get_rustc_cfg_options(config.cargo.target.as_ref());
             opts.insert_atom("test".into());
             opts.insert_atom("debug_assertion".into());
             opts
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/package.json b/editors/code/package.json
index b8aaa07d87..d363e9fbb1 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -237,6 +237,14 @@
                     "default": false,
                     "markdownDescription": "Run `cargo check` on startup to get the correct value for package OUT_DIRs"
                 },
+                "rust-analyzer.cargo.target": {
+                    "type": [
+                        "null",
+                        "string"
+                    ],
+                    "default": null,
+                    "description": "Specify the compilation target"
+                },
                 "rust-analyzer.rustfmt.extraArgs": {
                     "type": "array",
                     "items": {
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();


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/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![];