cairo_lang_semantic/items/
macro_declaration.rs

1use std::sync::Arc;
2
3use cairo_lang_defs::db::DefsGroup;
4use cairo_lang_defs::ids::{
5    LanguageElementId, LookupItemId, MacroDeclarationId, ModuleId, ModuleItemId,
6};
7use cairo_lang_diagnostics::{Diagnostics, Maybe, skip_diagnostic};
8use cairo_lang_filesystem::db::FilesGroup;
9use cairo_lang_filesystem::ids::{CodeMapping, CodeOrigin, SmolStrId};
10use cairo_lang_filesystem::span::{TextSpan, TextWidth};
11use cairo_lang_parser::macro_helpers::as_expr_macro_token_tree;
12use cairo_lang_syntax::attribute::structured::{Attribute, AttributeListStructurize};
13use cairo_lang_syntax::node::ast::{MacroElement, MacroParam};
14use cairo_lang_syntax::node::ids::SyntaxStablePtrId;
15use cairo_lang_syntax::node::kind::SyntaxKind;
16use cairo_lang_syntax::node::{SyntaxNode, Terminal, TypedStablePtr, TypedSyntaxNode, ast};
17use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
18use cairo_lang_utils::ordered_hash_set::OrderedHashSet;
19use salsa::Database;
20
21use crate::SemanticDiagnostic;
22use crate::diagnostic::{SemanticDiagnosticKind, SemanticDiagnostics, SemanticDiagnosticsBuilder};
23use crate::expr::inference::InferenceId;
24use crate::keyword::{MACRO_CALL_SITE, MACRO_DEF_SITE};
25use crate::resolve::{Resolver, ResolverData};
26
27/// A unique identifier for a repetition block inside a macro rule.
28/// Each `$( ... )` group in the macro pattern gets a new `RepetitionId`.
29#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
30pub struct RepetitionId(usize);
31
32/// The captures collected during macro pattern matching.
33/// Each macro parameter name maps to a flat list of matched strings.
34type Captures<'db> = OrderedHashMap<SmolStrId<'db>, Vec<CapturedValue<'db>>>;
35
36/// Context used during macro pattern matching and expansion.
37/// Tracks captured values, active repetition scopes, and repetition ownership per placeholder.
38#[derive(Default, Clone, Debug)]
39pub struct MatcherContext<'db> {
40    /// The captured values per macro parameter name.
41    /// These are flat lists, even for repeated placeholders.
42    pub captures: Captures<'db>,
43
44    /// Maps each placeholder to the `RepetitionId` of the repetition block
45    /// they are part of. This helps the expansion phase know which iterators to advance together.
46    pub placeholder_to_rep_id: OrderedHashMap<SmolStrId<'db>, RepetitionId>,
47
48    /// Stack of currently active repetition blocks. Used to assign placeholders
49    /// to their correct `RepetitionId` while recursing into nested repetitions.
50    pub current_repetition_stack: Vec<RepetitionId>,
51
52    /// Counter for generating unique `RepetitionId`s.
53    pub next_repetition_id: usize,
54
55    /// Tracks the current index for each active repetition during expansion.
56    pub repetition_indices: OrderedHashMap<RepetitionId, usize>,
57
58    /// Count how many times each repetition matched.
59    pub repetition_match_counts: OrderedHashMap<RepetitionId, usize>,
60
61    /// Store the repetition operator for each repetition.
62    pub repetition_operators: OrderedHashMap<RepetitionId, ast::MacroRepetitionOperator<'db>>,
63}
64
65/// The semantic data for a macro declaration.
66#[derive(Debug, Clone, PartialEq, Eq, salsa::Update)]
67pub struct MacroDeclarationData<'db> {
68    rules: Vec<MacroRuleData<'db>>,
69    attributes: Vec<Attribute<'db>>,
70    diagnostics: Diagnostics<'db, SemanticDiagnostic<'db>>,
71    resolver_data: Arc<ResolverData<'db>>,
72}
73
74/// The semantic data for a single macro rule in a macro declaration.
75#[derive(Debug, Clone, PartialEq, Eq, salsa::Update)]
76pub struct MacroRuleData<'db> {
77    pub pattern: ast::WrappedMacro<'db>,
78    pub expansion: ast::MacroElements<'db>,
79}
80
81/// The possible kinds of placeholders in a macro rule.
82#[derive(Debug, Clone, PartialEq, Eq)]
83enum PlaceholderKind {
84    Identifier,
85    Expr,
86}
87
88impl<'db> From<ast::MacroParamKind<'db>> for PlaceholderKind {
89    fn from(kind: ast::MacroParamKind<'db>) -> Self {
90        match kind {
91            ast::MacroParamKind::Identifier(_) => PlaceholderKind::Identifier,
92            ast::MacroParamKind::Expr(_) => PlaceholderKind::Expr,
93            ast::MacroParamKind::Missing(_) => unreachable!(
94                "Missing macro rule param kind, should have been handled by the parser."
95            ),
96        }
97    }
98}
99
100/// Information about a captured value in a macro.
101#[derive(Clone, Debug, PartialEq, Eq)]
102pub struct CapturedValue<'db> {
103    pub text: String,
104    pub stable_ptr: SyntaxStablePtrId<'db>,
105}
106
107/// Implementation of [MacroDeclarationSemantic::priv_macro_declaration_data].
108fn priv_macro_declaration_data<'db>(
109    db: &'db dyn Database,
110    macro_declaration_id: MacroDeclarationId<'db>,
111) -> Maybe<MacroDeclarationData<'db>> {
112    let mut diagnostics = SemanticDiagnostics::default();
113
114    let module_id = macro_declaration_id.module_id(db);
115    let macro_declaration_syntax = db.module_macro_declaration_by_id(macro_declaration_id)?;
116    if !are_user_defined_inline_macros_enabled(db, module_id) {
117        diagnostics.report(
118            macro_declaration_syntax.stable_ptr(db).untyped(),
119            SemanticDiagnosticKind::UserDefinedInlineMacrosDisabled,
120        );
121    }
122
123    let attributes = macro_declaration_syntax.attributes(db).structurize(db);
124    let inference_id = InferenceId::LookupItemDeclaration(LookupItemId::ModuleItem(
125        ModuleItemId::MacroDeclaration(macro_declaration_id),
126    ));
127    let resolver = Resolver::new(db, module_id, inference_id);
128
129    // TODO(Dean): Verify uniqueness of param names.
130    // TODO(Dean): Verify consistency bracket terminals.
131    let mut rules = vec![];
132    for rule_syntax in macro_declaration_syntax.rules(db).elements(db) {
133        let pattern = rule_syntax.lhs(db);
134        let expansion = rule_syntax.rhs(db).elements(db);
135        let pattern_elements = get_macro_elements(db, pattern.clone());
136        // Collect defined placeholders from pattern
137        let defined_placeholders =
138            OrderedHashSet::<_>::from_iter(pattern_elements.elements(db).filter_map(|element| {
139                match element {
140                    ast::MacroElement::Param(param) => {
141                        Some(param.name(db).as_syntax_node().get_text_without_trivia(db))
142                    }
143                    ast::MacroElement::Repetition(repetition) => repetition
144                        .elements(db)
145                        .elements(db)
146                        .filter_map(|inner_element| match inner_element {
147                            ast::MacroElement::Param(inner_param) => Some(
148                                inner_param.name(db).as_syntax_node().get_text_without_trivia(db),
149                            ),
150                            _ => None,
151                        })
152                        .collect::<Vec<_>>()
153                        .into_iter()
154                        .next(),
155                    _ => None,
156                }
157            }));
158
159        let used_placeholders = collect_expansion_placeholders(db, expansion.as_syntax_node());
160        // Verify all used placeholders are defined
161        for (placeholder_ptr, used_placeholder) in used_placeholders {
162            if !defined_placeholders.contains(&used_placeholder) {
163                diagnostics.report(
164                    placeholder_ptr,
165                    SemanticDiagnosticKind::UndefinedMacroPlaceholder(used_placeholder),
166                );
167            }
168        }
169        rules.push(MacroRuleData { pattern, expansion });
170    }
171    let resolver_data = Arc::new(resolver.data);
172    Ok(MacroDeclarationData { diagnostics: diagnostics.build(), attributes, resolver_data, rules })
173}
174
175/// Query implementation of [MacroDeclarationSemantic::priv_macro_declaration_data].
176#[salsa::tracked]
177fn priv_macro_declaration_data_tracked<'db>(
178    db: &'db dyn Database,
179    macro_declaration_id: MacroDeclarationId<'db>,
180) -> Maybe<MacroDeclarationData<'db>> {
181    priv_macro_declaration_data(db, macro_declaration_id)
182}
183
184/// Helper function to extract pattern elements from a WrappedMacro.
185fn get_macro_elements<'db>(
186    db: &'db dyn Database,
187    pattern: ast::WrappedMacro<'db>,
188) -> ast::MacroElements<'db> {
189    match pattern {
190        ast::WrappedMacro::Parenthesized(inner) => inner.elements(db),
191        ast::WrappedMacro::Braced(inner) => inner.elements(db),
192        ast::WrappedMacro::Bracketed(inner) => inner.elements(db),
193    }
194}
195
196/// Helper function to extract a placeholder name from an ExprPath node, if it represents a macro
197/// placeholder. Returns None if the path is not a valid macro placeholder.
198fn extract_placeholder<'db>(
199    db: &'db dyn Database,
200    path_node: &MacroParam<'db>,
201) -> Option<SmolStrId<'db>> {
202    let placeholder_name = path_node.name(db).as_syntax_node().get_text_without_trivia(db);
203    if ![MACRO_DEF_SITE, MACRO_CALL_SITE].contains(&placeholder_name.long(db).as_str()) {
204        return Some(placeholder_name);
205    }
206    None
207}
208
209/// Helper function to collect all placeholder names used in a macro expansion.
210fn collect_expansion_placeholders<'db>(
211    db: &'db dyn Database,
212    node: SyntaxNode<'db>,
213) -> Vec<(SyntaxStablePtrId<'db>, SmolStrId<'db>)> {
214    let mut placeholders = Vec::new();
215    if node.kind(db) == SyntaxKind::MacroParam {
216        let path_node = MacroParam::from_syntax_node(db, node);
217        if let Some(placeholder_name) = extract_placeholder(db, &path_node) {
218            placeholders.push((path_node.stable_ptr(db).untyped(), placeholder_name));
219            return placeholders;
220        }
221    }
222    if node.kind(db) == SyntaxKind::MacroRepetition {
223        let repetition = ast::MacroRepetition::from_syntax_node(db, node);
224        for element in repetition.elements(db).elements(db) {
225            placeholders.extend(collect_expansion_placeholders(db, element.as_syntax_node()));
226        }
227        return placeholders;
228    }
229    if !node.kind(db).is_terminal() {
230        for child in node.get_children(db).iter() {
231            placeholders.extend(collect_expansion_placeholders(db, *child));
232        }
233    }
234    placeholders
235}
236
237/// Given a macro declaration and an input token tree, checks if the input the given rule, and
238/// returns the captured params if it does.
239pub fn is_macro_rule_match<'db>(
240    db: &'db dyn Database,
241    rule: &MacroRuleData<'db>,
242    input: &ast::TokenTreeNode<'db>,
243) -> Option<(Captures<'db>, OrderedHashMap<SmolStrId<'db>, RepetitionId>)> {
244    let mut ctx = MatcherContext::default();
245
246    let matcher_elements = get_macro_elements(db, rule.pattern.clone());
247    let input_elements = match input.subtree(db) {
248        ast::WrappedTokenTree::Parenthesized(tt) => tt.tokens(db),
249        ast::WrappedTokenTree::Braced(tt) => tt.tokens(db),
250        ast::WrappedTokenTree::Bracketed(tt) => tt.tokens(db),
251        ast::WrappedTokenTree::Missing(_) => unreachable!(),
252    }
253    .elements_vec(db);
254    let mut input_iter = input_elements.iter().peekable();
255    is_macro_rule_match_ex(db, matcher_elements, &mut input_iter, &mut ctx, true)?;
256    if !validate_repetition_operator_constraints(&ctx) {
257        return None;
258    }
259    Some((ctx.captures, ctx.placeholder_to_rep_id))
260}
261
262/// Helper function for [expand_macro_rule].
263/// Traverses the macro expansion and replaces the placeholders with the provided values,
264/// while collecting the result in `res_buffer`.
265fn is_macro_rule_match_ex<'db>(
266    db: &'db dyn Database,
267    matcher_elements: ast::MacroElements<'db>,
268    input_iter: &mut std::iter::Peekable<std::slice::Iter<'_, ast::TokenTree<'db>>>,
269    ctx: &mut MatcherContext<'db>,
270    consume_all_input: bool,
271) -> Option<()> {
272    for matcher_element in matcher_elements.elements(db) {
273        match matcher_element {
274            ast::MacroElement::Token(matcher_token) => {
275                let input_token = input_iter.next()?;
276                match input_token {
277                    ast::TokenTree::Token(token_tree_leaf) => {
278                        if matcher_token.as_syntax_node().get_text_without_trivia(db)
279                            != token_tree_leaf.as_syntax_node().get_text_without_trivia(db)
280                        {
281                            return None;
282                        }
283                        continue;
284                    }
285                    ast::TokenTree::Subtree(_) => return None,
286                    ast::TokenTree::Repetition(_) => return None,
287                    ast::TokenTree::Param(_) => return None,
288                    ast::TokenTree::Missing(_) => unreachable!(),
289                }
290            }
291            ast::MacroElement::Param(param) => {
292                let placeholder_kind: PlaceholderKind =
293                    if let ast::OptionParamKind::ParamKind(param_kind) = param.kind(db) {
294                        param_kind.kind(db).into()
295                    } else {
296                        unreachable!(
297                            "Missing macro rule param kind, should have been handled by the \
298                             parser."
299                        )
300                    };
301                let placeholder_name = param.name(db).as_syntax_node().get_text_without_trivia(db);
302                match placeholder_kind {
303                    PlaceholderKind::Identifier => {
304                        let input_token = input_iter.next()?;
305                        let captured_text = match input_token {
306                            ast::TokenTree::Token(token_tree_leaf) => {
307                                match token_tree_leaf.leaf(db) {
308                                    ast::TokenNode::TerminalIdentifier(terminal_identifier) => {
309                                        terminal_identifier.text(db).to_string(db)
310                                    }
311                                    _ => return None,
312                                }
313                            }
314                            _ => return None,
315                        };
316                        ctx.captures.entry(placeholder_name).or_default().push(CapturedValue {
317                            text: captured_text,
318                            stable_ptr: input_token.stable_ptr(db).untyped(),
319                        });
320                        if let Some(rep_id) = ctx.current_repetition_stack.last() {
321                            ctx.placeholder_to_rep_id.insert(placeholder_name, *rep_id);
322                        }
323                        continue;
324                    }
325                    PlaceholderKind::Expr => {
326                        let mut cloned_iter = input_iter.clone();
327                        let peek_token = cloned_iter.peek()?;
328                        let file_id = peek_token.as_syntax_node().stable_ptr(db).file_id(db);
329                        let expr_node =
330                            as_expr_macro_token_tree(input_iter.clone().cloned(), file_id, db)?;
331                        let expr_text = expr_node.as_syntax_node().get_text(db);
332                        let expr_length = expr_text.len();
333                        // An empty expression is parsed successfully. However we don't want to
334                        // capture it a valid expr.
335                        if expr_length == 0 {
336                            return None;
337                        }
338
339                        ctx.captures.entry(placeholder_name).or_default().push(CapturedValue {
340                            text: expr_text.to_string(),
341                            stable_ptr: peek_token.stable_ptr(db).untyped(),
342                        });
343                        if let Some(rep_id) = ctx.current_repetition_stack.last() {
344                            ctx.placeholder_to_rep_id.insert(placeholder_name, *rep_id);
345                        }
346                        let expr_length = expr_text.len();
347                        let mut current_length = 0;
348
349                        // TODO(Dean): Use the iterator directly in the parser and advance it while
350                        // parsing the expression, instead of manually tracking the length and
351                        // iterating separately.
352                        for token_tree_leaf in input_iter.by_ref() {
353                            let token_text = match token_tree_leaf {
354                                ast::TokenTree::Token(token_tree_leaf) => {
355                                    token_tree_leaf.as_syntax_node().get_text(db)
356                                }
357                                ast::TokenTree::Subtree(token_subtree) => {
358                                    token_subtree.as_syntax_node().get_text(db)
359                                }
360                                ast::TokenTree::Repetition(token_repetition) => {
361                                    token_repetition.as_syntax_node().get_text(db)
362                                }
363                                ast::TokenTree::Param(token_param) => {
364                                    token_param.as_syntax_node().get_text(db)
365                                }
366                                ast::TokenTree::Missing(_) => unreachable!(),
367                            };
368                            current_length += token_text.len();
369                            if current_length >= expr_length {
370                                break;
371                            }
372                        }
373                        continue;
374                    }
375                }
376            }
377            ast::MacroElement::Subtree(matcher_subtree) => {
378                let input_token = input_iter.next()?;
379                if let ast::TokenTree::Subtree(input_subtree) = input_token {
380                    let inner_elements = get_macro_elements(db, matcher_subtree.subtree(db));
381                    let inner_input_elements = match input_subtree.subtree(db) {
382                        ast::WrappedTokenTree::Parenthesized(tt) => tt.tokens(db),
383                        ast::WrappedTokenTree::Braced(tt) => tt.tokens(db),
384                        ast::WrappedTokenTree::Bracketed(tt) => tt.tokens(db),
385                        ast::WrappedTokenTree::Missing(_) => unreachable!(),
386                    }
387                    .elements_vec(db);
388                    let mut inner_input_iter = inner_input_elements.iter().peekable();
389                    is_macro_rule_match_ex(db, inner_elements, &mut inner_input_iter, ctx, true)?;
390                    continue;
391                } else {
392                    return None;
393                }
394            }
395            ast::MacroElement::Repetition(repetition) => {
396                let rep_id = RepetitionId(ctx.next_repetition_id);
397                ctx.next_repetition_id += 1;
398                ctx.current_repetition_stack.push(rep_id);
399                let elements = repetition.elements(db);
400                let operator = repetition.operator(db);
401                let separator_token = repetition.separator(db);
402                let expected_separator = match separator_token {
403                    ast::OptionTerminalComma::TerminalComma(sep) => {
404                        Some(sep.as_syntax_node().get_text_without_trivia(db))
405                    }
406                    ast::OptionTerminalComma::Empty(_) => None,
407                };
408                let mut match_count = 0;
409                loop {
410                    let mut inner_ctx = ctx.clone();
411                    let mut temp_iter = input_iter.clone();
412                    if is_macro_rule_match_ex(
413                        db,
414                        elements.clone(),
415                        &mut temp_iter,
416                        &mut inner_ctx,
417                        false,
418                    )
419                    .is_none()
420                    {
421                        break;
422                    }
423                    *ctx = inner_ctx;
424                    *input_iter = temp_iter;
425                    match_count += 1;
426                    if let Some(expected_sep) = &expected_separator {
427                        if let Some(ast::TokenTree::Token(token_leaf)) = input_iter.peek() {
428                            let actual = token_leaf.as_syntax_node().get_text_without_trivia(db);
429                            if actual == *expected_sep {
430                                input_iter.next();
431                            } else {
432                                break;
433                            }
434                        } else {
435                            break;
436                        }
437                    }
438                }
439                ctx.repetition_match_counts.insert(rep_id, match_count);
440                ctx.repetition_operators.insert(rep_id, operator.clone());
441                for placeholder_name in ctx.captures.keys() {
442                    ctx.placeholder_to_rep_id.insert(*placeholder_name, rep_id);
443                }
444
445                for i in 0..match_count {
446                    ctx.repetition_indices.insert(rep_id, i);
447                }
448                ctx.current_repetition_stack.pop();
449                continue;
450            }
451        }
452    }
453
454    if consume_all_input && input_iter.next().is_some() {
455        return None;
456    }
457    Some(())
458}
459
460fn validate_repetition_operator_constraints(ctx: &MatcherContext<'_>) -> bool {
461    for (rep_id, count) in ctx.repetition_match_counts.clone() {
462        match ctx.repetition_operators.get(&rep_id) {
463            Some(ast::MacroRepetitionOperator::ZeroOrOne(_)) if count > 1 => return false,
464            Some(ast::MacroRepetitionOperator::OneOrMore(_)) if count < 1 => return false,
465            Some(ast::MacroRepetitionOperator::ZeroOrMore(_)) | None => {}
466            _ => {}
467        }
468    }
469    true
470}
471
472/// The result of expanding a macro rule.
473#[derive(Debug, Clone, PartialEq, Eq)]
474pub struct MacroExpansionResult {
475    /// The expanded text.
476    pub text: Arc<str>,
477    /// Information about placeholder expansions in this macro expansion.
478    pub code_mappings: Arc<[CodeMapping]>,
479}
480
481/// Traverse the macro expansion and replace the placeholders with the provided values, creates a
482/// string representation of the expanded macro.
483///
484/// Returns an error if any used placeholder in the expansion is not found in the captures.
485/// When an error is returned, appropriate diagnostics will already have been reported.
486pub fn expand_macro_rule(
487    db: &dyn Database,
488    rule: &MacroRuleData<'_>,
489    matcher_ctx: &mut MatcherContext<'_>,
490) -> Maybe<MacroExpansionResult> {
491    let node = rule.expansion.as_syntax_node();
492    let mut res_buffer = String::new();
493    let mut code_mappings = Vec::new();
494    expand_macro_rule_ex(db, node, matcher_ctx, &mut res_buffer, &mut code_mappings)?;
495    Ok(MacroExpansionResult { text: res_buffer.into(), code_mappings: code_mappings.into() })
496}
497
498/// Helper function for [expand_macro_rule]. Traverses the macro expansion and replaces the
499/// placeholders with the provided values while collecting the result in res_buffer.
500///
501/// Returns an error if a placeholder is not found in captures.
502/// When an error is returned, appropriate diagnostics will already have been reported.
503fn expand_macro_rule_ex(
504    db: &dyn Database,
505    node: SyntaxNode<'_>,
506    matcher_ctx: &mut MatcherContext<'_>,
507    res_buffer: &mut String,
508    code_mappings: &mut Vec<CodeMapping>,
509) -> Maybe<()> {
510    match node.kind(db) {
511        SyntaxKind::MacroParam => {
512            let path_node = MacroParam::from_syntax_node(db, node);
513            if let Some(name) = extract_placeholder(db, &path_node) {
514                let rep_index = matcher_ctx
515                    .placeholder_to_rep_id
516                    .get(&name)
517                    .and_then(|rep_id| matcher_ctx.repetition_indices.get(rep_id))
518                    .copied();
519                let value = matcher_ctx
520                    .captures
521                    .get(&name)
522                    .and_then(|v| rep_index.map_or_else(|| v.first(), |i| v.get(i)))
523                    .ok_or_else(skip_diagnostic)?;
524                let start = TextWidth::from_str(res_buffer).as_offset();
525                let span = TextSpan::new_with_width(start, TextWidth::from_str(&value.text));
526                res_buffer.push_str(&value.text);
527                code_mappings.push(CodeMapping {
528                    span,
529                    origin: CodeOrigin::Span(value.stable_ptr.lookup(db).span_without_trivia(db)),
530                });
531                return Ok(());
532            }
533        }
534        SyntaxKind::MacroRepetition => {
535            let repetition = ast::MacroRepetition::from_syntax_node(db, node);
536            let elements = repetition.elements(db).elements_vec(db);
537            let repetition_params = get_repetition_params(db, elements.iter().cloned());
538            let first_param = repetition_params.first().ok_or_else(skip_diagnostic)?;
539            let placeholder_name = first_param.name(db).text(db);
540            let rep_id = *matcher_ctx
541                .placeholder_to_rep_id
542                .get(&placeholder_name)
543                .ok_or_else(skip_diagnostic)?;
544            let repetition_len =
545                matcher_ctx.captures.get(&placeholder_name).map(|v| v.len()).unwrap_or(0);
546            for i in 0..repetition_len {
547                matcher_ctx.repetition_indices.insert(rep_id, i);
548                for element in &elements {
549                    expand_macro_rule_ex(
550                        db,
551                        element.as_syntax_node(),
552                        matcher_ctx,
553                        res_buffer,
554                        code_mappings,
555                    )?;
556                }
557
558                if i + 1 < repetition_len
559                    && let ast::OptionTerminalComma::TerminalComma(sep) = repetition.separator(db)
560                {
561                    res_buffer.push_str(sep.as_syntax_node().get_text(db));
562                }
563            }
564
565            matcher_ctx.repetition_indices.swap_remove(&rep_id);
566            return Ok(());
567        }
568        _ => {
569            if node.kind(db).is_terminal() {
570                res_buffer.push_str(node.get_text(db));
571                return Ok(());
572            }
573
574            for child in node.get_children(db).iter() {
575                expand_macro_rule_ex(db, *child, matcher_ctx, res_buffer, code_mappings)?;
576            }
577            return Ok(());
578        }
579    }
580    if node.kind(db).is_terminal() {
581        res_buffer.push_str(node.get_text(db));
582        return Ok(());
583    }
584    for child in node.get_children(db).iter() {
585        expand_macro_rule_ex(db, *child, matcher_ctx, res_buffer, code_mappings)?;
586    }
587    Ok(())
588}
589
590/// Gets a Vec of MacroElement, and returns a vec of the params within it.
591fn get_repetition_params<'db>(
592    db: &'db dyn Database,
593    elements: impl IntoIterator<Item = MacroElement<'db>>,
594) -> Vec<MacroParam<'db>> {
595    let mut params = vec![];
596    repetition_params_extend(db, elements, &mut params);
597    params
598}
599
600/// Recursively extends the provided params vector with all params within the given macro elements.
601fn repetition_params_extend<'db>(
602    db: &'db dyn Database,
603    elements: impl IntoIterator<Item = MacroElement<'db>>,
604    params: &mut Vec<MacroParam<'db>>,
605) {
606    for element in elements {
607        match element {
608            ast::MacroElement::Param(param) => {
609                params.push(param);
610            }
611            ast::MacroElement::Subtree(subtree) => {
612                let inner_elements = get_macro_elements(db, subtree.subtree(db)).elements(db);
613                repetition_params_extend(db, inner_elements, params);
614            }
615            ast::MacroElement::Repetition(repetition) => {
616                let inner_elements = repetition.elements(db).elements(db);
617                repetition_params_extend(db, inner_elements, params);
618            }
619            ast::MacroElement::Token(_) => {}
620        }
621    }
622}
623
624/// Implementation of [MacroDeclarationSemantic::macro_declaration_diagnostics].
625fn macro_declaration_diagnostics<'db>(
626    db: &'db dyn Database,
627    macro_declaration_id: MacroDeclarationId<'db>,
628) -> Diagnostics<'db, SemanticDiagnostic<'db>> {
629    priv_macro_declaration_data(db, macro_declaration_id)
630        .map(|data| data.diagnostics)
631        .unwrap_or_default()
632}
633
634/// Query implementation of [MacroDeclarationSemantic::macro_declaration_diagnostics].
635#[salsa::tracked]
636fn macro_declaration_diagnostics_tracked<'db>(
637    db: &'db dyn Database,
638    macro_declaration_id: MacroDeclarationId<'db>,
639) -> Diagnostics<'db, SemanticDiagnostic<'db>> {
640    macro_declaration_diagnostics(db, macro_declaration_id)
641}
642
643/// Implementation of [MacroDeclarationSemantic::macro_declaration_attributes].
644fn macro_declaration_attributes<'db>(
645    db: &'db dyn Database,
646    macro_declaration_id: MacroDeclarationId<'db>,
647) -> Maybe<Vec<Attribute<'db>>> {
648    priv_macro_declaration_data(db, macro_declaration_id).map(|data| data.attributes)
649}
650
651/// Query implementation of [MacroDeclarationSemantic::macro_declaration_attributes].
652#[salsa::tracked]
653fn macro_declaration_attributes_tracked<'db>(
654    db: &'db dyn Database,
655    macro_declaration_id: MacroDeclarationId<'db>,
656) -> Maybe<Vec<Attribute<'db>>> {
657    macro_declaration_attributes(db, macro_declaration_id)
658}
659
660/// Implementation of [MacroDeclarationSemantic::macro_declaration_resolver_data].
661fn macro_declaration_resolver_data<'db>(
662    db: &'db dyn Database,
663    macro_declaration_id: MacroDeclarationId<'db>,
664) -> Maybe<Arc<ResolverData<'db>>> {
665    priv_macro_declaration_data(db, macro_declaration_id).map(|data| data.resolver_data)
666}
667
668/// Query implementation of [MacroDeclarationSemantic::macro_declaration_resolver_data].
669#[salsa::tracked]
670fn macro_declaration_resolver_data_tracked<'db>(
671    db: &'db dyn Database,
672    macro_declaration_id: MacroDeclarationId<'db>,
673) -> Maybe<Arc<ResolverData<'db>>> {
674    macro_declaration_resolver_data(db, macro_declaration_id)
675}
676
677/// Implementation of [MacroDeclarationSemantic::macro_declaration_rules].
678fn macro_declaration_rules<'db>(
679    db: &'db dyn Database,
680    macro_declaration_id: MacroDeclarationId<'db>,
681) -> Maybe<Vec<MacroRuleData<'db>>> {
682    priv_macro_declaration_data(db, macro_declaration_id).map(|data| data.rules)
683}
684
685/// Query implementation of [MacroDeclarationSemantic::macro_declaration_rules].
686#[salsa::tracked]
687fn macro_declaration_rules_tracked<'db>(
688    db: &'db dyn Database,
689    macro_declaration_id: MacroDeclarationId<'db>,
690) -> Maybe<Vec<MacroRuleData<'db>>> {
691    macro_declaration_rules(db, macro_declaration_id)
692}
693
694/// Returns true if user defined user macros are enabled for the given module.
695fn are_user_defined_inline_macros_enabled<'db>(
696    db: &dyn Database,
697    module_id: ModuleId<'db>,
698) -> bool {
699    let owning_crate = module_id.owning_crate(db);
700    let Some(config) = db.crate_config(owning_crate) else { return false };
701    config.settings.experimental_features.user_defined_inline_macros
702}
703
704/// Trait for macro declaration-related semantic queries.
705pub trait MacroDeclarationSemantic<'db>: Database {
706    /// Private query to compute data about a macro declaration.
707    fn priv_macro_declaration_data(
708        &'db self,
709        macro_id: MacroDeclarationId<'db>,
710    ) -> Maybe<MacroDeclarationData<'db>> {
711        priv_macro_declaration_data_tracked(self.as_dyn_database(), macro_id)
712    }
713    /// Returns the semantic diagnostics of a macro declaration.
714    fn macro_declaration_diagnostics(
715        &'db self,
716        macro_id: MacroDeclarationId<'db>,
717    ) -> Diagnostics<'db, SemanticDiagnostic<'db>> {
718        macro_declaration_diagnostics_tracked(self.as_dyn_database(), macro_id)
719    }
720    /// Returns the resolver data of a macro declaration.
721    fn macro_declaration_resolver_data(
722        &'db self,
723        macro_id: MacroDeclarationId<'db>,
724    ) -> Maybe<Arc<ResolverData<'db>>> {
725        macro_declaration_resolver_data_tracked(self.as_dyn_database(), macro_id)
726    }
727    /// Returns the attributes of a macro declaration.
728    fn macro_declaration_attributes(
729        &'db self,
730        macro_id: MacroDeclarationId<'db>,
731    ) -> Maybe<Vec<Attribute<'db>>> {
732        macro_declaration_attributes_tracked(self.as_dyn_database(), macro_id)
733    }
734    /// Returns the rules semantic data of a macro declaration.
735    fn macro_declaration_rules(
736        &'db self,
737        macro_id: MacroDeclarationId<'db>,
738    ) -> Maybe<Vec<MacroRuleData<'db>>> {
739        macro_declaration_rules_tracked(self.as_dyn_database(), macro_id)
740    }
741}
742impl<'db, T: Database + ?Sized> MacroDeclarationSemantic<'db> for T {}