Skip to main content

ty_python_core/
definition.rs

1use std::ops::Deref;
2
3use ruff_db::PythonFile;
4use ruff_db::files::{File, FileRange};
5use ruff_db::parsed::{ParsedModuleRef, parsed_module};
6use ruff_python_ast::find_node::covering_node;
7use ruff_python_ast::name::Name;
8use ruff_python_ast::traversal::suite;
9use ruff_python_ast::{self as ast, AnyNodeRef, Expr};
10use ruff_text_size::{Ranged, TextRange, TextSize};
11use smallvec::SmallVec;
12
13use crate::LoopHeaderId;
14use crate::ProgramFile;
15use crate::ast_node_ref::AstNodeRef;
16use crate::member::ScopedMemberId;
17use crate::node_key::NodeKey;
18use crate::place::ScopedPlaceId;
19use crate::predicate::PatternPredicate;
20use crate::scope::{FileScopeId, ScopeId};
21use crate::symbol::ScopedSymbolId;
22use crate::unpack::{Unpack, UnpackPosition};
23use crate::use_def::BindingWithConstraintsIterator;
24use crate::{Db, Program, SemanticIndex};
25
26/// A definition of a place.
27///
28/// ## ID stability
29/// The `Definition`'s ID is stable when the only field that change is its `kind` (AST node).
30///
31/// The `Definition` changes when the `file`, `scope`, or `place` change. This can be
32/// because a new scope gets inserted before the `Definition` or a new place is inserted
33/// before this `Definition`. However, the ID can be considered stable and it is okay to use
34/// `Definition` in cross-module` salsa queries or as a field on other salsa tracked structs.
35#[salsa::tracked(
36    debug,
37    constructor = new_internal,
38    heap_size = ruff_memory_usage::heap_size
39)]
40#[derive(Ord, PartialOrd)]
41pub struct Definition<'db> {
42    /// The scope in which the definition occurs.
43    ///
44    /// Storing the interned scope avoids retaining the file and file-local scope separately, at
45    /// the cost of database lookups when either of those values is needed.
46    #[returns(copy)]
47    pub scope_id: ScopeId<'db>,
48
49    /// The place ID and re-export state of the definition.
50    #[returns(copy)]
51    place_info: DefinitionPlace,
52
53    /// WARNING: Only access this field when doing type inference for the same
54    /// file as where `Definition` is defined to avoid cross-file query dependencies.
55    #[no_eq]
56    #[returns(ref)]
57    #[tracked]
58    pub kind: DefinitionKind<'db>,
59}
60
61// The Salsa heap is tracked separately.
62impl get_size2::GetSize for Definition<'_> {}
63
64impl<'db> Definition<'db> {
65    pub(crate) fn new(
66        db: &'db dyn Db,
67        scope_id: ScopeId<'db>,
68        place: ScopedPlaceId,
69        kind: DefinitionKind<'db>,
70        is_reexported: bool,
71    ) -> Self {
72        Self::new_internal(
73            db,
74            scope_id,
75            DefinitionPlace::new(place, is_reexported),
76            kind,
77        )
78    }
79
80    pub fn scope(self, db: &'db dyn Db) -> ScopeId<'db> {
81        self.scope_id(db)
82    }
83
84    pub fn file(self, db: &'db dyn Db) -> File {
85        self.scope_id(db).file(db)
86    }
87
88    pub fn python_file(self, db: &'db dyn Db) -> PythonFile<'db> {
89        self.scope_id(db).python_file(db)
90    }
91
92    pub fn program_file(self, db: &'db dyn Db) -> ProgramFile<'db> {
93        self.scope_id(db).program_file(db)
94    }
95
96    pub fn program(self, db: &'db dyn Db) -> Program<'db> {
97        self.scope_id(db).program(db)
98    }
99
100    pub fn file_scope(self, db: &'db dyn Db) -> FileScopeId {
101        self.scope_id(db).file_scope_id(db)
102    }
103
104    pub fn place(self, db: &'db dyn Db) -> ScopedPlaceId {
105        self.place_info(db).place()
106    }
107
108    pub fn is_reexported(self, db: &'db dyn Db) -> bool {
109        self.place_info(db).is_reexported()
110    }
111
112    pub fn full_range(self, db: &'db dyn Db, module: &ParsedModuleRef) -> FileRange {
113        FileRange::new(self.file(db), self.kind(db).full_range(module))
114    }
115
116    pub fn focus_range(self, db: &'db dyn Db, module: &ParsedModuleRef) -> FileRange {
117        FileRange::new(self.file(db), self.kind(db).target_range(module))
118    }
119
120    /// Returns the name of the item being defined, if applicable.
121    pub fn name(self, db: &'db dyn Db) -> Option<String> {
122        let module = parsed_module(db, self.python_file(db)).load(db);
123        let kind = self.kind(db);
124        match kind {
125            DefinitionKind::Function(def) => {
126                let node = def.node(&module);
127                Some(node.name.as_str().to_string())
128            }
129            DefinitionKind::Class(def) => {
130                let node = def.node(&module);
131                Some(node.name.as_str().to_string())
132            }
133            DefinitionKind::TypeAlias(def) => {
134                let node = def.node(&module);
135                Some(
136                    node.name
137                        .as_name_expr()
138                        .expect("type alias name should be a NameExpr")
139                        .id
140                        .as_str()
141                        .to_string(),
142                )
143            }
144            DefinitionKind::Assignment(assignment) => {
145                let target_node = assignment.target.node(&module);
146                target_node
147                    .as_name_expr()
148                    .map(|name_expr| name_expr.id.as_str().to_string())
149            }
150            _ => None,
151        }
152    }
153
154    /// Extract a docstring from this definition, if applicable.
155    /// This method returns a docstring for function, class, and attribute definitions.
156    /// The docstring is extracted from the first statement in the body if it's a string literal.
157    pub fn docstring(self, db: &'db dyn Db) -> Option<String> {
158        let module = parsed_module(db, self.python_file(db)).load(db);
159        let kind = self.kind(db);
160
161        match kind {
162            DefinitionKind::Assignment(assign_def) => {
163                let assign_node = assign_def.target(&module);
164                attribute_docstring(&module, assign_node)
165                    .map(|docstring_expr| docstring_expr.value.to_str().to_owned())
166            }
167            DefinitionKind::AnnotatedAssignment(assign_def) => {
168                let assign_node = assign_def.target(&module);
169                attribute_docstring(&module, assign_node)
170                    .map(|docstring_expr| docstring_expr.value.to_str().to_owned())
171            }
172            DefinitionKind::Function(function_def) => {
173                let function_node = function_def.node(&module);
174                docstring_from_body(&function_node.body)
175                    .map(|docstring_expr| docstring_expr.value.to_str().to_owned())
176            }
177            DefinitionKind::Class(class_def) => {
178                let class_node = class_def.node(&module);
179                docstring_from_body(&class_node.body)
180                    .map(|docstring_expr| docstring_expr.value.to_str().to_owned())
181            }
182            _ => None,
183        }
184    }
185}
186
187/// The identity of the place defined by a [`Definition`] and whether it is re-exported.
188///
189/// Keeping the re-export state in the enum lets it share the place ID's otherwise-unused
190/// representation space. Storing it as a separate field on [`Definition`] would add padding to
191/// every tracked definition.
192#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, get_size2::GetSize)]
193pub enum DefinitionPlace {
194    Symbol {
195        id: ScopedSymbolId,
196        is_reexported: bool,
197    },
198    Member {
199        id: ScopedMemberId,
200        is_reexported: bool,
201    },
202}
203
204impl DefinitionPlace {
205    fn new(place: ScopedPlaceId, is_reexported: bool) -> Self {
206        match place {
207            ScopedPlaceId::Symbol(id) => Self::Symbol { id, is_reexported },
208            ScopedPlaceId::Member(id) => Self::Member { id, is_reexported },
209        }
210    }
211
212    fn place(self) -> ScopedPlaceId {
213        match self {
214            Self::Symbol { id, .. } => ScopedPlaceId::Symbol(id),
215            Self::Member { id, .. } => ScopedPlaceId::Member(id),
216        }
217    }
218
219    fn is_reexported(self) -> bool {
220        match self {
221            Self::Symbol { is_reexported, .. } | Self::Member { is_reexported, .. } => {
222                is_reexported
223            }
224        }
225    }
226}
227
228/// Extract a docstring from a function, module, or class body.
229pub fn docstring_from_body(body: &[ast::Stmt]) -> Option<&ast::ExprStringLiteral> {
230    let stmt = body.first()?;
231    // Require the docstring to be a standalone expression.
232    let ast::StmtExpr {
233        value,
234        range: _,
235        node_index: _,
236    } = stmt.as_expr_stmt()?;
237    // Only match string literals.
238    value.as_string_literal_expr()
239}
240
241/// Extract a docstring from an attribute.
242///
243/// This is a non-standardized but popular-and-supported-by-sphinx kind of docstring
244/// where you just place the docstring underneath an assignment to an attribute and
245/// that counts as docs.
246///
247/// This is annoying to extract because we have a reference to (part of) an assignment statement
248/// and we need to find the statement *after it*, which is easy to say but not something the
249/// AST wants to encourage.
250fn attribute_docstring<'a>(
251    module: &'a ParsedModuleRef,
252    assign_lvalue: &Expr,
253) -> Option<&'a ast::ExprStringLiteral> {
254    // Find all the ancestors of the assign lvalue
255    let covering_node = covering_node(module.syntax().into(), assign_lvalue.range());
256    // The assignment is the closest parent statement
257    let assign = covering_node.find_first(AnyNodeRef::is_statement).ok()?;
258    let parent = assign.parent()?;
259    let assign_node = assign.node();
260
261    // The docs must be the next statement
262    let parent_body = suite(assign_node, parent)?;
263    let next_stmt = parent_body.next_sibling()?;
264
265    // Require the docstring to be a standalone expression.
266    let ast::Stmt::Expr(ast::StmtExpr {
267        value,
268        range: _,
269        node_index: _,
270    }) = next_stmt
271    else {
272        return None;
273    };
274
275    // Only match string literals.
276    value.as_string_literal_expr()
277}
278
279/// One or more [`Definition`]s.
280#[derive(Debug, Default, PartialEq, Eq, get_size2::GetSize)]
281pub struct Definitions<'db> {
282    definitions: smallvec::SmallVec<[Definition<'db>; 1]>,
283}
284
285impl<'db> Definitions<'db> {
286    pub(crate) fn push(&mut self, definition: Definition<'db>) {
287        self.definitions.push(definition);
288    }
289
290    pub(crate) fn into_boxed_slice(self) -> Box<[Definition<'db>]> {
291        self.definitions.into_vec().into_boxed_slice()
292    }
293}
294
295impl<'db> Deref for Definitions<'db> {
296    type Target = [Definition<'db>];
297
298    fn deref(&self) -> &Self::Target {
299        &self.definitions
300    }
301}
302
303impl<'a, 'db> IntoIterator for &'a Definitions<'db> {
304    type Item = &'a Definition<'db>;
305    type IntoIter = std::slice::Iter<'a, Definition<'db>>;
306
307    fn into_iter(self) -> Self::IntoIter {
308        self.definitions.iter()
309    }
310}
311
312#[derive(Debug, Clone, Copy, PartialEq, Eq, get_size2::GetSize)]
313pub enum DefinitionState<'db> {
314    Defined(Definition<'db>),
315    /// Represents the implicit "unbound"/"undeclared" definition of every place.
316    Undefined,
317    /// Represents a definition that has been deleted.
318    /// This used when an attribute/subscript definition (such as `x.y = ...`, `x[0] = ...`) becomes obsolete due to a reassignment of the root place.
319    Deleted,
320}
321
322impl<'db> DefinitionState<'db> {
323    pub fn is_defined_and(self, f: impl Fn(Definition<'db>) -> bool) -> bool {
324        matches!(self, DefinitionState::Defined(def) if f(def))
325    }
326
327    pub fn is_undefined_or(self, f: impl Fn(Definition<'db>) -> bool) -> bool {
328        matches!(self, DefinitionState::Undefined)
329            || matches!(self, DefinitionState::Defined(def) if f(def))
330    }
331
332    #[allow(unused)]
333    pub fn definition(self) -> Option<Definition<'db>> {
334        match self {
335            DefinitionState::Defined(def) => Some(def),
336            DefinitionState::Deleted | DefinitionState::Undefined => None,
337        }
338    }
339}
340
341#[derive(Copy, Clone, Debug)]
342pub(crate) enum DefinitionNodeRef<'ast, 'db> {
343    Import(ImportDefinitionNodeRef<'ast>),
344    ImportFrom(ImportFromDefinitionNodeRef<'ast>),
345    ImportFromSubmodule(ImportFromSubmoduleDefinitionNodeRef<'ast>),
346    ImportStar(StarImportDefinitionNodeRef<'ast>),
347    For(ForStmtDefinitionNodeRef<'ast, 'db>),
348    Function(&'ast ast::StmtFunctionDef),
349    Class(&'ast ast::StmtClassDef),
350    TypeAlias(&'ast ast::StmtTypeAlias),
351    NamedExpression(&'ast ast::ExprNamed),
352    Assignment(AssignmentDefinitionNodeRef<'ast, 'db>),
353    AnnotatedAssignment(AnnotatedAssignmentDefinitionNodeRef<'ast>),
354    AugmentedAssignment(&'ast ast::StmtAugAssign),
355    DictKeyAssignment(DictKeyAssignmentNodeRef<'ast, 'db>),
356    Comprehension(ComprehensionDefinitionNodeRef<'ast, 'db>),
357    Parameter(ParameterDefinitionNodeRef<'ast>),
358    LambdaParameter(LambdaParameterDefinitionNodeRef<'ast>),
359    WithItem(WithItemDefinitionNodeRef<'ast, 'db>),
360    MatchPattern(MatchPatternDefinitionNodeRef<'ast, 'db>),
361    ExceptHandler(ExceptHandlerDefinitionNodeRef<'ast>),
362    TypeVar(&'ast ast::TypeParamTypeVar),
363    ParamSpec(&'ast ast::TypeParamParamSpec),
364    TypeVarTuple(&'ast ast::TypeParamTypeVarTuple),
365    LoopHeader(LoopHeaderDefinitionNodeRef<'ast>),
366}
367
368impl<'ast> From<&'ast ast::StmtFunctionDef> for DefinitionNodeRef<'ast, '_> {
369    fn from(node: &'ast ast::StmtFunctionDef) -> Self {
370        Self::Function(node)
371    }
372}
373
374impl<'ast> From<&'ast ast::StmtClassDef> for DefinitionNodeRef<'ast, '_> {
375    fn from(node: &'ast ast::StmtClassDef) -> Self {
376        Self::Class(node)
377    }
378}
379
380impl<'ast> From<&'ast ast::StmtTypeAlias> for DefinitionNodeRef<'ast, '_> {
381    fn from(node: &'ast ast::StmtTypeAlias) -> Self {
382        Self::TypeAlias(node)
383    }
384}
385
386impl<'ast> From<&'ast ast::ExprNamed> for DefinitionNodeRef<'ast, '_> {
387    fn from(node: &'ast ast::ExprNamed) -> Self {
388        Self::NamedExpression(node)
389    }
390}
391
392impl<'ast> From<&'ast ast::StmtAugAssign> for DefinitionNodeRef<'ast, '_> {
393    fn from(node: &'ast ast::StmtAugAssign) -> Self {
394        Self::AugmentedAssignment(node)
395    }
396}
397
398impl<'ast> From<&'ast ast::TypeParamTypeVar> for DefinitionNodeRef<'ast, '_> {
399    fn from(value: &'ast ast::TypeParamTypeVar) -> Self {
400        Self::TypeVar(value)
401    }
402}
403
404impl<'ast> From<&'ast ast::TypeParamParamSpec> for DefinitionNodeRef<'ast, '_> {
405    fn from(value: &'ast ast::TypeParamParamSpec) -> Self {
406        Self::ParamSpec(value)
407    }
408}
409
410impl<'ast> From<&'ast ast::TypeParamTypeVarTuple> for DefinitionNodeRef<'ast, '_> {
411    fn from(value: &'ast ast::TypeParamTypeVarTuple) -> Self {
412        Self::TypeVarTuple(value)
413    }
414}
415
416impl<'ast> From<LoopHeaderDefinitionNodeRef<'ast>> for DefinitionNodeRef<'ast, '_> {
417    fn from(value: LoopHeaderDefinitionNodeRef<'ast>) -> Self {
418        Self::LoopHeader(value)
419    }
420}
421
422impl<'ast> From<ImportDefinitionNodeRef<'ast>> for DefinitionNodeRef<'ast, '_> {
423    fn from(node_ref: ImportDefinitionNodeRef<'ast>) -> Self {
424        Self::Import(node_ref)
425    }
426}
427
428impl<'ast> From<ImportFromDefinitionNodeRef<'ast>> for DefinitionNodeRef<'ast, '_> {
429    fn from(node_ref: ImportFromDefinitionNodeRef<'ast>) -> Self {
430        Self::ImportFrom(node_ref)
431    }
432}
433
434impl<'ast> From<ImportFromSubmoduleDefinitionNodeRef<'ast>> for DefinitionNodeRef<'ast, '_> {
435    fn from(node_ref: ImportFromSubmoduleDefinitionNodeRef<'ast>) -> Self {
436        Self::ImportFromSubmodule(node_ref)
437    }
438}
439
440impl<'ast, 'db> From<ForStmtDefinitionNodeRef<'ast, 'db>> for DefinitionNodeRef<'ast, 'db> {
441    fn from(value: ForStmtDefinitionNodeRef<'ast, 'db>) -> Self {
442        Self::For(value)
443    }
444}
445
446impl<'ast, 'db> From<AssignmentDefinitionNodeRef<'ast, 'db>> for DefinitionNodeRef<'ast, 'db> {
447    fn from(node_ref: AssignmentDefinitionNodeRef<'ast, 'db>) -> Self {
448        Self::Assignment(node_ref)
449    }
450}
451
452impl<'ast> From<AnnotatedAssignmentDefinitionNodeRef<'ast>> for DefinitionNodeRef<'ast, '_> {
453    fn from(node_ref: AnnotatedAssignmentDefinitionNodeRef<'ast>) -> Self {
454        Self::AnnotatedAssignment(node_ref)
455    }
456}
457
458impl<'ast, 'db> From<DictKeyAssignmentNodeRef<'ast, 'db>> for DefinitionNodeRef<'ast, 'db> {
459    fn from(node_ref: DictKeyAssignmentNodeRef<'ast, 'db>) -> Self {
460        Self::DictKeyAssignment(node_ref)
461    }
462}
463
464impl<'ast, 'db> From<WithItemDefinitionNodeRef<'ast, 'db>> for DefinitionNodeRef<'ast, 'db> {
465    fn from(node_ref: WithItemDefinitionNodeRef<'ast, 'db>) -> Self {
466        Self::WithItem(node_ref)
467    }
468}
469
470impl<'ast, 'db> From<ComprehensionDefinitionNodeRef<'ast, 'db>> for DefinitionNodeRef<'ast, 'db> {
471    fn from(node: ComprehensionDefinitionNodeRef<'ast, 'db>) -> Self {
472        Self::Comprehension(node)
473    }
474}
475
476impl<'ast> From<ParameterDefinitionNodeRef<'ast>> for DefinitionNodeRef<'ast, '_> {
477    fn from(node: ParameterDefinitionNodeRef<'ast>) -> Self {
478        Self::Parameter(node)
479    }
480}
481
482impl<'ast> From<LambdaParameterDefinitionNodeRef<'ast>> for DefinitionNodeRef<'ast, '_> {
483    fn from(node: LambdaParameterDefinitionNodeRef<'ast>) -> Self {
484        Self::LambdaParameter(node)
485    }
486}
487
488impl<'ast, 'db> From<MatchPatternDefinitionNodeRef<'ast, 'db>> for DefinitionNodeRef<'ast, 'db> {
489    fn from(node: MatchPatternDefinitionNodeRef<'ast, 'db>) -> Self {
490        Self::MatchPattern(node)
491    }
492}
493
494impl<'ast> From<StarImportDefinitionNodeRef<'ast>> for DefinitionNodeRef<'ast, '_> {
495    fn from(node: StarImportDefinitionNodeRef<'ast>) -> Self {
496        Self::ImportStar(node)
497    }
498}
499
500#[derive(Copy, Clone, Debug)]
501pub(crate) struct ImportDefinitionNodeRef<'ast> {
502    pub(crate) node: &'ast ast::StmtImport,
503    pub(crate) alias_index: usize,
504    pub(crate) is_reexported: bool,
505}
506
507#[derive(Copy, Clone, Debug)]
508pub(crate) struct StarImportDefinitionNodeRef<'ast> {
509    pub(crate) node: &'ast ast::StmtImportFrom,
510    pub(crate) symbol_id: ScopedSymbolId,
511}
512
513#[derive(Copy, Clone, Debug)]
514pub(crate) struct ImportFromDefinitionNodeRef<'ast> {
515    pub(crate) node: &'ast ast::StmtImportFrom,
516    pub(crate) alias_index: usize,
517    pub(crate) is_reexported: bool,
518}
519
520#[derive(Copy, Clone, Debug)]
521pub(crate) struct ImportFromSubmoduleDefinitionNodeRef<'ast> {
522    pub(crate) node: &'ast ast::StmtImportFrom,
523    pub(crate) module_index: usize,
524}
525
526#[derive(Copy, Clone, Debug)]
527pub(crate) struct AssignmentDefinitionNodeRef<'ast, 'db> {
528    pub(crate) unpack: Option<Unpack<'db>>,
529    pub(crate) value: &'ast ast::Expr,
530    pub(crate) target: &'ast ast::Expr,
531}
532
533#[derive(Copy, Clone, Debug)]
534pub(crate) struct AnnotatedAssignmentDefinitionNodeRef<'ast> {
535    pub(crate) node: &'ast ast::StmtAnnAssign,
536}
537
538#[derive(Copy, Clone, Debug)]
539pub(crate) struct DictKeyAssignmentNodeRef<'ast, 'db> {
540    pub(crate) key: &'ast ast::Expr,
541    pub(crate) value: &'ast ast::Expr,
542    pub(crate) assignment: Definition<'db>,
543}
544
545#[derive(Copy, Clone, Debug)]
546pub(crate) struct WithItemDefinitionNodeRef<'ast, 'db> {
547    pub(crate) unpack: Option<(UnpackPosition, Unpack<'db>)>,
548    pub(crate) item: &'ast ast::WithItem,
549    pub(crate) target: &'ast ast::Expr,
550    pub(crate) is_async: bool,
551}
552
553#[derive(Copy, Clone, Debug)]
554pub(crate) struct ForStmtDefinitionNodeRef<'ast, 'db> {
555    pub(crate) unpack: Option<(UnpackPosition, Unpack<'db>)>,
556    pub(crate) node: &'ast ast::StmtFor,
557    pub(crate) target: &'ast ast::Expr,
558}
559
560#[derive(Copy, Clone, Debug)]
561pub(crate) struct ExceptHandlerDefinitionNodeRef<'ast> {
562    pub(crate) handler: &'ast ast::ExceptHandlerExceptHandler,
563    pub(crate) is_star: bool,
564}
565
566#[derive(Copy, Clone, Debug)]
567pub(crate) struct LoopHeaderDefinitionNodeRef<'ast> {
568    pub(crate) loop_stmt: LoopStmtRef<'ast>,
569    pub(crate) place: ScopedPlaceId,
570    pub(crate) loop_header_id: LoopHeaderId,
571}
572
573#[derive(Copy, Clone, Debug)]
574pub(crate) enum LoopStmtRef<'ast> {
575    While(&'ast ast::StmtWhile),
576    For(&'ast ast::StmtFor),
577}
578
579#[derive(Copy, Clone, Debug)]
580pub(crate) struct ComprehensionDefinitionNodeRef<'ast, 'db> {
581    pub(crate) unpack: Option<(UnpackPosition, Unpack<'db>)>,
582    pub(crate) node: &'ast ast::Comprehension,
583    pub(crate) target: &'ast ast::Expr,
584    pub(crate) first: bool,
585}
586
587#[derive(Copy, Clone, Debug)]
588pub(crate) enum ParameterDefinitionNodeRef<'ast> {
589    VariadicPositionalParameter(&'ast ast::Parameter),
590    VariadicKeywordParameter(&'ast ast::Parameter),
591    Parameter(&'ast ast::ParameterWithDefault),
592}
593
594impl ParameterDefinitionNodeRef<'_> {
595    fn into_owned(self, parsed: &ParsedModuleRef) -> ParameterDefinitionNodeKind {
596        match self {
597            Self::VariadicPositionalParameter(parameter) => {
598                ParameterDefinitionNodeKind::VariadicPositionalParameter(AstNodeRef::new(
599                    parsed, parameter,
600                ))
601            }
602            Self::VariadicKeywordParameter(parameter) => {
603                ParameterDefinitionNodeKind::VariadicKeywordParameter(AstNodeRef::new(
604                    parsed, parameter,
605                ))
606            }
607            Self::Parameter(parameter) => {
608                ParameterDefinitionNodeKind::Parameter(AstNodeRef::new(parsed, parameter))
609            }
610        }
611    }
612
613    fn key(self) -> DefinitionNodeKey {
614        match self {
615            Self::VariadicPositionalParameter(node) => node.into(),
616            Self::VariadicKeywordParameter(node) => node.into(),
617            Self::Parameter(node) => (&node.parameter).into(),
618        }
619    }
620}
621
622#[derive(Copy, Clone, Debug)]
623pub(crate) struct LambdaParameterDefinitionNodeRef<'ast> {
624    pub(crate) index: usize,
625    pub(crate) parameter: ParameterDefinitionNodeRef<'ast>,
626    pub(crate) lambda: &'ast ast::ExprLambda,
627}
628
629#[derive(Copy, Clone, Debug)]
630pub(crate) struct MatchPatternDefinitionNodeRef<'ast, 'db> {
631    /// The outermost pattern node in which the identifier being defined occurs.
632    pub(crate) pattern: &'ast ast::Pattern,
633    /// The identifier being defined.
634    pub(crate) identifier: &'ast ast::Identifier,
635    /// The predicate for the complete match case containing this binding.
636    pub(crate) predicate: PatternPredicate<'db>,
637}
638
639impl<'db> DefinitionNodeRef<'_, 'db> {
640    pub(super) fn into_owned(self, parsed: &ParsedModuleRef) -> DefinitionKind<'db> {
641        match self {
642            DefinitionNodeRef::Import(ImportDefinitionNodeRef {
643                node,
644                alias_index,
645                is_reexported,
646            }) => DefinitionKind::Import(ImportDefinitionKind {
647                node: AstNodeRef::new(parsed, node),
648                alias_index: alias_index
649                    .try_into()
650                    .expect("import alias index should fit in u32"),
651                is_reexported,
652            }),
653            DefinitionNodeRef::ImportFrom(ImportFromDefinitionNodeRef {
654                node,
655                alias_index,
656                is_reexported,
657            }) => DefinitionKind::ImportFrom(ImportFromDefinitionKind {
658                node: AstNodeRef::new(parsed, node),
659                alias_index: alias_index
660                    .try_into()
661                    .expect("import-from alias index should fit in u32"),
662                is_reexported,
663            }),
664            DefinitionNodeRef::ImportFromSubmodule(ImportFromSubmoduleDefinitionNodeRef {
665                node,
666                module_index,
667            }) => DefinitionKind::ImportFromSubmodule(ImportFromSubmoduleDefinitionKind {
668                node: AstNodeRef::new(parsed, node),
669                module_index: module_index
670                    .try_into()
671                    .expect("import-from submodule index should fit in u32"),
672            }),
673            DefinitionNodeRef::ImportStar(star_import) => {
674                let StarImportDefinitionNodeRef { node, symbol_id } = star_import;
675                DefinitionKind::StarImport(StarImportDefinitionKind {
676                    node: AstNodeRef::new(parsed, node),
677                    symbol_id,
678                })
679            }
680            DefinitionNodeRef::Function(function) => {
681                DefinitionKind::Function(AstNodeRef::new(parsed, function))
682            }
683            DefinitionNodeRef::Class(class) => {
684                DefinitionKind::Class(AstNodeRef::new(parsed, class))
685            }
686            DefinitionNodeRef::TypeAlias(type_alias) => {
687                DefinitionKind::TypeAlias(AstNodeRef::new(parsed, type_alias))
688            }
689            DefinitionNodeRef::NamedExpression(named) => {
690                DefinitionKind::NamedExpression(AstNodeRef::new(parsed, named))
691            }
692            DefinitionNodeRef::Assignment(AssignmentDefinitionNodeRef {
693                unpack,
694                value,
695                target,
696            }) => DefinitionKind::Assignment(AssignmentDefinitionKind {
697                unpack,
698                value: AstNodeRef::new(parsed, value),
699                target: AstNodeRef::new(parsed, target),
700            }),
701            DefinitionNodeRef::AnnotatedAssignment(AnnotatedAssignmentDefinitionNodeRef {
702                node,
703            }) => DefinitionKind::AnnotatedAssignment(AnnotatedAssignmentDefinitionKind {
704                node: AstNodeRef::new(parsed, node),
705                has_value: node.value.is_some(),
706            }),
707            DefinitionNodeRef::AugmentedAssignment(augmented_assignment) => {
708                DefinitionKind::AugmentedAssignment(AstNodeRef::new(parsed, augmented_assignment))
709            }
710            DefinitionNodeRef::DictKeyAssignment(DictKeyAssignmentNodeRef {
711                key,
712                value,
713                assignment,
714            }) => DefinitionKind::DictKeyAssignment(DictKeyAssignmentKind {
715                key: AstNodeRef::new(parsed, key),
716                value: AstNodeRef::new(parsed, value),
717                assignment,
718            }),
719            DefinitionNodeRef::For(ForStmtDefinitionNodeRef {
720                unpack,
721                node,
722                target,
723            }) => DefinitionKind::For(ForStmtDefinitionKind {
724                unpack: unpack.map(|(_, unpack)| unpack),
725                unpack_position: unpack.map_or(UnpackPosition::First, |(position, _)| position),
726                node: AstNodeRef::new(parsed, node),
727                target: AstNodeRef::new(parsed, target),
728                is_async: node.is_async,
729            }),
730            DefinitionNodeRef::Comprehension(ComprehensionDefinitionNodeRef {
731                unpack,
732                node,
733                target,
734                first,
735            }) => DefinitionKind::Comprehension(ComprehensionDefinitionKind {
736                unpack: unpack.map(|(_, unpack)| unpack),
737                unpack_position: unpack.map_or(UnpackPosition::First, |(position, _)| position),
738                node: AstNodeRef::new(parsed, node),
739                target: AstNodeRef::new(parsed, target),
740                first,
741                is_async: node.is_async,
742            }),
743            DefinitionNodeRef::Parameter(parameter) => {
744                DefinitionKind::Parameter(parameter.into_owned(parsed))
745            }
746            DefinitionNodeRef::LambdaParameter(LambdaParameterDefinitionNodeRef {
747                index,
748                parameter,
749                lambda,
750            }) => DefinitionKind::LambdaParameter(LambdaParameterDefinitionNodeKind {
751                index: index
752                    .try_into()
753                    .expect("lambda parameter index should fit in u32"),
754                parameter: parameter.into_owned(parsed),
755                lambda: AstNodeRef::new(parsed, lambda),
756            }),
757            DefinitionNodeRef::WithItem(WithItemDefinitionNodeRef {
758                unpack,
759                item,
760                target,
761                is_async,
762            }) => DefinitionKind::WithItem(WithItemDefinitionKind {
763                unpack: unpack.map(|(_, unpack)| unpack),
764                unpack_position: unpack.map_or(UnpackPosition::First, |(position, _)| position),
765                item: AstNodeRef::new(parsed, item),
766                target: AstNodeRef::new(parsed, target),
767                is_async,
768            }),
769            DefinitionNodeRef::MatchPattern(MatchPatternDefinitionNodeRef {
770                pattern,
771                identifier,
772                predicate,
773            }) => DefinitionKind::MatchPattern(MatchPatternDefinitionKind {
774                pattern: AstNodeRef::new(parsed, pattern),
775                identifier: AstNodeRef::new(parsed, identifier),
776                predicate,
777            }),
778            DefinitionNodeRef::ExceptHandler(ExceptHandlerDefinitionNodeRef {
779                handler,
780                is_star,
781            }) => DefinitionKind::ExceptHandler(ExceptHandlerDefinitionKind {
782                handler: AstNodeRef::new(parsed, handler),
783                is_star,
784            }),
785            DefinitionNodeRef::TypeVar(node) => {
786                DefinitionKind::TypeVar(AstNodeRef::new(parsed, node))
787            }
788            DefinitionNodeRef::ParamSpec(node) => {
789                DefinitionKind::ParamSpec(AstNodeRef::new(parsed, node))
790            }
791            DefinitionNodeRef::TypeVarTuple(node) => {
792                DefinitionKind::TypeVarTuple(AstNodeRef::new(parsed, node))
793            }
794            DefinitionNodeRef::LoopHeader(LoopHeaderDefinitionNodeRef {
795                loop_stmt,
796                place,
797                loop_header_id,
798            }) => DefinitionKind::LoopHeader(LoopHeaderDefinitionKind {
799                loop_header_id,
800                loop_stmt: match loop_stmt {
801                    LoopStmtRef::While(stmt) => LoopStmtKind::While(AstNodeRef::new(parsed, stmt)),
802                    LoopStmtRef::For(stmt) => LoopStmtKind::For(AstNodeRef::new(parsed, stmt)),
803                },
804                place,
805            }),
806        }
807    }
808
809    pub(super) fn key(self) -> DefinitionNodeKey {
810        match self {
811            Self::Import(ImportDefinitionNodeRef {
812                node,
813                alias_index,
814                is_reexported: _,
815            }) => (&node.names[alias_index]).into(),
816            Self::ImportFrom(ImportFromDefinitionNodeRef {
817                node,
818                alias_index,
819                is_reexported: _,
820            }) => (&node.names[alias_index]).into(),
821            Self::ImportFromSubmodule(ImportFromSubmoduleDefinitionNodeRef { node, .. }) => {
822                node.into()
823            }
824            // INVARIANT: for an invalid-syntax statement such as `from foo import *, bar, *`,
825            // we only create a `StarImportDefinitionKind` for the *first* `*` alias in the names list.
826            Self::ImportStar(StarImportDefinitionNodeRef { node, symbol_id: _ }) => node
827                .names
828                .iter()
829                .find(|alias| &alias.name == "*")
830                .expect(
831                    "The `StmtImportFrom` node of a `StarImportDefinitionKind` instance \
832                    should always have at least one `alias` with the name `*`.",
833                )
834                .into(),
835
836            Self::Function(node) => node.into(),
837            Self::Class(node) => node.into(),
838            Self::TypeAlias(node) => node.into(),
839            Self::NamedExpression(node) => node.into(),
840            Self::Assignment(AssignmentDefinitionNodeRef {
841                value: _,
842                unpack: _,
843                target,
844            }) => DefinitionNodeKey(NodeKey::from_node(target)),
845            Self::AnnotatedAssignment(ann_assign) => ann_assign.node.into(),
846            Self::AugmentedAssignment(node) => node.into(),
847            Self::DictKeyAssignment(node) => DefinitionNodeKey(NodeKey::from_node(node.key)),
848            Self::For(ForStmtDefinitionNodeRef {
849                target,
850                node: _,
851                unpack: _,
852            }) => DefinitionNodeKey(NodeKey::from_node(target)),
853            Self::Comprehension(ComprehensionDefinitionNodeRef { target, .. }) => {
854                DefinitionNodeKey(NodeKey::from_node(target))
855            }
856            Self::LambdaParameter(LambdaParameterDefinitionNodeRef { parameter, .. }) => {
857                parameter.key()
858            }
859            Self::Parameter(parameter) => parameter.key(),
860            Self::WithItem(WithItemDefinitionNodeRef {
861                item: _,
862                unpack: _,
863                is_async: _,
864                target,
865            }) => DefinitionNodeKey(NodeKey::from_node(target)),
866            Self::MatchPattern(MatchPatternDefinitionNodeRef { identifier, .. }) => {
867                identifier.into()
868            }
869            Self::ExceptHandler(ExceptHandlerDefinitionNodeRef { handler, .. }) => handler.into(),
870            Self::TypeVar(node) => node.into(),
871            Self::ParamSpec(node) => node.into(),
872            Self::TypeVarTuple(node) => node.into(),
873            Self::LoopHeader(LoopHeaderDefinitionNodeRef { loop_stmt, .. }) => match loop_stmt {
874                LoopStmtRef::While(stmt) => stmt.into(),
875                LoopStmtRef::For(stmt) => stmt.into(),
876            },
877        }
878    }
879}
880
881#[derive(Clone, Copy, Debug)]
882pub enum DefinitionCategory {
883    /// A Definition which binds a value to a name (e.g. `x = 1`).
884    Binding,
885    /// A Definition which declares the upper-bound of acceptable types for this name (`x: int`).
886    Declaration,
887    /// A Definition which both declares a type and binds a value (e.g. `x: int = 1`).
888    DeclarationAndBinding,
889}
890
891impl DefinitionCategory {
892    /// True if this definition establishes a "declared type" for the place.
893    ///
894    /// If so, any assignments reached by this definition are in error if they assign a value of a
895    /// type not assignable to the declared type.
896    ///
897    /// Annotations establish a declared type. So do function and class definitions, and imports.
898    pub fn is_declaration(self) -> bool {
899        matches!(
900            self,
901            DefinitionCategory::Declaration | DefinitionCategory::DeclarationAndBinding
902        )
903    }
904
905    /// True if this definition assigns a value to the place.
906    ///
907    /// False only for annotated assignments without a RHS.
908    pub fn is_binding(self) -> bool {
909        matches!(
910            self,
911            DefinitionCategory::Binding | DefinitionCategory::DeclarationAndBinding
912        )
913    }
914}
915
916/// The kind of a definition.
917///
918/// ## Usage in salsa tracked structs
919///
920/// [`DefinitionKind`] fields in salsa tracked structs should be tracked (attributed with `#[tracked]`)
921/// because the kind is a thin wrapper around [`AstNodeRef`]. See the [`AstNodeRef`] documentation
922/// for an in-depth explanation of why this is necessary.
923#[derive(Clone, Debug, get_size2::GetSize, salsa::SalsaValue)]
924pub enum DefinitionKind<'db> {
925    Import(ImportDefinitionKind),
926    ImportFrom(ImportFromDefinitionKind),
927    ImportFromSubmodule(ImportFromSubmoduleDefinitionKind),
928    StarImport(StarImportDefinitionKind),
929    Function(AstNodeRef<ast::StmtFunctionDef>),
930    Class(AstNodeRef<ast::StmtClassDef>),
931    TypeAlias(AstNodeRef<ast::StmtTypeAlias>),
932    NamedExpression(AstNodeRef<ast::ExprNamed>),
933    Assignment(AssignmentDefinitionKind<'db>),
934    AnnotatedAssignment(AnnotatedAssignmentDefinitionKind),
935    AugmentedAssignment(AstNodeRef<ast::StmtAugAssign>),
936    DictKeyAssignment(DictKeyAssignmentKind<'db>),
937    For(ForStmtDefinitionKind<'db>),
938    Comprehension(ComprehensionDefinitionKind<'db>),
939    Parameter(ParameterDefinitionNodeKind),
940    LambdaParameter(LambdaParameterDefinitionNodeKind),
941    WithItem(WithItemDefinitionKind<'db>),
942    MatchPattern(MatchPatternDefinitionKind<'db>),
943    ExceptHandler(ExceptHandlerDefinitionKind),
944    TypeVar(AstNodeRef<ast::TypeParamTypeVar>),
945    ParamSpec(AstNodeRef<ast::TypeParamParamSpec>),
946    TypeVarTuple(AstNodeRef<ast::TypeParamTypeVarTuple>),
947    LoopHeader(LoopHeaderDefinitionKind),
948    // Boxing here helps avoid growing the memory footprint of this enum.
949    NestedBindings(Box<NestedBindingsDefinitionKind>),
950}
951
952impl<'db> DefinitionKind<'db> {
953    pub(crate) fn is_reexported(&self) -> bool {
954        match self {
955            DefinitionKind::Import(import) => import.is_reexported(),
956            DefinitionKind::ImportFrom(import) => import.is_reexported(),
957            DefinitionKind::ImportFromSubmodule(_) => true,
958            _ => true,
959        }
960    }
961
962    pub const fn as_star_import(&self) -> Option<&StarImportDefinitionKind> {
963        match self {
964            DefinitionKind::StarImport(import) => Some(import),
965            _ => None,
966        }
967    }
968
969    pub const fn as_class(&self) -> Option<&AstNodeRef<ast::StmtClassDef>> {
970        match self {
971            DefinitionKind::Class(class) => Some(class),
972            _ => None,
973        }
974    }
975
976    pub fn is_import(&self) -> bool {
977        matches!(
978            self,
979            DefinitionKind::Import(_)
980                | DefinitionKind::ImportFrom(_)
981                | DefinitionKind::StarImport(_)
982                | DefinitionKind::ImportFromSubmodule(_)
983        )
984    }
985
986    pub const fn is_unannotated_assignment(&self) -> bool {
987        matches!(self, DefinitionKind::Assignment(_))
988    }
989
990    pub(crate) fn as_unannotated_assignment(&self) -> Option<AssignmentDefinitionKind<'db>> {
991        match self {
992            DefinitionKind::Assignment(assignment) => Some(assignment.clone()),
993            _ => None,
994        }
995    }
996
997    pub const fn is_function_def(&self) -> bool {
998        matches!(self, DefinitionKind::Function(_))
999    }
1000
1001    pub const fn is_parameter_def(&self) -> bool {
1002        matches!(self, DefinitionKind::Parameter(_))
1003    }
1004
1005    pub const fn is_loop_header(&self) -> bool {
1006        matches!(self, DefinitionKind::LoopHeader(_))
1007    }
1008
1009    /// Returns `true` if this definition is user-visible (i.e., not an internal
1010    /// synthetic definition like a loop header or nested bindings definition).
1011    pub const fn is_user_visible(&self) -> bool {
1012        !matches!(
1013            self,
1014            DefinitionKind::LoopHeader(_) | DefinitionKind::NestedBindings(_)
1015        )
1016    }
1017
1018    /// Returns the [`TextRange`] of the definition target.
1019    ///
1020    /// A definition target would mainly be the node representing the place being defined i.e.,
1021    /// [`ast::ExprName`], [`ast::Identifier`], [`ast::ExprAttribute`] or [`ast::ExprSubscript`] but could also be other nodes.
1022    pub fn target_range(&self, module: &ParsedModuleRef) -> TextRange {
1023        match self {
1024            DefinitionKind::Import(import) => import.alias(module).range(),
1025            DefinitionKind::ImportFrom(import) => import.alias(module).range(),
1026            DefinitionKind::ImportFromSubmodule(import) => import.target_range(module),
1027            DefinitionKind::StarImport(import) => import.alias(module).range(),
1028            DefinitionKind::Function(function) => function.node(module).name.range(),
1029            DefinitionKind::Class(class) => class.node(module).name.range(),
1030            DefinitionKind::TypeAlias(type_alias) => type_alias.node(module).name.range(),
1031            DefinitionKind::NamedExpression(named) => named.node(module).target.range(),
1032            DefinitionKind::Assignment(assignment) => assignment.target(module).range(),
1033            DefinitionKind::AnnotatedAssignment(assign) => assign.target(module).range(),
1034            DefinitionKind::AugmentedAssignment(aug_assign) => {
1035                aug_assign.node(module).target.range()
1036            }
1037            DefinitionKind::DictKeyAssignment(dict_key_assignment) => {
1038                dict_key_assignment.key.node(module).range()
1039            }
1040            DefinitionKind::For(for_stmt) => for_stmt.target(module).range(),
1041            DefinitionKind::Comprehension(comp) => comp.target(module).range(),
1042            DefinitionKind::Parameter(parameter) => parameter.target_range(module),
1043            DefinitionKind::LambdaParameter(LambdaParameterDefinitionNodeKind {
1044                parameter,
1045                ..
1046            }) => parameter.target_range(module),
1047            DefinitionKind::WithItem(with_item) => with_item.target(module).range(),
1048            DefinitionKind::MatchPattern(match_pattern) => {
1049                match_pattern.identifier.node(module).range()
1050            }
1051            DefinitionKind::ExceptHandler(handler) => handler
1052                .node(module)
1053                .name
1054                .as_ref()
1055                .map_or_else(|| handler.node(module).range(), Ranged::range),
1056            DefinitionKind::TypeVar(type_var) => type_var.node(module).name.range(),
1057            DefinitionKind::ParamSpec(param_spec) => param_spec.node(module).name.range(),
1058            DefinitionKind::TypeVarTuple(type_var_tuple) => {
1059                type_var_tuple.node(module).name.range()
1060            }
1061            DefinitionKind::LoopHeader(loop_header) => loop_header.range(module),
1062            DefinitionKind::NestedBindings(nested_bindings) => {
1063                // TODO: We only return the `TextRange` of one of the `nonlocal` or `global`
1064                // declarations that affect this variable, even if there's more than one. We could
1065                // find a way to return all of them, or split up the synthetic definition somehow.
1066                nested_bindings.nested_declarations[0].range
1067            }
1068        }
1069    }
1070
1071    /// Returns the [`TextRange`] of the entire definition.
1072    pub fn full_range(&self, module: &ParsedModuleRef) -> TextRange {
1073        match self {
1074            DefinitionKind::Import(import) => import.alias(module).range(),
1075            DefinitionKind::ImportFrom(import) => import.alias(module).range(),
1076            DefinitionKind::ImportFromSubmodule(import) => import.module(module).range(),
1077            DefinitionKind::StarImport(import) => import.import(module).range(),
1078            DefinitionKind::Function(function) => function.node(module).range(),
1079            DefinitionKind::Class(class) => class.node(module).range(),
1080            DefinitionKind::TypeAlias(type_alias) => type_alias.node(module).range(),
1081            DefinitionKind::NamedExpression(named) => named.node(module).range(),
1082            DefinitionKind::Assignment(assign) => {
1083                let target_range = assign.target(module).range();
1084                let value_range = assign.value(module).range();
1085                target_range.cover(value_range)
1086            }
1087            DefinitionKind::AnnotatedAssignment(assign) => {
1088                let mut full_range = assign.target(module).range();
1089                full_range = full_range.cover(assign.annotation(module).range());
1090
1091                if let Some(value) = assign.value(module) {
1092                    full_range = full_range.cover(value.range());
1093                }
1094
1095                full_range
1096            }
1097            DefinitionKind::AugmentedAssignment(aug_assign) => aug_assign.node(module).range(),
1098            DefinitionKind::DictKeyAssignment(dict_key_assignment) => {
1099                dict_key_assignment.key.node(module).range()
1100            }
1101            DefinitionKind::For(for_stmt) => for_stmt.target(module).range(),
1102            DefinitionKind::Comprehension(comp) => comp.target(module).range(),
1103            DefinitionKind::Parameter(parameter) => parameter.full_range(module),
1104            DefinitionKind::LambdaParameter(LambdaParameterDefinitionNodeKind {
1105                parameter,
1106                ..
1107            }) => parameter.full_range(module),
1108            DefinitionKind::WithItem(with_item) => with_item.target(module).range(),
1109            DefinitionKind::MatchPattern(match_pattern) => {
1110                match_pattern.identifier.node(module).range()
1111            }
1112            DefinitionKind::ExceptHandler(handler) => handler.node(module).range(),
1113            DefinitionKind::TypeVar(type_var) => type_var.node(module).range(),
1114            DefinitionKind::ParamSpec(param_spec) => param_spec.node(module).range(),
1115            DefinitionKind::TypeVarTuple(type_var_tuple) => type_var_tuple.node(module).range(),
1116            DefinitionKind::LoopHeader(loop_header) => loop_header.range(module),
1117            DefinitionKind::NestedBindings(nested_bindings) => {
1118                // TODO: We only return the `TextRange` of one of the `nonlocal` or `global`
1119                // declarations that affect this variable, even if there's more than one. We could
1120                // find a way to return all of them, or split up the synthetic definition somehow.
1121                nested_bindings.nested_declarations[0].range
1122            }
1123        }
1124    }
1125
1126    pub fn category(&self, in_stub: bool, module: &ParsedModuleRef) -> DefinitionCategory {
1127        match self {
1128            // functions, classes, and imports always bind, and we consider them declarations
1129            DefinitionKind::Function(_)
1130            | DefinitionKind::Class(_)
1131            | DefinitionKind::TypeAlias(_)
1132            | DefinitionKind::Import(_)
1133            | DefinitionKind::ImportFrom(_)
1134            | DefinitionKind::StarImport(_)
1135            | DefinitionKind::TypeVar(_)
1136            | DefinitionKind::ParamSpec(_)
1137            | DefinitionKind::TypeVarTuple(_) => DefinitionCategory::DeclarationAndBinding,
1138            DefinitionKind::Parameter(parameter) => parameter.category(module),
1139            DefinitionKind::LambdaParameter(LambdaParameterDefinitionNodeKind {
1140                parameter,
1141                ..
1142            }) => parameter.category(module),
1143            // Annotated assignment is always a declaration. It is also a binding if there is a RHS
1144            // or if we are in a stub file. Unfortunately, it is common for stubs to omit even an `...` value placeholder.
1145            DefinitionKind::AnnotatedAssignment(ann_assign) => {
1146                if in_stub || ann_assign.has_value() {
1147                    DefinitionCategory::DeclarationAndBinding
1148                } else {
1149                    DefinitionCategory::Declaration
1150                }
1151            }
1152            // all of these bind values without declaring a type
1153            DefinitionKind::DictKeyAssignment(_)
1154            | DefinitionKind::NamedExpression(_)
1155            | DefinitionKind::Assignment(_)
1156            | DefinitionKind::AugmentedAssignment(_)
1157            | DefinitionKind::For(_)
1158            | DefinitionKind::Comprehension(_)
1159            | DefinitionKind::WithItem(_)
1160            | DefinitionKind::MatchPattern(_)
1161            | DefinitionKind::ImportFromSubmodule(_)
1162            | DefinitionKind::ExceptHandler(_)
1163            | DefinitionKind::LoopHeader(_)
1164            | DefinitionKind::NestedBindings(_) => DefinitionCategory::Binding,
1165        }
1166    }
1167
1168    /// Returns the value expression for assignment-based definitions.
1169    ///
1170    /// Returns `Some` for `Assignment` and `AnnotatedAssignment` (if it has a value),
1171    /// `None` for all other definition kinds.
1172    pub fn value<'ast>(&self, module: &'ast ParsedModuleRef) -> Option<&'ast ast::Expr> {
1173        match self {
1174            DefinitionKind::Assignment(assignment) => Some(assignment.value(module)),
1175            DefinitionKind::AnnotatedAssignment(assignment) => assignment.value(module),
1176            _ => None,
1177        }
1178    }
1179}
1180
1181#[derive(Copy, Clone, Debug, PartialEq, Hash, get_size2::GetSize)]
1182pub enum TargetKind<'db> {
1183    Sequence(UnpackPosition, Unpack<'db>),
1184    /// Name, attribute, or subscript.
1185    Single,
1186}
1187
1188impl<'db> TargetKind<'db> {
1189    fn from_unpack(unpack: Option<Unpack<'db>>, unpack_position: UnpackPosition) -> Self {
1190        match unpack {
1191            Some(unpack) => TargetKind::Sequence(unpack_position, unpack),
1192            None => TargetKind::Single,
1193        }
1194    }
1195}
1196
1197#[derive(Clone, Debug, get_size2::GetSize)]
1198pub struct StarImportDefinitionKind {
1199    node: AstNodeRef<ast::StmtImportFrom>,
1200    symbol_id: ScopedSymbolId,
1201}
1202
1203impl StarImportDefinitionKind {
1204    pub fn import<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::StmtImportFrom {
1205        self.node.node(module)
1206    }
1207
1208    pub fn alias<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Alias {
1209        // INVARIANT: for an invalid-syntax statement such as `from foo import *, bar, *`,
1210        // we only create a `StarImportDefinitionKind` for the *first* `*` alias in the names list.
1211        self.node
1212            .node(module)
1213            .names
1214            .iter()
1215            .find(|alias| &alias.name == "*")
1216            .expect(
1217                "The `StmtImportFrom` node of a `StarImportDefinitionKind` instance \
1218                should always have at least one `alias` with the name `*`.",
1219            )
1220    }
1221
1222    pub fn symbol_id(&self) -> ScopedSymbolId {
1223        self.symbol_id
1224    }
1225}
1226
1227#[derive(Clone, Debug, get_size2::GetSize, salsa::SalsaValue)]
1228pub struct MatchPatternDefinitionKind<'db> {
1229    pattern: AstNodeRef<ast::Pattern>,
1230    identifier: AstNodeRef<ast::Identifier>,
1231    predicate: PatternPredicate<'db>,
1232}
1233
1234impl<'db> MatchPatternDefinitionKind<'db> {
1235    pub fn pattern<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Pattern {
1236        self.pattern.node(module)
1237    }
1238
1239    pub fn predicate(&self) -> PatternPredicate<'db> {
1240        self.predicate
1241    }
1242}
1243
1244/// Note that the elements of a comprehension can be in different scopes.
1245/// If the definition target of a comprehension is a name, it is in the comprehension's scope.
1246/// But if the target is an attribute or subscript, its definition is not in the comprehension's scope;
1247/// it is in the scope in which the root variable is bound.
1248/// TODO: currently we don't model this correctly and simply assume that it is in a scope outside the comprehension.
1249#[derive(Clone, Debug, get_size2::GetSize, salsa::SalsaValue)]
1250pub struct ComprehensionDefinitionKind<'db> {
1251    unpack: Option<Unpack<'db>>,
1252    node: AstNodeRef<ast::Comprehension>,
1253    target: AstNodeRef<ast::Expr>,
1254    first: bool,
1255    is_async: bool,
1256    unpack_position: UnpackPosition,
1257}
1258
1259impl<'db> ComprehensionDefinitionKind<'db> {
1260    pub fn iterable<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Expr {
1261        &self.node.node(module).iter
1262    }
1263
1264    pub fn target_kind(&self) -> TargetKind<'db> {
1265        TargetKind::from_unpack(self.unpack, self.unpack_position)
1266    }
1267
1268    pub fn target<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Expr {
1269        self.target.node(module)
1270    }
1271
1272    pub fn is_first(&self) -> bool {
1273        self.first
1274    }
1275
1276    pub fn is_async(&self) -> bool {
1277        self.is_async
1278    }
1279}
1280
1281#[derive(Clone, Debug, get_size2::GetSize)]
1282pub enum ParameterDefinitionNodeKind {
1283    VariadicPositionalParameter(AstNodeRef<ast::Parameter>),
1284    VariadicKeywordParameter(AstNodeRef<ast::Parameter>),
1285    Parameter(AstNodeRef<ast::ParameterWithDefault>),
1286}
1287
1288impl ParameterDefinitionNodeKind {
1289    fn target_range(&self, module: &ParsedModuleRef) -> TextRange {
1290        match self {
1291            Self::VariadicPositionalParameter(parameter) => parameter.node(module).name.range(),
1292            Self::VariadicKeywordParameter(parameter) => parameter.node(module).name.range(),
1293            Self::Parameter(parameter) => parameter.node(module).parameter.name.range(),
1294        }
1295    }
1296
1297    fn full_range(&self, module: &ParsedModuleRef) -> TextRange {
1298        match self {
1299            Self::VariadicPositionalParameter(parameter) => parameter.node(module).range(),
1300            Self::VariadicKeywordParameter(parameter) => parameter.node(module).range(),
1301            Self::Parameter(parameter) => parameter.node(module).parameter.range(),
1302        }
1303    }
1304
1305    fn category(&self, module: &ParsedModuleRef) -> DefinitionCategory {
1306        match self {
1307            // a parameter always binds a value, but is only a declaration if annotated
1308            Self::VariadicPositionalParameter(parameter)
1309            | Self::VariadicKeywordParameter(parameter) => {
1310                if parameter.node(module).annotation.is_some() {
1311                    DefinitionCategory::DeclarationAndBinding
1312                } else {
1313                    DefinitionCategory::Binding
1314                }
1315            }
1316            // presence of a default is irrelevant, same logic as for a no-default parameter
1317            Self::Parameter(parameter_with_default) => {
1318                if parameter_with_default
1319                    .node(module)
1320                    .parameter
1321                    .annotation
1322                    .is_some()
1323                {
1324                    DefinitionCategory::DeclarationAndBinding
1325                } else {
1326                    DefinitionCategory::Binding
1327                }
1328            }
1329        }
1330    }
1331}
1332
1333#[derive(Clone, Debug, get_size2::GetSize)]
1334pub struct LambdaParameterDefinitionNodeKind {
1335    pub index: u32,
1336    pub lambda: AstNodeRef<ast::ExprLambda>,
1337    pub parameter: ParameterDefinitionNodeKind,
1338}
1339
1340#[derive(Clone, Debug, get_size2::GetSize)]
1341pub struct ImportDefinitionKind {
1342    node: AstNodeRef<ast::StmtImport>,
1343    alias_index: u32,
1344    is_reexported: bool,
1345}
1346
1347impl ImportDefinitionKind {
1348    pub fn import<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::StmtImport {
1349        self.node.node(module)
1350    }
1351
1352    pub fn alias<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Alias {
1353        &self.node.node(module).names[self.alias_index as usize]
1354    }
1355
1356    fn is_reexported(&self) -> bool {
1357        self.is_reexported
1358    }
1359}
1360
1361#[derive(Clone, Debug, get_size2::GetSize)]
1362pub struct ImportFromDefinitionKind {
1363    node: AstNodeRef<ast::StmtImportFrom>,
1364    alias_index: u32,
1365    is_reexported: bool,
1366}
1367
1368impl ImportFromDefinitionKind {
1369    pub fn import<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::StmtImportFrom {
1370        self.node.node(module)
1371    }
1372
1373    pub fn alias<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Alias {
1374        &self.node.node(module).names[self.alias_index as usize]
1375    }
1376
1377    fn is_reexported(&self) -> bool {
1378        self.is_reexported
1379    }
1380}
1381#[derive(Clone, Debug, get_size2::GetSize)]
1382pub struct ImportFromSubmoduleDefinitionKind {
1383    node: AstNodeRef<ast::StmtImportFrom>,
1384    module_index: u32,
1385}
1386
1387impl ImportFromSubmoduleDefinitionKind {
1388    pub fn import<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::StmtImportFrom {
1389        self.node.node(module)
1390    }
1391
1392    fn module<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Identifier {
1393        self.import(module)
1394            .module
1395            .as_ref()
1396            .expect("import-from submodule definitions should always have a module identifier")
1397    }
1398
1399    fn target_range(&self, module: &ParsedModuleRef) -> TextRange {
1400        let module_ident = self.module(module);
1401        let module_str = module_ident.as_str();
1402
1403        // Find the dot that terminates the target component.
1404        let Some((end_offset, _)) = module_str
1405            .match_indices('.')
1406            .nth(self.module_index as usize)
1407        else {
1408            // This shouldn't happen but just in case, provide a safe default
1409            return module_ident.range();
1410        };
1411
1412        // Find the start of the target component (after the previous dot, or string start).
1413        let start_offset = module_str[..end_offset].rfind('.').map_or(0, |pos| pos + 1);
1414
1415        let Ok(start) = TextSize::try_from(start_offset) else {
1416            return module_ident.range();
1417        };
1418        let Ok(end) = TextSize::try_from(end_offset) else {
1419            return module_ident.range();
1420        };
1421        TextRange::new(start, end) + module_ident.start()
1422    }
1423}
1424
1425#[derive(Clone, Debug, get_size2::GetSize, salsa::SalsaValue)]
1426pub struct AssignmentDefinitionKind<'db> {
1427    unpack: Option<Unpack<'db>>,
1428    value: AstNodeRef<ast::Expr>,
1429    target: AstNodeRef<ast::Expr>,
1430}
1431
1432impl<'db> AssignmentDefinitionKind<'db> {
1433    pub fn unpack(&self) -> Option<Unpack<'db>> {
1434        self.unpack
1435    }
1436
1437    pub fn value<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Expr {
1438        self.value.node(module)
1439    }
1440
1441    pub fn target<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Expr {
1442        self.target.node(module)
1443    }
1444}
1445
1446#[derive(Clone, Debug, get_size2::GetSize)]
1447pub struct AnnotatedAssignmentDefinitionKind {
1448    node: AstNodeRef<ast::StmtAnnAssign>,
1449    has_value: bool,
1450}
1451
1452impl AnnotatedAssignmentDefinitionKind {
1453    fn node<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::StmtAnnAssign {
1454        self.node.node(module)
1455    }
1456
1457    pub fn value<'ast>(&self, module: &'ast ParsedModuleRef) -> Option<&'ast ast::Expr> {
1458        self.node(module).value.as_deref()
1459    }
1460
1461    /// Returns whether this annotated assignment has a right-hand-side value.
1462    pub const fn has_value(&self) -> bool {
1463        self.has_value
1464    }
1465
1466    pub fn annotation<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Expr {
1467        &self.node(module).annotation
1468    }
1469
1470    pub fn target<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Expr {
1471        &self.node(module).target
1472    }
1473}
1474
1475#[derive(Clone, Debug, get_size2::GetSize, salsa::SalsaValue)]
1476pub struct DictKeyAssignmentKind<'db> {
1477    key: AstNodeRef<ast::Expr>,
1478    value: AstNodeRef<ast::Expr>,
1479    assignment: Definition<'db>,
1480}
1481
1482impl<'db> DictKeyAssignmentKind<'db> {
1483    pub fn key<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Expr {
1484        self.key.node(module)
1485    }
1486
1487    pub fn value<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Expr {
1488        self.value.node(module)
1489    }
1490
1491    pub fn assignment(&self) -> Definition<'db> {
1492        self.assignment
1493    }
1494}
1495
1496#[derive(Clone, Debug, get_size2::GetSize, salsa::SalsaValue)]
1497pub struct WithItemDefinitionKind<'db> {
1498    unpack: Option<Unpack<'db>>,
1499    item: AstNodeRef<ast::WithItem>,
1500    target: AstNodeRef<ast::Expr>,
1501    is_async: bool,
1502    unpack_position: UnpackPosition,
1503}
1504
1505impl<'db> WithItemDefinitionKind<'db> {
1506    pub fn context_expr<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Expr {
1507        &self.item.node(module).context_expr
1508    }
1509
1510    pub fn target_kind(&self) -> TargetKind<'db> {
1511        TargetKind::from_unpack(self.unpack, self.unpack_position)
1512    }
1513
1514    pub fn target<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Expr {
1515        self.target.node(module)
1516    }
1517
1518    pub const fn is_async(&self) -> bool {
1519        self.is_async
1520    }
1521}
1522
1523#[derive(Clone, Debug, get_size2::GetSize, salsa::SalsaValue)]
1524pub struct ForStmtDefinitionKind<'db> {
1525    unpack: Option<Unpack<'db>>,
1526    node: AstNodeRef<ast::StmtFor>,
1527    target: AstNodeRef<ast::Expr>,
1528    is_async: bool,
1529    unpack_position: UnpackPosition,
1530}
1531
1532impl<'db> ForStmtDefinitionKind<'db> {
1533    pub fn iterable<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Expr {
1534        &self.node.node(module).iter
1535    }
1536
1537    pub fn target_kind(&self) -> TargetKind<'db> {
1538        TargetKind::from_unpack(self.unpack, self.unpack_position)
1539    }
1540
1541    pub fn target<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Expr {
1542        self.target.node(module)
1543    }
1544
1545    pub const fn is_async(&self) -> bool {
1546        self.is_async
1547    }
1548}
1549
1550#[derive(Clone, Debug, get_size2::GetSize)]
1551pub struct ExceptHandlerDefinitionKind {
1552    handler: AstNodeRef<ast::ExceptHandlerExceptHandler>,
1553    is_star: bool,
1554}
1555
1556impl ExceptHandlerDefinitionKind {
1557    pub fn node<'ast>(
1558        &self,
1559        module: &'ast ParsedModuleRef,
1560    ) -> &'ast ast::ExceptHandlerExceptHandler {
1561        self.handler.node(module)
1562    }
1563
1564    pub fn handled_exceptions<'ast>(
1565        &self,
1566        module: &'ast ParsedModuleRef,
1567    ) -> Option<&'ast ast::Expr> {
1568        self.node(module).type_.as_deref()
1569    }
1570
1571    pub fn is_star(&self) -> bool {
1572        self.is_star
1573    }
1574}
1575
1576/// Definition kind for a loop header entry.
1577#[derive(Clone, Debug, get_size2::GetSize)]
1578pub struct LoopHeaderDefinitionKind {
1579    /// The `LoopHeader` is reserved before walking the loop and populated afterward.
1580    loop_header_id: LoopHeaderId,
1581    loop_stmt: LoopStmtKind,
1582    place: ScopedPlaceId,
1583}
1584
1585#[derive(Clone, Debug, get_size2::GetSize)]
1586pub(crate) enum LoopStmtKind {
1587    While(AstNodeRef<ast::StmtWhile>),
1588    For(AstNodeRef<ast::StmtFor>),
1589}
1590
1591impl LoopHeaderDefinitionKind {
1592    pub fn loop_header_id(&self) -> LoopHeaderId {
1593        self.loop_header_id
1594    }
1595
1596    pub fn place(&self) -> ScopedPlaceId {
1597        self.place
1598    }
1599
1600    fn range(&self, module: &ParsedModuleRef) -> TextRange {
1601        match &self.loop_stmt {
1602            LoopStmtKind::While(stmt) => stmt.node(module).range(),
1603            LoopStmtKind::For(stmt) => stmt.node(module).range(),
1604        }
1605    }
1606}
1607
1608#[derive(Clone, Debug, get_size2::GetSize)]
1609pub struct NestedBindingsDefinitionKind {
1610    pub name: Name,
1611    pub execution: NestedBindingExecution,
1612    // Note that in general this can include both `global` and `nonlocal` declarations from
1613    // different nested scopes, because we don't necessarily know at synthesis time which of those
1614    // kind will be visible in the current scope.
1615    pub nested_declarations: SmallVec<[crate::builder::NestedDeclaration; 1]>,
1616}
1617
1618impl NestedBindingsDefinitionKind {
1619    /// Returns every nested binding source and whether it was declared `global`.
1620    ///
1621    /// Use [`Self::visible_binding_sources`] when resolving the binding in a particular scope.
1622    fn binding_sources<'index, 'db>(
1623        &'index self,
1624        index: &'index SemanticIndex<'db>,
1625    ) -> impl Iterator<Item = (bool, BindingWithConstraintsIterator<'index, 'db>)> + 'index {
1626        self.nested_declarations.iter().filter_map(|declaration| {
1627            debug_assert!(declaration.is_bound);
1628            let symbol = index
1629                .place_table(declaration.file_scope_id)
1630                .symbol_id(&self.name)?;
1631            let use_def = index.use_def_map(declaration.file_scope_id);
1632            let bindings = match self.execution {
1633                NestedBindingExecution::Lazy => use_def.reachable_bindings(symbol.into()),
1634                NestedBindingExecution::Eager => use_def.end_of_scope_bindings(symbol.into()),
1635            };
1636            Some((declaration.is_global(), bindings))
1637        })
1638    }
1639
1640    /// Returns nested binding sources that can update the same variable as `scope`.
1641    ///
1642    /// A synthetic binding can collect both `global` and `nonlocal` writes to one name:
1643    ///
1644    /// ```python
1645    /// x = 0
1646    ///
1647    /// def outer():
1648    ///     x = 1
1649    ///
1650    ///     def change_global():
1651    ///         global x
1652    ///         x = 2
1653    ///
1654    ///     def change_nonlocal():
1655    ///         nonlocal x
1656    ///         x = 3
1657    /// ```
1658    ///
1659    /// Only `change_nonlocal` can update `outer`'s local `x`. Nested functions also cannot
1660    /// capture a class-local variable, so class scopes do not see nonlocal writes to their
1661    /// own bindings.
1662    pub fn visible_binding_sources<'index, 'db>(
1663        &'index self,
1664        index: &'index SemanticIndex<'db>,
1665        scope: FileScopeId,
1666    ) -> impl Iterator<Item = BindingWithConstraintsIterator<'index, 'db>> + 'index {
1667        let symbol_id = index.place_table(scope).symbol_id(&self.name);
1668        let sees_global = symbol_id
1669            .is_some_and(|symbol_id| index.symbol_resolves_to_global_scope(symbol_id, scope));
1670        let sees_nonlocal = !sees_global
1671            && symbol_id.is_some_and(|symbol_id| {
1672                !(index.scope(scope).kind().is_class()
1673                    && index.place_table(scope).symbol(symbol_id).is_local())
1674            });
1675
1676        self.binding_sources(index)
1677            .filter_map(move |(is_global, bindings)| {
1678                (if is_global {
1679                    sees_global
1680                } else {
1681                    sees_nonlocal
1682                })
1683                .then_some(bindings)
1684            })
1685    }
1686}
1687
1688/// Describes when writes from a nested scope can affect its containing scope.
1689#[derive(Copy, Clone, Debug, Eq, PartialEq, get_size2::GetSize)]
1690pub enum NestedBindingExecution {
1691    /// The nested scope can run later or repeatedly, as with a function body.
1692    Lazy,
1693    /// The nested scope is modeled as running while evaluating the containing expression.
1694    Eager,
1695}
1696
1697#[derive(
1698    Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, get_size2::GetSize, salsa::SalsaValue,
1699)]
1700pub struct DefinitionNodeKey(NodeKey);
1701
1702impl DefinitionNodeKey {
1703    pub(crate) fn from_node_ref(node: ast::AnyNodeRef<'_>) -> Self {
1704        match node {
1705            ast::AnyNodeRef::ParameterWithDefault(parameter) => parameter.into(),
1706            _ => Self(NodeKey::from_node(node)),
1707        }
1708    }
1709
1710    pub fn from_assignment(node: &ast::StmtAssign) -> impl Iterator<Item = DefinitionNodeKey> {
1711        node.targets
1712            .iter()
1713            .map(|target| Self(NodeKey::from_node(target)))
1714    }
1715}
1716
1717impl From<&ast::Alias> for DefinitionNodeKey {
1718    fn from(node: &ast::Alias) -> Self {
1719        Self(NodeKey::from_node(node))
1720    }
1721}
1722
1723impl From<&ast::StmtImportFrom> for DefinitionNodeKey {
1724    fn from(node: &ast::StmtImportFrom) -> Self {
1725        Self(NodeKey::from_node(node))
1726    }
1727}
1728
1729impl From<&ast::StmtFunctionDef> for DefinitionNodeKey {
1730    fn from(node: &ast::StmtFunctionDef) -> Self {
1731        Self(NodeKey::from_node(node))
1732    }
1733}
1734
1735impl From<&ast::StmtClassDef> for DefinitionNodeKey {
1736    fn from(node: &ast::StmtClassDef) -> Self {
1737        Self(NodeKey::from_node(node))
1738    }
1739}
1740
1741impl From<&ast::StmtTypeAlias> for DefinitionNodeKey {
1742    fn from(node: &ast::StmtTypeAlias) -> Self {
1743        Self(NodeKey::from_node(node))
1744    }
1745}
1746
1747impl From<&ast::ExprName> for DefinitionNodeKey {
1748    fn from(node: &ast::ExprName) -> Self {
1749        Self(NodeKey::from_node(node))
1750    }
1751}
1752
1753impl From<&ast::ExprAttribute> for DefinitionNodeKey {
1754    fn from(node: &ast::ExprAttribute) -> Self {
1755        Self(NodeKey::from_node(node))
1756    }
1757}
1758
1759impl From<&ast::ExprSubscript> for DefinitionNodeKey {
1760    fn from(node: &ast::ExprSubscript) -> Self {
1761        Self(NodeKey::from_node(node))
1762    }
1763}
1764
1765impl From<&ast::ExprNamed> for DefinitionNodeKey {
1766    fn from(node: &ast::ExprNamed) -> Self {
1767        Self(NodeKey::from_node(node))
1768    }
1769}
1770
1771impl From<&ast::StmtAnnAssign> for DefinitionNodeKey {
1772    fn from(node: &ast::StmtAnnAssign) -> Self {
1773        Self(NodeKey::from_node(node))
1774    }
1775}
1776
1777impl From<&ast::StmtAugAssign> for DefinitionNodeKey {
1778    fn from(node: &ast::StmtAugAssign) -> Self {
1779        Self(NodeKey::from_node(node))
1780    }
1781}
1782
1783impl From<&ast::StmtWhile> for DefinitionNodeKey {
1784    fn from(node: &ast::StmtWhile) -> Self {
1785        Self(NodeKey::from_node(node))
1786    }
1787}
1788
1789impl From<&ast::StmtFor> for DefinitionNodeKey {
1790    fn from(node: &ast::StmtFor) -> Self {
1791        Self(NodeKey::from_node(node))
1792    }
1793}
1794
1795impl From<&ast::Parameter> for DefinitionNodeKey {
1796    fn from(node: &ast::Parameter) -> Self {
1797        Self(NodeKey::from_node(node))
1798    }
1799}
1800
1801impl From<&ast::ParameterWithDefault> for DefinitionNodeKey {
1802    fn from(node: &ast::ParameterWithDefault) -> Self {
1803        Self(NodeKey::from_node(&node.parameter))
1804    }
1805}
1806
1807impl From<ast::AnyParameterRef<'_>> for DefinitionNodeKey {
1808    fn from(value: ast::AnyParameterRef) -> Self {
1809        Self(match value {
1810            ast::AnyParameterRef::Variadic(node) => NodeKey::from_node(node),
1811            ast::AnyParameterRef::NonVariadic(node) => NodeKey::from_node(&node.parameter),
1812        })
1813    }
1814}
1815
1816impl From<&ast::Identifier> for DefinitionNodeKey {
1817    fn from(identifier: &ast::Identifier) -> Self {
1818        Self(NodeKey::from_node(identifier))
1819    }
1820}
1821
1822impl From<&ast::ExceptHandlerExceptHandler> for DefinitionNodeKey {
1823    fn from(handler: &ast::ExceptHandlerExceptHandler) -> Self {
1824        Self(NodeKey::from_node(handler))
1825    }
1826}
1827
1828impl From<&ast::TypeParamTypeVar> for DefinitionNodeKey {
1829    fn from(value: &ast::TypeParamTypeVar) -> Self {
1830        Self(NodeKey::from_node(value))
1831    }
1832}
1833
1834impl From<&ast::TypeParamParamSpec> for DefinitionNodeKey {
1835    fn from(value: &ast::TypeParamParamSpec) -> Self {
1836        Self(NodeKey::from_node(value))
1837    }
1838}
1839
1840impl From<&ast::TypeParamTypeVarTuple> for DefinitionNodeKey {
1841    fn from(value: &ast::TypeParamTypeVarTuple) -> Self {
1842        Self(NodeKey::from_node(value))
1843    }
1844}
1845
1846impl<T> From<&AstNodeRef<T>> for DefinitionNodeKey
1847where
1848    for<'a> &'a T: Into<DefinitionNodeKey>,
1849{
1850    fn from(value: &AstNodeRef<T>) -> Self {
1851        Self(NodeKey::from_node_ref(value))
1852    }
1853}