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

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

View file

@ -30,10 +30,7 @@ use crate::semantic_index::definition::{
StarImportDefinitionNodeRef, WithItemDefinitionNodeRef,
};
use crate::semantic_index::expression::{Expression, ExpressionKind};
use crate::semantic_index::place::{
FileScopeId, NodeWithScopeKey, NodeWithScopeKind, NodeWithScopeRef, PlaceExpr,
PlaceExprWithFlags, PlaceTableBuilder, Scope, ScopeId, ScopeKind, ScopedPlaceId,
};
use crate::semantic_index::place::{PlaceExpr, PlaceTableBuilder, ScopedPlaceId};
use crate::semantic_index::predicate::{
CallableAndCallExpr, ClassPatternKind, PatternPredicate, PatternPredicateKind, Predicate,
PredicateNode, PredicateOrLiteral, ScopedPredicateId, StarImportPlaceholderPredicate,
@ -42,10 +39,15 @@ use crate::semantic_index::re_exports::exported_names;
use crate::semantic_index::reachability_constraints::{
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::{
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::unpack::{Unpack, UnpackKind, UnpackPosition, UnpackValue};
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_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.
// Note that even if this place is bound in the popped scope,
// it may refer to the enclosing scope bindings
// so we also need to snapshot the bindings of the enclosing scope.
let Some(enclosing_place_id) =
enclosing_place_table.place_id_by_expr(&nested_place.expr)
else {
let Some(enclosing_place_id) = enclosing_place_table.place_id(nested_place) else {
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
// enclosing scope.
@ -332,11 +332,7 @@ impl<'db, 'ast> SemanticIndexBuilder<'db, 'ast> {
}
}
fn bound_scope(
&self,
enclosing_scope: FileScopeId,
place_expr: &PlaceExpr,
) -> Option<FileScopeId> {
fn bound_scope(&self, enclosing_scope: FileScopeId, symbol: &Symbol) -> Option<FileScopeId> {
self.scope_stack
.iter()
.rev()
@ -344,12 +340,8 @@ impl<'db, 'ast> SemanticIndexBuilder<'db, 'ast> {
.find_map(|scope_info| {
let scope_id = scope_info.file_scope_id;
let place_table = &self.place_tables[scope_id];
let place_id = place_table.place_id_by_expr(place_expr)?;
if place_table.place_expr(place_id).is_bound() {
Some(scope_id)
} else {
None
}
let place_id = place_table.symbol_id(symbol.name())?;
place_table.place(place_id).is_bound().then_some(scope_id)
})
}
@ -360,13 +352,11 @@ impl<'db, 'ast> SemanticIndexBuilder<'db, 'ast> {
let enclosing_scope_kind = self.scopes[enclosing_scope_id].kind();
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.
// Also, if the enclosing scope allows its members to be modified from elsewhere, the snapshot will not be recorded.
if !nested_place.is_name()
|| self.scopes[enclosing_scope_id].visibility().is_public()
{
if self.scopes[enclosing_scope_id].visibility().is_public() {
continue;
}
@ -374,17 +364,16 @@ impl<'db, 'ast> SemanticIndexBuilder<'db, 'ast> {
// Note that even if this place is bound in the popped scope,
// it may refer to the enclosing scope bindings
// so we also need to snapshot the bindings of the enclosing scope.
let Some(enclosing_place_id) =
enclosing_place_table.place_id_by_expr(&nested_place.expr)
let Some(enclosed_symbol_id) =
enclosing_place_table.symbol_id(nested_symbol.name())
else {
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 the bound scope of a place can be modified from elsewhere, the snapshot will not be recorded.
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())
{
continue;
@ -395,14 +384,14 @@ impl<'db, 'ast> SemanticIndexBuilder<'db, 'ast> {
// enclosing scope (this may later be invalidated and swept away).
let key = EnclosingSnapshotKey {
enclosing_scope: enclosing_scope_id,
enclosing_place: enclosing_place_id,
enclosing_place: enclosed_symbol_id.into(),
nested_scope: popped_scope_id,
nested_laziness: ScopeLaziness::Lazy,
};
let lazy_snapshot = self.use_def_maps[enclosing_scope_id].snapshot_outer_state(
enclosing_place_id,
enclosed_symbol_id.into(),
enclosing_scope_kind,
enclosing_place,
enclosing_place.into(),
);
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];
key.nested_laziness.is_eager()
|| 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, _| {
let place_table = &self.place_tables[key.enclosing_scope];
let is_place_bound_and_nonlocal = || -> bool {
let place_expr = place_table.place_expr(key.enclosing_place);
let is_bound_and_non_local = || -> bool {
let ScopedPlaceId::Symbol(symbol_id) = key.enclosing_place else {
return false;
};
let symbol = place_table.symbol(symbol_id);
self.scopes
.iter_enumerated()
.skip_while(|(scope_id, _)| *scope_id != key.enclosing_scope)
.any(|(scope_id, _)| {
let other_scope_place_table = &self.place_tables[scope_id];
let Some(place_id) =
other_scope_place_table.place_id_by_expr(&place_expr.expr)
let Some(symbol_id) = other_scope_place_table.symbol_id(symbol.name())
else {
return false;
};
let place = other_scope_place_table.place_expr(place_id);
place.is_marked_nonlocal() && place.is_bound()
let symbol = other_scope_place_table.symbol(symbol_id);
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.
/// Return the [`ScopedPlaceId`] that uniquely identifies the symbol in both.
fn add_symbol(&mut self, name: Name) -> ScopedPlaceId {
let (place_id, added) = self.current_place_table_mut().add_symbol(name);
fn add_symbol(&mut self, name: Name) -> ScopedSymbolId {
let (symbol_id, added) = self.current_place_table_mut().add_symbol(Symbol::new(name));
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.
/// 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);
if added {
self.current_use_def_map_mut().add_place(place_id);
@ -533,16 +528,19 @@ impl<'db, 'ast> SemanticIndexBuilder<'db, 'ast> {
place_id
}
#[track_caller]
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) {
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) {
self.current_place_table_mut().mark_place_used(id);
#[track_caller]
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> {
@ -572,23 +570,20 @@ impl<'db, 'ast> SemanticIndexBuilder<'db, 'ast> {
fn delete_associated_bindings(&mut self, place: ScopedPlaceId) {
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)
if self.scopes[scope].kind() == ScopeKind::Class
&& self.place_tables[scope].place_expr(place).is_name()
{
if self.scopes[scope].kind() == ScopeKind::Class && place.is_symbol() {
return;
}
for associated_place in self.place_tables[scope].associated_place_ids(place) {
let is_place_name = self.place_tables[scope]
.place_expr(associated_place)
.is_name();
self.use_def_maps[scope].delete_binding(associated_place, is_place_name);
for associated_place in self.place_tables[scope]
.associated_place_ids(place)
.iter()
.copied()
{
self.use_def_maps[scope].delete_binding(associated_place.into());
}
}
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, is_place_name);
self.current_use_def_map_mut().delete_binding(place);
}
/// Push a new [`Definition`] onto the list of definitions
@ -637,16 +632,15 @@ impl<'db, 'ast> SemanticIndexBuilder<'db, 'ast> {
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();
match category {
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);
}
DefinitionCategory::Declaration => use_def.record_declaration(place, definition),
DefinitionCategory::Binding => {
use_def.record_binding(place, definition, is_place_name);
use_def.record_binding(place, definition);
self.delete_associated_bindings(place);
}
}
@ -963,8 +957,8 @@ impl<'db, 'ast> SemanticIndexBuilder<'db, 'ast> {
// TODO create Definition for PEP 695 typevars
// 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.
self.mark_place_bound(symbol);
self.mark_place_declared(symbol);
self.mark_place_bound(symbol.into());
self.mark_place_declared(symbol.into());
if let Some(bounds) = bound {
self.visit_expr(bounds);
}
@ -972,9 +966,9 @@ impl<'db, 'ast> SemanticIndexBuilder<'db, 'ast> {
self.visit_expr(default);
}
match type_param {
ast::TypeParam::TypeVar(node) => self.add_definition(symbol, node),
ast::TypeParam::ParamSpec(node) => self.add_definition(symbol, node),
ast::TypeParam::TypeVarTuple(node) => self.add_definition(symbol, node),
ast::TypeParam::TypeVar(node) => self.add_definition(symbol.into(), node),
ast::TypeParam::ParamSpec(node) => self.add_definition(symbol.into(), 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() {
let symbol = self.add_symbol(vararg.name.id().clone());
self.add_definition(
symbol,
symbol.into(),
DefinitionNodeRef::VariadicPositionalParameter(vararg),
);
}
if let Some(kwarg) = parameters.kwarg.as_ref() {
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) {
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
// 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
// done on the `Identifier` node as opposed to `ExprName` because that's what the
// AST uses.
self.mark_place_used(symbol);
self.mark_symbol_used(symbol);
let use_id = self.current_ast_ids().record_use(name);
self.current_use_def_map_mut()
.record_use(symbol, use_id, NodeKey::from_node(name));
self.current_use_def_map_mut().record_use(
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) => {
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.
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) => {
let symbol = self.add_symbol(
@ -1334,7 +1334,7 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
.map(|name| name.id.clone())
.unwrap_or("<unknown>".into()),
);
self.add_definition(symbol, type_alias);
self.add_definition(symbol.into(), type_alias);
self.visit_expr(&type_alias.name);
self.with_type_params(
@ -1366,7 +1366,7 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
let symbol = self.add_symbol(symbol_name);
self.add_definition(
symbol,
symbol.into(),
ImportDefinitionNodeRef {
node,
alias_index,
@ -1438,10 +1438,7 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
// For more details, see the doc-comment on `StarImportPlaceholderPredicate`.
for export in exported_names(self.db, referenced_module) {
let symbol_id = self.add_symbol(export.clone());
let node_ref = StarImportDefinitionNodeRef {
node,
place_id: symbol_id,
};
let node_ref = StarImportDefinitionNodeRef { node, symbol_id };
let star_import = StarImportPlaceholderPredicate::new(
self.db,
self.file,
@ -1451,8 +1448,9 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
let star_import_predicate = self.add_predicate(star_import.into());
let pre_definition =
self.current_use_def_map().single_place_snapshot(symbol_id);
let pre_definition = self
.current_use_def_map()
.single_symbol_place_snapshot(symbol_id);
let pre_definition_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.push_additional_definition(symbol_id, node_ref);
self.push_additional_definition(symbol_id.into(), node_ref);
self.current_use_def_map_mut()
.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());
self.add_definition(
symbol,
symbol.into(),
ImportFromDefinitionNodeRef {
node,
alias_index,
@ -1580,9 +1578,9 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
if let ast::Expr::Name(name) = &*node.target {
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.
if symbol.is_marked_global() {
if symbol.is_global() {
self.report_semantic_error(SemanticSyntaxError {
kind: SemanticSyntaxErrorKind::AnnotatedGlobal(name.id.as_str().into()),
range: name.range,
@ -1590,7 +1588,7 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
});
}
// Check whether the variable has been declared nonlocal.
if symbol.is_marked_nonlocal() {
if symbol.is_nonlocal() {
self.report_semantic_error(SemanticSyntaxError {
kind: SemanticSyntaxErrorKind::AnnotatedNonlocal(
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());
self.add_definition(
symbol,
symbol.into(),
DefinitionNodeRef::ExceptHandler(ExceptHandlerDefinitionNodeRef {
handler: except_handler,
is_star: *is_star,
@ -2020,7 +2018,7 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
self.visit_body(handler_body);
// The caught exception is cleared at the end of the except clause
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.
post_except_states.push(self.flow_snapshot());
@ -2078,7 +2076,7 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
}) => {
for name in names {
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.
if symbol.is_bound() || symbol.is_declared() || symbol.is_used() {
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.
if symbol.is_marked_nonlocal() {
if symbol.is_nonlocal() {
self.report_semantic_error(SemanticSyntaxError {
kind: SemanticSyntaxErrorKind::NonlocalAndGlobal(name.to_string()),
range: name.range,
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);
}
@ -2109,7 +2109,7 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
}) => {
for name in names {
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.
if symbol.is_bound() || symbol.is_declared() || symbol.is_used() {
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.
if symbol.is_marked_global() {
if symbol.is_global() {
self.report_semantic_error(SemanticSyntaxError {
kind: SemanticSyntaxErrorKind::NonlocalAndGlobal(name.to_string()),
range: name.range,
@ -2139,7 +2139,8 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
// x = 1
// ```
self.current_place_table_mut()
.mark_place_nonlocal(symbol_id);
.symbol_mut(symbol_id)
.mark_nonlocal();
}
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.
walk_stmt(self, stmt);
for target in targets {
if let Ok(target) = PlaceExpr::try_from(target) {
let is_name = target.is_name();
let place_id = self.add_place(PlaceExprWithFlags::new(target));
let place_table = self.current_place_table_mut();
if is_name {
if let Some(mut target) = PlaceExpr::try_from_expr(target) {
if let PlaceExpr::Symbol(symbol) = &mut target {
// `del x` behaves like an assignment in that it forces all references
// to `x` in the current scope (including *prior* references) to refer
// 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
// 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);
}
}
@ -2238,19 +2238,20 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
ast::Expr::Name(ast::ExprName { ctx, .. })
| ast::Expr::Attribute(ast::ExprAttribute { ctx, .. })
| ast::Expr::Subscript(ast::ExprSubscript { ctx, .. }) => {
if let Ok(place_expr) = PlaceExpr::try_from(expr) {
let mut place_expr = PlaceExprWithFlags::new(place_expr);
if self.is_method_of_class().is_some()
&& place_expr.is_instance_attribute_candidate()
{
// We specifically mark attribute assignments to the first parameter of a method,
// i.e. typically `self` or `cls`.
let accessed_object_refers_to_first_parameter = self
.current_first_parameter_name
.is_some_and(|fst| place_expr.expr.root_name() == fst);
if let Some(mut place_expr) = PlaceExpr::try_from_expr(expr) {
if self.is_method_of_class().is_some() {
if let PlaceExpr::Member(member) = &mut place_expr {
if member.is_instance_attribute_candidate() {
// We specifically mark attribute assignments to the first parameter of a method,
// i.e. typically `self` or `cls`.
let accessed_object_refers_to_first_parameter = self
.current_first_parameter_name
.is_some_and(|first| member.symbol_name() == first);
if accessed_object_refers_to_first_parameter && place_expr.is_member() {
place_expr.mark_instance_attribute();
if accessed_object_refers_to_first_parameter {
member.mark_instance_attribute();
}
}
}
}
@ -2267,7 +2268,9 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
let place_id = self.add_place(place_expr);
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);
self.current_use_def_map_mut()
.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 state = self.current_match_case.as_ref().unwrap();
self.add_definition(
symbol,
symbol.into(),
MatchPatternDefinitionNodeRef {
pattern: state.pattern,
identifier: name,
@ -2582,7 +2585,7 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
let symbol = self.add_symbol(name.id().clone());
let state = self.current_match_case.as_ref().unwrap();
self.add_definition(
symbol,
symbol.into(),
MatchPatternDefinitionNodeRef {
pattern: state.pattern,
identifier: name,