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