Skip to content

Commit 49347d0

Browse files
Keeping SyntaxNode parent's data id for optimizations
1 parent 148a496 commit 49347d0

File tree

4 files changed

+73
-33
lines changed
  • crates
    • cairo-lang-defs/src
    • cairo-lang-semantic/src/resolve
    • cairo-lang-starknet/src/plugin/starknet_module
    • cairo-lang-syntax/src/node

4 files changed

+73
-33
lines changed

crates/cairo-lang-defs/src/ids.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -787,8 +787,7 @@ define_language_element_id_basic!(GenericParamId, GenericParamLongId, ast::Gener
787787
impl<'db> GenericParamLongId<'db> {
788788
pub fn name(&self, db: &'db dyn Database) -> Option<SmolStrId<'db>> {
789789
let node = self.1.0.0;
790-
// TODO(eytan-starkware): have a non-query accessor to parent and use it here.
791-
assert!(node.parent(db).is_some());
790+
assert!(!node.is_root());
792791
let key_fields = node.key_fields(db);
793792
let kind = node.kind(db);
794793
require(!matches!(
@@ -805,7 +804,7 @@ impl<'db> GenericParamLongId<'db> {
805804
}
806805
pub fn kind(&self, db: &dyn Database) -> GenericKind {
807806
let node = self.1.0.0;
808-
assert!(node.parent(db).is_some());
807+
assert!(!node.is_root());
809808
let kind = node.kind(db);
810809
match kind {
811810
SyntaxKind::GenericParamType => GenericKind::Type,
@@ -855,7 +854,7 @@ impl<'db> GenericParamId<'db> {
855854
pub fn format(&self, db: &'db dyn Database) -> SmolStrId<'db> {
856855
let long_ids = self.long(db);
857856
let node = long_ids.1.0.0;
858-
assert!(node.parent(db).is_some());
857+
assert!(!node.is_root());
859858
let key_fields = node.key_fields(db);
860859
let kind = node.kind(db);
861860

crates/cairo-lang-semantic/src/resolve/mod.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -571,9 +571,7 @@ impl<'db> Resolver<'db> {
571571
&& last.identifier(self.db).long(self.db) == SELF_PARAM_KW
572572
{
573573
// If the `self` keyword is used in a non-multi-use path, report an error.
574-
if use_path.as_syntax_node().parent(self.db).unwrap().kind(self.db)
575-
!= SyntaxKind::UsePathList
576-
{
574+
if use_path.as_syntax_node().parent_kind(self.db).unwrap() != SyntaxKind::UsePathList {
577575
diagnostics.report(use_path.stable_ptr(self.db), UseSelfNonMulti);
578576
}
579577
segments.segments.pop();

crates/cairo-lang-starknet/src/plugin/starknet_module/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ fn grand_grand_parent_starknet_module<'db>(
246246
) -> Option<(ast::ItemModule<'db>, StarknetModuleKind, ast::Attribute<'db>)> {
247247
// Get the containing module node. The parent is the item list, the grand parent is the module
248248
// body, and the grand grand parent is the module.
249-
let module_node = item_node.parent(db)?.parent(db)?.parent(db)?;
249+
let module_node = item_node.grandparent(db)?.parent(db)?;
250250
let module_ast = ast::ItemModule::cast(db, module_node)?;
251251
let (module_kind, attr) = StarknetModuleKind::from_module(db, &module_ast)?;
252252
Some((module_ast, module_kind, attr))

crates/cairo-lang-syntax/src/node/mod.rs

Lines changed: 68 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,16 @@ struct SyntaxNodeData<'a> {
6565
id: SyntaxNodeId<'a>,
6666
}
6767

68+
impl<'db> SyntaxNodeData<'db> {
69+
/// Gets the kind of the given node's parent if it exists.
70+
pub fn parent(&self, db: &'db dyn Database) -> Option<SyntaxNode<'db>> {
71+
match self.id(db) {
72+
SyntaxNodeId::Root(_) => None,
73+
SyntaxNodeId::Child { parent, .. } => Some(*parent),
74+
}
75+
}
76+
}
77+
6878
impl<'db> cairo_lang_debug::DebugWithDb<'db> for SyntaxNodeData<'db> {
6979
type Db = dyn Database;
7080
fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'db Self::Db) -> std::fmt::Result {
@@ -82,57 +92,81 @@ impl<'db> cairo_lang_debug::DebugWithDb<'db> for SyntaxNodeData<'db> {
8292
/// tracked functions to ensure uniqueness of SyntaxNodes.
8393
/// Use `SyntaxNode::new_root` or `SyntaxNode::new_root_with_offset` to create root nodes.
8494
#[derive(Clone, Copy, PartialEq, Eq, Hash, salsa::Update)]
85-
pub struct SyntaxNode<'a>(SyntaxNodeData<'a>);
95+
pub struct SyntaxNode<'a> {
96+
data: SyntaxNodeData<'a>,
97+
/// Cached parent data to avoid database lookups. None for root nodes.
98+
parent: Option<SyntaxNodeData<'a>>,
99+
}
86100

87101
impl<'db> std::fmt::Debug for SyntaxNode<'db> {
88102
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89-
write!(f, "SyntaxNode({:x})", self.0.as_id().index())
103+
write!(f, "SyntaxNode({:x})", self.data.as_id().index())
90104
}
91105
}
92106

93107
impl<'db> cairo_lang_debug::DebugWithDb<'db> for SyntaxNode<'db> {
94108
type Db = dyn Database;
95109
fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'db Self::Db) -> std::fmt::Result {
96-
self.0.fmt(f, db)
110+
self.data.fmt(f, db)
97111
}
98112
}
99113

100114
impl<'db> SyntaxNode<'db> {
101115
/// Get the offset of this syntax node from the beginning of the file.
102116
pub fn offset(self, db: &'db dyn Database) -> TextOffset {
103-
self.0.offset(db)
117+
self.data.offset(db)
104118
}
105119

106120
/// Get the parent syntax node, if any.
107121
pub fn parent(self, db: &'db dyn Database) -> Option<SyntaxNode<'db>> {
108-
match self.0.id(db) {
122+
match self.data.id(db) {
109123
SyntaxNodeId::Child { parent, .. } => Some(*parent),
110124
SyntaxNodeId::Root(_) => None,
111125
}
112126
}
113127

128+
/// Get the grandparent syntax node, if any.
129+
/// This uses a cached parent when available, avoiding some database lookups.
130+
pub fn grandparent(self, db: &'db dyn Database) -> Option<SyntaxNode<'db>> {
131+
self.parent?.parent(db)
132+
}
133+
134+
/// Check if this syntax node is the root of the syntax tree.
135+
pub fn is_root(self) -> bool {
136+
self.parent.is_none()
137+
}
138+
114139
/// Get the stable pointer for this syntax node.
115140
pub fn stable_ptr(self, _db: &'db dyn Database) -> SyntaxStablePtrId<'db> {
116141
SyntaxStablePtrId(self)
117142
}
118143

119144
pub fn raw_id(&self, db: &'db dyn Database) -> &'db SyntaxNodeId<'db> {
120-
self.0.id(db)
145+
self.data.id(db)
121146
}
122147

123148
/// Get the key fields of this syntax node. These define the unique identifier of the node.
124149
pub fn key_fields(self, db: &'db dyn Database) -> &'db [GreenId<'db>] {
125-
match self.0.id(db) {
150+
match self.data.id(db) {
126151
SyntaxNodeId::Child { key_fields, .. } => key_fields,
127152
SyntaxNodeId::Root(_) => &[],
128153
}
129154
}
130155

131156
/// Returns the file id of the file containing this node.
132157
pub fn file_id(&self, db: &'db dyn Database) -> FileId<'db> {
133-
match self.0.id(db) {
134-
SyntaxNodeId::Child { parent, .. } => parent.file_id(db),
158+
// Use cached parent if available to avoid database lookup
159+
if let Some(parent_data) = self.parent {
160+
return match parent_data.id(db) {
161+
SyntaxNodeId::Child { parent, .. } => parent.file_id(db),
162+
SyntaxNodeId::Root(file_id) => *file_id,
163+
};
164+
}
165+
match self.data.id(db) {
135166
SyntaxNodeId::Root(file_id) => *file_id,
167+
SyntaxNodeId::Child { .. } => {
168+
unreachable!("Parent already checked and found to not exist.")
169+
}
136170
}
137171
}
138172

@@ -142,18 +176,22 @@ impl<'db> SyntaxNode<'db> {
142176
/// n = 2: return the grand parent.
143177
/// And so on...
144178
/// Assumes that the `n`th parent exists. Panics otherwise.
145-
pub fn nth_parent<'r: 'db>(&self, db: &'r dyn Database, n: usize) -> SyntaxNode<'db> {
146-
let mut ptr = *self;
147-
for _ in 0..n {
148-
ptr = ptr.parent(db).unwrap_or_else(|| {
149-
panic!(
150-
"N'th parent did not exist. File {} Offset {:?}",
151-
self.file_id(db).long(db).full_path(db),
152-
self.offset(db)
153-
)
154-
});
179+
pub fn nth_parent<'r: 'db>(&self, db: &'r dyn Database, mut n: usize) -> SyntaxNode<'db> {
180+
let mut ptr = Some(*self);
181+
while ptr.is_some() && n > 1 {
182+
ptr = ptr.and_then(|p| p.grandparent(db));
183+
n -= 2;
155184
}
156-
ptr
185+
if n == 1 {
186+
ptr = ptr.and_then(|p| p.parent(db));
187+
}
188+
ptr.unwrap_or_else(|| {
189+
panic!(
190+
"N'th parent did not exist. File {} Offset {:?}",
191+
self.file_id(db).long(db).full_path(db),
192+
self.offset(db)
193+
)
194+
})
157195
}
158196
}
159197

@@ -165,7 +203,12 @@ pub fn new_syntax_node<'db>(
165203
offset: TextOffset,
166204
id: SyntaxNodeId<'db>,
167205
) -> SyntaxNode<'db> {
168-
SyntaxNode(SyntaxNodeData::new(db, green, offset, id))
206+
let parent = match &id {
207+
SyntaxNodeId::Child { parent, .. } => Some(parent.data),
208+
SyntaxNodeId::Root(_) => None,
209+
};
210+
let data = SyntaxNodeData::new(db, green, offset, id);
211+
SyntaxNode { data, parent }
169212
}
170213

171214
// Construction methods
@@ -212,7 +255,7 @@ impl<'a> SyntaxNode<'a> {
212255

213256
/// Returns the green node of the syntax node.
214257
pub fn green_node(&self, db: &'a dyn Database) -> &'a GreenNode<'a> {
215-
self.0.green(db).long(db)
258+
self.data.green(db).long(db)
216259
}
217260

218261
/// Returns the span of the syntax node without trivia.
@@ -507,17 +550,17 @@ impl<'a> SyntaxNode<'a> {
507550

508551
/// Gets the kind of the given node's parent if it exists.
509552
pub fn parent_kind(&self, db: &dyn Database) -> Option<SyntaxKind> {
510-
Some(self.parent(db)?.kind(db))
553+
Some(self.parent?.green(db).long(db).kind)
511554
}
512555

513556
/// Gets the kind of the given node's grandparent if it exists.
514557
pub fn grandparent_kind(&self, db: &dyn Database) -> Option<SyntaxKind> {
515-
Some(self.parent(db)?.parent(db)?.kind(db))
558+
self.parent(db)?.parent_kind(db)
516559
}
517560

518561
/// Gets the kind of the given node's grandgrandparent if it exists.
519562
pub fn grandgrandparent_kind(&self, db: &dyn Database) -> Option<SyntaxKind> {
520-
Some(self.parent(db)?.parent(db)?.parent(db)?.kind(db))
563+
self.grandparent(db)?.parent_kind(db)
521564
}
522565
}
523566

0 commit comments

Comments
 (0)