about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-08-02 10:27:30 +0000
committerbors <bors@rust-lang.org>2023-08-02 10:27:30 +0000
commit12cb6e7327f19c90158f4f64cc7a9d21431977d4 (patch)
tree34ea98bdfcf335612941fe23e70feaa4ac7e50d6
parent8e18b0f28c467cd5c7ae879dda71b30a57a47687 (diff)
parent6e2c3f610b6ed6e0c26aacf967117cbaf06c3754 (diff)
downloadrust-12cb6e7327f19c90158f4f64cc7a9d21431977d4.tar.gz
rust-12cb6e7327f19c90158f4f64cc7a9d21431977d4.zip
Auto merge of #15377 - Veykril:extern-crate-decl, r=Veykril
Add ExternCrateDecl to HIR

Adding these doesn't really require much design effort as they represent a single import, unlike use trees which are one item that represent 0 or more imports.

We only resolve to this definition when actually resolving on the name or alias of an `extern crate name as alias` item, not usages yet as that requires far more changes that won't lead anywhere without giving it more thought. Nevertheless the changes slightly improve IDE things, an example being hover on the decl showing the merged doc comments for example.

cc https://github.com/rust-lang/rust-analyzer/issues/14079
-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();