Skip to main content

cairo_lang_semantic/items/
macro_declaration.rs

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