Repository: rust-analyzer/rust-analyzer
Pull request #8267 (2021-03-30 23:26:30)

Adding gifs and screenshots for features in manual
For #6539

This includes most of gif or screenshot examples of most items in the "Features" header. With the exceptions of:

- **On Typing Assists** - couldn't get it to work for a demo, I'm probably missing something?
- **Structural search and replace** - looked to be already a visual example of the feature
- **Workspace symbol** - wasn't sure how best to show this, all of the examples maybe? Also wasn't sure of the best code example to show it off
- **Semantic Syntax Highlighting** - seemed obvious enough to not need a screenshot, but I could easily add this

All the gifs/pngs are hosted in this [comment](https://github.com/rust-analyzer/rust-analyzer/issues/6539#issuecomment-809574840). Please let me know if any of them aren't suitable (and why) and I'll improve it! Or if you don't like the theme/font


Patch:
diff --git a/crates/base_db/src/lib.rs b/crates/base_db/src/lib.rs
index 980a0ed985..5f77a0b1f0 100644
--- a/crates/base_db/src/lib.rs
+++ b/crates/base_db/src/lib.rs
@@ -59,8 +59,6 @@ pub trait CheckCanceled {
         Self: Sized + panic::RefUnwindSafe,
         F: FnOnce(&Self) -> T + panic::UnwindSafe,
     {
-        // Uncomment to debug missing cancellations.
-        // let _span = profile::heartbeat_span();
         panic::catch_unwind(|| f(self)).map_err(|err| match err.downcast::<Canceled>() {
             Ok(canceled) => *canceled,
             Err(payload) => panic::resume_unwind(payload),
@@ -70,7 +68,6 @@ pub trait CheckCanceled {
 
 impl<T: salsa::Database> CheckCanceled for T {
     fn check_canceled(&self) {
-        // profile::heartbeat();
         if self.salsa_runtime().is_current_revision_canceled() {
             Canceled::throw()
         }
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 97f1623153..4ee08ef212 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -44,7 +44,6 @@ use hir_def::{
     per_ns::PerNs,
     resolver::{HasResolver, Resolver},
     src::HasSource as _,
-    type_ref::TraitRef,
     AdtId, AssocContainerId, AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId,
     DefWithBodyId, EnumId, FunctionId, GenericDefId, HasModule, ImplId, LifetimeParamId,
     LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StaticId, StructId, TraitId, TypeAliasId,
@@ -1574,9 +1573,9 @@ impl Impl {
         };
 
         let filter = |impl_def: &Impl| {
-            let self_ty = impl_def.self_ty(db);
-            let rref = self_ty.remove_ref();
-            ty.equals_ctor(rref.as_ref().map_or(&self_ty.ty, |it| &it.ty))
+            let target_ty = impl_def.target_ty(db);
+            let rref = target_ty.remove_ref();
+            ty.equals_ctor(rref.as_ref().map_or(&target_ty.ty, |it| &it.ty))
         };
 
         let mut all = Vec::new();
@@ -1614,16 +1613,16 @@ impl Impl {
 
     // FIXME: the return type is wrong. This should be a hir version of
     // `TraitRef` (ie, resolved `TypeRef`).
-    pub fn trait_(self, db: &dyn HirDatabase) -> Option<TraitRef> {
+    pub fn target_trait(self, db: &dyn HirDatabase) -> Option<TypeRef> {
         db.impl_data(self.id).target_trait.clone()
     }
 
-    pub fn self_ty(self, db: &dyn HirDatabase) -> Type {
+    pub fn target_ty(self, db: &dyn HirDatabase) -> Type {
         let impl_data = db.impl_data(self.id);
         let resolver = self.id.resolver(db.upcast());
         let krate = self.id.lookup(db.upcast()).container.krate();
         let ctx = hir_ty::TyLoweringContext::new(db, &resolver);
-        let ty = ctx.lower_ty(&impl_data.self_ty);
+        let ty = ctx.lower_ty(&impl_data.target_type);
         Type::new_with_resolver_inner(db, krate, &resolver, ty)
     }
 
diff --git a/crates/hir_def/src/data.rs b/crates/hir_def/src/data.rs
index 214bcc6483..0be868ba20 100644
--- a/crates/hir_def/src/data.rs
+++ b/crates/hir_def/src/data.rs
@@ -10,7 +10,7 @@ use crate::{
     body::Expander,
     db::DefDatabase,
     item_tree::{AssocItem, FunctionQualifier, ItemTreeId, ModItem, Param},
-    type_ref::{TraitRef, TypeBound, TypeRef},
+    type_ref::{TypeBound, TypeRef},
     visibility::RawVisibility,
     AssocContainerId, AssocItemId, ConstId, ConstLoc, FunctionId, FunctionLoc, HasModule, ImplId,
     Intern, Lookup, ModuleId, StaticId, TraitId, TypeAliasId, TypeAliasLoc,
@@ -156,8 +156,8 @@ impl TraitData {
 
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub struct ImplData {
-    pub target_trait: Option<TraitRef>,
-    pub self_ty: TypeRef,
+    pub target_trait: Option<TypeRef>,
+    pub target_type: TypeRef,
     pub items: Vec<AssocItemId>,
     pub is_negative: bool,
 }
@@ -170,7 +170,7 @@ impl ImplData {
         let item_tree = impl_loc.id.item_tree(db);
         let impl_def = &item_tree[impl_loc.id.value];
         let target_trait = impl_def.target_trait.map(|id| item_tree[id].clone());
-        let self_ty = item_tree[impl_def.self_ty].clone();
+        let target_type = item_tree[impl_def.target_type].clone();
         let is_negative = impl_def.is_negative;
         let module_id = impl_loc.container;
         let container = AssocContainerId::ImplId(id);
@@ -187,7 +187,7 @@ impl ImplData {
         );
         let items = items.into_iter().map(|(_, item)| item).collect();
 
-        Arc::new(ImplData { target_trait, self_ty, items, is_negative })
+        Arc::new(ImplData { target_trait, target_type, items, is_negative })
     }
 }
 
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs
index 5449bbf5d0..ca0048b163 100644
--- a/crates/hir_def/src/item_tree.rs
+++ b/crates/hir_def/src/item_tree.rs
@@ -31,7 +31,7 @@ use crate::{
     db::DefDatabase,
     generics::GenericParams,
     path::{path, AssociatedTypeBinding, GenericArgs, ImportAlias, ModPath, Path, PathKind},
-    type_ref::{Mutability, TraitRef, TypeBound, TypeRef},
+    type_ref::{Mutability, TypeBound, TypeRef},
     visibility::RawVisibility,
 };
 
@@ -147,7 +147,6 @@ impl ItemTree {
                 vis,
                 generics,
                 type_refs,
-                trait_refs,
                 inner_items,
             } = &mut **data;
 
@@ -174,7 +173,6 @@ impl ItemTree {
             generics.arena.shrink_to_fit();
             type_refs.arena.shrink_to_fit();
             type_refs.map.shrink_to_fit();
-            trait_refs.map.shrink_to_fit();
 
             inner_items.shrink_to_fit();
         }
@@ -297,32 +295,6 @@ impl TypeRefStorage {
     }
 }
 
-/// `TraitRef` interner.
-#[derive(Default, Debug, Eq, PartialEq)]
-struct TraitRefStorage {
-    arena: Arena<Arc<TraitRef>>,
-    map: FxHashMap<Arc<TraitRef>, Idx<Arc<TraitRef>>>,
-}
-
-impl TraitRefStorage {
-    // Note: We lie about the `Idx<TraitRef>` to hide the interner details.
-
-    fn intern(&mut self, ty: TraitRef) -> Idx<TraitRef> {
-        if let Some(id) = self.map.get(&ty) {
-            return Idx::from_raw(id.into_raw());
-        }
-
-        let ty = Arc::new(ty);
-        let idx = self.arena.alloc(ty.clone());
-        self.map.insert(ty, idx);
-        Idx::from_raw(idx.into_raw())
-    }
-
-    fn lookup(&self, id: Idx<TraitRef>) -> &TraitRef {
-        &self.arena[Idx::from_raw(id.into_raw())]
-    }
-}
-
 #[derive(Default, Debug, Eq, PartialEq)]
 struct ItemTreeData {
     imports: Arena<Import>,
@@ -347,7 +319,6 @@ struct ItemTreeData {
     vis: ItemVisibilities,
     generics: GenericParamsStorage,
     type_refs: TypeRefStorage,
-    trait_refs: TraitRefStorage,
 
     inner_items: FxHashMap<FileAstId<ast::BlockExpr>, SmallVec<[ModItem; 1]>>,
 }
@@ -585,14 +556,6 @@ impl Index<Idx<TypeRef>> for ItemTree {
     }
 }
 
-impl Index<Idx<TraitRef>> for ItemTree {
-    type Output = TraitRef;
-
-    fn index(&self, id: Idx<TraitRef>) -> &Self::Output {
-        self.data().trait_refs.lookup(id)
-    }
-}
-
 impl<N: ItemTreeNode> Index<FileItemTreeId<N>> for ItemTree {
     type Output = N;
     fn index(&self, id: FileItemTreeId<N>) -> &N {
@@ -729,8 +692,8 @@ pub struct Trait {
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub struct Impl {
     pub generic_params: GenericParamsId,
-    pub target_trait: Option<Idx<TraitRef>>,
-    pub self_ty: Idx<TypeRef>,
+    pub target_trait: Option<Idx<TypeRef>>,
+    pub target_type: Idx<TypeRef>,
     pub is_negative: bool,
     pub items: Box<[AssocItem]>,
     pub ast_id: FileAstId<ast::Impl>,
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs
index 8d38628111..3f558edd81 100644
--- a/crates/hir_def/src/item_tree/lower.rs
+++ b/crates/hir_def/src/item_tree/lower.rs
@@ -11,7 +11,7 @@ use syntax::{
 
 use crate::{
     generics::{GenericParams, TypeParamData, TypeParamProvenance},
-    type_ref::{LifetimeRef, TraitRef},
+    type_ref::LifetimeRef,
 };
 
 use super::*;
@@ -536,11 +536,8 @@ impl Ctx {
     fn lower_impl(&mut self, impl_def: &ast::Impl) -> Option<FileItemTreeId<Impl>> {
         let generic_params =
             self.lower_generic_params_and_inner_items(GenericsOwner::Impl, impl_def);
-        // FIXME: If trait lowering fails, due to a non PathType for example, we treat this impl
-        // as if it was an non-trait impl. Ideally we want to create a unique missing ref that only
-        // equals itself.
-        let target_trait = impl_def.trait_().and_then(|tr| self.lower_trait_ref(&tr));
-        let self_ty = self.lower_type_ref(&impl_def.self_ty()?);
+        let target_trait = impl_def.trait_().map(|tr| self.lower_type_ref(&tr));
+        let target_type = self.lower_type_ref(&impl_def.self_ty()?);
         let is_negative = impl_def.excl_token().is_some();
 
         // We cannot use `assoc_items()` here as that does not include macro calls.
@@ -557,7 +554,7 @@ impl Ctx {
             })
             .collect();
         let ast_id = self.source_ast_id_map.ast_id(impl_def);
-        let res = Impl { generic_params, target_trait, self_ty, is_negative, items, ast_id };
+        let res = Impl { generic_params, target_trait, target_type, is_negative, items, ast_id };
         Some(id(self.data().impls.alloc(res)))
     }
 
@@ -743,16 +740,10 @@ impl Ctx {
         self.data().vis.alloc(vis)
     }
 
-    fn lower_trait_ref(&mut self, trait_ref: &ast::Type) -> Option<Idx<TraitRef>> {
-        let trait_ref = TraitRef::from_ast(&self.body_ctx, trait_ref.clone())?;
-        Some(self.data().trait_refs.intern(trait_ref))
-    }
-
     fn lower_type_ref(&mut self, type_ref: &ast::Type) -> Idx<TypeRef> {
         let tyref = TypeRef::from_ast(&self.body_ctx, type_ref.clone());
         self.data().type_refs.intern(tyref)
     }
-
     fn lower_type_ref_opt(&mut self, type_ref: Option<ast::Type>) -> Idx<TypeRef> {
         match type_ref.map(|ty| self.lower_type_ref(&ty)) {
             Some(it) => it,
diff --git a/crates/hir_def/src/type_ref.rs b/crates/hir_def/src/type_ref.rs
index 4c24aae94f..049b2e462e 100644
--- a/crates/hir_def/src/type_ref.rs
+++ b/crates/hir_def/src/type_ref.rs
@@ -51,23 +51,6 @@ impl Rawness {
     }
 }
 
-#[derive(Clone, PartialEq, Eq, Hash, Debug)]
-pub struct TraitRef {
-    pub path: Path,
-}
-
-impl TraitRef {
-    /// Converts an `ast::PathType` to a `hir::TraitRef`.
-    pub(crate) fn from_ast(ctx: &LowerCtx, node: ast::Type) -> Option<Self> {
-        // FIXME: Use `Path::from_src`
-        match node {
-            ast::Type::PathType(path) => {
-                path.path().and_then(|it| ctx.lower_path(it)).map(|path| TraitRef { path })
-            }
-            _ => None,
-        }
-    }
-}
 /// Compare ty::Ty
 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
 pub enum TypeRef {
diff --git a/crates/hir_ty/src/diagnostics/match_check.rs b/crates/hir_ty/src/diagnostics/match_check.rs
index 9cb472b515..5a5cdcbf3a 100644
--- a/crates/hir_ty/src/diagnostics/match_check.rs
+++ b/crates/hir_ty/src/diagnostics/match_check.rs
@@ -539,7 +539,7 @@ impl Matrix {
         if let Some(Pat::Or(pat_ids)) = row.get_head().map(|pat_id| pat_id.as_pat(cx)) {
             // Or patterns are expanded here
             for pat_id in pat_ids {
-                self.0.push(row.replace_head_with([pat_id].iter()));
+                self.0.push(PatStack::from_pattern(pat_id));
             }
         } else {
             self.0.push(row);
@@ -1084,20 +1084,6 @@ fn main() {
         );
     }
 
-    #[test]
-    fn or_pattern_no_diagnostic() {
-        check_diagnostics(
-            r#"
-enum Either {A, B}
-
-fn main() {
-    match (Either::A, Either::B) {
-        (Either::A | Either::B, _) => (),
-    }
-}"#,
-        )
-    }
-
     #[test]
     fn mismatched_types() {
         // Match statements with arms that don't match the
@@ -1349,6 +1335,30 @@ fn bang(never: !) {
         );
     }
 
+    #[test]
+    fn or_pattern_panic() {
+        check_diagnostics(
+            r#"
+pub enum Category { Infinity, Zero }
+
+fn panic(a: Category, b: Category) {
+    match (a, b) {
+        (Category::Zero | Category::Infinity, _) => (),
+        (_, Category::Zero | Category::Infinity) => (),
+    }
+
+    // FIXME: This is a false positive, but the code used to cause a panic in the match checker,
+    // so this acts as a regression test for that.
+    match (a, b) {
+        //^^^^^^ Missing match arm
+        (Category::Infinity, Category::Infinity) | (Category::Zero, Category::Zero) => (),
+        (Category::Infinity | Category::Zero, _) => (),
+    }
+}
+"#,
+        );
+    }
+
     #[test]
     fn unknown_type() {
         check_diagnostics(
diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs
index 6279aa572c..e6ede05ca1 100644
--- a/crates/hir_ty/src/infer/expr.rs
+++ b/crates/hir_ty/src/infer/expr.rs
@@ -119,8 +119,6 @@ impl<'a> InferenceContext<'a> {
     }
 
     fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
-        self.db.check_canceled();
-
         let body = Arc::clone(&self.body); // avoid borrow checker problem
         let ty = match &body[tgt_expr] {
             Expr::Missing => self.err_ty(),
diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs
index afbfa12d51..c87789d45b 100644
--- a/crates/hir_ty/src/lower.rs
+++ b/crates/hir_ty/src/lower.rs
@@ -15,7 +15,7 @@ use hir_def::{
     generics::{TypeParamProvenance, WherePredicate, WherePredicateTypeTarget},
     path::{GenericArg, Path, PathSegment, PathSegments},
     resolver::{HasResolver, Resolver, TypeNs},
-    type_ref::{TraitRef as HirTraitRef, TypeBound, TypeRef},
+    type_ref::{TypeBound, TypeRef},
     AdtId, AssocContainerId, AssocItemId, ConstId, ConstParamId, EnumId, EnumVariantId, FunctionId,
     GenericDefId, HasModule, ImplId, LocalFieldId, Lookup, StaticId, StructId, TraitId,
     TypeAliasId, TypeParamId, UnionId, VariantId,
@@ -667,10 +667,14 @@ impl<'a> TyLoweringContext<'a> {
 
     fn lower_trait_ref(
         &self,
-        trait_ref: &HirTraitRef,
+        type_ref: &TypeRef,
         explicit_self_ty: Option<Ty>,
     ) -> Option<TraitRef> {
-        self.lower_trait_ref_from_path(&trait_ref.path, explicit_self_ty)
+        let path = match type_ref {
+            TypeRef::Path(path) => path,
+            _ => return None,
+        };
+        self.lower_trait_ref_from_path(path, explicit_self_ty)
     }
 
     fn trait_ref_substs_from_path(
@@ -1249,7 +1253,7 @@ pub(crate) fn impl_self_ty_query(db: &dyn HirDatabase, impl_id: ImplId) -> Binde
     let generics = generics(db.upcast(), impl_id.into());
     let ctx =
         TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable);
-    Binders::new(generics.len(), ctx.lower_ty(&impl_data.self_ty))
+    Binders::new(generics.len(), ctx.lower_ty(&impl_data.target_type))
 }
 
 pub(crate) fn const_param_ty_query(db: &dyn HirDatabase, def: ConstParamId) -> Ty {
diff --git a/crates/ide/src/annotations.rs b/crates/ide/src/annotations.rs
index 64bc926f1c..5ebe7fd0ea 100644
--- a/crates/ide/src/annotations.rs
+++ b/crates/ide/src/annotations.rs
@@ -19,6 +19,8 @@ use crate::{
 //
 // Provides user with annotations above items for looking up references or impl blocks
 // and running/debugging binaries.
+//
+// image::https://user-images.githubusercontent.com/48062697/113020672-b7c34f00-917a-11eb-8f6e-858735660a0e.png[]
 #[derive(Debug)]
 pub struct Annotation {
     pub range: TextRange,
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs
index 67e2e5a1cf..9301cdeffe 100644
--- a/crates/ide/src/doc_links.rs
+++ b/crates/ide/src/doc_links.rs
@@ -98,29 +98,6 @@ pub(crate) fn remove_links(markdown: &str) -> String {
     out
 }
 
-/// Retrieve a link to documentation for the given symbol.
-pub(crate) fn external_docs(
-    db: &RootDatabase,
-    position: &FilePosition,
-) -> Option<DocumentationLink> {
-    let sema = Semantics::new(db);
-    let file = sema.parse(position.file_id).syntax().clone();
-    let token = pick_best(file.token_at_offset(position.offset))?;
-    let token = sema.descend_into_macros(token);
-
-    let node = token.parent()?;
-    let definition = match_ast! {
-        match node {
-            ast::NameRef(name_ref) => NameRefClass::classify(&sema, &name_ref).map(|d| d.referenced(sema.db)),
-            ast::Name(name) => NameClass::classify(&sema, &name).map(|d| d.referenced_or_defined(sema.db)),
-            _ => None,
-        }
-    };
-
-    get_doc_link(db, definition?)
-}
-
-/// Extracts all links from a given markdown text.
 pub(crate) fn extract_definitions_from_markdown(
     markdown: &str,
 ) -> Vec<(Range<usize>, String, Option<hir::Namespace>)> {
@@ -201,15 +178,15 @@ pub(crate) fn resolve_doc_path_for_def(
 ) -> Option<hir::ModuleDef> {
     match def {
         Definition::ModuleDef(def) => match def {
-            hir::ModuleDef::Module(it) => it.resolve_doc_path(db, &link, ns),
-            hir::ModuleDef::Function(it) => it.resolve_doc_path(db, &link, ns),
-            hir::ModuleDef::Adt(it) => it.resolve_doc_path(db, &link, ns),
-            hir::ModuleDef::Variant(it) => it.resolve_doc_path(db, &link, ns),
-            hir::ModuleDef::Const(it) => it.resolve_doc_path(db, &link, ns),
-            hir::ModuleDef::Static(it) => it.resolve_doc_path(db, &link, ns),
-            hir::ModuleDef::Trait(it) => it.resolve_doc_path(db, &link, ns),
-            hir::ModuleDef::TypeAlias(it) => it.resolve_doc_path(db, &link, ns),
-            hir::ModuleDef::BuiltinType(_) => None,
+            ModuleDef::Module(it) => it.resolve_doc_path(db, &link, ns),
+            ModuleDef::Function(it) => it.resolve_doc_path(db, &link, ns),
+            ModuleDef::Adt(it) => it.resolve_doc_path(db, &link, ns),
+            ModuleDef::Variant(it) => it.resolve_doc_path(db, &link, ns),
+            ModuleDef::Const(it) => it.resolve_doc_path(db, &link, ns),
+            ModuleDef::Static(it) => it.resolve_doc_path(db, &link, ns),
+            ModuleDef::Trait(it) => it.resolve_doc_path(db, &link, ns),
+            ModuleDef::TypeAlias(it) => it.resolve_doc_path(db, &link, ns),
+            ModuleDef::BuiltinType(_) => None,
         },
         Definition::Macro(it) => it.resolve_doc_path(db, &link, ns),
         Definition::Field(it) => it.resolve_doc_path(db, &link, ns),
@@ -237,7 +214,7 @@ fn get_doc_link(db: &RootDatabase, definition: Definition) -> Option<String> {
                 .and_then(|assoc| match assoc.container(db) {
                     AssocItemContainer::Trait(t) => Some(t.into()),
                     AssocItemContainer::Impl(impld) => {
-                        impld.self_ty(db).as_adt().map(|adt| adt.into())
+                        impld.target_ty(db).as_adt().map(|adt| adt.into())
                     }
                 })
                 .unwrap_or_else(|| f.clone().into()),
@@ -351,6 +328,28 @@ fn rewrite_url_link(db: &RootDatabase, def: ModuleDef, target: &str) -> Option<S
         .map(|url| url.into_string())
 }
 
+/// Retrieve a link to documentation for the given symbol.
+pub(crate) fn external_docs(
+    db: &RootDatabase,
+    position: &FilePosition,
+) -> Option<DocumentationLink> {
+    let sema = Semantics::new(db);
+    let file = sema.parse(position.file_id).syntax().clone();
+    let token = pick_best(file.token_at_offset(position.offset))?;
+    let token = sema.descend_into_macros(token);
+
+    let node = token.parent()?;
+    let definition = match_ast! {
+        match node {
+            ast::NameRef(name_ref) => NameRefClass::classify(&sema, &name_ref).map(|d| d.referenced(sema.db)),
+            ast::Name(name) => NameClass::classify(&sema, &name).map(|d| d.referenced_or_defined(sema.db)),
+            _ => None,
+        }
+    };
+
+    get_doc_link(db, definition?)
+}
+
 /// Rewrites a markdown document, applying 'callback' to each link.
 fn map_links<'e>(
     events: impl Iterator<Item = Event<'e>>,
diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs
index ffb3a6f7d2..9eeabbedaf 100644
--- a/crates/ide/src/expand_macro.rs
+++ b/crates/ide/src/expand_macro.rs
@@ -23,6 +23,8 @@ pub struct ExpandedMacro {
 //
 // | VS Code | **Rust Analyzer: Expand macro recursively**
 // |===
+//
+// image::https://user-images.githubusercontent.com/48062697/113020648-b3973180-917a-11eb-84a9-ecb921293dc5.gif[]
 pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<ExpandedMacro> {
     let sema = Semantics::new(db);
     let file = sema.parse(position.file_id);
diff --git a/crates/ide/src/extend_selection.rs b/crates/ide/src/extend_selection.rs
index 5201ce587a..7032889acc 100644
--- a/crates/ide/src/extend_selection.rs
+++ b/crates/ide/src/extend_selection.rs
@@ -24,6 +24,8 @@ use crate::FileRange;
 //
 // | VS Code | kbd:[Alt+Shift+→], kbd:[Alt+Shift+←]
 // |===
+//
+// image::https://user-images.githubusercontent.com/48062697/113020651-b42fc800-917a-11eb-8a4f-cf1a07859fac.gif[]
 pub(crate) fn extend_selection(db: &RootDatabase, frange: FileRange) -> TextRange {
     let sema = Semantics::new(db);
     let src = sema.parse(frange.file_id);
diff --git a/crates/ide/src/file_structure.rs b/crates/ide/src/file_structure.rs
index 2c898eae85..19071d6be9 100644
--- a/crates/ide/src/file_structure.rs
+++ b/crates/ide/src/file_structure.rs
@@ -35,6 +35,9 @@ pub enum StructureNodeKind {
 //
 // | VS Code | kbd:[Ctrl+Shift+O]
 // |===
+//
+// image::https://user-images.githubusercontent.com/48062697/113020654-b42fc800-917a-11eb-8388-e7dc4d92b02e.gif[]
+
 pub(crate) fn file_structure(file: &SourceFile) -> Vec<StructureNode> {
     let mut res = Vec::new();
     let mut stack = Vec::new();
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index c6556c4871..d66981fcb1 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -21,6 +21,8 @@ use crate::{
 //
 // | VS Code | kbd:[F12]
 // |===
+//
+// image::https://user-images.githubusercontent.com/48062697/113065563-025fbe00-91b1-11eb-83e4-a5a703610b23.gif[]
 pub(crate) fn goto_definition(
     db: &RootDatabase,
     position: FilePosition,
@@ -917,21 +919,6 @@ fn f() -> impl Iterator<Item$0 = u8> {}
         );
     }
 
-    #[test]
-    #[should_panic = "unresolved reference"]
-    fn unknown_assoc_ty() {
-        check(
-            r#"
-trait Iterator {
-    type Item;
-       //^^^^
-}
-
-fn f() -> impl Iterator<Invalid$0 = u8> {}
-            "#,
-        )
-    }
-
     #[test]
     fn goto_def_for_assoc_ty_in_path_multiple() {
         check(
diff --git a/crates/ide/src/goto_implementation.rs b/crates/ide/src/goto_implementation.rs
index f4d7c14a62..05130a2376 100644
--- a/crates/ide/src/goto_implementation.rs
+++ b/crates/ide/src/goto_implementation.rs
@@ -16,6 +16,8 @@ use crate::{display::TryToNav, FilePosition, NavigationTarget, RangeInfo};
 //
 // | VS Code | kbd:[Ctrl+F12]
 // |===
+//
+// image::https://user-images.githubusercontent.com/48062697/113065566-02f85480-91b1-11eb-9288-aaad8abd8841.gif[]
 pub(crate) fn goto_implementation(
     db: &RootDatabase,
     position: FilePosition,
diff --git a/crates/ide/src/goto_type_definition.rs b/crates/ide/src/goto_type_definition.rs
index 2d38cb1128..9d34b109b2 100644
--- a/crates/ide/src/goto_type_definition.rs
+++ b/crates/ide/src/goto_type_definition.rs
@@ -12,6 +12,8 @@ use crate::{display::TryToNav, FilePosition, NavigationTarget, RangeInfo};
 //
 // | VS Code | **Go to Type Definition*
 // |===
+//
+// image::https://user-images.githubusercontent.com/48062697/113020657-b560f500-917a-11eb-9007-0f809733a338.gif[]
 pub(crate) fn goto_type_definition(
     db: &RootDatabase,
     position: FilePosition,
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 7e35a1450e..2e477fd0ae 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -82,6 +82,8 @@ pub struct HoverResult {
 //
 // Shows additional information, like type of an expression or documentation for definition when "focusing" code.
 // Focusing is usually hovering with a mouse, but can also be triggered with a shortcut.
+//
+// image::https://user-images.githubusercontent.com/48062697/113020658-b5f98b80-917a-11eb-9f88-3dbc27320c95.gif[]
 pub(crate) fn hover(
     db: &RootDatabase,
     position: FilePosition,
@@ -195,7 +197,7 @@ fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<Hov
     let adt = match def {
         Definition::ModuleDef(ModuleDef::Trait(it)) => return it.try_to_nav(db).map(to_action),
         Definition::ModuleDef(ModuleDef::Adt(it)) => Some(it),
-        Definition::SelfType(it) => it.self_ty(db).as_adt(),
+        Definition::SelfType(it) => it.target_ty(db).as_adt(),
         _ => None,
     }?;
     adt.try_to_nav(db).map(to_action)
@@ -318,7 +320,7 @@ fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option<String>
         Definition::ModuleDef(md) => match md {
             ModuleDef::Function(f) => match f.as_assoc_item(db)?.container(db) {
                 AssocItemContainer::Trait(t) => Some(t.name(db)),
-                AssocItemContainer::Impl(i) => i.self_ty(db).as_adt().map(|adt| adt.name(db)),
+                AssocItemContainer::Impl(i) => i.target_ty(db).as_adt().map(|adt| adt.name(db)),
             },
             ModuleDef::Variant(e) => Some(e.parent_enum(db).name(db)),
             _ => None,
@@ -376,7 +378,7 @@ fn hover_for_definition(
         },
         Definition::Local(it) => hover_for_local(it, db),
         Definition::SelfType(impl_def) => {
-            impl_def.self_ty(db).as_adt().and_then(|adt| from_hir_fmt(db, adt, mod_path))
+            impl_def.target_ty(db).as_adt().and_then(|adt| from_hir_fmt(db, adt, mod_path))
         }
         Definition::GenericParam(it) => from_hir_fmt(db, it, None),
         Definition::Label(it) => Some(Markup::fenced_block(&it.name(db))),
@@ -470,7 +472,6 @@ fn find_std_module(famous_defs: &FamousDefs, name: &str) -> Option<hir::Module>
 
 fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
     return tokens.max_by_key(priority);
-
     fn priority(n: &SyntaxToken) -> usize {
         match n.kind() {
             IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | T![super] | T![crate] => 3,
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index 25f96222cd..f73edf8b65 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -52,6 +52,8 @@ pub struct InlayHint {
 //
 // | VS Code | **Rust Analyzer: Toggle inlay hints*
 // |===
+//
+// image::https://user-images.githubusercontent.com/48062697/113020660-b5f98b80-917a-11eb-8d70-3be3fd558cdd.png[]
 pub(crate) fn inlay_hints(
     db: &RootDatabase,
     file_id: FileId,
diff --git a/crates/ide/src/join_lines.rs b/crates/ide/src/join_lines.rs
index 4b25135cdb..d584190f7a 100644
--- a/crates/ide/src/join_lines.rs
+++ b/crates/ide/src/join_lines.rs
@@ -19,6 +19,8 @@ use text_edit::{TextEdit, TextEditBuilder};
 //
 // | VS Code | **Rust Analyzer: Join lines**
 // |===
+//
+// image::https://user-images.githubusercontent.com/48062697/113020661-b6922200-917a-11eb-87c4-b75acc028f11.gif[]
 pub(crate) fn join_lines(file: &SourceFile, range: TextRange) -> TextEdit {
     let range = if range.is_empty() {
         let syntax = file.syntax();
diff --git a/crates/ide/src/matching_brace.rs b/crates/ide/src/matching_brace.rs
index 4241a6dac9..261dcc2559 100644
--- a/crates/ide/src/matching_brace.rs
+++ b/crates/ide/src/matching_brace.rs
@@ -14,6 +14,8 @@ use syntax::{
 //
 // | VS Code | **Rust Analyzer: Find matching brace**
 // |===
+//
+// image::https://user-images.githubusercontent.com/48062697/113065573-04298180-91b1-11eb-8dec-d4e2a202f304.gif[]
 pub(crate) fn matching_brace(file: &SourceFile, offset: TextSize) -> Option<TextSize> {
     const BRACES: &[SyntaxKind] =
         &[T!['{'], T!['}'], T!['['], T![']'], T!['('], T![')'], T![<], T![>], T![|], T![|]];
diff --git a/crates/ide/src/move_item.rs b/crates/ide/src/move_item.rs
index d36dcd4e4f..eab2687e31 100644
--- a/crates/ide/src/move_item.rs
+++ b/crates/ide/src/move_item.rs
@@ -4,12 +4,10 @@ use hir::Semantics;
 use ide_db::{base_db::FileRange, RootDatabase};
 use itertools::Itertools;
 use syntax::{
-    algo, ast, match_ast, AstNode, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange,
-    TokenAtOffset,
+    algo, ast, match_ast, AstNode, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, TextRange,
 };
 use text_edit::{TextEdit, TextEditBuilder};
 
-#[derive(Copy, Clone, Debug)]
 pub enum Direction {
     Up,
     Down,
@@ -25,6 +23,8 @@ pub enum Direction {
 // | VS Code | **Rust Analyzer: Move item up**
 // | VS Code | **Rust Analyzer: Move item down**
 // |===
+//
+// image::https://user-images.githubusercontent.com/48062697/113065576-04298180-91b1-11eb-91ce-4505e99ed598.gif[]
 pub(crate) fn move_item(
     db: &RootDatabase,
     range: FileRange,
@@ -33,19 +33,14 @@ pub(crate) fn move_item(
     let sema = Semantics::new(db);
     let file = sema.parse(range.file_id);
 
-    let item = if range.range.is_empty() {
-        SyntaxElement::Token(pick_best(file.syntax().token_at_offset(range.range.start()))?)
-    } else {
-        file.syntax().covering_element(range.range)
-    };
-
+    let item = file.syntax().covering_element(range.range);
     find_ancestors(item, direction, range.range)
 }
 
 fn find_ancestors(item: SyntaxElement, direction: Direction, range: TextRange) -> Option<TextEdit> {
     let root = match item {
-        SyntaxElement::Node(node) => node,
-        SyntaxElement::Token(token) => token.parent()?,
+        NodeOrToken::Node(node) => node,
+        NodeOrToken::Token(token) => token.parent()?,
     };
 
     let movable = [
@@ -58,11 +53,6 @@ fn find_ancestors(item: SyntaxElement, direction: Direction, range: TextRange) -
         SyntaxKind::PARAM,
         SyntaxKind::LET_STMT,
         SyntaxKind::EXPR_STMT,
-        SyntaxKind::IF_EXPR,
-        SyntaxKind::FOR_EXPR,
-        SyntaxKind::LOOP_EXPR,
-        SyntaxKind::WHILE_EXPR,
-        SyntaxKind::RETURN_EXPR,
         SyntaxKind::MATCH_EXPR,
         SyntaxKind::MACRO_CALL,
         SyntaxKind::TYPE_ALIAS,
@@ -95,11 +85,11 @@ fn move_in_direction(
 ) -> Option<TextEdit> {
     match_ast! {
         match node {
-            ast::ArgList(it) => swap_sibling_in_list(node, it.args(), range, direction),
-            ast::GenericParamList(it) => swap_sibling_in_list(node, it.generic_params(), range, direction),
-            ast::GenericArgList(it) => swap_sibling_in_list(node, it.generic_args(), range, direction),
-            ast::VariantList(it) => swap_sibling_in_list(node, it.variants(), range, direction),
-            ast::TypeBoundList(it) => swap_sibling_in_list(node, it.bounds(), range, direction),
+            ast::ArgList(it) => swap_sibling_in_list(it.args(), range, direction),
+            ast::GenericParamList(it) => swap_sibling_in_list(it.generic_params(), range, direction),
+            ast::GenericArgList(it) => swap_sibling_in_list(it.generic_args(), range, direction),
+            ast::VariantList(it) => swap_sibling_in_list(it.variants(), range, direction),
+            ast::TypeBoundList(it) => swap_sibling_in_list(it.bounds(), range, direction),
             _ => Some(replace_nodes(node, &match direction {
                 Direction::Up => node.prev_sibling(),
                 Direction::Down => node.next_sibling(),
@@ -109,27 +99,19 @@ fn move_in_direction(
 }
 
 fn swap_sibling_in_list<A: AstNode + Clone, I: Iterator<Item = A>>(
-    node: &SyntaxNode,
     list: I,
     range: TextRange,
     direction: Direction,
 ) -> Option<TextEdit> {
-    let list_lookup = list
+    let (l, r) = list
         .tuple_windows()
         .filter(|(l, r)| match direction {
             Direction::Up => r.syntax().text_range().contains_range(range),
             Direction::Down => l.syntax().text_range().contains_range(range),
         })
-        .next();
-
-    if let Some((l, r)) = list_lookup {
-        Some(replace_nodes(l.syntax(), r.syntax()))
-    } else {
-        // Cursor is beyond any movable list item (for example, on curly brace in enum).
-        // It's not necessary, that parent of list is movable (arg list's parent is not, for example),
-        // and we have to continue tree traversal to find suitable node.
-        find_ancestors(SyntaxElement::Node(node.parent()?), direction, range)
-    }
+        .next()?;
+
+    Some(replace_nodes(l.syntax(), r.syntax()))
 }
 
 fn replace_nodes(first: &SyntaxNode, second: &SyntaxNode) -> TextEdit {
@@ -141,18 +123,6 @@ fn replace_nodes(first: &SyntaxNode, second: &SyntaxNode) -> TextEdit {
     edit.finish()
 }
 
-fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
-    return tokens.max_by_key(priority);
-
-    fn priority(n: &SyntaxToken) -> usize {
-        match n.kind() {
-            SyntaxKind::IDENT | SyntaxKind::LIFETIME_IDENT => 2,
-            kind if kind.is_trivia() => 0,
-            _ => 1,
-        }
-    }
-}
-
 #[cfg(test)]
 mod tests {
     use crate::fixture;
@@ -292,107 +262,6 @@ fn main() {
             expect![[r#"
 fn main() {
     println!("All I want to say is...");
-    println!("Hello, world");
-}
-            "#]],
-            Direction::Up,
-        );
-        check(
-            r#"
-fn main() {
-    println!("Hello, world");
-
-    if true {
-        println!("Test");
-    }$0$0
-}
-            "#,
-            expect![[r#"
-fn main() {
-    if true {
-        println!("Test");
-    }
-
-    println!("Hello, world");
-}
-            "#]],
-            Direction::Up,
-        );
-        check(
-            r#"
-fn main() {
-    println!("Hello, world");
-
-    for i in 0..10 {
-        println!("Test");
-    }$0$0
-}
-            "#,
-            expect![[r#"
-fn main() {
-    for i in 0..10 {
-        println!("Test");
-    }
-
-    println!("Hello, world");
-}
-            "#]],
-            Direction::Up,
-        );
-        check(
-            r#"
-fn main() {
-    println!("Hello, world");
-
-    loop {
-        println!("Test");
-    }$0$0
-}
-            "#,
-            expect![[r#"
-fn main() {
-    loop {
-        println!("Test");
-    }
-
-    println!("Hello, world");
-}
-            "#]],
-            Direction::Up,
-        );
-        check(
-            r#"
-fn main() {
-    println!("Hello, world");
-
-    while true {
-        println!("Test");
-    }$0$0
-}
-            "#,
-            expect![[r#"
-fn main() {
-    while true {
-        println!("Test");
-    }
-
-    println!("Hello, world");
-}
-            "#]],
-            Direction::Up,
-        );
-        check(
-            r#"
-fn main() {
-    println!("Hello, world");
-
-    return 123;$0$0
-}
-            "#,
-            expect![[r#"
-fn main() {
-    return 123;
-
     println!("Hello, world");
 }
             "#]],
@@ -747,115 +616,6 @@ fn test() {
         );
     }
 
-    #[test]
-    fn test_cursor_at_item_start() {
-        check(
-            r#"
-$0$0#[derive(Debug)]
-enum FooBar {
-    Foo,
-    Bar,
-}
-
-fn main() {}
-            "#,
-            expect![[r#"
-fn main() {}
-
-#[derive(Debug)]
-enum FooBar {
-    Foo,
-    Bar,
-}
-            "#]],
-            Direction::Down,
-        );
-        check(
-            r#"
-$0$0enum FooBar {
-    Foo,
-    Bar,
-}
-
-fn main() {}
-            "#,
-            expect![[r#"
-fn main() {}
-
-enum FooBar {
-    Foo,
-    Bar,
-}
-            "#]],
-            Direction::Down,
-        );
-        check(
-            r#"
-struct Test;
-
-trait SomeTrait {}
-
-$0$0impl SomeTrait for Test {}
-
-fn main() {}
-            "#,
-            expect![[r#"
-struct Test;
-
-impl SomeTrait for Test {}
-
-trait SomeTrait {}
-
-fn main() {}
-            "#]],
-            Direction::Up,
-        );
-    }
-
-    #[test]
-    fn test_cursor_at_item_end() {
-        check(
-            r#"
-enum FooBar {
-    Foo,
-    Bar,
-}$0$0
-
-fn main() {}
-            "#,
-            expect![[r#"
-fn main() {}
-
-enum FooBar {
-    Foo,
-    Bar,
-}
-            "#]],
-            Direction::Down,
-        );
-        check(
-            r#"
-struct Test;
-
-trait SomeTrait {}
-
-impl SomeTrait for Test {}$0$0
-
-fn main() {}
-            "#,
-            expect![[r#"
-struct Test;
-
-impl SomeTrait for Test {}
-
-trait SomeTrait {}
-
-fn main() {}
-            "#]],
-            Direction::Up,
-        );
-    }
-
     #[test]
     fn handles_empty_file() {
         check(r#"$0$0"#, expect![[r#""#]], Direction::Up);
diff --git a/crates/ide/src/parent_module.rs b/crates/ide/src/parent_module.rs
index 22b0d6ecbe..99365c8a73 100644
--- a/crates/ide/src/parent_module.rs
+++ b/crates/ide/src/parent_module.rs
@@ -18,6 +18,8 @@ use crate::NavigationTarget;
 //
 // | VS Code | **Rust Analyzer: Locate parent module**
 // |===
+//
+// image::https://user-images.githubusercontent.com/48062697/113065580-04c21800-91b1-11eb-9a32-00086161c0bd.gif[]
 
 /// This returns `Vec` because a module may be included from several places.
 pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<NavigationTarget> {
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs
index 95ed8a0457..11ca7ec6b6 100644
--- a/crates/ide/src/references.rs
+++ b/crates/ide/src/references.rs
@@ -48,6 +48,8 @@ pub struct Declaration {
 //
 // | VS Code | kbd:[Shift+Alt+F12]
 // |===
+//
+// image::https://user-images.githubusercontent.com/48062697/113020670-b7c34f00-917a-11eb-8003-370ac5f2b3cb.gif[]
 pub(crate) fn find_all_refs(
     sema: &Semantics<RootDatabase>,
     position: FilePosition,
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs
index 98456967a3..e6f7fa066f 100644
--- a/crates/ide/src/references/rename.rs
+++ b/crates/ide/src/references/rename.rs
@@ -70,6 +70,8 @@ pub(crate) fn prepare_rename(
 //
 // | VS Code | kbd:[F2]
 // |===
+//
+// image::https://user-images.githubusercontent.com/48062697/113065582-055aae80-91b1-11eb-8ade-2b58e6d81883.gif[]
 pub(crate) fn rename(
     db: &RootDatabase,
     position: FilePosition,
@@ -307,7 +309,7 @@ fn rename_to_self(sema: &Semantics<RootDatabase>, local: hir::Local) -> RenameRe
         hir::AssocItemContainer::Impl(impl_) => impl_,
     };
     let first_param_ty = first_param.ty();
-    let impl_ty = impl_.self_ty(sema.db);
+    let impl_ty = impl_.target_ty(sema.db);
     let (ty, self_param) = if impl_ty.remove_ref().is_some() {
         // if the impl is a ref to the type we can just match the `&T` with self directly
         (first_param_ty.clone(), "self")
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs
index 11bd385bb9..9435dfc067 100644
--- a/crates/ide/src/runnables.rs
+++ b/crates/ide/src/runnables.rs
@@ -98,6 +98,7 @@ impl Runnable {
 //
 // | VS Code | **Rust Analyzer: Run**
 // |===
+// image::https://user-images.githubusercontent.com/48062697/113065583-055aae80-91b1-11eb-958f-d67efcaf6a2f.gif[]
 pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
     let sema = Semantics::new(db);
 
@@ -298,7 +299,7 @@ fn module_def_doctest(sema: &Semantics<RootDatabase>, def: hir::ModuleDef) -> Op
             // FIXME: this also looks very wrong
             if let Some(assoc_def) = assoc_def {
                 if let hir::AssocItemContainer::Impl(imp) = assoc_def.container(sema.db) {
-                    let ty = imp.self_ty(sema.db);
+                    let ty = imp.target_ty(sema.db);
                     if let Some(adt) = ty.as_adt() {
                         let name = adt.name(sema.db);
                         let idx = path.rfind(':').map_or(0, |idx| idx + 1);
diff --git a/crates/ide/src/status.rs b/crates/ide/src/status.rs
index 137c38c0d9..49fde1945c 100644
--- a/crates/ide/src/status.rs
+++ b/crates/ide/src/status.rs
@@ -31,6 +31,7 @@ fn macro_syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats {
 //
 // | VS Code | **Rust Analyzer: Status**
 // |===
+// image::https://user-images.githubusercontent.com/48062697/113065584-05f34500-91b1-11eb-98cc-5c196f76be7f.gif[]
 pub(crate) fn status(db: &RootDatabase, file_id: Option<FileId>) -> String {
     let mut buf = String::new();
     format_to!(buf, "{}\n", FileTextQuery.in_db(db).entries::<FilesStats>());
diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs
index 5ccb847149..b0cfdd8b7d 100644
--- a/crates/ide/src/syntax_highlighting/highlight.rs
+++ b/crates/ide/src/syntax_highlighting/highlight.rs
@@ -1,6 +1,6 @@
 //! Computes color for a single element.
 
-use hir::{AsAssocItem, AssocItemContainer, Semantics, VariantDef};
+use hir::{AsAssocItem, Semantics, VariantDef};
 use ide_db::{
     defs::{Definition, NameClass, NameRefClass},
     RootDatabase, SymbolKind,
@@ -275,24 +275,12 @@ fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight {
             hir::ModuleDef::Module(_) => HlTag::Symbol(SymbolKind::Module),
             hir::ModuleDef::Function(func) => {
                 let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Function));
-                if let Some(item) = func.as_assoc_item(db) {
+                if func.as_assoc_item(db).is_some() {
                     h |= HlMod::Associated;
                     if func.self_param(db).is_none() {
                         h |= HlMod::Static
                     }
-
-                    match item.container(db) {
-                        AssocItemContainer::Impl(i) => {
-                            if i.trait_(db).is_some() {
-                                h |= HlMod::Trait;
-                            }
-                        }
-                        AssocItemContainer::Trait(_t) => {
-                            h |= HlMod::Trait;
-                        }
-                    }
                 }
-
                 if func.is_unsafe(db) {
                     h |= HlMod::Unsafe;
                 }
@@ -304,20 +292,9 @@ fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight {
             hir::ModuleDef::Variant(_) => HlTag::Symbol(SymbolKind::Variant),
             hir::ModuleDef::Const(konst) => {
                 let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Const));
-                if let Some(item) = konst.as_assoc_item(db) {
-                    h |= HlMod::Associated;
-                    match item.container(db) {
-                        AssocItemContainer::Impl(i) => {
-                            if i.trait_(db).is_some() {
-                                h |= HlMod::Trait;
-                            }
-                        }
-                        AssocItemContainer::Trait(_t) => {
-                            h |= HlMod::Trait;
-                        }
-                    }
+                if konst.as_assoc_item(db).is_some() {
+                    h |= HlMod::Associated
                 }
-
                 return h;
             }
             hir::ModuleDef::Trait(_) => HlTag::Symbol(SymbolKind::Trait),
@@ -385,10 +362,6 @@ fn highlight_method_call(
     if func.is_unsafe(sema.db) || sema.is_unsafe_method_call(&method_call) {
         h |= HlMod::Unsafe;
     }
-    if func.as_assoc_item(sema.db).and_then(|it| it.containing_trait(sema.db)).is_some() {
-        h |= HlMod::Trait
-    }
-
     if let Some(self_param) = func.self_param(sema.db) {
         match self_param.access(sema.db) {
             hir::Access::Shared => (),
diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs
index b62d43256c..963c3fb594 100644
--- a/crates/ide/src/syntax_highlighting/inject.rs
+++ b/crates/ide/src/syntax_highlighting/inject.rs
@@ -11,8 +11,7 @@ use syntax::{
 };
 
 use crate::{
-    doc_links::{extract_definitions_from_markdown, resolve_doc_path_for_def},
-    Analysis, HlMod, HlRange, HlTag, RootDatabase,
+    doc_links::extract_definitions_from_markdown, Analysis, HlMod, HlRange, HlTag, RootDatabase,
 };
 
 use super::{highlights::Highlights, injector::Injector};
@@ -191,7 +190,7 @@ pub(super) fn doc_comment(
                         extract_definitions_from_markdown(line)
                             .into_iter()
                             .filter_map(|(range, link, ns)| {
-                                Some(range).zip(resolve_doc_path_for_def(sema.db, def, &link, ns))
+                                Some(range).zip(validate_intra_doc_link(sema.db, &def, &link, ns))
                             })
                             .map(|(Range { start, end }, def)| {
                                 (
@@ -284,6 +283,33 @@ fn find_doc_string_in_attr(attr: &hir::Attr, it: &ast::Attr) -> Option<ast::Stri
     }
 }
 
+fn validate_intra_doc_link(
+    db: &RootDatabase,
+    def: &Definition,
+    link: &str,
+    ns: Option<hir::Namespace>,
+) -> Option<hir::ModuleDef> {
+    match def {
+        Definition::ModuleDef(def) => match def {
+            hir::ModuleDef::Module(it) => it.resolve_doc_path(db, &link, ns),
+            hir::ModuleDef::Function(it) => it.resolve_doc_path(db, &link, ns),
+            hir::ModuleDef::Adt(it) => it.resolve_doc_path(db, &link, ns),
+            hir::ModuleDef::Variant(it) => it.resolve_doc_path(db, &link, ns),
+            hir::ModuleDef::Const(it) => it.resolve_doc_path(db, &link, ns),
+            hir::ModuleDef::Static(it) => it.resolve_doc_path(db, &link, ns),
+            hir::ModuleDef::Trait(it) => it.resolve_doc_path(db, &link, ns),
+            hir::ModuleDef::TypeAlias(it) => it.resolve_doc_path(db, &link, ns),
+            hir::ModuleDef::BuiltinType(_) => None,
+        },
+        Definition::Macro(it) => it.resolve_doc_path(db, &link, ns),
+        Definition::Field(it) => it.resolve_doc_path(db, &link, ns),
+        Definition::SelfType(_)
+        | Definition::Local(_)
+        | Definition::GenericParam(_)
+        | Definition::Label(_) => None,
+    }
+}
+
 fn module_def_to_hl_tag(def: hir::ModuleDef) -> HlTag {
     let symbol = match def {
         hir::ModuleDef::Module(_) => SymbolKind::Module,
diff --git a/crates/ide/src/syntax_highlighting/tags.rs b/crates/ide/src/syntax_highlighting/tags.rs
index 1cec991aa6..93db79b895 100644
--- a/crates/ide/src/syntax_highlighting/tags.rs
+++ b/crates/ide/src/syntax_highlighting/tags.rs
@@ -58,8 +58,6 @@ pub enum HlMod {
     Associated,
     /// Used for intra doc links in doc injection.
     IntraDocLink,
-    /// Used for items in traits and trait impls.
-    Trait,
 
     /// Keep this last!
     Unsafe,
@@ -160,7 +158,6 @@ impl HlMod {
         HlMod::Callable,
         HlMod::Static,
         HlMod::Associated,
-        HlMod::Trait,
         HlMod::Unsafe,
     ];
 
@@ -177,7 +174,6 @@ impl HlMod {
             HlMod::IntraDocLink => "intra_doc_link",
             HlMod::Mutable => "mutable",
             HlMod::Static => "static",
-            HlMod::Trait => "trait",
             HlMod::Unsafe => "unsafe",
         }
     }
diff --git a/crates/ide/src/syntax_tree.rs b/crates/ide/src/syntax_tree.rs
index 8979de5287..633878d1c6 100644
--- a/crates/ide/src/syntax_tree.rs
+++ b/crates/ide/src/syntax_tree.rs
@@ -14,6 +14,7 @@ use syntax::{
 //
 // | VS Code | **Rust Analyzer: Show Syntax Tree**
 // |===
+// image::https://user-images.githubusercontent.com/48062697/113065586-068bdb80-91b1-11eb-9507-fee67f9f45a0.gif[]
 pub(crate) fn syntax_tree(
     db: &RootDatabase,
     file_id: FileId,
diff --git a/crates/ide/src/typing/on_enter.rs b/crates/ide/src/typing/on_enter.rs
index 978c479de3..9144681bfc 100644
--- a/crates/ide/src/typing/on_enter.rs
+++ b/crates/ide/src/typing/on_enter.rs
@@ -32,6 +32,8 @@ use text_edit::TextEdit;
 //   "when": "editorTextFocus && !suggestWidgetVisible && editorLangId == rust"
 // }
 // ----
+//
+// image::https://user-images.githubusercontent.com/48062697/113065578-04c21800-91b1-11eb-82b8-22b8c481e645.gif[]
 pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<TextEdit> {
     let parse = db.parse(position.file_id);
     let file = parse.tree();
diff --git a/crates/ide/src/view_hir.rs b/crates/ide/src/view_hir.rs
index f8f3fae3d0..7312afe531 100644
--- a/crates/ide/src/view_hir.rs
+++ b/crates/ide/src/view_hir.rs
@@ -10,6 +10,7 @@ use syntax::{algo::find_node_at_offset, ast, AstNode};
 //
 // | VS Code | **Rust Analyzer: View Hir**
 // |===
+// image::https://user-images.githubusercontent.com/48062697/113065588-068bdb80-91b1-11eb-9a78-0b4ef1e972fb.gif[]
 pub(crate) fn view_hir(db: &RootDatabase, position: FilePosition) -> String {
     body_hir(db, position).unwrap_or_else(|| "Not inside a function body".to_string())
 }
diff --git a/crates/ide_assists/src/handlers/auto_import.rs b/crates/ide_assists/src/handlers/auto_import.rs
index 7019039b9e..5ccd7f7a2c 100644
--- a/crates/ide_assists/src/handlers/auto_import.rs
+++ b/crates/ide_assists/src/handlers/auto_import.rs
@@ -61,6 +61,8 @@ use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel};
 // - `plain`: This setting does not impose any restrictions in imports.
 //
 // In `VS Code` the configuration for this is `rust-analyzer.assist.importPrefix`.
+//
+// image::https://user-images.githubusercontent.com/48062697/113020673-b85be580-917a-11eb-9022-59585f35d4f8.gif[]
 
 // Assist: auto_import
 //
diff --git a/crates/ide_assists/src/handlers/generate_default_from_new.rs b/crates/ide_assists/src/handlers/generate_default_from_new.rs
index dc14552d60..81c54ba3ec 100644
--- a/crates/ide_assists/src/handlers/generate_default_from_new.rs
+++ b/crates/ide_assists/src/handlers/generate_default_from_new.rs
@@ -92,7 +92,7 @@ fn is_default_implemented(ctx: &AssistContext, impl_: &Impl) -> bool {
         None => return false,
     };
 
-    let ty = impl_def.self_ty(db);
+    let ty = impl_def.target_ty(db);
     let krate = impl_def.module(db).krate();
     let default = FamousDefs(&ctx.sema, Some(krate)).core_default_Default();
     let default_trait = match default {
diff --git a/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs b/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs
index 910010a044..b8834d283d 100644
--- a/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs
+++ b/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs
@@ -91,7 +91,7 @@ fn get_impl_method(
 
     let scope = ctx.sema.scope(impl_.syntax());
     let krate = impl_def.module(db).krate();
-    let ty = impl_def.self_ty(db);
+    let ty = impl_def.target_ty(db);
     let traits_in_scope = scope.traits_in_scope();
     ty.iterate_method_candidates(db, krate, &traits_in_scope, Some(fn_name), |_, func| Some(func))
 }
diff --git a/crates/ide_assists/src/utils.rs b/crates/ide_assists/src/utils.rs
index d67524937e..5f630ec75a 100644
--- a/crates/ide_assists/src/utils.rs
+++ b/crates/ide_assists/src/utils.rs
@@ -338,11 +338,11 @@ pub(crate) fn find_struct_impl(
         // (we currently use the wrong type parameter)
         // also we wouldn't want to use e.g. `impl S<u32>`
 
-        let same_ty = match blk.self_ty(db).as_adt() {
+        let same_ty = match blk.target_ty(db).as_adt() {
             Some(def) => def == struct_def,
             None => false,
         };
-        let not_trait_impl = blk.trait_(db).is_none();
+        let not_trait_impl = blk.target_trait(db).is_none();
 
         if !(same_ty && not_trait_impl) {
             None
diff --git a/crates/ide_completion/src/completions.rs b/crates/ide_completion/src/completions.rs
index e2994eed4f..6d572a8361 100644
--- a/crates/ide_completion/src/completions.rs
+++ b/crates/ide_completion/src/completions.rs
@@ -220,7 +220,7 @@ fn complete_enum_variants(
         };
 
         if let Some(impl_) = ctx.impl_def.as_ref().and_then(|impl_| ctx.sema.to_def(impl_)) {
-            if impl_.self_ty(ctx.db) == *ty {
+            if impl_.target_ty(ctx.db) == *ty {
                 for &variant in &variants {
                     let self_path = hir::ModPath::from_segments(
                         hir::PathKind::Plain,
diff --git a/crates/ide_completion/src/completions/pattern.rs b/crates/ide_completion/src/completions/pattern.rs
index 808d7ff7e6..b06498e6da 100644
--- a/crates/ide_completion/src/completions/pattern.rs
+++ b/crates/ide_completion/src/completions/pattern.rs
@@ -40,7 +40,7 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
                 _ => false,
             },
             hir::ScopeDef::MacroDef(_) => true,
-            hir::ScopeDef::ImplSelfType(impl_) => match impl_.self_ty(ctx.db).as_adt() {
+            hir::ScopeDef::ImplSelfType(impl_) => match impl_.target_ty(ctx.db).as_adt() {
                 Some(hir::Adt::Struct(strukt)) => {
                     acc.add_struct_pat(ctx, strukt, Some(name.clone()));
                     true
diff --git a/crates/ide_completion/src/completions/postfix/format_like.rs b/crates/ide_completion/src/completions/postfix/format_like.rs
index 3f1c6730b4..e86ffa8f81 100644
--- a/crates/ide_completion/src/completions/postfix/format_like.rs
+++ b/crates/ide_completion/src/completions/postfix/format_like.rs
@@ -13,6 +13,8 @@
 //   + `logi` -> `log::info!(...)`
 //   + `logw` -> `log::warn!(...)`
 //   + `loge` -> `log::error!(...)`
+//
+// image::https://user-images.githubusercontent.com/48062697/113020656-b560f500-917a-11eb-87de-02991f61beb8.gif[]
 
 use ide_db::helpers::SnippetCap;
 use syntax::ast::{self, AstToken};
diff --git a/crates/ide_completion/src/completions/qualified_path.rs b/crates/ide_completion/src/completions/qualified_path.rs
index 1891eb5b36..105ff60131 100644
--- a/crates/ide_completion/src/completions/qualified_path.rs
+++ b/crates/ide_completion/src/completions/qualified_path.rs
@@ -117,7 +117,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
             if let Some(krate) = ctx.krate {
                 let ty = match resolution {
                     PathResolution::TypeParam(param) => param.ty(ctx.db),
-                    PathResolution::SelfType(impl_def) => impl_def.self_ty(ctx.db),
+                    PathResolution::SelfType(impl_def) => impl_def.target_ty(ctx.db),
                     _ => return,
                 };
 
diff --git a/crates/ide_completion/src/lib.rs b/crates/ide_completion/src/lib.rs
index 5ac1cb48d4..831d543bbd 100644
--- a/crates/ide_completion/src/lib.rs
+++ b/crates/ide_completion/src/lib.rs
@@ -81,6 +81,8 @@ pub use crate::{
 // And the auto import completions, enabled with the `rust-analyzer.completion.autoimport.enable` setting and the corresponding LSP client capabilities.
 // Those are the additional completion options with automatic `use` import and options from all project importable items,
 // fuzzy matched agains the completion imput.
+//
+// image::https://user-images.githubusercontent.com/48062697/113020667-b72ab880-917a-11eb-8778-716cf26a0eb3.gif[]
 
 /// Main entry point for completion. We run completion as a two-phase process.
 ///
diff --git a/crates/ide_db/src/apply_change.rs b/crates/ide_db/src/apply_change.rs
index 047a9b6bce..111e9325a4 100644
--- a/crates/ide_db/src/apply_change.rs
+++ b/crates/ide_db/src/apply_change.rs
@@ -101,6 +101,7 @@ impl RootDatabase {
     //
     // | VS Code | **Rust Analyzer: Memory Usage (Clears Database)**
     // |===
+    // image::https://user-images.githubusercontent.com/48062697/113065592-08559f00-91b1-11eb-8c96-64b88068ec02.gif[]
     pub fn per_query_memory_usage(&mut self) -> Vec<(String, Bytes)> {
         let mut acc: Vec<(String, Bytes)> = vec![];
         let sweep = SweepStrategy::default().discard_values().sweep_all_revisions();
diff --git a/crates/ide_db/src/defs.rs b/crates/ide_db/src/defs.rs
index de0dc2a40f..0d9808d243 100644
--- a/crates/ide_db/src/defs.rs
+++ b/crates/ide_db/src/defs.rs
@@ -330,30 +330,25 @@ impl NameRefClass {
             }
         }
 
-        if let Some(assoc_type_arg) = ast::AssocTypeArg::cast(parent.clone()) {
-            if assoc_type_arg.name_ref().as_ref() == Some(name_ref) {
-                // `Trait<Assoc = Ty>`
-                //        ^^^^^
-                let path = name_ref.syntax().ancestors().find_map(ast::Path::cast)?;
-                let resolved = sema.resolve_path(&path)?;
-                if let PathResolution::Def(ModuleDef::Trait(tr)) = resolved {
-                    // FIXME: resolve in supertraits
-                    if let Some(ty) = tr
-                        .items(sema.db)
-                        .iter()
-                        .filter_map(|assoc| match assoc {
-                            hir::AssocItem::TypeAlias(it) => Some(*it),
-                            _ => None,
-                        })
-                        .find(|alias| &alias.name(sema.db).to_string() == &name_ref.text())
-                    {
-                        return Some(NameRefClass::Definition(Definition::ModuleDef(
-                            ModuleDef::TypeAlias(ty),
-                        )));
-                    }
+        if ast::AssocTypeArg::cast(parent.clone()).is_some() {
+            // `Trait<Assoc = Ty>`
+            //        ^^^^^
+            let path = name_ref.syntax().ancestors().find_map(ast::Path::cast)?;
+            let resolved = sema.resolve_path(&path)?;
+            if let PathResolution::Def(ModuleDef::Trait(tr)) = resolved {
+                if let Some(ty) = tr
+                    .items(sema.db)
+                    .iter()
+                    .filter_map(|assoc| match assoc {
+                        hir::AssocItem::TypeAlias(it) => Some(*it),
+                        _ => None,
+                    })
+                    .find(|alias| &alias.name(sema.db).to_string() == &name_ref.text())
+                {
+                    return Some(NameRefClass::Definition(Definition::ModuleDef(
+                        ModuleDef::TypeAlias(ty),
+                    )));
                 }
-
-                return None;
             }
         }
 
diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs
index 8ce6483671..3deb0d159d 100644
--- a/crates/ide_db/src/helpers/import_assets.rs
+++ b/crates/ide_db/src/helpers/import_assets.rs
@@ -361,7 +361,7 @@ fn item_for_path_search(db: &RootDatabase, item: ItemInNs) -> Option<ItemInNs> {
             Some(assoc_item) => match assoc_item.container(db) {
                 AssocItemContainer::Trait(trait_) => ItemInNs::from(ModuleDef::from(trait_)),
                 AssocItemContainer::Impl(impl_) => {
-                    ItemInNs::from(ModuleDef::from(impl_.self_ty(db).as_adt()?))
+                    ItemInNs::from(ModuleDef::from(impl_.target_ty(db).as_adt()?))
                 }
             },
             None => item,
diff --git a/crates/profile/src/google_cpu_profiler.rs b/crates/profile/src/google_cpu_profiler.rs
index cae6caeaa6..db865c65be 100644
--- a/crates/profile/src/google_cpu_profiler.rs
+++ b/crates/profile/src/google_cpu_profiler.rs
@@ -14,31 +14,26 @@ extern "C" {
     fn ProfilerStop();
 }
 
+static PROFILER_STATE: AtomicUsize = AtomicUsize::new(OFF);
 const OFF: usize = 0;
 const ON: usize = 1;
 const PENDING: usize = 2;
 
-fn transition(current: usize, new: usize) -> bool {
-    static STATE: AtomicUsize = AtomicUsize::new(OFF);
-
-    STATE.compare_exchange(current, new, Ordering::SeqCst, Ordering::SeqCst).is_ok()
-}
-
-pub(crate) fn start(path: &Path) {
-    if !transition(OFF, PENDING) {
+pub fn start(path: &Path) {
+    if PROFILER_STATE.compare_and_swap(OFF, PENDING, Ordering::SeqCst) != OFF {
         panic!("profiler already started");
     }
     let path = CString::new(path.display().to_string()).unwrap();
     if unsafe { ProfilerStart(path.as_ptr()) } == 0 {
         panic!("profiler failed to start")
     }
-    assert!(transition(PENDING, ON));
+    assert!(PROFILER_STATE.compare_and_swap(PENDING, ON, Ordering::SeqCst) == PENDING);
 }
 
-pub(crate) fn stop() {
-    if !transition(ON, PENDING) {
+pub fn stop() {
+    if PROFILER_STATE.compare_and_swap(ON, PENDING, Ordering::SeqCst) != ON {
         panic!("profiler is not started")
     }
     unsafe { ProfilerStop() };
-    assert!(transition(PENDING, OFF));
+    assert!(PROFILER_STATE.compare_and_swap(PENDING, OFF, Ordering::SeqCst) == PENDING);
 }
diff --git a/crates/profile/src/hprof.rs b/crates/profile/src/hprof.rs
index 5fdb372061..014e906e62 100644
--- a/crates/profile/src/hprof.rs
+++ b/crates/profile/src/hprof.rs
@@ -69,20 +69,6 @@ pub fn span(label: Label) -> ProfileSpan {
     }
 }
 
-#[inline]
-pub fn heartbeat_span() -> HeartbeatSpan {
-    let enabled = PROFILING_ENABLED.load(Ordering::Relaxed);
-    HeartbeatSpan::new(enabled)
-}
-
-#[inline]
-pub fn heartbeat() {
-    let enabled = PROFILING_ENABLED.load(Ordering::Relaxed);
-    if enabled {
-        with_profile_stack(|it| it.heartbeat(1));
-    }
-}
-
 pub struct ProfileSpan(Option<ProfilerImpl>);
 
 struct ProfilerImpl {
@@ -106,28 +92,6 @@ impl Drop for ProfilerImpl {
     }
 }
 
-pub struct HeartbeatSpan {
-    enabled: bool,
-}
-
-impl HeartbeatSpan {
-    #[inline]
-    pub fn new(enabled: bool) -> Self {
-        if enabled {
-            with_profile_stack(|it| it.heartbeats(true))
-        }
-        Self { enabled }
-    }
-}
-
-impl Drop for HeartbeatSpan {
-    fn drop(&mut self) {
-        if self.enabled {
-            with_profile_stack(|it| it.heartbeats(false))
-        }
-    }
-}
-
 static PROFILING_ENABLED: AtomicBool = AtomicBool::new(false);
 static FILTER: Lazy<RwLock<Filter>> = Lazy::new(Default::default);
 
@@ -141,7 +105,6 @@ struct Filter {
     depth: usize,
     allowed: HashSet<String>,
     longer_than: Duration,
-    heartbeat_longer_than: Duration,
     version: usize,
 }
 
@@ -158,7 +121,6 @@ impl Filter {
         } else {
             Duration::new(0, 0)
         };
-        let heartbeat_longer_than = longer_than;
 
         let depth = if let Some(idx) = spec.rfind('@') {
             let depth: usize = spec[idx + 1..].parse().expect("invalid profile depth");
@@ -169,7 +131,7 @@ impl Filter {
         };
         let allowed =
             if spec == "*" { HashSet::new() } else { spec.split('|').map(String::from).collect() };
-        Filter { depth, allowed, longer_than, heartbeat_longer_than, version: 0 }
+        Filter { depth, allowed, longer_than, version: 0 }
     }
 
     fn install(mut self) {
@@ -181,15 +143,9 @@ impl Filter {
 }
 
 struct ProfileStack {
-    frames: Vec<Frame>,
+    starts: Vec<Instant>,
     filter: Filter,
     messages: Tree<Message>,
-    heartbeats: bool,
-}
-
-struct Frame {
-    t: Instant,
-    heartbeats: u32,
 }
 
 #[derive(Default)]
@@ -201,49 +157,35 @@ struct Message {
 
 impl ProfileStack {
     fn new() -> ProfileStack {
-        ProfileStack {
-            frames: Vec::new(),
-            messages: Tree::default(),
-            filter: Default::default(),
-            heartbeats: false,
-        }
+        ProfileStack { starts: Vec::new(), messages: Tree::default(), filter: Default::default() }
     }
 
     fn push(&mut self, label: Label) -> bool {
-        if self.frames.is_empty() {
+        if self.starts.is_empty() {
             if let Ok(f) = FILTER.try_read() {
                 if f.version > self.filter.version {
                     self.filter = f.clone();
                 }
             };
         }
-        if self.frames.len() > self.filter.depth {
+        if self.starts.len() > self.filter.depth {
             return false;
         }
         let allowed = &self.filter.allowed;
-        if self.frames.is_empty() && !allowed.is_empty() && !allowed.contains(label) {
+        if self.starts.is_empty() && !allowed.is_empty() && !allowed.contains(label) {
             return false;
         }
 
-        self.frames.push(Frame { t: Instant::now(), heartbeats: 0 });
+        self.starts.push(Instant::now());
         self.messages.start();
         true
     }
 
     fn pop(&mut self, label: Label, detail: Option<String>) {
-        let frame = self.frames.pop().unwrap();
-        let duration = frame.t.elapsed();
-
-        if self.heartbeats {
-            self.heartbeat(frame.heartbeats);
-            let avg_span = duration / (frame.heartbeats + 1);
-            if avg_span > self.filter.heartbeat_longer_than {
-                eprintln!("Too few heartbeats {} ({}/{:?})?", label, frame.heartbeats, duration)
-            }
-        }
-
+        let start = self.starts.pop().unwrap();
+        let duration = start.elapsed();
         self.messages.finish(Message { duration, label, detail });
-        if self.frames.is_empty() {
+        if self.starts.is_empty() {
             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
@@ -256,15 +198,6 @@ impl ProfileStack {
             self.messages.clear();
         }
     }
-
-    fn heartbeats(&mut self, yes: bool) {
-        self.heartbeats = yes;
-    }
-    fn heartbeat(&mut self, n: u32) {
-        if let Some(frame) = self.frames.last_mut() {
-            frame.heartbeats += n;
-        }
-    }
 }
 
 fn print(
diff --git a/crates/profile/src/lib.rs b/crates/profile/src/lib.rs
index a31fb8f439..79dba47d5e 100644
--- a/crates/profile/src/lib.rs
+++ b/crates/profile/src/lib.rs
@@ -10,7 +10,7 @@ mod tree;
 use std::cell::RefCell;
 
 pub use crate::{
-    hprof::{heartbeat, heartbeat_span, init, init_from, span},
+    hprof::{init, init_from, span},
     memory_usage::{Bytes, MemoryUsage},
     stop_watch::{StopWatch, StopWatchSpan},
 };
@@ -52,7 +52,7 @@ impl Drop for Scope {
 /// Usage:
 /// 1. Install gpref_tools (https://github.com/gperftools/gperftools), probably packaged with your Linux distro.
 /// 2. Build with `cpu_profiler` feature.
-/// 3. Run the code, the *raw* output would be in the `./out.profile` file.
+/// 3. Tun the code, the *raw* output would be in the `./out.profile` file.
 /// 4. Install pprof for visualization (https://github.com/google/pprof).
 /// 5. Bump sampling frequency to once per ms: `export CPUPROFILE_FREQUENCY=1000`
 /// 6. Use something like `pprof -svg target/release/rust-analyzer ./out.profile` to see the results.
@@ -60,17 +60,8 @@ impl Drop for Scope {
 /// For example, here's how I run profiling on NixOS:
 ///
 /// ```bash
-/// $ bat -p shell.nix
-/// with import <nixpkgs> {};
-/// mkShell {
-///   buildInputs = [ gperftools ];
-///   shellHook = ''
-///     export LD_LIBRARY_PATH="${gperftools}/lib:"
-///   '';
-/// }
-/// $ set -x CPUPROFILE_FREQUENCY 1000
-/// $ nix-shell --run 'cargo test --release --package rust-analyzer --lib -- benchmarks::benchmark_integrated_highlighting --exact --nocapture'
-/// $ pprof -svg target/release/deps/rust_analyzer-8739592dc93d63cb crates/rust-analyzer/out.profile > profile.svg
+/// $ nix-shell -p gperftools --run \
+///     'cargo run --release -p rust-analyzer -- parse < ~/projects/rustbench/parser.rs > /dev/null'
 /// ```
 ///
 /// See this diff for how to profile completions:
@@ -90,9 +81,7 @@ pub fn cpu_span() -> CpuSpan {
 
     #[cfg(not(feature = "cpu_profiler"))]
     {
-        eprintln!(
-            r#"cpu profiling is disabled, uncomment `default = [ "cpu_profiler" ]` in Cargo.toml to enable."#
-        )
+        eprintln!("cpu_profiler feature is disabled")
     }
 
     CpuSpan { _private: () }
@@ -102,23 +91,7 @@ impl Drop for CpuSpan {
     fn drop(&mut self) {
         #[cfg(feature = "cpu_profiler")]
         {
-            google_cpu_profiler::stop();
-            let profile_data = std::env::current_dir().unwrap().join("out.profile");
-            eprintln!("Profile data saved to:\n\n    {}\n", profile_data.display());
-            let mut cmd = std::process::Command::new("pprof");
-            cmd.arg("-svg").arg(std::env::current_exe().unwrap()).arg(&profile_data);
-            let out = cmd.output();
-
-            match out {
-                Ok(out) if out.status.success() => {
-                    let svg = profile_data.with_extension("svg");
-                    std::fs::write(&svg, &out.stdout).unwrap();
-                    eprintln!("Profile rendered to:\n\n    {}\n", svg.display());
-                }
-                _ => {
-                    eprintln!("Failed to run:\n\n   {:?}\n", cmd);
-                }
-            }
+            google_cpu_profiler::stop()
         }
     }
 }
diff --git a/crates/rust-analyzer/src/benchmarks.rs b/crates/rust-analyzer/src/benchmarks.rs
deleted file mode 100644
index bf569b40bb..0000000000
--- a/crates/rust-analyzer/src/benchmarks.rs
+++ /dev/null
@@ -1,71 +0,0 @@
-//! Fully integrated benchmarks for rust-analyzer, which load real cargo
-//! projects.
-//!
-//! The benchmark here is used to debug specific performance regressions. If you
-//! notice that, eg, completion is slow in some specific case, you can  modify
-//! code here exercise this specific completion, and thus have a fast
-//! edit/compile/test cycle.
-//!
-//! Note that "Rust Analyzer: Run" action does not allow running a single test
-//! in release mode in VS Code. There's however "Rust Analyzer: Copy Run Command Line"
-//! which you can use to paste the command in terminal and add `--release` manually.
-
-use std::sync::Arc;
-
-use ide::Change;
-use test_utils::project_root;
-use vfs::{AbsPathBuf, VfsPath};
-
-use crate::cli::load_cargo::{load_workspace_at, LoadCargoConfig};
-
-#[test]
-fn benchmark_integrated_highlighting() {
-    // Don't run slow benchmark by default
-    if true {
-        return;
-    }
-
-    // Load rust-analyzer itself.
-    let workspace_to_load = project_root();
-    let file = "./crates/ide_db/src/apply_change.rs";
-
-    let cargo_config = Default::default();
-    let load_cargo_config =
-        LoadCargoConfig { load_out_dirs_from_check: true, with_proc_macro: false };
-
-    let (mut host, vfs, _proc_macro) = {
-        let _it = stdx::timeit("workspace loading");
-        load_workspace_at(&workspace_to_load, &cargo_config, &load_cargo_config, &|_| {}).unwrap()
-    };
-
-    let file_id = {
-        let file = workspace_to_load.join(file);
-        let path = VfsPath::from(AbsPathBuf::assert(file));
-        vfs.file_id(&path).unwrap_or_else(|| panic!("can't find virtual file for {}", path))
-    };
-
-    {
-        let _it = stdx::timeit("initial");
-        let analysis = host.analysis();
-        analysis.highlight_as_html(file_id, false).unwrap();
-    }
-
-    profile::init_from("*>100");
-    // let _s = profile::heartbeat_span();
-
-    {
-        let _it = stdx::timeit("change");
-        let mut text = host.analysis().file_text(file_id).unwrap().to_string();
-        text.push_str("\npub fn _dummy() {}\n");
-        let mut change = Change::new();
-        change.change_file(file_id, Some(Arc::new(text)));
-        host.apply_change(change);
-    }
-
-    {
-        let _it = stdx::timeit("after change");
-        let _span = profile::cpu_span();
-        let analysis = host.analysis();
-        analysis.highlight_as_html(file_id, false).unwrap();
-    }
-}
diff --git a/crates/rust-analyzer/src/bin/flags.rs b/crates/rust-analyzer/src/bin/flags.rs
index b05fc00b95..d8987633d1 100644
--- a/crates/rust-analyzer/src/bin/flags.rs
+++ b/crates/rust-analyzer/src/bin/flags.rs
@@ -1,9 +1,10 @@
 //! Grammar for the command-line arguments.
 #![allow(unreachable_pub)]
-use std::path::PathBuf;
+use std::{env, path::PathBuf};
 
 use ide_ssr::{SsrPattern, SsrRule};
-use rust_analyzer::cli::Verbosity;
+use rust_analyzer::cli::{BenchWhat, Position, Verbosity};
+use vfs::AbsPathBuf;
 
 xflags::xflags! {
     src "./src/bin/flags.rs"
@@ -73,6 +74,27 @@ xflags::xflags! {
             optional --with-proc-macro
         }
 
+        /// Benchmark specific analysis operation
+        cmd analysis-bench
+            /// Directory with Cargo.toml.
+            required path: PathBuf
+        {
+            /// Collect memory usage statistics.
+            optional --memory-usage
+
+            /// Compute syntax highlighting for this file
+            optional --highlight path: PathBuf
+            /// Compute completions at file:line:column location.
+            optional --complete location: Position
+            /// Compute goto definition at file:line:column location.
+            optional --goto-def location: Position
+
+            /// Load OUT_DIR values by running `cargo check` before analysis.
+            optional --load-output-dirs
+            /// Use proc-macro-srv for proc-macro expanding.
+            optional --with-proc-macro
+        }
+
         cmd diagnostics
             /// Directory with Cargo.toml.
             required path: PathBuf
@@ -120,6 +142,7 @@ pub enum RustAnalyzerCmd {
     Symbols(Symbols),
     Highlight(Highlight),
     AnalysisStats(AnalysisStats),
+    AnalysisBench(AnalysisBench),
     Diagnostics(Diagnostics),
     Ssr(Ssr),
     Search(Search),
@@ -160,6 +183,18 @@ pub struct AnalysisStats {
     pub with_proc_macro: bool,
 }
 
+#[derive(Debug)]
+pub struct AnalysisBench {
+    pub path: PathBuf,
+
+    pub memory_usage: bool,
+    pub highlight: Option<PathBuf>,
+    pub complete: Option<Position>,
+    pub goto_def: Option<Position>,
+    pub load_output_dirs: bool,
+    pub with_proc_macro: bool,
+}
+
 #[derive(Debug)]
 pub struct Diagnostics {
     pub path: PathBuf,
@@ -204,3 +239,17 @@ impl RustAnalyzer {
         }
     }
 }
+
+impl AnalysisBench {
+    pub(crate) fn what(&self) -> BenchWhat {
+        match (&self.highlight, &self.complete, &self.goto_def) {
+            (Some(path), None, None) => {
+                let path = env::current_dir().unwrap().join(path);
+                BenchWhat::Highlight { path: AbsPathBuf::assert(path) }
+            }
+            (None, Some(position), None) => BenchWhat::Complete(position.clone()),
+            (None, None, Some(position)) => BenchWhat::GotoDef(position.clone()),
+            _ => panic!("exactly one of  `--highlight`, `--complete` or `--goto-def` must be set"),
+        }
+    }
+}
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs
index ae99eefe3e..a0b611bffc 100644
--- a/crates/rust-analyzer/src/bin/main.rs
+++ b/crates/rust-analyzer/src/bin/main.rs
@@ -9,7 +9,7 @@ use std::{convert::TryFrom, env, fs, path::Path, process};
 use lsp_server::Connection;
 use project_model::ProjectManifest;
 use rust_analyzer::{
-    cli::{self, AnalysisStatsCmd},
+    cli::{self, AnalysisStatsCmd, BenchCmd},
     config::Config,
     from_json,
     lsp_ext::supports_utf8,
@@ -80,6 +80,17 @@ fn try_main() -> Result<()> {
             with_proc_macro: cmd.with_proc_macro,
         }
         .run(verbosity)?,
+        flags::RustAnalyzerCmd::AnalysisBench(cmd) => {
+            let what = cmd.what();
+            BenchCmd {
+                memory_usage: cmd.memory_usage,
+                path: cmd.path,
+                load_output_dirs: cmd.load_output_dirs,
+                with_proc_macro: cmd.with_proc_macro,
+                what,
+            }
+            .run(verbosity)?
+        }
 
         flags::RustAnalyzerCmd::Diagnostics(cmd) => {
             cli::diagnostics(&cmd.path, cmd.load_output_dirs, cmd.with_proc_macro)?
diff --git a/crates/rust-analyzer/src/cli.rs b/crates/rust-analyzer/src/cli.rs
index 76b666dc27..ed732eb387 100644
--- a/crates/rust-analyzer/src/cli.rs
+++ b/crates/rust-analyzer/src/cli.rs
@@ -1,7 +1,8 @@
 //! Various batch processing tasks, intended primarily for debugging.
 
-pub(crate) mod load_cargo;
+mod load_cargo;
 mod analysis_stats;
+mod analysis_bench;
 mod diagnostics;
 mod progress_report;
 mod ssr;
@@ -14,6 +15,7 @@ use syntax::{AstNode, SourceFile};
 use vfs::Vfs;
 
 pub use self::{
+    analysis_bench::{BenchCmd, BenchWhat, Position},
     analysis_stats::AnalysisStatsCmd,
     diagnostics::diagnostics,
     load_cargo::{load_workspace, load_workspace_at, LoadCargoConfig},
diff --git a/crates/rust-analyzer/src/cli/analysis_bench.rs b/crates/rust-analyzer/src/cli/analysis_bench.rs
new file mode 100644
index 0000000000..49994824f5
--- /dev/null
+++ b/crates/rust-analyzer/src/cli/analysis_bench.rs
@@ -0,0 +1,196 @@
+//! Benchmark operations like highlighting or goto definition.
+
+use std::{env, path::PathBuf, str::FromStr, sync::Arc, time::Instant};
+
+use anyhow::{bail, format_err, Result};
+use hir::PrefixKind;
+use ide::{
+    Analysis, AnalysisHost, Change, CompletionConfig, DiagnosticsConfig, FilePosition, LineCol,
+};
+use ide_db::{
+    base_db::{
+        salsa::{Database, Durability},
+        FileId,
+    },
+    helpers::{insert_use::InsertUseConfig, SnippetCap},
+};
+use vfs::AbsPathBuf;
+
+use crate::cli::{
+    load_cargo::{load_workspace_at, LoadCargoConfig},
+    print_memory_usage, Verbosity,
+};
+
+pub struct BenchCmd {
+    pub path: PathBuf,
+    pub what: BenchWhat,
+    pub memory_usage: bool,
+    pub load_output_dirs: bool,
+    pub with_proc_macro: bool,
+}
+
+pub enum BenchWhat {
+    Highlight { path: AbsPathBuf },
+    Complete(Position),
+    GotoDef(Position),
+}
+
+#[derive(Debug, Clone)]
+pub struct Position {
+    pub path: AbsPathBuf,
+    pub line: u32,
+    pub column: u32,
+}
+
+impl FromStr for Position {
+    type Err = anyhow::Error;
+    fn from_str(s: &str) -> Result<Self> {
+        let mut split = s.rsplitn(3, ':');
+        match (split.next(), split.next(), split.next()) {
+            (Some(column), Some(line), Some(path)) => {
+                let path = env::current_dir().unwrap().join(path);
+                let path = AbsPathBuf::assert(path);
+                Ok(Position { path, line: line.parse()?, column: column.parse()? })
+            }
+            _ => bail!("position should be in file:line:column format: {:?}", s),
+        }
+    }
+}
+
+impl BenchCmd {
+    pub fn run(self, verbosity: Verbosity) -> Result<()> {
+        profile::init();
+
+        let start = Instant::now();
+        eprint!("loading: ");
+
+        let cargo_config = Default::default();
+        let load_cargo_config = LoadCargoConfig {
+            load_out_dirs_from_check: self.load_output_dirs,
+            with_proc_macro: self.with_proc_macro,
+        };
+        let (mut host, vfs, _proc_macro) =
+            load_workspace_at(&self.path, &cargo_config, &load_cargo_config, &|_| {})?;
+        eprintln!("{:?}\n", start.elapsed());
+
+        let file_id = {
+            let path = match &self.what {
+                BenchWhat::Highlight { path } => path,
+                BenchWhat::Complete(pos) | BenchWhat::GotoDef(pos) => &pos.path,
+            };
+            let path = path.clone().into();
+            vfs.file_id(&path).ok_or_else(|| format_err!("Can't find {}", path))?
+        };
+
+        match &self.what {
+            BenchWhat::Highlight { .. } => {
+                let res = do_work(&mut host, file_id, |analysis| {
+                    analysis.diagnostics(&DiagnosticsConfig::default(), file_id).unwrap();
+                    analysis.highlight_as_html(file_id, false).unwrap()
+                });
+                if verbosity.is_verbose() {
+                    println!("\n{}", res);
+                }
+            }
+            BenchWhat::Complete(pos) | BenchWhat::GotoDef(pos) => {
+                let is_completion = matches!(self.what, BenchWhat::Complete(..));
+
+                let offset = host
+                    .analysis()
+                    .file_line_index(file_id)?
+                    .offset(LineCol { line: pos.line - 1, col: pos.column });
+                let file_position = FilePosition { file_id, offset };
+
+                if is_completion {
+                    let options = CompletionConfig {
+                        enable_postfix_completions: true,
+                        enable_imports_on_the_fly: true,
+                        add_call_parenthesis: true,
+                        add_call_argument_snippets: true,
+                        snippet_cap: SnippetCap::new(true),
+                        insert_use: InsertUseConfig {
+                            merge: None,
+                            prefix_kind: PrefixKind::Plain,
+                            group: true,
+                        },
+                    };
+                    let res = do_work(&mut host, file_id, |analysis| {
+                        analysis.completions(&options, file_position)
+                    });
+                    if verbosity.is_verbose() {
+                        println!("\n{:#?}", res);
+                    }
+                } else {
+                    let res = do_work(&mut host, file_id, |analysis| {
+                        analysis.goto_definition(file_position)
+                    });
+                    if verbosity.is_verbose() {
+                        println!("\n{:#?}", res);
+                    }
+                }
+            }
+        }
+
+        if self.memory_usage {
+            print_memory_usage(host, vfs);
+        }
+
+        Ok(())
+    }
+}
+
+fn do_work<F: Fn(&Analysis) -> T, T>(host: &mut AnalysisHost, file_id: FileId, work: F) -> T {
+    {
+        let start = Instant::now();
+        eprint!("from scratch:   ");
+        work(&host.analysis());
+        eprintln!("{:?}", start.elapsed());
+    }
+    {
+        let start = Instant::now();
+        eprint!("no change:      ");
+        work(&host.analysis());
+        eprintln!("{:?}", start.elapsed());
+    }
+    {
+        let start = Instant::now();
+        eprint!("trivial change: ");
+        host.raw_database_mut().salsa_runtime_mut().synthetic_write(Durability::LOW);
+        work(&host.analysis());
+        eprintln!("{:?}", start.elapsed());
+    }
+    {
+        let start = Instant::now();
+        eprint!("comment change: ");
+        {
+            let mut text = host.analysis().file_text(file_id).unwrap().to_string();
+            text.push_str("\n/* Hello world */\n");
+            let mut change = Change::new();
+            change.change_file(file_id, Some(Arc::new(text)));
+            host.apply_change(change);
+        }
+        work(&host.analysis());
+        eprintln!("{:?}", start.elapsed());
+    }
+    {
+        let start = Instant::now();
+        eprint!("item change:    ");
+        {
+            let mut text = host.analysis().file_text(file_id).unwrap().to_string();
+            text.push_str("\npub fn _dummy() {}\n");
+            let mut change = Change::new();
+            change.change_file(file_id, Some(Arc::new(text)));
+            host.apply_change(change);
+        }
+        work(&host.analysis());
+        eprintln!("{:?}", start.elapsed());
+    }
+    {
+        let start = Instant::now();
+        eprint!("const change:   ");
+        host.raw_database_mut().salsa_runtime_mut().synthetic_write(Durability::HIGH);
+        let res = work(&host.analysis());
+        eprintln!("{:?}", start.elapsed());
+        res
+    }
+}
diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs
index d9a5030a0f..8b874239c7 100644
--- a/crates/rust-analyzer/src/lib.rs
+++ b/crates/rust-analyzer/src/lib.rs
@@ -39,9 +39,6 @@ mod op_queue;
 pub mod lsp_ext;
 pub mod config;
 
-#[cfg(test)]
-mod benchmarks;
-
 use serde::de::DeserializeOwned;
 use std::fmt;
 
diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs
index 2dc8a42f1e..a3c5e9ccf4 100644
--- a/crates/rust-analyzer/src/semantic_tokens.rs
+++ b/crates/rust-analyzer/src/semantic_tokens.rs
@@ -88,7 +88,6 @@ define_semantic_token_modifiers![
     (CONSUMING, "consuming"),
     (UNSAFE, "unsafe"),
     (ATTRIBUTE_MODIFIER, "attribute"),
-    (TRAIT_MODIFIER, "trait"),
     (CALLABLE, "callable"),
     (INTRA_DOC_LINK, "intraDocLink"),
 ];
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index c3820944bc..e297a72e64 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -474,7 +474,6 @@ fn semantic_token_type_and_modifiers(
             HlMod::Callable => semantic_tokens::CALLABLE,
             HlMod::Static => lsp_types::SemanticTokenModifier::STATIC,
             HlMod::IntraDocLink => semantic_tokens::INTRA_DOC_LINK,
-            HlMod::Trait => semantic_tokens::TRAIT_MODIFIER,
             HlMod::Associated => continue,
         };
         mods |= modifier;
diff --git a/docs/dev/README.md b/docs/dev/README.md
index eab21a7653..dcbab1a1d5 100644
--- a/docs/dev/README.md
+++ b/docs/dev/README.md
@@ -1,7 +1,7 @@
 # Contributing Quick Start
 
-Rust Analyzer is an ordinary Rust project, which is organized as a Cargo workspace, builds on stable and doesn't depend on C libraries.
-So, just
+Rust Analyzer is an ordinary Rust project, which is organized as a Cargo
+workspace, builds on stable and doesn't depend on C libraries. So, just
 
 ```
 $ cargo test
@@ -13,8 +13,9 @@ To learn more about how rust-analyzer works, see [./architecture.md](./architect
 It also explains the high-level layout of the source code.
 Do skim through that document.
 
-We also publish rustdoc docs to pages: https://rust-analyzer.github.io/rust-analyzer/ide/.
-Note though, that internal documentation is very incomplete.
+We also publish rustdoc docs to pages:
+
+https://rust-analyzer.github.io/rust-analyzer/ide/
 
 Various organizational and process issues are discussed in this document.
 
@@ -48,28 +49,21 @@ https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0
   Also a kind of fun.
   These issues should generally include a link to a Zulip discussion thread.
 
-# Code Style & Review Process
-
-Do see [./style.md](./style.md).
+# CI
 
-# Cookbook
-
-## CI
-
-We use GitHub Actions for CI.
-Most of the things, including formatting, are checked by `cargo test`.
-If `cargo test` passes locally, that's a good sign that CI will be green as well.
-The only exception is that some long-running tests are skipped locally by default.
+We use GitHub Actions for CI. Most of the things, including formatting, are checked by
+`cargo test` so, if `cargo test` passes locally, that's a good sign that CI will
+be green as well. The only exception is that some long-running tests are skipped locally by default.
 Use `env RUN_SLOW_TESTS=1 cargo test` to run the full suite.
 
 We use bors-ng to enforce the [not rocket science](https://graydon2.dreamwidth.org/1597.html) rule.
 
-## Launching rust-analyzer
+# Launching rust-analyzer
 
 Debugging the language server can be tricky.
 LSP is rather chatty, so driving it from the command line is not really feasible, driving it via VS Code requires interacting with two processes.
 
-For this reason, the best way to see how rust-analyzer works is to **find a relevant test and execute it**.
+For this reason, the best way to see how rust-analyzer works is to find a relevant test and execute it.
 VS Code & Emacs include an action for running a single test.
 
 Launching a VS Code instance with a locally built language server is also possible.
@@ -113,7 +107,12 @@ cd editors/code
 npm ci
 npm run lint
 ```
-## How to ...
+
+# Code Style & Review Process
+
+Do see [./style.md](./style.md).
+
+# How to ...
 
 * ... add an assist? [#7535](https://github.com/rust-analyzer/rust-analyzer/pull/7535)
 * ... add a new protocol extension? [#4569](https://github.com/rust-analyzer/rust-analyzer/pull/4569)
@@ -121,17 +120,18 @@ npm run lint
 * ... add a new completion? [#6964](https://github.com/rust-analyzer/rust-analyzer/pull/6964)
 * ... allow new syntax in the parser? [#7338](https://github.com/rust-analyzer/rust-analyzer/pull/7338)
 
-## Logging
+# Logging
 
-Logging is done by both rust-analyzer and VS Code, so it might be tricky to figure out where logs go.
+Logging is done by both rust-analyzer and VS Code, so it might be tricky to
+figure out where logs go.
 
-Inside rust-analyzer, we use the standard `log` crate for logging, and `env_logger` for logging frontend.
-By default, log goes to stderr, but the stderr itself is processed by VS Code.
-`--log-file <PATH>` CLI argument allows logging to file.
+Inside rust-analyzer, we use the standard `log` crate for logging, and
+`env_logger` for logging frontend. By default, log goes to stderr, but the
+stderr itself is processed by VS Code.
 
-To see stderr in the running VS Code instance, go to the "Output" tab of the panel and select `rust-analyzer`.
-This shows `eprintln!` as well.
-Note that `stdout` is used for the actual protocol, so `println!` will break things.
+To see stderr in the running VS Code instance, go to the "Output" tab of the
+panel and select `rust-analyzer`. This shows `eprintln!` as well. Note that
+`stdout` is used for the actual protocol, so `println!` will break things.
 
 To log all communication between the server and the client, there are two choices:
 
@@ -139,12 +139,17 @@ To log all communication between the server and the client, there are two choice
   ```
   env RA_LOG=lsp_server=debug code .
   ```
-* You can log on the client side, by enabling `"rust-analyzer.trace.server": "verbose"` workspace setting.
-  These logs are shown in a separate tab in the output and could be used with LSP inspector.
-  Kudos to [@DJMcNab](https://github.com/DJMcNab) for setting this awesome infra up!
 
+  By default, logs go to stderr, `--log-file <PATH>` CLI argument overrides
+  that.
 
-There are also several VS Code commands which might be of interest:
+* You can log on the client side, by enabling `"rust-analyzer.trace.server":
+  "verbose"` workspace setting. These logs are shown in a separate tab in the
+  output and could be used with LSP inspector. Kudos to
+  [@DJMcNab](https://github.com/DJMcNab) for setting this awesome infra up!
+
+
+There are also two VS Code commands which might be of interest:
 
 * `Rust Analyzer: Status` shows some memory-usage statistics.
 
@@ -162,7 +167,7 @@ There are also several VS Code commands which might be of interest:
 
   ![demo](https://user-images.githubusercontent.com/36276403/78225773-6636a480-74d3-11ea-9d9f-1c9d42da03b0.png)
 
-## Profiling
+# Profiling
 
 We have a built-in hierarchical profiler, you can enable it by using `RA_PROFILE` env-var:
 
@@ -190,9 +195,7 @@ $ cargo run --release -p rust-analyzer -- analysis-bench ../chalk/ --highlight .
 $ cargo run --release -p rust-analyzer -- analysis-bench ../chalk/ --complete ../chalk/chalk-engine/src/logic.rs:94:0
 ```
 
-Look for `fn benchmark_xxx` tests for a quick way to reproduce performance problems.
-
-## Release Process
+# Release Process
 
 Release process is handled by `release`, `dist` and `promote` xtasks, `release` being the main one.
 
@@ -229,7 +232,7 @@ Make sure to remove the new changelog post created when running `cargo xtask rel
 We release "nightly" every night automatically and promote the latest nightly to "stable" manually, every week.
 We don't do "patch" releases, unless something truly egregious comes up.
 
-## Permissions
+# Permissions
 
 There are three sets of people with extra permissions:
 
diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc
index e74b287fb7..b1beeb8838 100644
--- a/docs/user/manual.adoc
+++ b/docs/user/manual.adoc
@@ -254,10 +254,23 @@ let g:LanguageClient_serverCommands = {
 
 ==== YouCompleteMe
 
-Install YouCompleteMe by following the instructions
-  https://github.com/ycm-core/YouCompleteMe#installation[here].
+1. Install YouCompleteMe by following the instructions
+  https://github.com/ycm-core/lsp-examples#rust-rust-analyzer[here]
 
-rust-analyzer is the default in ycm, it should work out of the box.
+2. Configure by adding this to your vim/neovim config file (replacing the existing Rust-specific line if it exists):
++
+[source,vim]
+----
+let g:ycm_language_server =
+\ [
+\   {
+\     'name': 'rust',
+\     'cmdline': ['rust-analyzer'],
+\     'filetypes': ['rust'],
+\     'project_root_files': ['Cargo.toml']
+\   }
+\ ]
+----
 
 ==== ALE
 


Test patch:
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html b/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html
index 8cde3906c3..4635ea9276 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html
@@ -47,12 +47,12 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 <span class="brace">}</span>
 
 <span class="keyword">trait</span> <span class="trait declaration">t</span> <span class="brace">{</span>
-    <span class="keyword">fn</span> <span class="function declaration static associated trait">t_is_static</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
-    <span class="keyword">fn</span> <span class="function declaration associated trait">t_is_not_static</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
+    <span class="keyword">fn</span> <span class="function declaration static associated">t_is_static</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
+    <span class="keyword">fn</span> <span class="function declaration associated">t_is_not_static</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
 <span class="brace">}</span>
 
 <span class="keyword">impl</span> <span class="trait">t</span> <span class="keyword">for</span> <span class="struct">foo</span> <span class="brace">{</span>
-    <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration static associated trait">is_static</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
-    <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration associated trait">is_not_static</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
+    <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration static associated">is_static</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
+    <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration associated">is_not_static</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
 <span class="brace">}</span>
         </code></pre>
\ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
index 7c6694a276..9215ddd9e3 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
@@ -42,7 +42,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 <span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
     <span class="function">fixture</span><span class="parenthesis">(</span><span class="string_literal">r#"</span>
         <span class="keyword">trait</span> <span class="trait declaration">Foo</span> <span class="brace">{</span>
-            <span class="keyword">fn</span> <span class="function declaration static associated trait">foo</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
+            <span class="keyword">fn</span> <span class="function declaration static associated">foo</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
                 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"2 + 2 = {}"</span><span class="comma">,</span> <span class="numeric_literal">4</span><span class="parenthesis">)</span><span class="semicolon">;</span>
             <span class="brace">}</span>
         <span class="brace">}</span><span class="string_literal">"#</span>
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
index 72910421dc..6a6555208c 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
@@ -62,11 +62,11 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 <span class="brace">}</span>
 
 <span class="keyword">trait</span> <span class="trait declaration">DoTheAutoref</span> <span class="brace">{</span>
-    <span class="keyword">fn</span> <span class="function declaration associated trait">calls_autoref</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="keyword">fn</span> <span class="function declaration associated">calls_autoref</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span><span class="semicolon">;</span>
 <span class="brace">}</span>
 
 <span class="keyword">impl</span> <span class="trait">DoTheAutoref</span> <span class="keyword">for</span> <span class="builtin_type">u16</span> <span class="brace">{</span>
-    <span class="keyword">fn</span> <span class="function declaration associated trait">calls_autoref</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
+    <span class="keyword">fn</span> <span class="function declaration associated">calls_autoref</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
 <span class="brace">}</span>
 
 <span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
@@ -96,6 +96,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
         <span class="keyword">let</span> <span class="struct">Packed</span> <span class="brace">{</span> <span class="field">a</span><span class="colon">:</span> <span class="keyword unsafe">ref</span> <span class="variable declaration">_a</span> <span class="brace">}</span> <span class="operator">=</span> <span class="variable">packed</span><span class="semicolon">;</span>
 
         <span class="comment">// unsafe auto ref of packed field</span>
-        <span class="variable">packed</span><span class="operator">.</span><span class="field">a</span><span class="operator">.</span><span class="function associated trait unsafe">calls_autoref</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+        <span class="variable">packed</span><span class="operator">.</span><span class="field">a</span><span class="operator">.</span><span class="function associated unsafe">calls_autoref</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
     <span class="brace">}</span>
 <span class="brace">}</span></code></pre>
\ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
index 973173254c..1eaa7b75bc 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
@@ -67,11 +67,11 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 <span class="brace">}</span>
 
 <span class="keyword">trait</span> <span class="trait declaration">Bar</span> <span class="brace">{</span>
-    <span class="keyword">fn</span> <span class="function declaration associated trait">bar</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span><span class="semicolon">;</span>
+    <span class="keyword">fn</span> <span class="function declaration associated">bar</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span><span class="semicolon">;</span>
 <span class="brace">}</span>
 
 <span class="keyword">impl</span> <span class="trait">Bar</span> <span class="keyword">for</span> <span class="struct">Foo</span> <span class="brace">{</span>
-    <span class="keyword">fn</span> <span class="function declaration associated trait">bar</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="brace">{</span>
+    <span class="keyword">fn</span> <span class="function declaration associated">bar</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="brace">{</span>
         <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span>
     <span class="brace">}</span>
 <span class="brace">}</span>