[ty] Split ScopedPlaceId into ScopedSymbolId and ScopedMemberId (#19497)

This commit is contained in:
Micha Reiser 2025-07-25 13:54:33 +02:00 committed by GitHub
parent f722bfa9e6
commit b033fb6bfd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
30 changed files with 2454 additions and 1647 deletions

View file

@ -20,10 +20,12 @@ use crate::semantic_index::builder::SemanticIndexBuilder;
use crate::semantic_index::definition::{Definition, DefinitionNodeKey, Definitions};
use crate::semantic_index::expression::Expression;
use crate::semantic_index::narrowing_constraints::ScopedNarrowingConstraint;
use crate::semantic_index::place::{
FileScopeId, NodeWithScopeKey, NodeWithScopeRef, PlaceExpr, PlaceTable, Scope, ScopeId,
ScopeKind, ScopedPlaceId,
use crate::semantic_index::place::{PlaceExprRef, PlaceTable};
pub use crate::semantic_index::scope::FileScopeId;
use crate::semantic_index::scope::{
NodeWithScopeKey, NodeWithScopeRef, Scope, ScopeId, ScopeKind, ScopeLaziness,
};
use crate::semantic_index::symbol::ScopedSymbolId;
use crate::semantic_index::use_def::{EnclosingSnapshotKey, ScopedEnclosingSnapshotId, UseDefMap};
use crate::semantic_model::HasTrackedScope;
use crate::util::get_size::untracked_arc_size;
@ -32,11 +34,14 @@ pub mod ast_ids;
mod builder;
pub mod definition;
pub mod expression;
pub(crate) mod member;
pub(crate) mod narrowing_constraints;
pub mod place;
pub(crate) mod predicate;
mod re_exports;
mod reachability_constraints;
pub(crate) mod scope;
pub(crate) mod symbol;
mod use_def;
pub(crate) use self::use_def::{
@ -44,8 +49,6 @@ pub(crate) use self::use_def::{
DeclarationWithConstraint, DeclarationsIterator,
};
type PlaceSet = hashbrown::HashTable<ScopedPlaceId>;
/// Returns the semantic index for `file`.
///
/// Prefer using [`symbol_table`] when working with symbols from a single scope.
@ -117,10 +120,10 @@ pub(crate) fn attribute_assignments<'db, 's>(
attribute_scopes(db, class_body_scope).filter_map(|function_scope_id| {
let place_table = index.place_table(function_scope_id);
let place = place_table.place_id_by_instance_attribute_name(name)?;
let member = place_table.member_id_by_instance_attribute_name(name)?;
let use_def = &index.use_def_maps[function_scope_id];
Some((
use_def.inner.all_reachable_bindings(place),
use_def.inner.all_reachable_member_bindings(member),
function_scope_id,
))
})
@ -141,10 +144,10 @@ pub(crate) fn attribute_declarations<'db, 's>(
attribute_scopes(db, class_body_scope).filter_map(|function_scope_id| {
let place_table = index.place_table(function_scope_id);
let place = place_table.place_id_by_instance_attribute_name(name)?;
let member = place_table.member_id_by_instance_attribute_name(name)?;
let use_def = &index.use_def_maps[function_scope_id];
Some((
use_def.inner.all_reachable_declarations(place),
use_def.inner.all_reachable_member_declarations(member),
function_scope_id,
))
})
@ -188,38 +191,6 @@ pub(crate) fn global_scope(db: &dyn Db, file: File) -> ScopeId<'_> {
FileScopeId::global().to_scope_id(db, file)
}
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, get_size2::GetSize)]
pub(crate) enum ScopeVisibility {
/// The scope is private (e.g. function, type alias, comprehension scope).
Private,
/// The scope is public (e.g. module, class scope).
Public,
}
impl ScopeVisibility {
pub(crate) const fn is_public(self) -> bool {
matches!(self, ScopeVisibility::Public)
}
pub(crate) const fn is_private(self) -> bool {
matches!(self, ScopeVisibility::Private)
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, get_size2::GetSize)]
pub(crate) enum ScopeLaziness {
/// The scope is evaluated lazily (e.g. function, type alias scope).
Lazy,
/// The scope is evaluated eagerly (e.g. module, class, comprehension scope).
Eager,
}
impl ScopeLaziness {
pub(crate) const fn is_eager(self) -> bool {
matches!(self, ScopeLaziness::Eager)
}
}
pub(crate) enum EnclosingSnapshotResult<'map, 'db> {
FoundConstraint(ScopedNarrowingConstraint),
FoundBindings(BindingWithConstraintsIterator<'map, 'db>),
@ -337,22 +308,18 @@ impl<'db> SemanticIndex<'db> {
pub(crate) fn symbol_is_global_in_scope(
&self,
symbol: ScopedPlaceId,
symbol: ScopedSymbolId,
scope: FileScopeId,
) -> bool {
self.place_table(scope)
.place_expr(symbol)
.is_marked_global()
self.place_table(scope).symbol(symbol).is_global()
}
pub(crate) fn symbol_is_nonlocal_in_scope(
&self,
symbol: ScopedPlaceId,
symbol: ScopedSymbolId,
scope: FileScopeId,
) -> bool {
self.place_table(scope)
.place_expr(symbol)
.is_marked_nonlocal()
self.place_table(scope).symbol(symbol).is_nonlocal()
}
/// Returns the id of the parent scope.
@ -523,7 +490,7 @@ impl<'db> SemanticIndex<'db> {
pub(crate) fn enclosing_snapshot(
&self,
enclosing_scope: FileScopeId,
expr: &PlaceExpr,
expr: PlaceExprRef,
nested_scope: FileScopeId,
) -> EnclosingSnapshotResult<'_, 'db> {
for (ancestor_scope_id, ancestor_scope) in self.ancestor_scopes(nested_scope) {
@ -531,13 +498,13 @@ impl<'db> SemanticIndex<'db> {
break;
}
if !ancestor_scope.is_eager() {
if expr.is_name() {
if let PlaceExprRef::Symbol(symbol) = expr {
if let Some(place_id) =
self.place_tables[enclosing_scope].place_id_by_expr(expr)
self.place_tables[enclosing_scope].symbol_id(symbol.name())
{
let key = EnclosingSnapshotKey {
enclosing_scope,
enclosing_place: place_id,
enclosing_place: place_id.into(),
nested_scope,
nested_laziness: ScopeLaziness::Lazy,
};
@ -551,7 +518,7 @@ impl<'db> SemanticIndex<'db> {
return EnclosingSnapshotResult::NoLongerInEagerContext;
}
}
let Some(place_id) = self.place_tables[enclosing_scope].place_id_by_expr(expr) else {
let Some(place_id) = self.place_tables[enclosing_scope].place_id(expr) else {
return EnclosingSnapshotResult::NotFound;
};
let key = EnclosingSnapshotKey {
@ -595,7 +562,7 @@ impl<'db> ArcUseDefMap<'db> {
}
}
pub struct AncestorsIter<'a> {
pub(crate) struct AncestorsIter<'a> {
scopes: &'a IndexSlice<FileScopeId, Scope>,
next_id: Option<FileScopeId>,
}
@ -623,7 +590,7 @@ impl<'a> Iterator for AncestorsIter<'a> {
impl FusedIterator for AncestorsIter<'_> {}
pub struct VisibleAncestorsIter<'a> {
pub(crate) struct VisibleAncestorsIter<'a> {
inner: AncestorsIter<'a>,
starting_scope_kind: ScopeKind,
yielded_count: usize,
@ -670,7 +637,7 @@ impl<'a> Iterator for VisibleAncestorsIter<'a> {
impl FusedIterator for VisibleAncestorsIter<'_> {}
pub struct DescendantsIter<'a> {
pub(crate) struct DescendantsIter<'a> {
next_id: FileScopeId,
descendants: std::slice::Iter<'a, Scope>,
}
@ -707,7 +674,7 @@ impl FusedIterator for DescendantsIter<'_> {}
impl ExactSizeIterator for DescendantsIter<'_> {}
pub struct ChildrenIter<'a> {
pub(crate) struct ChildrenIter<'a> {
parent: FileScopeId,
descendants: DescendantsIter<'a>,
}
@ -777,13 +744,15 @@ mod tests {
use crate::db::tests::{TestDb, TestDbBuilder};
use crate::semantic_index::ast_ids::{HasScopedUseId, ScopedUseId};
use crate::semantic_index::definition::{Definition, DefinitionKind};
use crate::semantic_index::place::{FileScopeId, PlaceTable, Scope, ScopeKind, ScopedPlaceId};
use crate::semantic_index::place::PlaceTable;
use crate::semantic_index::scope::{FileScopeId, Scope, ScopeKind};
use crate::semantic_index::symbol::ScopedSymbolId;
use crate::semantic_index::use_def::UseDefMap;
use crate::semantic_index::{global_scope, place_table, semantic_index, use_def_map};
impl UseDefMap<'_> {
fn first_public_binding(&self, symbol: ScopedPlaceId) -> Option<Definition<'_>> {
self.end_of_scope_bindings(symbol)
fn first_public_binding(&self, symbol: ScopedSymbolId) -> Option<Definition<'_>> {
self.end_of_scope_symbol_bindings(symbol)
.find_map(|constrained_binding| constrained_binding.binding.definition())
}
@ -813,8 +782,8 @@ mod tests {
fn names(table: &PlaceTable) -> Vec<String> {
table
.places()
.filter_map(|expr| Some(expr.as_name()?.to_string()))
.symbols()
.map(|expr| expr.name().to_string())
.collect()
}
@ -852,7 +821,7 @@ mod tests {
let global_table = place_table(&db, scope);
assert_eq!(names(global_table), vec!["foo"]);
let foo = global_table.place_id_by_name("foo").unwrap();
let foo = global_table.symbol_id("foo").unwrap();
let use_def = use_def_map(&db, scope);
let binding = use_def.first_public_binding(foo).unwrap();
@ -884,18 +853,14 @@ mod tests {
assert_eq!(names(global_table), vec!["foo"]);
assert!(
global_table
.place_by_name("foo")
.symbol_by_name("foo")
.is_some_and(|symbol| { symbol.is_bound() && !symbol.is_used() }),
"symbols that are defined get the defined flag"
);
let use_def = use_def_map(&db, scope);
let binding = use_def
.first_public_binding(
global_table
.place_id_by_name("foo")
.expect("symbol to exist"),
)
.first_public_binding(global_table.symbol_id("foo").expect("symbol to exist"))
.unwrap();
assert!(matches!(binding.kind(&db), DefinitionKind::ImportFrom(_)));
}
@ -909,13 +874,13 @@ mod tests {
assert_eq!(names(global_table), vec!["foo", "x"]);
assert!(
global_table
.place_by_name("foo")
.symbol_by_name("foo")
.is_some_and(|symbol| { !symbol.is_bound() && symbol.is_used() }),
"a symbol used but not bound in a scope should have only the used flag"
);
let use_def = use_def_map(&db, scope);
let binding = use_def
.first_public_binding(global_table.place_id_by_name("x").expect("symbol exists"))
.first_public_binding(global_table.symbol_id("x").expect("symbol exists"))
.unwrap();
assert!(matches!(binding.kind(&db), DefinitionKind::Assignment(_)));
}
@ -930,7 +895,7 @@ mod tests {
let use_def = use_def_map(&db, scope);
let binding = use_def
.first_public_binding(global_table.place_id_by_name("x").unwrap())
.first_public_binding(global_table.symbol_id("x").unwrap())
.unwrap();
assert!(matches!(
@ -972,7 +937,7 @@ y = 2
let use_def = index.use_def_map(class_scope_id);
let binding = use_def
.first_public_binding(class_table.place_id_by_name("x").expect("symbol exists"))
.first_public_binding(class_table.symbol_id("x").expect("symbol exists"))
.unwrap();
assert!(matches!(binding.kind(&db), DefinitionKind::Assignment(_)));
}
@ -1009,7 +974,7 @@ y = 2
let use_def = index.use_def_map(function_scope_id);
let binding = use_def
.first_public_binding(function_table.place_id_by_name("x").expect("symbol exists"))
.first_public_binding(function_table.symbol_id("x").expect("symbol exists"))
.unwrap();
assert!(matches!(binding.kind(&db), DefinitionKind::Assignment(_)));
}
@ -1044,31 +1009,19 @@ def f(a: str, /, b: str, c: int = 1, *args, d: int = 2, **kwargs):
let use_def = index.use_def_map(function_scope_id);
for name in ["a", "b", "c", "d"] {
let binding = use_def
.first_public_binding(
function_table
.place_id_by_name(name)
.expect("symbol exists"),
)
.first_public_binding(function_table.symbol_id(name).expect("symbol exists"))
.unwrap();
assert!(matches!(binding.kind(&db), DefinitionKind::Parameter(_)));
}
let args_binding = use_def
.first_public_binding(
function_table
.place_id_by_name("args")
.expect("symbol exists"),
)
.first_public_binding(function_table.symbol_id("args").expect("symbol exists"))
.unwrap();
assert!(matches!(
args_binding.kind(&db),
DefinitionKind::VariadicPositionalParameter(_)
));
let kwargs_binding = use_def
.first_public_binding(
function_table
.place_id_by_name("kwargs")
.expect("symbol exists"),
)
.first_public_binding(function_table.symbol_id("kwargs").expect("symbol exists"))
.unwrap();
assert!(matches!(
kwargs_binding.kind(&db),
@ -1101,27 +1054,19 @@ def f(a: str, /, b: str, c: int = 1, *args, d: int = 2, **kwargs):
let use_def = index.use_def_map(lambda_scope_id);
for name in ["a", "b", "c", "d"] {
let binding = use_def
.first_public_binding(lambda_table.place_id_by_name(name).expect("symbol exists"))
.first_public_binding(lambda_table.symbol_id(name).expect("symbol exists"))
.unwrap();
assert!(matches!(binding.kind(&db), DefinitionKind::Parameter(_)));
}
let args_binding = use_def
.first_public_binding(
lambda_table
.place_id_by_name("args")
.expect("symbol exists"),
)
.first_public_binding(lambda_table.symbol_id("args").expect("symbol exists"))
.unwrap();
assert!(matches!(
args_binding.kind(&db),
DefinitionKind::VariadicPositionalParameter(_)
));
let kwargs_binding = use_def
.first_public_binding(
lambda_table
.place_id_by_name("kwargs")
.expect("symbol exists"),
)
.first_public_binding(lambda_table.symbol_id("kwargs").expect("symbol exists"))
.unwrap();
assert!(matches!(
kwargs_binding.kind(&db),
@ -1169,7 +1114,7 @@ def f(a: str, /, b: str, c: int = 1, *args, d: int = 2, **kwargs):
let binding = use_def
.first_public_binding(
comprehension_symbol_table
.place_id_by_name(name)
.symbol_id(name)
.expect("symbol exists"),
)
.unwrap();
@ -1298,7 +1243,7 @@ with item1 as x, item2 as y:
let use_def = index.use_def_map(FileScopeId::global());
for name in ["x", "y"] {
let binding = use_def
.first_public_binding(global_table.place_id_by_name(name).expect("symbol exists"))
.first_public_binding(global_table.symbol_id(name).expect("symbol exists"))
.expect("Expected with item definition for {name}");
assert!(matches!(binding.kind(&db), DefinitionKind::WithItem(_)));
}
@ -1321,7 +1266,7 @@ with context() as (x, y):
let use_def = index.use_def_map(FileScopeId::global());
for name in ["x", "y"] {
let binding = use_def
.first_public_binding(global_table.place_id_by_name(name).expect("symbol exists"))
.first_public_binding(global_table.symbol_id(name).expect("symbol exists"))
.expect("Expected with item definition for {name}");
assert!(matches!(binding.kind(&db), DefinitionKind::WithItem(_)));
}
@ -1371,11 +1316,7 @@ def func():
let use_def = index.use_def_map(FileScopeId::global());
let binding = use_def
.first_public_binding(
global_table
.place_id_by_name("func")
.expect("symbol exists"),
)
.first_public_binding(global_table.symbol_id("func").expect("symbol exists"))
.unwrap();
assert!(matches!(binding.kind(&db), DefinitionKind::Function(_)));
}
@ -1452,7 +1393,7 @@ class C[T]:
assert_eq!(names(&ann_table), vec!["T"]);
assert!(
ann_table
.place_by_name("T")
.symbol_by_name("T")
.is_some_and(|s| s.is_bound() && !s.is_used()),
"type parameters are defined by the scope that introduces them"
);
@ -1600,7 +1541,7 @@ match subject:
let global_scope_id = global_scope(&db, file);
let global_table = place_table(&db, global_scope_id);
assert!(global_table.place_by_name("Foo").unwrap().is_used());
assert!(global_table.symbol_by_name("Foo").unwrap().is_used());
assert_eq!(
names(global_table),
vec![
@ -1624,7 +1565,7 @@ match subject:
("l", 1),
] {
let binding = use_def
.first_public_binding(global_table.place_id_by_name(name).expect("symbol exists"))
.first_public_binding(global_table.symbol_id(name).expect("symbol exists"))
.expect("Expected with item definition for {name}");
if let DefinitionKind::MatchPattern(pattern) = binding.kind(&db) {
assert_eq!(pattern.index(), expected_index);
@ -1654,7 +1595,7 @@ match 1:
let use_def = use_def_map(&db, global_scope_id);
for (name, expected_index) in [("first", 0), ("second", 0)] {
let binding = use_def
.first_public_binding(global_table.place_id_by_name(name).expect("symbol exists"))
.first_public_binding(global_table.symbol_id(name).expect("symbol exists"))
.expect("Expected with item definition for {name}");
if let DefinitionKind::MatchPattern(pattern) = binding.kind(&db) {
assert_eq!(pattern.index(), expected_index);
@ -1674,7 +1615,7 @@ match 1:
let use_def = use_def_map(&db, scope);
let binding = use_def
.first_public_binding(global_table.place_id_by_name("x").unwrap())
.first_public_binding(global_table.symbol_id("x").unwrap())
.unwrap();
assert!(matches!(binding.kind(&db), DefinitionKind::For(_)));
@ -1690,10 +1631,10 @@ match 1:
let use_def = use_def_map(&db, scope);
let x_binding = use_def
.first_public_binding(global_table.place_id_by_name("x").unwrap())
.first_public_binding(global_table.symbol_id("x").unwrap())
.unwrap();
let y_binding = use_def
.first_public_binding(global_table.place_id_by_name("y").unwrap())
.first_public_binding(global_table.symbol_id("y").unwrap())
.unwrap();
assert!(matches!(x_binding.kind(&db), DefinitionKind::For(_)));
@ -1710,7 +1651,7 @@ match 1:
let use_def = use_def_map(&db, scope);
let binding = use_def
.first_public_binding(global_table.place_id_by_name("a").unwrap())
.first_public_binding(global_table.symbol_id("a").unwrap())
.unwrap();
assert!(matches!(binding.kind(&db), DefinitionKind::For(_)));