Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,319 changes: 1,179 additions & 140 deletions crates/syn-sem-name/src/db.rs

Large diffs are not rendered by default.

41 changes: 34 additions & 7 deletions crates/syn-sem-name/src/def.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,23 @@ pub struct Def<'cx> {
/// Scope that owns this definition.
pub parent_scope: ScopeId,

/// Scope containing this definition's importable children.
///
/// Modules and item-like definitions such as enums can expose names through a child scope.
/// Definitions without importable children leave this unset.
pub child_scope: Option<ScopeId>,

/// Scope containing this definition's generic parameters.
///
/// Generic parameters are lexical names, not importable children. Definitions without generic
/// parameters leave this unset.
pub generic_scope: Option<ScopeId>,

/// Definition this definition aliases.
///
/// Import definitions use this to point at their resolved target.
pub target: Option<DefId>,

/// Visibility of this definition.
pub visibility: Visibility,

Expand Down Expand Up @@ -81,18 +98,29 @@ impl DefKind {
/// Returns the default namespaces populated by this definition kind.
pub const fn namespaces(self) -> &'static [Namespace] {
match self {
// Type namespace
Self::Module
| Self::Struct
| Self::Enum
| Self::Trait
| Self::TypeAlias
| Self::TypeParam => &[Namespace::Type],

// Type & Value namespaces
Self::Variant => &[Namespace::Type, Namespace::Value],

// Value namespace
Self::Fn | Self::Const | Self::Static | Self::Local | Self::ConstParam => {
&[Namespace::Value]
}

// Lifetime namespace
Self::LifetimeParam => &[Namespace::Lifetime],

// Macro namespace
Self::Macro => &[Namespace::Macro],

// None
Self::Field | Self::Use | Self::Impl => &[],
}
}
Expand All @@ -113,13 +141,12 @@ pub enum Visibility {

/// Source origin associated with a definition or import.
///
/// The name crate deliberately does not depend on a concrete AST crate. Users can store their own
/// stable node index here and interpret it at the integration boundary.
/// This is the place where future source mapping can attach diagnostics, go-to-definition
/// information, or incremental invalidation data to `Def` and `Import` entries. For now, the name
/// database records untracked origins because no stable AST-node identity is wired through the
/// integration layer yet.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Origin {
/// No source node is associated with this entry.
Synthetic,

/// Opaque AST node index owned by the caller.
AstNode(usize),
/// Source origin is not tracked for this entry.
Untracked,
}
4 changes: 2 additions & 2 deletions crates/syn-sem-name/src/import.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use crate::{Name, Origin, ScopeId, Visibility};
use crate::{ImportId, Name, Origin, ScopeId, Visibility};

/// Import declaration collected during name resolution.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Import<'cx> {
/// Import id.
pub id: crate::ImportId,
pub id: ImportId,

/// Scope that receives the imported binding.
pub scope: ScopeId,
Expand Down
12 changes: 12 additions & 0 deletions crates/syn-sem-name/src/namespace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,15 @@ pub enum Namespace {
/// Lifetime namespace.
Lifetime,
}

impl Namespace {
/// Returns all Rust namespaces handled by the name database.
pub const fn all() -> [Self; 4] {
[
Namespace::Type,
Namespace::Value,
Namespace::Macro,
Namespace::Lifetime,
]
}
}
61 changes: 39 additions & 22 deletions crates/syn-sem-name/src/scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ pub struct Scope<'cx> {

