mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-29 13:24:57 +00:00
[ty] Split ScopedPlaceId
into ScopedSymbolId
and ScopedMemberId
(#19497)
This commit is contained in:
parent
f722bfa9e6
commit
b033fb6bfd
30 changed files with 2454 additions and 1647 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -4305,7 +4305,6 @@ dependencies = [
|
||||||
"strum_macros",
|
"strum_macros",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"test-case",
|
"test-case",
|
||||||
"thin-vec",
|
|
||||||
"thiserror 2.0.12",
|
"thiserror 2.0.12",
|
||||||
"tracing",
|
"tracing",
|
||||||
"ty_python_semantic",
|
"ty_python_semantic",
|
||||||
|
|
|
@ -166,7 +166,6 @@ strum_macros = { version = "0.27.0" }
|
||||||
syn = { version = "2.0.55" }
|
syn = { version = "2.0.55" }
|
||||||
tempfile = { version = "3.9.0" }
|
tempfile = { version = "3.9.0" }
|
||||||
test-case = { version = "3.3.1" }
|
test-case = { version = "3.3.1" }
|
||||||
thin-vec = { version = "0.2.14" }
|
|
||||||
thiserror = { version = "2.0.0" }
|
thiserror = { version = "2.0.0" }
|
||||||
tikv-jemallocator = { version = "0.6.0" }
|
tikv-jemallocator = { version = "0.6.0" }
|
||||||
toml = { version = "0.9.0" }
|
toml = { version = "0.9.0" }
|
||||||
|
|
|
@ -29,6 +29,10 @@ impl Name {
|
||||||
Self(compact_str::CompactString::const_new(name))
|
Self(compact_str::CompactString::const_new(name))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn shrink_to_fit(&mut self) {
|
||||||
|
self.0.shrink_to_fit();
|
||||||
|
}
|
||||||
|
|
||||||
pub fn as_str(&self) -> &str {
|
pub fn as_str(&self) -> &str {
|
||||||
self.0.as_str()
|
self.0.as_str()
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,6 @@ indexmap = { workspace = true }
|
||||||
itertools = { workspace = true }
|
itertools = { workspace = true }
|
||||||
ordermap = { workspace = true }
|
ordermap = { workspace = true }
|
||||||
salsa = { workspace = true, features = ["compact_str"] }
|
salsa = { workspace = true, features = ["compact_str"] }
|
||||||
thin-vec = { workspace = true }
|
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
rustc-hash = { workspace = true }
|
rustc-hash = { workspace = true }
|
||||||
|
|
|
@ -3,7 +3,8 @@ use ruff_db::files::File;
|
||||||
use crate::dunder_all::dunder_all_names;
|
use crate::dunder_all::dunder_all_names;
|
||||||
use crate::module_resolver::file_to_module;
|
use crate::module_resolver::file_to_module;
|
||||||
use crate::semantic_index::definition::{Definition, DefinitionState};
|
use crate::semantic_index::definition::{Definition, DefinitionState};
|
||||||
use crate::semantic_index::place::{PlaceExpr, ScopeId, ScopedPlaceId};
|
use crate::semantic_index::place::{PlaceExprRef, ScopedPlaceId};
|
||||||
|
use crate::semantic_index::scope::ScopeId;
|
||||||
use crate::semantic_index::{
|
use crate::semantic_index::{
|
||||||
BindingWithConstraints, BindingWithConstraintsIterator, DeclarationsIterator, place_table,
|
BindingWithConstraints, BindingWithConstraintsIterator, DeclarationsIterator, place_table,
|
||||||
};
|
};
|
||||||
|
@ -214,13 +215,13 @@ pub(crate) fn symbol<'db>(
|
||||||
pub(crate) fn place<'db>(
|
pub(crate) fn place<'db>(
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
scope: ScopeId<'db>,
|
scope: ScopeId<'db>,
|
||||||
expr: &PlaceExpr,
|
member: PlaceExprRef,
|
||||||
considered_definitions: ConsideredDefinitions,
|
considered_definitions: ConsideredDefinitions,
|
||||||
) -> PlaceAndQualifiers<'db> {
|
) -> PlaceAndQualifiers<'db> {
|
||||||
place_impl(
|
place_impl(
|
||||||
db,
|
db,
|
||||||
scope,
|
scope,
|
||||||
expr,
|
member,
|
||||||
RequiresExplicitReExport::No,
|
RequiresExplicitReExport::No,
|
||||||
considered_definitions,
|
considered_definitions,
|
||||||
)
|
)
|
||||||
|
@ -234,12 +235,12 @@ pub(crate) fn class_symbol<'db>(
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> PlaceAndQualifiers<'db> {
|
) -> PlaceAndQualifiers<'db> {
|
||||||
place_table(db, scope)
|
place_table(db, scope)
|
||||||
.place_id_by_name(name)
|
.symbol_id(name)
|
||||||
.map(|place| {
|
.map(|symbol_id| {
|
||||||
let place_and_quals = place_by_id(
|
let place_and_quals = place_by_id(
|
||||||
db,
|
db,
|
||||||
scope,
|
scope,
|
||||||
place,
|
symbol_id.into(),
|
||||||
RequiresExplicitReExport::No,
|
RequiresExplicitReExport::No,
|
||||||
ConsideredDefinitions::EndOfScope,
|
ConsideredDefinitions::EndOfScope,
|
||||||
);
|
);
|
||||||
|
@ -256,7 +257,7 @@ pub(crate) fn class_symbol<'db>(
|
||||||
{
|
{
|
||||||
// Otherwise, we need to check if the symbol has bindings
|
// Otherwise, we need to check if the symbol has bindings
|
||||||
let use_def = use_def_map(db, scope);
|
let use_def = use_def_map(db, scope);
|
||||||
let bindings = use_def.end_of_scope_bindings(place);
|
let bindings = use_def.end_of_scope_symbol_bindings(symbol_id);
|
||||||
let inferred = place_from_bindings_impl(db, bindings, RequiresExplicitReExport::No);
|
let inferred = place_from_bindings_impl(db, bindings, RequiresExplicitReExport::No);
|
||||||
|
|
||||||
// TODO: we should not need to calculate inferred type second time. This is a temporary
|
// TODO: we should not need to calculate inferred type second time. This is a temporary
|
||||||
|
@ -763,10 +764,12 @@ fn place_by_id<'db>(
|
||||||
// `TYPE_CHECKING` is a special variable that should only be assigned `False`
|
// `TYPE_CHECKING` is a special variable that should only be assigned `False`
|
||||||
// at runtime, but is always considered `True` in type checking.
|
// at runtime, but is always considered `True` in type checking.
|
||||||
// See mdtest/known_constants.md#user-defined-type_checking for details.
|
// See mdtest/known_constants.md#user-defined-type_checking for details.
|
||||||
let is_considered_non_modifiable = place_table(db, scope)
|
let is_considered_non_modifiable = place_id.as_symbol().is_some_and(|symbol_id| {
|
||||||
.place_expr(place_id)
|
matches!(
|
||||||
.expr
|
place_table(db, scope).symbol(symbol_id).name().as_str(),
|
||||||
.is_name_and(|name| matches!(name, "__slots__" | "TYPE_CHECKING"));
|
"__slots__" | "TYPE_CHECKING"
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
if scope.file(db).is_stub(db) || scope.scope(db).visibility().is_private() {
|
if scope.file(db).is_stub(db) || scope.scope(db).visibility().is_private() {
|
||||||
// We generally trust module-level undeclared places in stubs and do not union
|
// We generally trust module-level undeclared places in stubs and do not union
|
||||||
|
@ -832,12 +835,12 @@ fn symbol_impl<'db>(
|
||||||
}
|
}
|
||||||
|
|
||||||
place_table(db, scope)
|
place_table(db, scope)
|
||||||
.place_id_by_name(name)
|
.symbol_id(name)
|
||||||
.map(|symbol| {
|
.map(|symbol| {
|
||||||
place_by_id(
|
place_by_id(
|
||||||
db,
|
db,
|
||||||
scope,
|
scope,
|
||||||
symbol,
|
symbol.into(),
|
||||||
requires_explicit_reexport,
|
requires_explicit_reexport,
|
||||||
considered_definitions,
|
considered_definitions,
|
||||||
)
|
)
|
||||||
|
@ -845,18 +848,17 @@ fn symbol_impl<'db>(
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implementation of [`place`].
|
|
||||||
fn place_impl<'db>(
|
fn place_impl<'db>(
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
scope: ScopeId<'db>,
|
scope: ScopeId<'db>,
|
||||||
expr: &PlaceExpr,
|
place: PlaceExprRef,
|
||||||
requires_explicit_reexport: RequiresExplicitReExport,
|
requires_explicit_reexport: RequiresExplicitReExport,
|
||||||
considered_definitions: ConsideredDefinitions,
|
considered_definitions: ConsideredDefinitions,
|
||||||
) -> PlaceAndQualifiers<'db> {
|
) -> PlaceAndQualifiers<'db> {
|
||||||
let _span = tracing::trace_span!("place", ?expr).entered();
|
let _span = tracing::trace_span!("place_impl", ?place).entered();
|
||||||
|
|
||||||
place_table(db, scope)
|
place_table(db, scope)
|
||||||
.place_id_by_expr(expr)
|
.place_id(place)
|
||||||
.map(|place| {
|
.map(|place| {
|
||||||
place_by_id(
|
place_by_id(
|
||||||
db,
|
db,
|
||||||
|
@ -1265,7 +1267,8 @@ fn is_reexported(db: &dyn Db, definition: Definition<'_>) -> bool {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
let table = place_table(db, definition.scope(db));
|
let table = place_table(db, definition.scope(db));
|
||||||
let symbol_name = table.place_expr(definition.place(db)).expect_name();
|
let symbol_id = definition.place(db).expect_symbol();
|
||||||
|
let symbol_name = table.symbol(symbol_id).name();
|
||||||
all_names.contains(symbol_name)
|
all_names.contains(symbol_name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1274,19 +1277,19 @@ mod implicit_globals {
|
||||||
|
|
||||||
use crate::db::Db;
|
use crate::db::Db;
|
||||||
use crate::place::PlaceAndQualifiers;
|
use crate::place::PlaceAndQualifiers;
|
||||||
use crate::semantic_index::place::PlaceExpr;
|
use crate::semantic_index::symbol::Symbol;
|
||||||
use crate::semantic_index::{self, place_table, use_def_map};
|
use crate::semantic_index::{place_table, use_def_map};
|
||||||
use crate::types::{KnownClass, Type};
|
use crate::types::{KnownClass, Type};
|
||||||
|
|
||||||
use super::{Place, PlaceFromDeclarationsResult, place_from_declarations};
|
use super::{Place, PlaceFromDeclarationsResult, place_from_declarations};
|
||||||
|
|
||||||
pub(crate) fn module_type_implicit_global_declaration<'db>(
|
pub(crate) fn module_type_implicit_global_declaration<'db>(
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
expr: &PlaceExpr,
|
name: &str,
|
||||||
) -> PlaceFromDeclarationsResult<'db> {
|
) -> PlaceFromDeclarationsResult<'db> {
|
||||||
if !module_type_symbols(db)
|
if !module_type_symbols(db)
|
||||||
.iter()
|
.iter()
|
||||||
.any(|module_type_member| Some(module_type_member) == expr.as_name())
|
.any(|module_type_member| module_type_member == name)
|
||||||
{
|
{
|
||||||
return Ok(Place::Unbound.into());
|
return Ok(Place::Unbound.into());
|
||||||
}
|
}
|
||||||
|
@ -1296,12 +1299,12 @@ mod implicit_globals {
|
||||||
};
|
};
|
||||||
let module_type_scope = module_type_class.body_scope(db);
|
let module_type_scope = module_type_class.body_scope(db);
|
||||||
let place_table = place_table(db, module_type_scope);
|
let place_table = place_table(db, module_type_scope);
|
||||||
let Some(place_id) = place_table.place_id_by_expr(expr) else {
|
let Some(symbol_id) = place_table.symbol_id(name) else {
|
||||||
return Ok(Place::Unbound.into());
|
return Ok(Place::Unbound.into());
|
||||||
};
|
};
|
||||||
place_from_declarations(
|
place_from_declarations(
|
||||||
db,
|
db,
|
||||||
use_def_map(db, module_type_scope).end_of_scope_declarations(place_id),
|
use_def_map(db, module_type_scope).end_of_scope_symbol_declarations(symbol_id),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1380,11 +1383,14 @@ mod implicit_globals {
|
||||||
let module_type_symbol_table = place_table(db, module_type_scope);
|
let module_type_symbol_table = place_table(db, module_type_scope);
|
||||||
|
|
||||||
module_type_symbol_table
|
module_type_symbol_table
|
||||||
.places()
|
.symbols()
|
||||||
.filter(|place| place.is_declared() && place.is_name())
|
.filter(|symbol| symbol.is_declared())
|
||||||
.map(semantic_index::place::PlaceExprWithFlags::expect_name)
|
.map(Symbol::name)
|
||||||
.filter(|symbol_name| {
|
.filter(|symbol_name| {
|
||||||
!matches!(&***symbol_name, "__dict__" | "__getattr__" | "__init__")
|
!matches!(
|
||||||
|
symbol_name.as_str(),
|
||||||
|
"__dict__" | "__getattr__" | "__init__"
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.cloned()
|
.cloned()
|
||||||
.collect()
|
.collect()
|
||||||
|
|
|
@ -20,10 +20,12 @@ use crate::semantic_index::builder::SemanticIndexBuilder;
|
||||||
use crate::semantic_index::definition::{Definition, DefinitionNodeKey, Definitions};
|
use crate::semantic_index::definition::{Definition, DefinitionNodeKey, Definitions};
|
||||||
use crate::semantic_index::expression::Expression;
|
use crate::semantic_index::expression::Expression;
|
||||||
use crate::semantic_index::narrowing_constraints::ScopedNarrowingConstraint;
|
use crate::semantic_index::narrowing_constraints::ScopedNarrowingConstraint;
|
||||||
use crate::semantic_index::place::{
|
use crate::semantic_index::place::{PlaceExprRef, PlaceTable};
|
||||||
FileScopeId, NodeWithScopeKey, NodeWithScopeRef, PlaceExpr, PlaceTable, Scope, ScopeId,
|
pub use crate::semantic_index::scope::FileScopeId;
|
||||||
ScopeKind, ScopedPlaceId,
|
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_index::use_def::{EnclosingSnapshotKey, ScopedEnclosingSnapshotId, UseDefMap};
|
||||||
use crate::semantic_model::HasTrackedScope;
|
use crate::semantic_model::HasTrackedScope;
|
||||||
use crate::util::get_size::untracked_arc_size;
|
use crate::util::get_size::untracked_arc_size;
|
||||||
|
@ -32,11 +34,14 @@ pub mod ast_ids;
|
||||||
mod builder;
|
mod builder;
|
||||||
pub mod definition;
|
pub mod definition;
|
||||||
pub mod expression;
|
pub mod expression;
|
||||||
|
pub(crate) mod member;
|
||||||
pub(crate) mod narrowing_constraints;
|
pub(crate) mod narrowing_constraints;
|
||||||
pub mod place;
|
pub mod place;
|
||||||
pub(crate) mod predicate;
|
pub(crate) mod predicate;
|
||||||
mod re_exports;
|
mod re_exports;
|
||||||
mod reachability_constraints;
|
mod reachability_constraints;
|
||||||
|
pub(crate) mod scope;
|
||||||
|
pub(crate) mod symbol;
|
||||||
mod use_def;
|
mod use_def;
|
||||||
|
|
||||||
pub(crate) use self::use_def::{
|
pub(crate) use self::use_def::{
|
||||||
|
@ -44,8 +49,6 @@ pub(crate) use self::use_def::{
|
||||||
DeclarationWithConstraint, DeclarationsIterator,
|
DeclarationWithConstraint, DeclarationsIterator,
|
||||||
};
|
};
|
||||||
|
|
||||||
type PlaceSet = hashbrown::HashTable<ScopedPlaceId>;
|
|
||||||
|
|
||||||
/// Returns the semantic index for `file`.
|
/// Returns the semantic index for `file`.
|
||||||
///
|
///
|
||||||
/// Prefer using [`symbol_table`] when working with symbols from a single scope.
|
/// 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| {
|
attribute_scopes(db, class_body_scope).filter_map(|function_scope_id| {
|
||||||
let place_table = index.place_table(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];
|
let use_def = &index.use_def_maps[function_scope_id];
|
||||||
Some((
|
Some((
|
||||||
use_def.inner.all_reachable_bindings(place),
|
use_def.inner.all_reachable_member_bindings(member),
|
||||||
function_scope_id,
|
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| {
|
attribute_scopes(db, class_body_scope).filter_map(|function_scope_id| {
|
||||||
let place_table = index.place_table(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];
|
let use_def = &index.use_def_maps[function_scope_id];
|
||||||
Some((
|
Some((
|
||||||
use_def.inner.all_reachable_declarations(place),
|
use_def.inner.all_reachable_member_declarations(member),
|
||||||
function_scope_id,
|
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)
|
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> {
|
pub(crate) enum EnclosingSnapshotResult<'map, 'db> {
|
||||||
FoundConstraint(ScopedNarrowingConstraint),
|
FoundConstraint(ScopedNarrowingConstraint),
|
||||||
FoundBindings(BindingWithConstraintsIterator<'map, 'db>),
|
FoundBindings(BindingWithConstraintsIterator<'map, 'db>),
|
||||||
|
@ -337,22 +308,18 @@ impl<'db> SemanticIndex<'db> {
|
||||||
|
|
||||||
pub(crate) fn symbol_is_global_in_scope(
|
pub(crate) fn symbol_is_global_in_scope(
|
||||||
&self,
|
&self,
|
||||||
symbol: ScopedPlaceId,
|
symbol: ScopedSymbolId,
|
||||||
scope: FileScopeId,
|
scope: FileScopeId,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.place_table(scope)
|
self.place_table(scope).symbol(symbol).is_global()
|
||||||
.place_expr(symbol)
|
|
||||||
.is_marked_global()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn symbol_is_nonlocal_in_scope(
|
pub(crate) fn symbol_is_nonlocal_in_scope(
|
||||||
&self,
|
&self,
|
||||||
symbol: ScopedPlaceId,
|
symbol: ScopedSymbolId,
|
||||||
scope: FileScopeId,
|
scope: FileScopeId,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.place_table(scope)
|
self.place_table(scope).symbol(symbol).is_nonlocal()
|
||||||
.place_expr(symbol)
|
|
||||||
.is_marked_nonlocal()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the id of the parent scope.
|
/// Returns the id of the parent scope.
|
||||||
|
@ -523,7 +490,7 @@ impl<'db> SemanticIndex<'db> {
|
||||||
pub(crate) fn enclosing_snapshot(
|
pub(crate) fn enclosing_snapshot(
|
||||||
&self,
|
&self,
|
||||||
enclosing_scope: FileScopeId,
|
enclosing_scope: FileScopeId,
|
||||||
expr: &PlaceExpr,
|
expr: PlaceExprRef,
|
||||||
nested_scope: FileScopeId,
|
nested_scope: FileScopeId,
|
||||||
) -> EnclosingSnapshotResult<'_, 'db> {
|
) -> EnclosingSnapshotResult<'_, 'db> {
|
||||||
for (ancestor_scope_id, ancestor_scope) in self.ancestor_scopes(nested_scope) {
|
for (ancestor_scope_id, ancestor_scope) in self.ancestor_scopes(nested_scope) {
|
||||||
|
@ -531,13 +498,13 @@ impl<'db> SemanticIndex<'db> {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if !ancestor_scope.is_eager() {
|
if !ancestor_scope.is_eager() {
|
||||||
if expr.is_name() {
|
if let PlaceExprRef::Symbol(symbol) = expr {
|
||||||
if let Some(place_id) =
|
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 {
|
let key = EnclosingSnapshotKey {
|
||||||
enclosing_scope,
|
enclosing_scope,
|
||||||
enclosing_place: place_id,
|
enclosing_place: place_id.into(),
|
||||||
nested_scope,
|
nested_scope,
|
||||||
nested_laziness: ScopeLaziness::Lazy,
|
nested_laziness: ScopeLaziness::Lazy,
|
||||||
};
|
};
|
||||||
|
@ -551,7 +518,7 @@ impl<'db> SemanticIndex<'db> {
|
||||||
return EnclosingSnapshotResult::NoLongerInEagerContext;
|
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;
|
return EnclosingSnapshotResult::NotFound;
|
||||||
};
|
};
|
||||||
let key = EnclosingSnapshotKey {
|
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>,
|
scopes: &'a IndexSlice<FileScopeId, Scope>,
|
||||||
next_id: Option<FileScopeId>,
|
next_id: Option<FileScopeId>,
|
||||||
}
|
}
|
||||||
|
@ -623,7 +590,7 @@ impl<'a> Iterator for AncestorsIter<'a> {
|
||||||
|
|
||||||
impl FusedIterator for AncestorsIter<'_> {}
|
impl FusedIterator for AncestorsIter<'_> {}
|
||||||
|
|
||||||
pub struct VisibleAncestorsIter<'a> {
|
pub(crate) struct VisibleAncestorsIter<'a> {
|
||||||
inner: AncestorsIter<'a>,
|
inner: AncestorsIter<'a>,
|
||||||
starting_scope_kind: ScopeKind,
|
starting_scope_kind: ScopeKind,
|
||||||
yielded_count: usize,
|
yielded_count: usize,
|
||||||
|
@ -670,7 +637,7 @@ impl<'a> Iterator for VisibleAncestorsIter<'a> {
|
||||||
|
|
||||||
impl FusedIterator for VisibleAncestorsIter<'_> {}
|
impl FusedIterator for VisibleAncestorsIter<'_> {}
|
||||||
|
|
||||||
pub struct DescendantsIter<'a> {
|
pub(crate) struct DescendantsIter<'a> {
|
||||||
next_id: FileScopeId,
|
next_id: FileScopeId,
|
||||||
descendants: std::slice::Iter<'a, Scope>,
|
descendants: std::slice::Iter<'a, Scope>,
|
||||||
}
|
}
|
||||||
|
@ -707,7 +674,7 @@ impl FusedIterator for DescendantsIter<'_> {}
|
||||||
|
|
||||||
impl ExactSizeIterator for DescendantsIter<'_> {}
|
impl ExactSizeIterator for DescendantsIter<'_> {}
|
||||||
|
|
||||||
pub struct ChildrenIter<'a> {
|
pub(crate) struct ChildrenIter<'a> {
|
||||||
parent: FileScopeId,
|
parent: FileScopeId,
|
||||||
descendants: DescendantsIter<'a>,
|
descendants: DescendantsIter<'a>,
|
||||||
}
|
}
|
||||||
|
@ -777,13 +744,15 @@ mod tests {
|
||||||
use crate::db::tests::{TestDb, TestDbBuilder};
|
use crate::db::tests::{TestDb, TestDbBuilder};
|
||||||
use crate::semantic_index::ast_ids::{HasScopedUseId, ScopedUseId};
|
use crate::semantic_index::ast_ids::{HasScopedUseId, ScopedUseId};
|
||||||
use crate::semantic_index::definition::{Definition, DefinitionKind};
|
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::use_def::UseDefMap;
|
||||||
use crate::semantic_index::{global_scope, place_table, semantic_index, use_def_map};
|
use crate::semantic_index::{global_scope, place_table, semantic_index, use_def_map};
|
||||||
|
|
||||||
impl UseDefMap<'_> {
|
impl UseDefMap<'_> {
|
||||||
fn first_public_binding(&self, symbol: ScopedPlaceId) -> Option<Definition<'_>> {
|
fn first_public_binding(&self, symbol: ScopedSymbolId) -> Option<Definition<'_>> {
|
||||||
self.end_of_scope_bindings(symbol)
|
self.end_of_scope_symbol_bindings(symbol)
|
||||||
.find_map(|constrained_binding| constrained_binding.binding.definition())
|
.find_map(|constrained_binding| constrained_binding.binding.definition())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -813,8 +782,8 @@ mod tests {
|
||||||
|
|
||||||
fn names(table: &PlaceTable) -> Vec<String> {
|
fn names(table: &PlaceTable) -> Vec<String> {
|
||||||
table
|
table
|
||||||
.places()
|
.symbols()
|
||||||
.filter_map(|expr| Some(expr.as_name()?.to_string()))
|
.map(|expr| expr.name().to_string())
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -852,7 +821,7 @@ mod tests {
|
||||||
let global_table = place_table(&db, scope);
|
let global_table = place_table(&db, scope);
|
||||||
|
|
||||||
assert_eq!(names(global_table), vec!["foo"]);
|
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 use_def = use_def_map(&db, scope);
|
||||||
let binding = use_def.first_public_binding(foo).unwrap();
|
let binding = use_def.first_public_binding(foo).unwrap();
|
||||||
|
@ -884,18 +853,14 @@ mod tests {
|
||||||
assert_eq!(names(global_table), vec!["foo"]);
|
assert_eq!(names(global_table), vec!["foo"]);
|
||||||
assert!(
|
assert!(
|
||||||
global_table
|
global_table
|
||||||
.place_by_name("foo")
|
.symbol_by_name("foo")
|
||||||
.is_some_and(|symbol| { symbol.is_bound() && !symbol.is_used() }),
|
.is_some_and(|symbol| { symbol.is_bound() && !symbol.is_used() }),
|
||||||
"symbols that are defined get the defined flag"
|
"symbols that are defined get the defined flag"
|
||||||
);
|
);
|
||||||
|
|
||||||
let use_def = use_def_map(&db, scope);
|
let use_def = use_def_map(&db, scope);
|
||||||
let binding = use_def
|
let binding = use_def
|
||||||
.first_public_binding(
|
.first_public_binding(global_table.symbol_id("foo").expect("symbol to exist"))
|
||||||
global_table
|
|
||||||
.place_id_by_name("foo")
|
|
||||||
.expect("symbol to exist"),
|
|
||||||
)
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(matches!(binding.kind(&db), DefinitionKind::ImportFrom(_)));
|
assert!(matches!(binding.kind(&db), DefinitionKind::ImportFrom(_)));
|
||||||
}
|
}
|
||||||
|
@ -909,13 +874,13 @@ mod tests {
|
||||||
assert_eq!(names(global_table), vec!["foo", "x"]);
|
assert_eq!(names(global_table), vec!["foo", "x"]);
|
||||||
assert!(
|
assert!(
|
||||||
global_table
|
global_table
|
||||||
.place_by_name("foo")
|
.symbol_by_name("foo")
|
||||||
.is_some_and(|symbol| { !symbol.is_bound() && symbol.is_used() }),
|
.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"
|
"a symbol used but not bound in a scope should have only the used flag"
|
||||||
);
|
);
|
||||||
let use_def = use_def_map(&db, scope);
|
let use_def = use_def_map(&db, scope);
|
||||||
let binding = use_def
|
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();
|
.unwrap();
|
||||||
assert!(matches!(binding.kind(&db), DefinitionKind::Assignment(_)));
|
assert!(matches!(binding.kind(&db), DefinitionKind::Assignment(_)));
|
||||||
}
|
}
|
||||||
|
@ -930,7 +895,7 @@ mod tests {
|
||||||
|
|
||||||
let use_def = use_def_map(&db, scope);
|
let use_def = use_def_map(&db, scope);
|
||||||
let binding = use_def
|
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();
|
.unwrap();
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
|
@ -972,7 +937,7 @@ y = 2
|
||||||
|
|
||||||
let use_def = index.use_def_map(class_scope_id);
|
let use_def = index.use_def_map(class_scope_id);
|
||||||
let binding = use_def
|
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();
|
.unwrap();
|
||||||
assert!(matches!(binding.kind(&db), DefinitionKind::Assignment(_)));
|
assert!(matches!(binding.kind(&db), DefinitionKind::Assignment(_)));
|
||||||
}
|
}
|
||||||
|
@ -1009,7 +974,7 @@ y = 2
|
||||||
|
|
||||||
let use_def = index.use_def_map(function_scope_id);
|
let use_def = index.use_def_map(function_scope_id);
|
||||||
let binding = use_def
|
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();
|
.unwrap();
|
||||||
assert!(matches!(binding.kind(&db), DefinitionKind::Assignment(_)));
|
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);
|
let use_def = index.use_def_map(function_scope_id);
|
||||||
for name in ["a", "b", "c", "d"] {
|
for name in ["a", "b", "c", "d"] {
|
||||||
let binding = use_def
|
let binding = use_def
|
||||||
.first_public_binding(
|
.first_public_binding(function_table.symbol_id(name).expect("symbol exists"))
|
||||||
function_table
|
|
||||||
.place_id_by_name(name)
|
|
||||||
.expect("symbol exists"),
|
|
||||||
)
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(matches!(binding.kind(&db), DefinitionKind::Parameter(_)));
|
assert!(matches!(binding.kind(&db), DefinitionKind::Parameter(_)));
|
||||||
}
|
}
|
||||||
let args_binding = use_def
|
let args_binding = use_def
|
||||||
.first_public_binding(
|
.first_public_binding(function_table.symbol_id("args").expect("symbol exists"))
|
||||||
function_table
|
|
||||||
.place_id_by_name("args")
|
|
||||||
.expect("symbol exists"),
|
|
||||||
)
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
args_binding.kind(&db),
|
args_binding.kind(&db),
|
||||||
DefinitionKind::VariadicPositionalParameter(_)
|
DefinitionKind::VariadicPositionalParameter(_)
|
||||||
));
|
));
|
||||||
let kwargs_binding = use_def
|
let kwargs_binding = use_def
|
||||||
.first_public_binding(
|
.first_public_binding(function_table.symbol_id("kwargs").expect("symbol exists"))
|
||||||
function_table
|
|
||||||
.place_id_by_name("kwargs")
|
|
||||||
.expect("symbol exists"),
|
|
||||||
)
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
kwargs_binding.kind(&db),
|
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);
|
let use_def = index.use_def_map(lambda_scope_id);
|
||||||
for name in ["a", "b", "c", "d"] {
|
for name in ["a", "b", "c", "d"] {
|
||||||
let binding = use_def
|
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();
|
.unwrap();
|
||||||
assert!(matches!(binding.kind(&db), DefinitionKind::Parameter(_)));
|
assert!(matches!(binding.kind(&db), DefinitionKind::Parameter(_)));
|
||||||
}
|
}
|
||||||
let args_binding = use_def
|
let args_binding = use_def
|
||||||
.first_public_binding(
|
.first_public_binding(lambda_table.symbol_id("args").expect("symbol exists"))
|
||||||
lambda_table
|
|
||||||
.place_id_by_name("args")
|
|
||||||
.expect("symbol exists"),
|
|
||||||
)
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
args_binding.kind(&db),
|
args_binding.kind(&db),
|
||||||
DefinitionKind::VariadicPositionalParameter(_)
|
DefinitionKind::VariadicPositionalParameter(_)
|
||||||
));
|
));
|
||||||
let kwargs_binding = use_def
|
let kwargs_binding = use_def
|
||||||
.first_public_binding(
|
.first_public_binding(lambda_table.symbol_id("kwargs").expect("symbol exists"))
|
||||||
lambda_table
|
|
||||||
.place_id_by_name("kwargs")
|
|
||||||
.expect("symbol exists"),
|
|
||||||
)
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
kwargs_binding.kind(&db),
|
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
|
let binding = use_def
|
||||||
.first_public_binding(
|
.first_public_binding(
|
||||||
comprehension_symbol_table
|
comprehension_symbol_table
|
||||||
.place_id_by_name(name)
|
.symbol_id(name)
|
||||||
.expect("symbol exists"),
|
.expect("symbol exists"),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -1298,7 +1243,7 @@ with item1 as x, item2 as y:
|
||||||
let use_def = index.use_def_map(FileScopeId::global());
|
let use_def = index.use_def_map(FileScopeId::global());
|
||||||
for name in ["x", "y"] {
|
for name in ["x", "y"] {
|
||||||
let binding = use_def
|
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}");
|
.expect("Expected with item definition for {name}");
|
||||||
assert!(matches!(binding.kind(&db), DefinitionKind::WithItem(_)));
|
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());
|
let use_def = index.use_def_map(FileScopeId::global());
|
||||||
for name in ["x", "y"] {
|
for name in ["x", "y"] {
|
||||||
let binding = use_def
|
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}");
|
.expect("Expected with item definition for {name}");
|
||||||
assert!(matches!(binding.kind(&db), DefinitionKind::WithItem(_)));
|
assert!(matches!(binding.kind(&db), DefinitionKind::WithItem(_)));
|
||||||
}
|
}
|
||||||
|
@ -1371,11 +1316,7 @@ def func():
|
||||||
|
|
||||||
let use_def = index.use_def_map(FileScopeId::global());
|
let use_def = index.use_def_map(FileScopeId::global());
|
||||||
let binding = use_def
|
let binding = use_def
|
||||||
.first_public_binding(
|
.first_public_binding(global_table.symbol_id("func").expect("symbol exists"))
|
||||||
global_table
|
|
||||||
.place_id_by_name("func")
|
|
||||||
.expect("symbol exists"),
|
|
||||||
)
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(matches!(binding.kind(&db), DefinitionKind::Function(_)));
|
assert!(matches!(binding.kind(&db), DefinitionKind::Function(_)));
|
||||||
}
|
}
|
||||||
|
@ -1452,7 +1393,7 @@ class C[T]:
|
||||||
assert_eq!(names(&ann_table), vec!["T"]);
|
assert_eq!(names(&ann_table), vec!["T"]);
|
||||||
assert!(
|
assert!(
|
||||||
ann_table
|
ann_table
|
||||||
.place_by_name("T")
|
.symbol_by_name("T")
|
||||||
.is_some_and(|s| s.is_bound() && !s.is_used()),
|
.is_some_and(|s| s.is_bound() && !s.is_used()),
|
||||||
"type parameters are defined by the scope that introduces them"
|
"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_scope_id = global_scope(&db, file);
|
||||||
let global_table = place_table(&db, global_scope_id);
|
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!(
|
assert_eq!(
|
||||||
names(global_table),
|
names(global_table),
|
||||||
vec![
|
vec![
|
||||||
|
@ -1624,7 +1565,7 @@ match subject:
|
||||||
("l", 1),
|
("l", 1),
|
||||||
] {
|
] {
|
||||||
let binding = use_def
|
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}");
|
.expect("Expected with item definition for {name}");
|
||||||
if let DefinitionKind::MatchPattern(pattern) = binding.kind(&db) {
|
if let DefinitionKind::MatchPattern(pattern) = binding.kind(&db) {
|
||||||
assert_eq!(pattern.index(), expected_index);
|
assert_eq!(pattern.index(), expected_index);
|
||||||
|
@ -1654,7 +1595,7 @@ match 1:
|
||||||
let use_def = use_def_map(&db, global_scope_id);
|
let use_def = use_def_map(&db, global_scope_id);
|
||||||
for (name, expected_index) in [("first", 0), ("second", 0)] {
|
for (name, expected_index) in [("first", 0), ("second", 0)] {
|
||||||
let binding = use_def
|
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}");
|
.expect("Expected with item definition for {name}");
|
||||||
if let DefinitionKind::MatchPattern(pattern) = binding.kind(&db) {
|
if let DefinitionKind::MatchPattern(pattern) = binding.kind(&db) {
|
||||||
assert_eq!(pattern.index(), expected_index);
|
assert_eq!(pattern.index(), expected_index);
|
||||||
|
@ -1674,7 +1615,7 @@ match 1:
|
||||||
|
|
||||||
let use_def = use_def_map(&db, scope);
|
let use_def = use_def_map(&db, scope);
|
||||||
let binding = use_def
|
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();
|
.unwrap();
|
||||||
|
|
||||||
assert!(matches!(binding.kind(&db), DefinitionKind::For(_)));
|
assert!(matches!(binding.kind(&db), DefinitionKind::For(_)));
|
||||||
|
@ -1690,10 +1631,10 @@ match 1:
|
||||||
|
|
||||||
let use_def = use_def_map(&db, scope);
|
let use_def = use_def_map(&db, scope);
|
||||||
let x_binding = use_def
|
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();
|
.unwrap();
|
||||||
let y_binding = use_def
|
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();
|
.unwrap();
|
||||||
|
|
||||||
assert!(matches!(x_binding.kind(&db), DefinitionKind::For(_)));
|
assert!(matches!(x_binding.kind(&db), DefinitionKind::For(_)));
|
||||||
|
@ -1710,7 +1651,7 @@ match 1:
|
||||||
|
|
||||||
let use_def = use_def_map(&db, scope);
|
let use_def = use_def_map(&db, scope);
|
||||||
let binding = use_def
|
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();
|
.unwrap();
|
||||||
|
|
||||||
assert!(matches!(binding.kind(&db), DefinitionKind::For(_)));
|
assert!(matches!(binding.kind(&db), DefinitionKind::For(_)));
|
||||||
|
|
|
@ -6,7 +6,7 @@ use ruff_python_ast::ExprRef;
|
||||||
|
|
||||||
use crate::Db;
|
use crate::Db;
|
||||||
use crate::semantic_index::ast_ids::node_key::ExpressionNodeKey;
|
use crate::semantic_index::ast_ids::node_key::ExpressionNodeKey;
|
||||||
use crate::semantic_index::place::ScopeId;
|
use crate::semantic_index::scope::ScopeId;
|
||||||
use crate::semantic_index::semantic_index;
|
use crate::semantic_index::semantic_index;
|
||||||
|
|
||||||
/// AST ids for a single scope.
|
/// AST ids for a single scope.
|
||||||
|
@ -40,7 +40,7 @@ fn ast_ids<'db>(db: &'db dyn Db, scope: ScopeId) -> &'db AstIds {
|
||||||
semantic_index(db, scope.file(db)).ast_ids(scope.file_scope_id(db))
|
semantic_index(db, scope.file(db)).ast_ids(scope.file_scope_id(db))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Uniquely identifies a use of a name in a [`crate::semantic_index::place::FileScopeId`].
|
/// Uniquely identifies a use of a name in a [`crate::semantic_index::FileScopeId`].
|
||||||
#[newtype_index]
|
#[newtype_index]
|
||||||
#[derive(get_size2::GetSize)]
|
#[derive(get_size2::GetSize)]
|
||||||
pub struct ScopedUseId;
|
pub struct ScopedUseId;
|
||||||
|
|
|
@ -30,10 +30,7 @@ use crate::semantic_index::definition::{
|
||||||
StarImportDefinitionNodeRef, WithItemDefinitionNodeRef,
|
StarImportDefinitionNodeRef, WithItemDefinitionNodeRef,
|
||||||
};
|
};
|
||||||
use crate::semantic_index::expression::{Expression, ExpressionKind};
|
use crate::semantic_index::expression::{Expression, ExpressionKind};
|
||||||
use crate::semantic_index::place::{
|
use crate::semantic_index::place::{PlaceExpr, PlaceTableBuilder, ScopedPlaceId};
|
||||||
FileScopeId, NodeWithScopeKey, NodeWithScopeKind, NodeWithScopeRef, PlaceExpr,
|
|
||||||
PlaceExprWithFlags, PlaceTableBuilder, Scope, ScopeId, ScopeKind, ScopedPlaceId,
|
|
||||||
};
|
|
||||||
use crate::semantic_index::predicate::{
|
use crate::semantic_index::predicate::{
|
||||||
CallableAndCallExpr, ClassPatternKind, PatternPredicate, PatternPredicateKind, Predicate,
|
CallableAndCallExpr, ClassPatternKind, PatternPredicate, PatternPredicateKind, Predicate,
|
||||||
PredicateNode, PredicateOrLiteral, ScopedPredicateId, StarImportPlaceholderPredicate,
|
PredicateNode, PredicateOrLiteral, ScopedPredicateId, StarImportPlaceholderPredicate,
|
||||||
|
@ -42,10 +39,15 @@ use crate::semantic_index::re_exports::exported_names;
|
||||||
use crate::semantic_index::reachability_constraints::{
|
use crate::semantic_index::reachability_constraints::{
|
||||||
ReachabilityConstraintsBuilder, ScopedReachabilityConstraintId,
|
ReachabilityConstraintsBuilder, ScopedReachabilityConstraintId,
|
||||||
};
|
};
|
||||||
|
use crate::semantic_index::scope::{
|
||||||
|
FileScopeId, NodeWithScopeKey, NodeWithScopeKind, NodeWithScopeRef,
|
||||||
|
};
|
||||||
|
use crate::semantic_index::scope::{Scope, ScopeId, ScopeKind, ScopeLaziness};
|
||||||
|
use crate::semantic_index::symbol::{ScopedSymbolId, Symbol};
|
||||||
use crate::semantic_index::use_def::{
|
use crate::semantic_index::use_def::{
|
||||||
EnclosingSnapshotKey, FlowSnapshot, ScopedEnclosingSnapshotId, UseDefMapBuilder,
|
EnclosingSnapshotKey, FlowSnapshot, ScopedEnclosingSnapshotId, UseDefMapBuilder,
|
||||||
};
|
};
|
||||||
use crate::semantic_index::{ArcUseDefMap, ExpressionsScopeMap, ScopeLaziness, SemanticIndex};
|
use crate::semantic_index::{ArcUseDefMap, ExpressionsScopeMap, SemanticIndex};
|
||||||
use crate::semantic_model::HasTrackedScope;
|
use crate::semantic_model::HasTrackedScope;
|
||||||
use crate::unpack::{Unpack, UnpackKind, UnpackPosition, UnpackValue};
|
use crate::unpack::{Unpack, UnpackKind, UnpackPosition, UnpackValue};
|
||||||
use crate::{Db, Program};
|
use crate::{Db, Program};
|
||||||
|
@ -295,18 +297,16 @@ impl<'db, 'ast> SemanticIndexBuilder<'db, 'ast> {
|
||||||
let enclosing_scope_kind = self.scopes[enclosing_scope_id].kind();
|
let enclosing_scope_kind = self.scopes[enclosing_scope_id].kind();
|
||||||
let enclosing_place_table = &self.place_tables[enclosing_scope_id];
|
let enclosing_place_table = &self.place_tables[enclosing_scope_id];
|
||||||
|
|
||||||
for nested_place in self.place_tables[popped_scope_id].places() {
|
for nested_place in self.place_tables[popped_scope_id].iter() {
|
||||||
// Skip this place if this enclosing scope doesn't contain any bindings for it.
|
// Skip this place if this enclosing scope doesn't contain any bindings for it.
|
||||||
// Note that even if this place is bound in the popped scope,
|
// Note that even if this place is bound in the popped scope,
|
||||||
// it may refer to the enclosing scope bindings
|
// it may refer to the enclosing scope bindings
|
||||||
// so we also need to snapshot the bindings of the enclosing scope.
|
// so we also need to snapshot the bindings of the enclosing scope.
|
||||||
|
|
||||||
let Some(enclosing_place_id) =
|
let Some(enclosing_place_id) = enclosing_place_table.place_id(nested_place) else {
|
||||||
enclosing_place_table.place_id_by_expr(&nested_place.expr)
|
|
||||||
else {
|
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
let enclosing_place = enclosing_place_table.place_expr(enclosing_place_id);
|
let enclosing_place = enclosing_place_table.place(enclosing_place_id);
|
||||||
|
|
||||||
// Snapshot the state of this place that are visible at this point in this
|
// Snapshot the state of this place that are visible at this point in this
|
||||||
// enclosing scope.
|
// enclosing scope.
|
||||||
|
@ -332,11 +332,7 @@ impl<'db, 'ast> SemanticIndexBuilder<'db, 'ast> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bound_scope(
|
fn bound_scope(&self, enclosing_scope: FileScopeId, symbol: &Symbol) -> Option<FileScopeId> {
|
||||||
&self,
|
|
||||||
enclosing_scope: FileScopeId,
|
|
||||||
place_expr: &PlaceExpr,
|
|
||||||
) -> Option<FileScopeId> {
|
|
||||||
self.scope_stack
|
self.scope_stack
|
||||||
.iter()
|
.iter()
|
||||||
.rev()
|
.rev()
|
||||||
|
@ -344,12 +340,8 @@ impl<'db, 'ast> SemanticIndexBuilder<'db, 'ast> {
|
||||||
.find_map(|scope_info| {
|
.find_map(|scope_info| {
|
||||||
let scope_id = scope_info.file_scope_id;
|
let scope_id = scope_info.file_scope_id;
|
||||||
let place_table = &self.place_tables[scope_id];
|
let place_table = &self.place_tables[scope_id];
|
||||||
let place_id = place_table.place_id_by_expr(place_expr)?;
|
let place_id = place_table.symbol_id(symbol.name())?;
|
||||||
if place_table.place_expr(place_id).is_bound() {
|
place_table.place(place_id).is_bound().then_some(scope_id)
|
||||||
Some(scope_id)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -360,13 +352,11 @@ impl<'db, 'ast> SemanticIndexBuilder<'db, 'ast> {
|
||||||
let enclosing_scope_kind = self.scopes[enclosing_scope_id].kind();
|
let enclosing_scope_kind = self.scopes[enclosing_scope_id].kind();
|
||||||
let enclosing_place_table = &self.place_tables[enclosing_scope_id];
|
let enclosing_place_table = &self.place_tables[enclosing_scope_id];
|
||||||
|
|
||||||
for nested_place in self.place_tables[popped_scope_id].places() {
|
// We don't record lazy snapshots of attributes or subscripts, because these are difficult to track as they modify.
|
||||||
// We don't record lazy snapshots of attributes or subscripts, because these are difficult to track as they modify.
|
for nested_symbol in self.place_tables[popped_scope_id].symbols() {
|
||||||
// For the same reason, symbols declared as nonlocal or global are not recorded.
|
// For the same reason, symbols declared as nonlocal or global are not recorded.
|
||||||
// Also, if the enclosing scope allows its members to be modified from elsewhere, the snapshot will not be recorded.
|
// Also, if the enclosing scope allows its members to be modified from elsewhere, the snapshot will not be recorded.
|
||||||
if !nested_place.is_name()
|
if self.scopes[enclosing_scope_id].visibility().is_public() {
|
||||||
|| self.scopes[enclosing_scope_id].visibility().is_public()
|
|
||||||
{
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -374,17 +364,16 @@ impl<'db, 'ast> SemanticIndexBuilder<'db, 'ast> {
|
||||||
// Note that even if this place is bound in the popped scope,
|
// Note that even if this place is bound in the popped scope,
|
||||||
// it may refer to the enclosing scope bindings
|
// it may refer to the enclosing scope bindings
|
||||||
// so we also need to snapshot the bindings of the enclosing scope.
|
// so we also need to snapshot the bindings of the enclosing scope.
|
||||||
|
let Some(enclosed_symbol_id) =
|
||||||
let Some(enclosing_place_id) =
|
enclosing_place_table.symbol_id(nested_symbol.name())
|
||||||
enclosing_place_table.place_id_by_expr(&nested_place.expr)
|
|
||||||
else {
|
else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
let enclosing_place = enclosing_place_table.place_expr(enclosing_place_id);
|
let enclosing_place = enclosing_place_table.symbol(enclosed_symbol_id);
|
||||||
if !enclosing_place.is_bound() {
|
if !enclosing_place.is_bound() {
|
||||||
// If the bound scope of a place can be modified from elsewhere, the snapshot will not be recorded.
|
// If the bound scope of a place can be modified from elsewhere, the snapshot will not be recorded.
|
||||||
if self
|
if self
|
||||||
.bound_scope(enclosing_scope_id, &nested_place.expr)
|
.bound_scope(enclosing_scope_id, nested_symbol)
|
||||||
.is_none_or(|scope| self.scopes[scope].visibility().is_public())
|
.is_none_or(|scope| self.scopes[scope].visibility().is_public())
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
|
@ -395,14 +384,14 @@ impl<'db, 'ast> SemanticIndexBuilder<'db, 'ast> {
|
||||||
// enclosing scope (this may later be invalidated and swept away).
|
// enclosing scope (this may later be invalidated and swept away).
|
||||||
let key = EnclosingSnapshotKey {
|
let key = EnclosingSnapshotKey {
|
||||||
enclosing_scope: enclosing_scope_id,
|
enclosing_scope: enclosing_scope_id,
|
||||||
enclosing_place: enclosing_place_id,
|
enclosing_place: enclosed_symbol_id.into(),
|
||||||
nested_scope: popped_scope_id,
|
nested_scope: popped_scope_id,
|
||||||
nested_laziness: ScopeLaziness::Lazy,
|
nested_laziness: ScopeLaziness::Lazy,
|
||||||
};
|
};
|
||||||
let lazy_snapshot = self.use_def_maps[enclosing_scope_id].snapshot_outer_state(
|
let lazy_snapshot = self.use_def_maps[enclosing_scope_id].snapshot_outer_state(
|
||||||
enclosing_place_id,
|
enclosed_symbol_id.into(),
|
||||||
enclosing_scope_kind,
|
enclosing_scope_kind,
|
||||||
enclosing_place,
|
enclosing_place.into(),
|
||||||
);
|
);
|
||||||
self.enclosing_snapshots.insert(key, lazy_snapshot);
|
self.enclosing_snapshots.insert(key, lazy_snapshot);
|
||||||
}
|
}
|
||||||
|
@ -415,7 +404,10 @@ impl<'db, 'ast> SemanticIndexBuilder<'db, 'ast> {
|
||||||
let place_table = &self.place_tables[key.enclosing_scope];
|
let place_table = &self.place_tables[key.enclosing_scope];
|
||||||
key.nested_laziness.is_eager()
|
key.nested_laziness.is_eager()
|
||||||
|| key.enclosing_scope != popped_scope_id
|
|| key.enclosing_scope != popped_scope_id
|
||||||
|| !place_table.is_place_reassigned(key.enclosing_place)
|
|| !key
|
||||||
|
.enclosing_place
|
||||||
|
.as_symbol()
|
||||||
|
.is_some_and(|symbol_id| place_table.symbol(symbol_id).is_reassigned())
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -423,24 +415,27 @@ impl<'db, 'ast> SemanticIndexBuilder<'db, 'ast> {
|
||||||
self.enclosing_snapshots.retain(|key, _| {
|
self.enclosing_snapshots.retain(|key, _| {
|
||||||
let place_table = &self.place_tables[key.enclosing_scope];
|
let place_table = &self.place_tables[key.enclosing_scope];
|
||||||
|
|
||||||
let is_place_bound_and_nonlocal = || -> bool {
|
let is_bound_and_non_local = || -> bool {
|
||||||
let place_expr = place_table.place_expr(key.enclosing_place);
|
let ScopedPlaceId::Symbol(symbol_id) = key.enclosing_place else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
let symbol = place_table.symbol(symbol_id);
|
||||||
self.scopes
|
self.scopes
|
||||||
.iter_enumerated()
|
.iter_enumerated()
|
||||||
.skip_while(|(scope_id, _)| *scope_id != key.enclosing_scope)
|
.skip_while(|(scope_id, _)| *scope_id != key.enclosing_scope)
|
||||||
.any(|(scope_id, _)| {
|
.any(|(scope_id, _)| {
|
||||||
let other_scope_place_table = &self.place_tables[scope_id];
|
let other_scope_place_table = &self.place_tables[scope_id];
|
||||||
let Some(place_id) =
|
let Some(symbol_id) = other_scope_place_table.symbol_id(symbol.name())
|
||||||
other_scope_place_table.place_id_by_expr(&place_expr.expr)
|
|
||||||
else {
|
else {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
let place = other_scope_place_table.place_expr(place_id);
|
let symbol = other_scope_place_table.symbol(symbol_id);
|
||||||
place.is_marked_nonlocal() && place.is_bound()
|
symbol.is_nonlocal() && symbol.is_bound()
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
key.nested_laziness.is_eager() || !is_place_bound_and_nonlocal()
|
key.nested_laziness.is_eager() || !is_bound_and_non_local()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -515,17 +510,17 @@ impl<'db, 'ast> SemanticIndexBuilder<'db, 'ast> {
|
||||||
|
|
||||||
/// Add a symbol to the place table and the use-def map.
|
/// Add a symbol to the place table and the use-def map.
|
||||||
/// Return the [`ScopedPlaceId`] that uniquely identifies the symbol in both.
|
/// Return the [`ScopedPlaceId`] that uniquely identifies the symbol in both.
|
||||||
fn add_symbol(&mut self, name: Name) -> ScopedPlaceId {
|
fn add_symbol(&mut self, name: Name) -> ScopedSymbolId {
|
||||||
let (place_id, added) = self.current_place_table_mut().add_symbol(name);
|
let (symbol_id, added) = self.current_place_table_mut().add_symbol(Symbol::new(name));
|
||||||
if added {
|
if added {
|
||||||
self.current_use_def_map_mut().add_place(place_id);
|
self.current_use_def_map_mut().add_place(symbol_id.into());
|
||||||
}
|
}
|
||||||
place_id
|
symbol_id
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a place to the place table and the use-def map.
|
/// Add a place to the place table and the use-def map.
|
||||||
/// Return the [`ScopedPlaceId`] that uniquely identifies the place in both.
|
/// Return the [`ScopedPlaceId`] that uniquely identifies the place in both.
|
||||||
fn add_place(&mut self, place_expr: PlaceExprWithFlags) -> ScopedPlaceId {
|
fn add_place(&mut self, place_expr: PlaceExpr) -> ScopedPlaceId {
|
||||||
let (place_id, added) = self.current_place_table_mut().add_place(place_expr);
|
let (place_id, added) = self.current_place_table_mut().add_place(place_expr);
|
||||||
if added {
|
if added {
|
||||||
self.current_use_def_map_mut().add_place(place_id);
|
self.current_use_def_map_mut().add_place(place_id);
|
||||||
|
@ -533,16 +528,19 @@ impl<'db, 'ast> SemanticIndexBuilder<'db, 'ast> {
|
||||||
place_id
|
place_id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
fn mark_place_bound(&mut self, id: ScopedPlaceId) {
|
fn mark_place_bound(&mut self, id: ScopedPlaceId) {
|
||||||
self.current_place_table_mut().mark_place_bound(id);
|
self.current_place_table_mut().mark_bound(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
fn mark_place_declared(&mut self, id: ScopedPlaceId) {
|
fn mark_place_declared(&mut self, id: ScopedPlaceId) {
|
||||||
self.current_place_table_mut().mark_place_declared(id);
|
self.current_place_table_mut().mark_declared(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mark_place_used(&mut self, id: ScopedPlaceId) {
|
#[track_caller]
|
||||||
self.current_place_table_mut().mark_place_used(id);
|
fn mark_symbol_used(&mut self, id: ScopedSymbolId) {
|
||||||
|
self.current_place_table_mut().symbol_mut(id).mark_used();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_entry_for_definition_key(&mut self, key: DefinitionNodeKey) -> &mut Definitions<'db> {
|
fn add_entry_for_definition_key(&mut self, key: DefinitionNodeKey) -> &mut Definitions<'db> {
|
||||||
|
@ -572,23 +570,20 @@ impl<'db, 'ast> SemanticIndexBuilder<'db, 'ast> {
|
||||||
fn delete_associated_bindings(&mut self, place: ScopedPlaceId) {
|
fn delete_associated_bindings(&mut self, place: ScopedPlaceId) {
|
||||||
let scope = self.current_scope();
|
let scope = self.current_scope();
|
||||||
// Don't delete associated bindings if the scope is a class scope & place is a name (it's never visible to nested scopes)
|
// Don't delete associated bindings if the scope is a class scope & place is a name (it's never visible to nested scopes)
|
||||||
if self.scopes[scope].kind() == ScopeKind::Class
|
if self.scopes[scope].kind() == ScopeKind::Class && place.is_symbol() {
|
||||||
&& self.place_tables[scope].place_expr(place).is_name()
|
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for associated_place in self.place_tables[scope].associated_place_ids(place) {
|
for associated_place in self.place_tables[scope]
|
||||||
let is_place_name = self.place_tables[scope]
|
.associated_place_ids(place)
|
||||||
.place_expr(associated_place)
|
.iter()
|
||||||
.is_name();
|
.copied()
|
||||||
self.use_def_maps[scope].delete_binding(associated_place, is_place_name);
|
{
|
||||||
|
self.use_def_maps[scope].delete_binding(associated_place.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn delete_binding(&mut self, place: ScopedPlaceId) {
|
fn delete_binding(&mut self, place: ScopedPlaceId) {
|
||||||
let is_place_name = self.current_place_table().place_expr(place).is_name();
|
self.current_use_def_map_mut().delete_binding(place);
|
||||||
self.current_use_def_map_mut()
|
|
||||||
.delete_binding(place, is_place_name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Push a new [`Definition`] onto the list of definitions
|
/// Push a new [`Definition`] onto the list of definitions
|
||||||
|
@ -637,16 +632,15 @@ impl<'db, 'ast> SemanticIndexBuilder<'db, 'ast> {
|
||||||
self.mark_place_declared(place);
|
self.mark_place_declared(place);
|
||||||
}
|
}
|
||||||
|
|
||||||
let is_place_name = self.current_place_table().place_expr(place).is_name();
|
|
||||||
let use_def = self.current_use_def_map_mut();
|
let use_def = self.current_use_def_map_mut();
|
||||||
match category {
|
match category {
|
||||||
DefinitionCategory::DeclarationAndBinding => {
|
DefinitionCategory::DeclarationAndBinding => {
|
||||||
use_def.record_declaration_and_binding(place, definition, is_place_name);
|
use_def.record_declaration_and_binding(place, definition);
|
||||||
self.delete_associated_bindings(place);
|
self.delete_associated_bindings(place);
|
||||||
}
|
}
|
||||||
DefinitionCategory::Declaration => use_def.record_declaration(place, definition),
|
DefinitionCategory::Declaration => use_def.record_declaration(place, definition),
|
||||||
DefinitionCategory::Binding => {
|
DefinitionCategory::Binding => {
|
||||||
use_def.record_binding(place, definition, is_place_name);
|
use_def.record_binding(place, definition);
|
||||||
self.delete_associated_bindings(place);
|
self.delete_associated_bindings(place);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -963,8 +957,8 @@ impl<'db, 'ast> SemanticIndexBuilder<'db, 'ast> {
|
||||||
// TODO create Definition for PEP 695 typevars
|
// TODO create Definition for PEP 695 typevars
|
||||||
// note that the "bound" on the typevar is a totally different thing than whether
|
// note that the "bound" on the typevar is a totally different thing than whether
|
||||||
// or not a name is "bound" by a typevar declaration; the latter is always true.
|
// or not a name is "bound" by a typevar declaration; the latter is always true.
|
||||||
self.mark_place_bound(symbol);
|
self.mark_place_bound(symbol.into());
|
||||||
self.mark_place_declared(symbol);
|
self.mark_place_declared(symbol.into());
|
||||||
if let Some(bounds) = bound {
|
if let Some(bounds) = bound {
|
||||||
self.visit_expr(bounds);
|
self.visit_expr(bounds);
|
||||||
}
|
}
|
||||||
|
@ -972,9 +966,9 @@ impl<'db, 'ast> SemanticIndexBuilder<'db, 'ast> {
|
||||||
self.visit_expr(default);
|
self.visit_expr(default);
|
||||||
}
|
}
|
||||||
match type_param {
|
match type_param {
|
||||||
ast::TypeParam::TypeVar(node) => self.add_definition(symbol, node),
|
ast::TypeParam::TypeVar(node) => self.add_definition(symbol.into(), node),
|
||||||
ast::TypeParam::ParamSpec(node) => self.add_definition(symbol, node),
|
ast::TypeParam::ParamSpec(node) => self.add_definition(symbol.into(), node),
|
||||||
ast::TypeParam::TypeVarTuple(node) => self.add_definition(symbol, node),
|
ast::TypeParam::TypeVarTuple(node) => self.add_definition(symbol.into(), node),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1061,20 +1055,23 @@ impl<'db, 'ast> SemanticIndexBuilder<'db, 'ast> {
|
||||||
if let Some(vararg) = parameters.vararg.as_ref() {
|
if let Some(vararg) = parameters.vararg.as_ref() {
|
||||||
let symbol = self.add_symbol(vararg.name.id().clone());
|
let symbol = self.add_symbol(vararg.name.id().clone());
|
||||||
self.add_definition(
|
self.add_definition(
|
||||||
symbol,
|
symbol.into(),
|
||||||
DefinitionNodeRef::VariadicPositionalParameter(vararg),
|
DefinitionNodeRef::VariadicPositionalParameter(vararg),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if let Some(kwarg) = parameters.kwarg.as_ref() {
|
if let Some(kwarg) = parameters.kwarg.as_ref() {
|
||||||
let symbol = self.add_symbol(kwarg.name.id().clone());
|
let symbol = self.add_symbol(kwarg.name.id().clone());
|
||||||
self.add_definition(symbol, DefinitionNodeRef::VariadicKeywordParameter(kwarg));
|
self.add_definition(
|
||||||
|
symbol.into(),
|
||||||
|
DefinitionNodeRef::VariadicKeywordParameter(kwarg),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn declare_parameter(&mut self, parameter: &'ast ast::ParameterWithDefault) {
|
fn declare_parameter(&mut self, parameter: &'ast ast::ParameterWithDefault) {
|
||||||
let symbol = self.add_symbol(parameter.name().id().clone());
|
let symbol = self.add_symbol(parameter.name().id().clone());
|
||||||
|
|
||||||
let definition = self.add_definition(symbol, parameter);
|
let definition = self.add_definition(symbol.into(), parameter);
|
||||||
|
|
||||||
// Insert a mapping from the inner Parameter node to the same definition. This
|
// Insert a mapping from the inner Parameter node to the same definition. This
|
||||||
// ensures that calling `HasType::inferred_type` on the inner parameter returns
|
// ensures that calling `HasType::inferred_type` on the inner parameter returns
|
||||||
|
@ -1295,12 +1292,15 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
|
||||||
// used to collect all the overloaded definitions of a function. This needs to be
|
// used to collect all the overloaded definitions of a function. This needs to be
|
||||||
// done on the `Identifier` node as opposed to `ExprName` because that's what the
|
// done on the `Identifier` node as opposed to `ExprName` because that's what the
|
||||||
// AST uses.
|
// AST uses.
|
||||||
self.mark_place_used(symbol);
|
self.mark_symbol_used(symbol);
|
||||||
let use_id = self.current_ast_ids().record_use(name);
|
let use_id = self.current_ast_ids().record_use(name);
|
||||||
self.current_use_def_map_mut()
|
self.current_use_def_map_mut().record_use(
|
||||||
.record_use(symbol, use_id, NodeKey::from_node(name));
|
symbol.into(),
|
||||||
|
use_id,
|
||||||
|
NodeKey::from_node(name),
|
||||||
|
);
|
||||||
|
|
||||||
self.add_definition(symbol, function_def);
|
self.add_definition(symbol.into(), function_def);
|
||||||
}
|
}
|
||||||
ast::Stmt::ClassDef(class) => {
|
ast::Stmt::ClassDef(class) => {
|
||||||
for decorator in &class.decorator_list {
|
for decorator in &class.decorator_list {
|
||||||
|
@ -1324,7 +1324,7 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
|
||||||
|
|
||||||
// In Python runtime semantics, a class is registered after its scope is evaluated.
|
// In Python runtime semantics, a class is registered after its scope is evaluated.
|
||||||
let symbol = self.add_symbol(class.name.id.clone());
|
let symbol = self.add_symbol(class.name.id.clone());
|
||||||
self.add_definition(symbol, class);
|
self.add_definition(symbol.into(), class);
|
||||||
}
|
}
|
||||||
ast::Stmt::TypeAlias(type_alias) => {
|
ast::Stmt::TypeAlias(type_alias) => {
|
||||||
let symbol = self.add_symbol(
|
let symbol = self.add_symbol(
|
||||||
|
@ -1334,7 +1334,7 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
|
||||||
.map(|name| name.id.clone())
|
.map(|name| name.id.clone())
|
||||||
.unwrap_or("<unknown>".into()),
|
.unwrap_or("<unknown>".into()),
|
||||||
);
|
);
|
||||||
self.add_definition(symbol, type_alias);
|
self.add_definition(symbol.into(), type_alias);
|
||||||
self.visit_expr(&type_alias.name);
|
self.visit_expr(&type_alias.name);
|
||||||
|
|
||||||
self.with_type_params(
|
self.with_type_params(
|
||||||
|
@ -1366,7 +1366,7 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
|
||||||
|
|
||||||
let symbol = self.add_symbol(symbol_name);
|
let symbol = self.add_symbol(symbol_name);
|
||||||
self.add_definition(
|
self.add_definition(
|
||||||
symbol,
|
symbol.into(),
|
||||||
ImportDefinitionNodeRef {
|
ImportDefinitionNodeRef {
|
||||||
node,
|
node,
|
||||||
alias_index,
|
alias_index,
|
||||||
|
@ -1438,10 +1438,7 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
|
||||||
// For more details, see the doc-comment on `StarImportPlaceholderPredicate`.
|
// For more details, see the doc-comment on `StarImportPlaceholderPredicate`.
|
||||||
for export in exported_names(self.db, referenced_module) {
|
for export in exported_names(self.db, referenced_module) {
|
||||||
let symbol_id = self.add_symbol(export.clone());
|
let symbol_id = self.add_symbol(export.clone());
|
||||||
let node_ref = StarImportDefinitionNodeRef {
|
let node_ref = StarImportDefinitionNodeRef { node, symbol_id };
|
||||||
node,
|
|
||||||
place_id: symbol_id,
|
|
||||||
};
|
|
||||||
let star_import = StarImportPlaceholderPredicate::new(
|
let star_import = StarImportPlaceholderPredicate::new(
|
||||||
self.db,
|
self.db,
|
||||||
self.file,
|
self.file,
|
||||||
|
@ -1451,8 +1448,9 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
|
||||||
|
|
||||||
let star_import_predicate = self.add_predicate(star_import.into());
|
let star_import_predicate = self.add_predicate(star_import.into());
|
||||||
|
|
||||||
let pre_definition =
|
let pre_definition = self
|
||||||
self.current_use_def_map().single_place_snapshot(symbol_id);
|
.current_use_def_map()
|
||||||
|
.single_symbol_place_snapshot(symbol_id);
|
||||||
let pre_definition_reachability =
|
let pre_definition_reachability =
|
||||||
self.current_use_def_map().reachability;
|
self.current_use_def_map().reachability;
|
||||||
|
|
||||||
|
@ -1469,7 +1467,7 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
|
||||||
);
|
);
|
||||||
self.current_use_def_map_mut().reachability = definition_reachability;
|
self.current_use_def_map_mut().reachability = definition_reachability;
|
||||||
|
|
||||||
self.push_additional_definition(symbol_id, node_ref);
|
self.push_additional_definition(symbol_id.into(), node_ref);
|
||||||
|
|
||||||
self.current_use_def_map_mut()
|
self.current_use_def_map_mut()
|
||||||
.record_and_negate_star_import_reachability_constraint(
|
.record_and_negate_star_import_reachability_constraint(
|
||||||
|
@ -1503,7 +1501,7 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
|
||||||
let symbol = self.add_symbol(symbol_name.clone());
|
let symbol = self.add_symbol(symbol_name.clone());
|
||||||
|
|
||||||
self.add_definition(
|
self.add_definition(
|
||||||
symbol,
|
symbol.into(),
|
||||||
ImportFromDefinitionNodeRef {
|
ImportFromDefinitionNodeRef {
|
||||||
node,
|
node,
|
||||||
alias_index,
|
alias_index,
|
||||||
|
@ -1580,9 +1578,9 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
|
||||||
|
|
||||||
if let ast::Expr::Name(name) = &*node.target {
|
if let ast::Expr::Name(name) = &*node.target {
|
||||||
let symbol_id = self.add_symbol(name.id.clone());
|
let symbol_id = self.add_symbol(name.id.clone());
|
||||||
let symbol = self.current_place_table().place_expr(symbol_id);
|
let symbol = self.current_place_table().symbol(symbol_id);
|
||||||
// Check whether the variable has been declared global.
|
// Check whether the variable has been declared global.
|
||||||
if symbol.is_marked_global() {
|
if symbol.is_global() {
|
||||||
self.report_semantic_error(SemanticSyntaxError {
|
self.report_semantic_error(SemanticSyntaxError {
|
||||||
kind: SemanticSyntaxErrorKind::AnnotatedGlobal(name.id.as_str().into()),
|
kind: SemanticSyntaxErrorKind::AnnotatedGlobal(name.id.as_str().into()),
|
||||||
range: name.range,
|
range: name.range,
|
||||||
|
@ -1590,7 +1588,7 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// Check whether the variable has been declared nonlocal.
|
// Check whether the variable has been declared nonlocal.
|
||||||
if symbol.is_marked_nonlocal() {
|
if symbol.is_nonlocal() {
|
||||||
self.report_semantic_error(SemanticSyntaxError {
|
self.report_semantic_error(SemanticSyntaxError {
|
||||||
kind: SemanticSyntaxErrorKind::AnnotatedNonlocal(
|
kind: SemanticSyntaxErrorKind::AnnotatedNonlocal(
|
||||||
name.id.as_str().into(),
|
name.id.as_str().into(),
|
||||||
|
@ -2006,7 +2004,7 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
|
||||||
let symbol = self.add_symbol(symbol_name.id.clone());
|
let symbol = self.add_symbol(symbol_name.id.clone());
|
||||||
|
|
||||||
self.add_definition(
|
self.add_definition(
|
||||||
symbol,
|
symbol.into(),
|
||||||
DefinitionNodeRef::ExceptHandler(ExceptHandlerDefinitionNodeRef {
|
DefinitionNodeRef::ExceptHandler(ExceptHandlerDefinitionNodeRef {
|
||||||
handler: except_handler,
|
handler: except_handler,
|
||||||
is_star: *is_star,
|
is_star: *is_star,
|
||||||
|
@ -2020,7 +2018,7 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
|
||||||
self.visit_body(handler_body);
|
self.visit_body(handler_body);
|
||||||
// The caught exception is cleared at the end of the except clause
|
// The caught exception is cleared at the end of the except clause
|
||||||
if let Some(symbol) = symbol {
|
if let Some(symbol) = symbol {
|
||||||
self.delete_binding(symbol);
|
self.delete_binding(symbol.into());
|
||||||
}
|
}
|
||||||
// Each `except` block is mutually exclusive with all other `except` blocks.
|
// Each `except` block is mutually exclusive with all other `except` blocks.
|
||||||
post_except_states.push(self.flow_snapshot());
|
post_except_states.push(self.flow_snapshot());
|
||||||
|
@ -2078,7 +2076,7 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
|
||||||
}) => {
|
}) => {
|
||||||
for name in names {
|
for name in names {
|
||||||
let symbol_id = self.add_symbol(name.id.clone());
|
let symbol_id = self.add_symbol(name.id.clone());
|
||||||
let symbol = self.current_place_table().place_expr(symbol_id);
|
let symbol = self.current_place_table().symbol(symbol_id);
|
||||||
// Check whether the variable has already been accessed in this scope.
|
// Check whether the variable has already been accessed in this scope.
|
||||||
if symbol.is_bound() || symbol.is_declared() || symbol.is_used() {
|
if symbol.is_bound() || symbol.is_declared() || symbol.is_used() {
|
||||||
self.report_semantic_error(SemanticSyntaxError {
|
self.report_semantic_error(SemanticSyntaxError {
|
||||||
|
@ -2091,14 +2089,16 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// Check whether the variable has also been declared nonlocal.
|
// Check whether the variable has also been declared nonlocal.
|
||||||
if symbol.is_marked_nonlocal() {
|
if symbol.is_nonlocal() {
|
||||||
self.report_semantic_error(SemanticSyntaxError {
|
self.report_semantic_error(SemanticSyntaxError {
|
||||||
kind: SemanticSyntaxErrorKind::NonlocalAndGlobal(name.to_string()),
|
kind: SemanticSyntaxErrorKind::NonlocalAndGlobal(name.to_string()),
|
||||||
range: name.range,
|
range: name.range,
|
||||||
python_version: self.python_version,
|
python_version: self.python_version,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
self.current_place_table_mut().mark_place_global(symbol_id);
|
self.current_place_table_mut()
|
||||||
|
.symbol_mut(symbol_id)
|
||||||
|
.mark_global();
|
||||||
}
|
}
|
||||||
walk_stmt(self, stmt);
|
walk_stmt(self, stmt);
|
||||||
}
|
}
|
||||||
|
@ -2109,7 +2109,7 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
|
||||||
}) => {
|
}) => {
|
||||||
for name in names {
|
for name in names {
|
||||||
let symbol_id = self.add_symbol(name.id.clone());
|
let symbol_id = self.add_symbol(name.id.clone());
|
||||||
let symbol = self.current_place_table().place_expr(symbol_id);
|
let symbol = self.current_place_table().symbol(symbol_id);
|
||||||
// Check whether the variable has already been accessed in this scope.
|
// Check whether the variable has already been accessed in this scope.
|
||||||
if symbol.is_bound() || symbol.is_declared() || symbol.is_used() {
|
if symbol.is_bound() || symbol.is_declared() || symbol.is_used() {
|
||||||
self.report_semantic_error(SemanticSyntaxError {
|
self.report_semantic_error(SemanticSyntaxError {
|
||||||
|
@ -2122,7 +2122,7 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// Check whether the variable has also been declared global.
|
// Check whether the variable has also been declared global.
|
||||||
if symbol.is_marked_global() {
|
if symbol.is_global() {
|
||||||
self.report_semantic_error(SemanticSyntaxError {
|
self.report_semantic_error(SemanticSyntaxError {
|
||||||
kind: SemanticSyntaxErrorKind::NonlocalAndGlobal(name.to_string()),
|
kind: SemanticSyntaxErrorKind::NonlocalAndGlobal(name.to_string()),
|
||||||
range: name.range,
|
range: name.range,
|
||||||
|
@ -2139,7 +2139,8 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
|
||||||
// x = 1
|
// x = 1
|
||||||
// ```
|
// ```
|
||||||
self.current_place_table_mut()
|
self.current_place_table_mut()
|
||||||
.mark_place_nonlocal(symbol_id);
|
.symbol_mut(symbol_id)
|
||||||
|
.mark_nonlocal();
|
||||||
}
|
}
|
||||||
walk_stmt(self, stmt);
|
walk_stmt(self, stmt);
|
||||||
}
|
}
|
||||||
|
@ -2151,11 +2152,8 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
|
||||||
// We will check the target expressions and then delete them.
|
// We will check the target expressions and then delete them.
|
||||||
walk_stmt(self, stmt);
|
walk_stmt(self, stmt);
|
||||||
for target in targets {
|
for target in targets {
|
||||||
if let Ok(target) = PlaceExpr::try_from(target) {
|
if let Some(mut target) = PlaceExpr::try_from_expr(target) {
|
||||||
let is_name = target.is_name();
|
if let PlaceExpr::Symbol(symbol) = &mut target {
|
||||||
let place_id = self.add_place(PlaceExprWithFlags::new(target));
|
|
||||||
let place_table = self.current_place_table_mut();
|
|
||||||
if is_name {
|
|
||||||
// `del x` behaves like an assignment in that it forces all references
|
// `del x` behaves like an assignment in that it forces all references
|
||||||
// to `x` in the current scope (including *prior* references) to refer
|
// to `x` in the current scope (including *prior* references) to refer
|
||||||
// to the current scope's binding (unless `x` is declared `global` or
|
// to the current scope's binding (unless `x` is declared `global` or
|
||||||
|
@ -2169,9 +2167,11 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
|
||||||
// del x
|
// del x
|
||||||
// foo()
|
// foo()
|
||||||
// ```
|
// ```
|
||||||
place_table.mark_place_bound(place_id);
|
symbol.mark_bound();
|
||||||
|
symbol.mark_used();
|
||||||
}
|
}
|
||||||
place_table.mark_place_used(place_id);
|
|
||||||
|
let place_id = self.add_place(target);
|
||||||
self.delete_binding(place_id);
|
self.delete_binding(place_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2238,19 +2238,20 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
|
||||||
ast::Expr::Name(ast::ExprName { ctx, .. })
|
ast::Expr::Name(ast::ExprName { ctx, .. })
|
||||||
| ast::Expr::Attribute(ast::ExprAttribute { ctx, .. })
|
| ast::Expr::Attribute(ast::ExprAttribute { ctx, .. })
|
||||||
| ast::Expr::Subscript(ast::ExprSubscript { ctx, .. }) => {
|
| ast::Expr::Subscript(ast::ExprSubscript { ctx, .. }) => {
|
||||||
if let Ok(place_expr) = PlaceExpr::try_from(expr) {
|
if let Some(mut place_expr) = PlaceExpr::try_from_expr(expr) {
|
||||||
let mut place_expr = PlaceExprWithFlags::new(place_expr);
|
if self.is_method_of_class().is_some() {
|
||||||
if self.is_method_of_class().is_some()
|
if let PlaceExpr::Member(member) = &mut place_expr {
|
||||||
&& place_expr.is_instance_attribute_candidate()
|
if member.is_instance_attribute_candidate() {
|
||||||
{
|
// We specifically mark attribute assignments to the first parameter of a method,
|
||||||
// We specifically mark attribute assignments to the first parameter of a method,
|
// i.e. typically `self` or `cls`.
|
||||||
// i.e. typically `self` or `cls`.
|
let accessed_object_refers_to_first_parameter = self
|
||||||
let accessed_object_refers_to_first_parameter = self
|
.current_first_parameter_name
|
||||||
.current_first_parameter_name
|
.is_some_and(|first| member.symbol_name() == first);
|
||||||
.is_some_and(|fst| place_expr.expr.root_name() == fst);
|
|
||||||
|
|
||||||
if accessed_object_refers_to_first_parameter && place_expr.is_member() {
|
if accessed_object_refers_to_first_parameter {
|
||||||
place_expr.mark_instance_attribute();
|
member.mark_instance_attribute();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2267,7 +2268,9 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
|
||||||
let place_id = self.add_place(place_expr);
|
let place_id = self.add_place(place_expr);
|
||||||
|
|
||||||
if is_use {
|
if is_use {
|
||||||
self.mark_place_used(place_id);
|
if let ScopedPlaceId::Symbol(symbol_id) = place_id {
|
||||||
|
self.mark_symbol_used(symbol_id);
|
||||||
|
}
|
||||||
let use_id = self.current_ast_ids().record_use(expr);
|
let use_id = self.current_ast_ids().record_use(expr);
|
||||||
self.current_use_def_map_mut()
|
self.current_use_def_map_mut()
|
||||||
.record_use(place_id, use_id, node_key);
|
.record_use(place_id, use_id, node_key);
|
||||||
|
@ -2561,7 +2564,7 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
|
||||||
let symbol = self.add_symbol(name.id().clone());
|
let symbol = self.add_symbol(name.id().clone());
|
||||||
let state = self.current_match_case.as_ref().unwrap();
|
let state = self.current_match_case.as_ref().unwrap();
|
||||||
self.add_definition(
|
self.add_definition(
|
||||||
symbol,
|
symbol.into(),
|
||||||
MatchPatternDefinitionNodeRef {
|
MatchPatternDefinitionNodeRef {
|
||||||
pattern: state.pattern,
|
pattern: state.pattern,
|
||||||
identifier: name,
|
identifier: name,
|
||||||
|
@ -2582,7 +2585,7 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
|
||||||
let symbol = self.add_symbol(name.id().clone());
|
let symbol = self.add_symbol(name.id().clone());
|
||||||
let state = self.current_match_case.as_ref().unwrap();
|
let state = self.current_match_case.as_ref().unwrap();
|
||||||
self.add_definition(
|
self.add_definition(
|
||||||
symbol,
|
symbol.into(),
|
||||||
MatchPatternDefinitionNodeRef {
|
MatchPatternDefinitionNodeRef {
|
||||||
pattern: state.pattern,
|
pattern: state.pattern,
|
||||||
identifier: name,
|
identifier: name,
|
||||||
|
|
|
@ -8,7 +8,9 @@ use ruff_text_size::{Ranged, TextRange};
|
||||||
use crate::Db;
|
use crate::Db;
|
||||||
use crate::ast_node_ref::AstNodeRef;
|
use crate::ast_node_ref::AstNodeRef;
|
||||||
use crate::node_key::NodeKey;
|
use crate::node_key::NodeKey;
|
||||||
use crate::semantic_index::place::{FileScopeId, ScopeId, ScopedPlaceId};
|
use crate::semantic_index::place::ScopedPlaceId;
|
||||||
|
use crate::semantic_index::scope::{FileScopeId, ScopeId};
|
||||||
|
use crate::semantic_index::symbol::ScopedSymbolId;
|
||||||
use crate::unpack::{Unpack, UnpackPosition};
|
use crate::unpack::{Unpack, UnpackPosition};
|
||||||
|
|
||||||
/// A definition of a place.
|
/// A definition of a place.
|
||||||
|
@ -305,7 +307,7 @@ pub(crate) struct ImportDefinitionNodeRef<'ast> {
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub(crate) struct StarImportDefinitionNodeRef<'ast> {
|
pub(crate) struct StarImportDefinitionNodeRef<'ast> {
|
||||||
pub(crate) node: &'ast ast::StmtImportFrom,
|
pub(crate) node: &'ast ast::StmtImportFrom,
|
||||||
pub(crate) place_id: ScopedPlaceId,
|
pub(crate) symbol_id: ScopedSymbolId,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
@ -395,10 +397,10 @@ impl<'db> DefinitionNodeRef<'_, 'db> {
|
||||||
is_reexported,
|
is_reexported,
|
||||||
}),
|
}),
|
||||||
DefinitionNodeRef::ImportStar(star_import) => {
|
DefinitionNodeRef::ImportStar(star_import) => {
|
||||||
let StarImportDefinitionNodeRef { node, place_id } = star_import;
|
let StarImportDefinitionNodeRef { node, symbol_id } = star_import;
|
||||||
DefinitionKind::StarImport(StarImportDefinitionKind {
|
DefinitionKind::StarImport(StarImportDefinitionKind {
|
||||||
node: AstNodeRef::new(parsed, node),
|
node: AstNodeRef::new(parsed, node),
|
||||||
place_id,
|
symbol_id,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
DefinitionNodeRef::Function(function) => {
|
DefinitionNodeRef::Function(function) => {
|
||||||
|
@ -522,7 +524,7 @@ impl<'db> DefinitionNodeRef<'_, 'db> {
|
||||||
|
|
||||||
// INVARIANT: for an invalid-syntax statement such as `from foo import *, bar, *`,
|
// INVARIANT: for an invalid-syntax statement such as `from foo import *, bar, *`,
|
||||||
// we only create a `StarImportDefinitionKind` for the *first* `*` alias in the names list.
|
// we only create a `StarImportDefinitionKind` for the *first* `*` alias in the names list.
|
||||||
Self::ImportStar(StarImportDefinitionNodeRef { node, place_id: _ }) => node
|
Self::ImportStar(StarImportDefinitionNodeRef { node, symbol_id: _ }) => node
|
||||||
.names
|
.names
|
||||||
.iter()
|
.iter()
|
||||||
.find(|alias| &alias.name == "*")
|
.find(|alias| &alias.name == "*")
|
||||||
|
@ -822,7 +824,7 @@ impl<'db> From<Option<(UnpackPosition, Unpack<'db>)>> for TargetKind<'db> {
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct StarImportDefinitionKind {
|
pub struct StarImportDefinitionKind {
|
||||||
node: AstNodeRef<ast::StmtImportFrom>,
|
node: AstNodeRef<ast::StmtImportFrom>,
|
||||||
place_id: ScopedPlaceId,
|
symbol_id: ScopedSymbolId,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StarImportDefinitionKind {
|
impl StarImportDefinitionKind {
|
||||||
|
@ -844,8 +846,8 @@ impl StarImportDefinitionKind {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn place_id(&self) -> ScopedPlaceId {
|
pub(crate) fn symbol_id(&self) -> ScopedSymbolId {
|
||||||
self.place_id
|
self.symbol_id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::ast_node_ref::AstNodeRef;
|
use crate::ast_node_ref::AstNodeRef;
|
||||||
use crate::db::Db;
|
use crate::db::Db;
|
||||||
use crate::semantic_index::place::{FileScopeId, ScopeId};
|
use crate::semantic_index::scope::{FileScopeId, ScopeId};
|
||||||
use ruff_db::files::File;
|
use ruff_db::files::File;
|
||||||
use ruff_db::parsed::ParsedModuleRef;
|
use ruff_db::parsed::ParsedModuleRef;
|
||||||
use ruff_python_ast as ast;
|
use ruff_python_ast as ast;
|
||||||
|
|
423
crates/ty_python_semantic/src/semantic_index/member.rs
Normal file
423
crates/ty_python_semantic/src/semantic_index/member.rs
Normal file
|
@ -0,0 +1,423 @@
|
||||||
|
use bitflags::bitflags;
|
||||||
|
use hashbrown::hash_table::Entry;
|
||||||
|
use ruff_index::{IndexVec, newtype_index};
|
||||||
|
use ruff_python_ast::{self as ast, name::Name};
|
||||||
|
use rustc_hash::FxHasher;
|
||||||
|
use smallvec::SmallVec;
|
||||||
|
use std::hash::{Hash as _, Hasher as _};
|
||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
|
/// A member access, e.g. `x.y` or `x[1]` or `x["foo"]`.
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, get_size2::GetSize)]
|
||||||
|
pub(crate) struct Member {
|
||||||
|
expression: MemberExpr,
|
||||||
|
flags: MemberFlags,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Member {
|
||||||
|
pub(crate) fn new(expression: MemberExpr) -> Self {
|
||||||
|
Self {
|
||||||
|
expression,
|
||||||
|
flags: MemberFlags::empty(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the left most part of the member expression, e.g. `x` in `x.y.z`.
|
||||||
|
///
|
||||||
|
/// This is the symbol on which the member access is performed.
|
||||||
|
pub(crate) fn symbol_name(&self) -> &Name {
|
||||||
|
self.expression.symbol_name()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn expression(&self) -> &MemberExpr {
|
||||||
|
&self.expression
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Is the place given a value in its containing scope?
|
||||||
|
pub(crate) const fn is_bound(&self) -> bool {
|
||||||
|
self.flags.contains(MemberFlags::IS_BOUND)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Is the place declared in its containing scope?
|
||||||
|
pub(crate) fn is_declared(&self) -> bool {
|
||||||
|
self.flags.contains(MemberFlags::IS_DECLARED)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn mark_bound(&mut self) {
|
||||||
|
self.insert_flags(MemberFlags::IS_BOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn mark_declared(&mut self) {
|
||||||
|
self.insert_flags(MemberFlags::IS_DECLARED);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn mark_instance_attribute(&mut self) {
|
||||||
|
self.flags.insert(MemberFlags::IS_INSTANCE_ATTRIBUTE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Is the place an instance attribute?
|
||||||
|
pub(crate) fn is_instance_attribute(&self) -> bool {
|
||||||
|
let is_instance_attribute = self.flags.contains(MemberFlags::IS_INSTANCE_ATTRIBUTE);
|
||||||
|
if is_instance_attribute {
|
||||||
|
debug_assert!(self.is_instance_attribute_candidate());
|
||||||
|
}
|
||||||
|
is_instance_attribute
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insert_flags(&mut self, flags: MemberFlags) {
|
||||||
|
self.flags.insert(flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If the place expression has the form `<NAME>.<MEMBER>`
|
||||||
|
/// (meaning it *may* be an instance attribute),
|
||||||
|
/// return `Some(<MEMBER>)`. Else, return `None`.
|
||||||
|
///
|
||||||
|
/// This method is internal to the semantic-index submodule.
|
||||||
|
/// It *only* checks that the AST structure of the `Place` is
|
||||||
|
/// correct. It does not check whether the `Place` actually occurred in
|
||||||
|
/// a method context, or whether the `<NAME>` actually refers to the first
|
||||||
|
/// parameter of the method (i.e. `self`). To answer those questions,
|
||||||
|
/// use [`Self::as_instance_attribute`].
|
||||||
|
pub(super) fn as_instance_attribute_candidate(&self) -> Option<&Name> {
|
||||||
|
match &*self.expression.segments {
|
||||||
|
[MemberSegment::Attribute(name)] => Some(name),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return `true` if the place expression has the form `<NAME>.<MEMBER>`,
|
||||||
|
/// indicating that it *may* be an instance attribute if we are in a method context.
|
||||||
|
///
|
||||||
|
/// This method is internal to the semantic-index submodule.
|
||||||
|
/// It *only* checks that the AST structure of the `Place` is
|
||||||
|
/// correct. It does not check whether the `Place` actually occurred in
|
||||||
|
/// a method context, or whether the `<NAME>` actually refers to the first
|
||||||
|
/// parameter of the method (i.e. `self`). To answer those questions,
|
||||||
|
/// use [`Self::is_instance_attribute`].
|
||||||
|
pub(super) fn is_instance_attribute_candidate(&self) -> bool {
|
||||||
|
self.as_instance_attribute_candidate().is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Does the place expression have the form `self.{name}` (`self` is the first parameter of the method)?
|
||||||
|
pub(super) fn is_instance_attribute_named(&self, name: &str) -> bool {
|
||||||
|
self.as_instance_attribute().map(Name::as_str) == Some(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return `Some(<ATTRIBUTE>)` if the place expression is an instance attribute.
|
||||||
|
pub(crate) fn as_instance_attribute(&self) -> Option<&Name> {
|
||||||
|
if self.is_instance_attribute() {
|
||||||
|
debug_assert!(self.as_instance_attribute_candidate().is_some());
|
||||||
|
self.as_instance_attribute_candidate()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Member {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
std::fmt::Display::fmt(&self.expression, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
/// Flags that can be queried to obtain information about a member in a given scope.
|
||||||
|
///
|
||||||
|
/// See the doc-comment at the top of [`super::use_def`] for explanations of what it
|
||||||
|
/// means for a member to be *bound* as opposed to *declared*.
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
struct MemberFlags: u8 {
|
||||||
|
const IS_BOUND = 1 << 0;
|
||||||
|
const IS_DECLARED = 1 << 1;
|
||||||
|
const IS_INSTANCE_ATTRIBUTE = 1 << 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl get_size2::GetSize for MemberFlags {}
|
||||||
|
|
||||||
|
/// An expression accessing a member on a symbol named `symbol_name`, e.g. `x.y.z`.
|
||||||
|
///
|
||||||
|
/// The parts after the symbol name are called segments, and they can be either:
|
||||||
|
/// * An attribute access, e.g. `.y` in `x.y`
|
||||||
|
/// * An integer-based subscript, e.g. `[1]` in `x[1]`
|
||||||
|
/// * A string-based subscript, e.g. `["foo"]` in `x["foo"]`
|
||||||
|
///
|
||||||
|
/// Internally, the segments are stored in reverse order. This allows constructing
|
||||||
|
/// a `MemberExpr` from an ast expression without having to reverse the segments.
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, get_size2::GetSize, Hash)]
|
||||||
|
pub(crate) struct MemberExpr {
|
||||||
|
symbol_name: Name,
|
||||||
|
segments: SmallVec<[MemberSegment; 1]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MemberExpr {
|
||||||
|
pub(super) fn new(symbol_name: Name, segments: SmallVec<[MemberSegment; 1]>) -> Self {
|
||||||
|
debug_assert!(
|
||||||
|
!segments.is_empty(),
|
||||||
|
"A member without segments is a symbol."
|
||||||
|
);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
symbol_name,
|
||||||
|
segments,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn shrink_to_fit(&mut self) {
|
||||||
|
self.segments.shrink_to_fit();
|
||||||
|
self.segments.shrink_to_fit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the left most part of the member expression, e.g. `x` in `x.y.z`.
|
||||||
|
///
|
||||||
|
/// This is the symbol on which the member access is performed.
|
||||||
|
pub(crate) fn symbol_name(&self) -> &Name {
|
||||||
|
&self.symbol_name
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the segments of the member expression, e.g. `[MemberSegment::Attribute("y"), MemberSegment::IntSubscript(1)]` for `x.y[1]`.
|
||||||
|
pub(crate) fn member_segments(
|
||||||
|
&self,
|
||||||
|
) -> impl ExactSizeIterator<Item = &MemberSegment> + DoubleEndedIterator {
|
||||||
|
self.segments.iter().rev()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn as_ref(&self) -> MemberExprRef {
|
||||||
|
MemberExprRef {
|
||||||
|
name: self.symbol_name.as_str(),
|
||||||
|
segments: self.segments.as_slice(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for MemberExpr {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.write_str(self.symbol_name.as_str())?;
|
||||||
|
|
||||||
|
for segment in self.member_segments() {
|
||||||
|
match segment {
|
||||||
|
MemberSegment::Attribute(name) => write!(f, ".{name}")?,
|
||||||
|
MemberSegment::IntSubscript(int) => write!(f, "[{int}]")?,
|
||||||
|
MemberSegment::StringSubscript(string) => write!(f, "[\"{string}\"]")?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq<MemberExprRef<'_>> for MemberExpr {
|
||||||
|
fn eq(&self, other: &MemberExprRef) -> bool {
|
||||||
|
self.as_ref() == *other
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq<MemberExprRef<'_>> for &MemberExpr {
|
||||||
|
fn eq(&self, other: &MemberExprRef) -> bool {
|
||||||
|
self.as_ref() == *other
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq<MemberExpr> for MemberExprRef<'_> {
|
||||||
|
fn eq(&self, other: &MemberExpr) -> bool {
|
||||||
|
other == self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq<&MemberExpr> for MemberExprRef<'_> {
|
||||||
|
fn eq(&self, other: &&MemberExpr) -> bool {
|
||||||
|
*other == self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, get_size2::GetSize)]
|
||||||
|
pub(crate) enum MemberSegment {
|
||||||
|
/// An attribute access, e.g. `.y` in `x.y`
|
||||||
|
Attribute(Name),
|
||||||
|
/// An integer-based index access, e.g. `[1]` in `x[1]`
|
||||||
|
IntSubscript(ast::Int),
|
||||||
|
/// A string-based index access, e.g. `["foo"]` in `x["foo"]`
|
||||||
|
StringSubscript(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reference to a member expression.
|
||||||
|
#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
|
||||||
|
pub(crate) struct MemberExprRef<'a> {
|
||||||
|
name: &'a str,
|
||||||
|
segments: &'a [MemberSegment],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> MemberExprRef<'a> {
|
||||||
|
pub(super) fn symbol_name(&self) -> &'a str {
|
||||||
|
self.name
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new `MemberExprRef` from a name and segments.
|
||||||
|
///
|
||||||
|
/// Note that the segments are expected to be in reverse order, i.e. the last segment is the first one in the expression.
|
||||||
|
pub(super) fn from_raw(name: &'a str, segments: &'a [MemberSegment]) -> Self {
|
||||||
|
debug_assert!(
|
||||||
|
!segments.is_empty(),
|
||||||
|
"A member without segments is a symbol."
|
||||||
|
);
|
||||||
|
Self { name, segments }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a slice over the member segments. The segments are in reverse order,
|
||||||
|
pub(super) fn rev_member_segments(&self) -> &'a [MemberSegment] {
|
||||||
|
self.segments
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a MemberExpr> for MemberExprRef<'a> {
|
||||||
|
fn from(value: &'a MemberExpr) -> Self {
|
||||||
|
value.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Uniquely identifies a member in a scope.
|
||||||
|
#[newtype_index]
|
||||||
|
#[derive(get_size2::GetSize, salsa::Update)]
|
||||||
|
pub struct ScopedMemberId;
|
||||||
|
|
||||||
|
/// The members of a scope. Allows lookup by member path and [`ScopedMemberId`].
|
||||||
|
#[derive(Default, get_size2::GetSize)]
|
||||||
|
pub(super) struct MemberTable {
|
||||||
|
members: IndexVec<ScopedMemberId, Member>,
|
||||||
|
|
||||||
|
/// Map from member path to its ID.
|
||||||
|
///
|
||||||
|
/// Uses a hash table to avoid storing the path twice.
|
||||||
|
map: hashbrown::HashTable<ScopedMemberId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MemberTable {
|
||||||
|
/// Returns the member with the given ID.
|
||||||
|
///
|
||||||
|
/// ## Panics
|
||||||
|
/// If the ID is not valid for this table.
|
||||||
|
#[track_caller]
|
||||||
|
pub(crate) fn member(&self, id: ScopedMemberId) -> &Member {
|
||||||
|
&self.members[id]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a mutable reference to the member with the given ID.
|
||||||
|
///
|
||||||
|
/// ## Panics
|
||||||
|
/// If the ID is not valid for this table.
|
||||||
|
#[track_caller]
|
||||||
|
pub(super) fn member_mut(&mut self, id: ScopedMemberId) -> &mut Member {
|
||||||
|
&mut self.members[id]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns an iterator over all members in the table.
|
||||||
|
pub(crate) fn iter(&self) -> std::slice::Iter<Member> {
|
||||||
|
self.members.iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hash_member_expression_ref(member: MemberExprRef) -> u64 {
|
||||||
|
let mut h = FxHasher::default();
|
||||||
|
member.hash(&mut h);
|
||||||
|
h.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the ID of the member with the given expression, if it exists.
|
||||||
|
pub(crate) fn member_id<'a>(
|
||||||
|
&self,
|
||||||
|
member: impl Into<MemberExprRef<'a>>,
|
||||||
|
) -> Option<ScopedMemberId> {
|
||||||
|
let member = member.into();
|
||||||
|
let hash = Self::hash_member_expression_ref(member);
|
||||||
|
self.map
|
||||||
|
.find(hash, |id| self.members[*id].expression == member)
|
||||||
|
.copied()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn place_id_by_instance_attribute_name(&self, name: &str) -> Option<ScopedMemberId> {
|
||||||
|
for (id, member) in self.members.iter_enumerated() {
|
||||||
|
if member.is_instance_attribute_named(name) {
|
||||||
|
return Some(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for MemberTable {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
// It's sufficient to compare the members as the map is only a reverse lookup.
|
||||||
|
self.members == other.members
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for MemberTable {}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for MemberTable {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_tuple("MemberTable").field(&self.members).finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub(super) struct MemberTableBuilder {
|
||||||
|
table: MemberTable,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MemberTableBuilder {
|
||||||
|
/// Adds a member to the table or updates the flags of an existing member if it already exists.
|
||||||
|
///
|
||||||
|
/// Members are identified by their expression, which is hashed to find the entry in the table.
|
||||||
|
pub(super) fn add(&mut self, mut member: Member) -> (ScopedMemberId, bool) {
|
||||||
|
let hash = MemberTable::hash_member_expression_ref(member.expression.as_ref());
|
||||||
|
let entry = self.table.map.entry(
|
||||||
|
hash,
|
||||||
|
|id| self.table.members[*id].expression == member.expression,
|
||||||
|
|id| {
|
||||||
|
MemberTable::hash_member_expression_ref(self.table.members[*id].expression.as_ref())
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
match entry {
|
||||||
|
Entry::Occupied(entry) => {
|
||||||
|
let id = *entry.get();
|
||||||
|
|
||||||
|
if !member.flags.is_empty() {
|
||||||
|
self.members[id].flags.insert(member.flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
(id, false)
|
||||||
|
}
|
||||||
|
Entry::Vacant(entry) => {
|
||||||
|
member.expression.shrink_to_fit();
|
||||||
|
|
||||||
|
let id = self.table.members.push(member);
|
||||||
|
entry.insert(id);
|
||||||
|
(id, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn build(self) -> MemberTable {
|
||||||
|
let mut table = self.table;
|
||||||
|
table.members.shrink_to_fit();
|
||||||
|
table.map.shrink_to_fit(|id| {
|
||||||
|
MemberTable::hash_member_expression_ref(table.members[*id].expression.as_ref())
|
||||||
|
});
|
||||||
|
table
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for MemberTableBuilder {
|
||||||
|
type Target = MemberTable;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.table
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for MemberTableBuilder {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.table
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,8 +30,8 @@
|
||||||
|
|
||||||
use crate::list::{List, ListBuilder, ListSetReverseIterator, ListStorage};
|
use crate::list::{List, ListBuilder, ListSetReverseIterator, ListStorage};
|
||||||
use crate::semantic_index::ast_ids::ScopedUseId;
|
use crate::semantic_index::ast_ids::ScopedUseId;
|
||||||
use crate::semantic_index::place::FileScopeId;
|
|
||||||
use crate::semantic_index::predicate::ScopedPredicateId;
|
use crate::semantic_index::predicate::ScopedPredicateId;
|
||||||
|
use crate::semantic_index::scope::FileScopeId;
|
||||||
|
|
||||||
/// A narrowing constraint associated with a live binding.
|
/// A narrowing constraint associated with a live binding.
|
||||||
///
|
///
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -14,7 +14,8 @@ use ruff_python_ast::Singleton;
|
||||||
use crate::db::Db;
|
use crate::db::Db;
|
||||||
use crate::semantic_index::expression::Expression;
|
use crate::semantic_index::expression::Expression;
|
||||||
use crate::semantic_index::global_scope;
|
use crate::semantic_index::global_scope;
|
||||||
use crate::semantic_index::place::{FileScopeId, ScopeId, ScopedPlaceId};
|
use crate::semantic_index::scope::{FileScopeId, ScopeId};
|
||||||
|
use crate::semantic_index::symbol::ScopedSymbolId;
|
||||||
|
|
||||||
// A scoped identifier for each `Predicate` in a scope.
|
// A scoped identifier for each `Predicate` in a scope.
|
||||||
#[derive(Clone, Debug, Copy, PartialOrd, Ord, PartialEq, Eq, Hash, get_size2::GetSize)]
|
#[derive(Clone, Debug, Copy, PartialOrd, Ord, PartialEq, Eq, Hash, get_size2::GetSize)]
|
||||||
|
@ -217,7 +218,7 @@ pub(crate) struct StarImportPlaceholderPredicate<'db> {
|
||||||
/// for valid `*`-import definitions, and valid `*`-import definitions can only ever
|
/// for valid `*`-import definitions, and valid `*`-import definitions can only ever
|
||||||
/// exist in the global scope; thus, we know that the `symbol_id` here will be relative
|
/// exist in the global scope; thus, we know that the `symbol_id` here will be relative
|
||||||
/// to the global scope of the importing file.
|
/// to the global scope of the importing file.
|
||||||
pub(crate) symbol_id: ScopedPlaceId,
|
pub(crate) symbol_id: ScopedSymbolId,
|
||||||
|
|
||||||
pub(crate) referenced_file: File,
|
pub(crate) referenced_file: File,
|
||||||
}
|
}
|
||||||
|
|
|
@ -844,19 +844,17 @@ impl ReachabilityConstraints {
|
||||||
PredicateNode::Pattern(inner) => Self::analyze_single_pattern_predicate(db, inner),
|
PredicateNode::Pattern(inner) => Self::analyze_single_pattern_predicate(db, inner),
|
||||||
PredicateNode::StarImportPlaceholder(star_import) => {
|
PredicateNode::StarImportPlaceholder(star_import) => {
|
||||||
let place_table = place_table(db, star_import.scope(db));
|
let place_table = place_table(db, star_import.scope(db));
|
||||||
let symbol_name = place_table
|
let symbol = place_table.symbol(star_import.symbol_id(db));
|
||||||
.place_expr(star_import.symbol_id(db))
|
|
||||||
.expect_name();
|
|
||||||
let referenced_file = star_import.referenced_file(db);
|
let referenced_file = star_import.referenced_file(db);
|
||||||
|
|
||||||
let requires_explicit_reexport = match dunder_all_names(db, referenced_file) {
|
let requires_explicit_reexport = match dunder_all_names(db, referenced_file) {
|
||||||
Some(all_names) => {
|
Some(all_names) => {
|
||||||
if all_names.contains(symbol_name) {
|
if all_names.contains(symbol.name()) {
|
||||||
Some(RequiresExplicitReExport::No)
|
Some(RequiresExplicitReExport::No)
|
||||||
} else {
|
} else {
|
||||||
tracing::trace!(
|
tracing::trace!(
|
||||||
"Symbol `{}` (via star import) not found in `__all__` of `{}`",
|
"Symbol `{}` (via star import) not found in `__all__` of `{}`",
|
||||||
symbol_name,
|
symbol.name(),
|
||||||
referenced_file.path(db)
|
referenced_file.path(db)
|
||||||
);
|
);
|
||||||
return Truthiness::AlwaysFalse;
|
return Truthiness::AlwaysFalse;
|
||||||
|
@ -865,8 +863,13 @@ impl ReachabilityConstraints {
|
||||||
None => None,
|
None => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
match imported_symbol(db, referenced_file, symbol_name, requires_explicit_reexport)
|
match imported_symbol(
|
||||||
.place
|
db,
|
||||||
|
referenced_file,
|
||||||
|
symbol.name(),
|
||||||
|
requires_explicit_reexport,
|
||||||
|
)
|
||||||
|
.place
|
||||||
{
|
{
|
||||||
crate::place::Place::Type(_, crate::place::Boundness::Bound) => {
|
crate::place::Place::Type(_, crate::place::Boundness::Bound) => {
|
||||||
Truthiness::AlwaysTrue
|
Truthiness::AlwaysTrue
|
||||||
|
|
458
crates/ty_python_semantic/src/semantic_index/scope.rs
Normal file
458
crates/ty_python_semantic/src/semantic_index/scope.rs
Normal file
|
@ -0,0 +1,458 @@
|
||||||
|
use std::ops::Range;
|
||||||
|
|
||||||
|
use ruff_db::{files::File, parsed::ParsedModuleRef};
|
||||||
|
use ruff_index::newtype_index;
|
||||||
|
use ruff_python_ast as ast;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
Db,
|
||||||
|
ast_node_ref::AstNodeRef,
|
||||||
|
node_key::NodeKey,
|
||||||
|
semantic_index::{
|
||||||
|
SemanticIndex, reachability_constraints::ScopedReachabilityConstraintId, semantic_index,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A cross-module identifier of a scope that can be used as a salsa query parameter.
|
||||||
|
#[salsa::tracked(debug)]
|
||||||
|
pub struct ScopeId<'db> {
|
||||||
|
pub file: File,
|
||||||
|
|
||||||
|
pub file_scope_id: FileScopeId,
|
||||||
|
}
|
||||||
|
|
||||||
|
// The Salsa heap is tracked separately.
|
||||||
|
impl get_size2::GetSize for ScopeId<'_> {}
|
||||||
|
|
||||||
|
impl<'db> ScopeId<'db> {
|
||||||
|
pub(crate) fn is_function_like(self, db: &'db dyn Db) -> bool {
|
||||||
|
self.node(db).scope_kind().is_function_like()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn is_type_parameter(self, db: &'db dyn Db) -> bool {
|
||||||
|
self.node(db).scope_kind().is_type_parameter()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn node(self, db: &dyn Db) -> &NodeWithScopeKind {
|
||||||
|
self.scope(db).node()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn scope(self, db: &dyn Db) -> &Scope {
|
||||||
|
semantic_index(db, self.file(db)).scope(self.file_scope_id(db))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub(crate) fn name<'ast>(self, db: &'db dyn Db, module: &'ast ParsedModuleRef) -> &'ast str {
|
||||||
|
match self.node(db) {
|
||||||
|
NodeWithScopeKind::Module => "<module>",
|
||||||
|
NodeWithScopeKind::Class(class) | NodeWithScopeKind::ClassTypeParameters(class) => {
|
||||||
|
class.node(module).name.as_str()
|
||||||
|
}
|
||||||
|
NodeWithScopeKind::Function(function)
|
||||||
|
| NodeWithScopeKind::FunctionTypeParameters(function) => {
|
||||||
|
function.node(module).name.as_str()
|
||||||
|
}
|
||||||
|
NodeWithScopeKind::TypeAlias(type_alias)
|
||||||
|
| NodeWithScopeKind::TypeAliasTypeParameters(type_alias) => type_alias
|
||||||
|
.node(module)
|
||||||
|
.name
|
||||||
|
.as_name_expr()
|
||||||
|
.map(|name| name.id.as_str())
|
||||||
|
.unwrap_or("<type alias>"),
|
||||||
|
NodeWithScopeKind::Lambda(_) => "<lambda>",
|
||||||
|
NodeWithScopeKind::ListComprehension(_) => "<listcomp>",
|
||||||
|
NodeWithScopeKind::SetComprehension(_) => "<setcomp>",
|
||||||
|
NodeWithScopeKind::DictComprehension(_) => "<dictcomp>",
|
||||||
|
NodeWithScopeKind::GeneratorExpression(_) => "<generator>",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ID that uniquely identifies a scope inside of a module.
|
||||||
|
#[newtype_index]
|
||||||
|
#[derive(salsa::Update, get_size2::GetSize)]
|
||||||
|
pub struct FileScopeId;
|
||||||
|
|
||||||
|
impl FileScopeId {
|
||||||
|
/// Returns the scope id of the module-global scope.
|
||||||
|
pub fn global() -> Self {
|
||||||
|
FileScopeId::from_u32(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_global(self) -> bool {
|
||||||
|
self == FileScopeId::global()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_scope_id(self, db: &dyn Db, file: File) -> ScopeId<'_> {
|
||||||
|
let index = semantic_index(db, file);
|
||||||
|
index.scope_ids_by_scope[self]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn is_generator_function(self, index: &SemanticIndex) -> bool {
|
||||||
|
index.generator_functions.contains(&self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, salsa::Update, get_size2::GetSize)]
|
||||||
|
pub(crate) struct Scope {
|
||||||
|
/// The parent scope, if any.
|
||||||
|
parent: Option<FileScopeId>,
|
||||||
|
|
||||||
|
/// The node that introduces this scope.
|
||||||
|
node: NodeWithScopeKind,
|
||||||
|
|
||||||
|
/// The range of [`FileScopeId`]s that are descendants of this scope.
|
||||||
|
descendants: Range<FileScopeId>,
|
||||||
|
|
||||||
|
/// The constraint that determines the reachability of this scope.
|
||||||
|
reachability: ScopedReachabilityConstraintId,
|
||||||
|
|
||||||
|
/// Whether this scope is defined inside an `if TYPE_CHECKING:` block.
|
||||||
|
in_type_checking_block: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Scope {
|
||||||
|
pub(super) fn new(
|
||||||
|
parent: Option<FileScopeId>,
|
||||||
|
node: NodeWithScopeKind,
|
||||||
|
descendants: Range<FileScopeId>,
|
||||||
|
reachability: ScopedReachabilityConstraintId,
|
||||||
|
in_type_checking_block: bool,
|
||||||
|
) -> Self {
|
||||||
|
Scope {
|
||||||
|
parent,
|
||||||
|
node,
|
||||||
|
descendants,
|
||||||
|
reachability,
|
||||||
|
in_type_checking_block,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn parent(&self) -> Option<FileScopeId> {
|
||||||
|
self.parent
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn node(&self) -> &NodeWithScopeKind {
|
||||||
|
&self.node
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn kind(&self) -> ScopeKind {
|
||||||
|
self.node().scope_kind()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn visibility(&self) -> ScopeVisibility {
|
||||||
|
self.kind().visibility()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn descendants(&self) -> Range<FileScopeId> {
|
||||||
|
self.descendants.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn extend_descendants(&mut self, children_end: FileScopeId) {
|
||||||
|
self.descendants = self.descendants.start..children_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn is_eager(&self) -> bool {
|
||||||
|
self.kind().is_eager()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn reachability(&self) -> ScopedReachabilityConstraintId {
|
||||||
|
self.reachability
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn in_type_checking_block(&self) -> bool {
|
||||||
|
self.in_type_checking_block
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub(crate) enum ScopeKind {
|
||||||
|
Module,
|
||||||
|
Annotation,
|
||||||
|
Class,
|
||||||
|
Function,
|
||||||
|
Lambda,
|
||||||
|
Comprehension,
|
||||||
|
TypeAlias,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ScopeKind {
|
||||||
|
pub(crate) const fn is_eager(self) -> bool {
|
||||||
|
self.laziness().is_eager()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) const fn laziness(self) -> ScopeLaziness {
|
||||||
|
match self {
|
||||||
|
ScopeKind::Module | ScopeKind::Class | ScopeKind::Comprehension => ScopeLaziness::Eager,
|
||||||
|
ScopeKind::Annotation
|
||||||
|
| ScopeKind::Function
|
||||||
|
| ScopeKind::Lambda
|
||||||
|
| ScopeKind::TypeAlias => ScopeLaziness::Lazy,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) const fn visibility(self) -> ScopeVisibility {
|
||||||
|
match self {
|
||||||
|
ScopeKind::Module | ScopeKind::Class => ScopeVisibility::Public,
|
||||||
|
ScopeKind::Annotation
|
||||||
|
| ScopeKind::TypeAlias
|
||||||
|
| ScopeKind::Function
|
||||||
|
| ScopeKind::Lambda
|
||||||
|
| ScopeKind::Comprehension => ScopeVisibility::Private,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) const fn is_function_like(self) -> bool {
|
||||||
|
// Type parameter scopes behave like function scopes in terms of name resolution; CPython
|
||||||
|
// symbol table also uses the term "function-like" for these scopes.
|
||||||
|
matches!(
|
||||||
|
self,
|
||||||
|
ScopeKind::Annotation
|
||||||
|
| ScopeKind::Function
|
||||||
|
| ScopeKind::Lambda
|
||||||
|
| ScopeKind::TypeAlias
|
||||||
|
| ScopeKind::Comprehension
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) const fn is_class(self) -> bool {
|
||||||
|
matches!(self, ScopeKind::Class)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) const fn is_type_parameter(self) -> bool {
|
||||||
|
matches!(self, ScopeKind::Annotation | ScopeKind::TypeAlias)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) const fn is_non_lambda_function(self) -> bool {
|
||||||
|
matches!(self, ScopeKind::Function)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reference to a node that introduces a new scope.
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub(crate) enum NodeWithScopeRef<'a> {
|
||||||
|
Module,
|
||||||
|
Class(&'a ast::StmtClassDef),
|
||||||
|
Function(&'a ast::StmtFunctionDef),
|
||||||
|
Lambda(&'a ast::ExprLambda),
|
||||||
|
FunctionTypeParameters(&'a ast::StmtFunctionDef),
|
||||||
|
ClassTypeParameters(&'a ast::StmtClassDef),
|
||||||
|
TypeAlias(&'a ast::StmtTypeAlias),
|
||||||
|
TypeAliasTypeParameters(&'a ast::StmtTypeAlias),
|
||||||
|
ListComprehension(&'a ast::ExprListComp),
|
||||||
|
SetComprehension(&'a ast::ExprSetComp),
|
||||||
|
DictComprehension(&'a ast::ExprDictComp),
|
||||||
|
GeneratorExpression(&'a ast::ExprGenerator),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NodeWithScopeRef<'_> {
|
||||||
|
/// Converts the unowned reference to an owned [`NodeWithScopeKind`].
|
||||||
|
///
|
||||||
|
/// Note that node wrapped by `self` must be a child of `module`.
|
||||||
|
pub(super) fn to_kind(self, module: &ParsedModuleRef) -> NodeWithScopeKind {
|
||||||
|
match self {
|
||||||
|
NodeWithScopeRef::Module => NodeWithScopeKind::Module,
|
||||||
|
NodeWithScopeRef::Class(class) => {
|
||||||
|
NodeWithScopeKind::Class(AstNodeRef::new(module, class))
|
||||||
|
}
|
||||||
|
NodeWithScopeRef::Function(function) => {
|
||||||
|
NodeWithScopeKind::Function(AstNodeRef::new(module, function))
|
||||||
|
}
|
||||||
|
NodeWithScopeRef::TypeAlias(type_alias) => {
|
||||||
|
NodeWithScopeKind::TypeAlias(AstNodeRef::new(module, type_alias))
|
||||||
|
}
|
||||||
|
NodeWithScopeRef::TypeAliasTypeParameters(type_alias) => {
|
||||||
|
NodeWithScopeKind::TypeAliasTypeParameters(AstNodeRef::new(module, type_alias))
|
||||||
|
}
|
||||||
|
NodeWithScopeRef::Lambda(lambda) => {
|
||||||
|
NodeWithScopeKind::Lambda(AstNodeRef::new(module, lambda))
|
||||||
|
}
|
||||||
|
NodeWithScopeRef::FunctionTypeParameters(function) => {
|
||||||
|
NodeWithScopeKind::FunctionTypeParameters(AstNodeRef::new(module, function))
|
||||||
|
}
|
||||||
|
NodeWithScopeRef::ClassTypeParameters(class) => {
|
||||||
|
NodeWithScopeKind::ClassTypeParameters(AstNodeRef::new(module, class))
|
||||||
|
}
|
||||||
|
NodeWithScopeRef::ListComprehension(comprehension) => {
|
||||||
|
NodeWithScopeKind::ListComprehension(AstNodeRef::new(module, comprehension))
|
||||||
|
}
|
||||||
|
NodeWithScopeRef::SetComprehension(comprehension) => {
|
||||||
|
NodeWithScopeKind::SetComprehension(AstNodeRef::new(module, comprehension))
|
||||||
|
}
|
||||||
|
NodeWithScopeRef::DictComprehension(comprehension) => {
|
||||||
|
NodeWithScopeKind::DictComprehension(AstNodeRef::new(module, comprehension))
|
||||||
|
}
|
||||||
|
NodeWithScopeRef::GeneratorExpression(generator) => {
|
||||||
|
NodeWithScopeKind::GeneratorExpression(AstNodeRef::new(module, generator))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn node_key(self) -> NodeWithScopeKey {
|
||||||
|
match self {
|
||||||
|
NodeWithScopeRef::Module => NodeWithScopeKey::Module,
|
||||||
|
NodeWithScopeRef::Class(class) => NodeWithScopeKey::Class(NodeKey::from_node(class)),
|
||||||
|
NodeWithScopeRef::Function(function) => {
|
||||||
|
NodeWithScopeKey::Function(NodeKey::from_node(function))
|
||||||
|
}
|
||||||
|
NodeWithScopeRef::Lambda(lambda) => {
|
||||||
|
NodeWithScopeKey::Lambda(NodeKey::from_node(lambda))
|
||||||
|
}
|
||||||
|
NodeWithScopeRef::FunctionTypeParameters(function) => {
|
||||||
|
NodeWithScopeKey::FunctionTypeParameters(NodeKey::from_node(function))
|
||||||
|
}
|
||||||
|
NodeWithScopeRef::ClassTypeParameters(class) => {
|
||||||
|
NodeWithScopeKey::ClassTypeParameters(NodeKey::from_node(class))
|
||||||
|
}
|
||||||
|
NodeWithScopeRef::TypeAlias(type_alias) => {
|
||||||
|
NodeWithScopeKey::TypeAlias(NodeKey::from_node(type_alias))
|
||||||
|
}
|
||||||
|
NodeWithScopeRef::TypeAliasTypeParameters(type_alias) => {
|
||||||
|
NodeWithScopeKey::TypeAliasTypeParameters(NodeKey::from_node(type_alias))
|
||||||
|
}
|
||||||
|
NodeWithScopeRef::ListComprehension(comprehension) => {
|
||||||
|
NodeWithScopeKey::ListComprehension(NodeKey::from_node(comprehension))
|
||||||
|
}
|
||||||
|
NodeWithScopeRef::SetComprehension(comprehension) => {
|
||||||
|
NodeWithScopeKey::SetComprehension(NodeKey::from_node(comprehension))
|
||||||
|
}
|
||||||
|
NodeWithScopeRef::DictComprehension(comprehension) => {
|
||||||
|
NodeWithScopeKey::DictComprehension(NodeKey::from_node(comprehension))
|
||||||
|
}
|
||||||
|
NodeWithScopeRef::GeneratorExpression(generator) => {
|
||||||
|
NodeWithScopeKey::GeneratorExpression(NodeKey::from_node(generator))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Node that introduces a new scope.
|
||||||
|
#[derive(Clone, Debug, salsa::Update, get_size2::GetSize)]
|
||||||
|
pub(crate) enum NodeWithScopeKind {
|
||||||
|
Module,
|
||||||
|
Class(AstNodeRef<ast::StmtClassDef>),
|
||||||
|
ClassTypeParameters(AstNodeRef<ast::StmtClassDef>),
|
||||||
|
Function(AstNodeRef<ast::StmtFunctionDef>),
|
||||||
|
FunctionTypeParameters(AstNodeRef<ast::StmtFunctionDef>),
|
||||||
|
TypeAliasTypeParameters(AstNodeRef<ast::StmtTypeAlias>),
|
||||||
|
TypeAlias(AstNodeRef<ast::StmtTypeAlias>),
|
||||||
|
Lambda(AstNodeRef<ast::ExprLambda>),
|
||||||
|
ListComprehension(AstNodeRef<ast::ExprListComp>),
|
||||||
|
SetComprehension(AstNodeRef<ast::ExprSetComp>),
|
||||||
|
DictComprehension(AstNodeRef<ast::ExprDictComp>),
|
||||||
|
GeneratorExpression(AstNodeRef<ast::ExprGenerator>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NodeWithScopeKind {
|
||||||
|
pub(crate) const fn scope_kind(&self) -> ScopeKind {
|
||||||
|
match self {
|
||||||
|
Self::Module => ScopeKind::Module,
|
||||||
|
Self::Class(_) => ScopeKind::Class,
|
||||||
|
Self::Function(_) => ScopeKind::Function,
|
||||||
|
Self::Lambda(_) => ScopeKind::Lambda,
|
||||||
|
Self::FunctionTypeParameters(_)
|
||||||
|
| Self::ClassTypeParameters(_)
|
||||||
|
| Self::TypeAliasTypeParameters(_) => ScopeKind::Annotation,
|
||||||
|
Self::TypeAlias(_) => ScopeKind::TypeAlias,
|
||||||
|
Self::ListComprehension(_)
|
||||||
|
| Self::SetComprehension(_)
|
||||||
|
| Self::DictComprehension(_)
|
||||||
|
| Self::GeneratorExpression(_) => ScopeKind::Comprehension,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn expect_class<'ast>(
|
||||||
|
&self,
|
||||||
|
module: &'ast ParsedModuleRef,
|
||||||
|
) -> &'ast ast::StmtClassDef {
|
||||||
|
match self {
|
||||||
|
Self::Class(class) => class.node(module),
|
||||||
|
_ => panic!("expected class"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn as_class<'ast>(
|
||||||
|
&self,
|
||||||
|
module: &'ast ParsedModuleRef,
|
||||||
|
) -> Option<&'ast ast::StmtClassDef> {
|
||||||
|
match self {
|
||||||
|
Self::Class(class) => Some(class.node(module)),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn expect_function<'ast>(
|
||||||
|
&self,
|
||||||
|
module: &'ast ParsedModuleRef,
|
||||||
|
) -> &'ast ast::StmtFunctionDef {
|
||||||
|
self.as_function(module).expect("expected function")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn expect_type_alias<'ast>(
|
||||||
|
&self,
|
||||||
|
module: &'ast ParsedModuleRef,
|
||||||
|
) -> &'ast ast::StmtTypeAlias {
|
||||||
|
match self {
|
||||||
|
Self::TypeAlias(type_alias) => type_alias.node(module),
|
||||||
|
_ => panic!("expected type alias"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn as_function<'ast>(
|
||||||
|
&self,
|
||||||
|
module: &'ast ParsedModuleRef,
|
||||||
|
) -> Option<&'ast ast::StmtFunctionDef> {
|
||||||
|
match self {
|
||||||
|
Self::Function(function) => Some(function.node(module)),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, get_size2::GetSize)]
|
||||||
|
pub(crate) enum NodeWithScopeKey {
|
||||||
|
Module,
|
||||||
|
Class(NodeKey),
|
||||||
|
ClassTypeParameters(NodeKey),
|
||||||
|
Function(NodeKey),
|
||||||
|
FunctionTypeParameters(NodeKey),
|
||||||
|
TypeAlias(NodeKey),
|
||||||
|
TypeAliasTypeParameters(NodeKey),
|
||||||
|
Lambda(NodeKey),
|
||||||
|
ListComprehension(NodeKey),
|
||||||
|
SetComprehension(NodeKey),
|
||||||
|
DictComprehension(NodeKey),
|
||||||
|
GeneratorExpression(NodeKey),
|
||||||
|
}
|
237
crates/ty_python_semantic/src/semantic_index/symbol.rs
Normal file
237
crates/ty_python_semantic/src/semantic_index/symbol.rs
Normal file
|
@ -0,0 +1,237 @@
|
||||||
|
use bitflags::bitflags;
|
||||||
|
use hashbrown::hash_table::Entry;
|
||||||
|
use ruff_index::{IndexVec, newtype_index};
|
||||||
|
use ruff_python_ast::name::Name;
|
||||||
|
use rustc_hash::FxHasher;
|
||||||
|
use std::hash::{Hash as _, Hasher as _};
|
||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
|
/// Uniquely identifies a symbol in a given scope.
|
||||||
|
#[newtype_index]
|
||||||
|
#[derive(get_size2::GetSize)]
|
||||||
|
pub struct ScopedSymbolId;
|
||||||
|
|
||||||
|
/// A symbol in a given scope.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, get_size2::GetSize, salsa::Update)]
|
||||||
|
pub(crate) struct Symbol {
|
||||||
|
name: Name,
|
||||||
|
flags: SymbolFlags,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Symbol {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
self.name.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
/// Flags that can be queried to obtain information about a symbol in a given scope.
|
||||||
|
///
|
||||||
|
/// See the doc-comment at the top of [`super::use_def`] for explanations of what it
|
||||||
|
/// means for a symbol to be *bound* as opposed to *declared*.
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
struct SymbolFlags: u8 {
|
||||||
|
const IS_USED = 1 << 0;
|
||||||
|
const IS_BOUND = 1 << 1;
|
||||||
|
const IS_DECLARED = 1 << 2;
|
||||||
|
const MARKED_GLOBAL = 1 << 3;
|
||||||
|
const MARKED_NONLOCAL = 1 << 4;
|
||||||
|
const IS_REASSIGNED = 1 << 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl get_size2::GetSize for SymbolFlags {}
|
||||||
|
|
||||||
|
impl Symbol {
|
||||||
|
pub(crate) const fn new(name: Name) -> Self {
|
||||||
|
Self {
|
||||||
|
name,
|
||||||
|
flags: SymbolFlags::empty(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn name(&self) -> &Name {
|
||||||
|
&self.name
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Is the symbol used in its containing scope?
|
||||||
|
pub(crate) fn is_used(&self) -> bool {
|
||||||
|
self.flags.contains(SymbolFlags::IS_USED)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Is the symbol given a value in its containing scope?
|
||||||
|
pub(crate) const fn is_bound(&self) -> bool {
|
||||||
|
self.flags.contains(SymbolFlags::IS_BOUND)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Is the symbol declared in its containing scope?
|
||||||
|
pub(crate) fn is_declared(&self) -> bool {
|
||||||
|
self.flags.contains(SymbolFlags::IS_DECLARED)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Is the symbol `global` its containing scope?
|
||||||
|
pub(crate) fn is_global(&self) -> bool {
|
||||||
|
self.flags.contains(SymbolFlags::MARKED_GLOBAL)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Is the symbol `nonlocal` its containing scope?
|
||||||
|
pub(crate) fn is_nonlocal(&self) -> bool {
|
||||||
|
self.flags.contains(SymbolFlags::MARKED_NONLOCAL)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) const fn is_reassigned(&self) -> bool {
|
||||||
|
self.flags.contains(SymbolFlags::IS_REASSIGNED)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn mark_global(&mut self) {
|
||||||
|
self.insert_flags(SymbolFlags::MARKED_GLOBAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn mark_nonlocal(&mut self) {
|
||||||
|
self.insert_flags(SymbolFlags::MARKED_NONLOCAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn mark_bound(&mut self) {
|
||||||
|
if self.is_bound() {
|
||||||
|
self.insert_flags(SymbolFlags::IS_REASSIGNED);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.insert_flags(SymbolFlags::IS_BOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn mark_used(&mut self) {
|
||||||
|
self.insert_flags(SymbolFlags::IS_USED);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn mark_declared(&mut self) {
|
||||||
|
self.insert_flags(SymbolFlags::IS_DECLARED);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insert_flags(&mut self, flags: SymbolFlags) {
|
||||||
|
self.flags.insert(flags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The symbols of a given scope.
|
||||||
|
///
|
||||||
|
/// Allows lookup by name and a symbol's ID.
|
||||||
|
#[derive(Default, get_size2::GetSize)]
|
||||||
|
pub(super) struct SymbolTable {
|
||||||
|
symbols: IndexVec<ScopedSymbolId, Symbol>,
|
||||||
|
|
||||||
|
/// Map from symbol name to its ID.
|
||||||
|
///
|
||||||
|
/// Uses a hash table to avoid storing the name twice.
|
||||||
|
map: hashbrown::HashTable<ScopedSymbolId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SymbolTable {
|
||||||
|
/// Look up a symbol by its ID.
|
||||||
|
///
|
||||||
|
/// ## Panics
|
||||||
|
/// If the ID is not valid for this symbol table.
|
||||||
|
#[track_caller]
|
||||||
|
pub(crate) fn symbol(&self, id: ScopedSymbolId) -> &Symbol {
|
||||||
|
&self.symbols[id]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Look up a symbol by its ID, mutably.
|
||||||
|
///
|
||||||
|
/// ## Panics
|
||||||
|
/// If the ID is not valid for this symbol table.
|
||||||
|
#[track_caller]
|
||||||
|
pub(crate) fn symbol_mut(&mut self, id: ScopedSymbolId) -> &mut Symbol {
|
||||||
|
&mut self.symbols[id]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Look up the ID of a symbol by its name.
|
||||||
|
pub(crate) fn symbol_id(&self, name: &str) -> Option<ScopedSymbolId> {
|
||||||
|
self.map
|
||||||
|
.find(Self::hash_name(name), |id| self.symbols[*id].name == name)
|
||||||
|
.copied()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterate over the symbols in this symbol table.
|
||||||
|
pub(crate) fn iter(&self) -> std::slice::Iter<Symbol> {
|
||||||
|
self.symbols.iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hash_name(name: &str) -> u64 {
|
||||||
|
let mut h = FxHasher::default();
|
||||||
|
name.hash(&mut h);
|
||||||
|
h.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for SymbolTable {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
// It's sufficient to compare the symbols as the map is only a reverse lookup.
|
||||||
|
self.symbols == other.symbols
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for SymbolTable {}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for SymbolTable {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_tuple("SymbolTable").field(&self.symbols).finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub(super) struct SymbolTableBuilder {
|
||||||
|
table: SymbolTable,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SymbolTableBuilder {
|
||||||
|
/// Add a new symbol to this scope or update the flags if a symbol with the same name already exists.
|
||||||
|
pub(super) fn add(&mut self, mut symbol: Symbol) -> (ScopedSymbolId, bool) {
|
||||||
|
let hash = SymbolTable::hash_name(symbol.name());
|
||||||
|
let entry = self.table.map.entry(
|
||||||
|
hash,
|
||||||
|
|id| &self.table.symbols[*id].name == symbol.name(),
|
||||||
|
|id| SymbolTable::hash_name(&self.table.symbols[*id].name),
|
||||||
|
);
|
||||||
|
|
||||||
|
match entry {
|
||||||
|
Entry::Occupied(entry) => {
|
||||||
|
let id = *entry.get();
|
||||||
|
|
||||||
|
if !symbol.flags.is_empty() {
|
||||||
|
self.symbols[id].flags.insert(symbol.flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
(id, false)
|
||||||
|
}
|
||||||
|
Entry::Vacant(entry) => {
|
||||||
|
symbol.name.shrink_to_fit();
|
||||||
|
let id = self.table.symbols.push(symbol);
|
||||||
|
entry.insert(id);
|
||||||
|
(id, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn build(self) -> SymbolTable {
|
||||||
|
let mut table = self.table;
|
||||||
|
table.symbols.shrink_to_fit();
|
||||||
|
table
|
||||||
|
.map
|
||||||
|
.shrink_to_fit(|id| SymbolTable::hash_name(&table.symbols[*id].name));
|
||||||
|
table
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for SymbolTableBuilder {
|
||||||
|
type Target = SymbolTable;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.table
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for SymbolTableBuilder {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.table
|
||||||
|
}
|
||||||
|
}
|
|
@ -251,20 +251,21 @@ use crate::node_key::NodeKey;
|
||||||
use crate::place::BoundnessAnalysis;
|
use crate::place::BoundnessAnalysis;
|
||||||
use crate::semantic_index::ast_ids::ScopedUseId;
|
use crate::semantic_index::ast_ids::ScopedUseId;
|
||||||
use crate::semantic_index::definition::{Definition, DefinitionState};
|
use crate::semantic_index::definition::{Definition, DefinitionState};
|
||||||
|
use crate::semantic_index::member::ScopedMemberId;
|
||||||
use crate::semantic_index::narrowing_constraints::{
|
use crate::semantic_index::narrowing_constraints::{
|
||||||
ConstraintKey, NarrowingConstraints, NarrowingConstraintsBuilder, NarrowingConstraintsIterator,
|
ConstraintKey, NarrowingConstraints, NarrowingConstraintsBuilder, NarrowingConstraintsIterator,
|
||||||
};
|
};
|
||||||
use crate::semantic_index::place::{
|
use crate::semantic_index::place::{PlaceExprRef, ScopedPlaceId};
|
||||||
FileScopeId, PlaceExpr, PlaceExprWithFlags, ScopeKind, ScopedPlaceId,
|
|
||||||
};
|
|
||||||
use crate::semantic_index::predicate::{
|
use crate::semantic_index::predicate::{
|
||||||
Predicate, PredicateOrLiteral, Predicates, PredicatesBuilder, ScopedPredicateId,
|
Predicate, PredicateOrLiteral, Predicates, PredicatesBuilder, ScopedPredicateId,
|
||||||
};
|
};
|
||||||
use crate::semantic_index::reachability_constraints::{
|
use crate::semantic_index::reachability_constraints::{
|
||||||
ReachabilityConstraints, ReachabilityConstraintsBuilder, ScopedReachabilityConstraintId,
|
ReachabilityConstraints, ReachabilityConstraintsBuilder, ScopedReachabilityConstraintId,
|
||||||
};
|
};
|
||||||
|
use crate::semantic_index::scope::{FileScopeId, ScopeKind, ScopeLaziness};
|
||||||
|
use crate::semantic_index::symbol::ScopedSymbolId;
|
||||||
use crate::semantic_index::use_def::place_state::PreviousDefinitions;
|
use crate::semantic_index::use_def::place_state::PreviousDefinitions;
|
||||||
use crate::semantic_index::{EnclosingSnapshotResult, ScopeLaziness, SemanticIndex};
|
use crate::semantic_index::{EnclosingSnapshotResult, SemanticIndex};
|
||||||
use crate::types::{IntersectionBuilder, Truthiness, Type, infer_narrowing_constraint};
|
use crate::types::{IntersectionBuilder, Truthiness, Type, infer_narrowing_constraint};
|
||||||
|
|
||||||
mod place_state;
|
mod place_state;
|
||||||
|
@ -311,11 +312,17 @@ pub(crate) struct UseDefMap<'db> {
|
||||||
/// bindings to that symbol. If there are any, the assignment is invalid.
|
/// bindings to that symbol. If there are any, the assignment is invalid.
|
||||||
bindings_by_definition: FxHashMap<Definition<'db>, Bindings>,
|
bindings_by_definition: FxHashMap<Definition<'db>, Bindings>,
|
||||||
|
|
||||||
/// [`PlaceState`] visible at end of scope for each place.
|
/// [`PlaceState`] visible at end of scope for each symbol.
|
||||||
end_of_scope_places: IndexVec<ScopedPlaceId, PlaceState>,
|
end_of_scope_symbols: IndexVec<ScopedSymbolId, PlaceState>,
|
||||||
|
|
||||||
/// All potentially reachable bindings and declarations, for each place.
|
/// [`PlaceState`] visible at end of scope for each member.
|
||||||
reachable_definitions: IndexVec<ScopedPlaceId, ReachableDefinitions>,
|
end_of_scope_members: IndexVec<ScopedMemberId, PlaceState>,
|
||||||
|
|
||||||
|
/// All potentially reachable bindings and declarations, for each symbol.
|
||||||
|
reachable_definitions_by_symbol: IndexVec<ScopedSymbolId, ReachableDefinitions>,
|
||||||
|
|
||||||
|
/// All potentially reachable bindings and declarations, for each member.
|
||||||
|
reachable_definitions_by_member: IndexVec<ScopedMemberId, ReachableDefinitions>,
|
||||||
|
|
||||||
/// Snapshot of bindings in this scope that can be used to resolve a reference in a nested
|
/// Snapshot of bindings in this scope that can be used to resolve a reference in a nested
|
||||||
/// scope.
|
/// scope.
|
||||||
|
@ -361,7 +368,7 @@ impl<'db> UseDefMap<'db> {
|
||||||
&self,
|
&self,
|
||||||
constraint_key: ConstraintKey,
|
constraint_key: ConstraintKey,
|
||||||
enclosing_scope: FileScopeId,
|
enclosing_scope: FileScopeId,
|
||||||
expr: &PlaceExpr,
|
expr: PlaceExprRef,
|
||||||
index: &'db SemanticIndex,
|
index: &'db SemanticIndex,
|
||||||
) -> ApplicableConstraints<'_, 'db> {
|
) -> ApplicableConstraints<'_, 'db> {
|
||||||
match constraint_key {
|
match constraint_key {
|
||||||
|
@ -419,9 +426,29 @@ impl<'db> UseDefMap<'db> {
|
||||||
pub(crate) fn end_of_scope_bindings(
|
pub(crate) fn end_of_scope_bindings(
|
||||||
&self,
|
&self,
|
||||||
place: ScopedPlaceId,
|
place: ScopedPlaceId,
|
||||||
|
) -> BindingWithConstraintsIterator<'_, 'db> {
|
||||||
|
match place {
|
||||||
|
ScopedPlaceId::Symbol(symbol) => self.end_of_scope_symbol_bindings(symbol),
|
||||||
|
ScopedPlaceId::Member(member) => self.end_of_scope_member_bindings(member),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn end_of_scope_symbol_bindings(
|
||||||
|
&self,
|
||||||
|
symbol: ScopedSymbolId,
|
||||||
) -> BindingWithConstraintsIterator<'_, 'db> {
|
) -> BindingWithConstraintsIterator<'_, 'db> {
|
||||||
self.bindings_iterator(
|
self.bindings_iterator(
|
||||||
self.end_of_scope_places[place].bindings(),
|
self.end_of_scope_symbols[symbol].bindings(),
|
||||||
|
BoundnessAnalysis::BasedOnUnboundVisibility,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn end_of_scope_member_bindings(
|
||||||
|
&self,
|
||||||
|
member: ScopedMemberId,
|
||||||
|
) -> BindingWithConstraintsIterator<'_, 'db> {
|
||||||
|
self.bindings_iterator(
|
||||||
|
self.end_of_scope_members[member].bindings(),
|
||||||
BoundnessAnalysis::BasedOnUnboundVisibility,
|
BoundnessAnalysis::BasedOnUnboundVisibility,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -430,10 +457,26 @@ impl<'db> UseDefMap<'db> {
|
||||||
&self,
|
&self,
|
||||||
place: ScopedPlaceId,
|
place: ScopedPlaceId,
|
||||||
) -> BindingWithConstraintsIterator<'_, 'db> {
|
) -> BindingWithConstraintsIterator<'_, 'db> {
|
||||||
self.bindings_iterator(
|
match place {
|
||||||
&self.reachable_definitions[place].bindings,
|
ScopedPlaceId::Symbol(symbol) => self.all_reachable_symbol_bindings(symbol),
|
||||||
BoundnessAnalysis::AssumeBound,
|
ScopedPlaceId::Member(member) => self.all_reachable_member_bindings(member),
|
||||||
)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn all_reachable_symbol_bindings(
|
||||||
|
&self,
|
||||||
|
symbol: ScopedSymbolId,
|
||||||
|
) -> BindingWithConstraintsIterator<'_, 'db> {
|
||||||
|
let bindings = &self.reachable_definitions_by_symbol[symbol].bindings;
|
||||||
|
self.bindings_iterator(bindings, BoundnessAnalysis::AssumeBound)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn all_reachable_member_bindings(
|
||||||
|
&self,
|
||||||
|
symbol: ScopedMemberId,
|
||||||
|
) -> BindingWithConstraintsIterator<'_, 'db> {
|
||||||
|
let bindings = &self.reachable_definitions_by_member[symbol].bindings;
|
||||||
|
self.bindings_iterator(bindings, BoundnessAnalysis::AssumeBound)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn enclosing_snapshot(
|
pub(crate) fn enclosing_snapshot(
|
||||||
|
@ -482,33 +525,69 @@ impl<'db> UseDefMap<'db> {
|
||||||
&'map self,
|
&'map self,
|
||||||
place: ScopedPlaceId,
|
place: ScopedPlaceId,
|
||||||
) -> DeclarationsIterator<'map, 'db> {
|
) -> DeclarationsIterator<'map, 'db> {
|
||||||
let declarations = self.end_of_scope_places[place].declarations();
|
match place {
|
||||||
|
ScopedPlaceId::Symbol(symbol) => self.end_of_scope_symbol_declarations(symbol),
|
||||||
|
ScopedPlaceId::Member(member) => self.end_of_scope_member_declarations(member),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn end_of_scope_symbol_declarations<'map>(
|
||||||
|
&'map self,
|
||||||
|
symbol: ScopedSymbolId,
|
||||||
|
) -> DeclarationsIterator<'map, 'db> {
|
||||||
|
let declarations = self.end_of_scope_symbols[symbol].declarations();
|
||||||
self.declarations_iterator(declarations, BoundnessAnalysis::BasedOnUnboundVisibility)
|
self.declarations_iterator(declarations, BoundnessAnalysis::BasedOnUnboundVisibility)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn end_of_scope_member_declarations<'map>(
|
||||||
|
&'map self,
|
||||||
|
member: ScopedMemberId,
|
||||||
|
) -> DeclarationsIterator<'map, 'db> {
|
||||||
|
let declarations = self.end_of_scope_members[member].declarations();
|
||||||
|
self.declarations_iterator(declarations, BoundnessAnalysis::BasedOnUnboundVisibility)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn all_reachable_symbol_declarations(
|
||||||
|
&self,
|
||||||
|
symbol: ScopedSymbolId,
|
||||||
|
) -> DeclarationsIterator<'_, 'db> {
|
||||||
|
let declarations = &self.reachable_definitions_by_symbol[symbol].declarations;
|
||||||
|
self.declarations_iterator(declarations, BoundnessAnalysis::AssumeBound)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn all_reachable_member_declarations(
|
||||||
|
&self,
|
||||||
|
member: ScopedMemberId,
|
||||||
|
) -> DeclarationsIterator<'_, 'db> {
|
||||||
|
let declarations = &self.reachable_definitions_by_member[member].declarations;
|
||||||
|
self.declarations_iterator(declarations, BoundnessAnalysis::AssumeBound)
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn all_reachable_declarations(
|
pub(crate) fn all_reachable_declarations(
|
||||||
&self,
|
&self,
|
||||||
place: ScopedPlaceId,
|
place: ScopedPlaceId,
|
||||||
) -> DeclarationsIterator<'_, 'db> {
|
) -> DeclarationsIterator<'_, 'db> {
|
||||||
let declarations = &self.reachable_definitions[place].declarations;
|
match place {
|
||||||
self.declarations_iterator(declarations, BoundnessAnalysis::AssumeBound)
|
ScopedPlaceId::Symbol(symbol) => self.all_reachable_symbol_declarations(symbol),
|
||||||
|
ScopedPlaceId::Member(member) => self.all_reachable_member_declarations(member),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn all_end_of_scope_declarations<'map>(
|
pub(crate) fn all_end_of_scope_symbol_declarations<'map>(
|
||||||
&'map self,
|
&'map self,
|
||||||
) -> impl Iterator<Item = (ScopedPlaceId, DeclarationsIterator<'map, 'db>)> + 'map {
|
) -> impl Iterator<Item = (ScopedSymbolId, DeclarationsIterator<'map, 'db>)> + 'map {
|
||||||
(0..self.end_of_scope_places.len())
|
self.end_of_scope_symbols
|
||||||
.map(ScopedPlaceId::from_usize)
|
.indices()
|
||||||
.map(|place_id| (place_id, self.end_of_scope_declarations(place_id)))
|
.map(|symbol_id| (symbol_id, self.end_of_scope_symbol_declarations(symbol_id)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn all_end_of_scope_bindings<'map>(
|
pub(crate) fn all_end_of_scope_symbol_bindings<'map>(
|
||||||
&'map self,
|
&'map self,
|
||||||
) -> impl Iterator<Item = (ScopedPlaceId, BindingWithConstraintsIterator<'map, 'db>)> + 'map
|
) -> impl Iterator<Item = (ScopedSymbolId, BindingWithConstraintsIterator<'map, 'db>)> + 'map
|
||||||
{
|
{
|
||||||
(0..self.end_of_scope_places.len())
|
self.end_of_scope_symbols
|
||||||
.map(ScopedPlaceId::from_usize)
|
.indices()
|
||||||
.map(|place_id| (place_id, self.end_of_scope_bindings(place_id)))
|
.map(|symbol_id| (symbol_id, self.end_of_scope_symbol_bindings(symbol_id)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This function is intended to be called only once inside `TypeInferenceBuilder::infer_function_body`.
|
/// This function is intended to be called only once inside `TypeInferenceBuilder::infer_function_body`.
|
||||||
|
@ -730,7 +809,8 @@ struct ReachableDefinitions {
|
||||||
/// A snapshot of the definitions and constraints state at a particular point in control flow.
|
/// A snapshot of the definitions and constraints state at a particular point in control flow.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub(super) struct FlowSnapshot {
|
pub(super) struct FlowSnapshot {
|
||||||
place_states: IndexVec<ScopedPlaceId, PlaceState>,
|
symbol_states: IndexVec<ScopedSymbolId, PlaceState>,
|
||||||
|
member_states: IndexVec<ScopedMemberId, PlaceState>,
|
||||||
reachability: ScopedReachabilityConstraintId,
|
reachability: ScopedReachabilityConstraintId,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -765,10 +845,14 @@ pub(super) struct UseDefMapBuilder<'db> {
|
||||||
bindings_by_definition: FxHashMap<Definition<'db>, Bindings>,
|
bindings_by_definition: FxHashMap<Definition<'db>, Bindings>,
|
||||||
|
|
||||||
/// Currently live bindings and declarations for each place.
|
/// Currently live bindings and declarations for each place.
|
||||||
place_states: IndexVec<ScopedPlaceId, PlaceState>,
|
symbol_states: IndexVec<ScopedSymbolId, PlaceState>,
|
||||||
|
|
||||||
|
member_states: IndexVec<ScopedMemberId, PlaceState>,
|
||||||
|
|
||||||
/// All potentially reachable bindings and declarations, for each place.
|
/// All potentially reachable bindings and declarations, for each place.
|
||||||
reachable_definitions: IndexVec<ScopedPlaceId, ReachableDefinitions>,
|
reachable_symbol_definitions: IndexVec<ScopedSymbolId, ReachableDefinitions>,
|
||||||
|
|
||||||
|
reachable_member_definitions: IndexVec<ScopedMemberId, ReachableDefinitions>,
|
||||||
|
|
||||||
/// Snapshots of place states in this scope that can be used to resolve a reference in a
|
/// Snapshots of place states in this scope that can be used to resolve a reference in a
|
||||||
/// nested scope.
|
/// nested scope.
|
||||||
|
@ -790,8 +874,10 @@ impl<'db> UseDefMapBuilder<'db> {
|
||||||
node_reachability: FxHashMap::default(),
|
node_reachability: FxHashMap::default(),
|
||||||
declarations_by_binding: FxHashMap::default(),
|
declarations_by_binding: FxHashMap::default(),
|
||||||
bindings_by_definition: FxHashMap::default(),
|
bindings_by_definition: FxHashMap::default(),
|
||||||
place_states: IndexVec::new(),
|
symbol_states: IndexVec::new(),
|
||||||
reachable_definitions: IndexVec::new(),
|
member_states: IndexVec::new(),
|
||||||
|
reachable_member_definitions: IndexVec::new(),
|
||||||
|
reachable_symbol_definitions: IndexVec::new(),
|
||||||
enclosing_snapshots: EnclosingSnapshots::default(),
|
enclosing_snapshots: EnclosingSnapshots::default(),
|
||||||
is_class_scope,
|
is_class_scope,
|
||||||
}
|
}
|
||||||
|
@ -800,7 +886,14 @@ impl<'db> UseDefMapBuilder<'db> {
|
||||||
pub(super) fn mark_unreachable(&mut self) {
|
pub(super) fn mark_unreachable(&mut self) {
|
||||||
self.reachability = ScopedReachabilityConstraintId::ALWAYS_FALSE;
|
self.reachability = ScopedReachabilityConstraintId::ALWAYS_FALSE;
|
||||||
|
|
||||||
for state in &mut self.place_states {
|
for state in &mut self.symbol_states {
|
||||||
|
state.record_reachability_constraint(
|
||||||
|
&mut self.reachability_constraints,
|
||||||
|
ScopedReachabilityConstraintId::ALWAYS_FALSE,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
for state in &mut self.member_states {
|
||||||
state.record_reachability_constraint(
|
state.record_reachability_constraint(
|
||||||
&mut self.reachability_constraints,
|
&mut self.reachability_constraints,
|
||||||
ScopedReachabilityConstraintId::ALWAYS_FALSE,
|
ScopedReachabilityConstraintId::ALWAYS_FALSE,
|
||||||
|
@ -809,42 +902,73 @@ impl<'db> UseDefMapBuilder<'db> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn add_place(&mut self, place: ScopedPlaceId) {
|
pub(super) fn add_place(&mut self, place: ScopedPlaceId) {
|
||||||
let new_place = self
|
match place {
|
||||||
.place_states
|
ScopedPlaceId::Symbol(symbol) => {
|
||||||
.push(PlaceState::undefined(self.reachability));
|
let new_place = self
|
||||||
debug_assert_eq!(place, new_place);
|
.symbol_states
|
||||||
let new_place = self.reachable_definitions.push(ReachableDefinitions {
|
.push(PlaceState::undefined(self.reachability));
|
||||||
bindings: Bindings::unbound(self.reachability),
|
debug_assert_eq!(symbol, new_place);
|
||||||
declarations: Declarations::undeclared(self.reachability),
|
let new_place = self
|
||||||
});
|
.reachable_symbol_definitions
|
||||||
debug_assert_eq!(place, new_place);
|
.push(ReachableDefinitions {
|
||||||
|
bindings: Bindings::unbound(self.reachability),
|
||||||
|
declarations: Declarations::undeclared(self.reachability),
|
||||||
|
});
|
||||||
|
debug_assert_eq!(symbol, new_place);
|
||||||
|
}
|
||||||
|
ScopedPlaceId::Member(member) => {
|
||||||
|
let new_place = self
|
||||||
|
.member_states
|
||||||
|
.push(PlaceState::undefined(self.reachability));
|
||||||
|
debug_assert_eq!(member, new_place);
|
||||||
|
let new_place = self
|
||||||
|
.reachable_member_definitions
|
||||||
|
.push(ReachableDefinitions {
|
||||||
|
bindings: Bindings::unbound(self.reachability),
|
||||||
|
declarations: Declarations::undeclared(self.reachability),
|
||||||
|
});
|
||||||
|
debug_assert_eq!(member, new_place);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn record_binding(
|
pub(super) fn record_binding(&mut self, place: ScopedPlaceId, binding: Definition<'db>) {
|
||||||
&mut self,
|
let bindings = match place {
|
||||||
place: ScopedPlaceId,
|
ScopedPlaceId::Symbol(symbol) => self.symbol_states[symbol].bindings(),
|
||||||
binding: Definition<'db>,
|
ScopedPlaceId::Member(member) => self.member_states[member].bindings(),
|
||||||
is_place_name: bool,
|
};
|
||||||
) {
|
|
||||||
self.bindings_by_definition
|
self.bindings_by_definition
|
||||||
.insert(binding, self.place_states[place].bindings().clone());
|
.insert(binding, bindings.clone());
|
||||||
|
|
||||||
let def_id = self.all_definitions.push(DefinitionState::Defined(binding));
|
let def_id = self.all_definitions.push(DefinitionState::Defined(binding));
|
||||||
let place_state = &mut self.place_states[place];
|
let place_state = match place {
|
||||||
|
ScopedPlaceId::Symbol(symbol) => &mut self.symbol_states[symbol],
|
||||||
|
ScopedPlaceId::Member(member) => &mut self.member_states[member],
|
||||||
|
};
|
||||||
self.declarations_by_binding
|
self.declarations_by_binding
|
||||||
.insert(binding, place_state.declarations().clone());
|
.insert(binding, place_state.declarations().clone());
|
||||||
place_state.record_binding(
|
place_state.record_binding(
|
||||||
def_id,
|
def_id,
|
||||||
self.reachability,
|
self.reachability,
|
||||||
self.is_class_scope,
|
self.is_class_scope,
|
||||||
is_place_name,
|
place.is_symbol(),
|
||||||
);
|
);
|
||||||
|
|
||||||
self.reachable_definitions[place].bindings.record_binding(
|
let bindings = match place {
|
||||||
|
ScopedPlaceId::Symbol(symbol) => {
|
||||||
|
&mut self.reachable_symbol_definitions[symbol].bindings
|
||||||
|
}
|
||||||
|
ScopedPlaceId::Member(member) => {
|
||||||
|
&mut self.reachable_member_definitions[member].bindings
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
bindings.record_binding(
|
||||||
def_id,
|
def_id,
|
||||||
self.reachability,
|
self.reachability,
|
||||||
self.is_class_scope,
|
self.is_class_scope,
|
||||||
is_place_name,
|
place.is_symbol(),
|
||||||
PreviousDefinitions::AreKept,
|
PreviousDefinitions::AreKept,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -869,7 +993,12 @@ impl<'db> UseDefMapBuilder<'db> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let narrowing_constraint = predicate.into();
|
let narrowing_constraint = predicate.into();
|
||||||
for state in &mut self.place_states {
|
for state in &mut self.symbol_states {
|
||||||
|
state
|
||||||
|
.record_narrowing_constraint(&mut self.narrowing_constraints, narrowing_constraint);
|
||||||
|
}
|
||||||
|
|
||||||
|
for state in &mut self.member_states {
|
||||||
state
|
state
|
||||||
.record_narrowing_constraint(&mut self.narrowing_constraints, narrowing_constraint);
|
.record_narrowing_constraint(&mut self.narrowing_constraints, narrowing_constraint);
|
||||||
}
|
}
|
||||||
|
@ -880,8 +1009,8 @@ impl<'db> UseDefMapBuilder<'db> {
|
||||||
/// This is only used for `*`-import reachability constraints, which are handled differently
|
/// This is only used for `*`-import reachability constraints, which are handled differently
|
||||||
/// to most other reachability constraints. See the doc-comment for
|
/// to most other reachability constraints. See the doc-comment for
|
||||||
/// [`Self::record_and_negate_star_import_reachability_constraint`] for more details.
|
/// [`Self::record_and_negate_star_import_reachability_constraint`] for more details.
|
||||||
pub(super) fn single_place_snapshot(&self, place: ScopedPlaceId) -> PlaceState {
|
pub(super) fn single_symbol_place_snapshot(&self, symbol: ScopedSymbolId) -> PlaceState {
|
||||||
self.place_states[place].clone()
|
self.symbol_states[symbol].clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This method exists solely for handling `*`-import reachability constraints.
|
/// This method exists solely for handling `*`-import reachability constraints.
|
||||||
|
@ -916,7 +1045,7 @@ impl<'db> UseDefMapBuilder<'db> {
|
||||||
pub(super) fn record_and_negate_star_import_reachability_constraint(
|
pub(super) fn record_and_negate_star_import_reachability_constraint(
|
||||||
&mut self,
|
&mut self,
|
||||||
reachability_id: ScopedReachabilityConstraintId,
|
reachability_id: ScopedReachabilityConstraintId,
|
||||||
symbol: ScopedPlaceId,
|
symbol: ScopedSymbolId,
|
||||||
pre_definition_state: PlaceState,
|
pre_definition_state: PlaceState,
|
||||||
) {
|
) {
|
||||||
let negated_reachability_id = self
|
let negated_reachability_id = self
|
||||||
|
@ -924,17 +1053,17 @@ impl<'db> UseDefMapBuilder<'db> {
|
||||||
.add_not_constraint(reachability_id);
|
.add_not_constraint(reachability_id);
|
||||||
|
|
||||||
let mut post_definition_state =
|
let mut post_definition_state =
|
||||||
std::mem::replace(&mut self.place_states[symbol], pre_definition_state);
|
std::mem::replace(&mut self.symbol_states[symbol], pre_definition_state);
|
||||||
|
|
||||||
post_definition_state
|
post_definition_state
|
||||||
.record_reachability_constraint(&mut self.reachability_constraints, reachability_id);
|
.record_reachability_constraint(&mut self.reachability_constraints, reachability_id);
|
||||||
|
|
||||||
self.place_states[symbol].record_reachability_constraint(
|
self.symbol_states[symbol].record_reachability_constraint(
|
||||||
&mut self.reachability_constraints,
|
&mut self.reachability_constraints,
|
||||||
negated_reachability_id,
|
negated_reachability_id,
|
||||||
);
|
);
|
||||||
|
|
||||||
self.place_states[symbol].merge(
|
self.symbol_states[symbol].merge(
|
||||||
post_definition_state,
|
post_definition_state,
|
||||||
&mut self.narrowing_constraints,
|
&mut self.narrowing_constraints,
|
||||||
&mut self.reachability_constraints,
|
&mut self.reachability_constraints,
|
||||||
|
@ -949,7 +1078,11 @@ impl<'db> UseDefMapBuilder<'db> {
|
||||||
.reachability_constraints
|
.reachability_constraints
|
||||||
.add_and_constraint(self.reachability, constraint);
|
.add_and_constraint(self.reachability, constraint);
|
||||||
|
|
||||||
for state in &mut self.place_states {
|
for state in &mut self.symbol_states {
|
||||||
|
state.record_reachability_constraint(&mut self.reachability_constraints, constraint);
|
||||||
|
}
|
||||||
|
|
||||||
|
for state in &mut self.member_states {
|
||||||
state.record_reachability_constraint(&mut self.reachability_constraints, constraint);
|
state.record_reachability_constraint(&mut self.reachability_constraints, constraint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -962,56 +1095,81 @@ impl<'db> UseDefMapBuilder<'db> {
|
||||||
let def_id = self
|
let def_id = self
|
||||||
.all_definitions
|
.all_definitions
|
||||||
.push(DefinitionState::Defined(declaration));
|
.push(DefinitionState::Defined(declaration));
|
||||||
let place_state = &mut self.place_states[place];
|
|
||||||
|
let place_state = match place {
|
||||||
|
ScopedPlaceId::Symbol(symbol) => &mut self.symbol_states[symbol],
|
||||||
|
ScopedPlaceId::Member(member) => &mut self.member_states[member],
|
||||||
|
};
|
||||||
|
|
||||||
self.bindings_by_definition
|
self.bindings_by_definition
|
||||||
.insert(declaration, place_state.bindings().clone());
|
.insert(declaration, place_state.bindings().clone());
|
||||||
place_state.record_declaration(def_id, self.reachability);
|
place_state.record_declaration(def_id, self.reachability);
|
||||||
|
|
||||||
self.reachable_definitions[place]
|
let definitions = match place {
|
||||||
.declarations
|
ScopedPlaceId::Symbol(symbol) => &mut self.reachable_symbol_definitions[symbol],
|
||||||
.record_declaration(def_id, self.reachability, PreviousDefinitions::AreKept);
|
ScopedPlaceId::Member(member) => &mut self.reachable_member_definitions[member],
|
||||||
|
};
|
||||||
|
|
||||||
|
definitions.declarations.record_declaration(
|
||||||
|
def_id,
|
||||||
|
self.reachability,
|
||||||
|
PreviousDefinitions::AreKept,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn record_declaration_and_binding(
|
pub(super) fn record_declaration_and_binding(
|
||||||
&mut self,
|
&mut self,
|
||||||
place: ScopedPlaceId,
|
place: ScopedPlaceId,
|
||||||
definition: Definition<'db>,
|
definition: Definition<'db>,
|
||||||
is_place_name: bool,
|
|
||||||
) {
|
) {
|
||||||
// We don't need to store anything in self.bindings_by_declaration or
|
// We don't need to store anything in self.bindings_by_declaration or
|
||||||
// self.declarations_by_binding.
|
// self.declarations_by_binding.
|
||||||
let def_id = self
|
let def_id = self
|
||||||
.all_definitions
|
.all_definitions
|
||||||
.push(DefinitionState::Defined(definition));
|
.push(DefinitionState::Defined(definition));
|
||||||
let place_state = &mut self.place_states[place];
|
let place_state = match place {
|
||||||
|
ScopedPlaceId::Symbol(symbol) => &mut self.symbol_states[symbol],
|
||||||
|
ScopedPlaceId::Member(member) => &mut self.member_states[member],
|
||||||
|
};
|
||||||
place_state.record_declaration(def_id, self.reachability);
|
place_state.record_declaration(def_id, self.reachability);
|
||||||
place_state.record_binding(
|
place_state.record_binding(
|
||||||
def_id,
|
def_id,
|
||||||
self.reachability,
|
self.reachability,
|
||||||
self.is_class_scope,
|
self.is_class_scope,
|
||||||
is_place_name,
|
place.is_symbol(),
|
||||||
);
|
);
|
||||||
|
|
||||||
self.reachable_definitions[place]
|
let reachable_definitions = match place {
|
||||||
.declarations
|
ScopedPlaceId::Symbol(symbol) => &mut self.reachable_symbol_definitions[symbol],
|
||||||
.record_declaration(def_id, self.reachability, PreviousDefinitions::AreKept);
|
ScopedPlaceId::Member(member) => &mut self.reachable_member_definitions[member],
|
||||||
self.reachable_definitions[place].bindings.record_binding(
|
};
|
||||||
|
|
||||||
|
reachable_definitions.declarations.record_declaration(
|
||||||
|
def_id,
|
||||||
|
self.reachability,
|
||||||
|
PreviousDefinitions::AreKept,
|
||||||
|
);
|
||||||
|
reachable_definitions.bindings.record_binding(
|
||||||
def_id,
|
def_id,
|
||||||
self.reachability,
|
self.reachability,
|
||||||
self.is_class_scope,
|
self.is_class_scope,
|
||||||
is_place_name,
|
place.is_symbol(),
|
||||||
PreviousDefinitions::AreKept,
|
PreviousDefinitions::AreKept,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn delete_binding(&mut self, place: ScopedPlaceId, is_place_name: bool) {
|
pub(super) fn delete_binding(&mut self, place: ScopedPlaceId) {
|
||||||
let def_id = self.all_definitions.push(DefinitionState::Deleted);
|
let def_id = self.all_definitions.push(DefinitionState::Deleted);
|
||||||
let place_state = &mut self.place_states[place];
|
let place_state = match place {
|
||||||
|
ScopedPlaceId::Symbol(symbol) => &mut self.symbol_states[symbol],
|
||||||
|
ScopedPlaceId::Member(member) => &mut self.member_states[member],
|
||||||
|
};
|
||||||
|
|
||||||
place_state.record_binding(
|
place_state.record_binding(
|
||||||
def_id,
|
def_id,
|
||||||
self.reachability,
|
self.reachability,
|
||||||
self.is_class_scope,
|
self.is_class_scope,
|
||||||
is_place_name,
|
place.is_symbol(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1021,11 +1179,13 @@ impl<'db> UseDefMapBuilder<'db> {
|
||||||
use_id: ScopedUseId,
|
use_id: ScopedUseId,
|
||||||
node_key: NodeKey,
|
node_key: NodeKey,
|
||||||
) {
|
) {
|
||||||
|
let bindings = match place {
|
||||||
|
ScopedPlaceId::Symbol(symbol) => &mut self.symbol_states[symbol].bindings(),
|
||||||
|
ScopedPlaceId::Member(member) => &mut self.member_states[member].bindings(),
|
||||||
|
};
|
||||||
// We have a use of a place; clone the current bindings for that place, and record them
|
// We have a use of a place; clone the current bindings for that place, and record them
|
||||||
// as the live bindings for this use.
|
// as the live bindings for this use.
|
||||||
let new_use = self
|
let new_use = self.bindings_by_use.push(bindings.clone());
|
||||||
.bindings_by_use
|
|
||||||
.push(self.place_states[place].bindings().clone());
|
|
||||||
debug_assert_eq!(use_id, new_use);
|
debug_assert_eq!(use_id, new_use);
|
||||||
|
|
||||||
// Track reachability of all uses of places to silence `unresolved-reference`
|
// Track reachability of all uses of places to silence `unresolved-reference`
|
||||||
|
@ -1041,28 +1201,30 @@ impl<'db> UseDefMapBuilder<'db> {
|
||||||
&mut self,
|
&mut self,
|
||||||
enclosing_place: ScopedPlaceId,
|
enclosing_place: ScopedPlaceId,
|
||||||
scope: ScopeKind,
|
scope: ScopeKind,
|
||||||
enclosing_place_expr: &PlaceExprWithFlags,
|
enclosing_place_expr: PlaceExprRef,
|
||||||
) -> ScopedEnclosingSnapshotId {
|
) -> ScopedEnclosingSnapshotId {
|
||||||
|
let bindings = match enclosing_place {
|
||||||
|
ScopedPlaceId::Symbol(symbol) => self.symbol_states[symbol].bindings(),
|
||||||
|
ScopedPlaceId::Member(member) => self.member_states[member].bindings(),
|
||||||
|
};
|
||||||
|
|
||||||
// Names bound in class scopes are never visible to nested scopes (but attributes/subscripts are visible),
|
// Names bound in class scopes are never visible to nested scopes (but attributes/subscripts are visible),
|
||||||
// so we never need to save eager scope bindings in a class scope.
|
// so we never need to save eager scope bindings in a class scope.
|
||||||
if (scope.is_class() && enclosing_place_expr.is_name()) || !enclosing_place_expr.is_bound()
|
if (scope.is_class() && enclosing_place.is_symbol()) || !enclosing_place_expr.is_bound() {
|
||||||
{
|
|
||||||
self.enclosing_snapshots.push(EnclosingSnapshot::Constraint(
|
self.enclosing_snapshots.push(EnclosingSnapshot::Constraint(
|
||||||
self.place_states[enclosing_place]
|
bindings.unbound_narrowing_constraint(),
|
||||||
.bindings()
|
|
||||||
.unbound_narrowing_constraint(),
|
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
self.enclosing_snapshots.push(EnclosingSnapshot::Bindings(
|
self.enclosing_snapshots
|
||||||
self.place_states[enclosing_place].bindings().clone(),
|
.push(EnclosingSnapshot::Bindings(bindings.clone()))
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Take a snapshot of the current visible-places state.
|
/// Take a snapshot of the current visible-places state.
|
||||||
pub(super) fn snapshot(&self) -> FlowSnapshot {
|
pub(super) fn snapshot(&self) -> FlowSnapshot {
|
||||||
FlowSnapshot {
|
FlowSnapshot {
|
||||||
place_states: self.place_states.clone(),
|
symbol_states: self.symbol_states.clone(),
|
||||||
|
member_states: self.member_states.clone(),
|
||||||
reachability: self.reachability,
|
reachability: self.reachability,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1072,18 +1234,23 @@ impl<'db> UseDefMapBuilder<'db> {
|
||||||
// We never remove places from `place_states` (it's an IndexVec, and the place
|
// We never remove places from `place_states` (it's an IndexVec, and the place
|
||||||
// IDs must line up), so the current number of known places must always be equal to or
|
// IDs must line up), so the current number of known places must always be equal to or
|
||||||
// greater than the number of known places in a previously-taken snapshot.
|
// greater than the number of known places in a previously-taken snapshot.
|
||||||
let num_places = self.place_states.len();
|
let num_symbols = self.symbol_states.len();
|
||||||
debug_assert!(num_places >= snapshot.place_states.len());
|
let num_members = self.member_states.len();
|
||||||
|
debug_assert!(num_symbols >= snapshot.symbol_states.len());
|
||||||
|
|
||||||
// Restore the current visible-definitions state to the given snapshot.
|
// Restore the current visible-definitions state to the given snapshot.
|
||||||
self.place_states = snapshot.place_states;
|
self.symbol_states = snapshot.symbol_states;
|
||||||
|
self.member_states = snapshot.member_states;
|
||||||
self.reachability = snapshot.reachability;
|
self.reachability = snapshot.reachability;
|
||||||
|
|
||||||
// If the snapshot we are restoring is missing some places we've recorded since, we need
|
// If the snapshot we are restoring is missing some places we've recorded since, we need
|
||||||
// to fill them in so the place IDs continue to line up. Since they don't exist in the
|
// to fill them in so the place IDs continue to line up. Since they don't exist in the
|
||||||
// snapshot, the correct state to fill them in with is "undefined".
|
// snapshot, the correct state to fill them in with is "undefined".
|
||||||
self.place_states
|
self.symbol_states
|
||||||
.resize(num_places, PlaceState::undefined(self.reachability));
|
.resize(num_symbols, PlaceState::undefined(self.reachability));
|
||||||
|
|
||||||
|
self.member_states
|
||||||
|
.resize(num_members, PlaceState::undefined(self.reachability));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Merge the given snapshot into the current state, reflecting that we might have taken either
|
/// Merge the given snapshot into the current state, reflecting that we might have taken either
|
||||||
|
@ -1108,10 +1275,29 @@ impl<'db> UseDefMapBuilder<'db> {
|
||||||
// We never remove places from `place_states` (it's an IndexVec, and the place
|
// We never remove places from `place_states` (it's an IndexVec, and the place
|
||||||
// IDs must line up), so the current number of known places must always be equal to or
|
// IDs must line up), so the current number of known places must always be equal to or
|
||||||
// greater than the number of known places in a previously-taken snapshot.
|
// greater than the number of known places in a previously-taken snapshot.
|
||||||
debug_assert!(self.place_states.len() >= snapshot.place_states.len());
|
debug_assert!(self.symbol_states.len() >= snapshot.symbol_states.len());
|
||||||
|
debug_assert!(self.member_states.len() >= snapshot.member_states.len());
|
||||||
|
|
||||||
let mut snapshot_definitions_iter = snapshot.place_states.into_iter();
|
let mut snapshot_definitions_iter = snapshot.symbol_states.into_iter();
|
||||||
for current in &mut self.place_states {
|
for current in &mut self.symbol_states {
|
||||||
|
if let Some(snapshot) = snapshot_definitions_iter.next() {
|
||||||
|
current.merge(
|
||||||
|
snapshot,
|
||||||
|
&mut self.narrowing_constraints,
|
||||||
|
&mut self.reachability_constraints,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
current.merge(
|
||||||
|
PlaceState::undefined(snapshot.reachability),
|
||||||
|
&mut self.narrowing_constraints,
|
||||||
|
&mut self.reachability_constraints,
|
||||||
|
);
|
||||||
|
// Place not present in snapshot, so it's unbound/undeclared from that path.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut snapshot_definitions_iter = snapshot.member_states.into_iter();
|
||||||
|
for current in &mut self.member_states {
|
||||||
if let Some(snapshot) = snapshot_definitions_iter.next() {
|
if let Some(snapshot) = snapshot_definitions_iter.next() {
|
||||||
current.merge(
|
current.merge(
|
||||||
snapshot,
|
snapshot,
|
||||||
|
@ -1142,10 +1328,21 @@ impl<'db> UseDefMapBuilder<'db> {
|
||||||
for constraint in self.node_reachability.values() {
|
for constraint in self.node_reachability.values() {
|
||||||
self.reachability_constraints.mark_used(*constraint);
|
self.reachability_constraints.mark_used(*constraint);
|
||||||
}
|
}
|
||||||
for place_state in &mut self.place_states {
|
for symbol_state in &mut self.symbol_states {
|
||||||
place_state.finish(&mut self.reachability_constraints);
|
symbol_state.finish(&mut self.reachability_constraints);
|
||||||
}
|
}
|
||||||
for reachable_definition in &mut self.reachable_definitions {
|
for member_state in &mut self.member_states {
|
||||||
|
member_state.finish(&mut self.reachability_constraints);
|
||||||
|
}
|
||||||
|
for reachable_definition in &mut self.reachable_symbol_definitions {
|
||||||
|
reachable_definition
|
||||||
|
.bindings
|
||||||
|
.finish(&mut self.reachability_constraints);
|
||||||
|
reachable_definition
|
||||||
|
.declarations
|
||||||
|
.finish(&mut self.reachability_constraints);
|
||||||
|
}
|
||||||
|
for reachable_definition in &mut self.reachable_member_definitions {
|
||||||
reachable_definition
|
reachable_definition
|
||||||
.bindings
|
.bindings
|
||||||
.finish(&mut self.reachability_constraints);
|
.finish(&mut self.reachability_constraints);
|
||||||
|
@ -1169,8 +1366,10 @@ impl<'db> UseDefMapBuilder<'db> {
|
||||||
self.mark_reachability_constraints();
|
self.mark_reachability_constraints();
|
||||||
|
|
||||||
self.all_definitions.shrink_to_fit();
|
self.all_definitions.shrink_to_fit();
|
||||||
self.place_states.shrink_to_fit();
|
self.symbol_states.shrink_to_fit();
|
||||||
self.reachable_definitions.shrink_to_fit();
|
self.member_states.shrink_to_fit();
|
||||||
|
self.reachable_symbol_definitions.shrink_to_fit();
|
||||||
|
self.reachable_member_definitions.shrink_to_fit();
|
||||||
self.bindings_by_use.shrink_to_fit();
|
self.bindings_by_use.shrink_to_fit();
|
||||||
self.node_reachability.shrink_to_fit();
|
self.node_reachability.shrink_to_fit();
|
||||||
self.declarations_by_binding.shrink_to_fit();
|
self.declarations_by_binding.shrink_to_fit();
|
||||||
|
@ -1184,8 +1383,10 @@ impl<'db> UseDefMapBuilder<'db> {
|
||||||
reachability_constraints: self.reachability_constraints.build(),
|
reachability_constraints: self.reachability_constraints.build(),
|
||||||
bindings_by_use: self.bindings_by_use,
|
bindings_by_use: self.bindings_by_use,
|
||||||
node_reachability: self.node_reachability,
|
node_reachability: self.node_reachability,
|
||||||
end_of_scope_places: self.place_states,
|
end_of_scope_symbols: self.symbol_states,
|
||||||
reachable_definitions: self.reachable_definitions,
|
end_of_scope_members: self.member_states,
|
||||||
|
reachable_definitions_by_symbol: self.reachable_symbol_definitions,
|
||||||
|
reachable_definitions_by_member: self.reachable_member_definitions,
|
||||||
declarations_by_binding: self.declarations_by_binding,
|
declarations_by_binding: self.declarations_by_binding,
|
||||||
bindings_by_definition: self.bindings_by_definition,
|
bindings_by_definition: self.bindings_by_definition,
|
||||||
enclosing_snapshots: self.enclosing_snapshots,
|
enclosing_snapshots: self.enclosing_snapshots,
|
||||||
|
|
|
@ -7,7 +7,7 @@ use ruff_source_file::LineIndex;
|
||||||
use crate::Db;
|
use crate::Db;
|
||||||
use crate::module_name::ModuleName;
|
use crate::module_name::ModuleName;
|
||||||
use crate::module_resolver::{KnownModule, Module, resolve_module};
|
use crate::module_resolver::{KnownModule, Module, resolve_module};
|
||||||
use crate::semantic_index::place::FileScopeId;
|
use crate::semantic_index::scope::FileScopeId;
|
||||||
use crate::semantic_index::semantic_index;
|
use crate::semantic_index::semantic_index;
|
||||||
use crate::types::ide_support::all_declarations_and_bindings;
|
use crate::types::ide_support::all_declarations_and_bindings;
|
||||||
use crate::types::{Type, binding_type, infer_scope_types};
|
use crate::types::{Type, binding_type, infer_scope_types};
|
||||||
|
|
|
@ -33,7 +33,8 @@ use crate::module_name::ModuleName;
|
||||||
use crate::module_resolver::{KnownModule, resolve_module};
|
use crate::module_resolver::{KnownModule, resolve_module};
|
||||||
use crate::place::{Boundness, Place, PlaceAndQualifiers, imported_symbol};
|
use crate::place::{Boundness, Place, PlaceAndQualifiers, imported_symbol};
|
||||||
use crate::semantic_index::definition::Definition;
|
use crate::semantic_index::definition::Definition;
|
||||||
use crate::semantic_index::place::{ScopeId, ScopedPlaceId};
|
use crate::semantic_index::place::ScopedPlaceId;
|
||||||
|
use crate::semantic_index::scope::ScopeId;
|
||||||
use crate::semantic_index::{imported_modules, place_table, semantic_index};
|
use crate::semantic_index::{imported_modules, place_table, semantic_index};
|
||||||
use crate::suppression::check_suppressions;
|
use crate::suppression::check_suppressions;
|
||||||
use crate::types::call::{Binding, Bindings, CallArguments, CallableBinding};
|
use crate::types::call::{Binding, Bindings, CallArguments, CallableBinding};
|
||||||
|
@ -8840,7 +8841,7 @@ impl<'db> TypeIsType<'db> {
|
||||||
let (scope, place) = self.place_info(db)?;
|
let (scope, place) = self.place_info(db)?;
|
||||||
let table = place_table(db, scope);
|
let table = place_table(db, scope);
|
||||||
|
|
||||||
Some(format!("{}", table.place_expr(place)))
|
Some(format!("{}", table.place(place)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unbound(db: &'db dyn Db, ty: Type<'db>) -> Type<'db> {
|
pub fn unbound(db: &'db dyn Db, ty: Type<'db>) -> Type<'db> {
|
||||||
|
|
|
@ -10,7 +10,7 @@ use super::{
|
||||||
infer_expression_type, infer_unpack_types,
|
infer_expression_type, infer_unpack_types,
|
||||||
};
|
};
|
||||||
use crate::semantic_index::definition::{Definition, DefinitionState};
|
use crate::semantic_index::definition::{Definition, DefinitionState};
|
||||||
use crate::semantic_index::place::NodeWithScopeKind;
|
use crate::semantic_index::scope::NodeWithScopeKind;
|
||||||
use crate::semantic_index::{DeclarationWithConstraint, SemanticIndex, attribute_declarations};
|
use crate::semantic_index::{DeclarationWithConstraint, SemanticIndex, attribute_declarations};
|
||||||
use crate::types::context::InferContext;
|
use crate::types::context::InferContext;
|
||||||
use crate::types::diagnostic::{INVALID_LEGACY_TYPE_VARIABLE, INVALID_TYPE_ALIAS_TYPE};
|
use crate::types::diagnostic::{INVALID_LEGACY_TYPE_VARIABLE, INVALID_TYPE_ALIAS_TYPE};
|
||||||
|
@ -36,8 +36,9 @@ use crate::{
|
||||||
semantic_index::{
|
semantic_index::{
|
||||||
attribute_assignments,
|
attribute_assignments,
|
||||||
definition::{DefinitionKind, TargetKind},
|
definition::{DefinitionKind, TargetKind},
|
||||||
place::ScopeId,
|
place_table,
|
||||||
place_table, semantic_index, use_def_map,
|
scope::ScopeId,
|
||||||
|
semantic_index, use_def_map,
|
||||||
},
|
},
|
||||||
types::{
|
types::{
|
||||||
CallArguments, CallError, CallErrorKind, MetaclassCandidate, UnionBuilder, UnionType,
|
CallArguments, CallError, CallErrorKind, MetaclassCandidate, UnionBuilder, UnionType,
|
||||||
|
@ -1820,7 +1821,7 @@ impl<'db> ClassLiteral<'db> {
|
||||||
let table = place_table(db, class_body_scope);
|
let table = place_table(db, class_body_scope);
|
||||||
|
|
||||||
let use_def = use_def_map(db, class_body_scope);
|
let use_def = use_def_map(db, class_body_scope);
|
||||||
for (place_id, declarations) in use_def.all_end_of_scope_declarations() {
|
for (symbol_id, declarations) in use_def.all_end_of_scope_symbol_declarations() {
|
||||||
// Here, we exclude all declarations that are not annotated assignments. We need this because
|
// Here, we exclude all declarations that are not annotated assignments. We need this because
|
||||||
// things like function definitions and nested classes would otherwise be considered dataclass
|
// things like function definitions and nested classes would otherwise be considered dataclass
|
||||||
// fields. The check is too broad in the sense that it also excludes (weird) constructs where
|
// fields. The check is too broad in the sense that it also excludes (weird) constructs where
|
||||||
|
@ -1842,7 +1843,7 @@ impl<'db> ClassLiteral<'db> {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let place_expr = table.place_expr(place_id);
|
let symbol = table.symbol(symbol_id);
|
||||||
|
|
||||||
if let Ok(attr) = place_from_declarations(db, declarations) {
|
if let Ok(attr) = place_from_declarations(db, declarations) {
|
||||||
if attr.is_class_var() {
|
if attr.is_class_var() {
|
||||||
|
@ -1850,11 +1851,11 @@ impl<'db> ClassLiteral<'db> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(attr_ty) = attr.place.ignore_possibly_unbound() {
|
if let Some(attr_ty) = attr.place.ignore_possibly_unbound() {
|
||||||
let bindings = use_def.end_of_scope_bindings(place_id);
|
let bindings = use_def.end_of_scope_symbol_bindings(symbol_id);
|
||||||
let default_ty = place_from_bindings(db, bindings).ignore_possibly_unbound();
|
let default_ty = place_from_bindings(db, bindings).ignore_possibly_unbound();
|
||||||
|
|
||||||
attributes.insert(
|
attributes.insert(
|
||||||
place_expr.expect_name().clone(),
|
symbol.name().clone(),
|
||||||
DataclassField {
|
DataclassField {
|
||||||
field_ty: attr_ty.apply_optional_specialization(db, specialization),
|
field_ty: attr_ty.apply_optional_specialization(db, specialization),
|
||||||
default_ty: default_ty
|
default_ty: default_ty
|
||||||
|
@ -2041,9 +2042,9 @@ impl<'db> ClassLiteral<'db> {
|
||||||
let is_method_reachable =
|
let is_method_reachable =
|
||||||
if let Some(method_def) = method_scope.node(db).as_function(&module) {
|
if let Some(method_def) = method_scope.node(db).as_function(&module) {
|
||||||
let method = index.expect_single_definition(method_def);
|
let method = index.expect_single_definition(method_def);
|
||||||
let method_place = class_table.place_id_by_name(&method_def.name).unwrap();
|
let method_place = class_table.symbol_id(&method_def.name).unwrap();
|
||||||
class_map
|
class_map
|
||||||
.all_reachable_bindings(method_place)
|
.all_reachable_symbol_bindings(method_place)
|
||||||
.find_map(|bind| {
|
.find_map(|bind| {
|
||||||
(bind.binding.is_defined_and(|def| def == method))
|
(bind.binding.is_defined_and(|def| def == method))
|
||||||
.then(|| class_map.is_binding_reachable(db, &bind))
|
.then(|| class_map.is_binding_reachable(db, &bind))
|
||||||
|
@ -2258,10 +2259,10 @@ impl<'db> ClassLiteral<'db> {
|
||||||
let body_scope = self.body_scope(db);
|
let body_scope = self.body_scope(db);
|
||||||
let table = place_table(db, body_scope);
|
let table = place_table(db, body_scope);
|
||||||
|
|
||||||
if let Some(place_id) = table.place_id_by_name(name) {
|
if let Some(symbol_id) = table.symbol_id(name) {
|
||||||
let use_def = use_def_map(db, body_scope);
|
let use_def = use_def_map(db, body_scope);
|
||||||
|
|
||||||
let declarations = use_def.end_of_scope_declarations(place_id);
|
let declarations = use_def.end_of_scope_symbol_declarations(symbol_id);
|
||||||
let declared_and_qualifiers = place_from_declarations(db, declarations);
|
let declared_and_qualifiers = place_from_declarations(db, declarations);
|
||||||
|
|
||||||
match declared_and_qualifiers {
|
match declared_and_qualifiers {
|
||||||
|
@ -2288,7 +2289,7 @@ impl<'db> ClassLiteral<'db> {
|
||||||
|
|
||||||
// The attribute is declared in the class body.
|
// The attribute is declared in the class body.
|
||||||
|
|
||||||
let bindings = use_def.end_of_scope_bindings(place_id);
|
let bindings = use_def.end_of_scope_symbol_bindings(symbol_id);
|
||||||
let inferred = place_from_bindings(db, bindings);
|
let inferred = place_from_bindings(db, bindings);
|
||||||
let has_binding = !inferred.is_unbound();
|
let has_binding = !inferred.is_unbound();
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ use ruff_text_size::{Ranged, TextRange};
|
||||||
use super::{Type, TypeCheckDiagnostics, binding_type};
|
use super::{Type, TypeCheckDiagnostics, binding_type};
|
||||||
|
|
||||||
use crate::lint::LintSource;
|
use crate::lint::LintSource;
|
||||||
use crate::semantic_index::place::ScopeId;
|
use crate::semantic_index::scope::ScopeId;
|
||||||
use crate::semantic_index::semantic_index;
|
use crate::semantic_index::semantic_index;
|
||||||
use crate::types::function::FunctionDecorators;
|
use crate::types::function::FunctionDecorators;
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
|
@ -82,9 +82,8 @@ pub(crate) fn enum_metadata<'db>(
|
||||||
// TODO: handle `StrEnum` which uses lowercase names as values when using `auto()`.
|
// TODO: handle `StrEnum` which uses lowercase names as values when using `auto()`.
|
||||||
let mut auto_counter = 0;
|
let mut auto_counter = 0;
|
||||||
|
|
||||||
let ignored_names: Option<Vec<&str>> = if let Some(ignore) = table.place_id_by_name("_ignore_")
|
let ignored_names: Option<Vec<&str>> = if let Some(ignore) = table.symbol_id("_ignore_") {
|
||||||
{
|
let ignore_bindings = use_def_map.all_reachable_symbol_bindings(ignore);
|
||||||
let ignore_bindings = use_def_map.all_reachable_bindings(ignore);
|
|
||||||
let ignore_place = place_from_bindings(db, ignore_bindings);
|
let ignore_place = place_from_bindings(db, ignore_bindings);
|
||||||
|
|
||||||
match ignore_place {
|
match ignore_place {
|
||||||
|
@ -101,9 +100,9 @@ pub(crate) fn enum_metadata<'db>(
|
||||||
let mut aliases = FxHashMap::default();
|
let mut aliases = FxHashMap::default();
|
||||||
|
|
||||||
let members = use_def_map
|
let members = use_def_map
|
||||||
.all_end_of_scope_bindings()
|
.all_end_of_scope_symbol_bindings()
|
||||||
.filter_map(|(place_id, bindings)| {
|
.filter_map(|(symbol_id, bindings)| {
|
||||||
let name = table.place_expr(place_id).as_name()?;
|
let name = table.symbol(symbol_id).name();
|
||||||
|
|
||||||
if name.starts_with("__") && !name.ends_with("__") {
|
if name.starts_with("__") && !name.ends_with("__") {
|
||||||
// Skip private attributes
|
// Skip private attributes
|
||||||
|
@ -187,7 +186,7 @@ pub(crate) fn enum_metadata<'db>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let declarations = use_def_map.end_of_scope_declarations(place_id);
|
let declarations = use_def_map.end_of_scope_symbol_declarations(symbol_id);
|
||||||
let declared = place_from_declarations(db, declarations);
|
let declared = place_from_declarations(db, declarations);
|
||||||
|
|
||||||
match declared {
|
match declared {
|
||||||
|
@ -213,9 +212,8 @@ pub(crate) fn enum_metadata<'db>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(name)
|
Some(name.clone())
|
||||||
})
|
})
|
||||||
.cloned()
|
|
||||||
.collect::<FxOrderSet<_>>();
|
.collect::<FxOrderSet<_>>();
|
||||||
|
|
||||||
if members.is_empty() {
|
if members.is_empty() {
|
||||||
|
|
|
@ -62,7 +62,7 @@ use crate::module_resolver::{KnownModule, file_to_module};
|
||||||
use crate::place::{Boundness, Place, place_from_bindings};
|
use crate::place::{Boundness, Place, place_from_bindings};
|
||||||
use crate::semantic_index::ast_ids::HasScopedUseId;
|
use crate::semantic_index::ast_ids::HasScopedUseId;
|
||||||
use crate::semantic_index::definition::Definition;
|
use crate::semantic_index::definition::Definition;
|
||||||
use crate::semantic_index::place::ScopeId;
|
use crate::semantic_index::scope::ScopeId;
|
||||||
use crate::semantic_index::semantic_index;
|
use crate::semantic_index::semantic_index;
|
||||||
use crate::types::call::{Binding, CallArguments};
|
use crate::types::call::{Binding, CallArguments};
|
||||||
use crate::types::context::InferContext;
|
use crate::types::context::InferContext;
|
||||||
|
|
|
@ -5,7 +5,7 @@ use crate::place::{
|
||||||
};
|
};
|
||||||
use crate::semantic_index::definition::Definition;
|
use crate::semantic_index::definition::Definition;
|
||||||
use crate::semantic_index::definition::DefinitionKind;
|
use crate::semantic_index::definition::DefinitionKind;
|
||||||
use crate::semantic_index::place::ScopeId;
|
use crate::semantic_index::scope::ScopeId;
|
||||||
use crate::semantic_index::{
|
use crate::semantic_index::{
|
||||||
attribute_scopes, global_scope, place_table, semantic_index, use_def_map,
|
attribute_scopes, global_scope, place_table, semantic_index, use_def_map,
|
||||||
};
|
};
|
||||||
|
@ -31,35 +31,33 @@ pub(crate) fn all_declarations_and_bindings<'db>(
|
||||||
let table = place_table(db, scope_id);
|
let table = place_table(db, scope_id);
|
||||||
|
|
||||||
use_def_map
|
use_def_map
|
||||||
.all_end_of_scope_declarations()
|
.all_end_of_scope_symbol_declarations()
|
||||||
.filter_map(move |(symbol_id, declarations)| {
|
.filter_map(move |(symbol_id, declarations)| {
|
||||||
place_from_declarations(db, declarations)
|
place_from_declarations(db, declarations)
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|result| {
|
.and_then(|result| {
|
||||||
result.place.ignore_possibly_unbound().and_then(|ty| {
|
result.place.ignore_possibly_unbound().map(|ty| {
|
||||||
table
|
let symbol = table.symbol(symbol_id);
|
||||||
.place_expr(symbol_id)
|
Member {
|
||||||
.as_name()
|
name: symbol.name().clone(),
|
||||||
.cloned()
|
ty,
|
||||||
.map(|name| Member { name, ty })
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.chain(
|
.chain(use_def_map.all_end_of_scope_symbol_bindings().filter_map(
|
||||||
use_def_map
|
move |(symbol_id, bindings)| {
|
||||||
.all_end_of_scope_bindings()
|
place_from_bindings(db, bindings)
|
||||||
.filter_map(move |(symbol_id, bindings)| {
|
.ignore_possibly_unbound()
|
||||||
place_from_bindings(db, bindings)
|
.map(|ty| {
|
||||||
.ignore_possibly_unbound()
|
let symbol = table.symbol(symbol_id);
|
||||||
.and_then(|ty| {
|
Member {
|
||||||
table
|
name: symbol.name().clone(),
|
||||||
.place_expr(symbol_id)
|
ty,
|
||||||
.as_name()
|
}
|
||||||
.cloned()
|
})
|
||||||
.map(|name| Member { name, ty })
|
},
|
||||||
})
|
))
|
||||||
}),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct AllMembers<'db> {
|
struct AllMembers<'db> {
|
||||||
|
@ -164,10 +162,8 @@ impl<'db> AllMembers<'db> {
|
||||||
let use_def_map = use_def_map(db, module_scope);
|
let use_def_map = use_def_map(db, module_scope);
|
||||||
let place_table = place_table(db, module_scope);
|
let place_table = place_table(db, module_scope);
|
||||||
|
|
||||||
for (symbol_id, _) in use_def_map.all_end_of_scope_declarations() {
|
for (symbol_id, _) in use_def_map.all_end_of_scope_symbol_declarations() {
|
||||||
let Some(symbol_name) = place_table.place_expr(symbol_id).as_name() else {
|
let symbol_name = place_table.symbol(symbol_id).name();
|
||||||
continue;
|
|
||||||
};
|
|
||||||
let Place::Type(ty, _) = imported_symbol(db, file, symbol_name, None).place
|
let Place::Type(ty, _) = imported_symbol(db, file, symbol_name, None).place
|
||||||
else {
|
else {
|
||||||
continue;
|
continue;
|
||||||
|
@ -204,7 +200,7 @@ impl<'db> AllMembers<'db> {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.members.insert(Member {
|
self.members.insert(Member {
|
||||||
name: place_table.place_expr(symbol_id).expect_name().clone(),
|
name: symbol_name.clone(),
|
||||||
ty,
|
ty,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -276,7 +272,7 @@ impl<'db> AllMembers<'db> {
|
||||||
let index = semantic_index(db, file);
|
let index = semantic_index(db, file);
|
||||||
for function_scope_id in attribute_scopes(db, class_body_scope) {
|
for function_scope_id in attribute_scopes(db, class_body_scope) {
|
||||||
let place_table = index.place_table(function_scope_id);
|
let place_table = index.place_table(function_scope_id);
|
||||||
for place_expr in place_table.places() {
|
for place_expr in place_table.members() {
|
||||||
let Some(name) = place_expr.as_instance_attribute() else {
|
let Some(name) = place_expr.as_instance_attribute() else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
@ -376,11 +372,11 @@ pub fn definition_kind_for_name<'db>(
|
||||||
let place_table = index.place_table(file_scope);
|
let place_table = index.place_table(file_scope);
|
||||||
|
|
||||||
// Look up the place by name
|
// Look up the place by name
|
||||||
let place_id = place_table.place_id_by_name(name_str)?;
|
let symbol_id = place_table.symbol_id(name_str)?;
|
||||||
|
|
||||||
// Get the use-def map and look up definitions for this place
|
// Get the use-def map and look up definitions for this place
|
||||||
let use_def_map = index.use_def_map(file_scope);
|
let use_def_map = index.use_def_map(file_scope);
|
||||||
let declarations = use_def_map.all_reachable_declarations(place_id);
|
let declarations = use_def_map.all_reachable_symbol_declarations(symbol_id);
|
||||||
|
|
||||||
// Find the first valid definition and return its kind
|
// Find the first valid definition and return its kind
|
||||||
for declaration in declarations {
|
for declaration in declarations {
|
||||||
|
@ -412,14 +408,14 @@ pub fn definitions_for_name<'db>(
|
||||||
for (scope_id, _scope) in index.visible_ancestor_scopes(file_scope) {
|
for (scope_id, _scope) in index.visible_ancestor_scopes(file_scope) {
|
||||||
let place_table = index.place_table(scope_id);
|
let place_table = index.place_table(scope_id);
|
||||||
|
|
||||||
let Some(place_id) = place_table.place_id_by_name(name_str) else {
|
let Some(symbol_id) = place_table.symbol_id(name_str) else {
|
||||||
continue; // Name not found in this scope, try parent scope
|
continue; // Name not found in this scope, try parent scope
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check if this place is marked as global or nonlocal
|
// Check if this place is marked as global or nonlocal
|
||||||
let place_expr = place_table.place_expr(place_id);
|
let place_expr = place_table.symbol(symbol_id);
|
||||||
let is_global = place_expr.is_marked_global();
|
let is_global = place_expr.is_global();
|
||||||
let is_nonlocal = place_expr.is_marked_nonlocal();
|
let is_nonlocal = place_expr.is_nonlocal();
|
||||||
|
|
||||||
// TODO: The current algorithm doesn't return definintions or bindings
|
// TODO: The current algorithm doesn't return definintions or bindings
|
||||||
// for other scopes that are outside of this scope hierarchy that target
|
// for other scopes that are outside of this scope hierarchy that target
|
||||||
|
@ -432,11 +428,12 @@ pub fn definitions_for_name<'db>(
|
||||||
let global_scope_id = global_scope(db, file);
|
let global_scope_id = global_scope(db, file);
|
||||||
let global_place_table = crate::semantic_index::place_table(db, global_scope_id);
|
let global_place_table = crate::semantic_index::place_table(db, global_scope_id);
|
||||||
|
|
||||||
if let Some(global_place_id) = global_place_table.place_id_by_name(name_str) {
|
if let Some(global_symbol_id) = global_place_table.symbol_id(name_str) {
|
||||||
let global_use_def_map = crate::semantic_index::use_def_map(db, global_scope_id);
|
let global_use_def_map = crate::semantic_index::use_def_map(db, global_scope_id);
|
||||||
let global_bindings = global_use_def_map.all_reachable_bindings(global_place_id);
|
let global_bindings =
|
||||||
|
global_use_def_map.all_reachable_symbol_bindings(global_symbol_id);
|
||||||
let global_declarations =
|
let global_declarations =
|
||||||
global_use_def_map.all_reachable_declarations(global_place_id);
|
global_use_def_map.all_reachable_symbol_declarations(global_symbol_id);
|
||||||
|
|
||||||
for binding in global_bindings {
|
for binding in global_bindings {
|
||||||
if let Some(def) = binding.binding.definition() {
|
if let Some(def) = binding.binding.definition() {
|
||||||
|
@ -462,8 +459,8 @@ pub fn definitions_for_name<'db>(
|
||||||
let use_def_map = index.use_def_map(scope_id);
|
let use_def_map = index.use_def_map(scope_id);
|
||||||
|
|
||||||
// Get all definitions (both bindings and declarations) for this place
|
// Get all definitions (both bindings and declarations) for this place
|
||||||
let bindings = use_def_map.all_reachable_bindings(place_id);
|
let bindings = use_def_map.all_reachable_symbol_bindings(symbol_id);
|
||||||
let declarations = use_def_map.all_reachable_declarations(place_id);
|
let declarations = use_def_map.all_reachable_symbol_declarations(symbol_id);
|
||||||
|
|
||||||
for binding in bindings {
|
for binding in bindings {
|
||||||
if let Some(def) = binding.binding.definition() {
|
if let Some(def) = binding.binding.definition() {
|
||||||
|
@ -577,11 +574,11 @@ pub fn definitions_for_attribute<'db>(
|
||||||
let class_place_table = crate::semantic_index::place_table(db, class_scope);
|
let class_place_table = crate::semantic_index::place_table(db, class_scope);
|
||||||
|
|
||||||
// Look for class-level declarations and bindings
|
// Look for class-level declarations and bindings
|
||||||
if let Some(place_id) = class_place_table.place_id_by_name(name_str) {
|
if let Some(place_id) = class_place_table.symbol_id(name_str) {
|
||||||
let use_def = use_def_map(db, class_scope);
|
let use_def = use_def_map(db, class_scope);
|
||||||
|
|
||||||
// Check declarations first
|
// Check declarations first
|
||||||
for decl in use_def.all_reachable_declarations(place_id) {
|
for decl in use_def.all_reachable_symbol_declarations(place_id) {
|
||||||
if let Some(def) = decl.declaration.definition() {
|
if let Some(def) = decl.declaration.definition() {
|
||||||
resolved.extend(resolve_definition(db, def, Some(name_str)));
|
resolved.extend(resolve_definition(db, def, Some(name_str)));
|
||||||
break 'scopes;
|
break 'scopes;
|
||||||
|
@ -589,7 +586,7 @@ pub fn definitions_for_attribute<'db>(
|
||||||
}
|
}
|
||||||
|
|
||||||
// If no declarations found, check bindings
|
// If no declarations found, check bindings
|
||||||
for binding in use_def.all_reachable_bindings(place_id) {
|
for binding in use_def.all_reachable_symbol_bindings(place_id) {
|
||||||
if let Some(def) = binding.binding.definition() {
|
if let Some(def) = binding.binding.definition() {
|
||||||
resolved.extend(resolve_definition(db, def, Some(name_str)));
|
resolved.extend(resolve_definition(db, def, Some(name_str)));
|
||||||
break 'scopes;
|
break 'scopes;
|
||||||
|
@ -604,11 +601,11 @@ pub fn definitions_for_attribute<'db>(
|
||||||
for function_scope_id in attribute_scopes(db, class_scope) {
|
for function_scope_id in attribute_scopes(db, class_scope) {
|
||||||
let place_table = index.place_table(function_scope_id);
|
let place_table = index.place_table(function_scope_id);
|
||||||
|
|
||||||
if let Some(place_id) = place_table.place_id_by_instance_attribute_name(name_str) {
|
if let Some(place_id) = place_table.member_id_by_instance_attribute_name(name_str) {
|
||||||
let use_def = index.use_def_map(function_scope_id);
|
let use_def = index.use_def_map(function_scope_id);
|
||||||
|
|
||||||
// Check declarations first
|
// Check declarations first
|
||||||
for decl in use_def.all_reachable_declarations(place_id) {
|
for decl in use_def.all_reachable_member_declarations(place_id) {
|
||||||
if let Some(def) = decl.declaration.definition() {
|
if let Some(def) = decl.declaration.definition() {
|
||||||
resolved.extend(resolve_definition(db, def, Some(name_str)));
|
resolved.extend(resolve_definition(db, def, Some(name_str)));
|
||||||
break 'scopes;
|
break 'scopes;
|
||||||
|
@ -616,7 +613,7 @@ pub fn definitions_for_attribute<'db>(
|
||||||
}
|
}
|
||||||
|
|
||||||
// If no declarations found, check bindings
|
// If no declarations found, check bindings
|
||||||
for binding in use_def.all_reachable_bindings(place_id) {
|
for binding in use_def.all_reachable_member_bindings(place_id) {
|
||||||
if let Some(def) = binding.binding.definition() {
|
if let Some(def) = binding.binding.definition() {
|
||||||
resolved.extend(resolve_definition(db, def, Some(name_str)));
|
resolved.extend(resolve_definition(db, def, Some(name_str)));
|
||||||
break 'scopes;
|
break 'scopes;
|
||||||
|
@ -801,7 +798,7 @@ mod resolve_definition {
|
||||||
|
|
||||||
use crate::module_resolver::file_to_module;
|
use crate::module_resolver::file_to_module;
|
||||||
use crate::semantic_index::definition::{Definition, DefinitionKind};
|
use crate::semantic_index::definition::{Definition, DefinitionKind};
|
||||||
use crate::semantic_index::place::{NodeWithScopeKind, ScopeId};
|
use crate::semantic_index::scope::{NodeWithScopeKind, ScopeId};
|
||||||
use crate::semantic_index::{global_scope, place_table, semantic_index, use_def_map};
|
use crate::semantic_index::{global_scope, place_table, semantic_index, use_def_map};
|
||||||
use crate::{Db, ModuleName, resolve_module, resolve_real_module};
|
use crate::{Db, ModuleName, resolve_module, resolve_real_module};
|
||||||
|
|
||||||
|
@ -971,7 +968,7 @@ mod resolve_definition {
|
||||||
symbol_name: &str,
|
symbol_name: &str,
|
||||||
) -> IndexSet<Definition<'db>> {
|
) -> IndexSet<Definition<'db>> {
|
||||||
let place_table = place_table(db, scope);
|
let place_table = place_table(db, scope);
|
||||||
let Some(place_id) = place_table.place_id_by_name(symbol_name) else {
|
let Some(symbol_id) = place_table.symbol_id(symbol_name) else {
|
||||||
return IndexSet::new();
|
return IndexSet::new();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -979,8 +976,8 @@ mod resolve_definition {
|
||||||
let mut definitions = IndexSet::new();
|
let mut definitions = IndexSet::new();
|
||||||
|
|
||||||
// Get all definitions (both bindings and declarations) for this place
|
// Get all definitions (both bindings and declarations) for this place
|
||||||
let bindings = use_def_map.all_reachable_bindings(place_id);
|
let bindings = use_def_map.all_reachable_symbol_bindings(symbol_id);
|
||||||
let declarations = use_def_map.all_reachable_declarations(place_id);
|
let declarations = use_def_map.all_reachable_symbol_declarations(symbol_id);
|
||||||
|
|
||||||
for binding in bindings {
|
for binding in bindings {
|
||||||
if let Some(def) = binding.binding.definition() {
|
if let Some(def) = binding.binding.definition() {
|
||||||
|
|
|
@ -81,9 +81,11 @@ use crate::semantic_index::definition::{
|
||||||
};
|
};
|
||||||
use crate::semantic_index::expression::{Expression, ExpressionKind};
|
use crate::semantic_index::expression::{Expression, ExpressionKind};
|
||||||
use crate::semantic_index::narrowing_constraints::ConstraintKey;
|
use crate::semantic_index::narrowing_constraints::ConstraintKey;
|
||||||
use crate::semantic_index::place::{
|
use crate::semantic_index::place::{PlaceExpr, PlaceExprRef};
|
||||||
FileScopeId, NodeWithScopeKind, NodeWithScopeRef, PlaceExpr, ScopeId, ScopeKind, ScopedPlaceId,
|
use crate::semantic_index::scope::{
|
||||||
|
FileScopeId, NodeWithScopeKind, NodeWithScopeRef, ScopeId, ScopeKind,
|
||||||
};
|
};
|
||||||
|
use crate::semantic_index::symbol::ScopedSymbolId;
|
||||||
use crate::semantic_index::{
|
use crate::semantic_index::{
|
||||||
ApplicableConstraints, EnclosingSnapshotResult, SemanticIndex, place_table, semantic_index,
|
ApplicableConstraints, EnclosingSnapshotResult, SemanticIndex, place_table, semantic_index,
|
||||||
};
|
};
|
||||||
|
@ -1415,7 +1417,10 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
|
|
||||||
for place in overloaded_function_places {
|
for place in overloaded_function_places {
|
||||||
if let Place::Type(Type::FunctionLiteral(function), Boundness::Bound) =
|
if let Place::Type(Type::FunctionLiteral(function), Boundness::Bound) =
|
||||||
place_from_bindings(self.db(), use_def.end_of_scope_bindings(place))
|
place_from_bindings(
|
||||||
|
self.db(),
|
||||||
|
use_def.end_of_scope_symbol_bindings(place.as_symbol().unwrap()),
|
||||||
|
)
|
||||||
{
|
{
|
||||||
if function.file(self.db()) != self.file() {
|
if function.file(self.db()) != self.file() {
|
||||||
// If the function is not in this file, we don't need to check it.
|
// If the function is not in this file, we don't need to check it.
|
||||||
|
@ -1779,67 +1784,74 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
let global_use_def_map = self.index.use_def_map(FileScopeId::global());
|
let global_use_def_map = self.index.use_def_map(FileScopeId::global());
|
||||||
let nonlocal_use_def_map;
|
let nonlocal_use_def_map;
|
||||||
let place_id = binding.place(self.db());
|
let place_id = binding.place(self.db());
|
||||||
let place = place_table.place_expr(place_id);
|
let place = place_table.place(place_id);
|
||||||
let skip_non_global_scopes = self.skip_non_global_scopes(file_scope_id, place_id);
|
|
||||||
let (declarations, is_local) = if skip_non_global_scopes {
|
let (declarations, is_local) = if let Some(symbol) = place.as_symbol() {
|
||||||
match self
|
let symbol_id = place_id.expect_symbol();
|
||||||
|
let skip_non_global_scopes = self.skip_non_global_scopes(file_scope_id, symbol_id);
|
||||||
|
|
||||||
|
if skip_non_global_scopes {
|
||||||
|
match self
|
||||||
|
.index
|
||||||
|
.place_table(FileScopeId::global())
|
||||||
|
.symbol_id(symbol.name())
|
||||||
|
{
|
||||||
|
Some(id) => (
|
||||||
|
global_use_def_map.end_of_scope_symbol_declarations(id),
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
// This variable shows up in `global` declarations but doesn't have an explicit
|
||||||
|
// binding in the global scope.
|
||||||
|
None => (use_def.declarations_at_binding(binding), true),
|
||||||
|
}
|
||||||
|
} else if self
|
||||||
.index
|
.index
|
||||||
.place_table(FileScopeId::global())
|
.symbol_is_nonlocal_in_scope(symbol_id, file_scope_id)
|
||||||
.place_id_by_expr(&place.expr)
|
|
||||||
{
|
{
|
||||||
Some(id) => (global_use_def_map.end_of_scope_declarations(id), false),
|
// If we run out of ancestor scopes without finding a definition, we'll fall back to
|
||||||
// This variable shows up in `global` declarations but doesn't have an explicit
|
// the local scope. This will also be a syntax error in `infer_nonlocal_statement` (no
|
||||||
// binding in the global scope.
|
// binding for `nonlocal` found), but ignore that here.
|
||||||
None => (use_def.declarations_at_binding(binding), true),
|
let mut declarations = use_def.declarations_at_binding(binding);
|
||||||
}
|
let mut is_local = true;
|
||||||
} else if self
|
// Walk up parent scopes looking for the enclosing scope that has definition of this
|
||||||
.index
|
// name. `ancestor_scopes` includes the current scope, so skip that one.
|
||||||
.symbol_is_nonlocal_in_scope(place_id, file_scope_id)
|
for (enclosing_scope_file_id, enclosing_scope) in
|
||||||
{
|
self.index.ancestor_scopes(file_scope_id).skip(1)
|
||||||
// If we run out of ancestor scopes without finding a definition, we'll fall back to
|
|
||||||
// the local scope. This will also be a syntax error in `infer_nonlocal_statement` (no
|
|
||||||
// binding for `nonlocal` found), but ignore that here.
|
|
||||||
let mut declarations = use_def.declarations_at_binding(binding);
|
|
||||||
let mut is_local = true;
|
|
||||||
// Walk up parent scopes looking for the enclosing scope that has definition of this
|
|
||||||
// name. `ancestor_scopes` includes the current scope, so skip that one.
|
|
||||||
for (enclosing_scope_file_id, enclosing_scope) in
|
|
||||||
self.index.ancestor_scopes(file_scope_id).skip(1)
|
|
||||||
{
|
|
||||||
// Ignore class scopes and the global scope.
|
|
||||||
if !enclosing_scope.kind().is_function_like() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let enclosing_place_table = self.index.place_table(enclosing_scope_file_id);
|
|
||||||
let Some(enclosing_place_id) = enclosing_place_table.place_id_by_expr(&place.expr)
|
|
||||||
else {
|
|
||||||
// This ancestor scope doesn't have a binding. Keep going.
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
if self
|
|
||||||
.index
|
|
||||||
.symbol_is_nonlocal_in_scope(enclosing_place_id, enclosing_scope_file_id)
|
|
||||||
{
|
{
|
||||||
// The variable is `nonlocal` in this ancestor scope. Keep going.
|
// Ignore class scopes and the global scope.
|
||||||
continue;
|
if !enclosing_scope.kind().is_function_like() {
|
||||||
}
|
continue;
|
||||||
if self
|
}
|
||||||
.index
|
let enclosing_place_table = self.index.place_table(enclosing_scope_file_id);
|
||||||
.symbol_is_global_in_scope(enclosing_place_id, enclosing_scope_file_id)
|
let Some(enclosing_symbol_id) = enclosing_place_table.symbol_id(symbol.name())
|
||||||
{
|
else {
|
||||||
// The variable is `global` in this ancestor scope. This breaks the `nonlocal`
|
// This ancestor scope doesn't have a binding. Keep going.
|
||||||
// chain, and it's a syntax error in `infer_nonlocal_statement`. Ignore that
|
continue;
|
||||||
// here and just bail out of this loop.
|
};
|
||||||
|
|
||||||
|
let enclosing_symbol = enclosing_place_table.symbol(enclosing_symbol_id);
|
||||||
|
if enclosing_symbol.is_nonlocal() {
|
||||||
|
// The variable is `nonlocal` in this ancestor scope. Keep going.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if enclosing_symbol.is_global() {
|
||||||
|
// The variable is `global` in this ancestor scope. This breaks the `nonlocal`
|
||||||
|
// chain, and it's a syntax error in `infer_nonlocal_statement`. Ignore that
|
||||||
|
// here and just bail out of this loop.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// We found the closest definition. Note that (as in `infer_place_load`) this does
|
||||||
|
// *not* need to be a binding. It could be just a declaration, e.g. `x: int`.
|
||||||
|
nonlocal_use_def_map = self.index.use_def_map(enclosing_scope_file_id);
|
||||||
|
declarations =
|
||||||
|
nonlocal_use_def_map.end_of_scope_symbol_declarations(enclosing_symbol_id);
|
||||||
|
is_local = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// We found the closest definition. Note that (as in `infer_place_load`) this does
|
(declarations, is_local)
|
||||||
// *not* need to be a binding. It could be just a declaration, e.g. `x: int`.
|
} else {
|
||||||
nonlocal_use_def_map = self.index.use_def_map(enclosing_scope_file_id);
|
(use_def.declarations_at_binding(binding), true)
|
||||||
declarations = nonlocal_use_def_map.end_of_scope_declarations(enclosing_place_id);
|
|
||||||
is_local = false;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
(declarations, is_local)
|
|
||||||
} else {
|
} else {
|
||||||
(use_def.declarations_at_binding(binding), true)
|
(use_def.declarations_at_binding(binding), true)
|
||||||
};
|
};
|
||||||
|
@ -1849,12 +1861,18 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
Ok(
|
Ok(
|
||||||
if matches!(place_and_quals.place, Place::Type(_, Boundness::Bound)) {
|
if matches!(place_and_quals.place, Place::Type(_, Boundness::Bound)) {
|
||||||
place_and_quals
|
place_and_quals
|
||||||
} else if skip_non_global_scopes
|
} else if let PlaceExprRef::Symbol(symbol) = place {
|
||||||
|| self.scope().file_scope_id(self.db()).is_global()
|
let symbol_id = place_id.expect_symbol();
|
||||||
{
|
|
||||||
let module_type_declarations =
|
if self.skip_non_global_scopes(file_scope_id, symbol_id)
|
||||||
module_type_implicit_global_declaration(self.db(), &place.expr)?;
|
|| self.scope.file_scope_id(self.db()).is_global()
|
||||||
place_and_quals.or_fall_back_to(self.db(), || module_type_declarations)
|
{
|
||||||
|
let module_type_declarations =
|
||||||
|
module_type_implicit_global_declaration(self.db(), symbol.name())?;
|
||||||
|
place_and_quals.or_fall_back_to(self.db(), || module_type_declarations)
|
||||||
|
} else {
|
||||||
|
place_and_quals
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
place_and_quals
|
place_and_quals
|
||||||
},
|
},
|
||||||
|
@ -1867,7 +1885,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
}| {
|
}| {
|
||||||
let is_modifiable = !qualifiers.contains(TypeQualifiers::FINAL);
|
let is_modifiable = !qualifiers.contains(TypeQualifiers::FINAL);
|
||||||
|
|
||||||
if resolved_place.is_unbound() && !place_table.place_expr(place_id).is_name() {
|
if resolved_place.is_unbound() && !place_table.place(place_id).is_symbol() {
|
||||||
if let AnyNodeRef::ExprAttribute(ast::ExprAttribute {
|
if let AnyNodeRef::ExprAttribute(ast::ExprAttribute {
|
||||||
value, attr, ..
|
value, attr, ..
|
||||||
}) = node
|
}) = node
|
||||||
|
@ -1902,7 +1920,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
)
|
)
|
||||||
.unwrap_or_else(|(ty, conflicting)| {
|
.unwrap_or_else(|(ty, conflicting)| {
|
||||||
// TODO point out the conflicting declarations in the diagnostic?
|
// TODO point out the conflicting declarations in the diagnostic?
|
||||||
let place = place_table.place_expr(binding.place(db));
|
let place = place_table.place(binding.place(db));
|
||||||
if let Some(builder) = self.context.report_lint(&CONFLICTING_DECLARATIONS, node) {
|
if let Some(builder) = self.context.report_lint(&CONFLICTING_DECLARATIONS, node) {
|
||||||
builder.into_diagnostic(format_args!(
|
builder.into_diagnostic(format_args!(
|
||||||
"Conflicting declared types for `{place}`: {}",
|
"Conflicting declared types for `{place}`: {}",
|
||||||
|
@ -1925,7 +1943,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
.and_then(|r| r.binding.definition());
|
.and_then(|r| r.binding.definition());
|
||||||
|
|
||||||
if !is_local || previous_definition.is_some() {
|
if !is_local || previous_definition.is_some() {
|
||||||
let place = place_table.place_expr(binding.place(db));
|
let place = place_table.place(binding.place(db));
|
||||||
if let Some(builder) = self.context.report_lint(
|
if let Some(builder) = self.context.report_lint(
|
||||||
&INVALID_ASSIGNMENT,
|
&INVALID_ASSIGNMENT,
|
||||||
binding.full_range(self.db(), self.module()),
|
binding.full_range(self.db(), self.module()),
|
||||||
|
@ -2026,7 +2044,11 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
|
|
||||||
/// Returns `true` if `symbol_id` should be looked up in the global scope, skipping intervening
|
/// Returns `true` if `symbol_id` should be looked up in the global scope, skipping intervening
|
||||||
/// local scopes.
|
/// local scopes.
|
||||||
fn skip_non_global_scopes(&self, file_scope_id: FileScopeId, symbol_id: ScopedPlaceId) -> bool {
|
fn skip_non_global_scopes(
|
||||||
|
&self,
|
||||||
|
file_scope_id: FileScopeId,
|
||||||
|
symbol_id: ScopedSymbolId,
|
||||||
|
) -> bool {
|
||||||
!file_scope_id.is_global()
|
!file_scope_id.is_global()
|
||||||
&& self
|
&& self
|
||||||
.index
|
.index
|
||||||
|
@ -2054,9 +2076,13 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
// Fallback to bindings declared on `types.ModuleType` if it's a global symbol
|
// Fallback to bindings declared on `types.ModuleType` if it's a global symbol
|
||||||
let scope = self.scope().file_scope_id(self.db());
|
let scope = self.scope().file_scope_id(self.db());
|
||||||
let place_table = self.index.place_table(scope);
|
let place_table = self.index.place_table(scope);
|
||||||
let place = place_table.place_expr(declaration.place(self.db()));
|
let place = place_table.place(declaration.place(self.db()));
|
||||||
if scope.is_global() && place.is_name() {
|
if let PlaceExprRef::Symbol(symbol) = &place {
|
||||||
module_type_implicit_global_symbol(self.db(), place.expect_name())
|
if scope.is_global() {
|
||||||
|
module_type_implicit_global_symbol(self.db(), symbol.name())
|
||||||
|
} else {
|
||||||
|
Place::Unbound.into()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Place::Unbound.into()
|
Place::Unbound.into()
|
||||||
}
|
}
|
||||||
|
@ -2107,11 +2133,11 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
let file_scope_id = self.scope().file_scope_id(self.db());
|
let file_scope_id = self.scope().file_scope_id(self.db());
|
||||||
if file_scope_id.is_global() {
|
if file_scope_id.is_global() {
|
||||||
let place_table = self.index.place_table(file_scope_id);
|
let place_table = self.index.place_table(file_scope_id);
|
||||||
let place = place_table.place_expr(definition.place(self.db()));
|
let place = place_table.place(definition.place(self.db()));
|
||||||
if let Some(module_type_implicit_declaration) =
|
if let Some(module_type_implicit_declaration) = place
|
||||||
module_type_implicit_global_declaration(self.db(), &place.expr)
|
.as_symbol()
|
||||||
.ok()
|
.map(|symbol| module_type_implicit_global_symbol(self.db(), symbol.name()))
|
||||||
.and_then(|place| place.place.ignore_possibly_unbound())
|
.and_then(|place| place.place.ignore_possibly_unbound())
|
||||||
{
|
{
|
||||||
let declared_type = declared_ty.inner_type();
|
let declared_type = declared_ty.inner_type();
|
||||||
if !declared_type
|
if !declared_type
|
||||||
|
@ -4360,7 +4386,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
// If the target of an assignment is not one of the place expressions we support,
|
// If the target of an assignment is not one of the place expressions we support,
|
||||||
// then they are not definitions, so we can only be here if the target is in a form supported as a place expression.
|
// then they are not definitions, so we can only be here if the target is in a form supported as a place expression.
|
||||||
// In this case, we can simply store types in `target` below, instead of calling `infer_expression` (which would return `Never`).
|
// In this case, we can simply store types in `target` below, instead of calling `infer_expression` (which would return `Never`).
|
||||||
debug_assert!(PlaceExpr::try_from(target).is_ok());
|
debug_assert!(PlaceExpr::try_from_expr(target).is_some());
|
||||||
|
|
||||||
if let Some(value) = value {
|
if let Some(value) = value {
|
||||||
let inferred_ty = self.infer_maybe_standalone_expression(value);
|
let inferred_ty = self.infer_maybe_standalone_expression(value);
|
||||||
|
@ -4874,16 +4900,14 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
.kind(self.db())
|
.kind(self.db())
|
||||||
.as_star_import()
|
.as_star_import()
|
||||||
.map(|star_import| {
|
.map(|star_import| {
|
||||||
let symbol_table = self
|
let place_table = self
|
||||||
.index
|
.index
|
||||||
.place_table(self.scope().file_scope_id(self.db()));
|
.place_table(self.scope().file_scope_id(self.db()));
|
||||||
(star_import, symbol_table)
|
(star_import, place_table)
|
||||||
});
|
});
|
||||||
|
|
||||||
let name = if let Some((star_import, symbol_table)) = star_import_info.as_ref() {
|
let name = if let Some((star_import, symbol_table)) = star_import_info.as_ref() {
|
||||||
symbol_table
|
symbol_table.symbol(star_import.symbol_id()).name()
|
||||||
.place_expr(star_import.place_id())
|
|
||||||
.expect_name()
|
|
||||||
} else {
|
} else {
|
||||||
&alias.name.id
|
&alias.name.id
|
||||||
};
|
};
|
||||||
|
@ -5041,9 +5065,9 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
} = global;
|
} = global;
|
||||||
let global_place_table = self.index.place_table(FileScopeId::global());
|
let global_place_table = self.index.place_table(FileScopeId::global());
|
||||||
for name in names {
|
for name in names {
|
||||||
if let Some(place_id) = global_place_table.place_id_by_name(name) {
|
if let Some(symbol_id) = global_place_table.symbol_id(name) {
|
||||||
let place = global_place_table.place_expr(place_id);
|
let symbol = global_place_table.symbol(symbol_id);
|
||||||
if place.is_bound() || place.is_declared() {
|
if symbol.is_bound() || symbol.is_declared() {
|
||||||
// This name is explicitly defined in the global scope (not just in function
|
// This name is explicitly defined in the global scope (not just in function
|
||||||
// bodies that mark it `global`).
|
// bodies that mark it `global`).
|
||||||
continue;
|
continue;
|
||||||
|
@ -5095,11 +5119,11 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let enclosing_place_table = self.index.place_table(enclosing_scope_file_id);
|
let enclosing_place_table = self.index.place_table(enclosing_scope_file_id);
|
||||||
let Some(enclosing_place_id) = enclosing_place_table.place_id_by_name(name) else {
|
let Some(enclosing_symbol_id) = enclosing_place_table.symbol_id(name) else {
|
||||||
// This scope doesn't define this name. Keep going.
|
// This scope doesn't define this name. Keep going.
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
let enclosing_place = enclosing_place_table.place_expr(enclosing_place_id);
|
let enclosing_symbol = enclosing_place_table.symbol(enclosing_symbol_id);
|
||||||
// We've found a definition for this name in an enclosing function-like scope.
|
// We've found a definition for this name in an enclosing function-like scope.
|
||||||
// Either this definition is the valid place this name refers to, or else we'll
|
// Either this definition is the valid place this name refers to, or else we'll
|
||||||
// emit a syntax error. Either way, we won't walk any more enclosing scopes. Note
|
// emit a syntax error. Either way, we won't walk any more enclosing scopes. Note
|
||||||
|
@ -5110,16 +5134,16 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
// `nonlocal` keyword can't refer to global variables (that's a `SyntaxError`), and
|
// `nonlocal` keyword can't refer to global variables (that's a `SyntaxError`), and
|
||||||
// it also can't refer to local variables in enclosing functions that are declared
|
// it also can't refer to local variables in enclosing functions that are declared
|
||||||
// `global` (also a `SyntaxError`).
|
// `global` (also a `SyntaxError`).
|
||||||
if enclosing_place.is_marked_global() {
|
if enclosing_symbol.is_global() {
|
||||||
// A "chain" of `nonlocal` statements is "broken" by a `global` statement. Stop
|
// A "chain" of `nonlocal` statements is "broken" by a `global` statement. Stop
|
||||||
// looping and report that this `nonlocal` statement is invalid.
|
// looping and report that this `nonlocal` statement is invalid.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if !enclosing_place.is_bound()
|
if !enclosing_symbol.is_bound()
|
||||||
&& !enclosing_place.is_declared()
|
&& !enclosing_symbol.is_declared()
|
||||||
&& !enclosing_place.is_marked_nonlocal()
|
&& !enclosing_symbol.is_nonlocal()
|
||||||
{
|
{
|
||||||
debug_assert!(enclosing_place.is_used());
|
debug_assert!(enclosing_symbol.is_used());
|
||||||
// The name is only referenced here, not defined. Keep going.
|
// The name is only referenced here, not defined. Keep going.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -6041,9 +6065,9 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
Some(expr) => match PlaceExpr::try_from(expr) {
|
Some(expr) => match PlaceExpr::try_from_expr(expr) {
|
||||||
Ok(place_expr) => place_table(db, scope).place_id_by_expr(&place_expr),
|
Some(place_expr) => place_table(db, scope).place_id(&place_expr),
|
||||||
Err(()) => None,
|
None => None,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -6121,7 +6145,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
// Perform narrowing with applicable constraints between the current scope and the enclosing scope.
|
// Perform narrowing with applicable constraints between the current scope and the enclosing scope.
|
||||||
fn narrow_place_with_applicable_constraints(
|
fn narrow_place_with_applicable_constraints(
|
||||||
&self,
|
&self,
|
||||||
expr: &PlaceExpr,
|
expr: PlaceExprRef,
|
||||||
mut ty: Type<'db>,
|
mut ty: Type<'db>,
|
||||||
constraint_keys: &[(FileScopeId, ConstraintKey)],
|
constraint_keys: &[(FileScopeId, ConstraintKey)],
|
||||||
) -> Type<'db> {
|
) -> Type<'db> {
|
||||||
|
@ -6129,7 +6153,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
for (enclosing_scope_file_id, constraint_key) in constraint_keys {
|
for (enclosing_scope_file_id, constraint_key) in constraint_keys {
|
||||||
let use_def = self.index.use_def_map(*enclosing_scope_file_id);
|
let use_def = self.index.use_def_map(*enclosing_scope_file_id);
|
||||||
let place_table = self.index.place_table(*enclosing_scope_file_id);
|
let place_table = self.index.place_table(*enclosing_scope_file_id);
|
||||||
let place = place_table.place_id_by_expr(expr).unwrap();
|
let place = place_table.place_id(expr).unwrap();
|
||||||
|
|
||||||
match use_def.applicable_constraints(
|
match use_def.applicable_constraints(
|
||||||
*constraint_key,
|
*constraint_key,
|
||||||
|
@ -6258,11 +6282,11 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
id: symbol_name,
|
id: symbol_name,
|
||||||
ctx: _,
|
ctx: _,
|
||||||
} = name_node;
|
} = name_node;
|
||||||
let Ok(expr) = PlaceExpr::try_from(symbol_name);
|
let expr = PlaceExpr::from_expr_name(name_node);
|
||||||
let db = self.db();
|
let db = self.db();
|
||||||
|
|
||||||
let (resolved, constraint_keys) =
|
let (resolved, constraint_keys) =
|
||||||
self.infer_place_load(&expr, ast::ExprRef::Name(name_node));
|
self.infer_place_load(PlaceExprRef::from(&expr), ast::ExprRef::Name(name_node));
|
||||||
|
|
||||||
resolved
|
resolved
|
||||||
// Not found in the module's explicitly declared global symbols?
|
// Not found in the module's explicitly declared global symbols?
|
||||||
|
@ -6270,7 +6294,11 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
// These are looked up as attributes on `types.ModuleType`.
|
// These are looked up as attributes on `types.ModuleType`.
|
||||||
.or_fall_back_to(db, || {
|
.or_fall_back_to(db, || {
|
||||||
module_type_implicit_global_symbol(db, symbol_name).map_type(|ty| {
|
module_type_implicit_global_symbol(db, symbol_name).map_type(|ty| {
|
||||||
self.narrow_place_with_applicable_constraints(&expr, ty, &constraint_keys)
|
self.narrow_place_with_applicable_constraints(
|
||||||
|
PlaceExprRef::from(&expr),
|
||||||
|
ty,
|
||||||
|
&constraint_keys,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
// Not found in globals? Fallback to builtins
|
// Not found in globals? Fallback to builtins
|
||||||
|
@ -6314,7 +6342,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
|
|
||||||
fn infer_local_place_load(
|
fn infer_local_place_load(
|
||||||
&self,
|
&self,
|
||||||
expr: &PlaceExpr,
|
expr: PlaceExprRef,
|
||||||
expr_ref: ast::ExprRef,
|
expr_ref: ast::ExprRef,
|
||||||
) -> (Place<'db>, Option<ScopedUseId>) {
|
) -> (Place<'db>, Option<ScopedUseId>) {
|
||||||
let db = self.db();
|
let db = self.db();
|
||||||
|
@ -6325,7 +6353,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
|
|
||||||
// If we're inferring types of deferred expressions, always treat them as public symbols
|
// If we're inferring types of deferred expressions, always treat them as public symbols
|
||||||
if self.is_deferred() {
|
if self.is_deferred() {
|
||||||
let place = if let Some(place_id) = place_table.place_id_by_expr(expr) {
|
let place = if let Some(place_id) = place_table.place_id(expr) {
|
||||||
place_from_bindings(db, use_def.all_reachable_bindings(place_id))
|
place_from_bindings(db, use_def.all_reachable_bindings(place_id))
|
||||||
} else {
|
} else {
|
||||||
assert!(
|
assert!(
|
||||||
|
@ -6355,7 +6383,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
/// which is used to narrow by condition rather than by assignment.
|
/// which is used to narrow by condition rather than by assignment.
|
||||||
fn infer_place_load(
|
fn infer_place_load(
|
||||||
&self,
|
&self,
|
||||||
expr: &PlaceExpr,
|
place_expr: PlaceExprRef,
|
||||||
expr_ref: ast::ExprRef,
|
expr_ref: ast::ExprRef,
|
||||||
) -> (PlaceAndQualifiers<'db>, Vec<(FileScopeId, ConstraintKey)>) {
|
) -> (PlaceAndQualifiers<'db>, Vec<(FileScopeId, ConstraintKey)>) {
|
||||||
let db = self.db();
|
let db = self.db();
|
||||||
|
@ -6364,14 +6392,14 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
let place_table = self.index.place_table(file_scope_id);
|
let place_table = self.index.place_table(file_scope_id);
|
||||||
|
|
||||||
let mut constraint_keys = vec![];
|
let mut constraint_keys = vec![];
|
||||||
let (local_scope_place, use_id) = self.infer_local_place_load(expr, expr_ref);
|
let (local_scope_place, use_id) = self.infer_local_place_load(place_expr, expr_ref);
|
||||||
if let Some(use_id) = use_id {
|
if let Some(use_id) = use_id {
|
||||||
constraint_keys.push((file_scope_id, ConstraintKey::UseId(use_id)));
|
constraint_keys.push((file_scope_id, ConstraintKey::UseId(use_id)));
|
||||||
}
|
}
|
||||||
|
|
||||||
let place = PlaceAndQualifiers::from(local_scope_place).or_fall_back_to(db, || {
|
let place = PlaceAndQualifiers::from(local_scope_place).or_fall_back_to(db, || {
|
||||||
let has_bindings_in_this_scope = match place_table.place_by_expr(expr) {
|
let has_bindings_in_this_scope = match place_table.place_id(place_expr) {
|
||||||
Some(place_expr) => place_expr.is_bound(),
|
Some(place_id) => place_table.place(place_id).is_bound(),
|
||||||
None => {
|
None => {
|
||||||
assert!(
|
assert!(
|
||||||
self.deferred_state.in_string_annotation(),
|
self.deferred_state.in_string_annotation(),
|
||||||
|
@ -6384,16 +6412,18 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
let current_file = self.file();
|
let current_file = self.file();
|
||||||
|
|
||||||
let mut is_nonlocal_binding = false;
|
let mut is_nonlocal_binding = false;
|
||||||
if let Some(name) = expr.as_name() {
|
if let Some(symbol) = place_expr.as_symbol() {
|
||||||
if let Some(symbol_id) = place_table.place_id_by_name(name) {
|
if let Some(symbol_id) = place_table.symbol_id(symbol.name()) {
|
||||||
if self.skip_non_global_scopes(file_scope_id, symbol_id) {
|
if self.skip_non_global_scopes(file_scope_id, symbol_id) {
|
||||||
return global_symbol(self.db(), self.file(), name).map_type(|ty| {
|
return global_symbol(self.db(), self.file(), symbol.name()).map_type(
|
||||||
self.narrow_place_with_applicable_constraints(
|
|ty| {
|
||||||
expr,
|
self.narrow_place_with_applicable_constraints(
|
||||||
ty,
|
place_expr,
|
||||||
&constraint_keys,
|
ty,
|
||||||
)
|
&constraint_keys,
|
||||||
});
|
)
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
is_nonlocal_binding = self
|
is_nonlocal_binding = self
|
||||||
.index
|
.index
|
||||||
|
@ -6414,9 +6444,10 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
return Place::Unbound.into();
|
return Place::Unbound.into();
|
||||||
}
|
}
|
||||||
|
|
||||||
for root_expr in place_table.root_place_exprs(expr) {
|
for parent_id in place_table.parents(place_expr) {
|
||||||
|
let parent_expr = place_table.place(parent_id);
|
||||||
let mut expr_ref = expr_ref;
|
let mut expr_ref = expr_ref;
|
||||||
for _ in 0..(expr.sub_segments().len() - root_expr.expr.sub_segments().len()) {
|
for _ in 0..(place_expr.num_member_segments() - parent_expr.num_member_segments()) {
|
||||||
match expr_ref {
|
match expr_ref {
|
||||||
ast::ExprRef::Attribute(attribute) => {
|
ast::ExprRef::Attribute(attribute) => {
|
||||||
expr_ref = ast::ExprRef::from(&attribute.value);
|
expr_ref = ast::ExprRef::from(&attribute.value);
|
||||||
|
@ -6427,8 +6458,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let (parent_place, _use_id) =
|
let (parent_place, _use_id) = self.infer_local_place_load(parent_expr, expr_ref);
|
||||||
self.infer_local_place_load(&root_expr.expr, expr_ref);
|
|
||||||
if let Place::Type(_, _) = parent_place {
|
if let Place::Type(_, _) = parent_place {
|
||||||
return Place::Unbound.into();
|
return Place::Unbound.into();
|
||||||
}
|
}
|
||||||
|
@ -6463,7 +6493,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
if !self.is_deferred() {
|
if !self.is_deferred() {
|
||||||
match self.index.enclosing_snapshot(
|
match self.index.enclosing_snapshot(
|
||||||
enclosing_scope_file_id,
|
enclosing_scope_file_id,
|
||||||
expr,
|
place_expr,
|
||||||
file_scope_id,
|
file_scope_id,
|
||||||
) {
|
) {
|
||||||
EnclosingSnapshotResult::FoundConstraint(constraint) => {
|
EnclosingSnapshotResult::FoundConstraint(constraint) => {
|
||||||
|
@ -6473,7 +6503,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
EnclosingSnapshotResult::FoundBindings(bindings) => {
|
EnclosingSnapshotResult::FoundBindings(bindings) => {
|
||||||
if expr.is_name()
|
if place_expr.is_symbol()
|
||||||
&& !enclosing_scope_id.is_function_like(db)
|
&& !enclosing_scope_id.is_function_like(db)
|
||||||
&& !is_immediately_enclosing_scope
|
&& !is_immediately_enclosing_scope
|
||||||
{
|
{
|
||||||
|
@ -6481,7 +6511,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
}
|
}
|
||||||
let place = place_from_bindings(db, bindings).map_type(|ty| {
|
let place = place_from_bindings(db, bindings).map_type(|ty| {
|
||||||
self.narrow_place_with_applicable_constraints(
|
self.narrow_place_with_applicable_constraints(
|
||||||
expr,
|
place_expr,
|
||||||
ty,
|
ty,
|
||||||
&constraint_keys,
|
&constraint_keys,
|
||||||
)
|
)
|
||||||
|
@ -6497,13 +6527,15 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
EnclosingSnapshotResult::NotFound => {
|
EnclosingSnapshotResult::NotFound => {
|
||||||
let enclosing_place_table =
|
let enclosing_place_table =
|
||||||
self.index.place_table(enclosing_scope_file_id);
|
self.index.place_table(enclosing_scope_file_id);
|
||||||
for enclosing_root_place in enclosing_place_table.root_place_exprs(expr)
|
for enclosing_root_place_id in enclosing_place_table.parents(place_expr)
|
||||||
{
|
{
|
||||||
|
let enclosing_root_place =
|
||||||
|
enclosing_place_table.place(enclosing_root_place_id);
|
||||||
if enclosing_root_place.is_bound() {
|
if enclosing_root_place.is_bound() {
|
||||||
if let Place::Type(_, _) = place(
|
if let Place::Type(_, _) = place(
|
||||||
db,
|
db,
|
||||||
enclosing_scope_id,
|
enclosing_scope_id,
|
||||||
&enclosing_root_place.expr,
|
enclosing_root_place,
|
||||||
ConsideredDefinitions::AllReachable,
|
ConsideredDefinitions::AllReachable,
|
||||||
)
|
)
|
||||||
.place
|
.place
|
||||||
|
@ -6523,17 +6555,22 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let enclosing_place_table = self.index.place_table(enclosing_scope_file_id);
|
let enclosing_place_table = self.index.place_table(enclosing_scope_file_id);
|
||||||
let Some(enclosing_place) = enclosing_place_table.place_by_expr(expr) else {
|
let Some(enclosing_place_id) = enclosing_place_table.place_id(place_expr) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let enclosing_place = enclosing_place_table.place(enclosing_place_id);
|
||||||
|
|
||||||
// Reads of "free" variables terminate at any enclosing scope that marks the
|
// Reads of "free" variables terminate at any enclosing scope that marks the
|
||||||
// variable `global`, whether or not that scope actually binds the variable. If we
|
// variable `global`, whether or not that scope actually binds the variable. If we
|
||||||
// see a `global` declaration, stop walking scopes and proceed to the global
|
// see a `global` declaration, stop walking scopes and proceed to the global
|
||||||
// handling below. (If we're walking from a prior/inner scope where this variable
|
// handling below. (If we're walking from a prior/inner scope where this variable
|
||||||
// is `nonlocal`, then this is a semantic syntax error, but we don't enforce that
|
// is `nonlocal`, then this is a semantic syntax error, but we don't enforce that
|
||||||
// here. See `infer_nonlocal_statement`.)
|
// here. See `infer_nonlocal_statement`.)
|
||||||
if enclosing_place.is_marked_global() {
|
if enclosing_place
|
||||||
|
.as_symbol()
|
||||||
|
.is_some_and(super::super::semantic_index::symbol::Symbol::is_global)
|
||||||
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6547,11 +6584,15 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
let local_place_and_qualifiers = place(
|
let local_place_and_qualifiers = place(
|
||||||
db,
|
db,
|
||||||
enclosing_scope_id,
|
enclosing_scope_id,
|
||||||
expr,
|
place_expr,
|
||||||
ConsideredDefinitions::AllReachable,
|
ConsideredDefinitions::AllReachable,
|
||||||
)
|
)
|
||||||
.map_type(|ty| {
|
.map_type(|ty| {
|
||||||
self.narrow_place_with_applicable_constraints(expr, ty, &constraint_keys)
|
self.narrow_place_with_applicable_constraints(
|
||||||
|
place_expr,
|
||||||
|
ty,
|
||||||
|
&constraint_keys,
|
||||||
|
)
|
||||||
});
|
});
|
||||||
// We could have Place::Unbound here, despite the checks above, for example if
|
// We could have Place::Unbound here, despite the checks above, for example if
|
||||||
// this scope contains a `del` statement but no binding or declaration.
|
// this scope contains a `del` statement but no binding or declaration.
|
||||||
|
@ -6562,7 +6603,10 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
found_some_definition = true;
|
found_some_definition = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !enclosing_place.is_marked_nonlocal() {
|
if !enclosing_place
|
||||||
|
.as_symbol()
|
||||||
|
.is_some_and(super::super::semantic_index::symbol::Symbol::is_nonlocal)
|
||||||
|
{
|
||||||
// We've reached a function-like scope that marks this name bound or
|
// We've reached a function-like scope that marks this name bound or
|
||||||
// declared but doesn't mark it `nonlocal`. The name is therefore resolved,
|
// declared but doesn't mark it `nonlocal`. The name is therefore resolved,
|
||||||
// and we won't consider any scopes outside of this one.
|
// and we won't consider any scopes outside of this one.
|
||||||
|
@ -6586,7 +6630,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
if !self.is_deferred() {
|
if !self.is_deferred() {
|
||||||
match self.index.enclosing_snapshot(
|
match self.index.enclosing_snapshot(
|
||||||
FileScopeId::global(),
|
FileScopeId::global(),
|
||||||
expr,
|
place_expr,
|
||||||
file_scope_id,
|
file_scope_id,
|
||||||
) {
|
) {
|
||||||
EnclosingSnapshotResult::FoundConstraint(constraint) => {
|
EnclosingSnapshotResult::FoundConstraint(constraint) => {
|
||||||
|
@ -6598,7 +6642,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
EnclosingSnapshotResult::FoundBindings(bindings) => {
|
EnclosingSnapshotResult::FoundBindings(bindings) => {
|
||||||
let place = place_from_bindings(db, bindings).map_type(|ty| {
|
let place = place_from_bindings(db, bindings).map_type(|ty| {
|
||||||
self.narrow_place_with_applicable_constraints(
|
self.narrow_place_with_applicable_constraints(
|
||||||
expr,
|
place_expr,
|
||||||
ty,
|
ty,
|
||||||
&constraint_keys,
|
&constraint_keys,
|
||||||
)
|
)
|
||||||
|
@ -6617,12 +6661,16 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let Some(name) = expr.as_name() else {
|
let Some(symbol) = place_expr.as_symbol() else {
|
||||||
return Place::Unbound.into();
|
return Place::Unbound.into();
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit_global_symbol(db, self.file(), name).map_type(|ty| {
|
explicit_global_symbol(db, self.file(), symbol.name()).map_type(|ty| {
|
||||||
self.narrow_place_with_applicable_constraints(expr, ty, &constraint_keys)
|
self.narrow_place_with_applicable_constraints(
|
||||||
|
place_expr,
|
||||||
|
ty,
|
||||||
|
&constraint_keys,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
@ -6739,8 +6787,12 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
) -> Type<'db> {
|
) -> Type<'db> {
|
||||||
let target = target.into();
|
let target = target.into();
|
||||||
|
|
||||||
if let Ok(place_expr) = PlaceExpr::try_from(target) {
|
if let Some(place_expr) = PlaceExpr::try_from_expr(target) {
|
||||||
self.narrow_place_with_applicable_constraints(&place_expr, target_ty, constraint_keys)
|
self.narrow_place_with_applicable_constraints(
|
||||||
|
PlaceExprRef::from(&place_expr),
|
||||||
|
target_ty,
|
||||||
|
constraint_keys,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
target_ty
|
target_ty
|
||||||
}
|
}
|
||||||
|
@ -6761,9 +6813,11 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
let mut constraint_keys = vec![];
|
let mut constraint_keys = vec![];
|
||||||
|
|
||||||
let mut assigned_type = None;
|
let mut assigned_type = None;
|
||||||
if let Ok(place_expr) = PlaceExpr::try_from(attribute) {
|
if let Some(place_expr) = PlaceExpr::try_from_expr(attribute) {
|
||||||
let (resolved, keys) =
|
let (resolved, keys) = self.infer_place_load(
|
||||||
self.infer_place_load(&place_expr, ast::ExprRef::Attribute(attribute));
|
PlaceExprRef::from(&place_expr),
|
||||||
|
ast::ExprRef::Attribute(attribute),
|
||||||
|
);
|
||||||
constraint_keys.extend(keys);
|
constraint_keys.extend(keys);
|
||||||
if let Place::Type(ty, Boundness::Bound) = resolved.place {
|
if let Place::Type(ty, Boundness::Bound) = resolved.place {
|
||||||
assigned_type = Some(ty);
|
assigned_type = Some(ty);
|
||||||
|
@ -8309,9 +8363,11 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
|
|
||||||
// If `value` is a valid reference, we attempt type narrowing by assignment.
|
// If `value` is a valid reference, we attempt type narrowing by assignment.
|
||||||
if !value_ty.is_unknown() {
|
if !value_ty.is_unknown() {
|
||||||
if let Ok(expr) = PlaceExpr::try_from(subscript) {
|
if let Some(expr) = PlaceExpr::try_from_expr(subscript) {
|
||||||
let (place, keys) =
|
let (place, keys) = self.infer_place_load(
|
||||||
self.infer_place_load(&expr, ast::ExprRef::Subscript(subscript));
|
PlaceExprRef::from(&expr),
|
||||||
|
ast::ExprRef::Subscript(subscript),
|
||||||
|
);
|
||||||
constraint_keys.extend(keys);
|
constraint_keys.extend(keys);
|
||||||
if let Place::Type(ty, Boundness::Bound) = place.place {
|
if let Place::Type(ty, Boundness::Bound) = place.place {
|
||||||
// Even if we can obtain the subscript type based on the assignments, we still perform default type inference
|
// Even if we can obtain the subscript type based on the assignments, we still perform default type inference
|
||||||
|
@ -11001,7 +11057,7 @@ mod tests {
|
||||||
use crate::db::tests::{TestDb, setup_db};
|
use crate::db::tests::{TestDb, setup_db};
|
||||||
use crate::place::{global_symbol, symbol};
|
use crate::place::{global_symbol, symbol};
|
||||||
use crate::semantic_index::definition::Definition;
|
use crate::semantic_index::definition::Definition;
|
||||||
use crate::semantic_index::place::FileScopeId;
|
use crate::semantic_index::scope::FileScopeId;
|
||||||
use crate::semantic_index::{global_scope, place_table, semantic_index, use_def_map};
|
use crate::semantic_index::{global_scope, place_table, semantic_index, use_def_map};
|
||||||
use crate::types::check_types;
|
use crate::types::check_types;
|
||||||
use ruff_db::diagnostic::Diagnostic;
|
use ruff_db::diagnostic::Diagnostic;
|
||||||
|
@ -11278,7 +11334,7 @@ mod tests {
|
||||||
fn first_public_binding<'db>(db: &'db TestDb, file: File, name: &str) -> Definition<'db> {
|
fn first_public_binding<'db>(db: &'db TestDb, file: File, name: &str) -> Definition<'db> {
|
||||||
let scope = global_scope(db, file);
|
let scope = global_scope(db, file);
|
||||||
use_def_map(db, scope)
|
use_def_map(db, scope)
|
||||||
.end_of_scope_bindings(place_table(db, scope).place_id_by_name(name).unwrap())
|
.end_of_scope_symbol_bindings(place_table(db, scope).symbol_id(name).unwrap())
|
||||||
.find_map(|b| b.binding.definition())
|
.find_map(|b| b.binding.definition())
|
||||||
.expect("no binding found")
|
.expect("no binding found")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
use crate::Db;
|
use crate::Db;
|
||||||
use crate::semantic_index::expression::Expression;
|
use crate::semantic_index::expression::Expression;
|
||||||
use crate::semantic_index::place::{PlaceExpr, PlaceTable, ScopeId, ScopedPlaceId};
|
use crate::semantic_index::place::{PlaceExpr, PlaceTable, ScopedPlaceId};
|
||||||
use crate::semantic_index::place_table;
|
use crate::semantic_index::place_table;
|
||||||
use crate::semantic_index::predicate::{
|
use crate::semantic_index::predicate::{
|
||||||
CallableAndCallExpr, ClassPatternKind, PatternPredicate, PatternPredicateKind, Predicate,
|
CallableAndCallExpr, ClassPatternKind, PatternPredicate, PatternPredicateKind, Predicate,
|
||||||
PredicateNode,
|
PredicateNode,
|
||||||
};
|
};
|
||||||
|
use crate::semantic_index::scope::ScopeId;
|
||||||
use crate::types::enums::{enum_member_literals, enum_metadata};
|
use crate::types::enums::{enum_member_literals, enum_metadata};
|
||||||
use crate::types::function::KnownFunction;
|
use crate::types::function::KnownFunction;
|
||||||
use crate::types::infer::infer_same_file_expression_type;
|
use crate::types::infer::infer_same_file_expression_type;
|
||||||
|
@ -312,11 +313,8 @@ fn negate_if<'db>(constraints: &mut NarrowingConstraints<'db>, db: &'db dyn Db,
|
||||||
|
|
||||||
fn place_expr(expr: &ast::Expr) -> Option<PlaceExpr> {
|
fn place_expr(expr: &ast::Expr) -> Option<PlaceExpr> {
|
||||||
match expr {
|
match expr {
|
||||||
ast::Expr::Name(name) => Some(PlaceExpr::name(name.id.clone())),
|
ast::Expr::Named(named) => PlaceExpr::try_from_expr(named.target.as_ref()),
|
||||||
ast::Expr::Attribute(attr) => PlaceExpr::try_from(attr).ok(),
|
_ => PlaceExpr::try_from_expr(expr),
|
||||||
ast::Expr::Subscript(subscript) => PlaceExpr::try_from(subscript).ok(),
|
|
||||||
ast::Expr::Named(named) => PlaceExpr::try_from(named.target.as_ref()).ok(),
|
|
||||||
_ => None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -447,7 +445,7 @@ impl<'db, 'ast> NarrowingConstraintsBuilder<'db, 'ast> {
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn expect_place(&self, place_expr: &PlaceExpr) -> ScopedPlaceId {
|
fn expect_place(&self, place_expr: &PlaceExpr) -> ScopedPlaceId {
|
||||||
self.places()
|
self.places()
|
||||||
.place_id_by_expr(place_expr)
|
.place_id(place_expr)
|
||||||
.expect("We should always have a place for every `PlaceExpr`")
|
.expect("We should always have a place for every `PlaceExpr`")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,10 +4,12 @@ use itertools::Itertools;
|
||||||
|
|
||||||
use ruff_python_ast::name::Name;
|
use ruff_python_ast::name::Name;
|
||||||
|
|
||||||
|
use super::TypeVarVariance;
|
||||||
|
use crate::semantic_index::place_table;
|
||||||
use crate::{
|
use crate::{
|
||||||
Db, FxOrderSet,
|
Db, FxOrderSet,
|
||||||
place::{Boundness, Place, PlaceAndQualifiers, place_from_bindings, place_from_declarations},
|
place::{Boundness, Place, PlaceAndQualifiers, place_from_bindings, place_from_declarations},
|
||||||
semantic_index::{place_table, use_def_map},
|
semantic_index::use_def_map,
|
||||||
types::{
|
types::{
|
||||||
CallableType, ClassBase, ClassLiteral, KnownFunction, PropertyInstanceType, Signature,
|
CallableType, ClassBase, ClassLiteral, KnownFunction, PropertyInstanceType, Signature,
|
||||||
Type, TypeMapping, TypeQualifiers, TypeRelation, TypeTransformer, TypeVarInstance,
|
Type, TypeMapping, TypeQualifiers, TypeRelation, TypeTransformer, TypeVarInstance,
|
||||||
|
@ -16,8 +18,6 @@ use crate::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::TypeVarVariance;
|
|
||||||
|
|
||||||
impl<'db> ClassLiteral<'db> {
|
impl<'db> ClassLiteral<'db> {
|
||||||
/// Returns `Some` if this is a protocol class, `None` otherwise.
|
/// Returns `Some` if this is a protocol class, `None` otherwise.
|
||||||
pub(super) fn into_protocol_class(self, db: &'db dyn Db) -> Option<ProtocolClassLiteral<'db>> {
|
pub(super) fn into_protocol_class(self, db: &'db dyn Db) -> Option<ProtocolClassLiteral<'db>> {
|
||||||
|
@ -471,15 +471,15 @@ fn cached_protocol_interface<'db>(
|
||||||
|
|
||||||
members.extend(
|
members.extend(
|
||||||
use_def_map
|
use_def_map
|
||||||
.all_end_of_scope_declarations()
|
.all_end_of_scope_symbol_declarations()
|
||||||
.flat_map(|(place_id, declarations)| {
|
.flat_map(|(symbol_id, declarations)| {
|
||||||
place_from_declarations(db, declarations).map(|place| (place_id, place))
|
place_from_declarations(db, declarations).map(|place| (symbol_id, place))
|
||||||
})
|
})
|
||||||
.filter_map(|(place_id, place)| {
|
.filter_map(|(symbol_id, place)| {
|
||||||
place
|
place
|
||||||
.place
|
.place
|
||||||
.ignore_possibly_unbound()
|
.ignore_possibly_unbound()
|
||||||
.map(|ty| (place_id, ty, place.qualifiers, BoundOnClass::No))
|
.map(|ty| (symbol_id, ty, place.qualifiers, BoundOnClass::No))
|
||||||
})
|
})
|
||||||
// Bindings in the class body that are not declared in the class body
|
// Bindings in the class body that are not declared in the class body
|
||||||
// are not valid protocol members, and we plan to emit diagnostics for them
|
// are not valid protocol members, and we plan to emit diagnostics for them
|
||||||
|
@ -489,20 +489,20 @@ fn cached_protocol_interface<'db>(
|
||||||
// members at runtime, and it's important that we accurately understand
|
// members at runtime, and it's important that we accurately understand
|
||||||
// type narrowing that uses `isinstance()` or `issubclass()` with
|
// type narrowing that uses `isinstance()` or `issubclass()` with
|
||||||
// runtime-checkable protocols.
|
// runtime-checkable protocols.
|
||||||
.chain(use_def_map.all_end_of_scope_bindings().filter_map(
|
.chain(use_def_map.all_end_of_scope_symbol_bindings().filter_map(
|
||||||
|(place_id, bindings)| {
|
|(symbol_id, bindings)| {
|
||||||
place_from_bindings(db, bindings)
|
place_from_bindings(db, bindings)
|
||||||
.ignore_possibly_unbound()
|
.ignore_possibly_unbound()
|
||||||
.map(|ty| (place_id, ty, TypeQualifiers::default(), BoundOnClass::Yes))
|
.map(|ty| (symbol_id, ty, TypeQualifiers::default(), BoundOnClass::Yes))
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
.filter_map(|(place_id, member, qualifiers, bound_on_class)| {
|
.map(|(symbol_id, member, qualifiers, bound_on_class)| {
|
||||||
Some((
|
(
|
||||||
place_table.place_expr(place_id).as_name()?,
|
place_table.symbol(symbol_id).name(),
|
||||||
member,
|
member,
|
||||||
qualifiers,
|
qualifiers,
|
||||||
bound_on_class,
|
bound_on_class,
|
||||||
))
|
)
|
||||||
})
|
})
|
||||||
.filter(|(name, _, _, _)| !excluded_from_proto_members(name))
|
.filter(|(name, _, _, _)| !excluded_from_proto_members(name))
|
||||||
.map(|(name, ty, qualifiers, bound_on_class)| {
|
.map(|(name, ty, qualifiers, bound_on_class)| {
|
||||||
|
|
|
@ -7,7 +7,7 @@ use ruff_python_ast::{self as ast, AnyNodeRef};
|
||||||
|
|
||||||
use crate::Db;
|
use crate::Db;
|
||||||
use crate::semantic_index::ast_ids::node_key::ExpressionNodeKey;
|
use crate::semantic_index::ast_ids::node_key::ExpressionNodeKey;
|
||||||
use crate::semantic_index::place::ScopeId;
|
use crate::semantic_index::scope::ScopeId;
|
||||||
use crate::types::tuple::{ResizeTupleError, Tuple, TupleLength, TupleSpec, TupleUnpacker};
|
use crate::types::tuple::{ResizeTupleError, Tuple, TupleLength, TupleSpec, TupleUnpacker};
|
||||||
use crate::types::{Type, TypeCheckDiagnostics, infer_expression_types};
|
use crate::types::{Type, TypeCheckDiagnostics, infer_expression_types};
|
||||||
use crate::unpack::{UnpackKind, UnpackValue};
|
use crate::unpack::{UnpackKind, UnpackValue};
|
||||||
|
|
|
@ -6,7 +6,7 @@ use ruff_text_size::{Ranged, TextRange};
|
||||||
use crate::Db;
|
use crate::Db;
|
||||||
use crate::ast_node_ref::AstNodeRef;
|
use crate::ast_node_ref::AstNodeRef;
|
||||||
use crate::semantic_index::expression::Expression;
|
use crate::semantic_index::expression::Expression;
|
||||||
use crate::semantic_index::place::{FileScopeId, ScopeId};
|
use crate::semantic_index::scope::{FileScopeId, ScopeId};
|
||||||
|
|
||||||
/// This ingredient represents a single unpacking.
|
/// This ingredient represents a single unpacking.
|
||||||
///
|
///
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue