cairo_lang_defs/
plugin.rs

1use std::any::{self, Any};
2use std::ops::Deref;
3use std::sync::Arc;
4
5use cairo_lang_diagnostics::Severity;
6use cairo_lang_filesystem::cfg::CfgSet;
7use cairo_lang_filesystem::db::Edition;
8use cairo_lang_filesystem::ids::CodeMapping;
9use cairo_lang_filesystem::span::TextSpan;
10use cairo_lang_syntax::node::ast;
11use cairo_lang_syntax::node::db::SyntaxGroup;
12use cairo_lang_syntax::node::ids::SyntaxStablePtrId;
13use cairo_lang_utils::ordered_hash_set::OrderedHashSet;
14use serde::{Deserialize, Serialize};
15use smol_str::SmolStr;
16
17/// A trait for arbitrary data that a macro generates along with the generated file.
18#[typetag::serde]
19pub trait GeneratedFileAuxData: std::fmt::Debug + Sync + Send {
20    fn as_any(&self) -> &dyn Any;
21    fn eq(&self, other: &dyn GeneratedFileAuxData) -> bool;
22}
23
24#[derive(Clone, Debug, Serialize, Deserialize)]
25pub struct DynGeneratedFileAuxData(pub Arc<dyn GeneratedFileAuxData>);
26impl DynGeneratedFileAuxData {
27    pub fn new<T: GeneratedFileAuxData + 'static>(aux_data: T) -> Self {
28        DynGeneratedFileAuxData(Arc::new(aux_data))
29    }
30}
31impl Deref for DynGeneratedFileAuxData {
32    type Target = Arc<dyn GeneratedFileAuxData>;
33
34    fn deref(&self) -> &Self::Target {
35        &self.0
36    }
37}
38impl PartialEq for DynGeneratedFileAuxData {
39    fn eq(&self, that: &DynGeneratedFileAuxData) -> bool {
40        GeneratedFileAuxData::eq(&*self.0, &*that.0)
41    }
42}
43impl Eq for DynGeneratedFileAuxData {}
44
45/// Virtual code file generated by a plugin.
46pub struct PluginGeneratedFile {
47    /// Name for the virtual file. Will appear in diagnostics.
48    pub name: SmolStr,
49    /// Code content for the file.
50    pub content: String,
51    /// A code mapper, to allow more readable diagnostics that originate in plugin generated
52    /// virtual files.
53    pub code_mappings: Vec<CodeMapping>,
54    /// Arbitrary data that the plugin generates along with the file.
55    pub aux_data: Option<DynGeneratedFileAuxData>,
56    /// Diagnostic note for the plugin generated file.
57    /// This will be used as [`cairo_lang_diagnostics::DiagnosticNote`] on diagnostics originating
58    /// from this file.
59    pub diagnostics_note: Option<String>,
60}
61
62/// Result of plugin code generation.
63#[derive(Default)]
64pub struct PluginResult {
65    /// Filename, content.
66    pub code: Option<PluginGeneratedFile>,
67    /// Diagnostics.
68    pub diagnostics: Vec<PluginDiagnostic>,
69    /// If true - the original item should be removed, if false - it should remain as is.
70    pub remove_original_item: bool,
71}
72
73#[derive(Clone, Debug, Eq, Hash, PartialEq)]
74pub struct PluginDiagnostic {
75    pub stable_ptr: SyntaxStablePtrId,
76    /// Span relative to the start of the `stable_ptr`.
77    /// No assertion is made that the span is fully contained within the node pointed to by
78    /// `stable_ptr`. When printing diagnostics, any part of the span that falls outside the
79    /// referenced node will be silently ignored.
80    pub relative_span: Option<TextSpan>,
81    pub message: String,
82    pub severity: Severity,
83}
84impl PluginDiagnostic {
85    pub fn error(stable_ptr: impl Into<SyntaxStablePtrId>, message: String) -> PluginDiagnostic {
86        PluginDiagnostic {
87            stable_ptr: stable_ptr.into(),
88            relative_span: None,
89            message,
90            severity: Severity::Error,
91        }
92    }
93    pub fn warning(stable_ptr: impl Into<SyntaxStablePtrId>, message: String) -> PluginDiagnostic {
94        PluginDiagnostic {
95            stable_ptr: stable_ptr.into(),
96            relative_span: None,
97            message,
98            severity: Severity::Warning,
99        }
100    }
101
102    pub fn with_relative_span(mut self, span: TextSpan) -> Self {
103        self.relative_span = Some(span);
104        self
105    }
106}
107
108/// A structure containing additional info about the current module item on which macro plugin
109/// operates.
110pub struct MacroPluginMetadata<'a> {
111    /// Config set of the crate to which the current item belongs.
112    pub cfg_set: &'a CfgSet,
113    /// The possible derives declared by any plugin.
114    pub declared_derives: &'a OrderedHashSet<String>,
115    /// The allowed features at the macro activation site.
116    pub allowed_features: &'a OrderedHashSet<SmolStr>,
117    /// The edition of the crate to which the current item belongs.
118    pub edition: Edition,
119}
120
121// TODO(spapini): Move to another place.
122/// A trait for a macro plugin: external plugin that generates additional code for items.
123pub trait MacroPlugin: std::fmt::Debug + Sync + Send + Any {
124    /// Generates code for an item. If no code should be generated returns None.
125    /// Otherwise, returns (virtual_module_name, module_content), and a virtual submodule
126    /// with that name and content should be created.
127    fn generate_code(
128        &self,
129        db: &dyn SyntaxGroup,
130        item_ast: ast::ModuleItem,
131        metadata: &MacroPluginMetadata<'_>,
132    ) -> PluginResult;
133
134    /// Attributes this plugin uses.
135    /// Attributes the plugin uses without declaring here are likely to cause a compilation error
136    /// for unknown attribute.
137    /// Note: They may not cause a diagnostic if some other plugin declares such attribute, but
138    /// plugin writers should not rely on that.
139    fn declared_attributes(&self) -> Vec<String>;
140
141    /// Derives this plugin supplies.
142    /// Any derived classes the plugin supplies without declaring here are likely to cause a
143    /// compilation error for unknown derive.
144    /// Note: They may not cause a diagnostic if some other plugin declares such derive, but
145    /// plugin writers should not rely on that.
146    fn declared_derives(&self) -> Vec<String> {
147        Vec::new()
148    }
149
150    /// Attributes that should mark the function as an executable.
151    /// Functions marked with executable attributes will be listed
152    /// in a dedicated field in the generated program.
153    /// Must return a subset of `declared_attributes`.
154    /// This mechanism is optional.
155    fn executable_attributes(&self) -> Vec<String> {
156        Vec::new()
157    }
158
159    /// Attributes that mark a type as a phantom type. Must return a subset of
160    /// `declared_attributes`.
161    /// This mechanism is optional.
162    fn phantom_type_attributes(&self) -> Vec<String> {
163        Vec::new()
164    }
165
166    /// A `TypeId` of the plugin, used to compare the concrete types
167    /// of plugins given as trait objects.
168    fn plugin_type_id(&self) -> any::TypeId {
169        self.type_id()
170    }
171}
172
173/// Result of plugin code generation.
174#[derive(Default)]
175pub struct InlinePluginResult {
176    pub code: Option<PluginGeneratedFile>,
177    /// Diagnostics.
178    pub diagnostics: Vec<PluginDiagnostic>,
179}
180
181pub trait InlineMacroExprPlugin: std::fmt::Debug + Sync + Send + Any {
182    /// Generates code for an item. If no code should be generated returns None.
183    /// Otherwise, returns (virtual_module_name, module_content), and a virtual submodule
184    /// with that name and content should be created.
185    fn generate_code(
186        &self,
187        db: &dyn SyntaxGroup,
188        item_ast: &ast::ExprInlineMacro,
189        metadata: &MacroPluginMetadata<'_>,
190    ) -> InlinePluginResult;
191
192    /// Allows for the plugin to provide documentation for an inline macro.
193    fn documentation(&self) -> Option<String> {
194        None
195    }
196
197    /// A `TypeId` of the plugin, used to compare the concrete types
198    /// of plugins given as trait objects.
199    fn plugin_type_id(&self) -> any::TypeId {
200        self.type_id()
201    }
202}
203
204/// A trait for easier addition of macro plugins.
205pub trait NamedPlugin: Default + 'static {
206    const NAME: &'static str;
207}