Skip to main content

ty_python_core/
ast_ids.rs

1use rustc_hash::FxHashMap;
2
3use ruff_index::{IndexVec, newtype_index};
4use ruff_python_ast as ast;
5use ruff_python_ast::ExprRef;
6
7use crate::Db;
8use crate::ProgramFile;
9use crate::frozen::FrozenMap;
10use crate::scope::FileScopeId;
11use crate::semantic_index;
12
13pub use node_key::ExpressionNodeKey;
14
15/// AST ids for a file.
16///
17/// Use IDs are assigned per scope while building the semantic index. This keeps the property that
18/// IDs of outer scopes are unaffected by changes in inner scopes. Node IDs are unique within a
19/// file, so the final reverse lookup can merge the per-scope maps into a single map.
20///
21/// For example, we don't want that adding new statements to `foo` changes the statement id of `x = foo()` in:
22///
23/// ```python
24/// def foo():
25///     return 5
26///
27/// x = foo()
28/// ```
29#[derive(Debug, get_size2::GetSize)]
30pub(crate) struct AstIds {
31    /// Maps expressions which "use" a place (that is, [`ast::ExprName`], [`ast::ExprAttribute`] or [`ast::ExprSubscript`]) to a use id.
32    uses_map: FrozenMap<ExpressionNodeKey, ScopedUseId>,
33}
34
35impl AstIds {
36    pub(super) fn from_builders(builders: IndexVec<FileScopeId, AstIdsBuilder>) -> Self {
37        let capacity = builders.iter().map(|builder| builder.uses_map.len()).sum();
38        let mut uses_map = Vec::with_capacity(capacity);
39
40        for builder in builders {
41            uses_map.extend(builder.uses_map);
42        }
43
44        let uses_map = FrozenMap::from_entries(uses_map);
45        debug_assert!(
46            uses_map.keys().is_sorted_by(|left, right| left < right),
47            "AST ID builders must contain disjoint keys"
48        );
49
50        Self { uses_map }
51    }
52
53    fn use_id(&self, key: impl Into<ExpressionNodeKey>) -> ScopedUseId {
54        self.uses_map[&key.into()]
55    }
56}
57
58fn ast_ids<'db>(db: &'db dyn Db, file: ProgramFile<'db>) -> &'db AstIds {
59    semantic_index(db, file).ast_ids()
60}
61
62/// Uniquely identifies a use of a name in a [`crate::FileScopeId`].
63#[newtype_index]
64#[derive(Ord, PartialOrd, get_size2::GetSize)]
65pub struct ScopedUseId;
66
67pub trait HasScopedUseId {
68    /// Returns the ID that uniquely identifies the use in its scope.
69    fn scoped_use_id(&self, db: &dyn Db, file: ProgramFile<'_>) -> ScopedUseId;
70}
71
72impl HasScopedUseId for ast::Identifier {
73    fn scoped_use_id(&self, db: &dyn Db, file: ProgramFile<'_>) -> ScopedUseId {
74        let ast_ids = ast_ids(db, file);
75        ast_ids.use_id(self)
76    }
77}
78
79impl HasScopedUseId for ast::ExprName {
80    fn scoped_use_id(&self, db: &dyn Db, file: ProgramFile<'_>) -> ScopedUseId {
81        let expression_ref = ExprRef::from(self);
82        expression_ref.scoped_use_id(db, file)
83    }
84}
85
86impl HasScopedUseId for ast::ExprAttribute {
87    fn scoped_use_id(&self, db: &dyn Db, file: ProgramFile<'_>) -> ScopedUseId {
88        let expression_ref = ExprRef::from(self);
89        expression_ref.scoped_use_id(db, file)
90    }
91}
92
93impl HasScopedUseId for ast::ExprSubscript {
94    fn scoped_use_id(&self, db: &dyn Db, file: ProgramFile<'_>) -> ScopedUseId {
95        let expression_ref = ExprRef::from(self);
96        expression_ref.scoped_use_id(db, file)
97    }
98}
99
100impl HasScopedUseId for ast::Keyword {
101    fn scoped_use_id(&self, db: &dyn Db, file: ProgramFile<'_>) -> ScopedUseId {
102        let ast_ids = ast_ids(db, file);
103        ast_ids.use_id(self)
104    }
105}
106
107impl HasScopedUseId for ast::ExprRef<'_> {
108    fn scoped_use_id(&self, db: &dyn Db, file: ProgramFile<'_>) -> ScopedUseId {
109        let ast_ids = ast_ids(db, file);
110        ast_ids.use_id(*self)
111    }
112}
113
114#[derive(Debug, Default)]
115pub(super) struct AstIdsBuilder {
116    uses_map: FxHashMap<ExpressionNodeKey, ScopedUseId>,
117}
118
119impl AstIdsBuilder {
120    /// Adds `expr` to the use ids map and returns its id.
121    pub(super) fn record_use(&mut self, expr: impl Into<ExpressionNodeKey>) -> ScopedUseId {
122        let use_id = self.uses_map.len().into();
123        self.uses_map.insert(expr.into(), use_id);
124        use_id
125    }
126
127    pub(super) fn try_use_id(&self, key: impl Into<ExpressionNodeKey>) -> Option<ScopedUseId> {
128        self.uses_map.get(&key.into()).copied()
129    }
130}
131
132/// Node key that can only be constructed for expressions.
133pub(crate) mod node_key {
134    use ruff_python_ast as ast;
135
136    use crate::{ast_node_ref::AstNodeRef, node_key::NodeKey};
137
138    #[derive(
139        Copy,
140        Clone,
141        Eq,
142        PartialEq,
143        Ord,
144        PartialOrd,
145        Hash,
146        Debug,
147        get_size2::GetSize,
148        salsa::SalsaValue,
149    )]
150    pub struct ExpressionNodeKey(NodeKey);
151
152    impl From<ast::ExprRef<'_>> for ExpressionNodeKey {
153        fn from(value: ast::ExprRef<'_>) -> Self {
154            Self(NodeKey::from_node(value))
155        }
156    }
157
158    impl From<&ast::Expr> for ExpressionNodeKey {
159        fn from(value: &ast::Expr) -> Self {
160            Self(NodeKey::from_node(value))
161        }
162    }
163
164    impl From<&Box<ast::Expr>> for ExpressionNodeKey {
165        fn from(value: &Box<ast::Expr>) -> Self {
166            Self(NodeKey::from_node(&**value))
167        }
168    }
169
170    impl From<&ast::ExprCall> for ExpressionNodeKey {
171        fn from(value: &ast::ExprCall) -> Self {
172            Self(NodeKey::from_node(value))
173        }
174    }
175
176    impl From<&ast::ExprLambda> for ExpressionNodeKey {
177        fn from(value: &ast::ExprLambda) -> Self {
178            Self(NodeKey::from_node(value))
179        }
180    }
181
182    impl From<&ast::Identifier> for ExpressionNodeKey {
183        fn from(value: &ast::Identifier) -> Self {
184            Self(NodeKey::from_node(value))
185        }
186    }
187
188    impl From<&ast::Keyword> for ExpressionNodeKey {
189        fn from(value: &ast::Keyword) -> Self {
190            Self(NodeKey::from_node(value))
191        }
192    }
193
194    impl<T> From<&AstNodeRef<T>> for ExpressionNodeKey {
195        fn from(value: &AstNodeRef<T>) -> Self {
196            Self(NodeKey::from_node_ref(value))
197        }
198    }
199}