[ty] Add infrastructure for AST garbage collection (#18445)

## Summary

https://github.com/astral-sh/ty/issues/214 will require a couple
invasive changes that I would like to get merged even before garbage
collection is fully implemented (to avoid rebasing):
- `ParsedModule` can no longer be dereferenced directly. Instead you
need to load a `ParsedModuleRef` to access the AST, which requires a
reference to the salsa database (as it may require re-parsing the AST if
it was collected).
- `AstNodeRef` can only be dereferenced with the `node` method, which
takes a reference to the `ParsedModuleRef`. This allows us to encode the
fact that ASTs do not live as long as the database and may be collected
as soon a given instance of a `ParsedModuleRef` is dropped. There are a
number of places where we currently merge the `'db` and `'ast`
lifetimes, so this requires giving some types/functions two separate
lifetime parameters.
This commit is contained in:
Ibraheem Ahmed 2025-06-05 11:43:18 -04:00 committed by GitHub
parent 55100209c7
commit 8531f4b3ca
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
33 changed files with 886 additions and 689 deletions

View file

@ -5,7 +5,7 @@ use except_handlers::TryNodeContextStackManager;
use rustc_hash::{FxHashMap, FxHashSet};
use ruff_db::files::File;
use ruff_db::parsed::ParsedModule;
use ruff_db::parsed::ParsedModuleRef;
use ruff_db::source::{SourceText, source_text};
use ruff_index::IndexVec;
use ruff_python_ast::name::Name;
@ -69,20 +69,20 @@ struct ScopeInfo {
current_loop: Option<Loop>,
}
pub(super) struct SemanticIndexBuilder<'db> {
pub(super) struct SemanticIndexBuilder<'db, 'ast> {
// Builder state
db: &'db dyn Db,
file: File,
source_type: PySourceType,
module: &'db ParsedModule,
module: &'ast ParsedModuleRef,
scope_stack: Vec<ScopeInfo>,
/// The assignments we're currently visiting, with
/// the most recent visit at the end of the Vec
current_assignments: Vec<CurrentAssignment<'db>>,
current_assignments: Vec<CurrentAssignment<'ast, 'db>>,
/// The match case we're currently visiting.
current_match_case: Option<CurrentMatchCase<'db>>,
current_match_case: Option<CurrentMatchCase<'ast>>,
/// The name of the first function parameter of the innermost function that we're currently visiting.
current_first_parameter_name: Option<&'db str>,
current_first_parameter_name: Option<&'ast str>,
/// Per-scope contexts regarding nested `try`/`except` statements
try_node_context_stack_manager: TryNodeContextStackManager,
@ -116,13 +116,13 @@ pub(super) struct SemanticIndexBuilder<'db> {
semantic_syntax_errors: RefCell<Vec<SemanticSyntaxError>>,
}
impl<'db> SemanticIndexBuilder<'db> {
pub(super) fn new(db: &'db dyn Db, file: File, parsed: &'db ParsedModule) -> Self {
impl<'db, 'ast> SemanticIndexBuilder<'db, 'ast> {
pub(super) fn new(db: &'db dyn Db, file: File, module_ref: &'ast ParsedModuleRef) -> Self {
let mut builder = Self {
db,
file,
source_type: file.source_type(db.upcast()),
module: parsed,
module: module_ref,
scope_stack: Vec::new(),
current_assignments: vec![],
current_match_case: None,
@ -423,7 +423,7 @@ impl<'db> SemanticIndexBuilder<'db> {
fn add_definition(
&mut self,
place: ScopedPlaceId,
definition_node: impl Into<DefinitionNodeRef<'db>> + std::fmt::Debug + Copy,
definition_node: impl Into<DefinitionNodeRef<'ast, 'db>> + std::fmt::Debug + Copy,
) -> Definition<'db> {
let (definition, num_definitions) = self.push_additional_definition(place, definition_node);
debug_assert_eq!(
@ -463,16 +463,18 @@ impl<'db> SemanticIndexBuilder<'db> {
fn push_additional_definition(
&mut self,
place: ScopedPlaceId,
definition_node: impl Into<DefinitionNodeRef<'db>>,
definition_node: impl Into<DefinitionNodeRef<'ast, 'db>>,
) -> (Definition<'db>, usize) {
let definition_node: DefinitionNodeRef<'_> = definition_node.into();
let definition_node: DefinitionNodeRef<'ast, 'db> = definition_node.into();
#[expect(unsafe_code)]
// SAFETY: `definition_node` is guaranteed to be a child of `self.module`
let kind = unsafe { definition_node.into_owned(self.module.clone()) };
let category = kind.category(self.source_type.is_stub());
let category = kind.category(self.source_type.is_stub(), self.module);
let is_reexported = kind.is_reexported();
let definition = Definition::new(
let definition: Definition<'db> = Definition::new(
self.db,
self.file,
self.current_scope(),
@ -658,7 +660,7 @@ impl<'db> SemanticIndexBuilder<'db> {
.record_reachability_constraint(negated_constraint);
}
fn push_assignment(&mut self, assignment: CurrentAssignment<'db>) {
fn push_assignment(&mut self, assignment: CurrentAssignment<'ast, 'db>) {
self.current_assignments.push(assignment);
}
@ -667,11 +669,11 @@ impl<'db> SemanticIndexBuilder<'db> {
debug_assert!(popped_assignment.is_some());
}
fn current_assignment(&self) -> Option<CurrentAssignment<'db>> {
fn current_assignment(&self) -> Option<CurrentAssignment<'ast, 'db>> {
self.current_assignments.last().copied()
}
fn current_assignment_mut(&mut self) -> Option<&mut CurrentAssignment<'db>> {
fn current_assignment_mut(&mut self) -> Option<&mut CurrentAssignment<'ast, 'db>> {
self.current_assignments.last_mut()
}
@ -792,7 +794,7 @@ impl<'db> SemanticIndexBuilder<'db> {
fn with_type_params(
&mut self,
with_scope: NodeWithScopeRef,
type_params: Option<&'db ast::TypeParams>,
type_params: Option<&'ast ast::TypeParams>,
nested: impl FnOnce(&mut Self) -> FileScopeId,
) -> FileScopeId {
if let Some(type_params) = type_params {
@ -858,7 +860,7 @@ impl<'db> SemanticIndexBuilder<'db> {
fn with_generators_scope(
&mut self,
scope: NodeWithScopeRef,
generators: &'db [ast::Comprehension],
generators: &'ast [ast::Comprehension],
visit_outer_elt: impl FnOnce(&mut Self),
) {
let mut generators_iter = generators.iter();
@ -908,7 +910,7 @@ impl<'db> SemanticIndexBuilder<'db> {
self.pop_scope();
}
fn declare_parameters(&mut self, parameters: &'db ast::Parameters) {
fn declare_parameters(&mut self, parameters: &'ast ast::Parameters) {
for parameter in parameters.iter_non_variadic_params() {
self.declare_parameter(parameter);
}
@ -925,7 +927,7 @@ impl<'db> SemanticIndexBuilder<'db> {
}
}
fn declare_parameter(&mut self, parameter: &'db ast::ParameterWithDefault) {
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);
@ -946,8 +948,8 @@ impl<'db> SemanticIndexBuilder<'db> {
/// for statements, etc.
fn add_unpackable_assignment(
&mut self,
unpackable: &Unpackable<'db>,
target: &'db ast::Expr,
unpackable: &Unpackable<'ast>,
target: &'ast ast::Expr,
value: Expression<'db>,
) {
// We only handle assignments to names and unpackings here, other targets like
@ -1010,8 +1012,7 @@ impl<'db> SemanticIndexBuilder<'db> {
}
pub(super) fn build(mut self) -> SemanticIndex<'db> {
let module = self.module;
self.visit_body(module.suite());
self.visit_body(self.module.suite());
// Pop the root scope
self.pop_scope();
@ -1081,10 +1082,7 @@ impl<'db> SemanticIndexBuilder<'db> {
}
}
impl<'db, 'ast> Visitor<'ast> for SemanticIndexBuilder<'db>
where
'ast: 'db,
{
impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
fn visit_stmt(&mut self, stmt: &'ast ast::Stmt) {
self.with_semantic_checker(|semantic, context| semantic.visit_stmt(stmt, context));
@ -2299,7 +2297,7 @@ where
}
}
impl SemanticSyntaxContext for SemanticIndexBuilder<'_> {
impl SemanticSyntaxContext for SemanticIndexBuilder<'_, '_> {
fn future_annotations_or_stub(&self) -> bool {
self.has_future_annotations
}
@ -2324,7 +2322,7 @@ impl SemanticSyntaxContext for SemanticIndexBuilder<'_> {
match scope.kind() {
ScopeKind::Class | ScopeKind::Lambda => return false,
ScopeKind::Function => {
return scope.node().expect_function().is_async;
return scope.node().expect_function(self.module).is_async;
}
ScopeKind::Comprehension
| ScopeKind::Module
@ -2366,9 +2364,9 @@ impl SemanticSyntaxContext for SemanticIndexBuilder<'_> {
for scope_info in self.scope_stack.iter().rev() {
let scope = &self.scopes[scope_info.file_scope_id];
let generators = match scope.node() {
NodeWithScopeKind::ListComprehension(node) => &node.generators,
NodeWithScopeKind::SetComprehension(node) => &node.generators,
NodeWithScopeKind::DictComprehension(node) => &node.generators,
NodeWithScopeKind::ListComprehension(node) => &node.node(self.module).generators,
NodeWithScopeKind::SetComprehension(node) => &node.node(self.module).generators,
NodeWithScopeKind::DictComprehension(node) => &node.node(self.module).generators,
_ => continue,
};
if generators
@ -2409,31 +2407,31 @@ impl SemanticSyntaxContext for SemanticIndexBuilder<'_> {
}
#[derive(Copy, Clone, Debug, PartialEq)]
enum CurrentAssignment<'a> {
enum CurrentAssignment<'ast, 'db> {
Assign {
node: &'a ast::StmtAssign,
unpack: Option<(UnpackPosition, Unpack<'a>)>,
node: &'ast ast::StmtAssign,
unpack: Option<(UnpackPosition, Unpack<'db>)>,
},
AnnAssign(&'a ast::StmtAnnAssign),
AugAssign(&'a ast::StmtAugAssign),
AnnAssign(&'ast ast::StmtAnnAssign),
AugAssign(&'ast ast::StmtAugAssign),
For {
node: &'a ast::StmtFor,
unpack: Option<(UnpackPosition, Unpack<'a>)>,
node: &'ast ast::StmtFor,
unpack: Option<(UnpackPosition, Unpack<'db>)>,
},
Named(&'a ast::ExprNamed),
Named(&'ast ast::ExprNamed),
Comprehension {
node: &'a ast::Comprehension,
node: &'ast ast::Comprehension,
first: bool,
unpack: Option<(UnpackPosition, Unpack<'a>)>,
unpack: Option<(UnpackPosition, Unpack<'db>)>,
},
WithItem {
item: &'a ast::WithItem,
item: &'ast ast::WithItem,
is_async: bool,
unpack: Option<(UnpackPosition, Unpack<'a>)>,
unpack: Option<(UnpackPosition, Unpack<'db>)>,
},
}
impl CurrentAssignment<'_> {
impl CurrentAssignment<'_, '_> {
fn unpack_position_mut(&mut self) -> Option<&mut UnpackPosition> {
match self {
Self::Assign { unpack, .. }
@ -2445,28 +2443,28 @@ impl CurrentAssignment<'_> {
}
}
impl<'a> From<&'a ast::StmtAnnAssign> for CurrentAssignment<'a> {
fn from(value: &'a ast::StmtAnnAssign) -> Self {
impl<'ast> From<&'ast ast::StmtAnnAssign> for CurrentAssignment<'ast, '_> {
fn from(value: &'ast ast::StmtAnnAssign) -> Self {
Self::AnnAssign(value)
}
}
impl<'a> From<&'a ast::StmtAugAssign> for CurrentAssignment<'a> {
fn from(value: &'a ast::StmtAugAssign) -> Self {
impl<'ast> From<&'ast ast::StmtAugAssign> for CurrentAssignment<'ast, '_> {
fn from(value: &'ast ast::StmtAugAssign) -> Self {
Self::AugAssign(value)
}
}
impl<'a> From<&'a ast::ExprNamed> for CurrentAssignment<'a> {
fn from(value: &'a ast::ExprNamed) -> Self {
impl<'ast> From<&'ast ast::ExprNamed> for CurrentAssignment<'ast, '_> {
fn from(value: &'ast ast::ExprNamed) -> Self {
Self::Named(value)
}
}
#[derive(Debug, PartialEq)]
struct CurrentMatchCase<'a> {
struct CurrentMatchCase<'ast> {
/// The pattern that's part of the current match case.
pattern: &'a ast::Pattern,
pattern: &'ast ast::Pattern,
/// The index of the sub-pattern that's being currently visited within the pattern.
///
@ -2488,20 +2486,20 @@ impl<'a> CurrentMatchCase<'a> {
}
}
enum Unpackable<'a> {
Assign(&'a ast::StmtAssign),
For(&'a ast::StmtFor),
enum Unpackable<'ast> {
Assign(&'ast ast::StmtAssign),
For(&'ast ast::StmtFor),
WithItem {
item: &'a ast::WithItem,
item: &'ast ast::WithItem,
is_async: bool,
},
Comprehension {
first: bool,
node: &'a ast::Comprehension,
node: &'ast ast::Comprehension,
},
}
impl<'a> Unpackable<'a> {
impl<'ast> Unpackable<'ast> {
const fn kind(&self) -> UnpackKind {
match self {
Unpackable::Assign(_) => UnpackKind::Assign,
@ -2510,7 +2508,10 @@ impl<'a> Unpackable<'a> {
}
}
fn as_current_assignment(&self, unpack: Option<Unpack<'a>>) -> CurrentAssignment<'a> {
fn as_current_assignment<'db>(
&self,
unpack: Option<Unpack<'db>>,
) -> CurrentAssignment<'ast, 'db> {
let unpack = unpack.map(|unpack| (UnpackPosition::First, unpack));
match self {
Unpackable::Assign(stmt) => CurrentAssignment::Assign { node: stmt, unpack },

View file

@ -1,7 +1,7 @@
use std::ops::Deref;
use ruff_db::files::{File, FileRange};
use ruff_db::parsed::ParsedModule;
use ruff_db::parsed::ParsedModuleRef;
use ruff_python_ast as ast;
use ruff_text_size::{Ranged, TextRange};
@ -49,12 +49,12 @@ impl<'db> Definition<'db> {
self.file_scope(db).to_scope_id(db, self.file(db))
}
pub fn full_range(self, db: &'db dyn Db) -> FileRange {
FileRange::new(self.file(db), self.kind(db).full_range())
pub fn full_range(self, db: &'db dyn Db, module: &ParsedModuleRef) -> FileRange {
FileRange::new(self.file(db), self.kind(db).full_range(module))
}
pub fn focus_range(self, db: &'db dyn Db) -> FileRange {
FileRange::new(self.file(db), self.kind(db).target_range())
pub fn focus_range(self, db: &'db dyn Db, module: &ParsedModuleRef) -> FileRange {
FileRange::new(self.file(db), self.kind(db).target_range(module))
}
}
@ -123,218 +123,218 @@ impl<'db> DefinitionState<'db> {
}
#[derive(Copy, Clone, Debug)]
pub(crate) enum DefinitionNodeRef<'a> {
Import(ImportDefinitionNodeRef<'a>),
ImportFrom(ImportFromDefinitionNodeRef<'a>),
ImportStar(StarImportDefinitionNodeRef<'a>),
For(ForStmtDefinitionNodeRef<'a>),
Function(&'a ast::StmtFunctionDef),
Class(&'a ast::StmtClassDef),
TypeAlias(&'a ast::StmtTypeAlias),
NamedExpression(&'a ast::ExprNamed),
Assignment(AssignmentDefinitionNodeRef<'a>),
AnnotatedAssignment(AnnotatedAssignmentDefinitionNodeRef<'a>),
AugmentedAssignment(&'a ast::StmtAugAssign),
Comprehension(ComprehensionDefinitionNodeRef<'a>),
VariadicPositionalParameter(&'a ast::Parameter),
VariadicKeywordParameter(&'a ast::Parameter),
Parameter(&'a ast::ParameterWithDefault),
WithItem(WithItemDefinitionNodeRef<'a>),
MatchPattern(MatchPatternDefinitionNodeRef<'a>),
ExceptHandler(ExceptHandlerDefinitionNodeRef<'a>),
TypeVar(&'a ast::TypeParamTypeVar),
ParamSpec(&'a ast::TypeParamParamSpec),
TypeVarTuple(&'a ast::TypeParamTypeVarTuple),
pub(crate) enum DefinitionNodeRef<'ast, 'db> {
Import(ImportDefinitionNodeRef<'ast>),
ImportFrom(ImportFromDefinitionNodeRef<'ast>),
ImportStar(StarImportDefinitionNodeRef<'ast>),
For(ForStmtDefinitionNodeRef<'ast, 'db>),
Function(&'ast ast::StmtFunctionDef),
Class(&'ast ast::StmtClassDef),
TypeAlias(&'ast ast::StmtTypeAlias),
NamedExpression(&'ast ast::ExprNamed),
Assignment(AssignmentDefinitionNodeRef<'ast, 'db>),
AnnotatedAssignment(AnnotatedAssignmentDefinitionNodeRef<'ast>),
AugmentedAssignment(&'ast ast::StmtAugAssign),
Comprehension(ComprehensionDefinitionNodeRef<'ast, 'db>),
VariadicPositionalParameter(&'ast ast::Parameter),
VariadicKeywordParameter(&'ast ast::Parameter),
Parameter(&'ast ast::ParameterWithDefault),
WithItem(WithItemDefinitionNodeRef<'ast, 'db>),
MatchPattern(MatchPatternDefinitionNodeRef<'ast>),
ExceptHandler(ExceptHandlerDefinitionNodeRef<'ast>),
TypeVar(&'ast ast::TypeParamTypeVar),
ParamSpec(&'ast ast::TypeParamParamSpec),
TypeVarTuple(&'ast ast::TypeParamTypeVarTuple),
}
impl<'a> From<&'a ast::StmtFunctionDef> for DefinitionNodeRef<'a> {
fn from(node: &'a ast::StmtFunctionDef) -> Self {
impl<'ast> From<&'ast ast::StmtFunctionDef> for DefinitionNodeRef<'ast, '_> {
fn from(node: &'ast ast::StmtFunctionDef) -> Self {
Self::Function(node)
}
}
impl<'a> From<&'a ast::StmtClassDef> for DefinitionNodeRef<'a> {
fn from(node: &'a ast::StmtClassDef) -> Self {
impl<'ast> From<&'ast ast::StmtClassDef> for DefinitionNodeRef<'ast, '_> {
fn from(node: &'ast ast::StmtClassDef) -> Self {
Self::Class(node)
}
}
impl<'a> From<&'a ast::StmtTypeAlias> for DefinitionNodeRef<'a> {
fn from(node: &'a ast::StmtTypeAlias) -> Self {
impl<'ast> From<&'ast ast::StmtTypeAlias> for DefinitionNodeRef<'ast, '_> {
fn from(node: &'ast ast::StmtTypeAlias) -> Self {
Self::TypeAlias(node)
}
}
impl<'a> From<&'a ast::ExprNamed> for DefinitionNodeRef<'a> {
fn from(node: &'a ast::ExprNamed) -> Self {
impl<'ast> From<&'ast ast::ExprNamed> for DefinitionNodeRef<'ast, '_> {
fn from(node: &'ast ast::ExprNamed) -> Self {
Self::NamedExpression(node)
}
}
impl<'a> From<&'a ast::StmtAugAssign> for DefinitionNodeRef<'a> {
fn from(node: &'a ast::StmtAugAssign) -> Self {
impl<'ast> From<&'ast ast::StmtAugAssign> for DefinitionNodeRef<'ast, '_> {
fn from(node: &'ast ast::StmtAugAssign) -> Self {
Self::AugmentedAssignment(node)
}
}
impl<'a> From<&'a ast::TypeParamTypeVar> for DefinitionNodeRef<'a> {
fn from(value: &'a ast::TypeParamTypeVar) -> Self {
impl<'ast> From<&'ast ast::TypeParamTypeVar> for DefinitionNodeRef<'ast, '_> {
fn from(value: &'ast ast::TypeParamTypeVar) -> Self {
Self::TypeVar(value)
}
}
impl<'a> From<&'a ast::TypeParamParamSpec> for DefinitionNodeRef<'a> {
fn from(value: &'a ast::TypeParamParamSpec) -> Self {
impl<'ast> From<&'ast ast::TypeParamParamSpec> for DefinitionNodeRef<'ast, '_> {
fn from(value: &'ast ast::TypeParamParamSpec) -> Self {
Self::ParamSpec(value)
}
}
impl<'a> From<&'a ast::TypeParamTypeVarTuple> for DefinitionNodeRef<'a> {
fn from(value: &'a ast::TypeParamTypeVarTuple) -> Self {
impl<'ast> From<&'ast ast::TypeParamTypeVarTuple> for DefinitionNodeRef<'ast, '_> {
fn from(value: &'ast ast::TypeParamTypeVarTuple) -> Self {
Self::TypeVarTuple(value)
}
}
impl<'a> From<ImportDefinitionNodeRef<'a>> for DefinitionNodeRef<'a> {
fn from(node_ref: ImportDefinitionNodeRef<'a>) -> Self {
impl<'ast> From<ImportDefinitionNodeRef<'ast>> for DefinitionNodeRef<'ast, '_> {
fn from(node_ref: ImportDefinitionNodeRef<'ast>) -> Self {
Self::Import(node_ref)
}
}
impl<'a> From<ImportFromDefinitionNodeRef<'a>> for DefinitionNodeRef<'a> {
fn from(node_ref: ImportFromDefinitionNodeRef<'a>) -> Self {
impl<'ast> From<ImportFromDefinitionNodeRef<'ast>> for DefinitionNodeRef<'ast, '_> {
fn from(node_ref: ImportFromDefinitionNodeRef<'ast>) -> Self {
Self::ImportFrom(node_ref)
}
}
impl<'a> From<ForStmtDefinitionNodeRef<'a>> for DefinitionNodeRef<'a> {
fn from(value: ForStmtDefinitionNodeRef<'a>) -> Self {
impl<'ast, 'db> From<ForStmtDefinitionNodeRef<'ast, 'db>> for DefinitionNodeRef<'ast, 'db> {
fn from(value: ForStmtDefinitionNodeRef<'ast, 'db>) -> Self {
Self::For(value)
}
}
impl<'a> From<AssignmentDefinitionNodeRef<'a>> for DefinitionNodeRef<'a> {
fn from(node_ref: AssignmentDefinitionNodeRef<'a>) -> Self {
impl<'ast, 'db> From<AssignmentDefinitionNodeRef<'ast, 'db>> for DefinitionNodeRef<'ast, 'db> {
fn from(node_ref: AssignmentDefinitionNodeRef<'ast, 'db>) -> Self {
Self::Assignment(node_ref)
}
}
impl<'a> From<AnnotatedAssignmentDefinitionNodeRef<'a>> for DefinitionNodeRef<'a> {
fn from(node_ref: AnnotatedAssignmentDefinitionNodeRef<'a>) -> Self {
impl<'ast> From<AnnotatedAssignmentDefinitionNodeRef<'ast>> for DefinitionNodeRef<'ast, '_> {
fn from(node_ref: AnnotatedAssignmentDefinitionNodeRef<'ast>) -> Self {
Self::AnnotatedAssignment(node_ref)
}
}
impl<'a> From<WithItemDefinitionNodeRef<'a>> for DefinitionNodeRef<'a> {
fn from(node_ref: WithItemDefinitionNodeRef<'a>) -> Self {
impl<'ast, 'db> From<WithItemDefinitionNodeRef<'ast, 'db>> for DefinitionNodeRef<'ast, 'db> {
fn from(node_ref: WithItemDefinitionNodeRef<'ast, 'db>) -> Self {
Self::WithItem(node_ref)
}
}
impl<'a> From<ComprehensionDefinitionNodeRef<'a>> for DefinitionNodeRef<'a> {
fn from(node: ComprehensionDefinitionNodeRef<'a>) -> Self {
impl<'ast, 'db> From<ComprehensionDefinitionNodeRef<'ast, 'db>> for DefinitionNodeRef<'ast, 'db> {
fn from(node: ComprehensionDefinitionNodeRef<'ast, 'db>) -> Self {
Self::Comprehension(node)
}
}
impl<'a> From<&'a ast::ParameterWithDefault> for DefinitionNodeRef<'a> {
fn from(node: &'a ast::ParameterWithDefault) -> Self {
impl<'ast> From<&'ast ast::ParameterWithDefault> for DefinitionNodeRef<'ast, '_> {
fn from(node: &'ast ast::ParameterWithDefault) -> Self {
Self::Parameter(node)
}
}
impl<'a> From<MatchPatternDefinitionNodeRef<'a>> for DefinitionNodeRef<'a> {
fn from(node: MatchPatternDefinitionNodeRef<'a>) -> Self {
impl<'ast> From<MatchPatternDefinitionNodeRef<'ast>> for DefinitionNodeRef<'ast, '_> {
fn from(node: MatchPatternDefinitionNodeRef<'ast>) -> Self {
Self::MatchPattern(node)
}
}
impl<'a> From<StarImportDefinitionNodeRef<'a>> for DefinitionNodeRef<'a> {
fn from(node: StarImportDefinitionNodeRef<'a>) -> Self {
impl<'ast> From<StarImportDefinitionNodeRef<'ast>> for DefinitionNodeRef<'ast, '_> {
fn from(node: StarImportDefinitionNodeRef<'ast>) -> Self {
Self::ImportStar(node)
}
}
#[derive(Copy, Clone, Debug)]
pub(crate) struct ImportDefinitionNodeRef<'a> {
pub(crate) node: &'a ast::StmtImport,
pub(crate) struct ImportDefinitionNodeRef<'ast> {
pub(crate) node: &'ast ast::StmtImport,
pub(crate) alias_index: usize,
pub(crate) is_reexported: bool,
}
#[derive(Copy, Clone, Debug)]
pub(crate) struct StarImportDefinitionNodeRef<'a> {
pub(crate) node: &'a ast::StmtImportFrom,
pub(crate) struct StarImportDefinitionNodeRef<'ast> {
pub(crate) node: &'ast ast::StmtImportFrom,
pub(crate) place_id: ScopedPlaceId,
}
#[derive(Copy, Clone, Debug)]
pub(crate) struct ImportFromDefinitionNodeRef<'a> {
pub(crate) node: &'a ast::StmtImportFrom,
pub(crate) struct ImportFromDefinitionNodeRef<'ast> {
pub(crate) node: &'ast ast::StmtImportFrom,
pub(crate) alias_index: usize,
pub(crate) is_reexported: bool,
}
#[derive(Copy, Clone, Debug)]
pub(crate) struct AssignmentDefinitionNodeRef<'a> {
pub(crate) unpack: Option<(UnpackPosition, Unpack<'a>)>,
pub(crate) value: &'a ast::Expr,
pub(crate) target: &'a ast::Expr,
pub(crate) struct AssignmentDefinitionNodeRef<'ast, 'db> {
pub(crate) unpack: Option<(UnpackPosition, Unpack<'db>)>,
pub(crate) value: &'ast ast::Expr,
pub(crate) target: &'ast ast::Expr,
}
#[derive(Copy, Clone, Debug)]
pub(crate) struct AnnotatedAssignmentDefinitionNodeRef<'a> {
pub(crate) node: &'a ast::StmtAnnAssign,
pub(crate) annotation: &'a ast::Expr,
pub(crate) value: Option<&'a ast::Expr>,
pub(crate) target: &'a ast::Expr,
pub(crate) struct AnnotatedAssignmentDefinitionNodeRef<'ast> {
pub(crate) node: &'ast ast::StmtAnnAssign,
pub(crate) annotation: &'ast ast::Expr,
pub(crate) value: Option<&'ast ast::Expr>,
pub(crate) target: &'ast ast::Expr,
}
#[derive(Copy, Clone, Debug)]
pub(crate) struct WithItemDefinitionNodeRef<'a> {
pub(crate) unpack: Option<(UnpackPosition, Unpack<'a>)>,
pub(crate) context_expr: &'a ast::Expr,
pub(crate) target: &'a ast::Expr,
pub(crate) struct WithItemDefinitionNodeRef<'ast, 'db> {
pub(crate) unpack: Option<(UnpackPosition, Unpack<'db>)>,
pub(crate) context_expr: &'ast ast::Expr,
pub(crate) target: &'ast ast::Expr,
pub(crate) is_async: bool,
}
#[derive(Copy, Clone, Debug)]
pub(crate) struct ForStmtDefinitionNodeRef<'a> {
pub(crate) unpack: Option<(UnpackPosition, Unpack<'a>)>,
pub(crate) iterable: &'a ast::Expr,
pub(crate) target: &'a ast::Expr,
pub(crate) struct ForStmtDefinitionNodeRef<'ast, 'db> {
pub(crate) unpack: Option<(UnpackPosition, Unpack<'db>)>,
pub(crate) iterable: &'ast ast::Expr,
pub(crate) target: &'ast ast::Expr,
pub(crate) is_async: bool,
}
#[derive(Copy, Clone, Debug)]
pub(crate) struct ExceptHandlerDefinitionNodeRef<'a> {
pub(crate) handler: &'a ast::ExceptHandlerExceptHandler,
pub(crate) struct ExceptHandlerDefinitionNodeRef<'ast> {
pub(crate) handler: &'ast ast::ExceptHandlerExceptHandler,
pub(crate) is_star: bool,
}
#[derive(Copy, Clone, Debug)]
pub(crate) struct ComprehensionDefinitionNodeRef<'a> {
pub(crate) unpack: Option<(UnpackPosition, Unpack<'a>)>,
pub(crate) iterable: &'a ast::Expr,
pub(crate) target: &'a ast::Expr,
pub(crate) struct ComprehensionDefinitionNodeRef<'ast, 'db> {
pub(crate) unpack: Option<(UnpackPosition, Unpack<'db>)>,
pub(crate) iterable: &'ast ast::Expr,
pub(crate) target: &'ast ast::Expr,
pub(crate) first: bool,
pub(crate) is_async: bool,
}
#[derive(Copy, Clone, Debug)]
pub(crate) struct MatchPatternDefinitionNodeRef<'a> {
pub(crate) struct MatchPatternDefinitionNodeRef<'ast> {
/// The outermost pattern node in which the identifier being defined occurs.
pub(crate) pattern: &'a ast::Pattern,
pub(crate) pattern: &'ast ast::Pattern,
/// The identifier being defined.
pub(crate) identifier: &'a ast::Identifier,
pub(crate) identifier: &'ast ast::Identifier,
/// The index of the identifier in the pattern when visiting the `pattern` node in evaluation
/// order.
pub(crate) index: u32,
}
impl<'db> DefinitionNodeRef<'db> {
impl<'db> DefinitionNodeRef<'_, 'db> {
#[expect(unsafe_code)]
pub(super) unsafe fn into_owned(self, parsed: ParsedModule) -> DefinitionKind<'db> {
pub(super) unsafe fn into_owned(self, parsed: ParsedModuleRef) -> DefinitionKind<'db> {
match self {
DefinitionNodeRef::Import(ImportDefinitionNodeRef {
node,
@ -626,60 +626,74 @@ impl DefinitionKind<'_> {
///
/// A definition target would mainly be the node representing the place being defined i.e.,
/// [`ast::ExprName`], [`ast::Identifier`], [`ast::ExprAttribute`] or [`ast::ExprSubscript`] but could also be other nodes.
pub(crate) fn target_range(&self) -> TextRange {
pub(crate) fn target_range(&self, module: &ParsedModuleRef) -> TextRange {
match self {
DefinitionKind::Import(import) => import.alias().range(),
DefinitionKind::ImportFrom(import) => import.alias().range(),
DefinitionKind::StarImport(import) => import.alias().range(),
DefinitionKind::Function(function) => function.name.range(),
DefinitionKind::Class(class) => class.name.range(),
DefinitionKind::TypeAlias(type_alias) => type_alias.name.range(),
DefinitionKind::NamedExpression(named) => named.target.range(),
DefinitionKind::Assignment(assignment) => assignment.target.range(),
DefinitionKind::AnnotatedAssignment(assign) => assign.target.range(),
DefinitionKind::AugmentedAssignment(aug_assign) => aug_assign.target.range(),
DefinitionKind::For(for_stmt) => for_stmt.target.range(),
DefinitionKind::Comprehension(comp) => comp.target().range(),
DefinitionKind::VariadicPositionalParameter(parameter) => parameter.name.range(),
DefinitionKind::VariadicKeywordParameter(parameter) => parameter.name.range(),
DefinitionKind::Parameter(parameter) => parameter.parameter.name.range(),
DefinitionKind::WithItem(with_item) => with_item.target.range(),
DefinitionKind::MatchPattern(match_pattern) => match_pattern.identifier.range(),
DefinitionKind::ExceptHandler(handler) => handler.node().range(),
DefinitionKind::TypeVar(type_var) => type_var.name.range(),
DefinitionKind::ParamSpec(param_spec) => param_spec.name.range(),
DefinitionKind::TypeVarTuple(type_var_tuple) => type_var_tuple.name.range(),
DefinitionKind::Import(import) => import.alias(module).range(),
DefinitionKind::ImportFrom(import) => import.alias(module).range(),
DefinitionKind::StarImport(import) => import.alias(module).range(),
DefinitionKind::Function(function) => function.node(module).name.range(),
DefinitionKind::Class(class) => class.node(module).name.range(),
DefinitionKind::TypeAlias(type_alias) => type_alias.node(module).name.range(),
DefinitionKind::NamedExpression(named) => named.node(module).target.range(),
DefinitionKind::Assignment(assignment) => assignment.target.node(module).range(),
DefinitionKind::AnnotatedAssignment(assign) => assign.target.node(module).range(),
DefinitionKind::AugmentedAssignment(aug_assign) => {
aug_assign.node(module).target.range()
}
DefinitionKind::For(for_stmt) => for_stmt.target.node(module).range(),
DefinitionKind::Comprehension(comp) => comp.target(module).range(),
DefinitionKind::VariadicPositionalParameter(parameter) => {
parameter.node(module).name.range()
}
DefinitionKind::VariadicKeywordParameter(parameter) => {
parameter.node(module).name.range()
}
DefinitionKind::Parameter(parameter) => parameter.node(module).parameter.name.range(),
DefinitionKind::WithItem(with_item) => with_item.target.node(module).range(),
DefinitionKind::MatchPattern(match_pattern) => {
match_pattern.identifier.node(module).range()
}
DefinitionKind::ExceptHandler(handler) => handler.node(module).range(),
DefinitionKind::TypeVar(type_var) => type_var.node(module).name.range(),
DefinitionKind::ParamSpec(param_spec) => param_spec.node(module).name.range(),
DefinitionKind::TypeVarTuple(type_var_tuple) => {
type_var_tuple.node(module).name.range()
}
}
}
/// Returns the [`TextRange`] of the entire definition.
pub(crate) fn full_range(&self) -> TextRange {
pub(crate) fn full_range(&self, module: &ParsedModuleRef) -> TextRange {
match self {
DefinitionKind::Import(import) => import.alias().range(),
DefinitionKind::ImportFrom(import) => import.alias().range(),
DefinitionKind::StarImport(import) => import.import().range(),
DefinitionKind::Function(function) => function.range(),
DefinitionKind::Class(class) => class.range(),
DefinitionKind::TypeAlias(type_alias) => type_alias.range(),
DefinitionKind::NamedExpression(named) => named.range(),
DefinitionKind::Assignment(assignment) => assignment.target.range(),
DefinitionKind::AnnotatedAssignment(assign) => assign.target.range(),
DefinitionKind::AugmentedAssignment(aug_assign) => aug_assign.range(),
DefinitionKind::For(for_stmt) => for_stmt.target.range(),
DefinitionKind::Comprehension(comp) => comp.target().range(),
DefinitionKind::VariadicPositionalParameter(parameter) => parameter.range(),
DefinitionKind::VariadicKeywordParameter(parameter) => parameter.range(),
DefinitionKind::Parameter(parameter) => parameter.parameter.range(),
DefinitionKind::WithItem(with_item) => with_item.target.range(),
DefinitionKind::MatchPattern(match_pattern) => match_pattern.identifier.range(),
DefinitionKind::ExceptHandler(handler) => handler.node().range(),
DefinitionKind::TypeVar(type_var) => type_var.range(),
DefinitionKind::ParamSpec(param_spec) => param_spec.range(),
DefinitionKind::TypeVarTuple(type_var_tuple) => type_var_tuple.range(),
DefinitionKind::Import(import) => import.alias(module).range(),
DefinitionKind::ImportFrom(import) => import.alias(module).range(),
DefinitionKind::StarImport(import) => import.import(module).range(),
DefinitionKind::Function(function) => function.node(module).range(),
DefinitionKind::Class(class) => class.node(module).range(),
DefinitionKind::TypeAlias(type_alias) => type_alias.node(module).range(),
DefinitionKind::NamedExpression(named) => named.node(module).range(),
DefinitionKind::Assignment(assignment) => assignment.target.node(module).range(),
DefinitionKind::AnnotatedAssignment(assign) => assign.target.node(module).range(),
DefinitionKind::AugmentedAssignment(aug_assign) => aug_assign.node(module).range(),
DefinitionKind::For(for_stmt) => for_stmt.target.node(module).range(),
DefinitionKind::Comprehension(comp) => comp.target(module).range(),
DefinitionKind::VariadicPositionalParameter(parameter) => {
parameter.node(module).range()
}
DefinitionKind::VariadicKeywordParameter(parameter) => parameter.node(module).range(),
DefinitionKind::Parameter(parameter) => parameter.node(module).parameter.range(),
DefinitionKind::WithItem(with_item) => with_item.target.node(module).range(),
DefinitionKind::MatchPattern(match_pattern) => {
match_pattern.identifier.node(module).range()
}
DefinitionKind::ExceptHandler(handler) => handler.node(module).range(),
DefinitionKind::TypeVar(type_var) => type_var.node(module).range(),
DefinitionKind::ParamSpec(param_spec) => param_spec.node(module).range(),
DefinitionKind::TypeVarTuple(type_var_tuple) => type_var_tuple.node(module).range(),
}
}
pub(crate) fn category(&self, in_stub: bool) -> DefinitionCategory {
pub(crate) fn category(&self, in_stub: bool, module: &ParsedModuleRef) -> DefinitionCategory {
match self {
// functions, classes, and imports always bind, and we consider them declarations
DefinitionKind::Function(_)
@ -694,7 +708,7 @@ impl DefinitionKind<'_> {
// a parameter always binds a value, but is only a declaration if annotated
DefinitionKind::VariadicPositionalParameter(parameter)
| DefinitionKind::VariadicKeywordParameter(parameter) => {
if parameter.annotation.is_some() {
if parameter.node(module).annotation.is_some() {
DefinitionCategory::DeclarationAndBinding
} else {
DefinitionCategory::Binding
@ -702,7 +716,12 @@ impl DefinitionKind<'_> {
}
// presence of a default is irrelevant, same logic as for a no-default parameter
DefinitionKind::Parameter(parameter_with_default) => {
if parameter_with_default.parameter.annotation.is_some() {
if parameter_with_default
.node(module)
.parameter
.annotation
.is_some()
{
DefinitionCategory::DeclarationAndBinding
} else {
DefinitionCategory::Binding
@ -753,15 +772,15 @@ pub struct StarImportDefinitionKind {
}
impl StarImportDefinitionKind {
pub(crate) fn import(&self) -> &ast::StmtImportFrom {
self.node.node()
pub(crate) fn import<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::StmtImportFrom {
self.node.node(module)
}
pub(crate) fn alias(&self) -> &ast::Alias {
pub(crate) fn alias<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Alias {
// 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.
self.node
.node()
.node(module)
.names
.iter()
.find(|alias| &alias.name == "*")
@ -784,8 +803,8 @@ pub struct MatchPatternDefinitionKind {
}
impl MatchPatternDefinitionKind {
pub(crate) fn pattern(&self) -> &ast::Pattern {
self.pattern.node()
pub(crate) fn pattern<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Pattern {
self.pattern.node(module)
}
pub(crate) fn index(&self) -> u32 {
@ -808,16 +827,16 @@ pub struct ComprehensionDefinitionKind<'db> {
}
impl<'db> ComprehensionDefinitionKind<'db> {
pub(crate) fn iterable(&self) -> &ast::Expr {
self.iterable.node()
pub(crate) fn iterable<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Expr {
self.iterable.node(module)
}
pub(crate) fn target_kind(&self) -> TargetKind<'db> {
self.target_kind
}
pub(crate) fn target(&self) -> &ast::Expr {
self.target.node()
pub(crate) fn target<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Expr {
self.target.node(module)
}
pub(crate) fn is_first(&self) -> bool {
@ -837,12 +856,12 @@ pub struct ImportDefinitionKind {
}
impl ImportDefinitionKind {
pub(crate) fn import(&self) -> &ast::StmtImport {
self.node.node()
pub(crate) fn import<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::StmtImport {
self.node.node(module)
}
pub(crate) fn alias(&self) -> &ast::Alias {
&self.node.node().names[self.alias_index]
pub(crate) fn alias<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Alias {
&self.node.node(module).names[self.alias_index]
}
pub(crate) fn is_reexported(&self) -> bool {
@ -858,12 +877,12 @@ pub struct ImportFromDefinitionKind {
}
impl ImportFromDefinitionKind {
pub(crate) fn import(&self) -> &ast::StmtImportFrom {
self.node.node()
pub(crate) fn import<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::StmtImportFrom {
self.node.node(module)
}
pub(crate) fn alias(&self) -> &ast::Alias {
&self.node.node().names[self.alias_index]
pub(crate) fn alias<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Alias {
&self.node.node(module).names[self.alias_index]
}
pub(crate) fn is_reexported(&self) -> bool {
@ -883,12 +902,12 @@ impl<'db> AssignmentDefinitionKind<'db> {
self.target_kind
}
pub(crate) fn value(&self) -> &ast::Expr {
self.value.node()
pub(crate) fn value<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Expr {
self.value.node(module)
}
pub(crate) fn target(&self) -> &ast::Expr {
self.target.node()
pub(crate) fn target<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Expr {
self.target.node(module)
}
}
@ -900,16 +919,16 @@ pub struct AnnotatedAssignmentDefinitionKind {
}
impl AnnotatedAssignmentDefinitionKind {
pub(crate) fn value(&self) -> Option<&ast::Expr> {
self.value.as_deref()
pub(crate) fn value<'ast>(&self, module: &'ast ParsedModuleRef) -> Option<&'ast ast::Expr> {
self.value.as_ref().map(|value| value.node(module))
}
pub(crate) fn annotation(&self) -> &ast::Expr {
self.annotation.node()
pub(crate) fn annotation<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Expr {
self.annotation.node(module)
}
pub(crate) fn target(&self) -> &ast::Expr {
self.target.node()
pub(crate) fn target<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Expr {
self.target.node(module)
}
}
@ -922,16 +941,16 @@ pub struct WithItemDefinitionKind<'db> {
}
impl<'db> WithItemDefinitionKind<'db> {
pub(crate) fn context_expr(&self) -> &ast::Expr {
self.context_expr.node()
pub(crate) fn context_expr<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Expr {
self.context_expr.node(module)
}
pub(crate) fn target_kind(&self) -> TargetKind<'db> {
self.target_kind
}
pub(crate) fn target(&self) -> &ast::Expr {
self.target.node()
pub(crate) fn target<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Expr {
self.target.node(module)
}
pub(crate) const fn is_async(&self) -> bool {
@ -948,16 +967,16 @@ pub struct ForStmtDefinitionKind<'db> {
}
impl<'db> ForStmtDefinitionKind<'db> {
pub(crate) fn iterable(&self) -> &ast::Expr {
self.iterable.node()
pub(crate) fn iterable<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Expr {
self.iterable.node(module)
}
pub(crate) fn target_kind(&self) -> TargetKind<'db> {
self.target_kind
}
pub(crate) fn target(&self) -> &ast::Expr {
self.target.node()
pub(crate) fn target<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Expr {
self.target.node(module)
}
pub(crate) const fn is_async(&self) -> bool {
@ -972,12 +991,18 @@ pub struct ExceptHandlerDefinitionKind {
}
impl ExceptHandlerDefinitionKind {
pub(crate) fn node(&self) -> &ast::ExceptHandlerExceptHandler {
self.handler.node()
pub(crate) fn node<'ast>(
&self,
module: &'ast ParsedModuleRef,
) -> &'ast ast::ExceptHandlerExceptHandler {
self.handler.node(module)
}
pub(crate) fn handled_exceptions(&self) -> Option<&ast::Expr> {
self.node().type_.as_deref()
pub(crate) fn handled_exceptions<'ast>(
&self,
module: &'ast ParsedModuleRef,
) -> Option<&'ast ast::Expr> {
self.node(module).type_.as_deref()
}
pub(crate) fn is_star(&self) -> bool {

View file

@ -2,6 +2,7 @@ use crate::ast_node_ref::AstNodeRef;
use crate::db::Db;
use crate::semantic_index::place::{FileScopeId, ScopeId};
use ruff_db::files::File;
use ruff_db::parsed::ParsedModuleRef;
use ruff_python_ast as ast;
use salsa;
@ -41,8 +42,8 @@ pub(crate) struct Expression<'db> {
/// The expression node.
#[no_eq]
#[tracked]
#[returns(deref)]
pub(crate) node_ref: AstNodeRef<ast::Expr>,
#[returns(ref)]
pub(crate) _node_ref: AstNodeRef<ast::Expr>,
/// An assignment statement, if this expression is immediately used as the rhs of that
/// assignment.
@ -62,6 +63,14 @@ pub(crate) struct Expression<'db> {
}
impl<'db> Expression<'db> {
pub(crate) fn node_ref<'ast>(
self,
db: &'db dyn Db,
parsed: &'ast ParsedModuleRef,
) -> &'ast ast::Expr {
self._node_ref(db).node(parsed)
}
pub(crate) fn scope(self, db: &'db dyn Db) -> ScopeId<'db> {
self.file_scope(db).to_scope_id(db, self.file(db))
}

View file

@ -5,7 +5,7 @@ use std::ops::Range;
use bitflags::bitflags;
use hashbrown::hash_map::RawEntryMut;
use ruff_db::files::File;
use ruff_db::parsed::ParsedModule;
use ruff_db::parsed::ParsedModuleRef;
use ruff_index::{IndexVec, newtype_index};
use ruff_python_ast as ast;
use ruff_python_ast::name::Name;
@ -381,16 +381,19 @@ impl<'db> ScopeId<'db> {
}
#[cfg(test)]
pub(crate) fn name(self, db: &'db dyn Db) -> &'db str {
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.name.as_str()
class.node(module).name.as_str()
}
NodeWithScopeKind::Function(function)
| NodeWithScopeKind::FunctionTypeParameters(function) => function.name.as_str(),
| 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())
@ -778,7 +781,7 @@ impl NodeWithScopeRef<'_> {
/// # Safety
/// The node wrapped by `self` must be a child of `module`.
#[expect(unsafe_code)]
pub(super) unsafe fn to_kind(self, module: ParsedModule) -> NodeWithScopeKind {
pub(super) unsafe fn to_kind(self, module: ParsedModuleRef) -> NodeWithScopeKind {
unsafe {
match self {
NodeWithScopeRef::Module => NodeWithScopeKind::Module,
@ -892,34 +895,46 @@ impl NodeWithScopeKind {
}
}
pub fn expect_class(&self) -> &ast::StmtClassDef {
pub fn expect_class<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::StmtClassDef {
match self {
Self::Class(class) => class.node(),
Self::Class(class) => class.node(module),
_ => panic!("expected class"),
}
}
pub(crate) const fn as_class(&self) -> Option<&ast::StmtClassDef> {
pub(crate) fn as_class<'ast>(
&self,
module: &'ast ParsedModuleRef,
) -> Option<&'ast ast::StmtClassDef> {
match self {
Self::Class(class) => Some(class.node()),
Self::Class(class) => Some(class.node(module)),
_ => None,
}
}
pub fn expect_function(&self) -> &ast::StmtFunctionDef {
self.as_function().expect("expected function")
pub fn expect_function<'ast>(
&self,
module: &'ast ParsedModuleRef,
) -> &'ast ast::StmtFunctionDef {
self.as_function(module).expect("expected function")
}
pub fn expect_type_alias(&self) -> &ast::StmtTypeAlias {
pub fn expect_type_alias<'ast>(
&self,
module: &'ast ParsedModuleRef,
) -> &'ast ast::StmtTypeAlias {
match self {
Self::TypeAlias(type_alias) => type_alias.node(),
Self::TypeAlias(type_alias) => type_alias.node(module),
_ => panic!("expected type alias"),
}
}
pub const fn as_function(&self) -> Option<&ast::StmtFunctionDef> {
pub fn as_function<'ast>(
&self,
module: &'ast ParsedModuleRef,
) -> Option<&'ast ast::StmtFunctionDef> {
match self {
Self::Function(function) => Some(function.node()),
Self::Function(function) => Some(function.node(module)),
_ => None,
}
}

View file

@ -45,7 +45,7 @@ fn exports_cycle_initial(_db: &dyn Db, _file: File) -> Box<[Name]> {
#[salsa::tracked(returns(deref), cycle_fn=exports_cycle_recover, cycle_initial=exports_cycle_initial)]
pub(super) fn exported_names(db: &dyn Db, file: File) -> Box<[Name]> {
let module = parsed_module(db.upcast(), file);
let module = parsed_module(db.upcast(), file).load(db.upcast());
let mut finder = ExportFinder::new(db, file);
finder.visit_body(module.suite());
finder.resolve_exports()