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 module_id = macro_declaration_id.parent_module(db);
113 let mut diagnostics = SemanticDiagnostics::new(module_id);
114
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 mut input_iter = 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(db)
254 .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<
269 impl DoubleEndedIterator<Item = ast::TokenTree<'db>> + Clone,
270 >,
271 ctx: &mut MatcherContext<'db>,
272 consume_all_input: bool,
273) -> Option<()> {
274 for matcher_element in matcher_elements.elements(db) {
275 match matcher_element {
276 ast::MacroElement::Token(matcher_token) => {
277 let input_token = input_iter.next()?;
278 match input_token {
279 ast::TokenTree::Token(token_tree_leaf) => {
280 if matcher_token.as_syntax_node().get_text_without_trivia(db)
281 != token_tree_leaf.as_syntax_node().get_text_without_trivia(db)
282 {
283 return None;
284 }
285 continue;
286 }
287 ast::TokenTree::Subtree(_) => return None,
288 ast::TokenTree::Repetition(_) => return None,
289 ast::TokenTree::Param(_) => return None,
290 ast::TokenTree::Missing(_) => unreachable!(),
291 }
292 }
293 ast::MacroElement::Param(param) => {
294 let placeholder_kind: PlaceholderKind =
295 if let ast::OptionParamKind::ParamKind(param_kind) = param.kind(db) {
296 param_kind.kind(db).into()
297 } else {
298 unreachable!(
299 "Missing macro rule param kind, should have been handled by the \
300 parser."
301 )
302 };
303 let placeholder_name = param.name(db).as_syntax_node().get_text_without_trivia(db);
304 match placeholder_kind {
305 PlaceholderKind::Identifier => {
306 let input_token = input_iter.next()?;
307 let captured_text = match &input_token {
308 ast::TokenTree::Token(token_tree_leaf) => {
309 match token_tree_leaf.leaf(db) {
310 ast::TokenNode::TerminalIdentifier(terminal_identifier) => {
311 terminal_identifier.text(db).to_string(db)
312 }
313 _ => return None,
314 }
315 }
316 _ => return None,
317 };
318 ctx.captures.entry(placeholder_name).or_default().push(CapturedValue {
319 text: captured_text,
320 stable_ptr: input_token.stable_ptr(db).untyped(),
321 });
322 if let Some(rep_id) = ctx.current_repetition_stack.last() {
323 ctx.placeholder_to_rep_id.insert(placeholder_name, *rep_id);
324 }
325 continue;
326 }
327 PlaceholderKind::Expr => {
328 let peek_token = input_iter.peek().cloned()?;
329 let file_id = peek_token.as_syntax_node().stable_ptr(db).file_id(db);
330 let expr_node = as_expr_macro_token_tree(input_iter.clone(), file_id, db)?;
331 let expr_text = expr_node.as_syntax_node().get_text(db);
332 let expr_length = expr_text.len();
333 if expr_length == 0 {
336 return None;
337 }
338
339 ctx.captures.entry(placeholder_name).or_default().push(CapturedValue {
340 text: expr_text.to_string(),
341 stable_ptr: peek_token.stable_ptr(db).untyped(),
342 });
343 if let Some(rep_id) = ctx.current_repetition_stack.last() {
344 ctx.placeholder_to_rep_id.insert(placeholder_name, *rep_id);
345 }
346 let expr_length = expr_text.len();
347 let mut current_length = 0;
348
349 for token_tree_leaf in input_iter.by_ref() {
353 let token_text = match token_tree_leaf {
354 ast::TokenTree::Token(leaf) => leaf.as_syntax_node(),
355 ast::TokenTree::Subtree(subtree) => subtree.as_syntax_node(),
356 ast::TokenTree::Repetition(rep) => rep.as_syntax_node(),
357 ast::TokenTree::Param(param) => param.as_syntax_node(),
358 ast::TokenTree::Missing(_) => unreachable!(),
359 }
360 .get_text(db);
361 current_length += token_text.len();
362 if current_length >= expr_length {
363 break;
364 }
365 }
366 continue;
367 }
368 }
369 }
370 ast::MacroElement::Subtree(matcher_subtree) => {
371 let input_token = input_iter.next()?;
372 if let ast::TokenTree::Subtree(input_subtree) = input_token {
373 let inner_elements = get_macro_elements(db, matcher_subtree.subtree(db));
374 let mut inner_input_iter = match input_subtree.subtree(db) {
375 ast::WrappedTokenTree::Parenthesized(tt) => tt.tokens(db),
376 ast::WrappedTokenTree::Braced(tt) => tt.tokens(db),
377 ast::WrappedTokenTree::Bracketed(tt) => tt.tokens(db),
378 ast::WrappedTokenTree::Missing(_) => unreachable!(),
379 }
380 .elements(db)
381 .peekable();
382 is_macro_rule_match_ex(db, inner_elements, &mut inner_input_iter, ctx, true)?;
383 continue;
384 } else {
385 return None;
386 }
387 }
388 ast::MacroElement::Repetition(repetition) => {
389 let rep_id = RepetitionId(ctx.next_repetition_id);
390 ctx.next_repetition_id += 1;
391 ctx.current_repetition_stack.push(rep_id);
392 let elements = repetition.elements(db);
393 let operator = repetition.operator(db);
394 let separator_token = repetition.separator(db);
395 let expected_separator = match separator_token {
396 ast::OptionTerminalComma::TerminalComma(sep) => {
397 Some(sep.as_syntax_node().get_text_without_trivia(db))
398 }
399 ast::OptionTerminalComma::Empty(_) => None,
400 };
401 let mut match_count = 0;
402 loop {
403 let mut inner_ctx = ctx.clone();
404 let mut temp_iter = input_iter.clone();
405 if is_macro_rule_match_ex(
406 db,
407 elements.clone(),
408 &mut temp_iter,
409 &mut inner_ctx,
410 false,
411 )
412 .is_none()
413 {
414 break;
415 }
416 *ctx = inner_ctx;
417 *input_iter = temp_iter;
418 match_count += 1;
419 if let Some(expected_sep) = &expected_separator {
420 if let Some(ast::TokenTree::Token(token_leaf)) = input_iter.peek() {
421 let actual = token_leaf.as_syntax_node().get_text_without_trivia(db);
422 if actual == *expected_sep {
423 input_iter.next();
424 } else {
425 break;
426 }
427 } else {
428 break;
429 }
430 }
431 }
432 ctx.repetition_match_counts.insert(rep_id, match_count);
433 ctx.repetition_operators.insert(rep_id, operator.clone());
434 for placeholder_name in ctx.captures.keys() {
435 ctx.placeholder_to_rep_id.insert(*placeholder_name, rep_id);
436 }
437
438 for i in 0..match_count {
439 ctx.repetition_indices.insert(rep_id, i);
440 }
441 ctx.current_repetition_stack.pop();
442 continue;
443 }
444 }
445 }
446
447 if consume_all_input && input_iter.next().is_some() {
448 return None;
449 }
450 Some(())
451}
452
453fn validate_repetition_operator_constraints(ctx: &MatcherContext<'_>) -> bool {
454 for (&rep_id, &count) in ctx.repetition_match_counts.iter() {
455 match ctx.repetition_operators.get(&rep_id) {
456 Some(ast::MacroRepetitionOperator::ZeroOrOne(_)) if count > 1 => return false,
457 Some(ast::MacroRepetitionOperator::OneOrMore(_)) if count < 1 => return false,
458 Some(ast::MacroRepetitionOperator::ZeroOrMore(_)) | None => {}
459 _ => {}
460 }
461 }
462 true
463}
464
465#[derive(Debug, Clone, PartialEq, Eq)]
467pub struct MacroExpansionResult {
468 pub text: Arc<str>,
470 pub code_mappings: Arc<[CodeMapping]>,
472}
473
474pub fn expand_macro_rule(
480 db: &dyn Database,
481 rule: &MacroRuleData<'_>,
482 matcher_ctx: &mut MatcherContext<'_>,
483) -> Maybe<MacroExpansionResult> {
484 let node = rule.expansion.as_syntax_node();
485 let mut res_buffer = String::new();
486 let mut code_mappings = Vec::new();
487 expand_macro_rule_ex(db, node, matcher_ctx, &mut res_buffer, &mut code_mappings)?;
488 Ok(MacroExpansionResult { text: res_buffer.into(), code_mappings: code_mappings.into() })
489}
490
491fn expand_macro_rule_ex(
497 db: &dyn Database,
498 node: SyntaxNode<'_>,
499 matcher_ctx: &mut MatcherContext<'_>,
500 res_buffer: &mut String,
501 code_mappings: &mut Vec<CodeMapping>,
502) -> Maybe<()> {
503 match node.kind(db) {
504 SyntaxKind::MacroParam => {
505 let path_node = MacroParam::from_syntax_node(db, node);
506 if let Some(name) = extract_placeholder(db, &path_node) {
507 let rep_index = matcher_ctx
508 .placeholder_to_rep_id
509 .get(&name)
510 .and_then(|rep_id| matcher_ctx.repetition_indices.get(rep_id))
511 .copied();
512 let value = matcher_ctx
513 .captures
514 .get(&name)
515 .and_then(|v| rep_index.map_or_else(|| v.first(), |i| v.get(i)))
516 .ok_or_else(skip_diagnostic)?;
517 let start = TextWidth::from_str(res_buffer).as_offset();
518 let span = TextSpan::new_with_width(start, TextWidth::from_str(&value.text));
519 res_buffer.push_str(&value.text);
520 code_mappings.push(CodeMapping {
521 span,
522 origin: CodeOrigin::Span(value.stable_ptr.lookup(db).span_without_trivia(db)),
523 });
524 return Ok(());
525 }
526 }
527 SyntaxKind::MacroRepetition => {
528 let repetition = ast::MacroRepetition::from_syntax_node(db, node);
529 let elements = repetition.elements(db);
530 let first_param = find_first_repetition_param(db, elements.elements(db))
531 .ok_or_else(skip_diagnostic)?;
532 let placeholder_name = first_param.name(db).text(db);
533 let Some(rep_id) = matcher_ctx.placeholder_to_rep_id.get(&placeholder_name).copied()
536 else {
537 return Ok(());
538 };
539 let repetition_len =
540 matcher_ctx.captures.get(&placeholder_name).map(|v| v.len()).unwrap_or(0);
541 for i in 0..repetition_len {
542 matcher_ctx.repetition_indices.insert(rep_id, i);
543 for element in elements.elements(db) {
544 expand_macro_rule_ex(
545 db,
546 element.as_syntax_node(),
547 matcher_ctx,
548 res_buffer,
549 code_mappings,
550 )?;
551 }
552
553 if i + 1 < repetition_len
554 && let ast::OptionTerminalComma::TerminalComma(sep) = repetition.separator(db)
555 {
556 res_buffer.push_str(sep.as_syntax_node().get_text(db));
557 }
558 }
559
560 matcher_ctx.repetition_indices.swap_remove(&rep_id);
561 return Ok(());
562 }
563 _ => {
564 if node.kind(db).is_terminal() {
565 res_buffer.push_str(node.get_text(db));
566 return Ok(());
567 }
568
569 for child in node.get_children(db).iter() {
570 expand_macro_rule_ex(db, *child, matcher_ctx, res_buffer, code_mappings)?;
571 }
572 return Ok(());
573 }
574 }
575 if node.kind(db).is_terminal() {
576 res_buffer.push_str(node.get_text(db));
577 return Ok(());
578 }
579 for child in node.get_children(db).iter() {
580 expand_macro_rule_ex(db, *child, matcher_ctx, res_buffer, code_mappings)?;
581 }
582 Ok(())
583}
584
585fn find_first_repetition_param<'db>(
587 db: &'db dyn Database,
588 elements: impl IntoIterator<Item = MacroElement<'db>>,
589) -> Option<MacroParam<'db>> {
590 for element in elements {
591 match element {
592 ast::MacroElement::Param(param) => return Some(param),
593 ast::MacroElement::Subtree(subtree) => {
594 let inner_elements = get_macro_elements(db, subtree.subtree(db)).elements(db);
595 if let Some(param) = find_first_repetition_param(db, inner_elements) {
596 return Some(param);
597 }
598 }
599 ast::MacroElement::Repetition(repetition) => {
600 let inner_elements = repetition.elements(db).elements(db);
601 if let Some(param) = find_first_repetition_param(db, inner_elements) {
602 return Some(param);
603 }
604 }
605 ast::MacroElement::Token(_) => {}
606 }
607 }
608 None
609}
610
611fn macro_declaration_diagnostics<'db>(
613 db: &'db dyn Database,
614 macro_declaration_id: MacroDeclarationId<'db>,
615) -> Diagnostics<'db, SemanticDiagnostic<'db>> {
616 priv_macro_declaration_data(db, macro_declaration_id)
617 .map(|data| data.diagnostics)
618 .unwrap_or_default()
619}
620
621#[salsa::tracked]
623fn macro_declaration_diagnostics_tracked<'db>(
624 db: &'db dyn Database,
625 macro_declaration_id: MacroDeclarationId<'db>,
626) -> Diagnostics<'db, SemanticDiagnostic<'db>> {
627 macro_declaration_diagnostics(db, macro_declaration_id)
628}
629
630fn macro_declaration_attributes<'db>(
632 db: &'db dyn Database,
633 macro_declaration_id: MacroDeclarationId<'db>,
634) -> Maybe<Vec<Attribute<'db>>> {
635 priv_macro_declaration_data(db, macro_declaration_id).map(|data| data.attributes)
636}
637
638#[salsa::tracked]
640fn macro_declaration_attributes_tracked<'db>(
641 db: &'db dyn Database,
642 macro_declaration_id: MacroDeclarationId<'db>,
643) -> Maybe<Vec<Attribute<'db>>> {
644 macro_declaration_attributes(db, macro_declaration_id)
645}
646
647fn macro_declaration_resolver_data<'db>(
649 db: &'db dyn Database,
650 macro_declaration_id: MacroDeclarationId<'db>,
651) -> Maybe<Arc<ResolverData<'db>>> {
652 priv_macro_declaration_data(db, macro_declaration_id).map(|data| data.resolver_data)
653}
654
655#[salsa::tracked]
657fn macro_declaration_resolver_data_tracked<'db>(
658 db: &'db dyn Database,
659 macro_declaration_id: MacroDeclarationId<'db>,
660) -> Maybe<Arc<ResolverData<'db>>> {
661 macro_declaration_resolver_data(db, macro_declaration_id)
662}
663
664fn macro_declaration_rules<'db>(
666 db: &'db dyn Database,
667 macro_declaration_id: MacroDeclarationId<'db>,
668) -> Maybe<Vec<MacroRuleData<'db>>> {
669 priv_macro_declaration_data(db, macro_declaration_id).map(|data| data.rules)
670}
671
672#[salsa::tracked]
674fn macro_declaration_rules_tracked<'db>(
675 db: &'db dyn Database,
676 macro_declaration_id: MacroDeclarationId<'db>,
677) -> Maybe<Vec<MacroRuleData<'db>>> {
678 macro_declaration_rules(db, macro_declaration_id)
679}
680
681fn are_user_defined_inline_macros_enabled<'db>(
683 db: &dyn Database,
684 module_id: ModuleId<'db>,
685) -> bool {
686 let owning_crate = module_id.owning_crate(db);
687 let Some(config) = db.crate_config(owning_crate) else { return false };
688 config.settings.experimental_features.user_defined_inline_macros
689}
690
691pub trait MacroDeclarationSemantic<'db>: Database {
693 fn priv_macro_declaration_data(
695 &'db self,
696 macro_id: MacroDeclarationId<'db>,
697 ) -> Maybe<MacroDeclarationData<'db>> {
698 priv_macro_declaration_data_tracked(self.as_dyn_database(), macro_id)
699 }
700 fn macro_declaration_diagnostics(
702 &'db self,
703 macro_id: MacroDeclarationId<'db>,
704 ) -> Diagnostics<'db, SemanticDiagnostic<'db>> {
705 macro_declaration_diagnostics_tracked(self.as_dyn_database(), macro_id)
706 }
707 fn macro_declaration_resolver_data(
709 &'db self,
710 macro_id: MacroDeclarationId<'db>,
711 ) -> Maybe<Arc<ResolverData<'db>>> {
712 macro_declaration_resolver_data_tracked(self.as_dyn_database(), macro_id)
713 }
714 fn macro_declaration_attributes(
716 &'db self,
717 macro_id: MacroDeclarationId<'db>,
718 ) -> Maybe<Vec<Attribute<'db>>> {
719 macro_declaration_attributes_tracked(self.as_dyn_database(), macro_id)
720 }
721 fn macro_declaration_rules(
723 &'db self,
724 macro_id: MacroDeclarationId<'db>,
725 ) -> Maybe<Vec<MacroRuleData<'db>>> {
726 macro_declaration_rules_tracked(self.as_dyn_database(), macro_id)
727 }
728}
729impl<'db, T: Database + ?Sized> MacroDeclarationSemantic<'db> for T {}