//! This module implements import-resolution/macro expansion algorithm.
//!
-//! The result of this module is `CrateDefMap`: a data structure which contains:
+//! The result of this module is `DefMap`: a data structure which contains:
//!
//! * a tree of modules for the crate
//! * for each module, a set of items visible in the module (directly declared
//! or imported)
//!
-//! Note that `CrateDefMap` contains fully macro expanded code.
+//! Note that `DefMap` contains fully macro expanded code.
//!
-//! Computing `CrateDefMap` can be partitioned into several logically
+//! Computing `DefMap` can be partitioned into several logically
//! independent "phases". The phases are mutually recursive though, there's no
//! strict ordering.
//!
//! path and, upon success, we run macro expansion and "collect module" phase on
//! the result
+pub mod attr_resolution;
+pub mod diagnostics;
mod collector;
mod mod_resolution;
mod path_resolution;
+mod proc_macro;
#[cfg(test)]
mod tests;
-mod proc_macro;
use std::sync::Arc;
use base_db::{CrateId, Edition, FileId};
-use hir_expand::{diagnostics::DiagnosticSink, name::Name, InFile, MacroDefId};
+use hir_expand::{name::Name, InFile, MacroDefId};
use la_arena::Arena;
use profile::Count;
use rustc_hash::FxHashMap;
use stdx::format_to;
-use syntax::ast;
+use syntax::{ast, SmolStr};
use crate::{
db::DefDatabase,
item_scope::{BuiltinShadowMode, ItemScope},
+ item_tree::TreeId,
nameres::{diagnostics::DefDiagnostic, path_resolution::ResolveMode},
path::ModPath,
per_ns::PerNs,
+ visibility::Visibility,
AstId, BlockId, BlockLoc, LocalModuleId, ModuleDefId, ModuleId,
};
/// Side table with additional proc. macro info, for use by name resolution in downstream
/// crates.
///
- /// (the primary purpose is to resolve derive helpers)
+ /// (the primary purpose is to resolve derive helpers and fetch a proc-macros name)
exported_proc_macros: FxHashMap<MacroDefId, ProcMacroDef>,
+ /// Custom attributes registered with `#![register_attr]`.
+ registered_attrs: Vec<SmolStr>,
+ /// Custom tool modules registered with `#![register_tool]`.
+ registered_tools: Vec<SmolStr>,
+
edition: Edition,
+ recursion_limit: Option<u32>,
diagnostics: Vec<DefDiagnostic>,
}
},
}
-impl Default for ModuleOrigin {
- fn default() -> Self {
- ModuleOrigin::CrateRoot { definition: FileId(0) }
- }
-}
-
impl ModuleOrigin {
- fn declaration(&self) -> Option<AstId<ast::Module>> {
+ pub fn declaration(&self) -> Option<AstId<ast::Module>> {
match self {
ModuleOrigin::File { declaration: module, .. }
| ModuleOrigin::Inline { definition: module, .. } => Some(*module),
}
}
-#[derive(Default, Debug, PartialEq, Eq)]
+#[derive(Debug, PartialEq, Eq)]
pub struct ModuleData {
+ /// Where does this module come from?
+ pub origin: ModuleOrigin,
+ /// Declared visibility of this module.
+ pub visibility: Visibility,
+
pub parent: Option<LocalModuleId>,
pub children: FxHashMap<Name, LocalModuleId>,
pub scope: ItemScope,
-
- /// Where does this module come from?
- pub origin: ModuleOrigin,
}
impl DefMap {
let _p = profile::span("crate_def_map_query").detail(|| {
db.crate_graph()[krate].display_name.as_deref().unwrap_or_default().to_string()
});
- let edition = db.crate_graph()[krate].edition;
- let def_map = DefMap::empty(krate, edition);
- let def_map = collector::collect_defs(db, def_map, None);
+
+ let crate_graph = db.crate_graph();
+
+ let edition = crate_graph[krate].edition;
+ let origin = ModuleOrigin::CrateRoot { definition: crate_graph[krate].root_file_id };
+ let def_map = DefMap::empty(krate, edition, origin);
+ let def_map = collector::collect_defs(
+ db,
+ def_map,
+ TreeId::new(crate_graph[krate].root_file_id.into(), None),
+ );
+
Arc::new(def_map)
}
) -> Option<Arc<DefMap>> {
let block: BlockLoc = db.lookup_intern_block(block_id);
- let item_tree = db.file_item_tree(block.ast_id.file_id);
- if item_tree.inner_items_of_block(block.ast_id.value).is_empty() {
+ let tree_id = TreeId::new(block.ast_id.file_id, Some(block_id));
+ let item_tree = tree_id.item_tree(db);
+ if item_tree.top_level_items().is_empty() {
return None;
}
let block_info = BlockInfo { block: block_id, parent: block.module };
let parent_map = block.module.def_map(db);
- let mut def_map = DefMap::empty(block.module.krate, parent_map.edition);
+ let mut def_map = DefMap::empty(
+ block.module.krate,
+ parent_map.edition,
+ ModuleOrigin::BlockExpr { block: block.ast_id },
+ );
def_map.block = Some(block_info);
- let def_map = collector::collect_defs(db, def_map, Some(block.ast_id));
+ let def_map = collector::collect_defs(db, def_map, tree_id);
Some(Arc::new(def_map))
}
- fn empty(krate: CrateId, edition: Edition) -> DefMap {
+ fn empty(krate: CrateId, edition: Edition, root_module_origin: ModuleOrigin) -> DefMap {
let mut modules: Arena<ModuleData> = Arena::default();
- let root = modules.alloc(ModuleData::default());
+
+ let local_id = LocalModuleId::from_raw(la_arena::RawIdx::from(0));
+ // NB: we use `None` as block here, which would be wrong for implicit
+ // modules declared by blocks with items. At the moment, we don't use
+ // this visibility for anything outside IDE, so that's probably OK.
+ let visibility = Visibility::Module(ModuleId { krate, local_id, block: None });
+ let root = modules.alloc(ModuleData::new(root_module_origin, visibility));
+ assert_eq!(local_id, root);
+
DefMap {
_c: Count::new(),
block: None,
krate,
edition,
+ recursion_limit: None,
extern_prelude: FxHashMap::default(),
exported_proc_macros: FxHashMap::default(),
prelude: None,
root,
modules,
+ registered_attrs: Vec::new(),
+ registered_tools: Vec::new(),
diagnostics: Vec::new(),
}
}
- pub fn add_diagnostics(
- &self,
- db: &dyn DefDatabase,
- module: LocalModuleId,
- sink: &mut DiagnosticSink,
- ) {
- self.diagnostics.iter().for_each(|it| it.add_to(db, module, sink))
- }
-
pub fn modules_for_file(&self, file_id: FileId) -> impl Iterator<Item = LocalModuleId> + '_ {
self.modules
.iter()
pub fn modules(&self) -> impl Iterator<Item = (LocalModuleId, &ModuleData)> + '_ {
self.modules.iter()
}
-
+ pub fn exported_proc_macros(&self) -> impl Iterator<Item = (MacroDefId, Name)> + '_ {
+ self.exported_proc_macros.iter().map(|(id, def)| (*id, def.name.clone()))
+ }
+ pub fn registered_tools(&self) -> &[SmolStr] {
+ &self.registered_tools
+ }
+ pub fn registered_attrs(&self) -> &[SmolStr] {
+ &self.registered_attrs
+ }
pub fn root(&self) -> LocalModuleId {
self.root
}
pub fn containing_module(&self, local_mod: LocalModuleId) -> Option<ModuleId> {
match &self[local_mod].parent {
Some(parent) => Some(self.module_id(*parent)),
- None => match &self.block {
- Some(block) => Some(block.parent),
- None => None,
- },
+ None => self.block.as_ref().map(|block| block.parent),
}
}
}
}
+ pub fn dump_block_scopes(&self, db: &dyn DefDatabase) -> String {
+ let mut buf = String::new();
+ let mut arc;
+ let mut current_map = self;
+ while let Some(block) = ¤t_map.block {
+ format_to!(buf, "{:?} in {:?}\n", block.block, block.parent);
+ arc = block.parent.def_map(db);
+ current_map = &*arc;
+ }
+
+ format_to!(buf, "crate scope\n");
+ buf
+ }
+
fn shrink_to_fit(&mut self) {
// Exhaustive match to require handling new fields.
let Self {
extern_prelude,
diagnostics,
modules,
+ registered_attrs,
+ registered_tools,
block: _,
edition: _,
+ recursion_limit: _,
krate: _,
prelude: _,
root: _,
exported_proc_macros.shrink_to_fit();
diagnostics.shrink_to_fit();
modules.shrink_to_fit();
+ registered_attrs.shrink_to_fit();
+ registered_tools.shrink_to_fit();
for (_, module) in modules.iter_mut() {
module.children.shrink_to_fit();
module.scope.shrink_to_fit();
}
}
+
+ /// Get a reference to the def map's diagnostics.
+ pub fn diagnostics(&self) -> &[DefDiagnostic] {
+ self.diagnostics.as_slice()
+ }
+
+ pub fn recursion_limit(&self) -> Option<u32> {
+ self.recursion_limit
+ }
}
impl ModuleData {
+ pub(crate) fn new(origin: ModuleOrigin, visibility: Visibility) -> Self {
+ ModuleData {
+ origin,
+ visibility,
+ parent: None,
+ children: FxHashMap::default(),
+ scope: ItemScope::default(),
+ }
+ }
+
/// Returns a node which defines this module. That is, a file or a `mod foo {}` with items.
pub fn definition_source(&self, db: &dyn DefDatabase) -> InFile<ModuleSource> {
self.origin.definition_source(db)
Module(ast::Module),
BlockExpr(ast::BlockExpr),
}
-
-mod diagnostics {
- use cfg::{CfgExpr, CfgOptions};
- use hir_expand::diagnostics::DiagnosticSink;
- use hir_expand::hygiene::Hygiene;
- use hir_expand::{InFile, MacroCallKind};
- use syntax::ast::AttrsOwner;
- use syntax::{ast, AstNode, AstPtr, SyntaxKind, SyntaxNodePtr};
-
- use crate::path::ModPath;
- use crate::{db::DefDatabase, diagnostics::*, nameres::LocalModuleId, AstId};
-
- #[derive(Debug, PartialEq, Eq)]
- enum DiagnosticKind {
- UnresolvedModule { declaration: AstId<ast::Module>, candidate: String },
-
- UnresolvedExternCrate { ast: AstId<ast::ExternCrate> },
-
- UnresolvedImport { ast: AstId<ast::Use>, index: usize },
-
- UnconfiguredCode { ast: AstId<ast::Item>, cfg: CfgExpr, opts: CfgOptions },
-
- UnresolvedProcMacro { ast: MacroCallKind },
-
- UnresolvedMacroCall { ast: AstId<ast::MacroCall>, path: ModPath },
-
- MacroError { ast: MacroCallKind, message: String },
- }
-
- #[derive(Debug, PartialEq, Eq)]
- pub(super) struct DefDiagnostic {
- in_module: LocalModuleId,
- kind: DiagnosticKind,
- }
-
- impl DefDiagnostic {
- pub(super) fn unresolved_module(
- container: LocalModuleId,
- declaration: AstId<ast::Module>,
- candidate: String,
- ) -> Self {
- Self {
- in_module: container,
- kind: DiagnosticKind::UnresolvedModule { declaration, candidate },
- }
- }
-
- pub(super) fn unresolved_extern_crate(
- container: LocalModuleId,
- declaration: AstId<ast::ExternCrate>,
- ) -> Self {
- Self {
- in_module: container,
- kind: DiagnosticKind::UnresolvedExternCrate { ast: declaration },
- }
- }
-
- pub(super) fn unresolved_import(
- container: LocalModuleId,
- ast: AstId<ast::Use>,
- index: usize,
- ) -> Self {
- Self { in_module: container, kind: DiagnosticKind::UnresolvedImport { ast, index } }
- }
-
- pub(super) fn unconfigured_code(
- container: LocalModuleId,
- ast: AstId<ast::Item>,
- cfg: CfgExpr,
- opts: CfgOptions,
- ) -> Self {
- Self { in_module: container, kind: DiagnosticKind::UnconfiguredCode { ast, cfg, opts } }
- }
-
- pub(super) fn unresolved_proc_macro(container: LocalModuleId, ast: MacroCallKind) -> Self {
- Self { in_module: container, kind: DiagnosticKind::UnresolvedProcMacro { ast } }
- }
-
- pub(super) fn macro_error(
- container: LocalModuleId,
- ast: MacroCallKind,
- message: String,
- ) -> Self {
- Self { in_module: container, kind: DiagnosticKind::MacroError { ast, message } }
- }
-
- pub(super) fn unresolved_macro_call(
- container: LocalModuleId,
- ast: AstId<ast::MacroCall>,
- path: ModPath,
- ) -> Self {
- Self { in_module: container, kind: DiagnosticKind::UnresolvedMacroCall { ast, path } }
- }
-
- pub(super) fn add_to(
- &self,
- db: &dyn DefDatabase,
- target_module: LocalModuleId,
- sink: &mut DiagnosticSink,
- ) {
- if self.in_module != target_module {
- return;
- }
-
- match &self.kind {
- DiagnosticKind::UnresolvedModule { declaration, candidate } => {
- let decl = declaration.to_node(db.upcast());
- sink.push(UnresolvedModule {
- file: declaration.file_id,
- decl: AstPtr::new(&decl),
- candidate: candidate.clone(),
- })
- }
-
- DiagnosticKind::UnresolvedExternCrate { ast } => {
- let item = ast.to_node(db.upcast());
- sink.push(UnresolvedExternCrate {
- file: ast.file_id,
- item: AstPtr::new(&item),
- });
- }
-
- DiagnosticKind::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(
- InFile::new(ast.file_id, use_item),
- &hygiene,
- |_mod_path, use_tree, _is_glob, _alias| {
- if cur == *index {
- tree = Some(use_tree.clone());
- }
-
- cur += 1;
- },
- );
-
- if let Some(tree) = tree {
- sink.push(UnresolvedImport { file: ast.file_id, node: AstPtr::new(&tree) });
- }
- }
-
- DiagnosticKind::UnconfiguredCode { ast, cfg, opts } => {
- let item = ast.to_node(db.upcast());
- sink.push(InactiveCode {
- file: ast.file_id,
- node: AstPtr::new(&item).into(),
- cfg: cfg.clone(),
- opts: opts.clone(),
- });
- }
-
- DiagnosticKind::UnresolvedProcMacro { ast } => {
- let mut precise_location = None;
- let (file, ast, name) = match ast {
- MacroCallKind::FnLike { ast_id } => {
- let node = ast_id.to_node(db.upcast());
- (ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node)), None)
- }
- MacroCallKind::Derive { ast_id, derive_name, .. } => {
- let node = ast_id.to_node(db.upcast());
-
- // Compute the precise location of the macro name's token in the derive
- // list.
- // FIXME: This does not handle paths to the macro, but neither does the
- // rest of r-a.
- let derive_attrs =
- node.attrs().filter_map(|attr| match attr.as_simple_call() {
- Some((name, args)) if name == "derive" => Some(args),
- _ => None,
- });
- 'outer: for attr in derive_attrs {
- let tokens =
- attr.syntax().children_with_tokens().filter_map(|elem| {
- match elem {
- syntax::NodeOrToken::Node(_) => None,
- syntax::NodeOrToken::Token(tok) => Some(tok),
- }
- });
- for token in tokens {
- if token.kind() == SyntaxKind::IDENT
- && token.text() == derive_name.as_str()
- {
- precise_location = Some(token.text_range());
- break 'outer;
- }
- }
- }
-
- (
- ast_id.file_id,
- SyntaxNodePtr::from(AstPtr::new(&node)),
- Some(derive_name.clone()),
- )
- }
- };
- sink.push(UnresolvedProcMacro {
- file,
- node: ast,
- precise_location,
- macro_name: name,
- });
- }
-
- DiagnosticKind::UnresolvedMacroCall { ast, path } => {
- let node = ast.to_node(db.upcast());
- sink.push(UnresolvedMacroCall {
- file: ast.file_id,
- node: AstPtr::new(&node),
- path: path.clone(),
- });
- }
-
- DiagnosticKind::MacroError { ast, message } => {
- let (file, ast) = match ast {
- MacroCallKind::FnLike { ast_id, .. } => {
- let node = ast_id.to_node(db.upcast());
- (ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node)))
- }
- MacroCallKind::Derive { ast_id, .. } => {
- let node = ast_id.to_node(db.upcast());
- (ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node)))
- }
- };
- sink.push(MacroError { file, node: ast, message: message.clone() });
- }
- }
- }
- }
-}