mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-17 09:00:26 +00:00
Track conditional deletions in the semantic model (#10415)
## Summary Given `del X`, we'll typically add a `BindingKind::Deletion` to `X` to shadow the current binding. However, if the deletion is inside of a conditional operation, we _won't_, as in: ```python def f(): global X if X > 0: del X ``` We will, however, track it as a reference to the binding. This PR adds the expression context to those resolved references, so that we can detect that the `X` in `global X` was "assigned to". Closes https://github.com/astral-sh/ruff/issues/10397.
This commit is contained in:
parent
a8e50a7f40
commit
10ace88e9a
10 changed files with 133 additions and 45 deletions
|
@ -3,10 +3,10 @@ use std::ops::Deref;
|
|||
use bitflags::bitflags;
|
||||
|
||||
use ruff_index::{newtype_index, IndexSlice, IndexVec};
|
||||
use ruff_python_ast::ExprContext;
|
||||
use ruff_source_file::Locator;
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
use crate::context::ExecutionContext;
|
||||
use crate::scope::ScopeId;
|
||||
use crate::{Exceptions, NodeId, SemanticModelFlags};
|
||||
|
||||
|
@ -18,10 +18,12 @@ pub struct ResolvedReference {
|
|||
node_id: Option<NodeId>,
|
||||
/// The scope in which the reference is defined.
|
||||
scope_id: ScopeId,
|
||||
/// The range of the reference in the source code.
|
||||
range: TextRange,
|
||||
/// The expression context in which the reference occurs (e.g., `Load`, `Store`, `Del`).
|
||||
ctx: ExprContext,
|
||||
/// The model state in which the reference occurs.
|
||||
flags: SemanticModelFlags,
|
||||
/// The range of the reference in the source code.
|
||||
range: TextRange,
|
||||
}
|
||||
|
||||
impl ResolvedReference {
|
||||
|
@ -35,13 +37,19 @@ impl ResolvedReference {
|
|||
self.scope_id
|
||||
}
|
||||
|
||||
/// The [`ExecutionContext`] of the reference.
|
||||
pub const fn context(&self) -> ExecutionContext {
|
||||
if self.flags.intersects(SemanticModelFlags::TYPING_CONTEXT) {
|
||||
ExecutionContext::Typing
|
||||
} else {
|
||||
ExecutionContext::Runtime
|
||||
}
|
||||
/// Return `true` if the reference occurred in a `Load` operation.
|
||||
pub const fn is_load(&self) -> bool {
|
||||
self.ctx.is_load()
|
||||
}
|
||||
|
||||
/// Return `true` if the context is in a typing context.
|
||||
pub const fn in_typing_context(&self) -> bool {
|
||||
self.flags.intersects(SemanticModelFlags::TYPING_CONTEXT)
|
||||
}
|
||||
|
||||
/// Return `true` if the context is in a runtime context.
|
||||
pub const fn in_runtime_context(&self) -> bool {
|
||||
!self.flags.intersects(SemanticModelFlags::TYPING_CONTEXT)
|
||||
}
|
||||
|
||||
/// Return `true` if the context is in a typing-only type annotation.
|
||||
|
@ -108,14 +116,16 @@ impl ResolvedReferences {
|
|||
&mut self,
|
||||
scope_id: ScopeId,
|
||||
node_id: Option<NodeId>,
|
||||
range: TextRange,
|
||||
ctx: ExprContext,
|
||||
flags: SemanticModelFlags,
|
||||
range: TextRange,
|
||||
) -> ResolvedReferenceId {
|
||||
self.0.push(ResolvedReference {
|
||||
node_id,
|
||||
scope_id,
|
||||
range,
|
||||
ctx,
|
||||
flags,
|
||||
range,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue