diff options
Diffstat (limited to 'crates/ra_hir/src/ids.rs')
-rw-r--r-- | crates/ra_hir/src/ids.rs | 197 |
1 files changed, 45 insertions, 152 deletions
diff --git a/crates/ra_hir/src/ids.rs b/crates/ra_hir/src/ids.rs index 18401f865..eb9939df7 100644 --- a/crates/ra_hir/src/ids.rs +++ b/crates/ra_hir/src/ids.rs | |||
@@ -1,16 +1,15 @@ | |||
1 | use std::{ | 1 | use std::{ |
2 | marker::PhantomData, | ||
3 | hash::{Hash, Hasher}, | 2 | hash::{Hash, Hasher}, |
4 | sync::Arc, | 3 | sync::Arc, |
5 | }; | 4 | }; |
6 | 5 | ||
7 | use ra_db::{LocationInterner, FileId}; | 6 | use ra_db::{LocationInterner, FileId}; |
8 | use ra_syntax::{TreeArc, SyntaxNode, SourceFile, AstNode, SyntaxNodePtr, ast}; | 7 | use ra_syntax::{TreeArc, SourceFile, AstNode, ast}; |
9 | use ra_arena::{Arena, RawId, ArenaId, impl_arena_id}; | 8 | use ra_arena::{RawId, ArenaId, impl_arena_id}; |
9 | use mbe::MacroRules; | ||
10 | 10 | ||
11 | use crate::{ | 11 | use crate::{ |
12 | Module, | 12 | Module, DefDatabase, AstId, FileAstId, |
13 | DefDatabase, | ||
14 | }; | 13 | }; |
15 | 14 | ||
16 | #[derive(Debug, Default)] | 15 | #[derive(Debug, Default)] |
@@ -22,7 +21,7 @@ pub struct HirInterner { | |||
22 | consts: LocationInterner<ItemLoc<ast::ConstDef>, ConstId>, | 21 | consts: LocationInterner<ItemLoc<ast::ConstDef>, ConstId>, |
23 | statics: LocationInterner<ItemLoc<ast::StaticDef>, StaticId>, | 22 | statics: LocationInterner<ItemLoc<ast::StaticDef>, StaticId>, |
24 | traits: LocationInterner<ItemLoc<ast::TraitDef>, TraitId>, | 23 | traits: LocationInterner<ItemLoc<ast::TraitDef>, TraitId>, |
25 | types: LocationInterner<ItemLoc<ast::TypeAliasDef>, TypeId>, | 24 | types: LocationInterner<ItemLoc<ast::TypeAliasDef>, TypeAliasId>, |
26 | } | 25 | } |
27 | 26 | ||
28 | impl HirInterner { | 27 | impl HirInterner { |
@@ -68,7 +67,7 @@ impl HirFileId { | |||
68 | HirFileIdRepr::File(file_id) => file_id, | 67 | HirFileIdRepr::File(file_id) => file_id, |
69 | HirFileIdRepr::Macro(macro_call_id) => { | 68 | HirFileIdRepr::Macro(macro_call_id) => { |
70 | let loc = macro_call_id.loc(db); | 69 | let loc = macro_call_id.loc(db); |
71 | loc.source_item_id.file_id.original_file(db) | 70 | loc.ast_id.file_id().original_file(db) |
72 | } | 71 | } |
73 | } | 72 | } |
74 | } | 73 | } |
@@ -83,7 +82,10 @@ impl HirFileId { | |||
83 | } | 82 | } |
84 | } | 83 | } |
85 | 84 | ||
86 | pub(crate) fn hir_parse(db: &impl DefDatabase, file_id: HirFileId) -> TreeArc<SourceFile> { | 85 | pub(crate) fn hir_parse_query( |
86 | db: &impl DefDatabase, | ||
87 | file_id: HirFileId, | ||
88 | ) -> TreeArc<SourceFile> { | ||
87 | match file_id.0 { | 89 | match file_id.0 { |
88 | HirFileIdRepr::File(file_id) => db.parse(file_id), | 90 | HirFileIdRepr::File(file_id) => db.parse(file_id), |
89 | HirFileIdRepr::Macro(macro_call_id) => { | 91 | HirFileIdRepr::Macro(macro_call_id) => { |
@@ -96,14 +98,10 @@ impl HirFileId { | |||
96 | 98 | ||
97 | fn parse_macro(db: &impl DefDatabase, macro_call_id: MacroCallId) -> Option<TreeArc<SourceFile>> { | 99 | fn parse_macro(db: &impl DefDatabase, macro_call_id: MacroCallId) -> Option<TreeArc<SourceFile>> { |
98 | let loc = macro_call_id.loc(db); | 100 | let loc = macro_call_id.loc(db); |
99 | let syntax = db.file_item(loc.source_item_id); | 101 | let macro_call = loc.ast_id.to_node(db); |
100 | let macro_call = ast::MacroCall::cast(&syntax).unwrap(); | ||
101 | let (macro_arg, _) = macro_call.token_tree().and_then(mbe::ast_to_token_tree)?; | 102 | let (macro_arg, _) = macro_call.token_tree().and_then(mbe::ast_to_token_tree)?; |
102 | 103 | ||
103 | let def_map = db.crate_def_map(loc.module.krate); | 104 | let macro_rules = db.macro_def(loc.def)?; |
104 | let (krate, macro_id) = def_map.resolve_macro(macro_call_id)?; | ||
105 | let def_map = db.crate_def_map(krate); | ||
106 | let macro_rules = &def_map[macro_id]; | ||
107 | let tt = macro_rules.expand(¯o_arg).ok()?; | 105 | let tt = macro_rules.expand(¯o_arg).ok()?; |
108 | Some(mbe::token_tree_to_ast_item_list(&tt)) | 106 | Some(mbe::token_tree_to_ast_item_list(&tt)) |
109 | } | 107 | } |
@@ -126,6 +124,17 @@ impl From<MacroCallId> for HirFileId { | |||
126 | } | 124 | } |
127 | } | 125 | } |
128 | 126 | ||
127 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
128 | pub struct MacroDefId(pub(crate) AstId<ast::MacroCall>); | ||
129 | |||
130 | pub(crate) fn macro_def_query(db: &impl DefDatabase, id: MacroDefId) -> Option<Arc<MacroRules>> { | ||
131 | let macro_call = id.0.to_node(db); | ||
132 | let arg = macro_call.token_tree()?; | ||
133 | let (tt, _) = mbe::ast_to_token_tree(arg)?; | ||
134 | let rules = MacroRules::parse(&tt).ok()?; | ||
135 | Some(Arc::new(rules)) | ||
136 | } | ||
137 | |||
129 | /// `MacroCallId` identifies a particular macro invocation, like | 138 | /// `MacroCallId` identifies a particular macro invocation, like |
130 | /// `println!("Hello, {}", world)`. | 139 | /// `println!("Hello, {}", world)`. |
131 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 140 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
@@ -134,8 +143,8 @@ impl_arena_id!(MacroCallId); | |||
134 | 143 | ||
135 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 144 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
136 | pub struct MacroCallLoc { | 145 | pub struct MacroCallLoc { |
137 | pub(crate) module: Module, | 146 | pub(crate) def: MacroDefId, |
138 | pub(crate) source_item_id: SourceItemId, | 147 | pub(crate) ast_id: AstId<ast::MacroCall>, |
139 | } | 148 | } |
140 | 149 | ||
141 | impl MacroCallId { | 150 | impl MacroCallId { |
@@ -145,7 +154,6 @@ impl MacroCallId { | |||
145 | } | 154 | } |
146 | 155 | ||
147 | impl MacroCallLoc { | 156 | impl MacroCallLoc { |
148 | #[allow(unused)] | ||
149 | pub(crate) fn id(&self, db: &impl AsRef<HirInterner>) -> MacroCallId { | 157 | pub(crate) fn id(&self, db: &impl AsRef<HirInterner>) -> MacroCallId { |
150 | db.as_ref().macros.loc2id(&self) | 158 | db.as_ref().macros.loc2id(&self) |
151 | } | 159 | } |
@@ -154,26 +162,25 @@ impl MacroCallLoc { | |||
154 | #[derive(Debug)] | 162 | #[derive(Debug)] |
155 | pub struct ItemLoc<N: AstNode> { | 163 | pub struct ItemLoc<N: AstNode> { |
156 | pub(crate) module: Module, | 164 | pub(crate) module: Module, |
157 | raw: SourceItemId, | 165 | ast_id: AstId<N>, |
158 | _ty: PhantomData<N>, | ||
159 | } | 166 | } |
160 | 167 | ||
161 | impl<N: AstNode> PartialEq for ItemLoc<N> { | 168 | impl<N: AstNode> PartialEq for ItemLoc<N> { |
162 | fn eq(&self, other: &Self) -> bool { | 169 | fn eq(&self, other: &Self) -> bool { |
163 | self.module == other.module && self.raw == other.raw | 170 | self.module == other.module && self.ast_id == other.ast_id |
164 | } | 171 | } |
165 | } | 172 | } |
166 | impl<N: AstNode> Eq for ItemLoc<N> {} | 173 | impl<N: AstNode> Eq for ItemLoc<N> {} |
167 | impl<N: AstNode> Hash for ItemLoc<N> { | 174 | impl<N: AstNode> Hash for ItemLoc<N> { |
168 | fn hash<H: Hasher>(&self, hasher: &mut H) { | 175 | fn hash<H: Hasher>(&self, hasher: &mut H) { |
169 | self.module.hash(hasher); | 176 | self.module.hash(hasher); |
170 | self.raw.hash(hasher); | 177 | self.ast_id.hash(hasher); |
171 | } | 178 | } |
172 | } | 179 | } |
173 | 180 | ||
174 | impl<N: AstNode> Clone for ItemLoc<N> { | 181 | impl<N: AstNode> Clone for ItemLoc<N> { |
175 | fn clone(&self) -> ItemLoc<N> { | 182 | fn clone(&self) -> ItemLoc<N> { |
176 | ItemLoc { module: self.module, raw: self.raw, _ty: PhantomData } | 183 | ItemLoc { module: self.module, ast_id: self.ast_id } |
177 | } | 184 | } |
178 | } | 185 | } |
179 | 186 | ||
@@ -200,26 +207,19 @@ impl<'a, DB: DefDatabase> LocationCtx<&'a DB> { | |||
200 | pub(crate) trait AstItemDef<N: AstNode>: ArenaId + Clone { | 207 | pub(crate) trait AstItemDef<N: AstNode>: ArenaId + Clone { |
201 | fn interner(interner: &HirInterner) -> &LocationInterner<ItemLoc<N>, Self>; | 208 | fn interner(interner: &HirInterner) -> &LocationInterner<ItemLoc<N>, Self>; |
202 | fn from_ast(ctx: LocationCtx<&impl DefDatabase>, ast: &N) -> Self { | 209 | fn from_ast(ctx: LocationCtx<&impl DefDatabase>, ast: &N) -> Self { |
203 | let items = ctx.db.file_items(ctx.file_id); | 210 | let items = ctx.db.ast_id_map(ctx.file_id); |
204 | let item_id = items.id_of(ctx.file_id, ast.syntax()); | 211 | let item_id = items.ast_id(ast); |
205 | Self::from_source_item_id_unchecked(ctx, item_id) | 212 | Self::from_ast_id(ctx, item_id) |
206 | } | 213 | } |
207 | fn from_source_item_id_unchecked( | 214 | fn from_ast_id(ctx: LocationCtx<&impl DefDatabase>, ast_id: FileAstId<N>) -> Self { |
208 | ctx: LocationCtx<&impl DefDatabase>, | 215 | let loc = ItemLoc { module: ctx.module, ast_id: ast_id.with_file_id(ctx.file_id) }; |
209 | item_id: SourceFileItemId, | ||
210 | ) -> Self { | ||
211 | let raw = SourceItemId { file_id: ctx.file_id, item_id }; | ||
212 | let loc = ItemLoc { module: ctx.module, raw, _ty: PhantomData }; | ||
213 | |||
214 | Self::interner(ctx.db.as_ref()).loc2id(&loc) | 216 | Self::interner(ctx.db.as_ref()).loc2id(&loc) |
215 | } | 217 | } |
216 | fn source(self, db: &impl DefDatabase) -> (HirFileId, TreeArc<N>) { | 218 | fn source(self, db: &impl DefDatabase) -> (HirFileId, TreeArc<N>) { |
217 | let int = Self::interner(db.as_ref()); | 219 | let int = Self::interner(db.as_ref()); |
218 | let loc = int.id2loc(self); | 220 | let loc = int.id2loc(self); |
219 | let syntax = db.file_item(loc.raw); | 221 | let ast = loc.ast_id.to_node(db); |
220 | let ast = | 222 | (loc.ast_id.file_id(), ast) |
221 | N::cast(&syntax).unwrap_or_else(|| panic!("invalid ItemLoc: {:?}", loc.raw)).to_owned(); | ||
222 | (loc.raw.file_id, ast) | ||
223 | } | 223 | } |
224 | fn module(self, db: &impl DefDatabase) -> Module { | 224 | fn module(self, db: &impl DefDatabase) -> Module { |
225 | let int = Self::interner(db.as_ref()); | 225 | let int = Self::interner(db.as_ref()); |
@@ -229,7 +229,7 @@ pub(crate) trait AstItemDef<N: AstNode>: ArenaId + Clone { | |||
229 | } | 229 | } |
230 | 230 | ||
231 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 231 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
232 | pub struct FunctionId(RawId); | 232 | pub(crate) struct FunctionId(RawId); |
233 | impl_arena_id!(FunctionId); | 233 | impl_arena_id!(FunctionId); |
234 | impl AstItemDef<ast::FnDef> for FunctionId { | 234 | impl AstItemDef<ast::FnDef> for FunctionId { |
235 | fn interner(interner: &HirInterner) -> &LocationInterner<ItemLoc<ast::FnDef>, Self> { | 235 | fn interner(interner: &HirInterner) -> &LocationInterner<ItemLoc<ast::FnDef>, Self> { |
@@ -238,7 +238,7 @@ impl AstItemDef<ast::FnDef> for FunctionId { | |||
238 | } | 238 | } |
239 | 239 | ||
240 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 240 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
241 | pub struct StructId(RawId); | 241 | pub(crate) struct StructId(RawId); |
242 | impl_arena_id!(StructId); | 242 | impl_arena_id!(StructId); |
243 | impl AstItemDef<ast::StructDef> for StructId { | 243 | impl AstItemDef<ast::StructDef> for StructId { |
244 | fn interner(interner: &HirInterner) -> &LocationInterner<ItemLoc<ast::StructDef>, Self> { | 244 | fn interner(interner: &HirInterner) -> &LocationInterner<ItemLoc<ast::StructDef>, Self> { |
@@ -247,7 +247,7 @@ impl AstItemDef<ast::StructDef> for StructId { | |||
247 | } | 247 | } |
248 | 248 | ||
249 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 249 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
250 | pub struct EnumId(RawId); | 250 | pub(crate) struct EnumId(RawId); |
251 | impl_arena_id!(EnumId); | 251 | impl_arena_id!(EnumId); |
252 | impl AstItemDef<ast::EnumDef> for EnumId { | 252 | impl AstItemDef<ast::EnumDef> for EnumId { |
253 | fn interner(interner: &HirInterner) -> &LocationInterner<ItemLoc<ast::EnumDef>, Self> { | 253 | fn interner(interner: &HirInterner) -> &LocationInterner<ItemLoc<ast::EnumDef>, Self> { |
@@ -256,7 +256,7 @@ impl AstItemDef<ast::EnumDef> for EnumId { | |||
256 | } | 256 | } |
257 | 257 | ||
258 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 258 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
259 | pub struct ConstId(RawId); | 259 | pub(crate) struct ConstId(RawId); |
260 | impl_arena_id!(ConstId); | 260 | impl_arena_id!(ConstId); |
261 | impl AstItemDef<ast::ConstDef> for ConstId { | 261 | impl AstItemDef<ast::ConstDef> for ConstId { |
262 | fn interner(interner: &HirInterner) -> &LocationInterner<ItemLoc<ast::ConstDef>, Self> { | 262 | fn interner(interner: &HirInterner) -> &LocationInterner<ItemLoc<ast::ConstDef>, Self> { |
@@ -265,7 +265,7 @@ impl AstItemDef<ast::ConstDef> for ConstId { | |||
265 | } | 265 | } |
266 | 266 | ||
267 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 267 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
268 | pub struct StaticId(RawId); | 268 | pub(crate) struct StaticId(RawId); |
269 | impl_arena_id!(StaticId); | 269 | impl_arena_id!(StaticId); |
270 | impl AstItemDef<ast::StaticDef> for StaticId { | 270 | impl AstItemDef<ast::StaticDef> for StaticId { |
271 | fn interner(interner: &HirInterner) -> &LocationInterner<ItemLoc<ast::StaticDef>, Self> { | 271 | fn interner(interner: &HirInterner) -> &LocationInterner<ItemLoc<ast::StaticDef>, Self> { |
@@ -274,7 +274,7 @@ impl AstItemDef<ast::StaticDef> for StaticId { | |||
274 | } | 274 | } |
275 | 275 | ||
276 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 276 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
277 | pub struct TraitId(RawId); | 277 | pub(crate) struct TraitId(RawId); |
278 | impl_arena_id!(TraitId); | 278 | impl_arena_id!(TraitId); |
279 | impl AstItemDef<ast::TraitDef> for TraitId { | 279 | impl AstItemDef<ast::TraitDef> for TraitId { |
280 | fn interner(interner: &HirInterner) -> &LocationInterner<ItemLoc<ast::TraitDef>, Self> { | 280 | fn interner(interner: &HirInterner) -> &LocationInterner<ItemLoc<ast::TraitDef>, Self> { |
@@ -283,117 +283,10 @@ impl AstItemDef<ast::TraitDef> for TraitId { | |||
283 | } | 283 | } |
284 | 284 | ||
285 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 285 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
286 | pub struct TypeId(RawId); | 286 | pub(crate) struct TypeAliasId(RawId); |
287 | impl_arena_id!(TypeId); | 287 | impl_arena_id!(TypeAliasId); |
288 | impl AstItemDef<ast::TypeAliasDef> for TypeId { | 288 | impl AstItemDef<ast::TypeAliasDef> for TypeAliasId { |
289 | fn interner(interner: &HirInterner) -> &LocationInterner<ItemLoc<ast::TypeAliasDef>, Self> { | 289 | fn interner(interner: &HirInterner) -> &LocationInterner<ItemLoc<ast::TypeAliasDef>, Self> { |
290 | &interner.types | 290 | &interner.types |
291 | } | 291 | } |
292 | } | 292 | } |
293 | |||
294 | /// Identifier of item within a specific file. This is stable over reparses, so | ||
295 | /// it's OK to use it as a salsa key/value. | ||
296 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
297 | pub struct SourceFileItemId(RawId); | ||
298 | impl_arena_id!(SourceFileItemId); | ||
299 | |||
300 | impl SourceFileItemId { | ||
301 | pub(crate) fn with_file_id(self, file_id: HirFileId) -> SourceItemId { | ||
302 | SourceItemId { file_id, item_id: self } | ||
303 | } | ||
304 | } | ||
305 | |||
306 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
307 | pub struct SourceItemId { | ||
308 | pub(crate) file_id: HirFileId, | ||
309 | pub(crate) item_id: SourceFileItemId, | ||
310 | } | ||
311 | |||
312 | /// Maps items' `SyntaxNode`s to `SourceFileItemId`s and back. | ||
313 | #[derive(Debug, PartialEq, Eq)] | ||
314 | pub struct SourceFileItems { | ||
315 | file_id: HirFileId, | ||
316 | arena: Arena<SourceFileItemId, SyntaxNodePtr>, | ||
317 | } | ||
318 | |||
319 | impl SourceFileItems { | ||
320 | pub(crate) fn file_items_query( | ||
321 | db: &impl DefDatabase, | ||
322 | file_id: HirFileId, | ||
323 | ) -> Arc<SourceFileItems> { | ||
324 | let source_file = db.hir_parse(file_id); | ||
325 | Arc::new(SourceFileItems::from_source_file(&source_file, file_id)) | ||
326 | } | ||
327 | |||
328 | pub(crate) fn file_item_query( | ||
329 | db: &impl DefDatabase, | ||
330 | source_item_id: SourceItemId, | ||
331 | ) -> TreeArc<SyntaxNode> { | ||
332 | let source_file = db.hir_parse(source_item_id.file_id); | ||
333 | db.file_items(source_item_id.file_id)[source_item_id.item_id] | ||
334 | .to_node(&source_file) | ||
335 | .to_owned() | ||
336 | } | ||
337 | |||
338 | pub(crate) fn from_source_file( | ||
339 | source_file: &SourceFile, | ||
340 | file_id: HirFileId, | ||
341 | ) -> SourceFileItems { | ||
342 | let mut res = SourceFileItems { file_id, arena: Arena::default() }; | ||
343 | // By walking the tree in bread-first order we make sure that parents | ||
344 | // get lower ids then children. That is, adding a new child does not | ||
345 | // change parent's id. This means that, say, adding a new function to a | ||
346 | // trait does not change ids of top-level items, which helps caching. | ||
347 | bfs(source_file.syntax(), |it| { | ||
348 | if let Some(module_item) = ast::ModuleItem::cast(it) { | ||
349 | res.alloc(module_item.syntax()); | ||
350 | } else if let Some(macro_call) = ast::MacroCall::cast(it) { | ||
351 | res.alloc(macro_call.syntax()); | ||
352 | } | ||
353 | }); | ||
354 | res | ||
355 | } | ||
356 | |||
357 | fn alloc(&mut self, item: &SyntaxNode) -> SourceFileItemId { | ||
358 | self.arena.alloc(SyntaxNodePtr::new(item)) | ||
359 | } | ||
360 | pub(crate) fn id_of(&self, file_id: HirFileId, item: &SyntaxNode) -> SourceFileItemId { | ||
361 | assert_eq!( | ||
362 | self.file_id, file_id, | ||
363 | "SourceFileItems: wrong file, expected {:?}, got {:?}", | ||
364 | self.file_id, file_id | ||
365 | ); | ||
366 | self.id_of_unchecked(item) | ||
367 | } | ||
368 | pub(crate) fn id_of_unchecked(&self, item: &SyntaxNode) -> SourceFileItemId { | ||
369 | let ptr = SyntaxNodePtr::new(item); | ||
370 | if let Some((id, _)) = self.arena.iter().find(|(_id, i)| **i == ptr) { | ||
371 | return id; | ||
372 | } | ||
373 | panic!( | ||
374 | "Can't find {:?} in SourceFileItems:\n{:?}", | ||
375 | item, | ||
376 | self.arena.iter().map(|(_id, i)| i).collect::<Vec<_>>(), | ||
377 | ); | ||
378 | } | ||
379 | } | ||
380 | |||
381 | impl std::ops::Index<SourceFileItemId> for SourceFileItems { | ||
382 | type Output = SyntaxNodePtr; | ||
383 | fn index(&self, idx: SourceFileItemId) -> &SyntaxNodePtr { | ||
384 | &self.arena[idx] | ||
385 | } | ||
386 | } | ||
387 | |||
388 | /// Walks the subtree in bfs order, calling `f` for each node. | ||
389 | fn bfs(node: &SyntaxNode, mut f: impl FnMut(&SyntaxNode)) { | ||
390 | let mut curr_layer = vec![node]; | ||
391 | let mut next_layer = vec![]; | ||
392 | while !curr_layer.is_empty() { | ||
393 | curr_layer.drain(..).for_each(|node| { | ||
394 | next_layer.extend(node.children()); | ||
395 | f(node); | ||
396 | }); | ||
397 | std::mem::swap(&mut curr_layer, &mut next_layer); | ||
398 | } | ||
399 | } | ||