cairo_lang_defs/
plugin_utils.rs1use cairo_lang_syntax::node::db::SyntaxGroup;
2use cairo_lang_syntax::node::helpers::WrappedArgListHelper;
3use cairo_lang_syntax::node::ids::SyntaxStablePtrId;
4use cairo_lang_syntax::node::{SyntaxNode, TypedSyntaxNode, ast};
5use cairo_lang_utils::require;
6use itertools::Itertools;
7
8use crate::plugin::{InlinePluginResult, PluginDiagnostic, PluginResult};
9
10pub trait InlineMacroCall {
12 type PathNode: TypedSyntaxNode;
13 type Result: PluginResultTrait;
14 fn arguments(&self, db: &dyn SyntaxGroup) -> ast::WrappedArgList;
15 fn path(&self, db: &dyn SyntaxGroup) -> Self::PathNode;
16}
17
18impl InlineMacroCall for ast::LegacyExprInlineMacro {
19 type PathNode = ast::ExprPath;
20 type Result = InlinePluginResult;
21
22 fn arguments(&self, db: &dyn SyntaxGroup) -> ast::WrappedArgList {
23 self.arguments(db)
24 }
25
26 fn path(&self, db: &dyn SyntaxGroup) -> ast::ExprPath {
27 self.path(db)
28 }
29}
30
31impl InlineMacroCall for ast::LegacyItemInlineMacro {
32 type PathNode = ast::ExprPath;
33 type Result = PluginResult;
34
35 fn arguments(&self, db: &dyn SyntaxGroup) -> ast::WrappedArgList {
36 self.arguments(db)
37 }
38
39 fn path(&self, db: &dyn SyntaxGroup) -> ast::ExprPath {
40 self.path(db)
41 }
42}
43
44pub trait PluginResultTrait {
46 fn diagnostic_only(diagnostic: PluginDiagnostic) -> Self;
47}
48
49impl PluginResultTrait for InlinePluginResult {
50 fn diagnostic_only(diagnostic: PluginDiagnostic) -> Self {
51 InlinePluginResult { code: None, diagnostics: vec![diagnostic] }
52 }
53}
54
55impl PluginResultTrait for PluginResult {
56 fn diagnostic_only(diagnostic: PluginDiagnostic) -> Self {
57 PluginResult { code: None, diagnostics: vec![diagnostic], remove_original_item: true }
58 }
59}
60
61pub fn unsupported_bracket_diagnostic<CallAst: InlineMacroCall>(
63 db: &dyn SyntaxGroup,
64 legacy_macro_ast: &CallAst,
65 macro_ast: impl Into<SyntaxStablePtrId>,
66) -> CallAst::Result {
67 CallAst::Result::diagnostic_only(PluginDiagnostic::error_with_inner_span(
68 db,
69 macro_ast,
70 legacy_macro_ast.arguments(db).left_bracket_syntax_node(db),
71 format!(
72 "Macro `{}` does not support this bracket type.",
73 legacy_macro_ast.path(db).as_syntax_node().get_text_without_trivia(db)
74 ),
75 ))
76}
77
78pub fn not_legacy_macro_diagnostic(stable_ptr: SyntaxStablePtrId) -> PluginDiagnostic {
79 PluginDiagnostic::error(
80 stable_ptr,
81 "Macro can not be parsed as legacy macro. Expected an argument list wrapped in either \
82 parentheses, brackets, or braces."
83 .to_string(),
84 )
85}
86
87pub fn extract_single_unnamed_arg(
89 db: &dyn SyntaxGroup,
90 macro_arguments: ast::ArgList,
91) -> Option<ast::Expr> {
92 let mut elements = macro_arguments.elements(db);
93 if elements.len() == 1 { try_extract_unnamed_arg(db, &elements.next()?) } else { None }
94}
95
96pub fn extract_unnamed_args(
98 db: &dyn SyntaxGroup,
99 macro_arguments: &ast::ArgList,
100 n: usize,
101) -> Option<Vec<ast::Expr>> {
102 let elements = macro_arguments.elements(db);
103 require(elements.len() == n)?;
104 elements.map(|x| try_extract_unnamed_arg(db, &x)).collect()
105}
106
107pub fn try_extract_unnamed_arg(db: &dyn SyntaxGroup, arg_ast: &ast::Arg) -> Option<ast::Expr> {
109 if let ast::ArgClause::Unnamed(arg_clause) = arg_ast.arg_clause(db) {
110 Some(arg_clause.value(db))
111 } else {
112 None
113 }
114}
115
116pub fn escape_node(db: &dyn SyntaxGroup, node: SyntaxNode) -> String {
118 node.get_text_without_trivia(db).replace('{', "{{").replace('}', "}}").escape_unicode().join("")
119}
120
121#[macro_export]
135macro_rules! extract_macro_unnamed_args {
136 ($db:expr, $syntax:expr, $n:expr, $pattern:pat, $diagnostics_ptr:expr) => {{
137 let arguments = $crate::plugin_utils::InlineMacroCall::arguments($syntax, $db);
138 if !matches!(arguments, $pattern) {
139 return $crate::plugin_utils::unsupported_bracket_diagnostic(
140 $db,
141 $syntax,
142 $diagnostics_ptr,
143 );
144 }
145 let macro_arg_list =
148 cairo_lang_syntax::node::helpers::WrappedArgListHelper::arg_list(&arguments, $db)
149 .unwrap();
150
151 let args = $crate::plugin_utils::extract_unnamed_args($db, ¯o_arg_list, $n);
152 let Some(args) = args else {
153 return $crate::plugin_utils::PluginResultTrait::diagnostic_only(
154 PluginDiagnostic::error(
155 $diagnostics_ptr,
156 format!(
157 "Macro `{}` must have exactly {} unnamed arguments.",
158 $crate::plugin_utils::InlineMacroCall::path($syntax, $db)
159 .as_syntax_node()
160 .get_text_without_trivia($db),
161 $n
162 ),
163 ),
164 );
165 };
166 let args: [ast::Expr; $n] = args.try_into().unwrap();
167 args
168 }};
169}
170
171#[macro_export]
186macro_rules! extract_macro_single_unnamed_arg {
187 ($db:expr, $syntax:expr, $pattern:pat, $diagnostics_ptr:expr) => {{
188 let [x] = $crate::extract_macro_unnamed_args!($db, $syntax, 1, $pattern, $diagnostics_ptr);
189 x
190 }};
191}