about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--crates/hir-def/src/child_by_source.rs16
-rw-r--r--crates/hir-def/src/data.rs13
-rw-r--r--crates/hir-def/src/item_scope.rs6
-rw-r--r--crates/hir-def/src/lib.rs6
-rw-r--r--crates/hir-def/src/nameres/tests/incremental.rs18
-rw-r--r--crates/hir/src/attrs.rs39
-rw-r--r--crates/hir/src/db.rs5
-rw-r--r--crates/hir/src/display.rs18
-rw-r--r--crates/hir/src/from_id.rs5
-rw-r--r--crates/hir/src/has_source.rs14
-rw-r--r--crates/hir/src/lib.rs65
-rw-r--r--crates/hir/src/semantics.rs23
-rw-r--r--crates/hir/src/semantics/source_to_def.rs12
-rw-r--r--crates/ide-assists/src/handlers/add_turbo_fish.rs4
-rw-r--r--crates/ide-db/src/defs.rs53
-rw-r--r--crates/ide-db/src/famous_defs.rs2
-rw-r--r--crates/ide-db/src/rename.rs18
-rw-r--r--crates/ide-diagnostics/src/handlers/missing_fields.rs2
-rw-r--r--crates/ide/src/doc_links.rs7
-rw-r--r--crates/ide/src/goto_declaration.rs35
-rw-r--r--crates/ide/src/goto_definition.rs12
-rw-r--r--crates/ide/src/goto_implementation.rs88
-rw-r--r--crates/ide/src/hover.rs17
-rw-r--r--crates/ide/src/hover/render.rs15
-rw-r--r--crates/ide/src/hover/tests.rs54
-rw-r--r--crates/ide/src/moniker.rs4
-rw-r--r--crates/ide/src/navigation_target.rs27
-rw-r--r--crates/ide/src/references.rs3
-rw-r--r--crates/ide/src/rename.rs119
-rw-r--r--crates/ide/src/runnables.rs2
-rw-r--r--crates/ide/src/static_index.rs2
-rw-r--r--crates/ide/src/syntax_highlighting/highlight.rs29
-rw-r--r--crates/ide/src/syntax_highlighting/inject.rs2
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html3
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html2
-rw-r--r--crates/ide/src/syntax_highlighting/tests.rs1
-rw-r--r--crates/parser/src/shortcuts.rs3
-rw-r--r--crates/rust-analyzer/src/cli/analysis_stats.rs2
-rw-r--r--crates/rust-analyzer/src/cli/diagnostics.rs2
-rw-r--r--crates/rust-analyzer/src/cli/run_tests.rs2
40 files changed, 584 insertions, 166 deletions
diff --git a/crates/hir-def/src/child_by_source.rs b/crates/hir-def/src/child_by_source.rs
index bb79e28f267..814257745d0 100644
--- a/crates/hir-def/src/child_by_source.rs
+++ b/crates/hir-def/src/child_by_source.rs
@@ -14,8 +14,8 @@ use crate::{
     item_scope::ItemScope,
     nameres::DefMap,
     src::{HasChildSource, HasSource},
-    AdtId, AssocItemId, DefWithBodyId, EnumId, EnumVariantId, FieldId, ImplId, Lookup, MacroId,
-    ModuleDefId, ModuleId, TraitId, VariantId,
+    AdtId, AssocItemId, DefWithBodyId, EnumId, EnumVariantId, ExternCrateId, FieldId, ImplId,
+    Lookup, MacroId, ModuleDefId, ModuleId, TraitId, VariantId,
 };
 
 pub trait ChildBySource {
@@ -91,6 +91,7 @@ impl ChildBySource for ItemScope {
     fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
         self.declarations().for_each(|item| add_module_def(db, res, file_id, item));
         self.impls().for_each(|imp| add_impl(db, res, file_id, imp));
+        self.extern_crate_decls().for_each(|ext| add_extern_crate(db, res, file_id, ext));
         self.unnamed_consts().for_each(|konst| {
             let loc = konst.lookup(db);
             if loc.id.file_id() == file_id {
@@ -167,6 +168,17 @@ impl ChildBySource for ItemScope {
                 map[keys::IMPL].insert(loc.source(db).value, imp)
             }
         }
+        fn add_extern_crate(
+            db: &dyn DefDatabase,
+            map: &mut DynMap,
+            file_id: HirFileId,
+            ext: ExternCrateId,
+        ) {
+            let loc = ext.lookup(db);
+            if loc.id.file_id() == file_id {
+                map[keys::EXTERN_CRATE].insert(loc.source(db).value, ext)
+            }
+        }
     }
 }
 
diff --git a/crates/hir-def/src/data.rs b/crates/hir-def/src/data.rs
index 54fe9a2e844..91db68058b0 100644
--- a/crates/hir-def/src/data.rs
+++ b/crates/hir-def/src/data.rs
@@ -2,6 +2,7 @@
 
 pub mod adt;
 
+use base_db::CrateId;
 use hir_expand::{
     name::Name, AstId, ExpandResult, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefKind,
 };
@@ -467,6 +468,7 @@ pub struct ExternCrateDeclData {
     pub name: Name,
     pub alias: Option<ImportAlias>,
     pub visibility: RawVisibility,
+    pub crate_id: Option<CrateId>,
 }
 
 impl ExternCrateDeclData {
@@ -478,10 +480,21 @@ impl ExternCrateDeclData {
         let item_tree = loc.id.item_tree(db);
         let extern_crate = &item_tree[loc.id.value];
 
+        let name = extern_crate.name.clone();
+        let crate_id = if name == hir_expand::name![self] {
+            Some(loc.container.krate())
+        } else {
+            db.crate_def_map(loc.container.krate())
+                .extern_prelude()
+                .find(|&(prelude_name, ..)| *prelude_name == name)
+                .map(|(_, root)| root.krate())
+        };
+
         Arc::new(Self {
             name: extern_crate.name.clone(),
             visibility: item_tree[extern_crate.visibility].clone(),
             alias: extern_crate.alias.clone(),
+            crate_id,
         })
     }
 }
diff --git a/crates/hir-def/src/item_scope.rs b/crates/hir-def/src/item_scope.rs
index 2ac1bcdc079..639b8215c84 100644
--- a/crates/hir-def/src/item_scope.rs
+++ b/crates/hir-def/src/item_scope.rs
@@ -113,6 +113,12 @@ impl ItemScope {
         self.declarations.iter().copied()
     }
 
+    pub fn extern_crate_decls(
+        &self,
+    ) -> impl Iterator<Item = ExternCrateId> + ExactSizeIterator + '_ {
+        self.extern_crate_decls.iter().copied()
+    }
+
     pub fn impls(&self) -> impl Iterator<Item = ImplId> + ExactSizeIterator + '_ {
         self.impls.iter().copied()
     }
diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs
index e8187e05ada..67fd5a3635d 100644
--- a/crates/hir-def/src/lib.rs
+++ b/crates/hir-def/src/lib.rs
@@ -121,6 +121,12 @@ impl From<CrateRootModuleId> for ModuleDefId {
     }
 }
 
+impl From<CrateId> for CrateRootModuleId {
+    fn from(krate: CrateId) -> Self {
+        CrateRootModuleId { krate }
+    }
+}
+
 impl TryFrom<ModuleId> for CrateRootModuleId {
     type Error = ();
 
diff --git a/crates/hir-def/src/nameres/tests/incremental.rs b/crates/hir-def/src/nameres/tests/incremental.rs
index 4931c36bbca..40d3a16540d 100644
--- a/crates/hir-def/src/nameres/tests/incremental.rs
+++ b/crates/hir-def/src/nameres/tests/incremental.rs
@@ -213,17 +213,17 @@ pub type Ty = ();
 
             for (_, res) in module_data.scope.resolutions() {
                 match res.values.or(res.types).unwrap().0 {
-                    ModuleDefId::FunctionId(f) => drop(db.function_data(f)),
+                    ModuleDefId::FunctionId(f) => _ = db.function_data(f),
                     ModuleDefId::AdtId(adt) => match adt {
-                        AdtId::StructId(it) => drop(db.struct_data(it)),
-                        AdtId::UnionId(it) => drop(db.union_data(it)),
-                        AdtId::EnumId(it) => drop(db.enum_data(it)),
+                        AdtId::StructId(it) => _ = db.struct_data(it),
+                        AdtId::UnionId(it) => _ = db.union_data(it),
+                        AdtId::EnumId(it) => _ = db.enum_data(it),
                     },
-                    ModuleDefId::ConstId(it) => drop(db.const_data(it)),
-                    ModuleDefId::StaticId(it) => drop(db.static_data(it)),
-                    ModuleDefId::TraitId(it) => drop(db.trait_data(it)),
-                    ModuleDefId::TraitAliasId(it) => drop(db.trait_alias_data(it)),
-                    ModuleDefId::TypeAliasId(it) => drop(db.type_alias_data(it)),
+                    ModuleDefId::ConstId(it) => _ = db.const_data(it),
+                    ModuleDefId::StaticId(it) => _ = db.static_data(it),
+                    ModuleDefId::TraitId(it) => _ = db.trait_data(it),
+                    ModuleDefId::TraitAliasId(it) => _ = db.trait_alias_data(it),
+                    ModuleDefId::TypeAliasId(it) => _ = db.type_alias_data(it),
                     ModuleDefId::EnumVariantId(_)
                     | ModuleDefId::ModuleId(_)
                     | ModuleDefId::MacroId(_)
diff --git a/crates/hir/src/attrs.rs b/crates/hir/src/attrs.rs
index cf8db2a5a24..3ed03fe7c07 100644
--- a/crates/hir/src/attrs.rs
+++ b/crates/hir/src/attrs.rs
@@ -12,9 +12,9 @@ use hir_ty::db::HirDatabase;
 use syntax::{ast, AstNode};
 
 use crate::{
-    Adt, AssocItem, Const, ConstParam, Enum, Field, Function, GenericParam, Impl, LifetimeParam,
-    Macro, Module, ModuleDef, Static, Struct, Trait, TraitAlias, TypeAlias, TypeParam, Union,
-    Variant,
+    Adt, AssocItem, Const, ConstParam, Enum, ExternCrateDecl, Field, Function, GenericParam, Impl,
+    LifetimeParam, Macro, Module, ModuleDef, Static, Struct, Trait, TraitAlias, TypeAlias,
+    TypeParam, Union, Variant,
 };
 
 pub trait HasAttrs {
@@ -120,6 +120,39 @@ impl HasAttrs for AssocItem {
     }
 }
 
+impl HasAttrs for ExternCrateDecl {
+    fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner {
+        let def = AttrDefId::ExternCrateId(self.into());
+        db.attrs_with_owner(def)
+    }
+    fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> {
+        let crate_docs = self.resolved_crate(db)?.root_module().attrs(db).docs().map(String::from);
+        let def = AttrDefId::ExternCrateId(self.into());
+        let decl_docs = db.attrs(def).docs().map(String::from);
+        match (decl_docs, crate_docs) {
+            (None, None) => None,
+            (Some(decl_docs), None) => Some(decl_docs),
+            (None, Some(crate_docs)) => Some(crate_docs),
+            (Some(mut decl_docs), Some(crate_docs)) => {
+                decl_docs.push('\n');
+                decl_docs.push('\n');
+                decl_docs += &crate_docs;
+                Some(decl_docs)
+            }
+        }
+        .map(Documentation::new)
+    }
+    fn resolve_doc_path(
+        self,
+        db: &dyn HirDatabase,
+        link: &str,
+        ns: Option<Namespace>,
+    ) -> Option<ModuleDef> {
+        let def = AttrDefId::ExternCrateId(self.into());
+        resolve_doc_path(db, def, link, ns).map(ModuleDef::from)
+    }
+}
+
 /// Resolves the item `link` points to in the scope of `def`.
 fn resolve_doc_path(
     db: &dyn HirDatabase,
diff --git a/crates/hir/src/db.rs b/crates/hir/src/db.rs
index f3a0608944b..936581bfe32 100644
--- a/crates/hir/src/db.rs
+++ b/crates/hir/src/db.rs
@@ -10,8 +10,3 @@ pub use hir_expand::db::{
     MacroExpandQuery, ParseMacroExpansionErrorQuery, ParseMacroExpansionQuery,
 };
 pub use hir_ty::db::*;
-
-#[test]
-fn hir_database_is_object_safe() {
-    fn _assert_object_safe(_: &dyn HirDatabase) {}
-}
diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs
index 4de9c872ad6..9dfb98e459b 100644
--- a/crates/hir/src/display.rs
+++ b/crates/hir/src/display.rs
@@ -18,9 +18,9 @@ use hir_ty::{
 };
 
 use crate::{
-    Adt, AsAssocItem, AssocItemContainer, Const, ConstParam, Enum, Field, Function, GenericParam,
-    HasCrate, HasVisibility, LifetimeParam, Macro, Module, Static, Struct, Trait, TraitAlias,
-    TyBuilder, Type, TypeAlias, TypeOrConstParam, TypeParam, Union, Variant,
+    Adt, AsAssocItem, AssocItemContainer, Const, ConstParam, Enum, ExternCrateDecl, Field,
+    Function, GenericParam, HasCrate, HasVisibility, LifetimeParam, Macro, Module, Static, Struct,
+    Trait, TraitAlias, TyBuilder, Type, TypeAlias, TypeOrConstParam, TypeParam, Union, Variant,
 };
 
 impl HirDisplay for Function {
@@ -238,6 +238,18 @@ impl HirDisplay for Type {
     }
 }
 
+impl HirDisplay for ExternCrateDecl {
+    fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
+        write_visibility(self.module(f.db).id, self.visibility(f.db), f)?;
+        f.write_str("extern crate ")?;
+        write!(f, "{}", self.name(f.db).display(f.db.upcast()))?;
+        if let Some(alias) = self.alias(f.db) {
+            write!(f, " as {alias}",)?;
+        }
+        Ok(())
+    }
+}
+
 impl HirDisplay for GenericParam {
     fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
         match self {
diff --git a/crates/hir/src/from_id.rs b/crates/hir/src/from_id.rs
index de23902199f..fc4bbffdb83 100644
--- a/crates/hir/src/from_id.rs
+++ b/crates/hir/src/from_id.rs
@@ -15,7 +15,7 @@ use crate::{
 };
 
 macro_rules! from_id {
-    ($(($id:path, $ty:path)),*) => {$(
+    ($(($id:path, $ty:path)),* $(,)?) => {$(
         impl From<$id> for $ty {
             fn from(id: $id) -> $ty {
                 $ty { id }
@@ -47,7 +47,8 @@ from_id![
     (hir_def::TypeParamId, crate::TypeParam),
     (hir_def::ConstParamId, crate::ConstParam),
     (hir_def::LifetimeParamId, crate::LifetimeParam),
-    (hir_def::MacroId, crate::Macro)
+    (hir_def::MacroId, crate::Macro),
+    (hir_def::ExternCrateId, crate::ExternCrateDecl),
 ];
 
 impl From<AdtId> for Adt {
diff --git a/crates/hir/src/has_source.rs b/crates/hir/src/has_source.rs
index b46a3856d45..31cf8ba3364 100644
--- a/crates/hir/src/has_source.rs
+++ b/crates/hir/src/has_source.rs
@@ -11,9 +11,9 @@ use hir_expand::{HirFileId, InFile};
 use syntax::ast;
 
 use crate::{
-    db::HirDatabase, Adt, Const, Enum, Field, FieldSource, Function, Impl, LifetimeParam,
-    LocalSource, Macro, Module, Static, Struct, Trait, TraitAlias, TypeAlias, TypeOrConstParam,
-    Union, Variant,
+    db::HirDatabase, Adt, Const, Enum, ExternCrateDecl, Field, FieldSource, Function, Impl,
+    LifetimeParam, LocalSource, Macro, Module, Static, Struct, Trait, TraitAlias, TypeAlias,
+    TypeOrConstParam, Union, Variant,
 };
 
 pub trait HasSource {
@@ -207,3 +207,11 @@ impl HasSource for LocalSource {
         Some(self.source)
     }
 }
+
+impl HasSource for ExternCrateDecl {
+    type Ast = ast::ExternCrate;
+
+    fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
+        Some(self.id.lookup(db.upcast()).source(db.upcast()))
+    }
+}
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index b094bb7a068..bf041b61f2f 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -48,14 +48,15 @@ use hir_def::{
     layout::{self, ReprOptions, TargetDataLayout},
     macro_id_to_def_id,
     nameres::{self, diagnostics::DefDiagnostic},
+    path::ImportAlias,
     per_ns::PerNs,
     resolver::{HasResolver, Resolver},
     src::HasSource as _,
-    AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId, DefWithBodyId, EnumId,
-    EnumVariantId, FunctionId, GenericDefId, HasModule, ImplId, InTypeConstId, ItemContainerId,
-    LifetimeParamId, LocalEnumVariantId, LocalFieldId, Lookup, MacroExpander, MacroId, ModuleId,
-    StaticId, StructId, TraitAliasId, TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId,
-    UnionId,
+    AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId, CrateRootModuleId, DefWithBodyId,
+    EnumId, EnumVariantId, ExternCrateId, FunctionId, GenericDefId, HasModule, ImplId,
+    InTypeConstId, ItemContainerId, LifetimeParamId, LocalEnumVariantId, LocalFieldId, Lookup,
+    MacroExpander, MacroId, ModuleId, StaticId, StructId, TraitAliasId, TraitId, TypeAliasId,
+    TypeOrConstParamId, TypeParamId, UnionId,
 };
 use hir_expand::{name::name, MacroCallKind};
 use hir_ty::{
@@ -200,9 +201,8 @@ impl Crate {
         db.crate_graph().transitive_rev_deps(self.id).map(|id| Crate { id })
     }
 
-    pub fn root_module(self, db: &dyn HirDatabase) -> Module {
-        let def_map = db.crate_def_map(self.id);
-        Module { id: def_map.crate_root().into() }
+    pub fn root_module(self) -> Module {
+        Module { id: CrateRootModuleId::from(self.id).into() }
     }
 
     pub fn modules(self, db: &dyn HirDatabase) -> Vec<Module> {
@@ -247,7 +247,7 @@ impl Crate {
     /// Try to get the root URL of the documentation of a crate.
     pub fn get_html_root_url(self: &Crate, db: &dyn HirDatabase) -> Option<String> {
         // Look for #![doc(html_root_url = "...")]
-        let attrs = db.attrs(AttrDefId::ModuleId(self.root_module(db).into()));
+        let attrs = db.attrs(AttrDefId::ModuleId(self.root_module().into()));
         let doc_url = attrs.by_key("doc").find_string_value_in_tt("html_root_url");
         doc_url.map(|s| s.trim_matches('"').trim_end_matches('/').to_owned() + "/")
     }
@@ -2129,6 +2129,47 @@ impl HasVisibility for Function {
 }
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct ExternCrateDecl {
+    pub(crate) id: ExternCrateId,
+}
+
+impl ExternCrateDecl {
+    pub fn module(self, db: &dyn HirDatabase) -> Module {
+        self.id.module(db.upcast()).into()
+    }
+
+    pub fn resolved_crate(self, db: &dyn HirDatabase) -> Option<Crate> {
+        db.extern_crate_decl_data(self.id).crate_id.map(Into::into)
+    }
+
+    pub fn name(self, db: &dyn HirDatabase) -> Name {
+        db.extern_crate_decl_data(self.id).name.clone()
+    }
+
+    pub fn alias(self, db: &dyn HirDatabase) -> Option<ImportAlias> {
+        db.extern_crate_decl_data(self.id).alias.clone()
+    }
+
+    /// Returns the name under which this crate is made accessible, taking `_` into account.
+    pub fn alias_or_name(self, db: &dyn HirDatabase) -> Option<Name> {
+        let extern_crate_decl_data = db.extern_crate_decl_data(self.id);
+        match &extern_crate_decl_data.alias {
+            Some(ImportAlias::Underscore) => None,
+            Some(ImportAlias::Alias(alias)) => Some(alias.clone()),
+            None => Some(extern_crate_decl_data.name.clone()),
+        }
+    }
+}
+
+impl HasVisibility for ExternCrateDecl {
+    fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
+        db.extern_crate_decl_data(self.id)
+            .visibility
+            .resolve(db.upcast(), &self.id.resolver(db.upcast()))
+    }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct InTypeConst {
     pub(crate) id: InTypeConstId,
 }
@@ -4715,6 +4756,12 @@ pub trait HasContainer {
     fn container(&self, db: &dyn HirDatabase) -> ItemContainer;
 }
 
+impl HasContainer for ExternCrateDecl {
+    fn container(&self, db: &dyn HirDatabase) -> ItemContainer {
+        container_id_to_hir(self.id.lookup(db.upcast()).container.into())
+    }
+}
+
 impl HasContainer for Module {
     fn container(&self, db: &dyn HirDatabase) -> ItemContainer {
         // FIXME: handle block expressions as modules (their parent is in a different DefMap)
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 39a3e1c4489..e99d2984c36 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -15,11 +15,7 @@ use hir_def::{
     type_ref::Mutability,
     AsMacroCall, DefWithBodyId, FieldId, FunctionId, MacroId, TraitId, VariantId,
 };
-use hir_expand::{
-    db::ExpandDatabase,
-    name::{known, AsName},
-    ExpansionInfo, MacroCallId,
-};
+use hir_expand::{db::ExpandDatabase, name::AsName, ExpansionInfo, MacroCallId};
 use itertools::Itertools;
 use rustc_hash::{FxHashMap, FxHashSet};
 use smallvec::{smallvec, SmallVec};
@@ -439,10 +435,6 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
         self.imp.resolve_path(path)
     }
 
-    pub fn resolve_extern_crate(&self, extern_crate: &ast::ExternCrate) -> Option<Crate> {
-        self.imp.resolve_extern_crate(extern_crate)
-    }
-
     pub fn resolve_variant(&self, record_lit: ast::RecordExpr) -> Option<VariantDef> {
         self.imp.resolve_variant(record_lit).map(VariantDef::from)
     }
@@ -1242,18 +1234,6 @@ impl<'db> SemanticsImpl<'db> {
         self.analyze(path.syntax())?.resolve_path(self.db, path)
     }
 
-    fn resolve_extern_crate(&self, extern_crate: &ast::ExternCrate) -> Option<Crate> {
-        let krate = self.scope(extern_crate.syntax())?.krate();
-        let name = extern_crate.name_ref()?.as_name();
-        if name == known::SELF_PARAM {
-            return Some(krate);
-        }
-        krate
-            .dependencies(self.db)
-            .into_iter()
-            .find_map(|dep| (dep.name == name).then_some(dep.krate))
-    }
-
     fn resolve_variant(&self, record_lit: ast::RecordExpr) -> Option<VariantId> {
         self.analyze(record_lit.syntax())?.resolve_variant(self.db, record_lit)
     }
@@ -1603,6 +1583,7 @@ to_def_impls![
     (crate::Local, ast::SelfParam, self_param_to_def),
     (crate::Label, ast::Label, label_to_def),
     (crate::Adt, ast::Adt, adt_to_def),
+    (crate::ExternCrateDecl, ast::ExternCrate, extern_crate_to_def),
 ];
 
 fn find_root(node: &SyntaxNode) -> SyntaxNode {
diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs
index b971ca62387..e6eb7d7a563 100644
--- a/crates/hir/src/semantics/source_to_def.rs
+++ b/crates/hir/src/semantics/source_to_def.rs
@@ -93,9 +93,9 @@ use hir_def::{
         DynMap,
     },
     hir::{BindingId, LabelId},
-    AdtId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, FieldId, FunctionId,
-    GenericDefId, GenericParamId, ImplId, LifetimeParamId, MacroId, ModuleId, StaticId, StructId,
-    TraitAliasId, TraitId, TypeAliasId, TypeParamId, UnionId, VariantId,
+    AdtId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, ExternCrateId, FieldId,
+    FunctionId, GenericDefId, GenericParamId, ImplId, LifetimeParamId, MacroId, ModuleId, StaticId,
+    StructId, TraitAliasId, TraitId, TypeAliasId, TypeParamId, UnionId, VariantId,
 };
 use hir_expand::{attrs::AttrId, name::AsName, HirFileId, MacroCallId};
 use rustc_hash::FxHashMap;
@@ -203,6 +203,12 @@ impl SourceToDefCtx<'_, '_> {
     ) -> Option<EnumVariantId> {
         self.to_def(src, keys::VARIANT)
     }
+    pub(super) fn extern_crate_to_def(
+        &mut self,
+        src: InFile<ast::ExternCrate>,
+    ) -> Option<ExternCrateId> {
+        self.to_def(src, keys::EXTERN_CRATE)
+    }
     pub(super) fn adt_to_def(
         &mut self,
         InFile { file_id, value }: InFile<ast::Adt>,
diff --git a/crates/ide-assists/src/handlers/add_turbo_fish.rs b/crates/ide-assists/src/handlers/add_turbo_fish.rs
index acf82e4b257..36f68d17677 100644
--- a/crates/ide-assists/src/handlers/add_turbo_fish.rs
+++ b/crates/ide-assists/src/handlers/add_turbo_fish.rs
@@ -42,7 +42,9 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
     let name_ref = ast::NameRef::cast(ident.parent()?)?;
     let def = match NameRefClass::classify(&ctx.sema, &name_ref)? {
         NameRefClass::Definition(def) => def,
-        NameRefClass::FieldShorthand { .. } => return None,
+        NameRefClass::FieldShorthand { .. } | NameRefClass::ExternCrateShorthand { .. } => {
+            return None
+        }
     };
     let fun = match def {
         Definition::Function(it) => it,
diff --git a/crates/ide-db/src/defs.rs b/crates/ide-db/src/defs.rs
index 760834bfafc..5e4562d9c58 100644
--- a/crates/ide-db/src/defs.rs
+++ b/crates/ide-db/src/defs.rs
@@ -7,10 +7,10 @@
 
 use arrayvec::ArrayVec;
 use hir::{
-    Adt, AsAssocItem, AssocItem, BuiltinAttr, BuiltinType, Const, Crate, DeriveHelper, Field,
-    Function, GenericParam, HasVisibility, Impl, Label, Local, Macro, Module, ModuleDef, Name,
-    PathResolution, Semantics, Static, ToolModule, Trait, TraitAlias, TypeAlias, Variant,
-    Visibility,
+    Adt, AsAssocItem, AssocItem, BuiltinAttr, BuiltinType, Const, Crate, DeriveHelper,
+    ExternCrateDecl, Field, Function, GenericParam, HasVisibility, Impl, Label, Local, Macro,
+    Module, ModuleDef, Name, PathResolution, Semantics, Static, ToolModule, Trait, TraitAlias,
+    TypeAlias, Variant, Visibility,
 };
 use stdx::impl_from;
 use syntax::{
@@ -42,6 +42,7 @@ pub enum Definition {
     DeriveHelper(DeriveHelper),
     BuiltinAttr(BuiltinAttr),
     ToolModule(ToolModule),
+    ExternCrateDecl(ExternCrateDecl),
 }
 
 impl Definition {
@@ -73,6 +74,7 @@ impl Definition {
             Definition::Local(it) => it.module(db),
             Definition::GenericParam(it) => it.module(db),
             Definition::Label(it) => it.module(db),
+            Definition::ExternCrateDecl(it) => it.module(db),
             Definition::DeriveHelper(it) => it.derive().module(db),
             Definition::BuiltinAttr(_) | Definition::BuiltinType(_) | Definition::ToolModule(_) => {
                 return None
@@ -93,6 +95,7 @@ impl Definition {
             Definition::TraitAlias(it) => it.visibility(db),
             Definition::TypeAlias(it) => it.visibility(db),
             Definition::Variant(it) => it.visibility(db),
+            Definition::ExternCrateDecl(it) => it.visibility(db),
             Definition::BuiltinType(_) => Visibility::Public,
             Definition::Macro(_) => return None,
             Definition::BuiltinAttr(_)
@@ -127,6 +130,7 @@ impl Definition {
             Definition::BuiltinAttr(_) => return None, // FIXME
             Definition::ToolModule(_) => return None,  // FIXME
             Definition::DeriveHelper(it) => it.name(db),
+            Definition::ExternCrateDecl(it) => return it.alias_or_name(db),
         };
         Some(name)
     }
@@ -196,6 +200,10 @@ impl IdentClass {
                 res.push(Definition::Local(local_ref));
                 res.push(Definition::Field(field_ref));
             }
+            IdentClass::NameRefClass(NameRefClass::ExternCrateShorthand { decl, krate }) => {
+                res.push(Definition::ExternCrateDecl(decl));
+                res.push(Definition::Module(krate.root_module()));
+            }
             IdentClass::Operator(
                 OperatorClass::Await(func)
                 | OperatorClass::Prefix(func)
@@ -222,6 +230,10 @@ impl IdentClass {
                 res.push(Definition::Local(local_ref));
                 res.push(Definition::Field(field_ref));
             }
+            IdentClass::NameRefClass(NameRefClass::ExternCrateShorthand { decl, krate }) => {
+                res.push(Definition::ExternCrateDecl(decl));
+                res.push(Definition::Module(krate.root_module()));
+            }
             IdentClass::Operator(_) => (),
         }
         res
@@ -310,6 +322,7 @@ impl NameClass {
                 ast::Item::Enum(it) => Definition::Adt(hir::Adt::Enum(sema.to_def(&it)?)),
                 ast::Item::Struct(it) => Definition::Adt(hir::Adt::Struct(sema.to_def(&it)?)),
                 ast::Item::Union(it) => Definition::Adt(hir::Adt::Union(sema.to_def(&it)?)),
+                ast::Item::ExternCrate(it) => Definition::ExternCrateDecl(sema.to_def(&it)?),
                 _ => return None,
             };
             Some(definition)
@@ -346,10 +359,8 @@ impl NameClass {
                 let path = use_tree.path()?;
                 sema.resolve_path(&path).map(Definition::from)
             } else {
-                let extern_crate = rename.syntax().parent().and_then(ast::ExternCrate::cast)?;
-                let krate = sema.resolve_extern_crate(&extern_crate)?;
-                let root_module = krate.root_module(sema.db);
-                Some(Definition::Module(root_module))
+                sema.to_def(&rename.syntax().parent().and_then(ast::ExternCrate::cast)?)
+                    .map(Definition::ExternCrateDecl)
             }
         }
     }
@@ -427,7 +438,19 @@ impl OperatorClass {
 #[derive(Debug)]
 pub enum NameRefClass {
     Definition(Definition),
-    FieldShorthand { local_ref: Local, field_ref: Field },
+    FieldShorthand {
+        local_ref: Local,
+        field_ref: Field,
+    },
+    /// The specific situation where we have an extern crate decl without a rename
+    /// Here we have both a declaration and a reference.
+    /// ```rs
+    /// extern crate foo;
+    /// ```
+    ExternCrateShorthand {
+        decl: ExternCrateDecl,
+        krate: Crate,
+    },
 }
 
 impl NameRefClass {
@@ -513,10 +536,14 @@ impl NameRefClass {
                     }
                     None
                 },
-                ast::ExternCrate(extern_crate) => {
-                    let krate = sema.resolve_extern_crate(&extern_crate)?;
-                    let root_module = krate.root_module(sema.db);
-                    Some(NameRefClass::Definition(Definition::Module(root_module)))
+                ast::ExternCrate(extern_crate_ast) => {
+                    let extern_crate = sema.to_def(&extern_crate_ast)?;
+                    let krate = extern_crate.resolved_crate(sema.db)?;
+                    Some(if extern_crate_ast.rename().is_some() {
+                        NameRefClass::Definition(Definition::Module(krate.root_module()))
+                    } else {
+                        NameRefClass::ExternCrateShorthand { krate, decl: extern_crate }
+                    })
                 },
                 _ => None
             }
diff --git a/crates/ide-db/src/famous_defs.rs b/crates/ide-db/src/famous_defs.rs
index c8341fed1c7..b63dde2c21e 100644
--- a/crates/ide-db/src/famous_defs.rs
+++ b/crates/ide-db/src/famous_defs.rs
@@ -167,7 +167,7 @@ impl FamousDefs<'_, '_> {
             lang_crate => lang_crate,
         };
         let std_crate = self.find_lang_crate(lang_crate)?;
-        let mut module = std_crate.root_module(db);
+        let mut module = std_crate.root_module();
         for segment in path {
             module = module.children(db).find_map(|child| {
                 let name = child.name(db)?;
diff --git a/crates/ide-db/src/rename.rs b/crates/ide-db/src/rename.rs
index 52a23b4b8f3..aa0bb7cce69 100644
--- a/crates/ide-db/src/rename.rs
+++ b/crates/ide-db/src/rename.rs
@@ -82,8 +82,9 @@ impl Definition {
     }
 
     /// Textual range of the identifier which will change when renaming this
-    /// `Definition`. Note that some definitions, like builtin types, can't be
-    /// renamed.
+    /// `Definition`. Note that builtin types can't be
+    /// renamed and extern crate names will report its range, though a rename will introduce
+    /// an alias instead.
     pub fn range_for_rename(self, sema: &Semantics<'_, RootDatabase>) -> Option<FileRange> {
         let res = match self {
             Definition::Macro(mac) => {
@@ -146,6 +147,16 @@ impl Definition {
                 let lifetime = src.value.lifetime()?;
                 src.with_value(lifetime.syntax()).original_file_range_opt(sema.db)
             }
+            Definition::ExternCrateDecl(it) => {
+                let src = it.source(sema.db)?;
+                if let Some(rename) = src.value.rename() {
+                    let name = rename.name()?;
+                    src.with_value(name.syntax()).original_file_range_opt(sema.db)
+                } else {
+                    let name = src.value.name_ref()?;
+                    src.with_value(name.syntax()).original_file_range_opt(sema.db)
+                }
+            }
             Definition::BuiltinType(_) => return None,
             Definition::SelfType(_) => return None,
             Definition::BuiltinAttr(_) => return None,
@@ -526,6 +537,9 @@ fn source_edit_from_def(
             TextRange::new(range.start() + syntax::TextSize::from(1), range.end()),
             new_name.strip_prefix('\'').unwrap_or(new_name).to_owned(),
         ),
+        Definition::ExternCrateDecl(decl) if decl.alias(sema.db).is_none() => {
+            (TextRange::empty(range.end()), format!(" as {new_name}"))
+        }
         _ => (range, new_name.to_owned()),
     };
     edit.replace(range, new_name);
diff --git a/crates/ide-diagnostics/src/handlers/missing_fields.rs b/crates/ide-diagnostics/src/handlers/missing_fields.rs
index bb0e36ff3a1..acc31cd117a 100644
--- a/crates/ide-diagnostics/src/handlers/missing_fields.rs
+++ b/crates/ide-diagnostics/src/handlers/missing_fields.rs
@@ -208,7 +208,7 @@ fn get_default_constructor(
     }
 
     let krate = ctx.sema.to_module_def(d.file.original_file(ctx.sema.db))?.krate();
-    let module = krate.root_module(ctx.sema.db);
+    let module = krate.root_module();
 
     // Look for a ::new() associated function
     let has_new_func = ty
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs
index c90ba212535..d240127f376 100644
--- a/crates/ide/src/doc_links.rs
+++ b/crates/ide/src/doc_links.rs
@@ -153,6 +153,9 @@ pub(crate) fn external_docs(
                 NameRefClass::FieldShorthand { local_ref: _, field_ref } => {
                     Definition::Field(field_ref)
                 }
+                NameRefClass::ExternCrateShorthand { decl, .. } => {
+                    Definition::ExternCrateDecl(decl)
+                }
             },
             ast::Name(name) => match NameClass::classify(sema, &name)? {
                 NameClass::Definition(it) | NameClass::ConstReference(it) => it,
@@ -209,6 +212,7 @@ pub(crate) fn resolve_doc_path_for_def(
         Definition::Macro(it) => it.resolve_doc_path(db, link, ns),
         Definition::Field(it) => it.resolve_doc_path(db, link, ns),
         Definition::SelfType(it) => it.resolve_doc_path(db, link, ns),
+        Definition::ExternCrateDecl(it) => it.resolve_doc_path(db, link, ns),
         Definition::BuiltinAttr(_)
         | Definition::ToolModule(_)
         | Definition::BuiltinType(_)
@@ -617,6 +621,9 @@ fn filename_and_frag_for_def(
             // FIXME fragment numbering
             return Some((adt, file, Some(String::from("impl"))));
         }
+        Definition::ExternCrateDecl(it) => {
+            format!("{}/index.html", it.name(db).display(db.upcast()))
+        }
         Definition::Local(_)
         | Definition::GenericParam(_)
         | Definition::Label(_)
diff --git a/crates/ide/src/goto_declaration.rs b/crates/ide/src/goto_declaration.rs
index e70bc2ec541..c39c696cfd9 100644
--- a/crates/ide/src/goto_declaration.rs
+++ b/crates/ide/src/goto_declaration.rs
@@ -37,11 +37,15 @@ pub(crate) fn goto_declaration(
                 match parent {
                     ast::NameRef(name_ref) => match NameRefClass::classify(&sema, &name_ref)? {
                         NameRefClass::Definition(it) => Some(it),
-                        NameRefClass::FieldShorthand { field_ref, .. } => return field_ref.try_to_nav(db),
+                        NameRefClass::FieldShorthand { field_ref, .. } =>
+                            return field_ref.try_to_nav(db),
+                        NameRefClass::ExternCrateShorthand { decl, .. } =>
+                            return decl.try_to_nav(db),
                     },
                     ast::Name(name) => match NameClass::classify(&sema, &name)? {
                         NameClass::Definition(it) | NameClass::ConstReference(it) => Some(it),
-                        NameClass::PatFieldShorthand { field_ref, .. } => return field_ref.try_to_nav(db),
+                        NameClass::PatFieldShorthand { field_ref, .. } =>
+                            return field_ref.try_to_nav(db),
                     },
                     _ => None
                 }
@@ -53,6 +57,7 @@ pub(crate) fn goto_declaration(
                 Definition::Const(c) => c.as_assoc_item(db),
                 Definition::TypeAlias(ta) => ta.as_assoc_item(db),
                 Definition::Function(f) => f.as_assoc_item(db),
+                Definition::ExternCrateDecl(it) => return it.try_to_nav(db),
                 _ => None,
             }?;
 
@@ -211,4 +216,30 @@ fn main() {
 "#,
         );
     }
+
+    #[test]
+    fn goto_decl_for_extern_crate() {
+        check(
+            r#"
+//- /main.rs crate:main deps:std
+extern crate std$0;
+         /// ^^^
+//- /std/lib.rs crate:std
+// empty
+"#,
+        )
+    }
+
+    #[test]
+    fn goto_decl_for_renamed_extern_crate() {
+        check(
+            r#"
+//- /main.rs crate:main deps:std
+extern crate std as abc$0;
+                /// ^^^
+//- /std/lib.rs crate:std
+// empty
+"#,
+        )
+    }
 }
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index 4e641357e37..21471ab2a03 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -1,6 +1,9 @@
 use std::mem::discriminant;
 
-use crate::{doc_links::token_as_doc_comment, FilePosition, NavigationTarget, RangeInfo, TryToNav};
+use crate::{
+    doc_links::token_as_doc_comment, navigation_target::ToNav, FilePosition, NavigationTarget,
+    RangeInfo, TryToNav,
+};
 use hir::{AsAssocItem, AssocItem, Semantics};
 use ide_db::{
     base_db::{AnchoredPath, FileId, FileLoader},
@@ -73,6 +76,13 @@ pub(crate) fn goto_definition(
                     .definitions()
                     .into_iter()
                     .flat_map(|def| {
+                        if let Definition::ExternCrateDecl(crate_def) = def {
+                            return crate_def
+                                .resolved_crate(db)
+                                .map(|it| it.root_module().to_nav(sema.db))
+                                .into_iter()
+                                .collect();
+                        }
                         try_filter_trait_item_definition(sema, &def)
                             .unwrap_or_else(|| def_to_nav(sema.db, def))
                     })
diff --git a/crates/ide/src/goto_implementation.rs b/crates/ide/src/goto_implementation.rs
index a1a119629a9..37166bdbd0c 100644
--- a/crates/ide/src/goto_implementation.rs
+++ b/crates/ide/src/goto_implementation.rs
@@ -34,54 +34,50 @@ pub(crate) fn goto_implementation(
             _ => 0,
         })?;
     let range = original_token.text_range();
-    let navs = sema
-        .descend_into_macros(original_token)
-        .into_iter()
-        .filter_map(|token| token.parent().and_then(ast::NameLike::cast))
-        .filter_map(|node| match &node {
-            ast::NameLike::Name(name) => {
-                NameClass::classify(&sema, name).map(|class| match class {
-                    NameClass::Definition(it) | NameClass::ConstReference(it) => it,
-                    NameClass::PatFieldShorthand { local_def, field_ref: _ } => {
-                        Definition::Local(local_def)
+    let navs =
+        sema.descend_into_macros(original_token)
+            .into_iter()
+            .filter_map(|token| token.parent().and_then(ast::NameLike::cast))
+            .filter_map(|node| match &node {
+                ast::NameLike::Name(name) => {
+                    NameClass::classify(&sema, name).and_then(|class| match class {
+                        NameClass::Definition(it) | NameClass::ConstReference(it) => Some(it),
+                        NameClass::PatFieldShorthand { .. } => None,
+                    })
+                }
+                ast::NameLike::NameRef(name_ref) => NameRefClass::classify(&sema, name_ref)
+                    .and_then(|class| match class {
+                        NameRefClass::Definition(def) => Some(def),
+                        NameRefClass::FieldShorthand { .. }
+                        | NameRefClass::ExternCrateShorthand { .. } => None,
+                    }),
+                ast::NameLike::Lifetime(_) => None,
+            })
+            .unique()
+            .filter_map(|def| {
+                let navs = match def {
+                    Definition::Trait(trait_) => impls_for_trait(&sema, trait_),
+                    Definition::Adt(adt) => impls_for_ty(&sema, adt.ty(sema.db)),
+                    Definition::TypeAlias(alias) => impls_for_ty(&sema, alias.ty(sema.db)),
+                    Definition::BuiltinType(builtin) => impls_for_ty(&sema, builtin.ty(sema.db)),
+                    Definition::Function(f) => {
+                        let assoc = f.as_assoc_item(sema.db)?;
+                        let name = assoc.name(sema.db)?;
+                        let trait_ = assoc.containing_trait_or_trait_impl(sema.db)?;
+                        impls_for_trait_item(&sema, trait_, name)
                     }
-                })
-            }
-            ast::NameLike::NameRef(name_ref) => {
-                NameRefClass::classify(&sema, name_ref).map(|class| match class {
-                    NameRefClass::Definition(def) => def,
-                    NameRefClass::FieldShorthand { local_ref, field_ref: _ } => {
-                        Definition::Local(local_ref)
+                    Definition::Const(c) => {
+                        let assoc = c.as_assoc_item(sema.db)?;
+                        let name = assoc.name(sema.db)?;
+                        let trait_ = assoc.containing_trait_or_trait_impl(sema.db)?;
+                        impls_for_trait_item(&sema, trait_, name)
                     }
-                })
-            }
-            ast::NameLike::Lifetime(_) => None,
-        })
-        .unique()
-        .filter_map(|def| {
-            let navs = match def {
-                Definition::Trait(trait_) => impls_for_trait(&sema, trait_),
-                Definition::Adt(adt) => impls_for_ty(&sema, adt.ty(sema.db)),
-                Definition::TypeAlias(alias) => impls_for_ty(&sema, alias.ty(sema.db)),
-                Definition::BuiltinType(builtin) => impls_for_ty(&sema, builtin.ty(sema.db)),
-                Definition::Function(f) => {
-                    let assoc = f.as_assoc_item(sema.db)?;
-                    let name = assoc.name(sema.db)?;
-                    let trait_ = assoc.containing_trait_or_trait_impl(sema.db)?;
-                    impls_for_trait_item(&sema, trait_, name)
-                }
-                Definition::Const(c) => {
-                    let assoc = c.as_assoc_item(sema.db)?;
-                    let name = assoc.name(sema.db)?;
-                    let trait_ = assoc.containing_trait_or_trait_impl(sema.db)?;
-                    impls_for_trait_item(&sema, trait_, name)
-                }
-                _ => return None,
-            };
-            Some(navs)
-        })
-        .flatten()
-        .collect();
+                    _ => return None,
+                };
+                Some(navs)
+            })
+            .flatten()
+            .collect();
 
     Some(RangeInfo { range, info: navs })
 }
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 5ef6ac94807..40659e6c263 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -9,7 +9,7 @@ use either::Either;
 use hir::{db::DefDatabase, HasSource, LangItem, Semantics};
 use ide_db::{
     base_db::FileRange,
-    defs::{Definition, IdentClass, OperatorClass},
+    defs::{Definition, IdentClass, NameRefClass, OperatorClass},
     famous_defs::FamousDefs,
     helpers::pick_best_token,
     FxIndexSet, RootDatabase,
@@ -186,7 +186,20 @@ fn hover_simple(
                         // rendering poll is very confusing
                         return None;
                     }
-                    Some(class.definitions().into_iter().zip(iter::once(node).cycle()))
+                    if let IdentClass::NameRefClass(NameRefClass::ExternCrateShorthand {
+                        decl,
+                        ..
+                    }) = class
+                    {
+                        return Some(vec![(Definition::ExternCrateDecl(decl), node)]);
+                    }
+                    Some(
+                        class
+                            .definitions()
+                            .into_iter()
+                            .zip(iter::once(node).cycle())
+                            .collect::<Vec<_>>(),
+                    )
                 })
                 .flatten()
                 .unique_by(|&(def, _)| def)
diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs
index ef33386a7e9..a33a6ee1816 100644
--- a/crates/ide/src/hover/render.rs
+++ b/crates/ide/src/hover/render.rs
@@ -257,7 +257,7 @@ pub(super) fn keyword(
     let KeywordHint { description, keyword_mod, actions } = keyword_hints(sema, token, parent);
 
     let doc_owner = find_std_module(&famous_defs, &keyword_mod)?;
-    let docs = doc_owner.attrs(sema.db).docs()?;
+    let docs = doc_owner.docs(sema.db)?;
     let markup = process_markup(
         sema.db,
         Definition::Module(doc_owner),
@@ -472,6 +472,7 @@ pub(super) fn definition(
         }
         Definition::GenericParam(it) => label_and_docs(db, it),
         Definition::Label(it) => return Some(Markup::fenced_block(&it.name(db).display(db))),
+        Definition::ExternCrateDecl(it) => label_and_docs(db, it),
         // FIXME: We should be able to show more info about these
         Definition::BuiltinAttr(it) => return render_builtin_attr(db, it),
         Definition::ToolModule(it) => return Some(Markup::fenced_block(&it.name(db))),
@@ -620,7 +621,7 @@ where
     D: HasAttrs + HirDisplay,
 {
     let label = def.display(db).to_string();
-    let docs = def.attrs(db).docs();
+    let docs = def.docs(db);
     (label, docs)
 }
 
@@ -645,7 +646,7 @@ where
     ) {
         format_to!(label, "{layout}");
     }
-    let docs = def.attrs(db).docs();
+    let docs = def.docs(db);
     (label, docs)
 }
 
@@ -677,7 +678,7 @@ where
     ) {
         format_to!(label, "{layout}");
     }
-    let docs = def.attrs(db).docs();
+    let docs = def.docs(db);
     (label, docs)
 }
 
@@ -696,7 +697,7 @@ where
     } else {
         def.display(db).to_string()
     };
-    let docs = def.attrs(db).docs();
+    let docs = def.docs(db);
     (label, docs)
 }
 
@@ -727,14 +728,14 @@ fn builtin(famous_defs: &FamousDefs<'_, '_>, builtin: hir::BuiltinType) -> Optio
     // std exposes prim_{} modules with docstrings on the root to document the builtins
     let primitive_mod = format!("prim_{}", builtin.name().display(famous_defs.0.db));
     let doc_owner = find_std_module(famous_defs, &primitive_mod)?;
-    let docs = doc_owner.attrs(famous_defs.0.db).docs()?;
+    let docs = doc_owner.docs(famous_defs.0.db)?;
     markup(Some(docs.into()), builtin.name().display(famous_defs.0.db).to_string(), None)
 }
 
 fn find_std_module(famous_defs: &FamousDefs<'_, '_>, name: &str) -> Option<hir::Module> {
     let db = famous_defs.0.db;
     let std_crate = famous_defs.std()?;
-    let std_root_module = std_crate.root_module(db);
+    let std_root_module = std_crate.root_module();
     std_root_module.children(db).find(|module| {
         module.name(db).map_or(false, |module| module.display(db).to_string() == name)
     })
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index 00e21433daa..ddc71dffa8a 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -1616,6 +1616,9 @@ fn test_hover_extern_crate() {
     check(
         r#"
 //- /main.rs crate:main deps:std
+//! Crate docs
+
+/// Decl docs!
 extern crate st$0d;
 //- /std/lib.rs crate:std
 //! Standard library for this test
@@ -1624,23 +1627,32 @@ extern crate st$0d;
 //! abc123
 "#,
         expect![[r#"
-                *std*
+            *std*
 
-                ```rust
-                extern crate std
-                ```
+            ```rust
+            main
+            ```
 
-                ---
+            ```rust
+            extern crate std
+            ```
+
+            ---
 
-                Standard library for this test
+            Decl docs!
 
-                Printed?
-                abc123
-            "#]],
+            Standard library for this test
+
+            Printed?
+            abc123
+        "#]],
     );
     check(
         r#"
 //- /main.rs crate:main deps:std
+//! Crate docs
+
+/// Decl docs!
 extern crate std as ab$0c;
 //- /std/lib.rs crate:std
 //! Standard library for this test
@@ -1649,19 +1661,25 @@ extern crate std as ab$0c;
 //! abc123
 "#,
         expect![[r#"
-                *abc*
+            *abc*
 
-                ```rust
-                extern crate std
-                ```
+            ```rust
+            main
+            ```
 
-                ---
+            ```rust
+            extern crate std as abc
+            ```
 
-                Standard library for this test
+            ---
 
-                Printed?
-                abc123
-            "#]],
+            Decl docs!
+
+            Standard library for this test
+
+            Printed?
+            abc123
+        "#]],
     );
 }
 
diff --git a/crates/ide/src/moniker.rs b/crates/ide/src/moniker.rs
index d486a794e13..de8c5b8e830 100644
--- a/crates/ide/src/moniker.rs
+++ b/crates/ide/src/moniker.rs
@@ -247,6 +247,10 @@ pub(crate) fn def_to_moniker(
             name: s.name(db).display(db).to_string(),
             desc: MonikerDescriptorKind::Meta,
         },
+        Definition::ExternCrateDecl(m) => MonikerDescriptor {
+            name: m.name(db).display(db).to_string(),
+            desc: MonikerDescriptorKind::Namespace,
+        },
     };
 
     description.push(name_desc);
diff --git a/crates/ide/src/navigation_target.rs b/crates/ide/src/navigation_target.rs
index c7abecb4f1e..d1479dd1e58 100644
--- a/crates/ide/src/navigation_target.rs
+++ b/crates/ide/src/navigation_target.rs
@@ -102,7 +102,7 @@ impl NavigationTarget {
                 full_range,
                 SymbolKind::Module,
             );
-            res.docs = module.attrs(db).docs();
+            res.docs = module.docs(db);
             res.description = Some(module.display(db).to_string());
             return res;
         }
@@ -217,6 +217,7 @@ impl TryToNav for Definition {
             Definition::Trait(it) => it.try_to_nav(db),
             Definition::TraitAlias(it) => it.try_to_nav(db),
             Definition::TypeAlias(it) => it.try_to_nav(db),
+            Definition::ExternCrateDecl(it) => Some(it.try_to_nav(db)?),
             Definition::BuiltinType(_) => None,
             Definition::ToolModule(_) => None,
             Definition::BuiltinAttr(_) => None,
@@ -375,6 +376,30 @@ impl TryToNav for hir::Impl {
     }
 }
 
+impl TryToNav for hir::ExternCrateDecl {
+    fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
+        let src = self.source(db)?;
+        let InFile { file_id, value } = src;
+        let focus = value
+            .rename()
+            .map_or_else(|| value.name_ref().map(Either::Left), |it| it.name().map(Either::Right));
+        let (file_id, full_range, focus_range) =
+            orig_range_with_focus(db, file_id, value.syntax(), focus);
+        let mut res = NavigationTarget::from_syntax(
+            file_id,
+            self.alias_or_name(db).unwrap_or_else(|| self.name(db)).to_smol_str(),
+            focus_range,
+            full_range,
+            SymbolKind::Module,
+        );
+
+        res.docs = self.docs(db);
+        res.description = Some(self.display(db).to_string());
+        res.container_name = container_name(db, *self);
+        Some(res)
+    }
+}
+
 impl TryToNav for hir::Field {
     fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
         let src = self.source(db)?;
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs
index 44073fa757f..813f9ed943f 100644
--- a/crates/ide/src/references.rs
+++ b/crates/ide/src/references.rs
@@ -137,6 +137,9 @@ pub(crate) fn find_defs<'a>(
                             NameRefClass::FieldShorthand { local_ref, field_ref: _ } => {
                                 Definition::Local(local_ref)
                             }
+                            NameRefClass::ExternCrateShorthand { decl, .. } => {
+                                Definition::ExternCrateDecl(decl)
+                            }
                         }
                     }
                     ast::NameLike::Name(name) => match NameClass::classify(sema, &name)? {
diff --git a/crates/ide/src/rename.rs b/crates/ide/src/rename.rs
index 5c4beb7dd50..dae8e71e8a0 100644
--- a/crates/ide/src/rename.rs
+++ b/crates/ide/src/rename.rs
@@ -145,7 +145,14 @@ fn find_definitions(
                     if name
                         .syntax()
                         .parent()
-                        .map_or(false, |it| ast::Rename::can_cast(it.kind())) =>
+                        .map_or(false, |it| ast::Rename::can_cast(it.kind()))
+                        // FIXME: uncomment this once we resolve to usages to extern crate declarations
+                        // && name
+                        //     .syntax()
+                        //     .ancestors()
+                        //     .nth(2)
+                        //     .map_or(true, |it| !ast::ExternCrate::can_cast(it.kind()))
+                        =>
                 {
                     bail!("Renaming aliases is currently unsupported")
                 }
@@ -165,7 +172,12 @@ fn find_definitions(
                             NameRefClass::FieldShorthand { local_ref, field_ref: _ } => {
                                 Definition::Local(local_ref)
                             }
+                            NameRefClass::ExternCrateShorthand { decl, .. } => {
+                                Definition::ExternCrateDecl(decl)
+                            }
                         })
+                        // FIXME: uncomment this once we resolve to usages to extern crate declarations
+                        .filter(|def| !matches!(def, Definition::ExternCrateDecl(..)))
                         .ok_or_else(|| format_err!("No references found at position"))
                         .and_then(|def| {
                             // if the name differs from the definitions name it has to be an alias
@@ -2517,4 +2529,109 @@ fn main() {
 ",
         )
     }
+
+    #[test]
+    fn extern_crate() {
+        check_prepare(
+            r"
+//- /lib.rs crate:main deps:foo
+extern crate foo$0;
+use foo as qux;
+//- /foo.rs crate:foo
+",
+            expect![[r#"No references found at position"#]],
+        );
+        // FIXME: replace above check_prepare with this once we resolve to usages to extern crate declarations
+        //         check(
+        //             "bar",
+        //             r"
+        // //- /lib.rs crate:main deps:foo
+        // extern crate foo$0;
+        // use foo as qux;
+        // //- /foo.rs crate:foo
+        // ",
+        //             r"
+        // extern crate foo as bar;
+        // use bar as qux;
+        // ",
+        //         );
+    }
+
+    #[test]
+    fn extern_crate_rename() {
+        check_prepare(
+            r"
+//- /lib.rs crate:main deps:foo
+extern crate foo as qux$0;
+use qux as frob;
+//- /foo.rs crate:foo
+",
+            expect!["Renaming aliases is currently unsupported"],
+        );
+        // FIXME: replace above check_prepare with this once we resolve to usages to extern crate
+        // declarations
+        //         check(
+        //             "bar",
+        //             r"
+        // //- /lib.rs crate:main deps:foo
+        // extern crate foo as qux$0;
+        // use qux as frob;
+        // //- /foo.rs crate:foo
+        // ",
+        //             r"
+        // extern crate foo as bar;
+        // use bar as frob;
+        // ",
+        //         );
+    }
+
+    #[test]
+    fn extern_crate_self() {
+        check_prepare(
+            r"
+extern crate self$0;
+use self as qux;
+",
+            expect!["No references found at position"],
+        );
+        // FIXME: replace above check_prepare with this once we resolve to usages to extern crate declarations
+        //         check(
+        //             "bar",
+        //             r"
+        // extern crate self$0;
+        // use self as qux;
+        // ",
+        //             r"
+        // extern crate self as bar;
+        // use self as qux;
+        // ",
+        //         );
+    }
+
+    #[test]
+    fn extern_crate_self_rename() {
+        check_prepare(
+            r"
+//- /lib.rs crate:main deps:foo
+extern crate self as qux$0;
+use qux as frob;
+//- /foo.rs crate:foo
+",
+            expect!["Renaming aliases is currently unsupported"],
+        );
+        // FIXME: replace above check_prepare with this once we resolve to usages to extern crate declarations
+        //         check(
+        //             "bar",
+        //             r"
+        // //- /lib.rs crate:main deps:foo
+        // extern crate self as qux$0;
+        // use qux as frob;
+        // //- /foo.rs crate:foo
+        // ",
+        //             r"
+        // extern crate self as bar;
+        // use bar as frob;
+        // ",
+        //         );
+    }
 }
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs
index 8c445ddcb22..5f87a78551d 100644
--- a/crates/ide/src/runnables.rs
+++ b/crates/ide/src/runnables.rs
@@ -309,7 +309,7 @@ pub(crate) fn runnable_fn(
 ) -> Option<Runnable> {
     let name = def.name(sema.db).to_smol_str();
 
-    let root = def.module(sema.db).krate().root_module(sema.db);
+    let root = def.module(sema.db).krate().root_module();
 
     let kind = if name == "main" && def.module(sema.db) == root {
         RunnableKind::Bin
diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs
index 59e8300dcdb..d8696198d3b 100644
--- a/crates/ide/src/static_index.rs
+++ b/crates/ide/src/static_index.rs
@@ -88,7 +88,7 @@ pub struct StaticIndexedFile {
 
 fn all_modules(db: &dyn HirDatabase) -> Vec<Module> {
     let mut worklist: Vec<_> =
-        Crate::all(db).into_iter().map(|krate| krate.root_module(db)).collect();
+        Crate::all(db).into_iter().map(|krate| krate.root_module()).collect();
     let mut modules = Vec::new();
 
     while let Some(module) = worklist.pop() {
diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs
index 3c40246a69d..8e96bfa01ad 100644
--- a/crates/ide/src/syntax_highlighting/highlight.rs
+++ b/crates/ide/src/syntax_highlighting/highlight.rs
@@ -269,7 +269,26 @@ fn highlight_name_ref(
 
             h
         }
-        NameRefClass::FieldShorthand { .. } => SymbolKind::Field.into(),
+        NameRefClass::FieldShorthand { field_ref, .. } => {
+            highlight_def(sema, krate, field_ref.into())
+        }
+        NameRefClass::ExternCrateShorthand { decl, krate: resolved_krate } => {
+            let mut h = HlTag::Symbol(SymbolKind::Module).into();
+
+            if resolved_krate != krate {
+                h |= HlMod::Library
+            }
+            let is_public = decl.visibility(db) == hir::Visibility::Public;
+            if is_public {
+                h |= HlMod::Public
+            }
+            let is_from_builtin_crate = resolved_krate.is_builtin(db);
+            if is_from_builtin_crate {
+                h |= HlMod::DefaultLibrary;
+            }
+            h |= HlMod::CrateRoot;
+            h
+        }
     };
 
     h.tag = match name_ref.token_kind() {
@@ -474,6 +493,14 @@ fn highlight_def(
             }
             h
         }
+        Definition::ExternCrateDecl(extern_crate) => {
+            let mut highlight =
+                Highlight::new(HlTag::Symbol(SymbolKind::Module)) | HlMod::CrateRoot;
+            if extern_crate.alias(db).is_none() {
+                highlight |= HlMod::Library;
+            }
+            highlight
+        }
         Definition::Label(_) => Highlight::new(HlTag::Symbol(SymbolKind::Label)),
         Definition::BuiltinAttr(_) => Highlight::new(HlTag::Symbol(SymbolKind::BuiltinAttr)),
         Definition::ToolModule(_) => Highlight::new(HlTag::Symbol(SymbolKind::ToolModule)),
diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs
index 901df147d32..2657a641482 100644
--- a/crates/ide/src/syntax_highlighting/inject.rs
+++ b/crates/ide/src/syntax_highlighting/inject.rs
@@ -288,7 +288,7 @@ fn find_doc_string_in_attr(attr: &hir::Attr, it: &ast::Attr) -> Option<ast::Stri
 
 fn module_def_to_hl_tag(def: Definition) -> HlTag {
     let symbol = match def {
-        Definition::Module(_) => SymbolKind::Module,
+        Definition::Module(_) | Definition::ExternCrateDecl(_) => SymbolKind::Module,
         Definition::Function(_) => SymbolKind::Function,
         Definition::Adt(hir::Adt::Struct(_)) => SymbolKind::Struct,
         Definition::Adt(hir::Adt::Enum(_)) => SymbolKind::Enum,
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html b/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
index b15f7bca72b..88a008796b3 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
@@ -44,5 +44,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 </style>
 <pre><code><span class="keyword">extern</span> <span class="keyword">crate</span> <span class="module crate_root default_library library">std</span><span class="semicolon">;</span>
-<span class="keyword">extern</span> <span class="keyword">crate</span> <span class="module crate_root default_library library">alloc</span> <span class="keyword">as</span> <span class="module crate_root default_library declaration library">abc</span><span class="semicolon">;</span>
+<span class="keyword">extern</span> <span class="keyword">crate</span> <span class="module crate_root default_library library">alloc</span> <span class="keyword">as</span> <span class="module crate_root declaration">abc</span><span class="semicolon">;</span>
+<span class="keyword">extern</span> <span class="keyword">crate</span> <span class="unresolved_reference">unresolved</span> <span class="keyword">as</span> <span class="module crate_root declaration">definitely_unresolved</span><span class="semicolon">;</span>
 </code></pre>
\ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html
index fd3b39855e2..2043752bc74 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html
@@ -43,7 +43,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 </style>
-<pre><code><span class="keyword">extern</span> <span class="keyword">crate</span> <span class="self_keyword crate_root public">self</span><span class="semicolon">;</span>
+<pre><code><span class="keyword">extern</span> <span class="keyword">crate</span> <span class="self_keyword crate_root">self</span><span class="semicolon">;</span>
 
 <span class="keyword">use</span> <span class="keyword crate_root public">crate</span><span class="semicolon">;</span>
 <span class="keyword">use</span> <span class="self_keyword crate_root public">self</span><span class="semicolon">;</span>
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index 87470718070..8749d355c85 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -804,6 +804,7 @@ fn test_extern_crate() {
 //- /main.rs crate:main deps:std,alloc
 extern crate std;
 extern crate alloc as abc;
+extern crate unresolved as definitely_unresolved;
 //- /std/lib.rs crate:std
 pub struct S;
 //- /alloc/lib.rs crate:alloc
diff --git a/crates/parser/src/shortcuts.rs b/crates/parser/src/shortcuts.rs
index 6e3ae656b02..53cdad64992 100644
--- a/crates/parser/src/shortcuts.rs
+++ b/crates/parser/src/shortcuts.rs
@@ -223,7 +223,8 @@ fn n_attached_trivias<'a>(
 ) -> usize {
     match kind {
         CONST | ENUM | FN | IMPL | MACRO_CALL | MACRO_DEF | MACRO_RULES | MODULE | RECORD_FIELD
-        | STATIC | STRUCT | TRAIT | TUPLE_FIELD | TYPE_ALIAS | UNION | USE | VARIANT => {
+        | STATIC | STRUCT | TRAIT | TUPLE_FIELD | TYPE_ALIAS | UNION | USE | VARIANT
+        | EXTERN_CRATE => {
             let mut res = 0;
             let mut trivias = trivias.enumerate().peekable();
 
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs
index 33d7b5ed878..f446a7c0596 100644
--- a/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -128,7 +128,7 @@ impl flags::AnalysisStats {
         let mut visited_modules = FxHashSet::default();
         let mut visit_queue = Vec::new();
         for krate in krates {
-            let module = krate.root_module(db);
+            let module = krate.root_module();
             let file_id = module.definition_source_file_id(db);
             let file_id = file_id.original_file(db);
             let source_root = db.file_source_root(file_id);
diff --git a/crates/rust-analyzer/src/cli/diagnostics.rs b/crates/rust-analyzer/src/cli/diagnostics.rs
index 0db5fb4740e..8541be715a9 100644
--- a/crates/rust-analyzer/src/cli/diagnostics.rs
+++ b/crates/rust-analyzer/src/cli/diagnostics.rs
@@ -80,7 +80,7 @@ impl flags::Diagnostics {
 
 fn all_modules(db: &dyn HirDatabase) -> Vec<Module> {
     let mut worklist: Vec<_> =
-        Crate::all(db).into_iter().map(|krate| krate.root_module(db)).collect();
+        Crate::all(db).into_iter().map(|krate| krate.root_module()).collect();
     let mut modules = Vec::new();
 
     while let Some(module) = worklist.pop() {
diff --git a/crates/rust-analyzer/src/cli/run_tests.rs b/crates/rust-analyzer/src/cli/run_tests.rs
index b63a266a57a..e1704199151 100644
--- a/crates/rust-analyzer/src/cli/run_tests.rs
+++ b/crates/rust-analyzer/src/cli/run_tests.rs
@@ -76,7 +76,7 @@ fn all_modules(db: &dyn HirDatabase) -> Vec<Module> {
     let mut worklist: Vec<_> = Crate::all(db)
         .into_iter()
         .filter(|x| x.origin(db).is_local())
-        .map(|krate| krate.root_module(db))
+        .map(|krate| krate.root_module())
         .collect();
     let mut modules = Vec::new();