Skip to main content

ty_python_core/
expression.rs

1use crate::ast_node_ref::AstNodeRef;
2use crate::db::Db;
3use crate::scope::ScopeId;
4use crate::{Program, ProgramFile};
5use ruff_db::PythonFile;
6use ruff_db::files::File;
7use ruff_python_ast as ast;
8use salsa;
9
10/// Whether or not this expression should be inferred as a normal expression or
11/// a type expression. For example, in `self.x: <annotation> = <value>`, the
12/// `<annotation>` is inferred as a type expression, while `<value>` is inferred
13/// as a normal expression.
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, get_size2::GetSize)]
15pub enum ExpressionKind {
16    Normal,
17    TypeExpression,
18}
19
20/// An independently type-inferable expression.
21///
22/// Includes constraint expressions (e.g. if tests) and the RHS of an unpacking assignment.
23///
24/// ## Module-local type
25/// This type should not be used as part of any cross-module API because
26/// it holds a reference to the AST node. Range-offset changes
27/// then propagate through all usages, and deserialization requires
28/// reparsing the entire module.
29///
30/// E.g. don't use this type in:
31///
32/// * a return type of a cross-module query
33/// * a field of a type that is a return type of a cross-module query
34/// * an argument of a cross-module query
35#[salsa::tracked(debug, heap_size=ruff_memory_usage::heap_size)]
36pub struct Expression<'db> {
37    /// The scope in which the expression occurs.
38    ///
39    /// Storing the interned scope avoids retaining the file and file-local scope separately, at
40    /// the cost of database lookups when either of those values is needed.
41    #[returns(copy)]
42    pub scope_id: ScopeId<'db>,
43
44    /// The expression node.
45    #[no_eq]
46    #[tracked]
47    #[returns(ref)]
48    pub node_ref: AstNodeRef<ast::Expr>,
49
50    /// An assignment statement, if this expression is immediately used as the rhs of that
51    /// assignment.
52    ///
53    /// (Note that this is the _immediately_ containing assignment — if a complex expression is
54    /// assigned to some target, only the outermost expression node has this set. The inner
55    /// expressions are used to build up the assignment result, and are not "immediately assigned"
56    /// to the target, and so have `None` for this field.)
57    #[no_eq]
58    #[tracked]
59    #[returns(clone)]
60    pub assigned_to: Option<AstNodeRef<ast::StmtAssign>>,
61
62    /// Should this expression be inferred as a normal expression or a type expression?
63    #[returns(copy)]
64    pub kind: ExpressionKind,
65}
66
67// The Salsa heap is tracked separately.
68impl get_size2::GetSize for Expression<'_> {}
69
70impl<'db> Expression<'db> {
71    pub fn scope(self, db: &'db dyn Db) -> ScopeId<'db> {
72        self.scope_id(db)
73    }
74
75    pub fn file(self, db: &'db dyn Db) -> File {
76        self.scope_id(db).file(db)
77    }
78
79    pub fn python_file(self, db: &'db dyn Db) -> PythonFile<'db> {
80        self.scope_id(db).python_file(db)
81    }
82
83    pub fn program_file(self, db: &'db dyn Db) -> ProgramFile<'db> {
84        self.scope_id(db).program_file(db)
85    }
86
87    pub fn program(self, db: &'db dyn Db) -> Program<'db> {
88        self.scope_id(db).program(db)
89    }
90}