rune/indexing/
index.rs

1use core::mem::take;
2
3use tracing::instrument_ast;
4
5use crate::alloc::prelude::*;
6use crate::alloc::VecDeque;
7use crate::ast::{self, OptionSpanned, Spanned};
8use crate::compile::{
9    self, attrs, meta, Doc, DynLocation, ErrorKind, ItemMeta, Location, Visibility, WithSpan,
10};
11use crate::indexing::{self, Indexed};
12use crate::parse::{Resolve, ResolveContext};
13use crate::query::{DeferEntry, ImplItem, ImplItemKind};
14use crate::runtime::Call;
15use crate::worker::{Import, ImportKind, ImportState};
16
17use super::{ast_to_visibility, validate_call, Indexer};
18
19/// Macros are only allowed to expand recursively into other macros 64 times.
20const MAX_MACRO_RECURSION: usize = 64;
21
22/// Index the contents of a module known by its AST as a "file".
23pub(crate) fn file(idx: &mut Indexer<'_, '_>, ast: &mut ast::File) -> compile::Result<()> {
24    let mut p = attrs::Parser::new(&ast.attributes)?;
25
26    // This part catches comments interior to the module of the form `//!`.
27    for doc in p.parse_all::<attrs::Doc>(resolve_context!(idx.q), &ast.attributes)? {
28        let (span, doc) = doc?;
29
30        let doc_string = doc.doc_string.resolve(resolve_context!(idx.q))?;
31
32        idx.q
33            .visitor
34            .visit_doc_comment(
35                &DynLocation::new(idx.source_id, &span),
36                idx.q.pool.module_item(idx.item.module),
37                idx.q.pool.module_item_hash(idx.item.module),
38                &doc_string,
39            )
40            .with_span(span)?;
41    }
42
43    if let Some(first) = p.remaining(&ast.attributes).next() {
44        return Err(compile::Error::msg(
45            first,
46            "File attributes are not supported",
47        ));
48    }
49
50    // Items take priority.
51    let mut head = VecDeque::new();
52
53    // Macros and items with attributes are expanded as they are encountered, but after regular items have
54    // been processed.
55    let mut queue = VecDeque::new();
56
57    for (item, semi) in ast.items.drain(..) {
58        match item {
59            i @ ast::Item::MacroCall(_) => {
60                queue.try_push_back((0, i, Vec::new(), semi))?;
61            }
62            i if !i.attributes().is_empty() => {
63                queue.try_push_back((0, i, Vec::new(), semi))?;
64            }
65            i => {
66                head.try_push_back((i, semi))?;
67            }
68        }
69    }
70
71    'uses: while !head.is_empty() || !queue.is_empty() {
72        while let Some((i, semi)) = head.pop_front() {
73            if let Some(semi) = semi {
74                if !i.needs_semi_colon() {
75                    idx.q
76                        .diagnostics
77                        .unnecessary_semi_colon(idx.source_id, &semi)?;
78                }
79            }
80
81            item(idx, i)?;
82        }
83
84        while let Some((depth, mut item, mut skipped_attributes, semi)) = queue.pop_front() {
85            if depth >= MAX_MACRO_RECURSION {
86                return Err(compile::Error::new(
87                    &item,
88                    ErrorKind::MaxMacroRecursion {
89                        depth,
90                        max: MAX_MACRO_RECURSION,
91                    },
92                ));
93            }
94
95            // Before further processing all attributes are either expanded, or
96            // if unknown put in `skipped_attributes`, to either be reinserted
97            // for the `item` handler or to be used by the macro_call expansion
98            // below.
99            if let Some(mut attr) = item.remove_first_attribute() {
100                let Some(file) = idx.expand_attribute_macro::<ast::File>(&mut attr, &item)? else {
101                    skipped_attributes.try_push(attr)?;
102
103                    if !matches!(item, ast::Item::MacroCall(_)) && item.attributes().is_empty() {
104                        // For all we know only non macro attributes remain, which will be
105                        // handled by the item handler.
106                        *item.attributes_mut() = skipped_attributes;
107                        head.try_push_front((item, semi))?;
108                    } else {
109                        // items with remaining attributes and macro calls will be dealt with by
110                        // reinserting in the queue.
111                        queue.try_push_back((depth, item, skipped_attributes, semi))?;
112                    }
113
114                    continue;
115                };
116
117                for (item, semi) in file.items.into_iter().rev() {
118                    match item {
119                        item @ ast::Item::MacroCall(_) => {
120                            queue.try_push_back((depth.wrapping_add(1), item, Vec::new(), semi))?;
121                        }
122                        item if !item.attributes().is_empty() => {
123                            queue.try_push_back((depth.wrapping_add(1), item, Vec::new(), semi))?;
124                        }
125                        item => {
126                            head.try_push_front((item, semi))?;
127                        }
128                    }
129                }
130
131                continue;
132            }
133
134            let ast::Item::MacroCall(mut macro_call) = item else {
135                return Err(compile::Error::msg(
136                    &item,
137                    "Expected attributes on macro call",
138                ));
139            };
140
141            macro_call.attributes = skipped_attributes;
142
143            let mut p = attrs::Parser::new(&macro_call.attributes)?;
144
145            if idx.try_expand_internal_macro(&mut p, &mut macro_call)? {
146                if let Some(attr) = p.remaining(&macro_call.attributes).next() {
147                    return Err(compile::Error::msg(
148                        attr,
149                        "Attributes on macros are not supported",
150                    ));
151                }
152
153                // Macro call must be added to output to make sure its instructions are assembled.
154                ast.items
155                    .try_push((ast::Item::MacroCall(macro_call), semi))?;
156            } else {
157                if let Some(attr) = p.remaining(&macro_call.attributes).next() {
158                    return Err(compile::Error::msg(
159                        attr,
160                        "Attributes on macros are not supported",
161                    ));
162                }
163
164                let file = idx.expand_macro::<ast::File>(&mut macro_call)?;
165
166                for (item, semi) in file.items.into_iter().rev() {
167                    match item {
168                        item @ ast::Item::MacroCall(_) => {
169                            queue.try_push_back((depth.wrapping_add(1), item, Vec::new(), semi))?;
170                        }
171                        item if !item.attributes().is_empty() => {
172                            queue.try_push_back((depth.wrapping_add(1), item, Vec::new(), semi))?;
173                        }
174                        item => {
175                            head.try_push_front((item, semi))?;
176                        }
177                    }
178                }
179            }
180
181            if !head.is_empty() {
182                continue 'uses;
183            }
184        }
185    }
186
187    Ok(())
188}
189
190#[instrument_ast(span = span)]
191pub(crate) fn empty_block_fn(
192    idx: &mut Indexer<'_, '_>,
193    mut ast: ast::EmptyBlock,
194    span: &dyn Spanned,
195) -> compile::Result<()> {
196    let item_meta = idx.insert_new_item(span, Visibility::Public, &[])?;
197    let idx_item = idx.item.replace(item_meta.item);
198
199    idx.scopes.push()?;
200
201    statements(idx, &mut ast.statements)?;
202
203    idx.item = idx_item;
204
205    let layer = idx.scopes.pop().with_span(span)?;
206
207    let call = match (layer.awaits.is_empty(), layer.yields.is_empty()) {
208        (true, true) => Call::Immediate,
209        (false, true) => Call::Async,
210        (true, false) => Call::Generator,
211        (false, false) => Call::Stream,
212    };
213
214    idx.q.index_and_build(indexing::Entry {
215        item_meta,
216        indexed: Indexed::Function(indexing::Function {
217            ast: indexing::FunctionAst::Empty(Box::try_new(ast)?, span.span()),
218            call,
219            is_instance: false,
220            is_test: false,
221            is_bench: false,
222            impl_item: None,
223            args: Vec::new(),
224        }),
225    })?;
226
227    Ok(())
228}
229
230#[instrument_ast(span = ast)]
231pub(crate) fn item_fn(idx: &mut Indexer<'_, '_>, mut ast: ast::ItemFn) -> compile::Result<()> {
232    let name = ast.name.resolve(resolve_context!(idx.q))?;
233
234    let visibility = ast_to_visibility(&ast.visibility)?;
235
236    let mut p = attrs::Parser::new(&ast.attributes)?;
237
238    let docs = Doc::collect_from(resolve_context!(idx.q), &mut p, &ast.attributes)?;
239
240    let guard = idx.items.push_name(name.as_ref())?;
241    let item_meta = idx.insert_new_item(&ast, visibility, &docs)?;
242    let idx_item = idx.item.replace(item_meta.item);
243
244    for (arg, _) in &mut ast.args {
245        if let ast::FnArg::Pat(p) = arg {
246            pat(idx, p)?;
247        }
248    }
249
250    idx.scopes.push()?;
251
252    // Take and restore item nesting.
253    let last = idx.nested_item.replace(ast.descriptive_span());
254    block(idx, &mut ast.body)?;
255    idx.nested_item = last;
256
257    idx.item = idx_item;
258    idx.items.pop(guard).with_span(&ast)?;
259
260    let layer = idx.scopes.pop().with_span(&ast)?;
261
262    if let (Some(const_token), Some(async_token)) = (ast.const_token, ast.async_token) {
263        return Err(compile::Error::new(
264            const_token.span().join(async_token.span()),
265            ErrorKind::FnConstAsyncConflict,
266        ));
267    };
268
269    let call = validate_call(ast.const_token.is_some(), ast.async_token.is_some(), &layer)?;
270
271    let Some(call) = call else {
272        idx.q
273            .index_const_fn(item_meta, indexing::ConstFn::Ast(Box::try_new(ast)?))?;
274        return Ok(());
275    };
276
277    let is_test = match p.try_parse::<attrs::Test>(resolve_context!(idx.q), &ast.attributes)? {
278        Some((attr, _)) => {
279            if let Some(_nested_span) = idx.nested_item {
280                return Err(compile::Error::new(
281                    attr,
282                    ErrorKind::NestedTest {
283                        #[cfg(feature = "emit")]
284                        nested_span: _nested_span,
285                    },
286                ));
287            }
288
289            true
290        }
291        _ => false,
292    };
293
294    let is_bench = match p.try_parse::<attrs::Bench>(resolve_context!(idx.q), &ast.attributes)? {
295        Some((attr, _)) => {
296            if let Some(_nested_span) = idx.nested_item {
297                let span = attr.span().join(ast.descriptive_span());
298
299                return Err(compile::Error::new(
300                    span,
301                    ErrorKind::NestedBench {
302                        #[cfg(feature = "emit")]
303                        nested_span: _nested_span,
304                    },
305                ));
306            }
307
308            true
309        }
310        _ => false,
311    };
312
313    if let Some(attrs) = p.remaining(&ast.attributes).next() {
314        return Err(compile::Error::msg(
315            attrs,
316            "Attributes on functions are not supported",
317        ));
318    }
319
320    if ast.output.is_some() {
321        return Err(compile::Error::msg(
322            &ast,
323            "Adding a return type in functions is not supported",
324        ));
325    }
326
327    let is_instance = ast.is_instance();
328
329    if is_instance {
330        if is_test {
331            return Err(compile::Error::msg(
332                &ast,
333                "The #[test] attribute is not supported on functions receiving `self`",
334            ));
335        }
336
337        if is_bench {
338            return Err(compile::Error::msg(
339                &ast,
340                "The #[bench] attribute is not supported on functions receiving `self`",
341            ));
342        }
343
344        if idx.item.impl_item.is_none() {
345            return Err(compile::Error::new(
346                &ast,
347                ErrorKind::InstanceFunctionOutsideImpl,
348            ));
349        };
350    }
351
352    let name = ast.name;
353    let args = ast.args.iter().map(|(a, _)| a.span()).try_collect()?;
354
355    let entry = indexing::Entry {
356        item_meta,
357        indexed: Indexed::Function(indexing::Function {
358            ast: indexing::FunctionAst::Item(Box::try_new(ast)?, name),
359            call,
360            is_instance,
361            is_test,
362            is_bench,
363            impl_item: idx.item.impl_item,
364            args,
365        }),
366    };
367
368    // It's only a public item in the sense of exporting it if it's not inside
369    // of a nested item. Instance functions are always eagerly exported since
370    // they need to be accessed dynamically through `self`.
371    let is_exported = is_instance
372        || item_meta.is_public(idx.q.pool) && idx.nested_item.is_none()
373        || is_test
374        || is_bench;
375
376    if is_exported {
377        idx.q.index_and_build(entry)?;
378    } else {
379        idx.q.index(entry)?;
380    }
381
382    Ok(())
383}
384
385#[instrument_ast(span = ast)]
386fn expr_block(idx: &mut Indexer<'_, '_>, ast: &mut ast::ExprBlock) -> compile::Result<()> {
387    if let Some(span) = ast.attributes.option_span() {
388        return Err(compile::Error::msg(
389            span,
390            "Attributes on blocks are not supported",
391        ));
392    }
393
394    if ast.async_token.is_none() && ast.const_token.is_none() {
395        if let Some(span) = ast.move_token.option_span() {
396            return Err(compile::Error::msg(
397                span,
398                "The `move` modifier on blocks is not supported",
399            ));
400        }
401
402        block(idx, &mut ast.block)?;
403        return Ok(());
404    }
405
406    if ast.const_token.is_some() {
407        if let Some(async_token) = ast.async_token {
408            return Err(compile::Error::new(
409                async_token,
410                ErrorKind::BlockConstAsyncConflict,
411            ));
412        }
413
414        let item_meta = block(idx, &mut ast.block)?;
415        ast.block.id = item_meta.item;
416        idx.q.index_const_block(
417            item_meta,
418            indexing::ConstBlock::Ast(Box::try_new(ast.block.try_clone()?)?),
419        )?;
420    } else {
421        idx.scopes.push()?;
422        let item_meta = block(idx, &mut ast.block)?;
423        let layer = idx.scopes.pop().with_span(&ast)?;
424
425        let call = validate_call(ast.const_token.is_some(), ast.async_token.is_some(), &layer)?;
426
427        let Some(call) = call else {
428            return Err(compile::Error::new(ast, ErrorKind::ClosureKind));
429        };
430
431        ast.block.id = item_meta.item;
432        idx.q.index_meta(
433            &*ast,
434            item_meta,
435            meta::Kind::AsyncBlock {
436                call,
437                do_move: ast.move_token.is_some(),
438            },
439        )?;
440    }
441
442    Ok(())
443}
444
445fn statements(idx: &mut Indexer<'_, '_>, ast: &mut Vec<ast::Stmt>) -> compile::Result<()> {
446    let mut statements = Vec::new();
447
448    for stmt in ast.drain(..) {
449        match stmt {
450            ast::Stmt::Item(i, semi) => {
451                if let Some(semi) = semi {
452                    if !i.needs_semi_colon() {
453                        idx.q
454                            .diagnostics
455                            .unnecessary_semi_colon(idx.source_id, &semi)?;
456                    }
457                }
458
459                item(idx, i)?;
460            }
461            stmt => {
462                statements.try_push(stmt)?;
463            }
464        }
465    }
466
467    let mut must_be_last = None;
468
469    for stmt in &mut statements {
470        if let Some(span) = must_be_last {
471            return Err(compile::Error::new(
472                span,
473                ErrorKind::ExpectedBlockSemiColon {
474                    #[cfg(feature = "emit")]
475                    followed_span: stmt.span(),
476                },
477            ));
478        }
479
480        match stmt {
481            ast::Stmt::Local(l) => {
482                local(idx, l)?;
483            }
484            ast::Stmt::Expr(e) => {
485                if e.needs_semi() {
486                    must_be_last = Some(e.span());
487                }
488
489                expr(idx, e)?;
490            }
491            ast::Stmt::Semi(semi) => {
492                if !semi.needs_semi() {
493                    idx.q
494                        .diagnostics
495                        .unnecessary_semi_colon(idx.source_id, semi)?;
496                }
497
498                expr(idx, &mut semi.expr)?;
499            }
500            ast::Stmt::Item(i, ..) => {
501                return Err(compile::Error::msg(i, "Unexpected item in this stage"));
502            }
503        }
504    }
505
506    *ast = statements;
507    Ok(())
508}
509
510#[instrument_ast(span = ast)]
511fn block(idx: &mut Indexer<'_, '_>, ast: &mut ast::Block) -> compile::Result<ItemMeta> {
512    let guard = idx.push_id()?;
513
514    let item_meta = idx.insert_new_item(&ast, Visibility::Inherited, &[])?;
515    let idx_item = idx.item.replace(item_meta.item);
516
517    statements(idx, &mut ast.statements)?;
518    idx.item = idx_item;
519    idx.items.pop(guard).with_span(&ast)?;
520    Ok(item_meta)
521}
522
523#[instrument_ast(span = ast)]
524fn local(idx: &mut Indexer<'_, '_>, ast: &mut ast::Local) -> compile::Result<()> {
525    if let Some(span) = ast.attributes.option_span() {
526        return Err(compile::Error::msg(
527            span,
528            "Attributes on local declarations are not supported",
529        ));
530    }
531
532    if let Some(mut_token) = ast.mut_token {
533        return Err(compile::Error::new(mut_token, ErrorKind::UnsupportedMut));
534    }
535
536    // We index the rhs expression first so that it doesn't see it's own
537    // declaration and use that instead of capturing from the outside.
538    expr(idx, &mut ast.expr)?;
539    pat(idx, &mut ast.pat)?;
540    Ok(())
541}
542
543#[instrument_ast(span = ast)]
544fn expr_let(idx: &mut Indexer<'_, '_>, ast: &mut ast::ExprLet) -> compile::Result<()> {
545    if let Some(mut_token) = ast.mut_token {
546        return Err(compile::Error::new(mut_token, ErrorKind::UnsupportedMut));
547    }
548
549    pat(idx, &mut ast.pat)?;
550    expr(idx, &mut ast.expr)?;
551    Ok(())
552}
553
554#[instrument_ast(span = ast)]
555fn pat(idx: &mut Indexer<'_, '_>, ast: &mut ast::Pat) -> compile::Result<()> {
556    match ast {
557        ast::Pat::Path(pat) => {
558            path(idx, &mut pat.path)?;
559        }
560        ast::Pat::Object(pat) => {
561            pat_object(idx, pat)?;
562        }
563        ast::Pat::Vec(pat) => {
564            pat_vec(idx, pat)?;
565        }
566        ast::Pat::Tuple(pat) => {
567            pat_tuple(idx, pat)?;
568        }
569        ast::Pat::Binding(pat) => {
570            pat_binding(idx, pat)?;
571        }
572        ast::Pat::Ignore(..) => (),
573        ast::Pat::Lit(..) => (),
574        ast::Pat::Rest(..) => (),
575    }
576
577    Ok(())
578}
579
580#[instrument_ast(span = ast)]
581fn pat_tuple(idx: &mut Indexer<'_, '_>, ast: &mut ast::PatTuple) -> compile::Result<()> {
582    if let Some(p) = &mut ast.path {
583        path(idx, p)?;
584    }
585
586    for (p, _) in &mut ast.items {
587        pat(idx, p)?;
588    }
589
590    Ok(())
591}
592
593#[instrument_ast(span = ast)]
594fn pat_object(idx: &mut Indexer<'_, '_>, ast: &mut ast::PatObject) -> compile::Result<()> {
595    match &mut ast.ident {
596        ast::ObjectIdent::Anonymous(..) => (),
597        ast::ObjectIdent::Named(p) => {
598            path(idx, p)?;
599        }
600    }
601
602    for (p, _) in &mut ast.items {
603        pat(idx, p)?;
604    }
605
606    Ok(())
607}
608
609#[instrument_ast(span = ast)]
610fn pat_vec(idx: &mut Indexer<'_, '_>, ast: &mut ast::PatVec) -> compile::Result<()> {
611    for (p, _) in &mut ast.items {
612        pat(idx, p)?;
613    }
614
615    Ok(())
616}
617
618#[instrument_ast(span = ast)]
619fn pat_binding(idx: &mut Indexer<'_, '_>, ast: &mut ast::PatBinding) -> compile::Result<()> {
620    pat(idx, &mut ast.pat)?;
621    Ok(())
622}
623
624#[instrument_ast(span = ast)]
625pub(crate) fn expr(idx: &mut Indexer<'_, '_>, ast: &mut ast::Expr) -> compile::Result<()> {
626    match ast {
627        ast::Expr::Path(ast) => {
628            path(idx, ast)?;
629        }
630        ast::Expr::Let(ast) => {
631            expr_let(idx, ast)?;
632        }
633        ast::Expr::Block(ast) => {
634            expr_block(idx, ast)?;
635        }
636        ast::Expr::Group(ast) => {
637            expr(idx, &mut ast.expr)?;
638        }
639        ast::Expr::Empty(ast) => {
640            expr(idx, &mut ast.expr)?;
641        }
642        ast::Expr::If(ast) => {
643            expr_if(idx, ast)?;
644        }
645        ast::Expr::Assign(ast) => {
646            expr_assign(idx, ast)?;
647        }
648        ast::Expr::Binary(ast) => {
649            expr_binary(idx, ast)?;
650        }
651        ast::Expr::Match(ast) => {
652            expr_match(idx, ast)?;
653        }
654        ast::Expr::Closure(ast) => {
655            expr_closure(idx, ast)?;
656        }
657        ast::Expr::While(ast) => {
658            expr_while(idx, ast)?;
659        }
660        ast::Expr::Loop(ast) => {
661            expr_loop(idx, ast)?;
662        }
663        ast::Expr::For(ast) => {
664            expr_for(idx, ast)?;
665        }
666        ast::Expr::FieldAccess(ast) => {
667            expr_field_access(idx, ast)?;
668        }
669        ast::Expr::Unary(ast) => {
670            expr(idx, &mut ast.expr)?;
671        }
672        ast::Expr::Index(ast) => {
673            expr(idx, &mut ast.index)?;
674            expr(idx, &mut ast.target)?;
675        }
676        ast::Expr::Break(ast) => {
677            if let Some(ast) = &mut ast.expr {
678                expr(idx, ast)?;
679            }
680        }
681        ast::Expr::Yield(ast) => {
682            let l = idx.scopes.mark().with_span(&*ast)?;
683            l.yields.try_push(ast.span())?;
684
685            if let Some(e) = &mut ast.expr {
686                expr(idx, e)?;
687            }
688        }
689        ast::Expr::Return(ast) => {
690            if let Some(ast) = &mut ast.expr {
691                expr(idx, ast)?;
692            }
693        }
694        ast::Expr::Await(ast) => {
695            let l = idx.scopes.mark().with_span(&*ast)?;
696            l.awaits.try_push(ast.span())?;
697            expr(idx, &mut ast.expr)?;
698        }
699        ast::Expr::Try(ast) => {
700            expr(idx, &mut ast.expr)?;
701        }
702        ast::Expr::Select(e) => {
703            expr_select(idx, e)?;
704        }
705        // ignored because they have no effect on indexing.
706        ast::Expr::Call(e) => {
707            expr_call(idx, e)?;
708        }
709        ast::Expr::Lit(..) => {}
710        ast::Expr::Tuple(ast) => {
711            for (ast, _) in &mut ast.items {
712                expr(idx, ast)?;
713            }
714        }
715        ast::Expr::Vec(ast) => {
716            for (ast, _) in &mut ast.items {
717                expr(idx, ast)?;
718            }
719        }
720        ast::Expr::Object(ast) => {
721            expr_object(idx, ast)?;
722        }
723        ast::Expr::Range(ast) => {
724            if let Some(from) = &mut ast.start {
725                expr(idx, from)?;
726            }
727
728            if let Some(to) = &mut ast.end {
729                expr(idx, to)?;
730            }
731        }
732        // NB: macros have nothing to index, they don't export language
733        // items.
734        ast::Expr::MacroCall(macro_call) => {
735            // Note: There is a preprocessing step involved with statements for
736            // which the macro **might** have been expanded to a built-in macro
737            // if we end up here. So instead of expanding if the id is set, we
738            // just assert that the builtin macro has been added to the query
739            // engine.
740
741            if let Some(id) = macro_call.id {
742                // Assert that the built-in macro has been expanded.
743                idx.q.builtin_macro_for(id).with_span(&*macro_call)?;
744            } else {
745                let mut p = attrs::Parser::new(&macro_call.attributes)?;
746
747                let expanded = idx.try_expand_internal_macro(&mut p, macro_call)?;
748
749                if let Some(span) = p.remaining(&macro_call.attributes).next() {
750                    return Err(compile::Error::msg(span, "Unsupported macro attribute"));
751                }
752
753                if !expanded {
754                    let out = idx.expand_macro::<ast::Expr>(macro_call)?;
755                    idx.enter_macro(&macro_call)?;
756                    *ast = out;
757                    expr(idx, ast)?;
758                    idx.leave_macro();
759                }
760            }
761
762            return Ok(());
763        }
764        ast::Expr::Continue(..) => {}
765    }
766
767    if let [first, ..] = ast.attributes() {
768        return Err(compile::Error::msg(
769            first,
770            "Attributes on expressions are not supported",
771        ));
772    }
773
774    Ok(())
775}
776
777#[instrument_ast(span = ast)]
778fn expr_if(idx: &mut Indexer<'_, '_>, ast: &mut ast::ExprIf) -> compile::Result<()> {
779    condition(idx, &mut ast.condition)?;
780    block(idx, &mut ast.block)?;
781
782    for expr_else_if in &mut ast.expr_else_ifs {
783        condition(idx, &mut expr_else_if.condition)?;
784        block(idx, &mut expr_else_if.block)?;
785    }
786
787    if let Some(expr_else) = &mut ast.expr_else {
788        block(idx, &mut expr_else.block)?;
789    }
790
791    Ok(())
792}
793
794#[instrument_ast(span = ast)]
795fn expr_assign(idx: &mut Indexer<'_, '_>, ast: &mut ast::ExprAssign) -> compile::Result<()> {
796    expr(idx, &mut ast.lhs)?;
797    expr(idx, &mut ast.rhs)?;
798    Ok(())
799}
800
801#[instrument_ast(span = ast)]
802fn expr_binary(idx: &mut Indexer<'_, '_>, ast: &mut ast::ExprBinary) -> compile::Result<()> {
803    expr(idx, &mut ast.lhs)?;
804    expr(idx, &mut ast.rhs)?;
805    Ok(())
806}
807
808#[instrument_ast(span = ast)]
809fn expr_match(idx: &mut Indexer<'_, '_>, ast: &mut ast::ExprMatch) -> compile::Result<()> {
810    expr(idx, &mut ast.expr)?;
811
812    for (branch, _) in &mut ast.branches {
813        if let Some((_, condition)) = &mut branch.condition {
814            expr(idx, condition)?;
815        }
816
817        pat(idx, &mut branch.pat)?;
818        expr(idx, &mut branch.body)?;
819    }
820
821    Ok(())
822}
823
824#[instrument_ast(span = ast)]
825fn condition(idx: &mut Indexer<'_, '_>, ast: &mut ast::Condition) -> compile::Result<()> {
826    match ast {
827        ast::Condition::Expr(e) => {
828            expr(idx, e)?;
829        }
830        ast::Condition::ExprLet(e) => {
831            expr_let(idx, e)?;
832        }
833    }
834
835    Ok(())
836}
837
838#[instrument_ast(span = ast)]
839fn item_enum(idx: &mut Indexer<'_, '_>, mut ast: ast::ItemEnum) -> compile::Result<()> {
840    let mut p = attrs::Parser::new(&ast.attributes)?;
841
842    let docs = Doc::collect_from(resolve_context!(idx.q), &mut p, &ast.attributes)?;
843
844    if let Some(first) = p.remaining(&ast.attributes).next() {
845        return Err(compile::Error::msg(
846            first,
847            "Attributes on enums are not supported",
848        ));
849    }
850
851    let name = ast.name.resolve(resolve_context!(idx.q))?;
852    let guard = idx.items.push_name(name.as_ref())?;
853
854    let visibility = ast_to_visibility(&ast.visibility)?;
855
856    let enum_item = idx.insert_new_item(&ast, visibility, &docs)?;
857    let idx_item = idx.item.replace(enum_item.item);
858
859    idx.q.index_enum(enum_item)?;
860
861    for (mut variant, _) in ast.variants.drain() {
862        let mut p = attrs::Parser::new(&variant.attributes)?;
863
864        let docs = Doc::collect_from(resolve_context!(idx.q), &mut p, &variant.attributes)?;
865
866        if let Some(first) = p.remaining(&variant.attributes).next() {
867            return Err(compile::Error::msg(
868                first,
869                "Attributes on variants are not supported",
870            ));
871        }
872
873        let name = variant.name.resolve(resolve_context!(idx.q))?;
874        let guard = idx.items.push_name(name.as_ref())?;
875
876        let item_meta = idx.insert_new_item(&variant.name, Visibility::Public, &docs)?;
877        let idx_item = idx.item.replace(item_meta.item);
878
879        variant.id = item_meta.item;
880
881        let cx = resolve_context!(idx.q);
882
883        for (field, _) in variant.body.fields() {
884            let mut p = attrs::Parser::new(&field.attributes)?;
885            let docs = Doc::collect_from(cx, &mut p, &field.attributes)?;
886
887            if let Some(first) = p.remaining(&field.attributes).next() {
888                return Err(compile::Error::msg(
889                    first,
890                    "Attributes on variant fields are not supported",
891                ));
892            }
893
894            let name = field.name.resolve(cx)?;
895
896            for doc in docs {
897                idx.q
898                    .visitor
899                    .visit_field_doc_comment(
900                        &DynLocation::new(idx.source_id, &doc),
901                        idx.q.pool.item(item_meta.item),
902                        idx.q.pool.item_type_hash(item_meta.item),
903                        name,
904                        doc.doc_string.resolve(cx)?.as_ref(),
905                    )
906                    .with_span(doc)?;
907            }
908        }
909
910        idx.item = idx_item;
911        idx.items.pop(guard).with_span(&variant)?;
912
913        idx.q.index_variant(
914            item_meta,
915            indexing::Variant {
916                enum_id: enum_item.item,
917                fields: convert_fields(resolve_context!(idx.q), variant.body)?,
918            },
919        )?;
920    }
921
922    idx.item = idx_item;
923    idx.items.pop(guard).with_span(&ast)?;
924    Ok(())
925}
926
927#[instrument_ast(span = ast)]
928fn item_struct(idx: &mut Indexer<'_, '_>, mut ast: ast::ItemStruct) -> compile::Result<()> {
929    let mut p = attrs::Parser::new(&ast.attributes)?;
930
931    let docs = Doc::collect_from(resolve_context!(idx.q), &mut p, &ast.attributes)?;
932
933    if let Some(first) = p.remaining(&ast.attributes).next() {
934        return Err(compile::Error::msg(
935            first,
936            "Attributes on structs are not supported",
937        ));
938    }
939
940    let ident = ast.ident.resolve(resolve_context!(idx.q))?;
941    let guard = idx.items.push_name(ident)?;
942
943    let visibility = ast_to_visibility(&ast.visibility)?;
944    let item_meta = idx.insert_new_item(&ast, visibility, &docs)?;
945    let idx_item = idx.item.replace(item_meta.item);
946    ast.id = item_meta.item;
947
948    let cx = resolve_context!(idx.q);
949
950    for (field, _) in ast.body.fields() {
951        let mut p = attrs::Parser::new(&field.attributes)?;
952        let docs = Doc::collect_from(cx, &mut p, &field.attributes)?;
953
954        if let Some(first) = p.remaining(&field.attributes).next() {
955            return Err(compile::Error::msg(
956                first,
957                "Attributes on fields are not supported",
958            ));
959        }
960
961        if field.ty.is_some() {
962            return Err(compile::Error::msg(
963                field,
964                "Static typing on fields is not supported",
965            ));
966        }
967
968        let name = field.name.resolve(cx)?;
969
970        for doc in docs {
971            idx.q
972                .visitor
973                .visit_field_doc_comment(
974                    &DynLocation::new(idx.source_id, &doc),
975                    idx.q.pool.item(item_meta.item),
976                    idx.q.pool.item_type_hash(item_meta.item),
977                    name,
978                    doc.doc_string.resolve(cx)?.as_ref(),
979                )
980                .with_span(doc)?;
981        }
982
983        if !field.visibility.is_inherited() {
984            return Err(compile::Error::msg(
985                field,
986                "Field visibility is not supported",
987            ));
988        }
989    }
990
991    idx.item = idx_item;
992    idx.items.pop(guard).with_span(&ast)?;
993
994    let fields = convert_fields(resolve_context!(idx.q), ast.body)?;
995    idx.q.index_struct(item_meta, indexing::Struct { fields })?;
996    Ok(())
997}
998
999#[instrument_ast(span = ast)]
1000fn item_impl(idx: &mut Indexer<'_, '_>, mut ast: ast::ItemImpl) -> compile::Result<()> {
1001    if let Some(first) = ast.attributes.first() {
1002        return Err(compile::Error::msg(
1003            first,
1004            "Attributes on impl blocks are not supported",
1005        ));
1006    }
1007
1008    path(idx, &mut ast.path)?;
1009
1010    let location = Location::new(idx.source_id, ast.path.span());
1011
1012    idx.q
1013        .inner
1014        .defer_queue
1015        .try_push_back(DeferEntry::ImplItem(ImplItem {
1016            kind: ImplItemKind::Ast {
1017                path: Box::try_new(ast.path)?,
1018                functions: take(&mut ast.functions),
1019            },
1020            location,
1021            root: idx.root.map(TryToOwned::try_to_owned).transpose()?,
1022            nested_item: idx.nested_item,
1023            macro_depth: idx.macro_depth,
1024        }))?;
1025
1026    Ok(())
1027}
1028
1029#[instrument_ast(span = ast)]
1030fn item_mod(idx: &mut Indexer<'_, '_>, mut ast: ast::ItemMod) -> compile::Result<()> {
1031    let mut p = attrs::Parser::new(&ast.attributes)?;
1032
1033    let docs = Doc::collect_from(resolve_context!(idx.q), &mut p, &ast.attributes)?;
1034
1035    if let Some(first) = p.remaining(&ast.attributes).next() {
1036        return Err(compile::Error::msg(
1037            first,
1038            "Attributes on modules are not supported",
1039        ));
1040    }
1041
1042    let name_span = ast.name_span();
1043
1044    match &mut ast.body {
1045        ast::ItemModBody::EmptyBody(..) => {
1046            idx.handle_file_mod(&mut ast, &docs)?;
1047        }
1048        ast::ItemModBody::InlineBody(body) => {
1049            let name = ast.name.resolve(resolve_context!(idx.q))?;
1050            let guard = idx.items.push_name(name.as_ref())?;
1051
1052            let visibility = ast_to_visibility(&ast.visibility)?;
1053
1054            let (mod_item, mod_item_id) = idx.q.insert_mod(
1055                &idx.items,
1056                &DynLocation::new(idx.source_id, name_span),
1057                idx.item.module,
1058                visibility,
1059                &docs,
1060            )?;
1061
1062            ast.id = mod_item_id;
1063
1064            let idx_item = idx.item.replace_module(mod_item, mod_item_id);
1065            file(idx, &mut body.file)?;
1066            idx.item = idx_item;
1067
1068            idx.items.pop(guard).with_span(&ast)?;
1069        }
1070    }
1071
1072    Ok(())
1073}
1074
1075#[instrument_ast(span = ast)]
1076fn item_const(idx: &mut Indexer<'_, '_>, mut ast: ast::ItemConst) -> compile::Result<()> {
1077    let mut p = attrs::Parser::new(&ast.attributes)?;
1078
1079    let docs = Doc::collect_from(resolve_context!(idx.q), &mut p, &ast.attributes)?;
1080
1081    if let Some(first) = p.remaining(&ast.attributes).next() {
1082        return Err(compile::Error::msg(
1083            first,
1084            "Attributes on constants are not supported",
1085        ));
1086    }
1087
1088    let name = ast.name.resolve(resolve_context!(idx.q))?;
1089    let guard = idx.items.push_name(name.as_ref())?;
1090
1091    let item_meta = idx.insert_new_item(&ast, ast_to_visibility(&ast.visibility)?, &docs)?;
1092    let idx_item = idx.item.replace(item_meta.item);
1093
1094    ast.id = item_meta.item;
1095
1096    let last = idx.nested_item.replace(ast.descriptive_span());
1097    expr(idx, &mut ast.expr)?;
1098    idx.nested_item = last;
1099
1100    idx.q.index_const_expr(
1101        item_meta,
1102        indexing::ConstExpr::Ast(Box::try_new(ast.expr.try_clone()?)?),
1103    )?;
1104
1105    idx.item = idx_item;
1106    idx.items.pop(guard).with_span(&ast)?;
1107    Ok(())
1108}
1109
1110#[instrument_ast(span = ast)]
1111fn item(idx: &mut Indexer<'_, '_>, ast: ast::Item) -> compile::Result<()> {
1112    match ast {
1113        ast::Item::Enum(item) => {
1114            item_enum(idx, item)?;
1115        }
1116        ast::Item::Struct(item) => {
1117            item_struct(idx, item)?;
1118        }
1119        ast::Item::Fn(item) => {
1120            item_fn(idx, item)?;
1121        }
1122        ast::Item::Impl(item) => {
1123            item_impl(idx, item)?;
1124        }
1125        ast::Item::Mod(item) => {
1126            item_mod(idx, item)?;
1127        }
1128        ast::Item::Const(item) => {
1129            item_const(idx, item)?;
1130        }
1131        ast::Item::MacroCall(macro_call) => {
1132            // Note: There is a preprocessing step involved with items for
1133            // which the macro must have been expanded to a built-in macro
1134            // if we end up here. So instead of expanding here, we just
1135            // assert that the builtin macro has been added to the query
1136            // engine.
1137
1138            let Some(id) = macro_call.id else {
1139                return Err(compile::Error::msg(
1140                    &macro_call,
1141                    "macro expansion id not set",
1142                ));
1143            };
1144
1145            // Assert that the built-in macro has been expanded.
1146            idx.q.builtin_macro_for(id).with_span(&macro_call)?;
1147
1148            if let Some(span) = macro_call.attributes.first() {
1149                return Err(compile::Error::msg(
1150                    span,
1151                    "attributes on macros are not supported",
1152                ));
1153            }
1154        }
1155        // NB: imports are ignored during indexing.
1156        ast::Item::Use(item_use) => {
1157            if let Some(span) = item_use.attributes.first() {
1158                return Err(compile::Error::msg(
1159                    span,
1160                    "Attributes on uses are not supported",
1161                ));
1162            }
1163
1164            let Some(queue) = idx.queue.as_mut() else {
1165                return Err(compile::Error::msg(
1166                    &item_use,
1167                    "Imports are not supported in this context",
1168                ));
1169            };
1170
1171            let visibility = ast_to_visibility(&item_use.visibility)?;
1172
1173            let import = Import {
1174                state: ImportState::Ast(Box::try_new(item_use)?),
1175                kind: ImportKind::Global,
1176                visibility,
1177                module: idx.item.module,
1178                item: idx.items.item().try_to_owned()?,
1179                source_id: idx.source_id,
1180            };
1181
1182            import.process(&mut idx.q, &mut |task| {
1183                queue.try_push_back(task)?;
1184                Ok(())
1185            })?;
1186        }
1187    }
1188
1189    Ok(())
1190}
1191
1192#[instrument_ast(span = ast)]
1193fn path(idx: &mut Indexer<'_, '_>, ast: &mut ast::Path) -> compile::Result<()> {
1194    ast.id = idx.item.id;
1195
1196    path_segment(idx, &mut ast.first)?;
1197
1198    for (_, segment) in &mut ast.rest {
1199        path_segment(idx, segment)?;
1200    }
1201
1202    Ok(())
1203}
1204
1205#[instrument_ast(span = ast)]
1206fn path_segment(idx: &mut Indexer<'_, '_>, ast: &mut ast::PathSegment) -> compile::Result<()> {
1207    if let ast::PathSegment::Generics(generics) = ast {
1208        for (param, _) in generics {
1209            expr(idx, &mut param.expr)?;
1210        }
1211    }
1212
1213    Ok(())
1214}
1215
1216#[instrument_ast(span = ast)]
1217fn expr_while(idx: &mut Indexer<'_, '_>, ast: &mut ast::ExprWhile) -> compile::Result<()> {
1218    condition(idx, &mut ast.condition)?;
1219    block(idx, &mut ast.body)?;
1220    Ok(())
1221}
1222
1223#[instrument_ast(span = ast)]
1224fn expr_loop(idx: &mut Indexer<'_, '_>, ast: &mut ast::ExprLoop) -> compile::Result<()> {
1225    block(idx, &mut ast.body)?;
1226    Ok(())
1227}
1228
1229#[instrument_ast(span = ast)]
1230fn expr_for(idx: &mut Indexer<'_, '_>, ast: &mut ast::ExprFor) -> compile::Result<()> {
1231    expr(idx, &mut ast.iter)?;
1232    pat(idx, &mut ast.binding)?;
1233    block(idx, &mut ast.body)?;
1234    Ok(())
1235}
1236
1237#[instrument_ast(span = ast)]
1238fn expr_closure(idx: &mut Indexer<'_, '_>, ast: &mut ast::ExprClosure) -> compile::Result<()> {
1239    let guard = idx.push_id()?;
1240
1241    idx.scopes.push()?;
1242
1243    let item_meta = idx.insert_new_item(&*ast, Visibility::Inherited, &[])?;
1244    let idx_item = idx.item.replace(item_meta.item);
1245
1246    ast.id = item_meta.item;
1247
1248    for (arg, _) in ast.args.as_slice_mut() {
1249        match arg {
1250            ast::FnArg::SelfValue(s) => {
1251                return Err(compile::Error::new(s, ErrorKind::UnsupportedSelf));
1252            }
1253            ast::FnArg::Pat(p) => {
1254                pat(idx, p)?;
1255            }
1256        }
1257    }
1258
1259    expr(idx, &mut ast.body)?;
1260
1261    let layer = idx.scopes.pop().with_span(&*ast)?;
1262
1263    let call = validate_call(false, ast.async_token.is_some(), &layer)?;
1264
1265    let Some(call) = call else {
1266        return Err(compile::Error::new(&*ast, ErrorKind::ClosureKind));
1267    };
1268
1269    idx.q.index_meta(
1270        ast,
1271        item_meta,
1272        meta::Kind::Closure {
1273            call,
1274            do_move: ast.move_token.is_some(),
1275        },
1276    )?;
1277
1278    idx.item = idx_item;
1279    idx.items.pop(guard).with_span(&ast)?;
1280    Ok(())
1281}
1282
1283#[instrument_ast(span = ast)]
1284fn expr_field_access(
1285    idx: &mut Indexer<'_, '_>,
1286    ast: &mut ast::ExprFieldAccess,
1287) -> compile::Result<()> {
1288    expr(idx, &mut ast.expr)?;
1289
1290    if let ast::ExprField::Path(p) = &mut ast.expr_field {
1291        path(idx, p)?;
1292    }
1293
1294    Ok(())
1295}
1296
1297#[instrument_ast(span = ast)]
1298fn expr_select(idx: &mut Indexer<'_, '_>, ast: &mut ast::ExprSelect) -> compile::Result<()> {
1299    let l = idx.scopes.mark().with_span(&*ast)?;
1300    l.awaits.try_push(ast.span())?;
1301
1302    for (branch, _) in &mut ast.branches {
1303        match branch {
1304            ast::ExprSelectBranch::Pat(p) => {
1305                expr(idx, &mut p.expr)?;
1306                pat(idx, &mut p.pat)?;
1307                expr(idx, &mut p.body)?;
1308            }
1309            ast::ExprSelectBranch::Default(def) => {
1310                expr(idx, &mut def.body)?;
1311            }
1312        }
1313    }
1314
1315    Ok(())
1316}
1317
1318#[instrument_ast(span = ast)]
1319fn expr_call(idx: &mut Indexer<'_, '_>, ast: &mut ast::ExprCall) -> compile::Result<()> {
1320    ast.id = idx.item.id;
1321
1322    for (e, _) in &mut ast.args {
1323        expr(idx, e)?;
1324    }
1325
1326    expr(idx, &mut ast.expr)?;
1327    Ok(())
1328}
1329
1330#[instrument_ast(span = ast)]
1331fn expr_object(idx: &mut Indexer<'_, '_>, ast: &mut ast::ExprObject) -> compile::Result<()> {
1332    if let ast::ObjectIdent::Named(p) = &mut ast.ident {
1333        // Not a variable use: Name of the object.
1334        path(idx, p)?;
1335    }
1336
1337    for (assign, _) in &mut ast.assignments {
1338        if let Some((_, e)) = &mut assign.assign {
1339            expr(idx, e)?;
1340        }
1341    }
1342
1343    Ok(())
1344}
1345
1346/// Convert AST fields into meta fields.
1347fn convert_fields(cx: ResolveContext<'_>, body: ast::Fields) -> compile::Result<meta::Fields> {
1348    Ok(match body {
1349        ast::Fields::Empty => meta::Fields::Empty,
1350        ast::Fields::Unnamed(tuple) => meta::Fields::Unnamed(tuple.len()),
1351        ast::Fields::Named(st) => {
1352            let mut fields = Vec::try_with_capacity(st.len())?;
1353
1354            for (position, (ast::Field { name, .. }, _)) in st.iter().enumerate() {
1355                let name = name.resolve(cx)?;
1356                fields.try_push(meta::FieldMeta {
1357                    name: name.try_into()?,
1358                    position,
1359                })?;
1360            }
1361
1362            meta::Fields::Named(meta::FieldsNamed {
1363                fields: fields.try_into()?,
1364            })
1365        }
1366    })
1367}