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 salsa::Database;
19
20use crate::SemanticDiagnostic;
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#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
29pub struct RepetitionId(usize);
30
31type Captures<'db> = OrderedHashMap<SmolStrId<'db>, Vec<CapturedValue<'db>>>;
34
35#[derive(Default, Clone, Debug)]
38pub struct MatcherContext<'db> {
39 pub captures: Captures<'db>,
42
43 pub placeholder_to_rep_id: OrderedHashMap<SmolStrId<'db>, RepetitionId>,
46
47 pub current_repetition_stack: Vec<RepetitionId>,
50
51 pub next_repetition_id: usize,
53
54 pub repetition_indices: OrderedHashMap<RepetitionId, usize>,
56
57 pub repetition_match_counts: OrderedHashMap<RepetitionId, usize>,
59
60 pub repetition_operators: OrderedHashMap<RepetitionId, ast::MacroRepetitionOperator<'db>>,
62}
63
64#[derive(Debug, Clone, PartialEq, Eq, salsa::Update)]
66pub struct MacroDeclarationData<'db> {
67 rules: Vec<MacroRuleData<'db>>,
68 attributes: Vec<Attribute<'db>>,
69 diagnostics: Diagnostics<'db, SemanticDiagnostic<'db>>,
70 resolver_data: Arc<ResolverData<'db>>,
71}
72
73#[derive(Debug, Clone, PartialEq, Eq, salsa::Update)]
75pub struct MacroRuleData<'db> {
76 pub pattern: ast::WrappedMacro<'db>,
77 pub expansion: ast::MacroElements<'db>,
78 pub err: Maybe<()>,
81}
82
83#[derive(Debug, Clone, PartialEq, Eq)]
85enum PlaceholderKind {
86 Identifier,
87 Expr,
88}
89
90impl<'db> From<ast::MacroParamKind<'db>> for PlaceholderKind {
91 fn from(kind: ast::MacroParamKind<'db>) -> Self {
92 match kind {
93 ast::MacroParamKind::Identifier(_) => PlaceholderKind::Identifier,
94 ast::MacroParamKind::Expr(_) => PlaceholderKind::Expr,
95 ast::MacroParamKind::Missing(_) => unreachable!(
96 "Missing macro rule param kind, should have been handled by the parser."
97 ),
98 }
99 }
100}
101
102#[derive(Clone, Debug, PartialEq, Eq)]
104pub struct CapturedValue<'db> {
105 pub text: String,
106 pub stable_ptr: SyntaxStablePtrId<'db>,
107}
108
109fn priv_macro_declaration_data<'db>(
111 db: &'db dyn Database,
112 macro_declaration_id: MacroDeclarationId<'db>,
113) -> Maybe<MacroDeclarationData<'db>> {
114 let module_id = macro_declaration_id.parent_module(db);
115 let mut diagnostics = SemanticDiagnostics::new(module_id);
116
117 let macro_declaration_syntax = db.module_macro_declaration_by_id(macro_declaration_id)?;
118 if !are_user_defined_inline_macros_enabled(db, module_id) {
119 diagnostics.report(
120 macro_declaration_syntax.stable_ptr(db).untyped(),
121 SemanticDiagnosticKind::UserDefinedInlineMacrosDisabled,
122 );
123 }
124
125 let attributes = macro_declaration_syntax.attributes(db).structurize(db);
126 let inference_id = InferenceId::LookupItemDeclaration(LookupItemId::ModuleItem(
127 ModuleItemId::MacroDeclaration(macro_declaration_id),
128 ));
129 let resolver = Resolver::new(db, module_id, inference_id);
130
131 let mut rules = vec![];
134 for rule_syntax in macro_declaration_syntax.rules(db).elements(db) {
135 let pattern = rule_syntax.lhs(db);
136 let expansion = rule_syntax.rhs(db).elements(db);
137 let pattern_elements = get_macro_elements(db, pattern.clone());
138 let mut placeholder_paths: OrderedHashMap<SmolStrId<'db>, Vec<usize>> = Default::default();
141 let mut next_rep_id = 0;
142 collect_placeholder_paths(
143 db,
144 pattern_elements.elements(db),
145 &mut vec![],
146 &mut next_rep_id,
147 &mut placeholder_paths,
148 );
149
150 let mut ctx = ExpansionCheckCtx {
151 db,
152 known_path: &[],
153 curr_rep_depth: 0,
154 placeholder_paths: &placeholder_paths,
155 diagnostics: &mut diagnostics,
156 rule_err: Ok(()),
157 };
158 ctx.check_node(expansion.as_syntax_node());
159 rules.push(MacroRuleData { pattern, expansion, err: ctx.rule_err });
160 }
161 let resolver_data = Arc::new(resolver.data);
162 Ok(MacroDeclarationData { diagnostics: diagnostics.build(), attributes, resolver_data, rules })
163}
164
165#[salsa::tracked]
167fn priv_macro_declaration_data_tracked<'db>(
168 db: &'db dyn Database,
169 macro_declaration_id: MacroDeclarationId<'db>,
170) -> Maybe<MacroDeclarationData<'db>> {
171 priv_macro_declaration_data(db, macro_declaration_id)
172}
173
174fn get_macro_elements<'db>(
176 db: &'db dyn Database,
177 pattern: ast::WrappedMacro<'db>,
178) -> ast::MacroElements<'db> {
179 match pattern {
180 ast::WrappedMacro::Parenthesized(inner) => inner.elements(db),
181 ast::WrappedMacro::Braced(inner) => inner.elements(db),
182 ast::WrappedMacro::Bracketed(inner) => inner.elements(db),
183 }
184}
185
186fn extract_placeholder<'db>(
189 db: &'db dyn Database,
190 path_node: &MacroParam<'db>,
191) -> Option<SmolStrId<'db>> {
192 let placeholder_name = path_node.name(db).as_syntax_node().get_text_without_trivia(db);
193 if ![MACRO_DEF_SITE, MACRO_CALL_SITE].contains(&placeholder_name.long(db).as_str()) {
194 return Some(placeholder_name);
195 }
196 None
197}
198
199fn collect_placeholder_paths<'db>(
204 db: &'db dyn Database,
205 elements: impl IntoIterator<Item = ast::MacroElement<'db>>,
206 current_path: &mut Vec<usize>,
207 next_rep_id: &mut usize,
208 result: &mut OrderedHashMap<SmolStrId<'db>, Vec<usize>>,
209) {
210 for element in elements {
211 match element {
212 ast::MacroElement::Param(param) => {
213 result.insert(
214 param.name(db).as_syntax_node().get_text_without_trivia(db),
215 current_path.clone(),
216 );
217 }
218 ast::MacroElement::Repetition(rep) => {
219 let rep_id = *next_rep_id;
220 *next_rep_id += 1;
221 current_path.push(rep_id);
222 let inner = rep.elements(db).elements(db);
223 collect_placeholder_paths(db, inner, current_path, next_rep_id, result);
224 assert_eq!(current_path.pop(), Some(rep_id));
225 }
226 ast::MacroElement::Subtree(subtree) => {
227 let inner = get_macro_elements(db, subtree.subtree(db)).elements(db);
228 collect_placeholder_paths(db, inner, current_path, next_rep_id, result);
229 }
230 ast::MacroElement::Token(_) => {}
231 }
232 }
233}
234
235struct ExpansionCheckCtx<'db, 'a> {
237 db: &'db dyn Database,
238 placeholder_paths: &'a OrderedHashMap<SmolStrId<'db>, Vec<usize>>,
241 curr_rep_depth: usize,
244 known_path: &'a [usize],
249 diagnostics: &'a mut SemanticDiagnostics<'db>,
250 rule_err: Maybe<()>,
252}
253
254impl<'db> ExpansionCheckCtx<'db, '_> {
255 fn check_node(&mut self, node: SyntaxNode<'db>) {
261 let db = self.db;
262 if let Some(param) = MacroParam::cast(db, node) {
263 if let Some(name) = extract_placeholder(db, ¶m) {
264 let ptr = param.stable_ptr(db).untyped();
265 match self.placeholder_paths.get(&name) {
266 None => {
267 self.rule_err = Err(self
268 .diagnostics
269 .report(ptr, SemanticDiagnosticKind::UndefinedMacroPlaceholder(name)));
270 }
271 Some(path) => {
272 if path.len() > self.curr_rep_depth {
273 self.rule_err = Err(self.diagnostics.report(
274 ptr,
275 SemanticDiagnosticKind::MacroPlaceholderRepDepthMismatch {
276 name,
277 required: path.len(),
278 actual: self.curr_rep_depth,
279 },
280 ));
281 } else {
282 let cmp_size = path.len().min(self.known_path.len());
283 if path[..cmp_size] != self.known_path[..cmp_size] {
284 self.rule_err = Err(self.diagnostics.report(
285 ptr,
286 SemanticDiagnosticKind::MacroPlaceholderRepDriverMismatch(name),
287 ));
288 } else if path.len() > self.known_path.len() {
289 self.known_path = path;
290 }
291 }
292 }
293 }
294 }
295 return;
296 }
297
298 if let Some(repetition) = ast::MacroRepetition::cast(db, node) {
299 self.curr_rep_depth += 1;
300 for element in repetition.elements(db).elements(db) {
301 self.check_node(element.as_syntax_node());
302 }
303 self.curr_rep_depth -= 1;
304 if self.curr_rep_depth < self.known_path.len() {
305 self.known_path = &self.known_path[..self.curr_rep_depth];
307 }
308 } else if !node.kind(db).is_terminal() {
309 for child in node.get_children(db).iter() {
310 self.check_node(*child);
311 }
312 }
313 }
314}
315
316pub fn is_macro_rule_match<'db>(
319 db: &'db dyn Database,
320 rule: &MacroRuleData<'db>,
321 input: &ast::TokenTreeNode<'db>,
322) -> Option<(Captures<'db>, OrderedHashMap<SmolStrId<'db>, RepetitionId>)> {
323 let mut ctx = MatcherContext::default();
324
325 let matcher_elements = get_macro_elements(db, rule.pattern.clone());
326 let mut input_iter = match input.subtree(db) {
327 ast::WrappedTokenTree::Parenthesized(tt) => tt.tokens(db),
328 ast::WrappedTokenTree::Braced(tt) => tt.tokens(db),
329 ast::WrappedTokenTree::Bracketed(tt) => tt.tokens(db),
330 ast::WrappedTokenTree::Missing(_) => unreachable!(),
331 }
332 .elements(db)
333 .peekable();
334 is_macro_rule_match_ex(db, matcher_elements, &mut input_iter, &mut ctx, true)?;
335 if !validate_repetition_operator_constraints(&ctx) {
336 return None;
337 }
338 Some((ctx.captures, ctx.placeholder_to_rep_id))
339}
340
341fn is_macro_rule_match_ex<'db>(
348 db: &'db dyn Database,
349 matcher_elements: ast::MacroElements<'db>,
350 input_iter: &mut std::iter::Peekable<
351 impl DoubleEndedIterator<Item = ast::TokenTree<'db>> + Clone,
352 >,
353 ctx: &mut MatcherContext<'db>,
354 consume_all_input: bool,
355) -> Option<bool> {
356 let mut advanced = false;
357 for matcher_element in matcher_elements.elements(db) {
358 match matcher_element {
359 ast::MacroElement::Token(matcher_token) => {
360 advanced = true;
361 let input_token = input_iter.next()?;
362 match input_token {
363 ast::TokenTree::Token(token_tree_leaf) => {
364 if matcher_token.as_syntax_node().get_text_without_trivia(db)
365 != token_tree_leaf.as_syntax_node().get_text_without_trivia(db)
366 {
367 return None;
368 }
369 continue;
370 }
371 ast::TokenTree::Subtree(_) => return None,
372 ast::TokenTree::Repetition(_) => return None,
373 ast::TokenTree::Param(_) => return None,
374 ast::TokenTree::Missing(_) => unreachable!(),
375 }
376 }
377 ast::MacroElement::Param(param) => {
378 advanced = true;
379 let placeholder_kind: PlaceholderKind =
380 if let ast::OptionParamKind::ParamKind(param_kind) = param.kind(db) {
381 param_kind.kind(db).into()
382 } else {
383 return None;
384 };
385 let placeholder_name = param.name(db).as_syntax_node().get_text_without_trivia(db);
386 match placeholder_kind {
387 PlaceholderKind::Identifier => {
388 let input_token = input_iter.next()?;
389 let captured_text = match &input_token {
390 ast::TokenTree::Token(token_tree_leaf) => {
391 match token_tree_leaf.leaf(db) {
392 ast::TokenNode::TerminalIdentifier(terminal_identifier) => {
393 terminal_identifier.text(db).to_string(db)
394 }
395 _ => return None,
396 }
397 }
398 _ => return None,
399 };
400 ctx.captures.entry(placeholder_name).or_default().push(CapturedValue {
401 text: captured_text,
402 stable_ptr: input_token.stable_ptr(db).untyped(),
403 });
404 if let Some(rep_id) = ctx.current_repetition_stack.last() {
405 ctx.placeholder_to_rep_id.insert(placeholder_name, *rep_id);
406 }
407 continue;
408 }
409 PlaceholderKind::Expr => {
410 let peek_token = input_iter.peek().cloned()?;
411 let file_id = peek_token.as_syntax_node().stable_ptr(db).file_id(db);
412 let expr_node = as_expr_macro_token_tree(input_iter.clone(), file_id, db)?;
413 let expr_text = expr_node.as_syntax_node().get_text(db);
414 let expr_length = expr_text.len();
415 if expr_length == 0 {
418 return None;
419 }
420
421 ctx.captures.entry(placeholder_name).or_default().push(CapturedValue {
422 text: expr_text.to_string(),
423 stable_ptr: peek_token.stable_ptr(db).untyped(),
424 });
425 if let Some(rep_id) = ctx.current_repetition_stack.last() {
426 ctx.placeholder_to_rep_id.insert(placeholder_name, *rep_id);
427 }
428 let expr_length = expr_text.len();
429 let mut current_length = 0;
430
431 for token_tree_leaf in input_iter.by_ref() {
435 let token_text = match token_tree_leaf {
436 ast::TokenTree::Token(leaf) => leaf.as_syntax_node(),
437 ast::TokenTree::Subtree(subtree) => subtree.as_syntax_node(),
438 ast::TokenTree::Repetition(rep) => rep.as_syntax_node(),
439 ast::TokenTree::Param(param) => param.as_syntax_node(),
440 ast::TokenTree::Missing(_) => unreachable!(),
441 }
442 .get_text(db);
443 current_length += token_text.len();
444 if current_length >= expr_length {
445 break;
446 }
447 }
448 continue;
449 }
450 }
451 }
452 ast::MacroElement::Subtree(matcher_subtree) => {
453 advanced = true;
454 let input_token = input_iter.next()?;
455 if let ast::TokenTree::Subtree(input_subtree) = input_token {
456 let inner_elements = get_macro_elements(db, matcher_subtree.subtree(db));
457 let mut inner_input_iter = match input_subtree.subtree(db) {
458 ast::WrappedTokenTree::Parenthesized(tt) => tt.tokens(db),
459 ast::WrappedTokenTree::Braced(tt) => tt.tokens(db),
460 ast::WrappedTokenTree::Bracketed(tt) => tt.tokens(db),
461 ast::WrappedTokenTree::Missing(_) => unreachable!(),
462 }
463 .elements(db)
464 .peekable();
465 is_macro_rule_match_ex(db, inner_elements, &mut inner_input_iter, ctx, true)?;
466 continue;
467 } else {
468 return None;
469 }
470 }
471 ast::MacroElement::Repetition(repetition) => {
472 let rep_id = RepetitionId(ctx.next_repetition_id);
473 ctx.next_repetition_id += 1;
474 ctx.current_repetition_stack.push(rep_id);
475 let elements = repetition.elements(db);
476 let operator = repetition.operator(db);
477 let separator_token = repetition.separator(db);
478 let expected_separator = match separator_token {
479 ast::OptionTerminalComma::TerminalComma(sep) => {
480 Some(sep.as_syntax_node().get_text_without_trivia(db))
481 }
482 ast::OptionTerminalComma::Empty(_) => None,
483 };
484 let mut match_count = 0;
485 loop {
486 let mut inner_ctx = ctx.clone();
487 let mut temp_iter = input_iter.clone();
488 let Some(true) = is_macro_rule_match_ex(
489 db,
490 elements.clone(),
491 &mut temp_iter,
492 &mut inner_ctx,
493 false,
494 ) else {
495 break;
496 };
497 advanced = true;
498 *ctx = inner_ctx;
499 *input_iter = temp_iter;
500 match_count += 1;
501 if let Some(expected_sep) = &expected_separator {
502 if let Some(ast::TokenTree::Token(token_leaf)) = input_iter.peek() {
503 let actual = token_leaf.as_syntax_node().get_text_without_trivia(db);
504 if actual == *expected_sep {
505 input_iter.next();
506 } else {
507 break;
508 }
509 } else {
510 break;
511 }
512 }
513 }
514 ctx.repetition_match_counts.insert(rep_id, match_count);
515 ctx.repetition_operators.insert(rep_id, operator.clone());
516 for placeholder_name in ctx.captures.keys() {
517 ctx.placeholder_to_rep_id.insert(*placeholder_name, rep_id);
518 }
519
520 for i in 0..match_count {
521 ctx.repetition_indices.insert(rep_id, i);
522 }
523 ctx.current_repetition_stack.pop();
524 continue;
525 }
526 }
527 }
528
529 if consume_all_input && input_iter.next().is_some() {
530 return None;
531 }
532 Some(advanced)
533}
534
535fn validate_repetition_operator_constraints(ctx: &MatcherContext<'_>) -> bool {
536 for (&rep_id, &count) in ctx.repetition_match_counts.iter() {
537 match ctx.repetition_operators.get(&rep_id) {
538 Some(ast::MacroRepetitionOperator::ZeroOrOne(_)) if count > 1 => return false,
539 Some(ast::MacroRepetitionOperator::OneOrMore(_)) if count < 1 => return false,
540 Some(ast::MacroRepetitionOperator::ZeroOrMore(_)) | None => {}
541 _ => {}
542 }
543 }
544 true
545}
546
547#[derive(Debug, Clone, PartialEq, Eq)]
549pub struct MacroExpansionResult {
550 pub text: Arc<str>,
552 pub code_mappings: Arc<[CodeMapping]>,
554}
555
556pub fn expand_macro_rule(
562 db: &dyn Database,
563 rule: &MacroRuleData<'_>,
564 matcher_ctx: &mut MatcherContext<'_>,
565) -> Maybe<MacroExpansionResult> {
566 let node = rule.expansion.as_syntax_node();
567 let mut res_buffer = String::new();
568 let mut code_mappings = Vec::new();
569 expand_macro_rule_ex(db, node, matcher_ctx, &mut res_buffer, &mut code_mappings)?;
570 Ok(MacroExpansionResult { text: res_buffer.into(), code_mappings: code_mappings.into() })
571}
572
573fn expand_macro_rule_ex(
579 db: &dyn Database,
580 node: SyntaxNode<'_>,
581 matcher_ctx: &mut MatcherContext<'_>,
582 res_buffer: &mut String,
583 code_mappings: &mut Vec<CodeMapping>,
584) -> Maybe<()> {
585 match node.kind(db) {
586 SyntaxKind::MacroParam => {
587 let path_node = MacroParam::from_syntax_node(db, node);
588 if let Some(name) = extract_placeholder(db, &path_node) {
589 let rep_index = matcher_ctx
590 .placeholder_to_rep_id
591 .get(&name)
592 .and_then(|rep_id| matcher_ctx.repetition_indices.get(rep_id))
593 .copied();
594 let value = matcher_ctx
595 .captures
596 .get(&name)
597 .and_then(|v| rep_index.map_or_else(|| v.first(), |i| v.get(i)))
598 .ok_or_else(skip_diagnostic)?;
599 let start = TextWidth::from_str(res_buffer).as_offset();
600 let span = TextSpan::new_with_width(start, TextWidth::from_str(&value.text));
601 res_buffer.push_str(&value.text);
602 code_mappings.push(CodeMapping {
603 span,
604 origin: CodeOrigin::Span(value.stable_ptr.lookup(db).span_without_trivia(db)),
605 });
606 return Ok(());
607 }
608 }
609 SyntaxKind::MacroRepetition => {
610 let repetition = ast::MacroRepetition::from_syntax_node(db, node);
611 let elements = repetition.elements(db);
612 let first_param = find_first_repetition_param(db, elements.elements(db))
613 .ok_or_else(skip_diagnostic)?;
614 let placeholder_name = first_param.name(db).text(db);
615 let Some(rep_id) = matcher_ctx.placeholder_to_rep_id.get(&placeholder_name).copied()
618 else {
619 return Ok(());
620 };
621 let repetition_len =
622 matcher_ctx.captures.get(&placeholder_name).map(|v| v.len()).unwrap_or(0);
623 for i in 0..repetition_len {
624 matcher_ctx.repetition_indices.insert(rep_id, i);
625 for element in elements.elements(db) {
626 expand_macro_rule_ex(
627 db,
628 element.as_syntax_node(),
629 matcher_ctx,
630 res_buffer,
631 code_mappings,
632 )?;
633 }
634
635 if i + 1 < repetition_len
636 && let ast::OptionTerminalComma::TerminalComma(sep) = repetition.separator(db)
637 {
638 res_buffer.push_str(sep.as_syntax_node().get_text(db));
639 }
640 }
641
642 matcher_ctx.repetition_indices.swap_remove(&rep_id);
643 return Ok(());
644 }
645 _ => {
646 if node.kind(db).is_terminal() {
647 res_buffer.push_str(node.get_text(db));
648 return Ok(());
649 }
650
651 for child in node.get_children(db).iter() {
652 expand_macro_rule_ex(db, *child, matcher_ctx, res_buffer, code_mappings)?;
653 }
654 return Ok(());
655 }
656 }
657 if node.kind(db).is_terminal() {
658 res_buffer.push_str(node.get_text(db));
659 return Ok(());
660 }
661 for child in node.get_children(db).iter() {
662 expand_macro_rule_ex(db, *child, matcher_ctx, res_buffer, code_mappings)?;
663 }
664 Ok(())
665}
666
667fn find_first_repetition_param<'db>(
669 db: &'db dyn Database,
670 elements: impl IntoIterator<Item = MacroElement<'db>>,
671) -> Option<MacroParam<'db>> {
672 for element in elements {
673 match element {
674 ast::MacroElement::Param(param) => return Some(param),
675 ast::MacroElement::Subtree(subtree) => {
676 let inner_elements = get_macro_elements(db, subtree.subtree(db)).elements(db);
677 if let Some(param) = find_first_repetition_param(db, inner_elements) {
678 return Some(param);
679 }
680 }
681 ast::MacroElement::Repetition(repetition) => {
682 let inner_elements = repetition.elements(db).elements(db);
683 if let Some(param) = find_first_repetition_param(db, inner_elements) {
684 return Some(param);
685 }
686 }
687 ast::MacroElement::Token(_) => {}
688 }
689 }
690 None
691}
692
693fn macro_declaration_diagnostics<'db>(
695 db: &'db dyn Database,
696 macro_declaration_id: MacroDeclarationId<'db>,
697) -> Diagnostics<'db, SemanticDiagnostic<'db>> {
698 priv_macro_declaration_data(db, macro_declaration_id)
699 .map(|data| data.diagnostics)
700 .unwrap_or_default()
701}
702
703#[salsa::tracked]
705fn macro_declaration_diagnostics_tracked<'db>(
706 db: &'db dyn Database,
707 macro_declaration_id: MacroDeclarationId<'db>,
708) -> Diagnostics<'db, SemanticDiagnostic<'db>> {
709 macro_declaration_diagnostics(db, macro_declaration_id)
710}
711
712fn macro_declaration_attributes<'db>(
714 db: &'db dyn Database,
715 macro_declaration_id: MacroDeclarationId<'db>,
716) -> Maybe<Vec<Attribute<'db>>> {
717 priv_macro_declaration_data(db, macro_declaration_id).map(|data| data.attributes)
718}
719
720#[salsa::tracked]
722fn macro_declaration_attributes_tracked<'db>(
723 db: &'db dyn Database,
724 macro_declaration_id: MacroDeclarationId<'db>,
725) -> Maybe<Vec<Attribute<'db>>> {
726 macro_declaration_attributes(db, macro_declaration_id)
727}
728
729fn macro_declaration_resolver_data<'db>(
731 db: &'db dyn Database,
732 macro_declaration_id: MacroDeclarationId<'db>,
733) -> Maybe<Arc<ResolverData<'db>>> {
734 priv_macro_declaration_data(db, macro_declaration_id).map(|data| data.resolver_data)
735}
736
737#[salsa::tracked]
739fn macro_declaration_resolver_data_tracked<'db>(
740 db: &'db dyn Database,
741 macro_declaration_id: MacroDeclarationId<'db>,
742) -> Maybe<Arc<ResolverData<'db>>> {
743 macro_declaration_resolver_data(db, macro_declaration_id)
744}
745
746fn macro_declaration_rules<'db>(
748 db: &'db dyn Database,
749 macro_declaration_id: MacroDeclarationId<'db>,
750) -> Maybe<Vec<MacroRuleData<'db>>> {
751 priv_macro_declaration_data(db, macro_declaration_id).map(|data| data.rules)
752}
753
754#[salsa::tracked]
756fn macro_declaration_rules_tracked<'db>(
757 db: &'db dyn Database,
758 macro_declaration_id: MacroDeclarationId<'db>,
759) -> Maybe<Vec<MacroRuleData<'db>>> {
760 macro_declaration_rules(db, macro_declaration_id)
761}
762
763fn are_user_defined_inline_macros_enabled<'db>(
765 db: &dyn Database,
766 module_id: ModuleId<'db>,
767) -> bool {
768 let owning_crate = module_id.owning_crate(db);
769 let Some(config) = db.crate_config(owning_crate) else { return false };
770 config.settings.experimental_features.user_defined_inline_macros
771}
772
773pub trait MacroDeclarationSemantic<'db>: Database {
775 fn priv_macro_declaration_data(
777 &'db self,
778 macro_id: MacroDeclarationId<'db>,
779 ) -> Maybe<MacroDeclarationData<'db>> {
780 priv_macro_declaration_data_tracked(self.as_dyn_database(), macro_id)
781 }
782 fn macro_declaration_diagnostics(
784 &'db self,
785 macro_id: MacroDeclarationId<'db>,
786 ) -> Diagnostics<'db, SemanticDiagnostic<'db>> {
787 macro_declaration_diagnostics_tracked(self.as_dyn_database(), macro_id)
788 }
789 fn macro_declaration_resolver_data(
791 &'db self,
792 macro_id: MacroDeclarationId<'db>,
793 ) -> Maybe<Arc<ResolverData<'db>>> {
794 macro_declaration_resolver_data_tracked(self.as_dyn_database(), macro_id)
795 }
796 fn macro_declaration_attributes(
798 &'db self,
799 macro_id: MacroDeclarationId<'db>,
800 ) -> Maybe<Vec<Attribute<'db>>> {
801 macro_declaration_attributes_tracked(self.as_dyn_database(), macro_id)
802 }
803 fn macro_declaration_rules(
805 &'db self,
806 macro_id: MacroDeclarationId<'db>,
807 ) -> Maybe<Vec<MacroRuleData<'db>>> {
808 macro_declaration_rules_tracked(self.as_dyn_database(), macro_id)
809 }
810}
811impl<'db, T: Database + ?Sized> MacroDeclarationSemantic<'db> for T {}