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#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
30pub struct RepetitionId(usize);
31
32type Captures<'db> = OrderedHashMap<SmolStrId<'db>, Vec<CapturedValue<'db>>>;
35
36#[derive(Default, Clone, Debug)]
39pub struct MatcherContext<'db> {
40 pub captures: Captures<'db>,
43
44 pub placeholder_to_rep_id: OrderedHashMap<SmolStrId<'db>, RepetitionId>,
47
48 pub current_repetition_stack: Vec<RepetitionId>,
51
52 pub next_repetition_id: usize,
54
55 pub repetition_indices: OrderedHashMap<RepetitionId, usize>,
57
58 pub repetition_match_counts: OrderedHashMap<RepetitionId, usize>,
60
61 pub repetition_operators: OrderedHashMap<RepetitionId, ast::MacroRepetitionOperator<'db>>,
63}
64
65#[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#[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#[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#[derive(Clone, Debug, PartialEq, Eq)]
102pub struct CapturedValue<'db> {
103 pub text: String,
104 pub stable_ptr: SyntaxStablePtrId<'db>,
105}
106
107fn 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.parent_module(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 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 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 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#[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
184fn 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
196fn 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
209fn 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
237pub 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
262fn 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 peek_token = input_iter.peek().cloned()?;
327 let file_id = peek_token.as_syntax_node().stable_ptr(db).file_id(db);
328 let expr_node =
329 as_expr_macro_token_tree(input_iter.clone().cloned(), file_id, db)?;
330 let expr_text = expr_node.as_syntax_node().get_text(db);
331 let expr_length = expr_text.len();
332 if expr_length == 0 {
335 return None;
336 }
337
338 ctx.captures.entry(placeholder_name).or_default().push(CapturedValue {
339 text: expr_text.to_string(),
340 stable_ptr: peek_token.stable_ptr(db).untyped(),
341 });
342 if let Some(rep_id) = ctx.current_repetition_stack.last() {
343 ctx.placeholder_to_rep_id.insert(placeholder_name, *rep_id);
344 }
345 let expr_length = expr_text.len();
346 let mut current_length = 0;
347
348 for token_tree_leaf in input_iter.by_ref() {
352 let token_text = match token_tree_leaf {
353 ast::TokenTree::Token(token_tree_leaf) => {
354 token_tree_leaf.as_syntax_node().get_text(db)
355 }
356 ast::TokenTree::Subtree(token_subtree) => {
357 token_subtree.as_syntax_node().get_text(db)
358 }
359 ast::TokenTree::Repetition(token_repetition) => {
360 token_repetition.as_syntax_node().get_text(db)
361 }
362 ast::TokenTree::Param(token_param) => {
363 token_param.as_syntax_node().get_text(db)
364 }
365 ast::TokenTree::Missing(_) => unreachable!(),
366 };
367 current_length += token_text.len();
368 if current_length >= expr_length {
369 break;
370 }
371 }
372 continue;
373 }
374 }
375 }
376 ast::MacroElement::Subtree(matcher_subtree) => {
377 let input_token = input_iter.next()?;
378 if let ast::TokenTree::Subtree(input_subtree) = input_token {
379 let inner_elements = get_macro_elements(db, matcher_subtree.subtree(db));
380 let inner_input_elements = match input_subtree.subtree(db) {
381 ast::WrappedTokenTree::Parenthesized(tt) => tt.tokens(db),
382 ast::WrappedTokenTree::Braced(tt) => tt.tokens(db),
383 ast::WrappedTokenTree::Bracketed(tt) => tt.tokens(db),
384 ast::WrappedTokenTree::Missing(_) => unreachable!(),
385 }
386 .elements_vec(db);
387 let mut inner_input_iter = inner_input_elements.iter().peekable();
388 is_macro_rule_match_ex(db, inner_elements, &mut inner_input_iter, ctx, true)?;
389 continue;
390 } else {
391 return None;
392 }
393 }
394 ast::MacroElement::Repetition(repetition) => {
395 let rep_id = RepetitionId(ctx.next_repetition_id);
396 ctx.next_repetition_id += 1;
397 ctx.current_repetition_stack.push(rep_id);
398 let elements = repetition.elements(db);
399 let operator = repetition.operator(db);
400 let separator_token = repetition.separator(db);
401 let expected_separator = match separator_token {
402 ast::OptionTerminalComma::TerminalComma(sep) => {
403 Some(sep.as_syntax_node().get_text_without_trivia(db))
404 }
405 ast::OptionTerminalComma::Empty(_) => None,
406 };
407 let mut match_count = 0;
408 loop {
409 let mut inner_ctx = ctx.clone();
410 let mut temp_iter = input_iter.clone();
411 if is_macro_rule_match_ex(
412 db,
413 elements.clone(),
414 &mut temp_iter,
415 &mut inner_ctx,
416 false,
417 )
418 .is_none()
419 {
420 break;
421 }
422 *ctx = inner_ctx;
423 *input_iter = temp_iter;
424 match_count += 1;
425 if let Some(expected_sep) = &expected_separator {
426 if let Some(ast::TokenTree::Token(token_leaf)) = input_iter.peek() {
427 let actual = token_leaf.as_syntax_node().get_text_without_trivia(db);
428 if actual == *expected_sep {
429 input_iter.next();
430 } else {
431 break;
432 }
433 } else {
434 break;
435 }
436 }
437 }
438 ctx.repetition_match_counts.insert(rep_id, match_count);
439 ctx.repetition_operators.insert(rep_id, operator.clone());
440 for placeholder_name in ctx.captures.keys() {
441 ctx.placeholder_to_rep_id.insert(*placeholder_name, rep_id);
442 }
443
444 for i in 0..match_count {
445 ctx.repetition_indices.insert(rep_id, i);
446 }
447 ctx.current_repetition_stack.pop();
448 continue;
449 }
450 }
451 }
452
453 if consume_all_input && input_iter.next().is_some() {
454 return None;
455 }
456 Some(())
457}
458
459fn validate_repetition_operator_constraints(ctx: &MatcherContext<'_>) -> bool {
460 for (&rep_id, &count) in ctx.repetition_match_counts.iter() {
461 match ctx.repetition_operators.get(&rep_id) {
462 Some(ast::MacroRepetitionOperator::ZeroOrOne(_)) if count > 1 => return false,
463 Some(ast::MacroRepetitionOperator::OneOrMore(_)) if count < 1 => return false,
464 Some(ast::MacroRepetitionOperator::ZeroOrMore(_)) | None => {}
465 _ => {}
466 }
467 }
468 true
469}
470
471#[derive(Debug, Clone, PartialEq, Eq)]
473pub struct MacroExpansionResult {
474 pub text: Arc<str>,
476 pub code_mappings: Arc<[CodeMapping]>,
478}
479
480pub fn expand_macro_rule(
486 db: &dyn Database,
487 rule: &MacroRuleData<'_>,
488 matcher_ctx: &mut MatcherContext<'_>,
489) -> Maybe<MacroExpansionResult> {
490 let node = rule.expansion.as_syntax_node();
491 let mut res_buffer = String::new();
492 let mut code_mappings = Vec::new();
493 expand_macro_rule_ex(db, node, matcher_ctx, &mut res_buffer, &mut code_mappings)?;
494 Ok(MacroExpansionResult { text: res_buffer.into(), code_mappings: code_mappings.into() })
495}
496
497fn expand_macro_rule_ex(
503 db: &dyn Database,
504 node: SyntaxNode<'_>,
505 matcher_ctx: &mut MatcherContext<'_>,
506 res_buffer: &mut String,
507 code_mappings: &mut Vec<CodeMapping>,
508) -> Maybe<()> {
509 match node.kind(db) {
510 SyntaxKind::MacroParam => {
511 let path_node = MacroParam::from_syntax_node(db, node);
512 if let Some(name) = extract_placeholder(db, &path_node) {
513 let rep_index = matcher_ctx
514 .placeholder_to_rep_id
515 .get(&name)
516 .and_then(|rep_id| matcher_ctx.repetition_indices.get(rep_id))
517 .copied();
518 let value = matcher_ctx
519 .captures
520 .get(&name)
521 .and_then(|v| rep_index.map_or_else(|| v.first(), |i| v.get(i)))
522 .ok_or_else(skip_diagnostic)?;
523 let start = TextWidth::from_str(res_buffer).as_offset();
524 let span = TextSpan::new_with_width(start, TextWidth::from_str(&value.text));
525 res_buffer.push_str(&value.text);
526 code_mappings.push(CodeMapping {
527 span,
528 origin: CodeOrigin::Span(value.stable_ptr.lookup(db).span_without_trivia(db)),
529 });
530 return Ok(());
531 }
532 }
533 SyntaxKind::MacroRepetition => {
534 let repetition = ast::MacroRepetition::from_syntax_node(db, node);
535 let elements = repetition.elements(db);
536 let first_param = find_first_repetition_param(db, elements.elements(db))
537 .ok_or_else(skip_diagnostic)?;
538 let placeholder_name = first_param.name(db).text(db);
539 let rep_id = *matcher_ctx
540 .placeholder_to_rep_id
541 .get(&placeholder_name)
542 .ok_or_else(skip_diagnostic)?;
543 let repetition_len =
544 matcher_ctx.captures.get(&placeholder_name).map(|v| v.len()).unwrap_or(0);
545 for i in 0..repetition_len {
546 matcher_ctx.repetition_indices.insert(rep_id, i);
547 for element in elements.elements(db) {
548 expand_macro_rule_ex(
549 db,
550 element.as_syntax_node(),
551 matcher_ctx,
552 res_buffer,
553 code_mappings,
554 )?;
555 }
556
557 if i + 1 < repetition_len
558 && let ast::OptionTerminalComma::TerminalComma(sep) = repetition.separator(db)
559 {
560 res_buffer.push_str(sep.as_syntax_node().get_text(db));
561 }
562 }
563
564 matcher_ctx.repetition_indices.swap_remove(&rep_id);
565 return Ok(());
566 }
567 _ => {
568 if node.kind(db).is_terminal() {
569 res_buffer.push_str(node.get_text(db));
570 return Ok(());
571 }
572
573 for child in node.get_children(db).iter() {
574 expand_macro_rule_ex(db, *child, matcher_ctx, res_buffer, code_mappings)?;
575 }
576 return Ok(());
577 }
578 }
579 if node.kind(db).is_terminal() {
580 res_buffer.push_str(node.get_text(db));
581 return Ok(());
582 }
583 for child in node.get_children(db).iter() {
584 expand_macro_rule_ex(db, *child, matcher_ctx, res_buffer, code_mappings)?;
585 }
586 Ok(())
587}
588
589fn find_first_repetition_param<'db>(
591 db: &'db dyn Database,
592 elements: impl IntoIterator<Item = MacroElement<'db>>,
593) -> Option<MacroParam<'db>> {
594 for element in elements {
595 match element {
596 ast::MacroElement::Param(param) => return Some(param),
597 ast::MacroElement::Subtree(subtree) => {
598 let inner_elements = get_macro_elements(db, subtree.subtree(db)).elements(db);
599 if let Some(param) = find_first_repetition_param(db, inner_elements) {
600 return Some(param);
601 }
602 }
603 ast::MacroElement::Repetition(repetition) => {
604 let inner_elements = repetition.elements(db).elements(db);
605 if let Some(param) = find_first_repetition_param(db, inner_elements) {
606 return Some(param);
607 }
608 }
609 ast::MacroElement::Token(_) => {}
610 }
611 }
612 None
613}
614
615fn macro_declaration_diagnostics<'db>(
617 db: &'db dyn Database,
618 macro_declaration_id: MacroDeclarationId<'db>,
619) -> Diagnostics<'db, SemanticDiagnostic<'db>> {
620 priv_macro_declaration_data(db, macro_declaration_id)
621 .map(|data| data.diagnostics)
622 .unwrap_or_default()
623}
624
625#[salsa::tracked]
627fn macro_declaration_diagnostics_tracked<'db>(
628 db: &'db dyn Database,
629 macro_declaration_id: MacroDeclarationId<'db>,
630) -> Diagnostics<'db, SemanticDiagnostic<'db>> {
631 macro_declaration_diagnostics(db, macro_declaration_id)
632}
633
634fn macro_declaration_attributes<'db>(
636 db: &'db dyn Database,
637 macro_declaration_id: MacroDeclarationId<'db>,
638) -> Maybe<Vec<Attribute<'db>>> {
639 priv_macro_declaration_data(db, macro_declaration_id).map(|data| data.attributes)
640}
641
642#[salsa::tracked]
644fn macro_declaration_attributes_tracked<'db>(
645 db: &'db dyn Database,
646 macro_declaration_id: MacroDeclarationId<'db>,
647) -> Maybe<Vec<Attribute<'db>>> {
648 macro_declaration_attributes(db, macro_declaration_id)
649}
650
651fn macro_declaration_resolver_data<'db>(
653 db: &'db dyn Database,
654 macro_declaration_id: MacroDeclarationId<'db>,
655) -> Maybe<Arc<ResolverData<'db>>> {
656 priv_macro_declaration_data(db, macro_declaration_id).map(|data| data.resolver_data)
657}
658
659#[salsa::tracked]
661fn macro_declaration_resolver_data_tracked<'db>(
662 db: &'db dyn Database,
663 macro_declaration_id: MacroDeclarationId<'db>,
664) -> Maybe<Arc<ResolverData<'db>>> {
665 macro_declaration_resolver_data(db, macro_declaration_id)
666}
667
668fn macro_declaration_rules<'db>(
670 db: &'db dyn Database,
671 macro_declaration_id: MacroDeclarationId<'db>,
672) -> Maybe<Vec<MacroRuleData<'db>>> {
673 priv_macro_declaration_data(db, macro_declaration_id).map(|data| data.rules)
674}
675
676#[salsa::tracked]
678fn macro_declaration_rules_tracked<'db>(
679 db: &'db dyn Database,
680 macro_declaration_id: MacroDeclarationId<'db>,
681) -> Maybe<Vec<MacroRuleData<'db>>> {
682 macro_declaration_rules(db, macro_declaration_id)
683}
684
685fn are_user_defined_inline_macros_enabled<'db>(
687 db: &dyn Database,
688 module_id: ModuleId<'db>,
689) -> bool {
690 let owning_crate = module_id.owning_crate(db);
691 let Some(config) = db.crate_config(owning_crate) else { return false };
692 config.settings.experimental_features.user_defined_inline_macros
693}
694
695pub trait MacroDeclarationSemantic<'db>: Database {
697 fn priv_macro_declaration_data(
699 &'db self,
700 macro_id: MacroDeclarationId<'db>,
701 ) -> Maybe<MacroDeclarationData<'db>> {
702 priv_macro_declaration_data_tracked(self.as_dyn_database(), macro_id)
703 }
704 fn macro_declaration_diagnostics(
706 &'db self,
707 macro_id: MacroDeclarationId<'db>,
708 ) -> Diagnostics<'db, SemanticDiagnostic<'db>> {
709 macro_declaration_diagnostics_tracked(self.as_dyn_database(), macro_id)
710 }
711 fn macro_declaration_resolver_data(
713 &'db self,
714 macro_id: MacroDeclarationId<'db>,
715 ) -> Maybe<Arc<ResolverData<'db>>> {
716 macro_declaration_resolver_data_tracked(self.as_dyn_database(), macro_id)
717 }
718 fn macro_declaration_attributes(
720 &'db self,
721 macro_id: MacroDeclarationId<'db>,
722 ) -> Maybe<Vec<Attribute<'db>>> {
723 macro_declaration_attributes_tracked(self.as_dyn_database(), macro_id)
724 }
725 fn macro_declaration_rules(
727 &'db self,
728 macro_id: MacroDeclarationId<'db>,
729 ) -> Maybe<Vec<MacroRuleData<'db>>> {
730 macro_declaration_rules_tracked(self.as_dyn_database(), macro_id)
731 }
732}
733impl<'db, T: Database + ?Sized> MacroDeclarationSemantic<'db> for T {}