impl<'cx> Scope<'cx> {
/// Creates an empty scope.
pub fn new(id: ScopeId, kind: ScopeKind, parent: Option<ScopeId>) -> Self {
pub(crate) fn new(id: ScopeId, kind: ScopeKind, parent: Option<ScopeId>) -> Self {
Self {
id,
parent,
Expand Down Expand Up @@ -66,16 +66,6 @@ pub struct Bindings<'cx> {
}

impl<'cx> Bindings<'cx> {
/// Creates empty bindings.
pub fn new() -> Self {
Self::default()
}

/// Inserts a definition under `name` in `namespace`.
pub fn insert(&mut self, namespace: Namespace, name: Name<'cx>, def: DefId) {
self.map_mut(namespace).entry(name).or_default().push(def);
}

/// Returns the binding for `name` in `namespace`.
pub fn get(&self, namespace: Namespace, name: Name<'cx>) -> Option<&Binding> {
self.map(namespace).get(&name)
Expand All @@ -91,8 +81,35 @@ impl<'cx> Bindings<'cx> {
}
}

/// Creates empty bindings.
pub(crate) fn new() -> Self {
Self::default()
}

/// Inserts a definition under `name` in `namespace`.
pub(crate) fn insert(&mut self, namespace: Namespace, name: Name<'cx>, def: DefId) {
self.map_mut(namespace).entry(name).or_default().push(def);
}

/// Inserts a definition unless the same definition is already bound there, then returns true
/// if the given definition is successfully inserted.
pub(crate) fn insert_unique(
&mut self,
namespace: Namespace,
name: Name<'cx>,
def: DefId,
) -> bool {
let binding = self.map_mut(namespace).entry(name).or_default();
if binding.contains(def) {
false
} else {
binding.push(def);
true
}
}

/// Returns the mutable map for `namespace`.
pub fn map_mut(&mut self, namespace: Namespace) -> &mut Map<Name<'cx>, Binding> {
pub(crate) fn map_mut(&mut self, namespace: Namespace) -> &mut Map<Name<'cx>, Binding> {
match namespace {
Namespace::Type => &mut self.types,
Namespace::Value => &mut self.values,
Expand All @@ -109,16 +126,6 @@ pub struct Binding {
}

impl Binding {
/// Creates an empty binding.
pub fn new() -> Self {
Self::default()
}

/// Appends a definition to this binding.
pub fn push(&mut self, def: DefId) {
self.defs.push(def);
}

/// Iterates definitions attached to this binding.
pub fn iter(&self) -> impl ExactSizeIterator<Item = DefId> + '_ {
self.defs.iter().copied()
Expand All @@ -141,4 +148,14 @@ impl Binding {
pub fn is_empty(&self) -> bool {
self.defs.is_empty()
}

/// Appends a definition to this binding.
pub(crate) fn push(&mut self, def: DefId) {
self.defs.push(def);
}

/// Returns whether this binding already contains `def`.
pub(crate) fn contains(&self, def: DefId) -> bool {
self.defs.contains(&def)
}
}
27 changes: 25 additions & 2 deletions crates/syn-sem-name/tests/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ impl<'cx> AstNameCollector<'cx> {
kind,
Some(name),
Visibility::Private,
Origin::Synthetic,
Origin::Untracked,
)
}
}
Expand Down Expand Up @@ -175,13 +175,36 @@ fn expect_def(
name: Name<'_>,
kind: DefKind,
) -> DefId {
let ResolveResult::Found(def) = db.resolve_lexical(scope, namespace, name) else {
let ResolveResult::Found(def) = resolve_lexical(db, scope, namespace, name) else {
panic!("expected {name:?} to resolve in {namespace:?}");
};
assert_eq!(db[def].kind, kind);
def
}

fn resolve_lexical(
db: &NameDb<'_>,
mut scope: ScopeId,
namespace: Namespace,
name: Name<'_>,
) -> ResolveResult {
loop {
if let Some(binding) = db.binding(scope, namespace, name) {
let mut defs = binding.iter();
return match defs.len() {
0 => ResolveResult::NotFound,
1 => ResolveResult::Found(defs.next().unwrap()),
_ => ResolveResult::Ambiguous(defs.collect()),
};
}

let Some(parent) = db[scope].parent else {
return ResolveResult::NotFound;
};
scope = parent;
}
}

#[test]
fn resolves_function_generics_params_and_locals_from_ast() {
let ccx = CommonCx::new();
Expand Down
5 changes: 3 additions & 2 deletions crates/syn-sem-top/src/context.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::Semantics;
use crate::{NameCollector, Semantics};
use std::{fs, path::Path};
use syn_sem_ast::SyntaxCx;
use syn_sem_common::{CommonCx, FilePath, Result, SourceText};
Expand All @@ -8,6 +8,7 @@ use syn_sem_common::{CommonCx, FilePath, Result, SourceText};
/// `TopCx` owns shared infrastructure and phase contexts. Phase contexts borrow the owned common
/// context through the top-level root, keeping lower-level contexts from owning one another.
pub struct TopCx<'tcx> {
/// Syntax parsing and source storage context.
pub syntax: SyntaxCx<'tcx>,

/// Shared common infrastructure context.
Expand All @@ -29,7 +30,7 @@ impl<'tcx> TopCx<'tcx> {
/// Analyzes a previously inserted or read entry file.
pub fn analyze(&'tcx self, entry_path: FilePath<'tcx>) -> Result<Semantics<'tcx>> {
let file = self.syntax.lookup_source(entry_path)?.ast();
let names = crate::collect_names_in_top(self, entry_path, file)?;
let names = NameCollector::new(self).collect(entry_path, file)?;
Ok(Semantics::new(self, names))
}

Expand Down
Loading
Loading