mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-25 17:38:19 +00:00
## Summary The motivation of `ScopedExpressionId` was that we have an expression identifier that's local to a scope and, therefore, unlikely to change if a user makes changes in another scope. A local identifier like this has the advantage that query results may remain unchanged even if other parts of the file change, which in turn allows Salsa to short-circuit dependent queries. However, I noticed that we aren't using `ScopedExpressionId` in a place where it's important that the identifier is local. It's main use is inside `infer` which we always run for the entire file. The one exception to this is `Unpack` but unpack runs as part of `infer`. Edit: The above isn't entirely correct. We used ScopedExpressionId in TypeInference which is a query result. Now using ExpressionNodeKey does mean that a change to the AST invalidates most if not all TypeInference results of a single file. Salsa then has to run all dependent queries to see if they're affected by this change even if the change was local to another scope. If this locality proves to be important I suggest that we create two queries on top of TypeInference: one that returns the expression map which is mainly used in the linter and type inference and a second that returns all remaining fields. This should give us a similar optimization at a much lower cost I also considered remove `ScopedUseId` but I believe that one is still useful because using `ExpressionNodeKey` for it instead would mean that all `UseDefMap` change when a single AST node changes. Whether this is important is something difficult to assess. I'm simply not familiar enough with the `UseDefMap`. If the locality doesn't matter for the `UseDefMap`, then a similar change could be made and `bindings_by_use` could be changed to an `FxHashMap<UseId, Bindings>` where `UseId` is a thin wrapper around `NodeKey`. Closes https://github.com/astral-sh/ty/issues/721
121 lines
3.9 KiB
Rust
121 lines
3.9 KiB
Rust
use ruff_db::files::File;
|
|
use ruff_db::parsed::ParsedModuleRef;
|
|
use ruff_python_ast::{self as ast, AnyNodeRef};
|
|
use ruff_text_size::{Ranged, TextRange};
|
|
|
|
use crate::Db;
|
|
use crate::ast_node_ref::AstNodeRef;
|
|
use crate::semantic_index::expression::Expression;
|
|
use crate::semantic_index::place::{FileScopeId, ScopeId};
|
|
|
|
/// This ingredient represents a single unpacking.
|
|
///
|
|
/// This is required to make use of salsa to cache the complete unpacking of multiple variables
|
|
/// involved. It allows us to:
|
|
/// 1. Avoid doing structural match multiple times for each definition
|
|
/// 2. Avoid highlighting the same error multiple times
|
|
///
|
|
/// ## Module-local type
|
|
/// This type should not be used as part of any cross-module API because
|
|
/// it holds a reference to the AST node. Range-offset changes
|
|
/// then propagate through all usages, and deserialization requires
|
|
/// reparsing the entire module.
|
|
///
|
|
/// E.g. don't use this type in:
|
|
///
|
|
/// * a return type of a cross-module query
|
|
/// * a field of a type that is a return type of a cross-module query
|
|
/// * an argument of a cross-module query
|
|
#[salsa::tracked(debug)]
|
|
pub(crate) struct Unpack<'db> {
|
|
pub(crate) file: File,
|
|
|
|
pub(crate) value_file_scope: FileScopeId,
|
|
|
|
pub(crate) target_file_scope: FileScopeId,
|
|
|
|
/// The target expression that is being unpacked. For example, in `(a, b) = (1, 2)`, the target
|
|
/// expression is `(a, b)`.
|
|
#[no_eq]
|
|
#[tracked]
|
|
#[returns(ref)]
|
|
pub(crate) _target: AstNodeRef<ast::Expr>,
|
|
|
|
/// The ingredient representing the value expression of the unpacking. For example, in
|
|
/// `(a, b) = (1, 2)`, the value expression is `(1, 2)`.
|
|
pub(crate) value: UnpackValue<'db>,
|
|
|
|
count: countme::Count<Unpack<'static>>,
|
|
}
|
|
|
|
impl<'db> Unpack<'db> {
|
|
pub(crate) fn target<'ast>(
|
|
self,
|
|
db: &'db dyn Db,
|
|
parsed: &'ast ParsedModuleRef,
|
|
) -> &'ast ast::Expr {
|
|
self._target(db).node(parsed)
|
|
}
|
|
|
|
/// Returns the scope where the unpack target expression belongs to.
|
|
pub(crate) fn target_scope(self, db: &'db dyn Db) -> ScopeId<'db> {
|
|
self.target_file_scope(db).to_scope_id(db, self.file(db))
|
|
}
|
|
|
|
/// Returns the range of the unpack target expression.
|
|
pub(crate) fn range(self, db: &'db dyn Db, module: &ParsedModuleRef) -> TextRange {
|
|
self.target(db, module).range()
|
|
}
|
|
}
|
|
|
|
/// The expression that is being unpacked.
|
|
#[derive(Clone, Copy, Debug, Hash, salsa::Update)]
|
|
pub(crate) struct UnpackValue<'db> {
|
|
/// The kind of unpack expression
|
|
kind: UnpackKind,
|
|
/// The expression we are unpacking
|
|
expression: Expression<'db>,
|
|
}
|
|
|
|
impl<'db> UnpackValue<'db> {
|
|
pub(crate) fn new(kind: UnpackKind, expression: Expression<'db>) -> Self {
|
|
Self { kind, expression }
|
|
}
|
|
|
|
/// Returns the underlying [`Expression`] that is being unpacked.
|
|
pub(crate) const fn expression(self) -> Expression<'db> {
|
|
self.expression
|
|
}
|
|
|
|
/// Returns the expression as an [`AnyNodeRef`].
|
|
pub(crate) fn as_any_node_ref<'ast>(
|
|
self,
|
|
db: &'db dyn Db,
|
|
module: &'ast ParsedModuleRef,
|
|
) -> AnyNodeRef<'ast> {
|
|
self.expression().node_ref(db, module).into()
|
|
}
|
|
|
|
pub(crate) const fn kind(self) -> UnpackKind {
|
|
self.kind
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, Hash, salsa::Update)]
|
|
pub(crate) enum UnpackKind {
|
|
/// An iterable expression like the one in a `for` loop or a comprehension.
|
|
Iterable,
|
|
/// An context manager expression like the one in a `with` statement.
|
|
ContextManager,
|
|
/// An expression that is being assigned to a target.
|
|
Assign,
|
|
}
|
|
|
|
/// The position of the target element in an unpacking.
|
|
#[derive(Clone, Copy, Debug, Hash, PartialEq, salsa::Update)]
|
|
pub(crate) enum UnpackPosition {
|
|
/// The target element is in the first position of the unpacking.
|
|
First,
|
|
/// The target element is in the position other than the first position of the unpacking.
|
|
Other,
|
|
}
|