mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-28 12:54:58 +00:00
Reorganize name resolution
This commit is contained in:
parent
d4449945a0
commit
967a4b64af
14 changed files with 485 additions and 526 deletions
|
@ -8,8 +8,7 @@ use crate::{
|
||||||
Name, ScopesWithSourceMap, Ty, HirFileId,
|
Name, ScopesWithSourceMap, Ty, HirFileId,
|
||||||
HirDatabase, PersistentHirDatabase,
|
HirDatabase, PersistentHirDatabase,
|
||||||
type_ref::TypeRef,
|
type_ref::TypeRef,
|
||||||
nameres::{ModuleScope, Namespace, lower::ImportId},
|
nameres::{ModuleScope, Namespace, ImportId, ModuleId},
|
||||||
nameres::crate_def_map::ModuleId,
|
|
||||||
expr::{Body, BodySourceMap},
|
expr::{Body, BodySourceMap},
|
||||||
ty::InferenceResult,
|
ty::InferenceResult,
|
||||||
adt::{EnumVariantId, StructFieldId, VariantDef},
|
adt::{EnumVariantId, StructFieldId, VariantDef},
|
||||||
|
|
|
@ -3,8 +3,7 @@ use ra_syntax::{ast, SyntaxNode, TreeArc, AstNode};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Module, ModuleSource, Problem, Name,
|
Module, ModuleSource, Problem, Name,
|
||||||
nameres::crate_def_map::ModuleId,
|
nameres::{ModuleId, ImportId},
|
||||||
nameres::lower::ImportId,
|
|
||||||
HirDatabase, PersistentHirDatabase,
|
HirDatabase, PersistentHirDatabase,
|
||||||
HirFileId, SourceItemId,
|
HirFileId, SourceItemId,
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,7 +10,7 @@ use crate::{
|
||||||
Struct, Enum, StructField,
|
Struct, Enum, StructField,
|
||||||
Const, ConstSignature, Static,
|
Const, ConstSignature, Static,
|
||||||
macros::MacroExpansion,
|
macros::MacroExpansion,
|
||||||
nameres::{Namespace, lower::{ImportSourceMap}, crate_def_map::{RawItems, CrateDefMap}},
|
nameres::{Namespace, ImportSourceMap, RawItems, CrateDefMap},
|
||||||
ty::{InferenceResult, Ty, method_resolution::CrateImplBlocks, TypableDef, CallableDef, FnSig},
|
ty::{InferenceResult, Ty, method_resolution::CrateImplBlocks, TypableDef, CallableDef, FnSig},
|
||||||
adt::{StructData, EnumData},
|
adt::{StructData, EnumData},
|
||||||
impl_block::{ModuleImplBlocks, ImplSourceMap},
|
impl_block::{ModuleImplBlocks, ImplSourceMap},
|
||||||
|
|
|
@ -1,35 +1,128 @@
|
||||||
//! Name resolution algorithm. The end result of the algorithm is an `ItemMap`:
|
/// This module implements import-resolution/macro expansion algorithm.
|
||||||
//! a map which maps each module to its scope: the set of items visible in the
|
///
|
||||||
//! module. That is, we only resolve imports here, name resolution of item
|
/// The result of this module is `CrateDefMap`: a datastructure which contains:
|
||||||
//! bodies will be done in a separate step.
|
///
|
||||||
//!
|
/// * a tree of modules for the crate
|
||||||
//! Like Rustc, we use an interactive per-crate algorithm: we start with scopes
|
/// * for each module, a set of items visible in the module (directly declared
|
||||||
//! containing only directly defined items, and then iteratively resolve
|
/// or imported)
|
||||||
//! imports.
|
///
|
||||||
//!
|
/// Note that `CrateDefMap` contains fully macro expanded code.
|
||||||
//! To make this work nicely in the IDE scenario, we place `InputModuleItems`
|
///
|
||||||
//! in between raw syntax and name resolution. `InputModuleItems` are computed
|
/// Computing `CrateDefMap` can be partitioned into several logically
|
||||||
//! using only the module's syntax, and it is all directly defined items plus
|
/// independent "phases". The phases are mutually recursive though, there's no
|
||||||
//! imports. The plan is to make `InputModuleItems` independent of local
|
/// stric ordering.
|
||||||
//! modifications (that is, typing inside a function should not change IMIs),
|
///
|
||||||
//! so that the results of name resolution can be preserved unless the module
|
/// ## Collecting RawItems
|
||||||
//! structure itself is modified.
|
///
|
||||||
pub(crate) mod lower;
|
/// This happens in the `raw` module, which parses a single source file into a
|
||||||
pub(crate) mod crate_def_map;
|
/// set of top-level items. Nested importa are desugared to flat imports in
|
||||||
|
/// this phase. Macro calls are represented as a triple of (Path, Option<Name>,
|
||||||
|
/// TokenTree).
|
||||||
|
///
|
||||||
|
/// ## Collecting Modules
|
||||||
|
///
|
||||||
|
/// This happens in the `collector` module. In this phase, we recursively walk
|
||||||
|
/// tree of modules, collect raw items from submodules, populate module scopes
|
||||||
|
/// with defined items (so, we assign item ids in this phase) and record the set
|
||||||
|
/// of unresovled imports and macros.
|
||||||
|
///
|
||||||
|
/// While we walk tree of modules, we also record macro_rules defenitions and
|
||||||
|
/// expand calls to macro_rules defined macros.
|
||||||
|
///
|
||||||
|
/// ## Resolving Imports
|
||||||
|
///
|
||||||
|
/// TBD
|
||||||
|
///
|
||||||
|
/// ## Resolving Macros
|
||||||
|
///
|
||||||
|
/// While macro_rules from the same crate use a global mutable namespace, macros
|
||||||
|
/// from other crates (including proc-macros) can be used with `foo::bar!`
|
||||||
|
/// syntax.
|
||||||
|
///
|
||||||
|
/// TBD;
|
||||||
|
|
||||||
|
mod per_ns;
|
||||||
|
mod raw;
|
||||||
|
mod collector;
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use ra_db::Edition;
|
use ra_arena::{Arena, RawId, impl_arena_id};
|
||||||
|
use ra_db::{FileId, Edition};
|
||||||
|
use test_utils::tested_by;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ModuleDef, Name,
|
ModuleDef, Name, Crate, Module, Problem,
|
||||||
nameres::lower::ImportId,
|
PersistentHirDatabase, Path, PathKind, HirFileId,
|
||||||
|
ids::{SourceItemId, SourceFileItemId},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) use self::crate_def_map::{CrateDefMap, ModuleId};
|
pub(crate) use self::raw::{RawItems, ImportId, ImportSourceMap};
|
||||||
|
|
||||||
|
pub use self::per_ns::{PerNs, Namespace};
|
||||||
|
|
||||||
|
/// Contans all top-level defs from a macro-expanded crate
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub struct CrateDefMap {
|
||||||
|
krate: Crate,
|
||||||
|
edition: Edition,
|
||||||
|
/// The prelude module for this crate. This either comes from an import
|
||||||
|
/// marked with the `prelude_import` attribute, or (in the normal case) from
|
||||||
|
/// a dependency (`std` or `core`).
|
||||||
|
prelude: Option<Module>,
|
||||||
|
extern_prelude: FxHashMap<Name, ModuleDef>,
|
||||||
|
root: ModuleId,
|
||||||
|
modules: Arena<ModuleId, ModuleData>,
|
||||||
|
public_macros: FxHashMap<Name, mbe::MacroRules>,
|
||||||
|
problems: CrateDefMapProblems,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Index<ModuleId> for CrateDefMap {
|
||||||
|
type Output = ModuleData;
|
||||||
|
fn index(&self, id: ModuleId) -> &ModuleData {
|
||||||
|
&self.modules[id]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An ID of a module, **local** to a specific crate
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub(crate) struct ModuleId(RawId);
|
||||||
|
impl_arena_id!(ModuleId);
|
||||||
|
|
||||||
|
#[derive(Default, Debug, PartialEq, Eq)]
|
||||||
|
pub(crate) struct ModuleData {
|
||||||
|
pub(crate) parent: Option<ModuleId>,
|
||||||
|
pub(crate) children: FxHashMap<Name, ModuleId>,
|
||||||
|
pub(crate) scope: ModuleScope,
|
||||||
|
/// None for root
|
||||||
|
pub(crate) declaration: Option<SourceItemId>,
|
||||||
|
/// None for inline modules.
|
||||||
|
///
|
||||||
|
/// Note that non-inline modules, by definition, live inside non-macro file.
|
||||||
|
pub(crate) definition: Option<FileId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, PartialEq, Eq)]
|
||||||
|
pub(crate) struct CrateDefMapProblems {
|
||||||
|
problems: Vec<(SourceItemId, Problem)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CrateDefMapProblems {
|
||||||
|
fn add(&mut self, source_item_id: SourceItemId, problem: Problem) {
|
||||||
|
self.problems.push((source_item_id, problem))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn iter<'a>(&'a self) -> impl Iterator<Item = (&'a SourceItemId, &'a Problem)> + 'a {
|
||||||
|
self.problems.iter().map(|(s, p)| (s, p))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, PartialEq, Eq, Clone)]
|
#[derive(Debug, Default, PartialEq, Eq, Clone)]
|
||||||
pub struct ModuleScope {
|
pub struct ModuleScope {
|
||||||
pub(crate) items: FxHashMap<Name, Resolution>,
|
items: FxHashMap<Name, Resolution>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ModuleScope {
|
impl ModuleScope {
|
||||||
|
@ -41,8 +134,6 @@ impl ModuleScope {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `Resolution` is basically `DefId` atm, but it should account for stuff like
|
|
||||||
/// multiple namespaces, ambiguity and errors.
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
||||||
pub struct Resolution {
|
pub struct Resolution {
|
||||||
/// None for unresolved
|
/// None for unresolved
|
||||||
|
@ -51,85 +142,6 @@ pub struct Resolution {
|
||||||
pub import: Option<ImportId>,
|
pub import: Option<ImportId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
||||||
pub enum Namespace {
|
|
||||||
Types,
|
|
||||||
Values,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
|
||||||
pub struct PerNs<T> {
|
|
||||||
pub types: Option<T>,
|
|
||||||
pub values: Option<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Default for PerNs<T> {
|
|
||||||
fn default() -> Self {
|
|
||||||
PerNs { types: None, values: None }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> PerNs<T> {
|
|
||||||
pub fn none() -> PerNs<T> {
|
|
||||||
PerNs { types: None, values: None }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn values(t: T) -> PerNs<T> {
|
|
||||||
PerNs { types: None, values: Some(t) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn types(t: T) -> PerNs<T> {
|
|
||||||
PerNs { types: Some(t), values: None }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn both(types: T, values: T) -> PerNs<T> {
|
|
||||||
PerNs { types: Some(types), values: Some(values) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_none(&self) -> bool {
|
|
||||||
self.types.is_none() && self.values.is_none()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_both(&self) -> bool {
|
|
||||||
self.types.is_some() && self.values.is_some()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn take(self, namespace: Namespace) -> Option<T> {
|
|
||||||
match namespace {
|
|
||||||
Namespace::Types => self.types,
|
|
||||||
Namespace::Values => self.values,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn take_types(self) -> Option<T> {
|
|
||||||
self.take(Namespace::Types)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn take_values(self) -> Option<T> {
|
|
||||||
self.take(Namespace::Values)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get(&self, namespace: Namespace) -> Option<&T> {
|
|
||||||
self.as_ref().take(namespace)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_ref(&self) -> PerNs<&T> {
|
|
||||||
PerNs { types: self.types.as_ref(), values: self.values.as_ref() }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn or(self, other: PerNs<T>) -> PerNs<T> {
|
|
||||||
PerNs { types: self.types.or(other.types), values: self.values.or(other.values) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn and_then<U>(self, f: impl Fn(T) -> Option<U>) -> PerNs<U> {
|
|
||||||
PerNs { types: self.types.and_then(&f), values: self.values.and_then(&f) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn map<U>(self, f: impl Fn(T) -> U) -> PerNs<U> {
|
|
||||||
PerNs { types: self.types.map(&f), values: self.values.map(&f) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct ResolvePathResult {
|
struct ResolvePathResult {
|
||||||
resolved_def: PerNs<ModuleDef>,
|
resolved_def: PerNs<ModuleDef>,
|
||||||
|
@ -162,3 +174,253 @@ enum ReachedFixedPoint {
|
||||||
Yes,
|
Yes,
|
||||||
No,
|
No,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl CrateDefMap {
|
||||||
|
pub(crate) fn crate_def_map_query(
|
||||||
|
db: &impl PersistentHirDatabase,
|
||||||
|
krate: Crate,
|
||||||
|
) -> Arc<CrateDefMap> {
|
||||||
|
let def_map = {
|
||||||
|
let edition = krate.edition(db);
|
||||||
|
let mut modules: Arena<ModuleId, ModuleData> = Arena::default();
|
||||||
|
let root = modules.alloc(ModuleData::default());
|
||||||
|
CrateDefMap {
|
||||||
|
krate,
|
||||||
|
edition,
|
||||||
|
extern_prelude: FxHashMap::default(),
|
||||||
|
prelude: None,
|
||||||
|
root,
|
||||||
|
modules,
|
||||||
|
public_macros: FxHashMap::default(),
|
||||||
|
problems: CrateDefMapProblems::default(),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let def_map = collector::collect_defs(db, def_map);
|
||||||
|
Arc::new(def_map)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn root(&self) -> ModuleId {
|
||||||
|
self.root
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn problems(&self) -> &CrateDefMapProblems {
|
||||||
|
&self.problems
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn mk_module(&self, module_id: ModuleId) -> Module {
|
||||||
|
Module { krate: self.krate, module_id }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn prelude(&self) -> Option<Module> {
|
||||||
|
self.prelude
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn extern_prelude(&self) -> &FxHashMap<Name, ModuleDef> {
|
||||||
|
&self.extern_prelude
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn find_module_by_source(
|
||||||
|
&self,
|
||||||
|
file_id: HirFileId,
|
||||||
|
decl_id: Option<SourceFileItemId>,
|
||||||
|
) -> Option<ModuleId> {
|
||||||
|
let decl_id = decl_id.map(|it| it.with_file_id(file_id));
|
||||||
|
let (module_id, _module_data) = self.modules.iter().find(|(_module_id, module_data)| {
|
||||||
|
if decl_id.is_some() {
|
||||||
|
module_data.declaration == decl_id
|
||||||
|
} else {
|
||||||
|
module_data.definition.map(|it| it.into()) == Some(file_id)
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
Some(module_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn resolve_path(
|
||||||
|
&self,
|
||||||
|
db: &impl PersistentHirDatabase,
|
||||||
|
original_module: ModuleId,
|
||||||
|
path: &Path,
|
||||||
|
) -> (PerNs<ModuleDef>, Option<usize>) {
|
||||||
|
let res = self.resolve_path_fp(db, ResolveMode::Other, original_module, path);
|
||||||
|
(res.resolved_def, res.segment_index)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns Yes if we are sure that additions to `ItemMap` wouldn't change
|
||||||
|
// the result.
|
||||||
|
fn resolve_path_fp(
|
||||||
|
&self,
|
||||||
|
db: &impl PersistentHirDatabase,
|
||||||
|
mode: ResolveMode,
|
||||||
|
original_module: ModuleId,
|
||||||
|
path: &Path,
|
||||||
|
) -> ResolvePathResult {
|
||||||
|
let mut segments = path.segments.iter().enumerate();
|
||||||
|
let mut curr_per_ns: PerNs<ModuleDef> = match path.kind {
|
||||||
|
PathKind::Crate => {
|
||||||
|
PerNs::types(Module { krate: self.krate, module_id: self.root }.into())
|
||||||
|
}
|
||||||
|
PathKind::Self_ => {
|
||||||
|
PerNs::types(Module { krate: self.krate, module_id: original_module }.into())
|
||||||
|
}
|
||||||
|
// plain import or absolute path in 2015: crate-relative with
|
||||||
|
// fallback to extern prelude (with the simplification in
|
||||||
|
// rust-lang/rust#57745)
|
||||||
|
// TODO there must be a nicer way to write this condition
|
||||||
|
PathKind::Plain | PathKind::Abs
|
||||||
|
if self.edition == Edition::Edition2015
|
||||||
|
&& (path.kind == PathKind::Abs || mode == ResolveMode::Import) =>
|
||||||
|
{
|
||||||
|
let segment = match segments.next() {
|
||||||
|
Some((_, segment)) => segment,
|
||||||
|
None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
|
||||||
|
};
|
||||||
|
log::debug!("resolving {:?} in crate root (+ extern prelude)", segment);
|
||||||
|
self.resolve_name_in_crate_root_or_extern_prelude(&segment.name)
|
||||||
|
}
|
||||||
|
PathKind::Plain => {
|
||||||
|
let segment = match segments.next() {
|
||||||
|
Some((_, segment)) => segment,
|
||||||
|
None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
|
||||||
|
};
|
||||||
|
log::debug!("resolving {:?} in module", segment);
|
||||||
|
self.resolve_name_in_module(db, original_module, &segment.name)
|
||||||
|
}
|
||||||
|
PathKind::Super => {
|
||||||
|
if let Some(p) = self.modules[original_module].parent {
|
||||||
|
PerNs::types(Module { krate: self.krate, module_id: p }.into())
|
||||||
|
} else {
|
||||||
|
log::debug!("super path in root module");
|
||||||
|
return ResolvePathResult::empty(ReachedFixedPoint::Yes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PathKind::Abs => {
|
||||||
|
// 2018-style absolute path -- only extern prelude
|
||||||
|
let segment = match segments.next() {
|
||||||
|
Some((_, segment)) => segment,
|
||||||
|
None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
|
||||||
|
};
|
||||||
|
if let Some(def) = self.extern_prelude.get(&segment.name) {
|
||||||
|
log::debug!("absolute path {:?} resolved to crate {:?}", path, def);
|
||||||
|
PerNs::types(*def)
|
||||||
|
} else {
|
||||||
|
return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (i, segment) in segments {
|
||||||
|
let curr = match curr_per_ns.as_ref().take_types() {
|
||||||
|
Some(r) => r,
|
||||||
|
None => {
|
||||||
|
// we still have path segments left, but the path so far
|
||||||
|
// didn't resolve in the types namespace => no resolution
|
||||||
|
// (don't break here because `curr_per_ns` might contain
|
||||||
|
// something in the value namespace, and it would be wrong
|
||||||
|
// to return that)
|
||||||
|
return ResolvePathResult::empty(ReachedFixedPoint::No);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// resolve segment in curr
|
||||||
|
|
||||||
|
curr_per_ns = match curr {
|
||||||
|
ModuleDef::Module(module) => {
|
||||||
|
if module.krate != self.krate {
|
||||||
|
let path = Path {
|
||||||
|
segments: path.segments[i..].iter().cloned().collect(),
|
||||||
|
kind: PathKind::Self_,
|
||||||
|
};
|
||||||
|
log::debug!("resolving {:?} in other crate", path);
|
||||||
|
let defp_map = db.crate_def_map(module.krate);
|
||||||
|
let (def, s) = defp_map.resolve_path(db, module.module_id, &path);
|
||||||
|
return ResolvePathResult::with(
|
||||||
|
def,
|
||||||
|
ReachedFixedPoint::Yes,
|
||||||
|
s.map(|s| s + i),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
match self[module.module_id].scope.items.get(&segment.name) {
|
||||||
|
Some(res) if !res.def.is_none() => res.def,
|
||||||
|
_ => {
|
||||||
|
log::debug!("path segment {:?} not found", segment.name);
|
||||||
|
return ResolvePathResult::empty(ReachedFixedPoint::No);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ModuleDef::Enum(e) => {
|
||||||
|
// enum variant
|
||||||
|
tested_by!(can_import_enum_variant);
|
||||||
|
match e.variant(db, &segment.name) {
|
||||||
|
Some(variant) => PerNs::both(variant.into(), variant.into()),
|
||||||
|
None => {
|
||||||
|
return ResolvePathResult::with(
|
||||||
|
PerNs::types((*e).into()),
|
||||||
|
ReachedFixedPoint::Yes,
|
||||||
|
Some(i),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s => {
|
||||||
|
// could be an inherent method call in UFCS form
|
||||||
|
// (`Struct::method`), or some other kind of associated item
|
||||||
|
log::debug!(
|
||||||
|
"path segment {:?} resolved to non-module {:?}, but is not last",
|
||||||
|
segment.name,
|
||||||
|
curr,
|
||||||
|
);
|
||||||
|
|
||||||
|
return ResolvePathResult::with(
|
||||||
|
PerNs::types((*s).into()),
|
||||||
|
ReachedFixedPoint::Yes,
|
||||||
|
Some(i),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
ResolvePathResult::with(curr_per_ns, ReachedFixedPoint::Yes, None)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_name_in_crate_root_or_extern_prelude(&self, name: &Name) -> PerNs<ModuleDef> {
|
||||||
|
let from_crate_root =
|
||||||
|
self[self.root].scope.items.get(name).map_or(PerNs::none(), |it| it.def);
|
||||||
|
let from_extern_prelude = self.resolve_name_in_extern_prelude(name);
|
||||||
|
|
||||||
|
from_crate_root.or(from_extern_prelude)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn resolve_name_in_module(
|
||||||
|
&self,
|
||||||
|
db: &impl PersistentHirDatabase,
|
||||||
|
module: ModuleId,
|
||||||
|
name: &Name,
|
||||||
|
) -> PerNs<ModuleDef> {
|
||||||
|
// Resolve in:
|
||||||
|
// - current module / scope
|
||||||
|
// - extern prelude
|
||||||
|
// - std prelude
|
||||||
|
let from_scope = self[module].scope.items.get(name).map_or(PerNs::none(), |it| it.def);
|
||||||
|
let from_extern_prelude =
|
||||||
|
self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it));
|
||||||
|
let from_prelude = self.resolve_in_prelude(db, name);
|
||||||
|
|
||||||
|
from_scope.or(from_extern_prelude).or(from_prelude)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs<ModuleDef> {
|
||||||
|
self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_in_prelude(&self, db: &impl PersistentHirDatabase, name: &Name) -> PerNs<ModuleDef> {
|
||||||
|
if let Some(prelude) = self.prelude {
|
||||||
|
let resolution = if prelude.krate == self.krate {
|
||||||
|
self[prelude.module_id].scope.items.get(name).cloned()
|
||||||
|
} else {
|
||||||
|
db.crate_def_map(prelude.krate)[prelude.module_id].scope.items.get(name).cloned()
|
||||||
|
};
|
||||||
|
resolution.map(|r| r.def).unwrap_or_else(PerNs::none)
|
||||||
|
} else {
|
||||||
|
PerNs::none()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -8,11 +8,11 @@ use crate::{
|
||||||
Function, Module, Struct, Enum, Const, Static, Trait, TypeAlias,
|
Function, Module, Struct, Enum, Const, Static, Trait, TypeAlias,
|
||||||
PersistentHirDatabase, HirFileId, Name, Path, Problem,
|
PersistentHirDatabase, HirFileId, Name, Path, Problem,
|
||||||
KnownName,
|
KnownName,
|
||||||
nameres::{Resolution, PerNs, ModuleDef, ReachedFixedPoint, ResolveMode},
|
nameres::{Resolution, PerNs, ModuleDef, ReachedFixedPoint, ResolveMode, raw},
|
||||||
ids::{AstItemDef, LocationCtx, MacroCallLoc, SourceItemId, MacroCallId},
|
ids::{AstItemDef, LocationCtx, MacroCallLoc, SourceItemId, MacroCallId},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{CrateDefMap, ModuleId, ModuleData, raw};
|
use super::{CrateDefMap, ModuleId, ModuleData};
|
||||||
|
|
||||||
pub(super) fn collect_defs(
|
pub(super) fn collect_defs(
|
||||||
db: &impl PersistentHirDatabase,
|
db: &impl PersistentHirDatabase,
|
|
@ -1,368 +0,0 @@
|
||||||
/// This module implements new import-resolution/macro expansion algorithm.
|
|
||||||
///
|
|
||||||
/// The result of this module is `CrateDefMap`: a datastructure 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.
|
|
||||||
///
|
|
||||||
/// Computing `CrateDefMap` can be partitioned into several logically
|
|
||||||
/// independent "phases". The phases are mutually recursive though, there's no
|
|
||||||
/// stric ordering.
|
|
||||||
///
|
|
||||||
/// ## Collecting RawItems
|
|
||||||
///
|
|
||||||
/// This happens in the `raw` module, which parses a single source file into a
|
|
||||||
/// set of top-level items. Nested importa are desugared to flat imports in
|
|
||||||
/// this phase. Macro calls are represented as a triple of (Path, Option<Name>,
|
|
||||||
/// TokenTree).
|
|
||||||
///
|
|
||||||
/// ## Collecting Modules
|
|
||||||
///
|
|
||||||
/// This happens in the `collector` module. In this phase, we recursively walk
|
|
||||||
/// tree of modules, collect raw items from submodules, populate module scopes
|
|
||||||
/// with defined items (so, we assign item ids in this phase) and record the set
|
|
||||||
/// of unresovled imports and macros.
|
|
||||||
///
|
|
||||||
/// While we walk tree of modules, we also record macro_rules defenitions and
|
|
||||||
/// expand calls to macro_rules defined macros.
|
|
||||||
///
|
|
||||||
/// ## Resolving Imports
|
|
||||||
///
|
|
||||||
/// TBD
|
|
||||||
///
|
|
||||||
/// ## Resolving Macros
|
|
||||||
///
|
|
||||||
/// While macro_rules from the same crate use a global mutable namespace, macros
|
|
||||||
/// from other crates (including proc-macros) can be used with `foo::bar!`
|
|
||||||
/// syntax.
|
|
||||||
///
|
|
||||||
/// TBD;
|
|
||||||
|
|
||||||
mod raw;
|
|
||||||
mod collector;
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests;
|
|
||||||
|
|
||||||
use rustc_hash::FxHashMap;
|
|
||||||
use test_utils::tested_by;
|
|
||||||
use ra_arena::{Arena, RawId, impl_arena_id};
|
|
||||||
use ra_db::FileId;
|
|
||||||
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
Name, Module, Path, PathKind, ModuleDef, Crate, Problem, HirFileId,
|
|
||||||
PersistentHirDatabase,
|
|
||||||
nameres::{ModuleScope, ResolveMode, ResolvePathResult, PerNs, Edition, ReachedFixedPoint},
|
|
||||||
ids::{SourceItemId, SourceFileItemId},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub(crate) use self::raw::RawItems;
|
|
||||||
|
|
||||||
#[derive(Default, Debug, PartialEq, Eq)]
|
|
||||||
pub(crate) struct ModuleData {
|
|
||||||
pub(crate) parent: Option<ModuleId>,
|
|
||||||
pub(crate) children: FxHashMap<Name, ModuleId>,
|
|
||||||
pub(crate) scope: ModuleScope,
|
|
||||||
/// None for root
|
|
||||||
pub(crate) declaration: Option<SourceItemId>,
|
|
||||||
/// None for inline modules.
|
|
||||||
///
|
|
||||||
/// Note that non-inline modules, by definition, live inside non-macro file.
|
|
||||||
pub(crate) definition: Option<FileId>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
||||||
pub(crate) struct ModuleId(RawId);
|
|
||||||
impl_arena_id!(ModuleId);
|
|
||||||
|
|
||||||
/// Contans all top-level defs from a macro-expanded crate
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
|
||||||
pub struct CrateDefMap {
|
|
||||||
krate: Crate,
|
|
||||||
edition: Edition,
|
|
||||||
/// The prelude module for this crate. This either comes from an import
|
|
||||||
/// marked with the `prelude_import` attribute, or (in the normal case) from
|
|
||||||
/// a dependency (`std` or `core`).
|
|
||||||
prelude: Option<Module>,
|
|
||||||
extern_prelude: FxHashMap<Name, ModuleDef>,
|
|
||||||
root: ModuleId,
|
|
||||||
modules: Arena<ModuleId, ModuleData>,
|
|
||||||
public_macros: FxHashMap<Name, mbe::MacroRules>,
|
|
||||||
problems: CrateDefMapProblems,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug, PartialEq, Eq)]
|
|
||||||
pub(crate) struct CrateDefMapProblems {
|
|
||||||
problems: Vec<(SourceItemId, Problem)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CrateDefMapProblems {
|
|
||||||
fn add(&mut self, source_item_id: SourceItemId, problem: Problem) {
|
|
||||||
self.problems.push((source_item_id, problem))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn iter<'a>(&'a self) -> impl Iterator<Item = (&'a SourceItemId, &'a Problem)> + 'a {
|
|
||||||
self.problems.iter().map(|(s, p)| (s, p))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::ops::Index<ModuleId> for CrateDefMap {
|
|
||||||
type Output = ModuleData;
|
|
||||||
fn index(&self, id: ModuleId) -> &ModuleData {
|
|
||||||
&self.modules[id]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CrateDefMap {
|
|
||||||
pub(crate) fn crate_def_map_query(
|
|
||||||
db: &impl PersistentHirDatabase,
|
|
||||||
krate: Crate,
|
|
||||||
) -> Arc<CrateDefMap> {
|
|
||||||
let def_map = {
|
|
||||||
let edition = krate.edition(db);
|
|
||||||
let mut modules: Arena<ModuleId, ModuleData> = Arena::default();
|
|
||||||
let root = modules.alloc(ModuleData::default());
|
|
||||||
CrateDefMap {
|
|
||||||
krate,
|
|
||||||
edition,
|
|
||||||
extern_prelude: FxHashMap::default(),
|
|
||||||
prelude: None,
|
|
||||||
root,
|
|
||||||
modules,
|
|
||||||
public_macros: FxHashMap::default(),
|
|
||||||
problems: CrateDefMapProblems::default(),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let def_map = collector::collect_defs(db, def_map);
|
|
||||||
Arc::new(def_map)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn root(&self) -> ModuleId {
|
|
||||||
self.root
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn problems(&self) -> &CrateDefMapProblems {
|
|
||||||
&self.problems
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn mk_module(&self, module_id: ModuleId) -> Module {
|
|
||||||
Module { krate: self.krate, module_id }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn prelude(&self) -> Option<Module> {
|
|
||||||
self.prelude
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn extern_prelude(&self) -> &FxHashMap<Name, ModuleDef> {
|
|
||||||
&self.extern_prelude
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn find_module_by_source(
|
|
||||||
&self,
|
|
||||||
file_id: HirFileId,
|
|
||||||
decl_id: Option<SourceFileItemId>,
|
|
||||||
) -> Option<ModuleId> {
|
|
||||||
let decl_id = decl_id.map(|it| it.with_file_id(file_id));
|
|
||||||
let (module_id, _module_data) = self.modules.iter().find(|(_module_id, module_data)| {
|
|
||||||
if decl_id.is_some() {
|
|
||||||
module_data.declaration == decl_id
|
|
||||||
} else {
|
|
||||||
module_data.definition.map(|it| it.into()) == Some(file_id)
|
|
||||||
}
|
|
||||||
})?;
|
|
||||||
Some(module_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn resolve_path(
|
|
||||||
&self,
|
|
||||||
db: &impl PersistentHirDatabase,
|
|
||||||
original_module: ModuleId,
|
|
||||||
path: &Path,
|
|
||||||
) -> (PerNs<ModuleDef>, Option<usize>) {
|
|
||||||
let res = self.resolve_path_fp(db, ResolveMode::Other, original_module, path);
|
|
||||||
(res.resolved_def, res.segment_index)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns Yes if we are sure that additions to `ItemMap` wouldn't change
|
|
||||||
// the result.
|
|
||||||
fn resolve_path_fp(
|
|
||||||
&self,
|
|
||||||
db: &impl PersistentHirDatabase,
|
|
||||||
mode: ResolveMode,
|
|
||||||
original_module: ModuleId,
|
|
||||||
path: &Path,
|
|
||||||
) -> ResolvePathResult {
|
|
||||||
let mut segments = path.segments.iter().enumerate();
|
|
||||||
let mut curr_per_ns: PerNs<ModuleDef> = match path.kind {
|
|
||||||
PathKind::Crate => {
|
|
||||||
PerNs::types(Module { krate: self.krate, module_id: self.root }.into())
|
|
||||||
}
|
|
||||||
PathKind::Self_ => {
|
|
||||||
PerNs::types(Module { krate: self.krate, module_id: original_module }.into())
|
|
||||||
}
|
|
||||||
// plain import or absolute path in 2015: crate-relative with
|
|
||||||
// fallback to extern prelude (with the simplification in
|
|
||||||
// rust-lang/rust#57745)
|
|
||||||
// TODO there must be a nicer way to write this condition
|
|
||||||
PathKind::Plain | PathKind::Abs
|
|
||||||
if self.edition == Edition::Edition2015
|
|
||||||
&& (path.kind == PathKind::Abs || mode == ResolveMode::Import) =>
|
|
||||||
{
|
|
||||||
let segment = match segments.next() {
|
|
||||||
Some((_, segment)) => segment,
|
|
||||||
None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
|
|
||||||
};
|
|
||||||
log::debug!("resolving {:?} in crate root (+ extern prelude)", segment);
|
|
||||||
self.resolve_name_in_crate_root_or_extern_prelude(&segment.name)
|
|
||||||
}
|
|
||||||
PathKind::Plain => {
|
|
||||||
let segment = match segments.next() {
|
|
||||||
Some((_, segment)) => segment,
|
|
||||||
None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
|
|
||||||
};
|
|
||||||
log::debug!("resolving {:?} in module", segment);
|
|
||||||
self.resolve_name_in_module(db, original_module, &segment.name)
|
|
||||||
}
|
|
||||||
PathKind::Super => {
|
|
||||||
if let Some(p) = self.modules[original_module].parent {
|
|
||||||
PerNs::types(Module { krate: self.krate, module_id: p }.into())
|
|
||||||
} else {
|
|
||||||
log::debug!("super path in root module");
|
|
||||||
return ResolvePathResult::empty(ReachedFixedPoint::Yes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PathKind::Abs => {
|
|
||||||
// 2018-style absolute path -- only extern prelude
|
|
||||||
let segment = match segments.next() {
|
|
||||||
Some((_, segment)) => segment,
|
|
||||||
None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
|
|
||||||
};
|
|
||||||
if let Some(def) = self.extern_prelude.get(&segment.name) {
|
|
||||||
log::debug!("absolute path {:?} resolved to crate {:?}", path, def);
|
|
||||||
PerNs::types(*def)
|
|
||||||
} else {
|
|
||||||
return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
for (i, segment) in segments {
|
|
||||||
let curr = match curr_per_ns.as_ref().take_types() {
|
|
||||||
Some(r) => r,
|
|
||||||
None => {
|
|
||||||
// we still have path segments left, but the path so far
|
|
||||||
// didn't resolve in the types namespace => no resolution
|
|
||||||
// (don't break here because `curr_per_ns` might contain
|
|
||||||
// something in the value namespace, and it would be wrong
|
|
||||||
// to return that)
|
|
||||||
return ResolvePathResult::empty(ReachedFixedPoint::No);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// resolve segment in curr
|
|
||||||
|
|
||||||
curr_per_ns = match curr {
|
|
||||||
ModuleDef::Module(module) => {
|
|
||||||
if module.krate != self.krate {
|
|
||||||
let path = Path {
|
|
||||||
segments: path.segments[i..].iter().cloned().collect(),
|
|
||||||
kind: PathKind::Self_,
|
|
||||||
};
|
|
||||||
log::debug!("resolving {:?} in other crate", path);
|
|
||||||
let defp_map = db.crate_def_map(module.krate);
|
|
||||||
let (def, s) = defp_map.resolve_path(db, module.module_id, &path);
|
|
||||||
return ResolvePathResult::with(
|
|
||||||
def,
|
|
||||||
ReachedFixedPoint::Yes,
|
|
||||||
s.map(|s| s + i),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
match self[module.module_id].scope.items.get(&segment.name) {
|
|
||||||
Some(res) if !res.def.is_none() => res.def,
|
|
||||||
_ => {
|
|
||||||
log::debug!("path segment {:?} not found", segment.name);
|
|
||||||
return ResolvePathResult::empty(ReachedFixedPoint::No);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ModuleDef::Enum(e) => {
|
|
||||||
// enum variant
|
|
||||||
tested_by!(can_import_enum_variant);
|
|
||||||
match e.variant(db, &segment.name) {
|
|
||||||
Some(variant) => PerNs::both(variant.into(), variant.into()),
|
|
||||||
None => {
|
|
||||||
return ResolvePathResult::with(
|
|
||||||
PerNs::types((*e).into()),
|
|
||||||
ReachedFixedPoint::Yes,
|
|
||||||
Some(i),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s => {
|
|
||||||
// could be an inherent method call in UFCS form
|
|
||||||
// (`Struct::method`), or some other kind of associated item
|
|
||||||
log::debug!(
|
|
||||||
"path segment {:?} resolved to non-module {:?}, but is not last",
|
|
||||||
segment.name,
|
|
||||||
curr,
|
|
||||||
);
|
|
||||||
|
|
||||||
return ResolvePathResult::with(
|
|
||||||
PerNs::types((*s).into()),
|
|
||||||
ReachedFixedPoint::Yes,
|
|
||||||
Some(i),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
ResolvePathResult::with(curr_per_ns, ReachedFixedPoint::Yes, None)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resolve_name_in_crate_root_or_extern_prelude(&self, name: &Name) -> PerNs<ModuleDef> {
|
|
||||||
let from_crate_root =
|
|
||||||
self[self.root].scope.items.get(name).map_or(PerNs::none(), |it| it.def);
|
|
||||||
let from_extern_prelude = self.resolve_name_in_extern_prelude(name);
|
|
||||||
|
|
||||||
from_crate_root.or(from_extern_prelude)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn resolve_name_in_module(
|
|
||||||
&self,
|
|
||||||
db: &impl PersistentHirDatabase,
|
|
||||||
module: ModuleId,
|
|
||||||
name: &Name,
|
|
||||||
) -> PerNs<ModuleDef> {
|
|
||||||
// Resolve in:
|
|
||||||
// - current module / scope
|
|
||||||
// - extern prelude
|
|
||||||
// - std prelude
|
|
||||||
let from_scope = self[module].scope.items.get(name).map_or(PerNs::none(), |it| it.def);
|
|
||||||
let from_extern_prelude =
|
|
||||||
self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it));
|
|
||||||
let from_prelude = self.resolve_in_prelude(db, name);
|
|
||||||
|
|
||||||
from_scope.or(from_extern_prelude).or(from_prelude)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs<ModuleDef> {
|
|
||||||
self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resolve_in_prelude(&self, db: &impl PersistentHirDatabase, name: &Name) -> PerNs<ModuleDef> {
|
|
||||||
if let Some(prelude) = self.prelude {
|
|
||||||
let resolution = if prelude.krate == self.krate {
|
|
||||||
self[prelude.module_id].scope.items.get(name).cloned()
|
|
||||||
} else {
|
|
||||||
db.crate_def_map(prelude.krate)[prelude.module_id].scope.items.get(name).cloned()
|
|
||||||
};
|
|
||||||
resolution.map(|r| r.def).unwrap_or_else(PerNs::none)
|
|
||||||
} else {
|
|
||||||
PerNs::none()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
use ra_syntax::{
|
|
||||||
AstNode, SourceFile, TreeArc, AstPtr,
|
|
||||||
ast,
|
|
||||||
};
|
|
||||||
use ra_arena::{RawId, impl_arena_id, map::ArenaMap};
|
|
||||||
|
|
||||||
use crate::{Path, ModuleSource, Name};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
||||||
pub struct ImportId(RawId);
|
|
||||||
impl_arena_id!(ImportId);
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
||||||
pub struct ImportData {
|
|
||||||
pub(super) path: Path,
|
|
||||||
pub(super) alias: Option<Name>,
|
|
||||||
pub(super) is_glob: bool,
|
|
||||||
pub(super) is_prelude: bool,
|
|
||||||
pub(super) is_extern_crate: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default, PartialEq, Eq)]
|
|
||||||
pub struct ImportSourceMap {
|
|
||||||
map: ArenaMap<ImportId, AstPtr<ast::PathSegment>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ImportSourceMap {
|
|
||||||
pub(crate) fn insert(&mut self, import: ImportId, segment: &ast::PathSegment) {
|
|
||||||
self.map.insert(import, AstPtr::new(segment))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get(&self, source: &ModuleSource, import: ImportId) -> TreeArc<ast::PathSegment> {
|
|
||||||
let file = match source {
|
|
||||||
ModuleSource::SourceFile(file) => &*file,
|
|
||||||
ModuleSource::Module(m) => m.syntax().ancestors().find_map(SourceFile::cast).unwrap(),
|
|
||||||
};
|
|
||||||
|
|
||||||
self.map[import].to_node(file).to_owned()
|
|
||||||
}
|
|
||||||
}
|
|
78
crates/ra_hir/src/nameres/per_ns.rs
Normal file
78
crates/ra_hir/src/nameres/per_ns.rs
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub enum Namespace {
|
||||||
|
Types,
|
||||||
|
Values,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub struct PerNs<T> {
|
||||||
|
pub types: Option<T>,
|
||||||
|
pub values: Option<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Default for PerNs<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
PerNs { types: None, values: None }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> PerNs<T> {
|
||||||
|
pub fn none() -> PerNs<T> {
|
||||||
|
PerNs { types: None, values: None }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn values(t: T) -> PerNs<T> {
|
||||||
|
PerNs { types: None, values: Some(t) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn types(t: T) -> PerNs<T> {
|
||||||
|
PerNs { types: Some(t), values: None }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn both(types: T, values: T) -> PerNs<T> {
|
||||||
|
PerNs { types: Some(types), values: Some(values) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_none(&self) -> bool {
|
||||||
|
self.types.is_none() && self.values.is_none()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_both(&self) -> bool {
|
||||||
|
self.types.is_some() && self.values.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn take(self, namespace: Namespace) -> Option<T> {
|
||||||
|
match namespace {
|
||||||
|
Namespace::Types => self.types,
|
||||||
|
Namespace::Values => self.values,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn take_types(self) -> Option<T> {
|
||||||
|
self.take(Namespace::Types)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn take_values(self) -> Option<T> {
|
||||||
|
self.take(Namespace::Values)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self, namespace: Namespace) -> Option<&T> {
|
||||||
|
self.as_ref().take(namespace)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_ref(&self) -> PerNs<&T> {
|
||||||
|
PerNs { types: self.types.as_ref(), values: self.values.as_ref() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn or(self, other: PerNs<T>) -> PerNs<T> {
|
||||||
|
PerNs { types: self.types.or(other.types), values: self.values.or(other.values) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn and_then<U>(self, f: impl Fn(T) -> Option<U>) -> PerNs<U> {
|
||||||
|
PerNs { types: self.types.and_then(&f), values: self.values.and_then(&f) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn map<U>(self, f: impl Fn(T) -> U) -> PerNs<U> {
|
||||||
|
PerNs { types: self.types.map(&f), values: self.values.map(&f) }
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,16 +5,15 @@ use std::{
|
||||||
|
|
||||||
use test_utils::tested_by;
|
use test_utils::tested_by;
|
||||||
use ra_db::FileId;
|
use ra_db::FileId;
|
||||||
use ra_arena::{Arena, impl_arena_id, RawId};
|
use ra_arena::{Arena, impl_arena_id, RawId, map::ArenaMap};
|
||||||
use ra_syntax::{
|
use ra_syntax::{
|
||||||
AstNode, SourceFile,
|
AstNode, SourceFile, AstPtr, TreeArc,
|
||||||
ast::{self, NameOwner, AttrsOwner},
|
ast::{self, NameOwner, AttrsOwner},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
PersistentHirDatabase, Name, AsName, Path, HirFileId,
|
PersistentHirDatabase, Name, AsName, Path, HirFileId, ModuleSource,
|
||||||
ids::{SourceFileItemId, SourceFileItems},
|
ids::{SourceFileItemId, SourceFileItems},
|
||||||
nameres::lower::ImportSourceMap,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Default, PartialEq, Eq)]
|
#[derive(Debug, Default, PartialEq, Eq)]
|
||||||
|
@ -27,6 +26,26 @@ pub struct RawItems {
|
||||||
items: Vec<RawItem>,
|
items: Vec<RawItem>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, PartialEq, Eq)]
|
||||||
|
pub struct ImportSourceMap {
|
||||||
|
map: ArenaMap<ImportId, AstPtr<ast::PathSegment>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ImportSourceMap {
|
||||||
|
pub(crate) fn insert(&mut self, import: ImportId, segment: &ast::PathSegment) {
|
||||||
|
self.map.insert(import, AstPtr::new(segment))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self, source: &ModuleSource, import: ImportId) -> TreeArc<ast::PathSegment> {
|
||||||
|
let file = match source {
|
||||||
|
ModuleSource::SourceFile(file) => &*file,
|
||||||
|
ModuleSource::Module(m) => m.syntax().ancestors().find_map(SourceFile::cast).unwrap(),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.map[import].to_node(file).to_owned()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl RawItems {
|
impl RawItems {
|
||||||
pub(crate) fn raw_items_query(
|
pub(crate) fn raw_items_query(
|
||||||
db: &impl PersistentHirDatabase,
|
db: &impl PersistentHirDatabase,
|
||||||
|
@ -113,8 +132,18 @@ pub(crate) enum ModuleData {
|
||||||
Definition { name: Name, source_item_id: SourceFileItemId, items: Vec<RawItem> },
|
Definition { name: Name, source_item_id: SourceFileItemId, items: Vec<RawItem> },
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) use crate::nameres::lower::ImportId;
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub(super) use crate::nameres::lower::ImportData;
|
pub struct ImportId(RawId);
|
||||||
|
impl_arena_id!(ImportId);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct ImportData {
|
||||||
|
pub(crate) path: Path,
|
||||||
|
pub(crate) alias: Option<Name>,
|
||||||
|
pub(crate) is_glob: bool,
|
||||||
|
pub(crate) is_prelude: bool,
|
||||||
|
pub(crate) is_extern_crate: bool,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub(crate) struct Def(RawId);
|
pub(crate) struct Def(RawId);
|
|
@ -11,7 +11,7 @@ use crate::{
|
||||||
ids::TraitId,
|
ids::TraitId,
|
||||||
impl_block::{ImplId, ImplBlock, ImplItem},
|
impl_block::{ImplId, ImplBlock, ImplItem},
|
||||||
ty::{AdtDef, Ty},
|
ty::{AdtDef, Ty},
|
||||||
nameres::crate_def_map::ModuleId,
|
nameres::ModuleId,
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue