Add ExternCrateDecl to HIR

This commit is contained in:
Lukas Wirth 2023-08-02 11:52:55 +02:00
parent 151c750dac
commit bcff166b3a
39 changed files with 585 additions and 167 deletions

View file

@ -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(_)

View file

@ -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
"#,
)
}
}

View file

@ -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,12 @@ pub(crate) fn goto_definition(
.definitions()
.into_iter()
.flat_map(|def| {
if let Definition::ExternCrateDecl(crate_def) = def {
return vec![crate_def
.resolved_crate(db)
.root_module()
.to_nav(sema.db)];
}
try_filter_trait_item_definition(sema, &def)
.unwrap_or_else(|| def_to_nav(sema.db, def))
})

View file

@ -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)
}
})
}
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)
}
})
}
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)
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,
})
}
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();
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)
}
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();
Some(RangeInfo { range, info: navs })
}

View file

@ -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)

View file

@ -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)
})

View file

@ -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
---
Printed?
abc123
"#]],
Decl docs!
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
"#]],
);
}

View file

@ -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);

View file

@ -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)?;

View file

@ -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)? {

View file

@ -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;
// ",
// );
}
}

View file

@ -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

View file

@ -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() {

View file

@ -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)),

View file

@ -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,

View file

@ -44,5 +44,5 @@ 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>
</code></pre>

View file

@ -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>