2196: Touch up nameres doc comment r=matklad a=matklad



Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
This commit is contained in:
bors[bot] 2019-11-08 21:24:02 +00:00 committed by GitHub
commit 23939cabcc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 304 additions and 281 deletions

View File

@ -14,10 +14,10 @@
//!
//! ## Collecting RawItems
//!
//! This happens in the `raw` module, which parses a single source file into a
//! set of top-level items. Nested imports are desugared to flat imports in
//! this phase. Macro calls are represented as a triple of (Path, Option<Name>,
//! TokenTree).
//! This happens in the `raw` module, which parses a single source file into a
//! set of top-level items. Nested imports are desugared to flat imports in this
//! phase. Macro calls are represented as a triple of (Path, Option<Name>,
//! TokenTree).
//!
//! ## Collecting Modules
//!
@ -44,14 +44,14 @@
//! Macros from other crates (including proc-macros) can be used with
//! `foo::bar!` syntax. We handle them similarly to imports. There's a list of
//! unexpanded macros. On every iteration, we try to resolve each macro call
//! path and, upon success, we run macro expansion and "collect module" phase
//! on the result
//! path and, upon success, we run macro expansion and "collect module" phase on
//! the result
// FIXME: review privacy of submodules
pub mod raw;
pub mod per_ns;
mod collector;
mod mod_resolution;
mod path_resolution;
#[cfg(test)]
mod tests;
@ -65,14 +65,15 @@ use ra_db::{CrateId, Edition, FileId};
use ra_prof::profile;
use ra_syntax::ast;
use rustc_hash::{FxHashMap, FxHashSet};
use test_utils::tested_by;
use crate::{
builtin_type::BuiltinType,
db::DefDatabase2,
nameres::{diagnostics::DefDiagnostic, per_ns::PerNs, raw::ImportId},
path::{Path, PathKind},
AdtId, AstId, CrateModuleId, EnumVariantId, ModuleDefId, ModuleId, TraitId,
nameres::{
diagnostics::DefDiagnostic, path_resolution::ResolveMode, per_ns::PerNs, raw::ImportId,
},
path::Path,
AstId, CrateModuleId, ModuleDefId, ModuleId, TraitId,
};
/// Contains all top-level defs from a macro-expanded crate
@ -195,45 +196,6 @@ pub struct Resolution {
pub import: Option<ImportId>,
}
impl Resolution {
fn from_macro(macro_: MacroDefId) -> Self {
Resolution { def: PerNs::macros(macro_), import: None }
}
}
#[derive(Debug, Clone)]
struct ResolvePathResult {
resolved_def: PerNs,
segment_index: Option<usize>,
reached_fixedpoint: ReachedFixedPoint,
}
impl ResolvePathResult {
fn empty(reached_fixedpoint: ReachedFixedPoint) -> ResolvePathResult {
ResolvePathResult::with(PerNs::none(), reached_fixedpoint, None)
}
fn with(
resolved_def: PerNs,
reached_fixedpoint: ReachedFixedPoint,
segment_index: Option<usize>,
) -> ResolvePathResult {
ResolvePathResult { resolved_def, reached_fixedpoint, segment_index }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ResolveMode {
Import,
Other,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ReachedFixedPoint {
Yes,
No,
}
impl CrateDefMap {
pub(crate) fn crate_def_map_query(
// Note that this doesn't have `+ AstDatabase`!
@ -296,210 +258,6 @@ impl CrateDefMap {
let res = self.resolve_path_fp_with_macro(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_with_macro(
&self,
db: &impl DefDatabase2,
mode: ResolveMode,
original_module: CrateModuleId,
path: &Path,
) -> ResolvePathResult {
let mut segments = path.segments.iter().enumerate();
let mut curr_per_ns: PerNs = match path.kind {
PathKind::DollarCrate(krate) => {
if krate == self.krate {
tested_by!(macro_dollar_crate_self);
PerNs::types(ModuleId { krate: self.krate, module_id: self.root }.into())
} else {
let def_map = db.crate_def_map(krate);
let module = ModuleId { krate, module_id: def_map.root };
tested_by!(macro_dollar_crate_other);
PerNs::types(module.into())
}
}
PathKind::Crate => {
PerNs::types(ModuleId { krate: self.krate, module_id: self.root }.into())
}
PathKind::Self_ => {
PerNs::types(ModuleId { 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)
// FIXME 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(ModuleId { 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
}
}
PathKind::Type(_) => {
// This is handled in `infer::infer_path_expr`
// The result returned here does not matter
return ResolvePathResult::empty(ReachedFixedPoint::Yes);
}
};
for (i, segment) in segments {
let curr = match curr_per_ns.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 {
ModuleDefId::ModuleId(module) => {
if module.krate != self.krate {
let path =
Path { segments: path.segments[i..].to_vec(), 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),
);
}
// Since it is a qualified path here, it should not contains legacy macros
match self[module.module_id].scope.get(&segment.name) {
Some(res) => res.def,
_ => {
log::debug!("path segment {:?} not found", segment.name);
return ResolvePathResult::empty(ReachedFixedPoint::No);
}
}
}
ModuleDefId::AdtId(AdtId::EnumId(e)) => {
// enum variant
tested_by!(can_import_enum_variant);
let enum_data = db.enum_data(e);
match enum_data.variant(&segment.name) {
Some(local_id) => {
let variant = EnumVariantId { parent: e, local_id };
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),
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 {
let from_crate_root =
self[self.root].scope.get(name).map_or_else(PerNs::none, |res| res.def);
let from_extern_prelude = self.resolve_name_in_extern_prelude(name);
from_crate_root.or(from_extern_prelude)
}
fn resolve_name_in_module(
&self,
db: &impl DefDatabase2,
module: CrateModuleId,
name: &Name,
) -> PerNs {
// Resolve in:
// - legacy scope of macro
// - current module / scope
// - extern prelude
// - std prelude
let from_legacy_macro =
self[module].scope.get_legacy_macro(name).map_or_else(PerNs::none, PerNs::macros);
let from_scope = self[module].scope.get(name).map_or_else(PerNs::none, |res| res.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_legacy_macro.or(from_scope).or(from_extern_prelude).or(from_prelude)
}
fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs {
self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it))
}
fn resolve_in_prelude(&self, db: &impl DefDatabase2, name: &Name) -> PerNs {
if let Some(prelude) = self.prelude {
let keep;
let def_map = if prelude.krate == self.krate {
self
} else {
// Extend lifetime
keep = db.crate_def_map(prelude.krate);
&keep
};
def_map[prelude.module_id].scope.get(name).map_or_else(PerNs::none, |res| res.def)
} else {
PerNs::none()
}
}
}
mod diagnostics {

View File

@ -14,8 +14,8 @@ use crate::{
attr::Attr,
db::DefDatabase2,
nameres::{
diagnostics::DefDiagnostic, mod_resolution::ModDir, per_ns::PerNs, raw, CrateDefMap,
ModuleData, ReachedFixedPoint, Resolution, ResolveMode,
diagnostics::DefDiagnostic, mod_resolution::ModDir, path_resolution::ReachedFixedPoint,
per_ns::PerNs, raw, CrateDefMap, ModuleData, Resolution, ResolveMode,
},
path::{Path, PathKind},
AdtId, AstId, AstItemDef, ConstId, CrateModuleId, EnumId, EnumVariantId, FunctionId,
@ -182,7 +182,11 @@ where
// In Rust, `#[macro_export]` macros are unconditionally visible at the
// crate root, even if the parent modules is **not** visible.
if export {
self.update(self.def_map.root, None, &[(name, Resolution::from_macro(macro_))]);
self.update(
self.def_map.root,
None,
&[(name, Resolution { def: PerNs::macros(macro_), import: None })],
);
}
}

View File

@ -0,0 +1,261 @@
//! This modules implements a function to resolve a path `foo::bar::baz` to a
//! def, which is used within the name resolution.
//!
//! When name resolution is finished, the result of resolving a path is either
//! `Some(def)` or `None`. However, when we are in process of resolving imports
//! or macros, there's a third possibility:
//!
//! I can't resolve this path right now, but I might be resolve this path
//! later, when more macros are expanded.
//!
//! `ReachedFixedPoint` signals about this.
use hir_expand::name::Name;
use ra_db::Edition;
use test_utils::tested_by;
use crate::{
db::DefDatabase2,
nameres::{per_ns::PerNs, CrateDefMap},
path::{Path, PathKind},
AdtId, CrateModuleId, EnumVariantId, ModuleDefId, ModuleId,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum ResolveMode {
Import,
Other,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum ReachedFixedPoint {
Yes,
No,
}
#[derive(Debug, Clone)]
pub(super) struct ResolvePathResult {
pub(super) resolved_def: PerNs,
pub(super) segment_index: Option<usize>,
pub(super) reached_fixedpoint: ReachedFixedPoint,
}
impl ResolvePathResult {
fn empty(reached_fixedpoint: ReachedFixedPoint) -> ResolvePathResult {
ResolvePathResult::with(PerNs::none(), reached_fixedpoint, None)
}
fn with(
resolved_def: PerNs,
reached_fixedpoint: ReachedFixedPoint,
segment_index: Option<usize>,
) -> ResolvePathResult {
ResolvePathResult { resolved_def, reached_fixedpoint, segment_index }
}
}
impl CrateDefMap {
pub(super) fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs {
self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it))
}
// Returns Yes if we are sure that additions to `ItemMap` wouldn't change
// the result.
pub(super) fn resolve_path_fp_with_macro(
&self,
db: &impl DefDatabase2,
mode: ResolveMode,
original_module: CrateModuleId,
path: &Path,
) -> ResolvePathResult {
let mut segments = path.segments.iter().enumerate();
let mut curr_per_ns: PerNs = match path.kind {
PathKind::DollarCrate(krate) => {
if krate == self.krate {
tested_by!(macro_dollar_crate_self);
PerNs::types(ModuleId { krate: self.krate, module_id: self.root }.into())
} else {
let def_map = db.crate_def_map(krate);
let module = ModuleId { krate, module_id: def_map.root };
tested_by!(macro_dollar_crate_other);
PerNs::types(module.into())
}
}
PathKind::Crate => {
PerNs::types(ModuleId { krate: self.krate, module_id: self.root }.into())
}
PathKind::Self_ => {
PerNs::types(ModuleId { 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)
// FIXME 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(ModuleId { 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
}
}
PathKind::Type(_) => {
// This is handled in `infer::infer_path_expr`
// The result returned here does not matter
return ResolvePathResult::empty(ReachedFixedPoint::Yes);
}
};
for (i, segment) in segments {
let curr = match curr_per_ns.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 {
ModuleDefId::ModuleId(module) => {
if module.krate != self.krate {
let path =
Path { segments: path.segments[i..].to_vec(), 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),
);
}
// Since it is a qualified path here, it should not contains legacy macros
match self[module.module_id].scope.get(&segment.name) {
Some(res) => res.def,
_ => {
log::debug!("path segment {:?} not found", segment.name);
return ResolvePathResult::empty(ReachedFixedPoint::No);
}
}
}
ModuleDefId::AdtId(AdtId::EnumId(e)) => {
// enum variant
tested_by!(can_import_enum_variant);
let enum_data = db.enum_data(e);
match enum_data.variant(&segment.name) {
Some(local_id) => {
let variant = EnumVariantId { parent: e, local_id };
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),
ReachedFixedPoint::Yes,
Some(i),
);
}
};
}
ResolvePathResult::with(curr_per_ns, ReachedFixedPoint::Yes, None)
}
fn resolve_name_in_module(
&self,
db: &impl DefDatabase2,
module: CrateModuleId,
name: &Name,
) -> PerNs {
// Resolve in:
// - legacy scope of macro
// - current module / scope
// - extern prelude
// - std prelude
let from_legacy_macro =
self[module].scope.get_legacy_macro(name).map_or_else(PerNs::none, PerNs::macros);
let from_scope = self[module].scope.get(name).map_or_else(PerNs::none, |res| res.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_legacy_macro.or(from_scope).or(from_extern_prelude).or(from_prelude)
}
fn resolve_name_in_crate_root_or_extern_prelude(&self, name: &Name) -> PerNs {
let from_crate_root =
self[self.root].scope.get(name).map_or_else(PerNs::none, |res| res.def);
let from_extern_prelude = self.resolve_name_in_extern_prelude(name);
from_crate_root.or(from_extern_prelude)
}
fn resolve_in_prelude(&self, db: &impl DefDatabase2, name: &Name) -> PerNs {
if let Some(prelude) = self.prelude {
let keep;
let def_map = if prelude.krate == self.krate {
self
} else {
// Extend lifetime
keep = db.crate_def_map(prelude.krate);
&keep
};
def_map[prelude.module_id].scope.get(name).map_or_else(PerNs::none, |res| res.def)
} else {
PerNs::none()
}
}
}

View File

@ -88,7 +88,7 @@ impl RawItems {
(Arc::new(collector.raw_items), Arc::new(collector.source_map))
}
pub fn items(&self) -> &[RawItem] {
pub(super) fn items(&self) -> &[RawItem] {
&self.items
}
}
@ -125,19 +125,19 @@ impl Index<Macro> for RawItems {
type Attrs = Option<Arc<[Attr]>>;
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct RawItem {
pub(super) struct RawItem {
attrs: Attrs,
pub kind: RawItemKind,
pub(super) kind: RawItemKind,
}
impl RawItem {
pub fn attrs(&self) -> &[Attr] {
pub(super) fn attrs(&self) -> &[Attr] {
self.attrs.as_ref().map_or(&[], |it| &*it)
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum RawItemKind {
pub(super) enum RawItemKind {
Module(Module),
Import(ImportId),
Def(Def),
@ -145,11 +145,11 @@ pub enum RawItemKind {
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Module(RawId);
pub(super) struct Module(RawId);
impl_arena_id!(Module);
#[derive(Debug, PartialEq, Eq)]
pub enum ModuleData {
pub(super) enum ModuleData {
Declaration { name: Name, ast_id: FileAstId<ast::Module> },
Definition { name: Name, ast_id: FileAstId<ast::Module>, items: Vec<RawItem> },
}
@ -160,26 +160,26 @@ impl_arena_id!(ImportId);
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ImportData {
pub path: Path,
pub alias: Option<Name>,
pub is_glob: bool,
pub is_prelude: bool,
pub is_extern_crate: bool,
pub is_macro_use: bool,
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,
pub(super) is_macro_use: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Def(RawId);
pub(super) struct Def(RawId);
impl_arena_id!(Def);
#[derive(Debug, PartialEq, Eq)]
pub struct DefData {
pub name: Name,
pub kind: DefKind,
pub(super) struct DefData {
pub(super) name: Name,
pub(super) kind: DefKind,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum DefKind {
pub(super) enum DefKind {
Function(FileAstId<ast::FnDef>),
Struct(FileAstId<ast::StructDef>),
Union(FileAstId<ast::StructDef>),
@ -191,15 +191,15 @@ pub enum DefKind {
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Macro(RawId);
pub(super) struct Macro(RawId);
impl_arena_id!(Macro);
#[derive(Debug, PartialEq, Eq)]
pub struct MacroData {
pub ast_id: FileAstId<ast::MacroCall>,
pub path: Path,
pub name: Option<Name>,
pub export: bool,
pub(super) struct MacroData {
pub(super) ast_id: FileAstId<ast::MacroCall>,
pub(super) path: Path,
pub(super) name: Option<Name>,
pub(super) export: bool,
}
struct RawItemsCollector {