Repository: rust-analyzer/rust-analyzer
Pull request #4760 (2020-06-05 15:02:02)

Minimize FileLoader interface


bors r+
🤖

bors[bot]: Build failed:
  * [Rust (ubuntu-latest)](https://github.com/rust-analyzer/rust-analyzer/runs/742700247)
matklad: bors r+
bors[bot]: Build failed:
  * [Rust (windows-latest)](https://github.com/rust-analyzer/rust-analyzer/runs/742719996)

Patch:
diff --git a/crates/ra_db/src/lib.rs b/crates/ra_db/src/lib.rs
index fcfa2788c6..7354b3832b 100644
--- a/crates/ra_db/src/lib.rs
+++ b/crates/ra_db/src/lib.rs
@@ -11,8 +11,8 @@ use ra_syntax::{ast, Parse, SourceFile, TextRange, TextSize};
 pub use crate::{
     cancellation::Canceled,
     input::{
-        CrateData, CrateGraph, CrateId, CrateName, Dependency, Edition, Env, ExternSource,
-        ExternSourceId, FileId, ProcMacroId, SourceRoot, SourceRootId,
+        CrateGraph, CrateId, CrateName, Dependency, Edition, Env, ExternSource, ExternSourceId,
+        FileId, ProcMacroId, SourceRoot, SourceRootId,
     },
 };
 pub use relative_path::{RelativePath, RelativePathBuf};
@@ -89,14 +89,13 @@ pub const DEFAULT_LRU_CAP: usize = 128;
 pub trait FileLoader {
     /// Text of the file.
     fn file_text(&self, file_id: FileId) -> Arc<String>;
+    /// Note that we intentionally accept a `&str` and not a `&Path` here. This
+    /// method exists to handle `#[path = "/some/path.rs"] mod foo;` and such,
+    /// so the input is guaranteed to be utf-8 string. We might introduce
+    /// `struct StrPath(str)` for clarity some day, but it's a bit messy, so we
+    /// get by with a `&str` for the time being.
     fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId>;
     fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>>;
-
-    fn resolve_extern_path(
-        &self,
-        extern_id: ExternSourceId,
-        relative_path: &RelativePath,
-    ) -> Option<FileId>;
 }
 
 /// Database which stores all significant input facts: source code and project
@@ -154,34 +153,30 @@ impl<T: SourceDatabaseExt> FileLoader for FileLoaderDelegate<&'_ T> {
     fn file_text(&self, file_id: FileId) -> Arc<String> {
         SourceDatabaseExt::file_text(self.0, file_id)
     }
-    /// Note that we intentionally accept a `&str` and not a `&Path` here. This
-    /// method exists to handle `#[path = "/some/path.rs"] mod foo;` and such,
-    /// so the input is guaranteed to be utf-8 string. We might introduce
-    /// `struct StrPath(str)` for clarity some day, but it's a bit messy, so we
-    /// get by with a `&str` for the time being.
     fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> {
-        let rel_path = {
-            let mut rel_path = self.0.file_relative_path(anchor);
-            assert!(rel_path.pop());
-            rel_path.push(path);
-            rel_path.normalize()
-        };
-        let source_root = self.0.file_source_root(anchor);
-        let source_root = self.0.source_root(source_root);
-        source_root.file_by_relative_path(&rel_path)
+        // FIXME: this *somehow* should be platform agnostic...
+        if std::path::Path::new(path).is_absolute() {
+            let krate = *self.relevant_crates(anchor).get(0)?;
+            let (extern_source_id, relative_file) =
+                self.0.crate_graph()[krate].extern_source.extern_path(path)?;
+
+            let source_root = self.0.source_root(SourceRootId(extern_source_id.0));
+            source_root.file_by_relative_path(&relative_file)
+        } else {
+            let rel_path = {
+                let mut rel_path = self.0.file_relative_path(anchor);
+                assert!(rel_path.pop());
+                rel_path.push(path);
+                rel_path.normalize()
+            };
+            let source_root = self.0.file_source_root(anchor);
+            let source_root = self.0.source_root(source_root);
+            source_root.file_by_relative_path(&rel_path)
+        }
     }
 
     fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> {
         let source_root = self.0.file_source_root(file_id);
         self.0.source_root_crates(source_root)
     }
-
-    fn resolve_extern_path(
-        &self,
-        extern_id: ExternSourceId,
-        relative_path: &RelativePath,
-    ) -> Option<FileId> {
-        let source_root = self.0.source_root(SourceRootId(extern_id.0));
-        source_root.file_by_relative_path(&relative_path)
-    }
 }
diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs
index 098b665292..ec931b34fe 100644
--- a/crates/ra_hir/src/db.rs
+++ b/crates/ra_hir/src/db.rs
@@ -3,11 +3,11 @@
 pub use hir_def::db::{
     AttrsQuery, BodyQuery, BodyWithSourceMapQuery, ConstDataQuery, CrateDefMapQueryQuery,
     CrateLangItemsQuery, DefDatabase, DefDatabaseStorage, DocumentationQuery, EnumDataQuery,
-    ExprScopesQuery, FunctionDataQuery, GenericParamsQuery, ImplDataQuery, ImportMapQuery,
-    InternConstQuery, InternDatabase, InternDatabaseStorage, InternEnumQuery, InternFunctionQuery,
-    InternImplQuery, InternStaticQuery, InternStructQuery, InternTraitQuery, InternTypeAliasQuery,
-    InternUnionQuery, LangItemQuery, ModuleLangItemsQuery, RawItemsQuery, StaticDataQuery,
-    StructDataQuery, TraitDataQuery, TypeAliasDataQuery, UnionDataQuery,
+    ExprScopesQuery, FunctionDataQuery, GenericParamsQuery, ImplDataQuery, InternConstQuery,
+    InternDatabase, InternDatabaseStorage, InternEnumQuery, InternFunctionQuery, InternImplQuery,
+    InternStaticQuery, InternStructQuery, InternTraitQuery, InternTypeAliasQuery, InternUnionQuery,
+    LangItemQuery, ModuleLangItemsQuery, RawItemsQuery, StaticDataQuery, StructDataQuery,
+    TraitDataQuery, TypeAliasDataQuery, UnionDataQuery,
 };
 pub use hir_expand::db::{
     AstDatabase, AstDatabaseStorage, AstIdMapQuery, InternEagerExpansionQuery, InternMacroQuery,
diff --git a/crates/ra_hir_def/src/db.rs b/crates/ra_hir_def/src/db.rs
index 10cc26480f..945a0025e5 100644
--- a/crates/ra_hir_def/src/db.rs
+++ b/crates/ra_hir_def/src/db.rs
@@ -1,7 +1,7 @@
 //! Defines database & queries for name resolution.
 use std::sync::Arc;
 
-use hir_expand::{db::AstDatabase, HirFileId};
+use hir_expand::{db::AstDatabase, name::Name, HirFileId};
 use ra_db::{salsa, CrateId, SourceDatabase, Upcast};
 use ra_prof::profile;
 use ra_syntax::SmolStr;
@@ -12,10 +12,13 @@ use crate::{
     body::{scope::ExprScopes, Body, BodySourceMap},
     data::{ConstData, FunctionData, ImplData, StaticData, TraitData, TypeAliasData},
     docs::Documentation,
+    find_path,
     generics::GenericParams,
-    import_map::ImportMap,
+    item_scope::ItemInNs,
     lang_item::{LangItemTarget, LangItems},
     nameres::{raw::RawItems, CrateDefMap},
+    path::ModPath,
+    visibility::Visibility,
     AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc,
     GenericDefId, ImplId, ImplLoc, ModuleId, StaticId, StaticLoc, StructId, StructLoc, TraitId,
     TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc,
@@ -110,8 +113,15 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> {
     #[salsa::invoke(Documentation::documentation_query)]
     fn documentation(&self, def: AttrDefId) -> Option<Documentation>;
 
-    #[salsa::invoke(ImportMap::import_map_query)]
-    fn import_map(&self, krate: CrateId) -> Arc<ImportMap>;
+    #[salsa::invoke(find_path::importable_locations_of_query)]
+    fn importable_locations_of(
+        &self,
+        item: ItemInNs,
+        krate: CrateId,
+    ) -> Arc<[(ModuleId, Name, Visibility)]>;
+
+    #[salsa::invoke(find_path::find_path_inner_query)]
+    fn find_path_inner(&self, item: ItemInNs, from: ModuleId, max_len: usize) -> Option<ModPath>;
 }
 
 fn crate_def_map_wait(db: &impl DefDatabase, krate: CrateId) -> Arc<CrateDefMap> {
diff --git a/crates/ra_hir_def/src/find_path.rs b/crates/ra_hir_def/src/find_path.rs
index a7f59e0287..4db7984730 100644
--- a/crates/ra_hir_def/src/find_path.rs
+++ b/crates/ra_hir_def/src/find_path.rs
@@ -1,8 +1,9 @@
 //! An algorithm to find a path to refer to a certain item.
 
+use std::sync::Arc;
+
 use hir_expand::name::{known, AsName, Name};
 use ra_prof::profile;
-use rustc_hash::FxHashSet;
 use test_utils::mark;
 
 use crate::{
@@ -10,7 +11,7 @@ use crate::{
     item_scope::ItemInNs,
     path::{ModPath, PathKind},
     visibility::Visibility,
-    ModuleDefId, ModuleId,
+    CrateId, ModuleDefId, ModuleId,
 };
 
 // FIXME: handle local items
@@ -19,7 +20,7 @@ use crate::{
 /// *from where* you're referring to the item, hence the `from` parameter.
 pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> {
     let _p = profile("find_path");
-    find_path_inner(db, item, from, MAX_PATH_LEN)
+    db.find_path_inner(item, from, MAX_PATH_LEN)
 }
 
 const MAX_PATH_LEN: usize = 15;
@@ -35,9 +36,20 @@ impl ModPath {
         let first_segment = self.segments.first();
         first_segment == Some(&known::alloc) || first_segment == Some(&known::core)
     }
+
+    fn len(&self) -> usize {
+        self.segments.len()
+            + match self.kind {
+                PathKind::Plain => 0,
+                PathKind::Super(i) => i as usize,
+                PathKind::Crate => 1,
+                PathKind::Abs => 0,
+                PathKind::DollarCrate(_) => 1,
+            }
+    }
 }
 
-fn find_path_inner(
+pub(crate) fn find_path_inner_query(
     db: &dyn DefDatabase,
     item: ItemInNs,
     from: ModuleId,
@@ -121,61 +133,31 @@ fn find_path_inner(
     }
 
     // - otherwise, look for modules containing (reexporting) it and import it from one of those
-
     let crate_root = ModuleId { local_id: def_map.root, krate: from.krate };
     let crate_attrs = db.attrs(crate_root.into());
     let prefer_no_std = crate_attrs.by_key("no_std").exists();
+    let importable_locations = find_importable_locations(db, item, from);
     let mut best_path = None;
     let mut best_path_len = max_len;
+    for (module_id, name) in importable_locations {
+        let mut path = match db.find_path_inner(
+            ItemInNs::Types(ModuleDefId::ModuleId(module_id)),
+            from,
+            best_path_len - 1,
+        ) {
+            None => continue,
+            Some(path) => path,
+        };
+        path.segments.push(name);
 
-    if item.krate(db) == Some(from.krate) {
-        // Item was defined in the same crate that wants to import it. It cannot be found in any
-        // dependency in this case.
-
-        let local_imports = find_local_import_locations(db, item, from);
-        for (module_id, name) in local_imports {
-            if let Some(mut path) = find_path_inner(
-                db,
-                ItemInNs::Types(ModuleDefId::ModuleId(module_id)),
-                from,
-                best_path_len - 1,
-            ) {
-                path.segments.push(name);
-
-                let new_path = if let Some(best_path) = best_path {
-                    select_best_path(best_path, path, prefer_no_std)
-                } else {
-                    path
-                };
-                best_path_len = new_path.len();
-                best_path = Some(new_path);
-            }
-        }
-    } else {
-        // Item was defined in some upstream crate. This means that it must be exported from one,
-        // too (unless we can't name it at all). It could *also* be (re)exported by the same crate
-        // that wants to import it here, but we always prefer to use the external path here.
-
-        let crate_graph = db.crate_graph();
-        let extern_paths = crate_graph[from.krate].dependencies.iter().filter_map(|dep| {
-            let import_map = db.import_map(dep.crate_id);
-            import_map.path_of(item).map(|modpath| {
-                let mut modpath = modpath.clone();
-                modpath.segments.insert(0, dep.as_name());
-                modpath
-            })
-        });
-
-        for path in extern_paths {
-            let new_path = if let Some(best_path) = best_path {
-                select_best_path(best_path, path, prefer_no_std)
-            } else {
-                path
-            };
-            best_path = Some(new_path);
-        }
+        let new_path = if let Some(best_path) = best_path {
+            select_best_path(best_path, path, prefer_no_std)
+        } else {
+            path
+        };
+        best_path_len = new_path.len();
+        best_path = Some(new_path);
     }
-
     best_path
 }
 
@@ -203,86 +185,69 @@ fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -
     }
 }
 
-/// Finds locations in `from.krate` from which `item` can be imported by `from`.
-fn find_local_import_locations(
+fn find_importable_locations(
     db: &dyn DefDatabase,
     item: ItemInNs,
     from: ModuleId,
 ) -> Vec<(ModuleId, Name)> {
-    let _p = profile("find_local_import_locations");
-
-    // `from` can import anything below `from` with visibility of at least `from`, and anything
-    // above `from` with any visibility. That means we do not need to descend into private siblings
-    // of `from` (and similar).
-
-    let def_map = db.crate_def_map(from.krate);
-
-    // Compute the initial worklist. We start with all direct child modules of `from` as well as all
-    // of its (recursive) parent modules.
-    let data = &def_map.modules[from.local_id];
-    let mut worklist = data
-        .children
-        .values()
-        .map(|child| ModuleId { krate: from.krate, local_id: *child })
-        .collect::<Vec<_>>();
-    let mut parent = data.parent;
-    while let Some(p) = parent {
-        worklist.push(ModuleId { krate: from.krate, local_id: p });
-        parent = def_map.modules[p].parent;
-    }
-
-    let mut seen: FxHashSet<_> = FxHashSet::default();
-
-    let mut locations = Vec::new();
-    while let Some(module) = worklist.pop() {
-        if !seen.insert(module) {
-            continue; // already processed this module
-        }
-
-        let ext_def_map;
-        let data = if module.krate == from.krate {
-            &def_map[module.local_id]
-        } else {
-            // The crate might reexport a module defined in another crate.
-            ext_def_map = db.crate_def_map(module.krate);
-            &ext_def_map[module.local_id]
-        };
+    let crate_graph = db.crate_graph();
+    let mut result = Vec::new();
+    // We only look in the crate from which we are importing, and the direct
+    // dependencies. We cannot refer to names from transitive dependencies
+    // directly (only through reexports in direct dependencies).
+    for krate in Some(from.krate)
+        .into_iter()
+        .chain(crate_graph[from.krate].dependencies.iter().map(|dep| dep.crate_id))
+    {
+        result.extend(
+            db.importable_locations_of(item, krate)
+                .iter()
+                .filter(|(_, _, vis)| vis.is_visible_from(db, from))
+                .map(|(m, n, _)| (*m, n.clone())),
+        );
+    }
+    result
+}
 
+/// Collects all locations from which we might import the item in a particular
+/// crate. These include the original definition of the item, and any
+/// non-private `use`s.
+///
+/// Note that the crate doesn't need to be the one in which the item is defined;
+/// it might be re-exported in other crates.
+pub(crate) fn importable_locations_of_query(
+    db: &dyn DefDatabase,
+    item: ItemInNs,
+    krate: CrateId,
+) -> Arc<[(ModuleId, Name, Visibility)]> {
+    let _p = profile("importable_locations_of_query");
+    let def_map = db.crate_def_map(krate);
+    let mut result = Vec::new();
+    for (local_id, data) in def_map.modules.iter() {
         if let Some((name, vis)) = data.scope.name_of(item) {
-            if vis.is_visible_from(db, from) {
-                let is_private = if let Visibility::Module(private_to) = vis {
-                    private_to.local_id == module.local_id
-                } else {
-                    false
-                };
-                let is_original_def = if let Some(module_def_id) = item.as_module_def_id() {
-                    data.scope.declarations().any(|it| it == module_def_id)
-                } else {
-                    false
-                };
-
+            let is_private = if let Visibility::Module(private_to) = vis {
+                private_to.local_id == local_id
+            } else {
+                false
+            };
+            let is_original_def = if let Some(module_def_id) = item.as_module_def_id() {
+                data.scope.declarations().any(|it| it == module_def_id)
+            } else {
+                false
+            };
+            if is_private && !is_original_def {
                 // Ignore private imports. these could be used if we are
                 // in a submodule of this module, but that's usually not
                 // what the user wants; and if this module can import
                 // the item and we're a submodule of it, so can we.
                 // Also this keeps the cached data smaller.
-                if !is_private || is_original_def {
-                    locations.push((module, name.clone()));
-                }
-            }
-        }
-
-        // Descend into all modules visible from `from`.
-        for (_, per_ns) in data.scope.entries() {
-            if let Some((ModuleDefId::ModuleId(module), vis)) = per_ns.take_types_vis() {
-                if vis.is_visible_from(db, from) {
-                    worklist.push(module);
-                }
+                continue;
             }
+            result.push((ModuleId { krate, local_id }, name.clone(), vis));
         }
     }
 
-    locations
+    Arc::from(result)
 }
 
 #[cfg(test)]
@@ -420,7 +385,6 @@ mod tests {
 
     #[test]
     fn different_crate_renamed() {
-        // Even if a local path exists, if the item is defined externally, prefer an external path.
         let code = r#"
             //- /main.rs crate:main deps:std
             extern crate std as std_renamed;
@@ -428,7 +392,7 @@ mod tests {
             //- /std.rs crate:std
             pub struct S;
         "#;
-        check_found_path(code, "std::S");
+        check_found_path(code, "std_renamed::S");
     }
 
     #[test]
diff --git a/crates/ra_hir_def/src/import_map.rs b/crates/ra_hir_def/src/import_map.rs
deleted file mode 100644
index 4284a0a912..0000000000
--- a/crates/ra_hir_def/src/import_map.rs
+++ /dev/null
@@ -1,331 +0,0 @@
-//! A map of all publicly exported items in a crate.
-
-use std::{collections::hash_map::Entry, fmt, sync::Arc};
-
-use ra_db::CrateId;
-use rustc_hash::FxHashMap;
-
-use crate::{
-    db::DefDatabase,
-    item_scope::ItemInNs,
-    path::{ModPath, PathKind},
-    visibility::Visibility,
-    ModuleDefId, ModuleId,
-};
-
-/// A map from publicly exported items to the path needed to import/name them from a downstream
-/// crate.
-///
-/// Reexports of items are taken into account, ie. if something is exported under multiple
-/// names, the one with the shortest import path will be used.
-///
-/// Note that all paths are relative to the containing crate's root, so the crate name still needs
-/// to be prepended to the `ModPath` before the path is valid.
-#[derive(Eq, PartialEq)]
-pub struct ImportMap {
-    map: FxHashMap<ItemInNs, ModPath>,
-}
-
-impl ImportMap {
-    pub fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<Self> {
-        let _p = ra_prof::profile("import_map_query");
-        let def_map = db.crate_def_map(krate);
-        let mut import_map = FxHashMap::with_capacity_and_hasher(64, Default::default());
-
-        // We look only into modules that are public(ly reexported), starting with the crate root.
-        let empty = ModPath { kind: PathKind::Plain, segments: vec![] };
-        let root = ModuleId { krate, local_id: def_map.root };
-        let mut worklist = vec![(root, empty)];
-        while let Some((module, mod_path)) = worklist.pop() {
-            let ext_def_map;
-            let mod_data = if module.krate == krate {
-                &def_map[module.local_id]
-            } else {
-                // The crate might reexport a module defined in another crate.
-                ext_def_map = db.crate_def_map(module.krate);
-                &ext_def_map[module.local_id]
-            };
-
-            let visible_items = mod_data.scope.entries().filter_map(|(name, per_ns)| {
-                let per_ns = per_ns.filter_visibility(|vis| vis == Visibility::Public);
-                if per_ns.is_none() {
-                    None
-                } else {
-                    Some((name, per_ns))
-                }
-            });
-
-            for (name, per_ns) in visible_items {
-                let mk_path = || {
-                    let mut path = mod_path.clone();
-                    path.segments.push(name.clone());
-                    path
-                };
-
-                for item in per_ns.iter_items() {
-                    let path = mk_path();
-                    match import_map.entry(item) {
-                        Entry::Vacant(entry) => {
-                            entry.insert(path);
-                        }
-                        Entry::Occupied(mut entry) => {
-                            // If the new path is shorter, prefer that one.
-                            if path.len() < entry.get().len() {
-                                *entry.get_mut() = path;
-                            } else {
-                                continue;
-                            }
-                        }
-                    }
-
-                    // If we've just added a path to a module, descend into it. We might traverse
-                    // modules multiple times, but only if the new path to it is shorter than the
-                    // first (else we `continue` above).
-                    if let Some(ModuleDefId::ModuleId(mod_id)) = item.as_module_def_id() {
-                        worklist.push((mod_id, mk_path()));
-                    }
-                }
-            }
-        }
-
-        Arc::new(Self { map: import_map })
-    }
-
-    /// Returns the `ModPath` needed to import/mention `item`, relative to this crate's root.
-    pub fn path_of(&self, item: ItemInNs) -> Option<&ModPath> {
-        self.map.get(&item)
-    }
-}
-
-impl fmt::Debug for ImportMap {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let mut importable_paths: Vec<_> = self
-            .map
-            .iter()
-            .map(|(item, modpath)| {
-                let ns = match item {
-                    ItemInNs::Types(_) => "t",
-                    ItemInNs::Values(_) => "v",
-                    ItemInNs::Macros(_) => "m",
-                };
-                format!("- {} ({})", modpath, ns)
-            })
-            .collect();
-
-        importable_paths.sort();
-        f.write_str(&importable_paths.join("\n"))
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use crate::test_db::TestDB;
-    use insta::assert_snapshot;
-    use ra_db::fixture::WithFixture;
-    use ra_db::SourceDatabase;
-
-    fn import_map(ra_fixture: &str) -> String {
-        let db = TestDB::with_files(ra_fixture);
-        let crate_graph = db.crate_graph();
-
-        let import_maps: Vec<_> = crate_graph
-            .iter()
-            .filter_map(|krate| {
-                let cdata = &crate_graph[krate];
-                let name = cdata.display_name.as_ref()?;
-
-                let map = db.import_map(krate);
-
-                Some(format!("{}:\n{:?}", name, map))
-            })
-            .collect();
-
-        import_maps.join("\n")
-    }
-
-    #[test]
-    fn smoke() {
-        let map = import_map(
-            r"
-            //- /main.rs crate:main deps:lib
-
-            mod private {
-                pub use lib::Pub;
-                pub struct InPrivateModule;
-            }
-
-            pub mod publ1 {
-                use lib::Pub;
-            }
-
-            pub mod real_pub {
-                pub use lib::Pub;
-            }
-            pub mod real_pu2 { // same path length as above
-                pub use lib::Pub;
-            }
-
-            //- /lib.rs crate:lib
-            pub struct Pub {}
-            pub struct Pub2; // t + v
-            struct Priv;
-        ",
-        );
-
-        assert_snapshot!(map, @r###"
-        main:
-        - publ1 (t)
-        - real_pu2 (t)
-        - real_pub (t)
-        - real_pub::Pub (t)
-        lib:
-        - Pub (t)
-        - Pub2 (t)
-        - Pub2 (v)
-        "###);
-    }
-
-    #[test]
-    fn prefers_shortest_path() {
-        let map = import_map(
-            r"
-            //- /main.rs crate:main
-
-            pub mod sub {
-                pub mod subsub {
-                    pub struct Def {}
-                }
-
-                pub use super::sub::subsub::Def;
-            }
-        ",
-        );
-
-        assert_snapshot!(map, @r###"
-        main:
-        - sub (t)
-        - sub::Def (t)
-        - sub::subsub (t)
-        "###);
-    }
-
-    #[test]
-    fn type_reexport_cross_crate() {
-        // Reexports need to be visible from a crate, even if the original crate exports the item
-        // at a shorter path.
-        let map = import_map(
-            r"
-            //- /main.rs crate:main deps:lib
-            pub mod m {
-                pub use lib::S;
-            }
-            //- /lib.rs crate:lib
-            pub struct S;
-        ",
-        );
-
-        assert_snapshot!(map, @r###"
-        main:
-        - m (t)
-        - m::S (t)
-        - m::S (v)
-        lib:
-        - S (t)
-        - S (v)
-        "###);
-    }
-
-    #[test]
-    fn macro_reexport() {
-        let map = import_map(
-            r"
-            //- /main.rs crate:main deps:lib
-            pub mod m {
-                pub use lib::pub_macro;
-            }
-            //- /lib.rs crate:lib
-            #[macro_export]
-            macro_rules! pub_macro {
-                () => {};
-            }
-        ",
-        );
-
-        assert_snapshot!(map, @r###"
-        main:
-        - m (t)
-        - m::pub_macro (m)
-        lib:
-        - pub_macro (m)
-        "###);
-    }
-
-    #[test]
-    fn module_reexport() {
-        // Reexporting modules from a dependency adds all contents to the import map.
-        let map = import_map(
-            r"
-            //- /main.rs crate:main deps:lib
-            pub use lib::module as reexported_module;
-            //- /lib.rs crate:lib
-            pub mod module {
-                pub struct S;
-            }
-        ",
-        );
-
-        assert_snapshot!(map, @r###"
-        main:
-        - reexported_module (t)
-        - reexported_module::S (t)
-        - reexported_module::S (v)
-        lib:
-        - module (t)
-        - module::S (t)
-        - module::S (v)
-        "###);
-    }
-
-    #[test]
-    fn cyclic_module_reexport() {
-        // A cyclic reexport does not hang.
-        let map = import_map(
-            r"
-            //- /lib.rs crate:lib
-            pub mod module {
-                pub struct S;
-                pub use super::sub::*;
-            }
-
-            pub mod sub {
-                pub use super::module;
-            }
-        ",
-        );
-
-        assert_snapshot!(map, @r###"
-        lib:
-        - module (t)
-        - module::S (t)
-        - module::S (v)
-        - sub (t)
-        "###);
-    }
-
-    #[test]
-    fn private_macro() {
-        let map = import_map(
-            r"
-            //- /lib.rs crate:lib
-            macro_rules! private_macro {
-                () => {};
-            }
-        ",
-        );
-
-        assert_snapshot!(map, @r###"
-        lib:
-        "###);
-    }
-}
diff --git a/crates/ra_hir_def/src/item_scope.rs b/crates/ra_hir_def/src/item_scope.rs
index b03ba939a5..fc15948adf 100644
--- a/crates/ra_hir_def/src/item_scope.rs
+++ b/crates/ra_hir_def/src/item_scope.rs
@@ -3,12 +3,11 @@
 
 use hir_expand::name::Name;
 use once_cell::sync::Lazy;
-use ra_db::CrateId;
 use rustc_hash::FxHashMap;
 
 use crate::{
-    db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, HasModule, ImplId,
-    Lookup, MacroDefId, ModuleDefId, TraitId,
+    per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ImplId, MacroDefId, ModuleDefId,
+    TraitId,
 };
 
 #[derive(Debug, Default, PartialEq, Eq)]
@@ -204,22 +203,4 @@ impl ItemInNs {
             ItemInNs::Macros(_) => None,
         }
     }
-
-    /// Returns the crate defining this item (or `None` if `self` is built-in).
-    pub fn krate(&self, db: &dyn DefDatabase) -> Option<CrateId> {
-        Some(match self {
-            ItemInNs::Types(did) | ItemInNs::Values(did) => match did {
-                ModuleDefId::ModuleId(id) => id.krate,
-                ModuleDefId::FunctionId(id) => id.lookup(db).module(db).krate,
-                ModuleDefId::AdtId(id) => id.module(db).krate,
-                ModuleDefId::EnumVariantId(id) => id.parent.lookup(db).container.module(db).krate,
-                ModuleDefId::ConstId(id) => id.lookup(db).container.module(db).krate,
-                ModuleDefId::StaticId(id) => id.lookup(db).container.module(db).krate,
-                ModuleDefId::TraitId(id) => id.lookup(db).container.module(db).krate,
-                ModuleDefId::TypeAliasId(id) => id.lookup(db).module(db).krate,
-                ModuleDefId::BuiltinType(_) => return None,
-            },
-            ItemInNs::Macros(id) => return id.krate,
-        })
-    }
 }
diff --git a/crates/ra_hir_def/src/lib.rs b/crates/ra_hir_def/src/lib.rs
index de490fcc58..5325a27608 100644
--- a/crates/ra_hir_def/src/lib.rs
+++ b/crates/ra_hir_def/src/lib.rs
@@ -43,7 +43,6 @@ pub mod child_by_source;
 
 pub mod visibility;
 pub mod find_path;
-pub mod import_map;
 
 #[cfg(test)]
 mod test_db;
diff --git a/crates/ra_hir_def/src/path.rs b/crates/ra_hir_def/src/path.rs
index bfa921de22..4512448e02 100644
--- a/crates/ra_hir_def/src/path.rs
+++ b/crates/ra_hir_def/src/path.rs
@@ -76,19 +76,6 @@ impl ModPath {
         }
     }
 
-    /// Returns the number of segments in the path (counting special segments like `$crate` and
-    /// `super`).
-    pub fn len(&self) -> usize {
-        self.segments.len()
-            + match self.kind {
-                PathKind::Plain => 0,
-                PathKind::Super(i) => i as usize,
-                PathKind::Crate => 1,
-                PathKind::Abs => 0,
-                PathKind::DollarCrate(_) => 1,
-            }
-    }
-
     pub fn is_ident(&self) -> bool {
         self.kind == PathKind::Plain && self.segments.len() == 1
     }
diff --git a/crates/ra_hir_def/src/per_ns.rs b/crates/ra_hir_def/src/per_ns.rs
index 74665c5885..6e435c8c12 100644
--- a/crates/ra_hir_def/src/per_ns.rs
+++ b/crates/ra_hir_def/src/per_ns.rs
@@ -5,7 +5,7 @@
 
 use hir_expand::MacroDefId;
 
-use crate::{item_scope::ItemInNs, visibility::Visibility, ModuleDefId};
+use crate::{visibility::Visibility, ModuleDefId};
 
 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
 pub struct PerNs {
@@ -84,12 +84,4 @@ impl PerNs {
             macros: self.macros.or(other.macros),
         }
     }
-
-    pub fn iter_items(self) -> impl Iterator<Item = ItemInNs> {
-        self.types
-            .map(|it| ItemInNs::Types(it.0))
-            .into_iter()
-            .chain(self.values.map(|it| ItemInNs::Values(it.0)).into_iter())
-            .chain(self.macros.map(|it| ItemInNs::Macros(it.0)).into_iter())
-    }
 }
diff --git a/crates/ra_hir_expand/src/builtin_macro.rs b/crates/ra_hir_expand/src/builtin_macro.rs
index eec5fb8ebf..7579546d2f 100644
--- a/crates/ra_hir_expand/src/builtin_macro.rs
+++ b/crates/ra_hir_expand/src/builtin_macro.rs
@@ -295,19 +295,13 @@ fn concat_expand(
 
 fn relative_file(db: &dyn AstDatabase, call_id: MacroCallId, path: &str) -> Option<FileId> {
     let call_site = call_id.as_file().original_file(db);
-
-    // Handle trivial case
-    if let Some(res) = db.resolve_path(call_site, path) {
-        // Prevent include itself
-        return if res == call_site { None } else { Some(res) };
+    let res = db.resolve_path(call_site, path)?;
+    // Prevent include itself
+    if res == call_site {
+        None
+    } else {
+        Some(res)
     }
-
-    // Extern paths ?
-    let krate = *db.relevant_crates(call_site).get(0)?;
-    let (extern_source_id, relative_file) =
-        db.crate_graph()[krate].extern_source.extern_path(path)?;
-
-    db.resolve_extern_path(extern_source_id, &relative_file)
 }
 
 fn parse_string(tt: &tt::Subtree) -> Result<String, mbe::ExpandError> {
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs
index 62df074594..9636cd0d6a 100644
--- a/crates/ra_ide/src/hover.rs
+++ b/crates/ra_ide/src/hover.rs
@@ -13,43 +13,14 @@ use ra_ide_db::{
 use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset};
 
 use crate::{
-    display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel, ToNav},
-    FilePosition, NavigationTarget, RangeInfo,
+    display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel},
+    FilePosition, RangeInfo,
 };
 
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub struct HoverConfig {
-    pub implementations: bool,
-}
-
-impl Default for HoverConfig {
-    fn default() -> Self {
-        Self { implementations: true }
-    }
-}
-
-impl HoverConfig {
-    pub const NO_ACTIONS: Self = Self { implementations: false };
-
-    pub fn any(&self) -> bool {
-        self.implementations
-    }
-
-    pub fn none(&self) -> bool {
-        !self.any()
-    }
-}
-
-#[derive(Debug, Clone)]
-pub enum HoverAction {
-    Implementaion(FilePosition),
-}
-
 /// Contains the results when hovering over an item
 #[derive(Debug, Default)]
 pub struct HoverResult {
     results: Vec<String>,
-    actions: Vec<HoverAction>,
 }
 
 impl HoverResult {
@@ -77,20 +48,10 @@ impl HoverResult {
         &self.results
     }
 
-    pub fn actions(&self) -> &[HoverAction] {
-        &self.actions
-    }
-
-    pub fn push_action(&mut self, action: HoverAction) {
-        self.actions.push(action);
-    }
-
     /// Returns the results converted into markup
     /// for displaying in a UI
-    ///
-    /// Does not process actions!
     pub fn to_markup(&self) -> String {
-        self.results.join("\n\n___\n")
+        self.results.join("\n\n---\n")
     }
 }
 
@@ -121,10 +82,6 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
         res.extend(hover_text_from_name_kind(db, name_kind));
 
         if !res.is_empty() {
-            if let Some(action) = show_implementations_action(db, name_kind) {
-                res.push_action(action);
-            }
-
             return Some(RangeInfo::new(range, res));
         }
     }
@@ -155,26 +112,6 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
     Some(RangeInfo::new(range, res))
 }
 
-fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
-    fn to_action(nav_target: NavigationTarget) -> HoverAction {
-        HoverAction::Implementaion(FilePosition {
-            file_id: nav_target.file_id(),
-            offset: nav_target.range().start(),
-        })
-    }
-
-    match def {
-        Definition::ModuleDef(it) => match it {
-            ModuleDef::Adt(Adt::Struct(it)) => Some(to_action(it.to_nav(db))),
-            ModuleDef::Adt(Adt::Union(it)) => Some(to_action(it.to_nav(db))),
-            ModuleDef::Adt(Adt::Enum(it)) => Some(to_action(it.to_nav(db))),
-            ModuleDef::Trait(it) => Some(to_action(it.to_nav(db))),
-            _ => None,
-        },
-        _ => None,
-    }
-}
-
 fn hover_text(
     docs: Option<String>,
     desc: Option<String>,
@@ -291,8 +228,6 @@ fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
 
 #[cfg(test)]
 mod tests {
-    use super::*;
-
     use ra_db::FileLoader;
     use ra_syntax::TextRange;
 
@@ -306,14 +241,7 @@ mod tests {
         s.map(trim_markup)
     }
 
-    fn assert_impl_action(action: &HoverAction, position: u32) {
-        let offset = match action {
-            HoverAction::Implementaion(pos) => pos.offset,
-        };
-        assert_eq!(offset, position.into());
-    }
-
-    fn check_hover_result(fixture: &str, expected: &[&str]) -> (String, Vec<HoverAction>) {
+    fn check_hover_result(fixture: &str, expected: &[&str]) -> String {
         let (analysis, position) = analysis_and_position(fixture);
         let hover = analysis.hover(position).unwrap().unwrap();
         let mut results = Vec::from(hover.info.results());
@@ -328,7 +256,7 @@ mod tests {
         assert_eq!(hover.info.len(), expected.len());
 
         let content = analysis.db.file_text(position.file_id);
-        (content[hover.range].to_string(), hover.info.actions().to_vec())
+        content[hover.range].to_string()
     }
 
     fn check_hover_no_result(fixture: &str) {
@@ -818,7 +746,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
 
     #[test]
     fn test_hover_through_macro() {
-        let (hover_on, _) = check_hover_result(
+        let hover_on = check_hover_result(
             "
             //- /lib.rs
             macro_rules! id {
@@ -839,7 +767,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
 
     #[test]
     fn test_hover_through_expr_in_macro() {
-        let (hover_on, _) = check_hover_result(
+        let hover_on = check_hover_result(
             "
             //- /lib.rs
             macro_rules! id {
@@ -857,7 +785,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
 
     #[test]
     fn test_hover_through_expr_in_macro_recursive() {
-        let (hover_on, _) = check_hover_result(
+        let hover_on = check_hover_result(
             "
             //- /lib.rs
             macro_rules! id_deep {
@@ -878,7 +806,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
 
     #[test]
     fn test_hover_through_func_in_macro_recursive() {
-        let (hover_on, _) = check_hover_result(
+        let hover_on = check_hover_result(
             "
             //- /lib.rs
             macro_rules! id_deep {
@@ -902,7 +830,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
 
     #[test]
     fn test_hover_through_literal_string_in_macro() {
-        let (hover_on, _) = check_hover_result(
+        let hover_on = check_hover_result(
             r#"
             //- /lib.rs
             macro_rules! arr {
@@ -921,7 +849,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
 
     #[test]
     fn test_hover_through_assert_macro() {
-        let (hover_on, _) = check_hover_result(
+        let hover_on = check_hover_result(
             r#"
             //- /lib.rs
             #[rustc_builtin_macro]
@@ -997,14 +925,13 @@ fn func(foo: i32) { if true { <|>foo; }; }
 
     #[test]
     fn test_hover_trait_show_qualifiers() {
-        let (_, actions) = check_hover_result(
+        check_hover_result(
             "
             //- /lib.rs
             unsafe trait foo<|>() {}
             ",
             &["unsafe trait foo"],
         );
-        assert_impl_action(&actions[0], 13);
     }
 
     #[test]
@@ -1125,55 +1052,4 @@ fn func(foo: i32) { if true { <|>foo; }; }
             &["Bar\n```\n\n```rust\nfn foo(&self)\n```\n___\n\nDo the foo"],
         );
     }
-
-    #[test]
-    fn test_hover_trait_has_impl_action() {
-        let (_, actions) = check_hover_result(
-            "
-            //- /lib.rs
-            trait foo<|>() {}
-            ",
-            &["trait foo"],
-        );
-        assert_impl_action(&actions[0], 6);
-    }
-
-    #[test]
-    fn test_hover_struct_has_impl_action() {
-        let (_, actions) = check_hover_result(
-            "
-            //- /lib.rs
-            struct foo<|>() {}
-            ",
-            &["struct foo"],
-        );
-        assert_impl_action(&actions[0], 7);
-    }
-
-    #[test]
-    fn test_hover_union_has_impl_action() {
-        let (_, actions) = check_hover_result(
-            "
-            //- /lib.rs
-            union foo<|>() {}
-            ",
-            &["union foo"],
-        );
-        assert_impl_action(&actions[0], 6);
-    }
-
-    #[test]
-    fn test_hover_enum_has_impl_action() {
-        let (_, actions) = check_hover_result(
-            "
-            //- /lib.rs
-            enum foo<|>() {
-                A,
-                B
-            }
-            ",
-            &["enum foo"],
-        );
-        assert_impl_action(&actions[0], 5);
-    }
 }
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs
index a56718d3f5..34c2d75fed 100644
--- a/crates/ra_ide/src/lib.rs
+++ b/crates/ra_ide/src/lib.rs
@@ -66,7 +66,7 @@ pub use crate::{
     display::{file_structure, FunctionSignature, NavigationTarget, StructureNode},
     expand_macro::ExpandedMacro,
     folding_ranges::{Fold, FoldKind},
-    hover::{HoverAction, HoverConfig, HoverResult},
+    hover::HoverResult,
     inlay_hints::{InlayHint, InlayHintsConfig, InlayKind},
     references::{Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult},
     runnables::{Runnable, RunnableKind, TestId},
diff --git a/crates/ra_ide_db/src/change.rs b/crates/ra_ide_db/src/change.rs
index ea78e49e05..8446ef88e8 100644
--- a/crates/ra_ide_db/src/change.rs
+++ b/crates/ra_ide_db/src/change.rs
@@ -334,7 +334,6 @@ impl RootDatabase {
             hir::db::CrateLangItemsQuery
             hir::db::LangItemQuery
             hir::db::DocumentationQuery
-            hir::db::ImportMapQuery
 
             // InternDatabase
             hir::db::InternFunctionQuery
diff --git a/crates/ra_ide_db/src/defs.rs b/crates/ra_ide_db/src/defs.rs
index 1db60b87fb..8b06cbfc54 100644
--- a/crates/ra_ide_db/src/defs.rs
+++ b/crates/ra_ide_db/src/defs.rs
@@ -18,7 +18,7 @@ use ra_syntax::{
 use crate::RootDatabase;
 
 // FIXME: a more precise name would probably be `Symbol`?
-#[derive(Debug, PartialEq, Eq, Copy, Clone)]
+#[derive(Debug, PartialEq, Eq)]
 pub enum Definition {
     Macro(MacroDef),
     Field(Field),
diff --git a/crates/ra_ide_db/src/lib.rs b/crates/ra_ide_db/src/lib.rs
index 93d5891a04..727d743b5f 100644
--- a/crates/ra_ide_db/src/lib.rs
+++ b/crates/ra_ide_db/src/lib.rs
@@ -16,8 +16,8 @@ use std::sync::Arc;
 use hir::db::{AstDatabase, DefDatabase};
 use ra_db::{
     salsa::{self, Database, Durability},
-    Canceled, CheckCanceled, CrateId, FileId, FileLoader, FileLoaderDelegate, RelativePath,
-    SourceDatabase, SourceRootId, Upcast,
+    Canceled, CheckCanceled, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase,
+    SourceRootId, Upcast,
 };
 use rustc_hash::FxHashMap;
 
@@ -63,13 +63,6 @@ impl FileLoader for RootDatabase {
     fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> {
         FileLoaderDelegate(self).relevant_crates(file_id)
     }
-    fn resolve_extern_path(
-        &self,
-        extern_id: ra_db::ExternSourceId,
-        relative_path: &RelativePath,
-    ) -> Option<FileId> {
-        FileLoaderDelegate(self).resolve_extern_path(extern_id, relative_path)
-    }
 }
 
 impl salsa::Database for RootDatabase {
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 8d6efdbe8c..23168c3ae9 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -11,7 +11,7 @@ use std::{ffi::OsString, path::PathBuf};
 
 use lsp_types::ClientCapabilities;
 use ra_flycheck::FlycheckConfig;
-use ra_ide::{AssistConfig, CompletionConfig, HoverConfig, InlayHintsConfig};
+use ra_ide::{AssistConfig, CompletionConfig, InlayHintsConfig};
 use ra_project_model::{CargoConfig, JsonProject, ProjectManifest};
 use serde::Deserialize;
 
@@ -34,7 +34,6 @@ pub struct Config {
     pub assist: AssistConfig,
     pub call_info_full: bool,
     pub lens: LensConfig,
-    pub hover: HoverConfig,
 
     pub with_sysroot: bool,
     pub linked_projects: Vec<LinkedProject>,
@@ -125,7 +124,6 @@ pub struct ClientCapsConfig {
     pub work_done_progress: bool,
     pub code_action_group: bool,
     pub resolve_code_action: bool,
-    pub hover_actions: bool,
 }
 
 impl Default for Config {
@@ -164,7 +162,6 @@ impl Default for Config {
             assist: AssistConfig::default(),
             call_info_full: true,
             lens: LensConfig::default(),
-            hover: HoverConfig::default(),
             linked_projects: Vec::new(),
         }
     }
@@ -281,14 +278,6 @@ impl Config {
             }
         }
 
-        let mut use_hover_actions = false;
-        set(value, "/hoverActions/enable", &mut use_hover_actions);
-        if use_hover_actions {
-            set(value, "/hoverActions/implementations", &mut self.hover.implementations);
-        } else {
-            self.hover = HoverConfig::NO_ACTIONS;
-        }
-
         log::info!("Config::update() = {:#?}", self);
 
         fn get<'a, T: Deserialize<'a>>(value: &'a serde_json::Value, pointer: &str) -> Option<T> {
@@ -342,15 +331,17 @@ impl Config {
 
         self.assist.allow_snippets(false);
         if let Some(experimental) = &caps.experimental {
-            let get_bool =
-                |index: &str| experimental.get(index).and_then(|it| it.as_bool()) == Some(true);
-
-            let snippet_text_edit = get_bool("snippetTextEdit");
+            let snippet_text_edit =
+                experimental.get("snippetTextEdit").and_then(|it| it.as_bool()) == Some(true);
             self.assist.allow_snippets(snippet_text_edit);
 
-            self.client_caps.code_action_group = get_bool("codeActionGroup");
-            self.client_caps.resolve_code_action = get_bool("resolveCodeAction");
-            self.client_caps.hover_actions = get_bool("hoverActions");
+            let code_action_group =
+                experimental.get("codeActionGroup").and_then(|it| it.as_bool()) == Some(true);
+            self.client_caps.code_action_group = code_action_group;
+
+            let resolve_code_action =
+                experimental.get("resolveCodeAction").and_then(|it| it.as_bool()) == Some(true);
+            self.client_caps.resolve_code_action = resolve_code_action;
         }
     }
 }
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs
index 1371f6cb4a..3b957534dd 100644
--- a/crates/rust-analyzer/src/lsp_ext.rs
+++ b/crates/rust-analyzer/src/lsp_ext.rs
@@ -260,35 +260,3 @@ pub struct SnippetTextEdit {
     #[serde(skip_serializing_if = "Option::is_none")]
     pub insert_text_format: Option<lsp_types::InsertTextFormat>,
 }
-
-pub enum HoverRequest {}
-
-impl Request for HoverRequest {
-    type Params = lsp_types::HoverParams;
-    type Result = Option<Hover>;
-    const METHOD: &'static str = "textDocument/hover";
-}
-
-#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
-pub struct Hover {
-    #[serde(flatten)]
-    pub hover: lsp_types::Hover,
-    #[serde(skip_serializing_if = "Vec::is_empty")]
-    pub actions: Vec<CommandLinkGroup>,
-}
-
-#[derive(Debug, PartialEq, Clone, Default, Deserialize, Serialize)]
-pub struct CommandLinkGroup {
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub title: Option<String>,
-    pub commands: Vec<CommandLink>,
-}
-
-// LSP v3.15 Command does not have a `tooltip` field, vscode supports one.
-#[derive(Debug, PartialEq, Clone, Default, Deserialize, Serialize)]
-pub struct CommandLink {
-    #[serde(flatten)]
-    pub command: lsp_types::Command,
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub tooltip: Option<String>,
-}
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 752dbf1452..e60337b8e8 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -510,7 +510,6 @@ fn on_request(
         .on::<lsp_ext::InlayHints>(handlers::handle_inlay_hints)?
         .on::<lsp_ext::CodeActionRequest>(handlers::handle_code_action)?
         .on::<lsp_ext::ResolveCodeActionRequest>(handlers::handle_resolve_code_action)?
-        .on::<lsp_ext::HoverRequest>(handlers::handle_hover)?
         .on::<lsp_types::request::OnTypeFormatting>(handlers::handle_on_type_formatting)?
         .on::<lsp_types::request::DocumentSymbolRequest>(handlers::handle_document_symbol)?
         .on::<lsp_types::request::WorkspaceSymbol>(handlers::handle_workspace_symbol)?
@@ -522,6 +521,7 @@ fn on_request(
         .on::<lsp_types::request::CodeLensResolve>(handlers::handle_code_lens_resolve)?
         .on::<lsp_types::request::FoldingRangeRequest>(handlers::handle_folding_range)?
         .on::<lsp_types::request::SignatureHelpRequest>(handlers::handle_signature_help)?
+        .on::<lsp_types::request::HoverRequest>(handlers::handle_hover)?
         .on::<lsp_types::request::PrepareRenameRequest>(handlers::handle_prepare_rename)?
         .on::<lsp_types::request::Rename>(handlers::handle_rename)?
         .on::<lsp_types::request::References>(handlers::handle_references)?
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs
index 3ff7797024..6acf80c582 100644
--- a/crates/rust-analyzer/src/main_loop/handlers.rs
+++ b/crates/rust-analyzer/src/main_loop/handlers.rs
@@ -12,14 +12,13 @@ use lsp_types::{
     CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem,
     CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams,
     CodeLens, Command, CompletionItem, Diagnostic, DocumentFormattingParams, DocumentHighlight,
-    DocumentSymbol, FoldingRange, FoldingRangeParams, HoverContents, Location, MarkupContent,
-    MarkupKind, Position, PrepareRenameResponse, Range, RenameParams, SemanticTokensParams,
-    SemanticTokensRangeParams, SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation,
-    TextDocumentIdentifier, Url, WorkspaceEdit,
+    DocumentSymbol, FoldingRange, FoldingRangeParams, Hover, HoverContents, Location,
+    MarkupContent, MarkupKind, Position, PrepareRenameResponse, Range, RenameParams,
+    SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult,
+    SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, Url, WorkspaceEdit,
 };
 use ra_ide::{
-    FileId, FilePosition, FileRange, HoverAction, Query, RangeInfo, RunnableKind, SearchScope,
-    TextEdit,
+    FileId, FilePosition, FileRange, Query, RangeInfo, RunnableKind, SearchScope, TextEdit,
 };
 use ra_prof::profile;
 use ra_project_model::TargetKind;
@@ -538,7 +537,7 @@ pub fn handle_signature_help(
 pub fn handle_hover(
     snap: GlobalStateSnapshot,
     params: lsp_types::HoverParams,
-) -> Result<Option<lsp_ext::Hover>> {
+) -> Result<Option<Hover>> {
     let _p = profile("handle_hover");
     let position = from_proto::file_position(&snap, params.text_document_position_params)?;
     let info = match snap.analysis().hover(position)? {
@@ -547,18 +546,14 @@ pub fn handle_hover(
     };
     let line_index = snap.analysis.file_line_index(position.file_id)?;
     let range = to_proto::range(&line_index, info.range);
-    let hover = lsp_ext::Hover {
-        hover: lsp_types::Hover {
-            contents: HoverContents::Markup(MarkupContent {
-                kind: MarkupKind::Markdown,
-                value: crate::markdown::format_docs(&info.info.to_markup()),
-            }),
-            range: Some(range),
-        },
-        actions: prepare_hover_actions(&snap, info.info.actions()),
+    let res = Hover {
+        contents: HoverContents::Markup(MarkupContent {
+            kind: MarkupKind::Markdown,
+            value: crate::markdown::format_docs(&info.info.to_markup()),
+        }),
+        range: Some(range),
     };
-
-    Ok(Some(hover))
+    Ok(Some(res))
 }
 
 pub fn handle_prepare_rename(
@@ -929,13 +924,24 @@ pub fn handle_code_lens_resolve(
                     _ => vec![],
                 };
 
-            let title = implementation_title(locations.len());
-            let cmd = show_references_command(
+            let title = if locations.len() == 1 {
+                "1 implementation".into()
+            } else {
+                format!("{} implementations", locations.len())
+            };
+
+            // We cannot use the 'editor.action.showReferences' command directly
+            // because that command requires vscode types which we convert in the handler
+            // on the client side.
+            let cmd = Command {
                 title,
-                &lens_params.text_document_position_params.text_document.uri,
-                code_lens.range.start,
-                locations,
-            );
+                command: "rust-analyzer.showReferences".into(),
+                arguments: Some(vec![
+                    to_value(&lens_params.text_document_position_params.text_document.uri).unwrap(),
+                    to_value(code_lens.range.start).unwrap(),
+                    to_value(locations).unwrap(),
+                ]),
+            };
             Ok(CodeLens { range: code_lens.range, command: Some(cmd), data: None })
         }
         None => Ok(CodeLens {
@@ -1139,78 +1145,3 @@ pub fn handle_semantic_tokens_range(
     let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights);
     Ok(Some(semantic_tokens.into()))
 }
-
-fn implementation_title(count: usize) -> String {
-    if count == 1 {
-        "1 implementation".into()
-    } else {
-        format!("{} implementations", count)
-    }
-}
-
-fn show_references_command(
-    title: String,
-    uri: &lsp_types::Url,
-    position: lsp_types::Position,
-    locations: Vec<lsp_types::Location>,
-) -> Command {
-    // We cannot use the 'editor.action.showReferences' command directly
-    // because that command requires vscode types which we convert in the handler
-    // on the client side.
-
-    Command {
-        title,
-        command: "rust-analyzer.showReferences".into(),
-        arguments: Some(vec![
-            to_value(uri).unwrap(),
-            to_value(position).unwrap(),
-            to_value(locations).unwrap(),
-        ]),
-    }
-}
-
-fn to_command_link(command: Command, tooltip: String) -> lsp_ext::CommandLink {
-    lsp_ext::CommandLink { tooltip: Some(tooltip), command }
-}
-
-fn show_impl_command_link(
-    snap: &GlobalStateSnapshot,
-    position: &FilePosition,
-) -> Option<lsp_ext::CommandLinkGroup> {
-    if snap.config.hover.implementations {
-        if let Some(nav_data) = snap.analysis().goto_implementation(*position).unwrap_or(None) {
-            let uri = to_proto::url(snap, position.file_id).ok()?;
-            let line_index = snap.analysis().file_line_index(position.file_id).ok()?;
-            let position = to_proto::position(&line_index, position.offset);
-            let locations: Vec<_> = nav_data
-                .info
-                .iter()
-                .filter_map(|it| to_proto::location(snap, it.file_range()).ok())
-                .collect();
-            let title = implementation_title(locations.len());
-            let command = show_references_command(title, &uri, position, locations);
-
-            return Some(lsp_ext::CommandLinkGroup {
-                commands: vec![to_command_link(command, "Go to implementations".into())],
-                ..Default::default()
-            });
-        }
-    }
-    None
-}
-
-fn prepare_hover_actions(
-    snap: &GlobalStateSnapshot,
-    actions: &[HoverAction],
-) -> Vec<lsp_ext::CommandLinkGroup> {
-    if snap.config.hover.none() || !snap.config.client_caps.hover_actions {
-        return Vec::new();
-    }
-
-    actions
-        .iter()
-        .filter_map(|it| match it {
-            HoverAction::Implementaion(position) => show_impl_command_link(snap, position),
-        })
-        .collect()
-}
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md
index a0847dad3a..7f7940d0b6 100644
--- a/docs/dev/lsp-extensions.md
+++ b/docs/dev/lsp-extensions.md
@@ -467,41 +467,3 @@ interface InlayHint {
     label: string,
 }
 ```
-
-## Hover Actions
-
-**Client Capability:** `{ "hoverActions": boolean }`
-
-If this capability is set, `Hover` request returned from the server might contain an additional field, `actions`:
-
-```typescript
-interface Hover {
-    ...
-    actions?: CommandLinkGroup[];
-}
-
-interface CommandLink extends Command {
-    /**
-     * A tooltip for the command, when represented in the UI.
-     */
-    tooltip?: string;
-}
-
-interface CommandLinkGroup {
-    title?: string;
-    commands: CommandLink[];
-}
-```
-
-Such actions on the client side are appended to a hover bottom as command links:
-```
-  +-----------------------------+
-  | Hover content               |
-  |                             |
-  +-----------------------------+
-  | _Action1_ | _Action2_       |  <- first group, no TITLE
-  +-----------------------------+
-  | TITLE _Action1_ | _Action2_ |  <- second group
-  +-----------------------------+
-  ...
-```
\ No newline at end of file
diff --git a/editors/code/package.json b/editors/code/package.json
index b9c57db3bc..30ab7ba4a9 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -462,27 +462,17 @@
                     "default": true
                 },
                 "rust-analyzer.lens.run": {
-                    "markdownDescription": "Whether to show `Run` lens. Only applies when `#rust-analyzer.lens.enable#` is set.",
+                    "markdownDescription": "Whether to show Run lens. Only applies when `#rust-analyzer.lens.enable#` is set.",
                     "type": "boolean",
                     "default": true
                 },
                 "rust-analyzer.lens.debug": {
-                    "markdownDescription": "Whether to show `Debug` lens. Only applies when `#rust-analyzer.lens.enable#` is set.",
+                    "markdownDescription": "Whether to show Debug lens. Only applies when `#rust-analyzer.lens.enable#` is set.",
                     "type": "boolean",
                     "default": true
                 },
                 "rust-analyzer.lens.implementations": {
-                    "markdownDescription": "Whether to show `Implementations` lens. Only applies when `#rust-analyzer.lens.enable#` is set.",
-                    "type": "boolean",
-                    "default": true
-                },
-                "rust-analyzer.hoverActions.enable": {
-                    "description": "Whether to show HoverActions in Rust files.",
-                    "type": "boolean",
-                    "default": true
-                },
-                "rust-analyzer.hoverActions.implementations": {
-                    "markdownDescription": "Whether to show `Implementations` action. Only applies when `#rust-analyzer.hoverActions.enable#` is set.",
+                    "markdownDescription": "Whether to show Implementations lens. Only applies when `#rust-analyzer.lens.enable#` is set.",
                     "type": "boolean",
                     "default": true
                 },
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts
index 65ad573d8c..40ad1e3cd8 100644
--- a/editors/code/src/client.ts
+++ b/editors/code/src/client.ts
@@ -7,20 +7,6 @@ import { CallHierarchyFeature } from 'vscode-languageclient/lib/callHierarchy.pr
 import { SemanticTokensFeature, DocumentSemanticsTokensSignature } from 'vscode-languageclient/lib/semanticTokens.proposed';
 import { assert } from './util';
 
-function renderCommand(cmd: ra.CommandLink) {
-    return `[${cmd.title}](command:${cmd.command}?${encodeURIComponent(JSON.stringify(cmd.arguments))} '${cmd.tooltip!}')`;
-}
-
-function renderHoverActions(actions: ra.CommandLinkGroup[]): vscode.MarkdownString {
-    const text = actions.map(group =>
-        (group.title ? (group.title + " ") : "") + group.commands.map(renderCommand).join(' | ')
-    ).join('___');
-
-    const result = new vscode.MarkdownString(text);
-    result.isTrusted = true;
-    return result;
-}
-
 export function createClient(serverPath: string, cwd: string): lc.LanguageClient {
     // '.' Is the fallback if no folder is open
     // TODO?: Workspace folders support Uri's (eg: file://test.txt).
@@ -49,23 +35,6 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient
                 if (res === undefined) throw new Error('busy');
                 return res;
             },
-            async provideHover(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, _next: lc.ProvideHoverSignature) {
-                return client.sendRequest(lc.HoverRequest.type, client.code2ProtocolConverter.asTextDocumentPositionParams(document, position), token).then(
-                    (result) => {
-                        const hover = client.protocol2CodeConverter.asHover(result);
-                        if (hover) {
-                            const actions = (<any>result).actions;
-                            if (actions) {
-                                hover.contents.push(renderHoverActions(actions));
-                            }
-                        }
-                        return hover;
-                    },
-                    (error) => {
-                        client.logFailedRequest(lc.HoverRequest.type, error);
-                        return Promise.resolve(null);
-                    });
-            },
             // Using custom handling of CodeActions where each code action is resloved lazily
             // That's why we are not waiting for any command or edits
             async provideCodeActions(document: vscode.TextDocument, range: vscode.Range, context: vscode.CodeActionContext, token: vscode.CancellationToken, _next: lc.ProvideCodeActionsSignature) {
@@ -160,7 +129,6 @@ class ExperimentalFeatures implements lc.StaticFeature {
         caps.snippetTextEdit = true;
         caps.codeActionGroup = true;
         caps.resolveCodeAction = true;
-        caps.hoverActions = true;
         capabilities.experimental = caps;
     }
     initialize(_capabilities: lc.ServerCapabilities<any>, _documentSelector: lc.DocumentSelector | undefined): void {
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index d8f0037d4c..e8abf8284e 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -16,8 +16,10 @@ export class Config {
         "files",
         "highlighting",
         "updates.channel",
-        "lens", // works as lens.*
-        "hoverActions", // works as hoverActions.*
+        "lens.enable",
+        "lens.run",
+        "lens.debug",
+        "lens.implementations",
     ]
         .map(opt => `${this.rootSection}.${opt}`);
 
@@ -130,11 +132,4 @@ export class Config {
             implementations: this.get<boolean>("lens.implementations"),
         };
     }
-
-    get hoverActions() {
-        return {
-            enable: this.get<boolean>("hoverActions.enable"),
-            implementations: this.get<boolean>("hoverActions.implementations"),
-        };
-    }
 }
diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts
index e16ea799ce..9793b926c2 100644
--- a/editors/code/src/lsp_ext.ts
+++ b/editors/code/src/lsp_ext.ts
@@ -90,15 +90,3 @@ export interface SsrParams {
     parseOnly: boolean;
 }
 export const ssr = new lc.RequestType<SsrParams, lc.WorkspaceEdit, void>('experimental/ssr');
-
-export interface CommandLink extends lc.Command {
-    /**
-     * A tooltip for the command, when represented in the UI.
-     */
-    tooltip?: string;
-}
-
-export interface CommandLinkGroup {
-    title?: string;
-    commands: CommandLink[];
-}


Test patch:
diff --git a/crates/ra_hir_def/src/test_db.rs b/crates/ra_hir_def/src/test_db.rs
index e7a5182f03..bcfa66ac95 100644
--- a/crates/ra_hir_def/src/test_db.rs
+++ b/crates/ra_hir_def/src/test_db.rs
@@ -6,9 +6,7 @@ use std::{
 };
 
 use hir_expand::db::AstDatabase;
-use ra_db::{
-    salsa, CrateId, ExternSourceId, FileId, FileLoader, FileLoaderDelegate, RelativePath, Upcast,
-};
+use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, Upcast};
 
 use crate::db::DefDatabase;
 
@@ -64,14 +62,6 @@ impl FileLoader for TestDB {
     fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> {
         FileLoaderDelegate(self).relevant_crates(file_id)
     }
-
-    fn resolve_extern_path(
-        &self,
-        extern_id: ExternSourceId,
-        relative_path: &RelativePath,
-    ) -> Option<FileId> {
-        FileLoaderDelegate(self).resolve_extern_path(extern_id, relative_path)
-    }
 }
 
 impl TestDB {
diff --git a/crates/ra_hir_expand/src/test_db.rs b/crates/ra_hir_expand/src/test_db.rs
index 765a2f6d18..fdf225f55b 100644
--- a/crates/ra_hir_expand/src/test_db.rs
+++ b/crates/ra_hir_expand/src/test_db.rs
@@ -5,7 +5,7 @@ use std::{
     sync::{Arc, Mutex},
 };
 
-use ra_db::{salsa, CrateId, ExternSourceId, FileId, FileLoader, FileLoaderDelegate, RelativePath};
+use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate};
 
 #[salsa::database(
     ra_db::SourceDatabaseExtStorage,
@@ -47,11 +47,4 @@ impl FileLoader for TestDB {
     fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> {
         FileLoaderDelegate(self).relevant_crates(file_id)
     }
-    fn resolve_extern_path(
-        &self,
-        anchor: ExternSourceId,
-        relative_path: &RelativePath,
-    ) -> Option<FileId> {
-        FileLoaderDelegate(self).resolve_extern_path(anchor, relative_path)
-    }
 }
diff --git a/crates/ra_hir_ty/src/test_db.rs b/crates/ra_hir_ty/src/test_db.rs
index 21a3bdfd15..e484968a09 100644
--- a/crates/ra_hir_ty/src/test_db.rs
+++ b/crates/ra_hir_ty/src/test_db.rs
@@ -7,9 +7,7 @@ use std::{
 
 use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId, ModuleId};
 use hir_expand::{db::AstDatabase, diagnostics::DiagnosticSink};
-use ra_db::{
-    salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, RelativePath, SourceDatabase, Upcast,
-};
+use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast};
 use stdx::format_to;
 
 use crate::{db::HirDatabase, diagnostics::Diagnostic, expr::ExprValidator};
@@ -78,13 +76,6 @@ impl FileLoader for TestDB {
     fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> {
         FileLoaderDelegate(self).relevant_crates(file_id)
     }
-    fn resolve_extern_path(
-        &self,
-        extern_id: ra_db::ExternSourceId,
-        relative_path: &RelativePath,
-    ) -> Option<FileId> {
-        FileLoaderDelegate(self).resolve_extern_path(extern_id, relative_path)
-    }
 }
 
 impl TestDB {