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 if pattern.as_syntax_node().descendants(db).any(|node| node.kind(db).is_missing()) {
161 continue;
162 }
163 rules.push(MacroRuleData { pattern, expansion, err: ctx.rule_err });
164 }
165 let resolver_data = Arc::new(resolver.data);
166 Ok(MacroDeclarationData { diagnostics: diagnostics.build(), attributes, resolver_data, rules })
167}
168
169#[salsa::tracked]
171fn priv_macro_declaration_data_tracked<'db>(
172 db: &'db dyn Database,
173 macro_declaration_id: MacroDeclarationId<'db>,
174) -> Maybe<MacroDeclarationData<'db>> {
175 priv_macro_declaration_data(db, macro_declaration_id)
176}
177
178fn get_macro_elements<'db>(
180 db: &'db dyn Database,
181 pattern: ast::WrappedMacro<'db>,
182) -> ast::MacroElements<'db> {
183 match pattern {
184 ast::WrappedMacro::Parenthesized(inner) => inner.elements(db),
185 ast::WrappedMacro::Braced(inner) => inner.elements(db),
186 ast::WrappedMacro::Bracketed(inner) => inner.elements(db),
187 }
188}
189
190fn extract_placeholder<'db>(
193 db: &'db dyn Database,
194 path_node: &MacroParam<'db>,
195) -> Option<SmolStrId<'db>> {
196 let placeholder_name = path_node.name(db).as_syntax_node().get_text_without_trivia(db);
197 if ![MACRO_DEF_SITE, MACRO_CALL_SITE].contains(&placeholder_name.long(db).as_str()) {
198 return Some(placeholder_name);
199 }
200 None
201}
202
203fn collect_placeholder_paths<'db>(
208 db: &'db dyn Database,
209 elements: impl IntoIterator<Item = ast::MacroElement<'db>>,
210 current_path: &mut Vec<usize>,
211 next_rep_id: &mut usize,
212 result: &mut OrderedHashMap<SmolStrId<'db>, Vec<usize>>,
213) {
214 for element in elements {
215 match element {
216 ast::MacroElement::Param(param) => {
217 result.insert(
218 param.name(db).as_syntax_node().get_text_without_trivia(db),
219 current_path.clone(),
220 );
221 }
222 ast::MacroElement::Repetition(rep) => {
223 let rep_id = *next_rep_id;
224 *next_rep_id += 1;
225 current_path.push(rep_id);
226 let inner = rep.elements(db).elements(db);
227 collect_placeholder_paths(db, inner, current_path, next_rep_id, result);
228 assert_eq!(current_path.pop(), Some(rep_id));
229 }
230 ast::MacroElement::Subtree(subtree) => {
231 let inner = get_macro_elements(db, subtree.subtree(db)).elements(db);
232 collect_placeholder_paths(db, inner, current_path, next_rep_id, result);
233 }
234 ast::MacroElement::Token(_) => {}
235 }
236 }
237}
238
239struct ExpansionCheckCtx<'db, 'a> {
241 db: &'db dyn Database,
242 placeholder_paths: &'a OrderedHashMap<SmolStrId<'db>, Vec<usize>>,
245 curr_rep_depth: usize,
248 known_path: &'a [usize],
253 diagnostics: &'a mut SemanticDiagnostics<'db>,
254 rule_err: Maybe<()>,
256}
257
258impl<'db> ExpansionCheckCtx<'db, '_> {
259 fn check_node(&mut self, node: SyntaxNode<'db>) {
265 let db = self.db;
266 if let Some(param) = MacroParam::cast(db, node) {
267 if let Some(name) = extract_placeholder(db, ¶m) {
268 let ptr = param.stable_ptr(db).untyped();
269 match self.placeholder_paths.get(&name) {
270 None => {
271 self.rule_err = Err(self
272 .diagnostics
273 .report(ptr, SemanticDiagnosticKind::UndefinedMacroPlaceholder(name)));
274 }
275 Some(path) => {
276 if path.len() > self.curr_rep_depth {
277 self.rule_err = Err(self.diagnostics.report(
278 ptr,
279 SemanticDiagnosticKind::MacroPlaceholderRepDepthMismatch {
280 name,
281 required: path.len(),
282 actual: self.curr_rep_depth,
283 },
284 ));
285 } else {
286 let cmp_size = path.len().min(self.known_path.len());
287 if path[..cmp_size] != self.known_path[..cmp_size] {
288 self.rule_err = Err(self.diagnostics.report(
289 ptr,
290 SemanticDiagnosticKind::MacroPlaceholderRepDriverMismatch(name),
291 ));
292 } else if path.len() > self.known_path.len() {
293 self.known_path = path;
294 }
295 }
296 }
297 }
298 }
299 return;
300 }
301
302 if let Some(repetition) = ast::MacroRepetition::cast(db, node) {
303 self.curr_rep_depth += 1;
304 for element in repetition.elements(db).elements(db) {
305 self.check_node(element.as_syntax_node());
306 }
307 self.curr_rep_depth -= 1;
308 if self.curr_rep_depth < self.known_path.len() {
309 self.known_path = &self.known_path[..self.curr_rep_depth];
311 }
312 } else if !node.kind(db).is_terminal() {
313 for child in node.get_children(db).iter() {
314 self.check_node(*child);
315 }
316 }
317 }
318}
319
320pub fn is_macro_rule_match<'db>(
323 db: &'db dyn Database,
324 rule: &MacroRuleData<'db>,
325 input: &ast::TokenTreeNode<'db>,
326) -> Option<(Captures<'db>, OrderedHashMap<SmolStrId<'db>, RepetitionId>)> {
327 let mut ctx = MatcherContext::default();
328
329 let matcher_elements = get_macro_elements(db, rule.pattern.clone());
330 let mut input_iter = match input.subtree(db) {
331 ast::WrappedTokenTree::Parenthesized(tt) => tt.tokens(db),
332 ast::WrappedTokenTree::Braced(tt) => tt.tokens(db),
333 ast::WrappedTokenTree::Bracketed(tt) => tt.tokens(db),
334 ast::WrappedTokenTree::Missing(_) => return None,
335 }
336 .elements(db)
337 .peekable();
338 is_macro_rule_match_ex(db, matcher_elements, &mut input_iter, &mut ctx, true)?;
339 if !validate_repetition_operator_constraints(&ctx) {
340 return None;
341 }
342 Some((ctx.captures, ctx.placeholder_to_rep_id))
343}
344
345fn is_macro_rule_match_ex<'db>(
352 db: &'db dyn Database,
353 matcher_elements: ast::MacroElements<'db>,
354 input_iter: &mut std::iter::Peekable<
355 impl DoubleEndedIterator<Item = ast::TokenTree<'db>> + Clone,
356 >,
357 ctx: &mut MatcherContext<'db>,
358 consume_all_input: bool,
359) -> Option<bool> {
360 let mut advanced = false;
361 for matcher_element in matcher_elements.elements(db) {
362 match matcher_element {
363 ast::MacroElement::Token(matcher_token) => {
364 advanced = true;
365 let input_token = input_iter.next()?;
366 match input_token {
367 ast::TokenTree::Token(token_tree_leaf) => {
368 if matcher_token.as_syntax_node().get_text_without_trivia(db)
369 != token_tree_leaf.as_syntax_node().get_text_without_trivia(db)
370 {
371 return None;
372 }
373 continue;
374 }
375 ast::TokenTree::Subtree(_) => return None,
376 ast::TokenTree::Repetition(_) => return None,
377 ast::TokenTree::Param(_) => return None,
378 ast::TokenTree::Missing(_) => unreachable!(),
379 }
380 }
381 ast::MacroElement::Param(param) => {
382 advanced = true;
383 let ast::OptionParamKind::ParamKind(param_kind) = param.kind(db) else {
384 return None;
385 };
386 let placeholder_kind: PlaceholderKind = param_kind.kind(db).into();
387 let placeholder_name = param.name(db).as_syntax_node().get_text_without_trivia(db);
388 match placeholder_kind {
389 PlaceholderKind::Identifier => {
390 let input_token = input_iter.next()?;
391 let captured_text = match &input_token {
392 ast::TokenTree::Token(token_tree_leaf) => {
393 match token_tree_leaf.leaf(db) {
394 ast::TokenNode::TerminalIdentifier(terminal_identifier) => {
395 terminal_identifier.text(db).to_string(db)
396 }
397 _ => return None,
398 }
399 }
400 _ => return None,
401 };
402 ctx.captures.entry(placeholder_name).or_default().push(CapturedValue {
403 text: captured_text,
404 stable_ptr: input_token.stable_ptr(db).untyped(),
405 });
406 if let Some(rep_id) = ctx.current_repetition_stack.last() {
407 ctx.placeholder_to_rep_id.insert(placeholder_name, *rep_id);
408 }
409 continue;
410 }
411 PlaceholderKind::Expr => {
412 let peek_token = input_iter.peek().cloned()?;
413 let file_id = peek_token.as_syntax_node().stable_ptr(db).file_id(db);
414 let expr_node = as_expr_macro_token_tree(input_iter.clone(), file_id, db)?;
415 let expr_text = expr_node.as_syntax_node().get_text(db);
416 let expr_length = expr_text.len();
417 if expr_length == 0 {
420 return None;
421 }
422
423 ctx.captures.entry(placeholder_name).or_default().push(CapturedValue {
424 text: expr_text.to_string(),
425 stable_ptr: peek_token.stable_ptr(db).untyped(),
426 });
427 if let Some(rep_id) = ctx.current_repetition_stack.last() {
428 ctx.placeholder_to_rep_id.insert(placeholder_name, *rep_id);
429 }
430 let expr_length = expr_text.len();
431 let mut current_length = 0;
432
433 for token_tree_leaf in input_iter.by_ref() {
437 let token_text = match token_tree_leaf {
438 ast::TokenTree::Token(leaf) => leaf.as_syntax_node(),
439 ast::TokenTree::Subtree(subtree) => subtree.as_syntax_node(),
440 ast::TokenTree::Repetition(rep) => rep.as_syntax_node(),
441 ast::TokenTree::Param(param) => param.as_syntax_node(),
442 ast::TokenTree::Missing(_) => unreachable!(),
443 }
444 .get_text(db);
445 current_length += token_text.len();
446 if current_length >= expr_length {
447 break;
448 }
449 }
450 continue;
451 }
452 }
453 }
454 ast::MacroElement::Subtree(matcher_subtree) => {
455 advanced = true;
456 let input_token = input_iter.next()?;
457 if let ast::TokenTree::Subtree(input_subtree) = input_token {
458 let inner_elements = get_macro_elements(db, matcher_subtree.subtree(db));
459 let mut inner_input_iter = match input_subtree.subtree(db) {
460 ast::WrappedTokenTree::Parenthesized(tt) => tt.tokens(db),
461 ast::WrappedTokenTree::Braced(tt) => tt.tokens(db),
462 ast::WrappedTokenTree::Bracketed(tt) => tt.tokens(db),
463 ast::WrappedTokenTree::Missing(_) => unreachable!(),
464 }
465 .elements(db)
466 .peekable();
467 is_macro_rule_match_ex(db, inner_elements, &mut inner_input_iter, ctx, true)?;
468 continue;
469 } else {
470 return None;
471 }
472 }
473 ast::MacroElement::Repetition(repetition) => {
474 let rep_id = RepetitionId(ctx.next_repetition_id);
475 ctx.next_repetition_id += 1;
476 ctx.current_repetition_stack.push(rep_id);
477 let elements = repetition.elements(db);
478 let operator = repetition.operator(db);
479 let separator_token = repetition.separator(db);
480 let expected_separator = match separator_token {
481 ast::OptionTerminalComma::TerminalComma(sep) => {
482 Some(sep.as_syntax_node().get_text_without_trivia(db))
483 }
484 ast::OptionTerminalComma::Empty(_) => None,
485 };
486 let mut match_count = 0;
487 loop {
488 let mut inner_ctx = ctx.clone();
489 let mut temp_iter = input_iter.clone();
490 let Some(true) = is_macro_rule_match_ex(
491 db,
492 elements.clone(),
493 &mut temp_iter,
494 &mut inner_ctx,
495 false,
496 ) else {
497 break;
498 };
499 advanced = true;
500 *ctx = inner_ctx;
501 *input_iter = temp_iter;
502 match_count += 1;
503 if let Some(expected_sep) = &expected_separator {
504 if let Some(ast::TokenTree::Token(token_leaf)) = input_iter.peek() {
505 let actual = token_leaf.as_syntax_node().get_text_without_trivia(db);
506 if actual == *expected_sep {
507 input_iter.next();
508 } else {
509 break;
510 }
511 } else {
512 break;
513 }
514 }
515 }
516 ctx.repetition_match_counts.insert(rep_id, match_count);
517 ctx.repetition_operators.insert(rep_id, operator.clone());
518 for placeholder_name in ctx.captures.keys() {
519 ctx.placeholder_to_rep_id.insert(*placeholder_name, rep_id);
520 }
521
522 for i in 0..match_count {
523 ctx.repetition_indices.insert(rep_id, i);
524 }
525 ctx.current_repetition_stack.pop();
526 continue;
527 }
528 }
529 }
530
531 if consume_all_input && input_iter.next().is_some() {
532 return None;
533 }
534 Some(advanced)
535}
536
537fn validate_repetition_operator_constraints(ctx: &MatcherContext<'_>) -> bool {
538 for (&rep_id, &count) in ctx.repetition_match_counts.iter() {
539 match ctx.repetition_operators.get(&rep_id) {
540 Some(ast::MacroRepetitionOperator::ZeroOrOne(_)) if count > 1 => return false,
541 Some(ast::MacroRepetitionOperator::OneOrMore(_)) if count < 1 => return false,
542 Some(ast::MacroRepetitionOperator::ZeroOrMore(_)) | None => {}
543 _ => {}
544 }
545 }
546 true
547}
548
549#[derive(Debug, Clone, PartialEq, Eq)]
551pub struct MacroExpansionResult {
552 pub text: Arc<str>,
554 pub code_mappings: Arc<[CodeMapping]>,
556}
557
558pub fn expand_macro_rule(
564 db: &dyn Database,
565 rule: &MacroRuleData<'_>,
566 matcher_ctx: &mut MatcherContext<'_>,
567) -> Maybe<MacroExpansionResult> {
568 let node = rule.expansion.as_syntax_node();
569 let mut res_buffer = String::new();
570 let mut code_mappings = Vec::new();
571 expand_macro_rule_ex(db, node, matcher_ctx, &mut res_buffer, &mut code_mappings)?;
572 Ok(MacroExpansionResult { text: res_buffer.into(), code_mappings: code_mappings.into() })
573}
574
575fn expand_macro_rule_ex(
581 db: &dyn Database,
582 node: SyntaxNode<'_>,
583 matcher_ctx: &mut MatcherContext<'_>,
584 res_buffer: &mut String,
585 code_mappings: &mut Vec<CodeMapping>,
586) -> Maybe<()> {
587 match node.kind(db) {
588 SyntaxKind::MacroParam => {
589 let path_node = MacroParam::from_syntax_node(db, node);
590 if let Some(name) = extract_placeholder(db, &path_node) {
591 let rep_index = matcher_ctx
592 .placeholder_to_rep_id
593 .get(&name)
594 .and_then(|rep_id| matcher_ctx.repetition_indices.get(rep_id))
595 .copied();
596 let value = matcher_ctx
597 .captures
598 .get(&name)
599 .and_then(|v| rep_index.map_or_else(|| v.first(), |i| v.get(i)))
600 .ok_or_else(skip_diagnostic)?;
601 let start = TextWidth::from_str(res_buffer).as_offset();
602 let span = TextSpan::new_with_width(start, TextWidth::from_str(&value.text));
603 res_buffer.push_str(&value.text);
604 code_mappings.push(CodeMapping {
605 span,
606 origin: CodeOrigin::Span(value.stable_ptr.lookup(db).span_without_trivia(db)),
607 });
608 return Ok(());
609 }
610 }
611 SyntaxKind::MacroRepetition => {
612 let repetition = ast::MacroRepetition::from_syntax_node(db, node);
613 let elements = repetition.elements(db);
614 let first_param = find_first_repetition_param(db, elements.elements(db))
615 .ok_or_else(skip_diagnostic)?;
616 let placeholder_name = first_param.name(db).text(db);
617 let Some(rep_id) = matcher_ctx.placeholder_to_rep_id.get(&placeholder_name).copied()
620 else {
621 return Ok(());
622 };
623 let repetition_len =
624 matcher_ctx.captures.get(&placeholder_name).map(|v| v.len()).unwrap_or(0);
625 for i in 0..repetition_len {
626 matcher_ctx.repetition_indices.insert(rep_id, i);
627 for element in elements.elements(db) {
628 expand_macro_rule_ex(
629 db,
630 element.as_syntax_node(),
631 matcher_ctx,
632 res_buffer,
633 code_mappings,
634 )?;
635 }
636
637 if i + 1 < repetition_len
638 && let ast::OptionTerminalComma::TerminalComma(sep) = repetition.separator(db)
639 {
640 res_buffer.push_str(sep.as_syntax_node().get_text(db));
641 }
642 }
643
644 matcher_ctx.repetition_indices.swap_remove(&rep_id);
645 return Ok(());
646 }
647 _ => {
648 if node.kind(db).is_terminal() {
649 res_buffer.push_str(node.get_text(db));
650 return Ok(());
651 }
652
653 for child in node.get_children(db).iter() {
654 expand_macro_rule_ex(db, *child, matcher_ctx, res_buffer, code_mappings)?;
655 }
656 return Ok(());
657 }
658 }
659 if node.kind(db).is_terminal() {
660 res_buffer.push_str(node.get_text(db));
661 return Ok(());
662 }
663 for child in node.get_children(db).iter() {
664 expand_macro_rule_ex(db, *child, matcher_ctx, res_buffer, code_mappings)?;
665 }
666 Ok(())
667}
668
669fn find_first_repetition_param<'db>(
671 db: &'db dyn Database,
672 elements: impl IntoIterator<Item = MacroElement<'db>>,
673) -> Option<MacroParam<'db>> {
674 for element in elements {
675 match element {
676 ast::MacroElement::Param(param) => return Some(param),
677 ast::MacroElement::Subtree(subtree) => {
678 let inner_elements = get_macro_elements(db, subtree.subtree(db)).elements(db);
679 if let Some(param) = find_first_repetition_param(db, inner_elements) {
680 return Some(param);
681 }
682 }
683 ast::MacroElement::Repetition(repetition) => {
684 let inner_elements = repetition.elements(db).elements(db);
685 if let Some(param) = find_first_repetition_param(db, inner_elements) {
686 return Some(param);
687 }
688 }
689 ast::MacroElement::Token(_) => {}
690 }
691 }
692 None
693}
694
695fn macro_declaration_diagnostics<'db>(
697 db: &'db dyn Database,
698 macro_declaration_id: MacroDeclarationId<'db>,
699) -> Diagnostics<'db, SemanticDiagnostic<'db>> {
700 priv_macro_declaration_data(db, macro_declaration_id)
701 .map(|data| data.diagnostics)
702 .unwrap_or_default()
703}
704
705#[salsa::tracked]
707fn macro_declaration_diagnostics_tracked<'db>(
708 db: &'db dyn Database,
709 macro_declaration_id: MacroDeclarationId<'db>,
710) -> Diagnostics<'db, SemanticDiagnostic<'db>> {
711 macro_declaration_diagnostics(db, macro_declaration_id)
712}
713
714fn macro_declaration_attributes<'db>(
716 db: &'db dyn Database,
717 macro_declaration_id: MacroDeclarationId<'db>,
718) -> Maybe<Vec<Attribute<'db>>> {
719 priv_macro_declaration_data(db, macro_declaration_id).map(|data| data.attributes)
720}
721
722#[salsa::tracked]
724fn macro_declaration_attributes_tracked<'db>(
725 db: &'db dyn Database,
726 macro_declaration_id: MacroDeclarationId<'db>,
727) -> Maybe<Vec<Attribute<'db>>> {
728 macro_declaration_attributes(db, macro_declaration_id)
729}
730
731fn macro_declaration_resolver_data<'db>(
733 db: &'db dyn Database,
734 macro_declaration_id: MacroDeclarationId<'db>,
735) -> Maybe<Arc<ResolverData<'db>>> {
736 priv_macro_declaration_data(db, macro_declaration_id).map(|data| data.resolver_data)
737}
738
739#[salsa::tracked]
741fn macro_declaration_resolver_data_tracked<'db>(
742 db: &'db dyn Database,
743 macro_declaration_id: MacroDeclarationId<'db>,
744) -> Maybe<Arc<ResolverData<'db>>> {
745 macro_declaration_resolver_data(db, macro_declaration_id)
746}
747
748fn macro_declaration_rules<'db>(
750 db: &'db dyn Database,
751 macro_declaration_id: MacroDeclarationId<'db>,
752) -> Maybe<Vec<MacroRuleData<'db>>> {
753 priv_macro_declaration_data(db, macro_declaration_id).map(|data| data.rules)
754}
755
756#[salsa::tracked]
758fn macro_declaration_rules_tracked<'db>(
759 db: &'db dyn Database,
760 macro_declaration_id: MacroDeclarationId<'db>,
761) -> Maybe<Vec<MacroRuleData<'db>>> {
762 macro_declaration_rules(db, macro_declaration_id)
763}
764
765fn are_user_defined_inline_macros_enabled<'db>(
767 db: &dyn Database,
768 module_id: ModuleId<'db>,
769) -> bool {
770 let owning_crate = module_id.owning_crate(db);
771 let Some(config) = db.crate_config(owning_crate) else { return false };
772 config.settings.experimental_features.user_defined_inline_macros
773}
774
775pub trait MacroDeclarationSemantic<'db>: Database {
777 fn priv_macro_declaration_data(
779 &'db self,
780 macro_id: MacroDeclarationId<'db>,
781 ) -> Maybe<MacroDeclarationData<'db>> {
782 priv_macro_declaration_data_tracked(self.as_dyn_database(), macro_id)
783 }
784 fn macro_declaration_diagnostics(
786 &'db self,
787 macro_id: MacroDeclarationId<'db>,
788 ) -> Diagnostics<'db, SemanticDiagnostic<'db>> {
789 macro_declaration_diagnostics_tracked(self.as_dyn_database(), macro_id)
790 }
791 fn macro_declaration_resolver_data(
793 &'db self,
794 macro_id: MacroDeclarationId<'db>,
795 ) -> Maybe<Arc<ResolverData<'db>>> {
796 macro_declaration_resolver_data_tracked(self.as_dyn_database(), macro_id)
797 }
798 fn macro_declaration_attributes(
800 &'db self,
801 macro_id: MacroDeclarationId<'db>,
802 ) -> Maybe<Vec<Attribute<'db>>> {
803 macro_declaration_attributes_tracked(self.as_dyn_database(), macro_id)
804 }
805 fn macro_declaration_rules(
807 &'db self,
808 macro_id: MacroDeclarationId<'db>,
809 ) -> Maybe<Vec<MacroRuleData<'db>>> {
810 macro_declaration_rules_tracked(self.as_dyn_database(), macro_id)
811 }
812}
813impl<'db, T: Database + ?Sized> MacroDeclarationSemantic<'db> for T {}