Skip to content

Commit

Permalink
[WIP] import resolver
Browse files Browse the repository at this point in the history
  • Loading branch information
Y-Nak committed Apr 27, 2023
1 parent ee63de0 commit b5b84b5
Show file tree
Hide file tree
Showing 6 changed files with 183 additions and 187 deletions.
36 changes: 36 additions & 0 deletions crates/hir-analysis/src/name_resolution/import_resolver.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#![allow(dead_code)]
use hir::hir_def::scope_graph::{ScopeEdge, ScopeId};
use rustc_hash::FxHashMap;

use crate::Spanned;

use super::Importer;

pub struct ResolvedImports {
pub resolved: FxHashMap<ScopeId, ScopeEdge>,
}

pub struct ImportResolver {
resolved: FxHashMap<ScopeId, Vec<Spanned<ScopeEdge>>>,
glob_resolved: FxHashMap<ScopeId, Vec<Spanned<ScopeEdge>>>,
states: FxHashMap<ScopeId, ScopeState>,
}

/// This is the state of import resolution for a given scope.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum ScopeState {
// The scope is open, meaning that the scope needs further processing.
Open,
// The scope is closed, meaning that the scope is fully resolved.
Close,
}

impl Importer for ImportResolver {
fn glob_imports(&self, scope: ScopeId) -> &[Spanned<ScopeEdge>] {
&self.glob_resolved[&scope]
}

fn named_imports(&self, scope: ScopeId) -> &[Spanned<ScopeEdge>] {
&self.resolved[&scope]
}
}
23 changes: 7 additions & 16 deletions crates/hir-analysis/src/name_resolution/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use hir::{
ScopeEdge, ScopeId, SelfEdge, SelfTyEdge, SuperEdge, TraitEdge, TypeEdge, ValueEdge,
VariantEdge,
},
IdentId, Visibility,
IdentId,
},
span::DynLazySpan,
};
Expand All @@ -26,8 +26,8 @@ pub struct NameResolver<'db, 'a> {
impl<'db, 'a> NameResolver<'db, 'a> {
pub fn resolve_query(&mut self, scope: ScopeId, query: NameQuery) -> Vec<ResolvedName> {
// If the query is already resolved, return the cached result.
if let Some(answer) = self.cache_store.get(scope, query) {
return answer.clone();
if let Some(resolved) = self.cache_store.get(scope, query) {
return resolved;
};

// The shadowing rule is
Expand All @@ -44,7 +44,7 @@ impl<'db, 'a> NameResolver<'db, 'a> {
match edge.kind.propagate(self.db, query) {
PropagatedQuery::Terminated => {
if found_scopes.insert(edge.dest) {
results.push(ResolvedName::new(edge.dest, edge.vis, None));
results.push(ResolvedName::new(edge.dest, None));
}
}

Expand All @@ -64,7 +64,6 @@ impl<'db, 'a> NameResolver<'db, 'a> {
if found_scopes.insert(edge.dest) {
results.push(ResolvedName::new(
edge.dest,
edge.vis,
Some(named_import.span.clone()),
));
}
Expand All @@ -89,7 +88,6 @@ impl<'db, 'a> NameResolver<'db, 'a> {
if found_scopes.insert(edge.dest) {
results.push(ResolvedName::new_glob(
edge.dest,
edge.vis,
Some(glob_import.span.clone()),
));
}
Expand All @@ -114,11 +112,7 @@ impl<'db, 'a> NameResolver<'db, 'a> {
} else if query.domain == NameDomain::Item {
for (name, root_mod) in scope.top_mod.external_ingots(self.db.upcast()) {
if *name == query.name {
results.push(ResolvedName::new(
ScopeId::root(*root_mod),
Visibility::Public,
None,
));
results.push(ResolvedName::new(ScopeId::root(*root_mod), None));
}
}

Expand Down Expand Up @@ -148,25 +142,22 @@ pub struct NameQuery {
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct ResolvedName {
pub scope: ScopeId,
pub vis: Visibility,
pub import_span: Option<DynLazySpan>,
pub via_glob_import: bool,
}

impl ResolvedName {
pub fn new(scope: ScopeId, vis: Visibility, import_span: Option<DynLazySpan>) -> Self {
pub fn new(scope: ScopeId, import_span: Option<DynLazySpan>) -> Self {
Self {
scope,
vis,
import_span,
via_glob_import: false,
}
}

pub fn new_glob(scope: ScopeId, vis: Visibility, import_span: Option<DynLazySpan>) -> Self {
pub fn new_glob(scope: ScopeId, import_span: Option<DynLazySpan>) -> Self {
Self {
scope,
vis,
import_span,
via_glob_import: true,
}
Expand Down
42 changes: 23 additions & 19 deletions crates/hir-analysis/src/name_resolution/visibility_checker.rs
Original file line number Diff line number Diff line change
@@ -1,38 +1,42 @@
use hir::hir_def::scope_graph::ScopeId;
use hir::hir_def::scope_graph::{ScopeId, ScopeKind};

use crate::HirAnalysisDb;

use super::ResolvedName;

/// Return `true` if the given `resolved` is visible from the `ref_site`.
/// Return `true` if the given `resolved` is visible from the `ref_scope`.
pub fn check_visibility(
db: &dyn HirAnalysisDb,
ref_site: ScopeId,
ref_scope: ScopeId,
resolved: &ResolvedName,
) -> bool {
// If resolved is public, then it is visible.
if resolved.vis.is_pub() {
if resolved.scope.data(db.upcast()).vis.is_pub() {
return true;
}

// If ref site is the same module as resolved is defined in, then it is visible.
if Some(ref_site) == resolved.scope.parent_module(db.upcast()) {
return true;
}

// If ref site is a child module of the module that resolved is defined in, then
// it is visible.
let mut current = ref_site;
let resolved_mod = if let Some(parent) = resolved.scope.parent_module(db.upcast()) {
parent
} else {
resolved.scope
let Some(def_scope) = (match resolved.scope.kind(db.upcast()) {
// We treat fields as if they are defined in the parent of the parent scope so
// that field can be accessible from the scope where the parent is defined.
ScopeKind::Field(_) => {
resolved.scope.parent(db.upcast()).and_then(|scope| scope.parent(db.upcast()))
},
_ => {
resolved.scope.parent(db.upcast())
}
}) else {
return false;
};
while let Some(parent) = current.parent_module(db.upcast()) {
if parent == resolved_mod {

// If ref scope is a child scope or the same scope of the def scope, then it is
// visible.
let mut parent = Some(ref_scope);
while let Some(scope) = parent {
if scope == def_scope {
return true;
} else {
parent = scope.parent(db.upcast());
}
current = parent;
}

false
Expand Down
19 changes: 19 additions & 0 deletions crates/hir/src/hir_def/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,25 @@ pub enum ItemKind {
Body(Body),
}

impl ItemKind {
pub fn vis(self, db: &dyn HirDb) -> Visibility {
use ItemKind::*;
match self {
TopMod(top_mod) => top_mod.vis(db),
Mod(mod_) => mod_.vis(db),
Func(func) => func.vis(db),
Struct(struct_) => struct_.vis(db),
Contract(contract) => contract.vis(db),
Enum(enum_) => enum_.vis(db),
TypeAlias(type_) => type_.vis(db),
Trait(trait_) => trait_.vis(db),
Const(const_) => const_.vis(db),
Use(use_) => use_.vis(db),
Impl(_) | ImplTrait(_) | Body(_) => Visibility::Private,
}
}
}

#[salsa::tracked]
pub struct TopLevelMod {
// No #[id] here, because `TopLevelMod` is always unique to a `InputFile` that is an argument
Expand Down
21 changes: 17 additions & 4 deletions crates/hir/src/hir_def/scope_graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,14 +77,16 @@ pub struct LocalScope {
pub kind: ScopeKind,
pub edges: Vec<ScopeEdge>,
pub parent_module: Option<ScopeId>,
pub vis: Visibility,
}

impl LocalScope {
pub fn new(kind: ScopeKind, parent_module: Option<ScopeId>) -> Self {
pub fn new(kind: ScopeKind, parent_module: Option<ScopeId>, vis: Visibility) -> Self {
Self {
kind,
edges: vec![],
parent_module,
vis,
}
}
}
Expand All @@ -102,7 +104,6 @@ pub enum ScopeKind {
pub struct ScopeEdge {
pub dest: ScopeId,
pub kind: EdgeKind,
pub vis: Visibility,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
Expand Down Expand Up @@ -135,14 +136,26 @@ impl ScopeId {
self != Self::invalid()
}

pub fn scope_data(self, db: &dyn HirDb) -> &LocalScope {
pub fn data(self, db: &dyn HirDb) -> &LocalScope {
self.top_mod
.module_scope_graph(db)
.scope_data(self.local_id)
}

pub fn kind(self, db: &dyn HirDb) -> ScopeKind {
self.data(db).kind
}

pub fn parent(self, db: &dyn HirDb) -> Option<Self> {
self.data(db)
.edges
.iter()
.find(|e| matches!(e.kind, EdgeKind::Lex(_) | EdgeKind::Super(_)))
.map(|e| e.dest)
}

pub fn parent_module(self, db: &dyn HirDb) -> Option<Self> {
self.scope_data(db).parent_module
self.data(db).parent_module
}
}

Expand Down
Loading

0 comments on commit b5b84b5

Please sign in to comment.