Stop expanding UseTrees during ItemTree lowering
This commit is contained in:
parent
5587d0a3e3
commit
b52df91877
@ -472,27 +472,13 @@ pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) {
|
||||
});
|
||||
}
|
||||
|
||||
DefDiagnosticKind::UnresolvedImport { ast, index } => {
|
||||
let use_item = ast.to_node(db.upcast());
|
||||
let hygiene = Hygiene::new(db.upcast(), ast.file_id);
|
||||
let mut cur = 0;
|
||||
let mut tree = None;
|
||||
ModPath::expand_use_item(
|
||||
db.upcast(),
|
||||
InFile::new(ast.file_id, use_item),
|
||||
&hygiene,
|
||||
|_mod_path, use_tree, _is_glob, _alias| {
|
||||
if cur == *index {
|
||||
tree = Some(use_tree.clone());
|
||||
}
|
||||
DefDiagnosticKind::UnresolvedImport { id, index } => {
|
||||
let file_id = id.file_id();
|
||||
let item_tree = id.item_tree(db.upcast());
|
||||
let import = &item_tree[id.value];
|
||||
|
||||
cur += 1;
|
||||
},
|
||||
);
|
||||
|
||||
if let Some(tree) = tree {
|
||||
sink.push(UnresolvedImport { file: ast.file_id, node: AstPtr::new(&tree) });
|
||||
}
|
||||
let use_tree = import.use_tree_to_ast(db.upcast(), file_id, *index);
|
||||
sink.push(UnresolvedImport { file: file_id, node: AstPtr::new(&use_tree) });
|
||||
}
|
||||
|
||||
DefDiagnosticKind::UnconfiguredCode { ast, cfg, opts } => {
|
||||
|
@ -523,21 +523,38 @@ fn index(&self, id: FileItemTreeId<N>) -> &N {
|
||||
}
|
||||
}
|
||||
|
||||
/// A desugared `use` import.
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct Import {
|
||||
pub path: Interned<ModPath>,
|
||||
pub alias: Option<ImportAlias>,
|
||||
pub visibility: RawVisibilityId,
|
||||
pub is_glob: bool,
|
||||
/// AST ID of the `use` item this import was derived from. Note that many `Import`s can map to
|
||||
/// the same `use` item.
|
||||
pub ast_id: FileAstId<ast::Use>,
|
||||
/// Index of this `Import` when the containing `Use` is visited via `ModPath::expand_use_item`.
|
||||
///
|
||||
/// This can be used to get the `UseTree` this `Import` corresponds to and allows emitting
|
||||
/// precise diagnostics.
|
||||
pub index: usize,
|
||||
pub use_tree: UseTree,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct UseTree {
|
||||
pub index: Idx<ast::UseTree>,
|
||||
kind: UseTreeKind,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum UseTreeKind {
|
||||
/// ```ignore
|
||||
/// use path::to::Item;
|
||||
/// use path::to::Item as Renamed;
|
||||
/// use path::to::Trait as _;
|
||||
/// ```
|
||||
Single { path: ModPath, alias: Option<ImportAlias> },
|
||||
|
||||
/// ```ignore
|
||||
/// use *; // (invalid, but can occur in nested tree)
|
||||
/// use path::*;
|
||||
/// ```
|
||||
Glob { path: Option<ModPath> },
|
||||
|
||||
/// ```ignore
|
||||
/// use prefix::{self, Item, ...};
|
||||
/// ```
|
||||
Prefixed { prefix: Option<ModPath>, list: Vec<UseTree> },
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
@ -711,6 +728,97 @@ pub struct MacroDef {
|
||||
pub ast_id: FileAstId<ast::MacroDef>,
|
||||
}
|
||||
|
||||
impl Import {
|
||||
/// Maps a `UseTree` contained in this import back to its AST node.
|
||||
pub fn use_tree_to_ast(
|
||||
&self,
|
||||
db: &dyn DefDatabase,
|
||||
file_id: HirFileId,
|
||||
index: Idx<ast::UseTree>,
|
||||
) -> ast::UseTree {
|
||||
// Re-lower the AST item and get the source map.
|
||||
// Note: The AST unwraps are fine, since if they fail we should have never obtained `index`.
|
||||
let ast = InFile::new(file_id, self.ast_id).to_node(db.upcast());
|
||||
let ast_use_tree = ast.use_tree().expect("missing `use_tree`");
|
||||
let hygiene = Hygiene::new(db.upcast(), file_id);
|
||||
let (_, source_map) =
|
||||
lower::lower_use_tree(db, &hygiene, ast_use_tree).expect("failed to lower use tree");
|
||||
source_map[index].clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl UseTree {
|
||||
/// Expands the `UseTree` into individually imported `ModPath`s.
|
||||
pub fn expand(
|
||||
&self,
|
||||
mut cb: impl FnMut(Idx<ast::UseTree>, ModPath, /* is_glob */ bool, Option<ImportAlias>),
|
||||
) {
|
||||
self.expand_impl(None, &mut cb)
|
||||
}
|
||||
|
||||
fn expand_impl(
|
||||
&self,
|
||||
prefix: Option<ModPath>,
|
||||
cb: &mut dyn FnMut(
|
||||
Idx<ast::UseTree>,
|
||||
ModPath,
|
||||
/* is_glob */ bool,
|
||||
Option<ImportAlias>,
|
||||
),
|
||||
) {
|
||||
fn concat_mod_paths(prefix: Option<ModPath>, path: &ModPath) -> Option<ModPath> {
|
||||
match (prefix, &path.kind) {
|
||||
(None, _) => Some(path.clone()),
|
||||
(Some(mut prefix), PathKind::Plain) => {
|
||||
for segment in path.segments() {
|
||||
prefix.push_segment(segment.clone());
|
||||
}
|
||||
Some(prefix)
|
||||
}
|
||||
(Some(prefix), PathKind::Super(0)) => {
|
||||
// `some::path::self` == `some::path`
|
||||
if path.segments().is_empty() {
|
||||
Some(prefix)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
(Some(_), _) => None,
|
||||
}
|
||||
}
|
||||
|
||||
match &self.kind {
|
||||
UseTreeKind::Single { path, alias } => {
|
||||
if let Some(path) = concat_mod_paths(prefix, path) {
|
||||
cb(self.index, path, false, alias.clone());
|
||||
}
|
||||
}
|
||||
UseTreeKind::Glob { path: Some(path) } => {
|
||||
if let Some(path) = concat_mod_paths(prefix, path) {
|
||||
cb(self.index, path, true, None);
|
||||
}
|
||||
}
|
||||
UseTreeKind::Glob { path: None } => {
|
||||
if let Some(prefix) = prefix {
|
||||
cb(self.index, prefix, true, None);
|
||||
}
|
||||
}
|
||||
UseTreeKind::Prefixed { prefix: additional_prefix, list } => {
|
||||
let prefix = match additional_prefix {
|
||||
Some(path) => match concat_mod_paths(prefix, path) {
|
||||
Some(path) => Some(path),
|
||||
None => return,
|
||||
},
|
||||
None => prefix,
|
||||
};
|
||||
for tree in list {
|
||||
tree.expand_impl(prefix.clone(), cb);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_froms {
|
||||
($e:ident { $($v:ident ($t:ty)),* $(,)? }) => {
|
||||
$(
|
||||
|
@ -35,7 +35,6 @@ pub(super) struct Ctx<'a> {
|
||||
db: &'a dyn DefDatabase,
|
||||
tree: ItemTree,
|
||||
hygiene: Hygiene,
|
||||
file: HirFileId,
|
||||
source_ast_id_map: Arc<AstIdMap>,
|
||||
body_ctx: crate::body::LowerCtx<'a>,
|
||||
forced_visibility: Option<RawVisibilityId>,
|
||||
@ -47,7 +46,6 @@ pub(super) fn new(db: &'a dyn DefDatabase, hygiene: Hygiene, file: HirFileId) ->
|
||||
db,
|
||||
tree: ItemTree::default(),
|
||||
hygiene,
|
||||
file,
|
||||
source_ast_id_map: db.ast_id_map(file),
|
||||
body_ctx: crate::body::LowerCtx::new(db, file),
|
||||
forced_visibility: None,
|
||||
@ -561,30 +559,13 @@ fn lower_impl(&mut self, impl_def: &ast::Impl) -> Option<FileItemTreeId<Impl>> {
|
||||
Some(id(self.data().impls.alloc(res)))
|
||||
}
|
||||
|
||||
fn lower_use(&mut self, use_item: &ast::Use) -> Vec<FileItemTreeId<Import>> {
|
||||
fn lower_use(&mut self, use_item: &ast::Use) -> Option<FileItemTreeId<Import>> {
|
||||
let visibility = self.lower_visibility(use_item);
|
||||
let ast_id = self.source_ast_id_map.ast_id(use_item);
|
||||
let (use_tree, _) = lower_use_tree(self.db, &self.hygiene, use_item.use_tree()?)?;
|
||||
|
||||
// Every use item can expand to many `Import`s.
|
||||
let mut imports = Vec::new();
|
||||
let tree = self.tree.data_mut();
|
||||
ModPath::expand_use_item(
|
||||
self.db,
|
||||
InFile::new(self.file, use_item.clone()),
|
||||
&self.hygiene,
|
||||
|path, _use_tree, is_glob, alias| {
|
||||
imports.push(id(tree.imports.alloc(Import {
|
||||
path: Interned::new(path),
|
||||
alias,
|
||||
visibility,
|
||||
is_glob,
|
||||
ast_id,
|
||||
index: imports.len(),
|
||||
})));
|
||||
},
|
||||
);
|
||||
|
||||
imports
|
||||
let res = Import { visibility, ast_id, use_tree };
|
||||
Some(id(self.data().imports.alloc(res)))
|
||||
}
|
||||
|
||||
fn lower_extern_crate(
|
||||
@ -884,3 +865,76 @@ fn lower_abi(abi: ast::Abi) -> Interned<str> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct UseTreeLowering<'a> {
|
||||
db: &'a dyn DefDatabase,
|
||||
hygiene: &'a Hygiene,
|
||||
mapping: Arena<ast::UseTree>,
|
||||
}
|
||||
|
||||
impl UseTreeLowering<'_> {
|
||||
fn lower_use_tree(&mut self, tree: ast::UseTree) -> Option<UseTree> {
|
||||
if let Some(use_tree_list) = tree.use_tree_list() {
|
||||
let prefix = match tree.path() {
|
||||
// E.g. use something::{{{inner}}};
|
||||
None => None,
|
||||
// E.g. `use something::{inner}` (prefix is `None`, path is `something`)
|
||||
// or `use something::{path::{inner::{innerer}}}` (prefix is `something::path`, path is `inner`)
|
||||
Some(path) => {
|
||||
match ModPath::from_src(self.db, path, &self.hygiene) {
|
||||
Some(it) => Some(it),
|
||||
None => return None, // FIXME: report errors somewhere
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let list =
|
||||
use_tree_list.use_trees().filter_map(|tree| self.lower_use_tree(tree)).collect();
|
||||
|
||||
Some(self.use_tree(UseTreeKind::Prefixed { prefix, list }, tree))
|
||||
} else {
|
||||
let is_glob = tree.star_token().is_some();
|
||||
let path = match tree.path() {
|
||||
Some(path) => Some(ModPath::from_src(self.db, path, &self.hygiene)?),
|
||||
None => None,
|
||||
};
|
||||
let alias = tree.rename().map(|a| {
|
||||
a.name().map(|it| it.as_name()).map_or(ImportAlias::Underscore, ImportAlias::Alias)
|
||||
});
|
||||
if alias.is_some() && is_glob {
|
||||
return None;
|
||||
}
|
||||
|
||||
match (path, alias, is_glob) {
|
||||
(path, None, true) => {
|
||||
if path.is_none() {
|
||||
cov_mark::hit!(glob_enum_group);
|
||||
}
|
||||
Some(self.use_tree(UseTreeKind::Glob { path }, tree))
|
||||
}
|
||||
// Globs can't be renamed
|
||||
(_, Some(_), true) | (None, None, false) => None,
|
||||
// `bla::{ as Name}` is invalid
|
||||
(None, Some(_), false) => None,
|
||||
(Some(path), alias, false) => {
|
||||
Some(self.use_tree(UseTreeKind::Single { path, alias }, tree))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn use_tree(&mut self, kind: UseTreeKind, ast: ast::UseTree) -> UseTree {
|
||||
let index = self.mapping.alloc(ast);
|
||||
UseTree { index, kind }
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn lower_use_tree(
|
||||
db: &dyn DefDatabase,
|
||||
hygiene: &Hygiene,
|
||||
tree: ast::UseTree,
|
||||
) -> Option<(UseTree, Arena<ast::UseTree>)> {
|
||||
let mut lowering = UseTreeLowering { db, hygiene, mapping: Arena::new() };
|
||||
let tree = lowering.lower_use_tree(tree)?;
|
||||
Some((tree, lowering.mapping))
|
||||
}
|
||||
|
@ -163,21 +163,46 @@ fn print_fields_and_where_clause(&mut self, fields: &Fields, params: &GenericPar
|
||||
}
|
||||
}
|
||||
|
||||
fn print_use_tree(&mut self, use_tree: &UseTree) {
|
||||
match &use_tree.kind {
|
||||
UseTreeKind::Single { path, alias } => {
|
||||
w!(self, "{}", path);
|
||||
if let Some(alias) = alias {
|
||||
w!(self, " as {}", alias);
|
||||
}
|
||||
}
|
||||
UseTreeKind::Glob { path } => {
|
||||
if let Some(path) = path {
|
||||
w!(self, "{}::", path);
|
||||
}
|
||||
w!(self, "*");
|
||||
}
|
||||
UseTreeKind::Prefixed { prefix, list } => {
|
||||
if let Some(prefix) = prefix {
|
||||
w!(self, "{}::", prefix);
|
||||
}
|
||||
w!(self, "{{");
|
||||
for (i, tree) in list.iter().enumerate() {
|
||||
if i != 0 {
|
||||
w!(self, ", ");
|
||||
}
|
||||
self.print_use_tree(tree);
|
||||
}
|
||||
w!(self, "}}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn print_mod_item(&mut self, item: ModItem) {
|
||||
self.print_attrs_of(item);
|
||||
|
||||
match item {
|
||||
ModItem::Import(it) => {
|
||||
let Import { visibility, path, is_glob, alias, ast_id: _, index } = &self.tree[it];
|
||||
let Import { visibility, use_tree, ast_id: _ } = &self.tree[it];
|
||||
self.print_visibility(*visibility);
|
||||
w!(self, "use {}", path);
|
||||
if *is_glob {
|
||||
w!(self, "::*");
|
||||
}
|
||||
if let Some(alias) = alias {
|
||||
w!(self, " as {}", alias);
|
||||
}
|
||||
wln!(self, "; // {}", index);
|
||||
w!(self, "use ");
|
||||
self.print_use_tree(use_tree);
|
||||
wln!(self, ";");
|
||||
}
|
||||
ModItem::ExternCrate(it) => {
|
||||
let ExternCrate { name, alias, visibility, ast_id: _ } = &self.tree[it];
|
||||
|
@ -26,6 +26,8 @@ fn imports() {
|
||||
|
||||
/// docs on import
|
||||
use crate::{A, B};
|
||||
|
||||
use a::{c, d::{e}};
|
||||
"#,
|
||||
expect![[r##"
|
||||
#![doc = " file comment"] // AttrId { is_doc_comment: true, ast_index: 0 }
|
||||
@ -36,19 +38,14 @@ fn imports() {
|
||||
|
||||
pub(super) extern crate bli;
|
||||
|
||||
pub use crate::path::nested; // 0
|
||||
pub use crate::path::{nested, items as renamed, Trait as _};
|
||||
|
||||
pub use crate::path::items as renamed; // 1
|
||||
|
||||
pub use crate::path::Trait as _; // 2
|
||||
|
||||
pub(self) use globs::*; // 0
|
||||
pub(self) use globs::*;
|
||||
|
||||
#[doc = " docs on import"] // AttrId { is_doc_comment: true, ast_index: 0 }
|
||||
pub(self) use crate::A; // 0
|
||||
pub(self) use crate::{A, B};
|
||||
|
||||
#[doc = " docs on import"] // AttrId { is_doc_comment: true, ast_index: 0 }
|
||||
pub(self) use crate::B; // 1
|
||||
pub(self) use a::{c, d::{e}};
|
||||
"##]],
|
||||
);
|
||||
}
|
||||
@ -218,7 +215,7 @@ fn fn_in_module() {}
|
||||
#[doc = " outer"] // AttrId { is_doc_comment: true, ast_index: 0 }
|
||||
#[doc = " inner"] // AttrId { is_doc_comment: true, ast_index: 1 }
|
||||
pub(self) mod inline {
|
||||
pub(self) use super::*; // 0
|
||||
pub(self) use super::*;
|
||||
|
||||
// flags = 0x2
|
||||
pub(self) fn fn_in_module() -> ();
|
||||
|
@ -17,6 +17,7 @@
|
||||
};
|
||||
use hir_expand::{InFile, MacroCallLoc};
|
||||
use itertools::Itertools;
|
||||
use la_arena::Idx;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use syntax::ast;
|
||||
|
||||
@ -143,7 +144,7 @@ fn namespaces(&self) -> PerNs {
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
enum ImportSource {
|
||||
Import(ItemTreeId<item_tree::Import>),
|
||||
Import { id: ItemTreeId<item_tree::Import>, use_tree: Idx<ast::UseTree> },
|
||||
ExternCrate(ItemTreeId<item_tree::ExternCrate>),
|
||||
}
|
||||
|
||||
@ -165,20 +166,26 @@ fn from_use(
|
||||
krate: CrateId,
|
||||
tree: &ItemTree,
|
||||
id: ItemTreeId<item_tree::Import>,
|
||||
) -> Self {
|
||||
) -> Vec<Self> {
|
||||
let it = &tree[id.value];
|
||||
let attrs = &tree.attrs(db, krate, ModItem::from(id.value).into());
|
||||
let visibility = &tree[it.visibility];
|
||||
Self {
|
||||
path: it.path.clone(),
|
||||
alias: it.alias.clone(),
|
||||
visibility: visibility.clone(),
|
||||
is_glob: it.is_glob,
|
||||
is_prelude: attrs.by_key("prelude_import").exists(),
|
||||
is_extern_crate: false,
|
||||
is_macro_use: false,
|
||||
source: ImportSource::Import(id),
|
||||
}
|
||||
let is_prelude = attrs.by_key("prelude_import").exists();
|
||||
|
||||
let mut res = Vec::new();
|
||||
it.use_tree.expand(|idx, path, is_glob, alias| {
|
||||
res.push(Self {
|
||||
path: Interned::new(path), // FIXME this makes little sense
|
||||
alias,
|
||||
visibility: visibility.clone(),
|
||||
is_glob,
|
||||
is_prelude,
|
||||
is_extern_crate: false,
|
||||
is_macro_use: false,
|
||||
source: ImportSource::Import { id, use_tree: idx },
|
||||
});
|
||||
});
|
||||
res
|
||||
}
|
||||
|
||||
fn from_extern_crate(
|
||||
@ -1130,11 +1137,8 @@ fn finish(mut self) -> DefMap {
|
||||
}
|
||||
|
||||
for directive in &self.unresolved_imports {
|
||||
if let ImportSource::Import(import) = &directive.import.source {
|
||||
let item_tree = import.item_tree(self.db);
|
||||
let import_data = &item_tree[import.value];
|
||||
|
||||
match (import_data.path.segments().first(), &import_data.path.kind) {
|
||||
if let ImportSource::Import { id: import, use_tree } = &directive.import.source {
|
||||
match (directive.import.path.segments().first(), &directive.import.path.kind) {
|
||||
(Some(krate), PathKind::Plain) | (Some(krate), PathKind::Abs) => {
|
||||
if diagnosed_extern_crates.contains(krate) {
|
||||
continue;
|
||||
@ -1145,8 +1149,8 @@ fn finish(mut self) -> DefMap {
|
||||
|
||||
self.def_map.diagnostics.push(DefDiagnostic::unresolved_import(
|
||||
directive.module_id,
|
||||
InFile::new(import.file_id(), import_data.ast_id),
|
||||
import_data.index,
|
||||
*import,
|
||||
*use_tree,
|
||||
));
|
||||
}
|
||||
}
|
||||
@ -1222,16 +1226,20 @@ fn collect(&mut self, items: &[ModItem]) {
|
||||
match item {
|
||||
ModItem::Mod(m) => self.collect_module(&self.item_tree[m], &attrs),
|
||||
ModItem::Import(import_id) => {
|
||||
self.def_collector.unresolved_imports.push(ImportDirective {
|
||||
module_id: self.module_id,
|
||||
import: Import::from_use(
|
||||
self.def_collector.db,
|
||||
krate,
|
||||
&self.item_tree,
|
||||
ItemTreeId::new(self.file_id, import_id),
|
||||
),
|
||||
status: PartialResolvedImport::Unresolved,
|
||||
})
|
||||
let module_id = self.module_id;
|
||||
let imports = Import::from_use(
|
||||
self.def_collector.db,
|
||||
krate,
|
||||
&self.item_tree,
|
||||
ItemTreeId::new(self.file_id, import_id),
|
||||
);
|
||||
self.def_collector.unresolved_imports.extend(imports.into_iter().map(
|
||||
|import| ImportDirective {
|
||||
module_id,
|
||||
import,
|
||||
status: PartialResolvedImport::Unresolved,
|
||||
},
|
||||
));
|
||||
}
|
||||
ModItem::ExternCrate(import_id) => {
|
||||
self.def_collector.unresolved_imports.push(ImportDirective {
|
||||
|
@ -2,9 +2,15 @@
|
||||
|
||||
use cfg::{CfgExpr, CfgOptions};
|
||||
use hir_expand::MacroCallKind;
|
||||
use la_arena::Idx;
|
||||
use syntax::ast;
|
||||
|
||||
use crate::{nameres::LocalModuleId, path::ModPath, AstId};
|
||||
use crate::{
|
||||
item_tree::{self, ItemTreeId},
|
||||
nameres::LocalModuleId,
|
||||
path::ModPath,
|
||||
AstId,
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum DefDiagnosticKind {
|
||||
@ -12,7 +18,7 @@ pub enum DefDiagnosticKind {
|
||||
|
||||
UnresolvedExternCrate { ast: AstId<ast::ExternCrate> },
|
||||
|
||||
UnresolvedImport { ast: AstId<ast::Use>, index: usize },
|
||||
UnresolvedImport { id: ItemTreeId<item_tree::Import>, index: Idx<ast::UseTree> },
|
||||
|
||||
UnconfiguredCode { ast: AstId<ast::Item>, cfg: CfgExpr, opts: CfgOptions },
|
||||
|
||||
@ -53,10 +59,10 @@ pub(super) fn unresolved_extern_crate(
|
||||
|
||||
pub(super) fn unresolved_import(
|
||||
container: LocalModuleId,
|
||||
ast: AstId<ast::Use>,
|
||||
index: usize,
|
||||
id: ItemTreeId<item_tree::Import>,
|
||||
index: Idx<ast::UseTree>,
|
||||
) -> Self {
|
||||
Self { in_module: container, kind: DefDiagnosticKind::UnresolvedImport { ast, index } }
|
||||
Self { in_module: container, kind: DefDiagnosticKind::UnresolvedImport { id, index } }
|
||||
}
|
||||
|
||||
pub(super) fn unconfigured_code(
|
||||
|
@ -14,10 +14,7 @@
|
||||
};
|
||||
use syntax::ast;
|
||||
|
||||
use crate::{
|
||||
type_ref::{TypeBound, TypeRef},
|
||||
InFile,
|
||||
};
|
||||
use crate::type_ref::{TypeBound, TypeRef};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct ModPath {
|
||||
@ -56,8 +53,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
|
||||
impl ModPath {
|
||||
pub fn from_src(db: &dyn DefDatabase, path: ast::Path, hygiene: &Hygiene) -> Option<ModPath> {
|
||||
let ctx = LowerCtx::with_hygiene(db, hygiene);
|
||||
lower::lower_path(path, &ctx).map(|it| (*it.mod_path).clone())
|
||||
lower::convert_path(db, None, path, hygiene)
|
||||
}
|
||||
|
||||
pub fn from_segments(kind: PathKind, segments: impl IntoIterator<Item = Name>) -> ModPath {
|
||||
@ -70,18 +66,6 @@ pub const fn from_kind(kind: PathKind) -> ModPath {
|
||||
ModPath { kind, segments: Vec::new() }
|
||||
}
|
||||
|
||||
/// Calls `cb` with all paths, represented by this use item.
|
||||
pub fn expand_use_item(
|
||||
db: &dyn DefDatabase,
|
||||
item_src: InFile<ast::Use>,
|
||||
hygiene: &Hygiene,
|
||||
mut cb: impl FnMut(ModPath, &ast::UseTree, /* is_glob */ bool, Option<ImportAlias>),
|
||||
) {
|
||||
if let Some(tree) = item_src.value.use_tree() {
|
||||
lower::lower_use_tree(db, None, tree, hygiene, &mut cb);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn segments(&self) -> &[Name] {
|
||||
&self.segments
|
||||
}
|
||||
|
@ -15,7 +15,7 @@
|
||||
type_ref::{LifetimeRef, TypeBound, TypeRef},
|
||||
};
|
||||
|
||||
pub(super) use lower_use::lower_use_tree;
|
||||
pub(super) use lower_use::convert_path;
|
||||
|
||||
/// Converts an `ast::Path` to `Path`. Works with use trees.
|
||||
/// It correctly handles `$crate` based path from macro call.
|
||||
|
@ -4,68 +4,15 @@
|
||||
use std::iter;
|
||||
|
||||
use either::Either;
|
||||
use hir_expand::{hygiene::Hygiene, name::AsName};
|
||||
use syntax::ast::{self, NameOwner};
|
||||
use hir_expand::hygiene::Hygiene;
|
||||
use syntax::{ast, AstNode};
|
||||
|
||||
use crate::{
|
||||
db::DefDatabase,
|
||||
path::{ImportAlias, ModPath, PathKind},
|
||||
path::{ModPath, PathKind},
|
||||
};
|
||||
|
||||
pub(crate) fn lower_use_tree(
|
||||
db: &dyn DefDatabase,
|
||||
prefix: Option<ModPath>,
|
||||
tree: ast::UseTree,
|
||||
hygiene: &Hygiene,
|
||||
cb: &mut dyn FnMut(ModPath, &ast::UseTree, bool, Option<ImportAlias>),
|
||||
) {
|
||||
if let Some(use_tree_list) = tree.use_tree_list() {
|
||||
let prefix = match tree.path() {
|
||||
// E.g. use something::{{{inner}}};
|
||||
None => prefix,
|
||||
// E.g. `use something::{inner}` (prefix is `None`, path is `something`)
|
||||
// or `use something::{path::{inner::{innerer}}}` (prefix is `something::path`, path is `inner`)
|
||||
Some(path) => match convert_path(db, prefix, path, hygiene) {
|
||||
Some(it) => Some(it),
|
||||
None => return, // FIXME: report errors somewhere
|
||||
},
|
||||
};
|
||||
for child_tree in use_tree_list.use_trees() {
|
||||
lower_use_tree(db, prefix.clone(), child_tree, hygiene, cb);
|
||||
}
|
||||
} else {
|
||||
let alias = tree.rename().map(|a| {
|
||||
a.name().map(|it| it.as_name()).map_or(ImportAlias::Underscore, ImportAlias::Alias)
|
||||
});
|
||||
let is_glob = tree.star_token().is_some();
|
||||
if let Some(ast_path) = tree.path() {
|
||||
// Handle self in a path.
|
||||
// E.g. `use something::{self, <...>}`
|
||||
if ast_path.qualifier().is_none() {
|
||||
if let Some(segment) = ast_path.segment() {
|
||||
if segment.kind() == Some(ast::PathSegmentKind::SelfKw) {
|
||||
if let Some(prefix) = prefix {
|
||||
cb(prefix, &tree, false, alias);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(path) = convert_path(db, prefix, ast_path, hygiene) {
|
||||
cb(path, &tree, is_glob, alias)
|
||||
}
|
||||
// FIXME: report errors somewhere
|
||||
// We get here if we do
|
||||
} else if is_glob {
|
||||
cov_mark::hit!(glob_enum_group);
|
||||
if let Some(prefix) = prefix {
|
||||
cb(prefix, &tree, is_glob, None)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_path(
|
||||
pub(crate) fn convert_path(
|
||||
db: &dyn DefDatabase,
|
||||
prefix: Option<ModPath>,
|
||||
path: ast::Path,
|
||||
@ -78,7 +25,7 @@ fn convert_path(
|
||||
};
|
||||
|
||||
let segment = path.segment()?;
|
||||
let res = match segment.kind()? {
|
||||
let mut mod_path = match segment.kind()? {
|
||||
ast::PathSegmentKind::Name(name_ref) => {
|
||||
match hygiene.name_ref_to_name(db.upcast(), name_ref) {
|
||||
Either::Left(name) => {
|
||||
@ -125,5 +72,18 @@ fn convert_path(
|
||||
return None;
|
||||
}
|
||||
};
|
||||
Some(res)
|
||||
|
||||
// handle local_inner_macros :
|
||||
// Basically, even in rustc it is quite hacky:
|
||||
// https://github.com/rust-lang/rust/blob/614f273e9388ddd7804d5cbc80b8865068a3744e/src/librustc_resolve/macros.rs#L456
|
||||
// We follow what it did anyway :)
|
||||
if mod_path.segments.len() == 1 && mod_path.kind == PathKind::Plain {
|
||||
if let Some(_macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) {
|
||||
if let Some(crate_id) = hygiene.local_inner_macros(db.upcast(), path) {
|
||||
mod_path.kind = PathKind::DollarCrate(crate_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some(mod_path)
|
||||
}
|
||||
|
@ -278,9 +278,11 @@ pub(crate) fn diagnostics(&self, cb: &mut dyn FnMut(FileRange, String)) {
|
||||
let node = ast.to_node(self.upcast());
|
||||
(InFile::new(ast.file_id, node.syntax().clone()), "UnresolvedExternCrate")
|
||||
}
|
||||
DefDiagnosticKind::UnresolvedImport { ast, .. } => {
|
||||
let node = ast.to_node(self.upcast());
|
||||
(InFile::new(ast.file_id, node.syntax().clone()), "UnresolvedImport")
|
||||
DefDiagnosticKind::UnresolvedImport { id, .. } => {
|
||||
let item_tree = id.item_tree(self.upcast());
|
||||
let import = &item_tree[id.value];
|
||||
let node = InFile::new(id.file_id(), import.ast_id).to_node(self.upcast());
|
||||
(InFile::new(id.file_id(), node.syntax().clone()), "UnresolvedImport")
|
||||
}
|
||||
DefDiagnosticKind::UnconfiguredCode { ast, .. } => {
|
||||
let node = ast.to_node(self.upcast());
|
||||
|
@ -311,6 +311,7 @@ mod tests {
|
||||
/// * a diagnostic is produced
|
||||
/// * the first diagnostic fix trigger range touches the input cursor position
|
||||
/// * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied
|
||||
#[track_caller]
|
||||
pub(crate) fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) {
|
||||
check_nth_fix(0, ra_fixture_before, ra_fixture_after);
|
||||
}
|
||||
@ -325,6 +326,7 @@ pub(crate) fn check_fixes(ra_fixture_before: &str, ra_fixtures_after: Vec<&str>)
|
||||
}
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn check_nth_fix(nth: usize, ra_fixture_before: &str, ra_fixture_after: &str) {
|
||||
let after = trim_indent(ra_fixture_after);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user