cairo_lang_defs/
plugin.rs1use std::any::{self, Any};
2use std::hash::{Hash, Hasher};
3use std::ops::Deref;
4use std::sync::Arc;
5
6use cairo_lang_diagnostics::Severity;
7use cairo_lang_filesystem::cfg::CfgSet;
8use cairo_lang_filesystem::db::Edition;
9use cairo_lang_filesystem::ids::{CodeMapping, SmolStrId};
10use cairo_lang_filesystem::span::TextWidth;
11use cairo_lang_syntax::node::ids::SyntaxStablePtrId;
12use cairo_lang_syntax::node::{SyntaxNode, ast};
13use cairo_lang_utils::ordered_hash_set::OrderedHashSet;
14use salsa::Database;
15use serde::{Deserialize, Serialize};
16
17#[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 fn hash_value(&self) -> u64;
25}
26
27#[derive(Clone, Debug, Serialize, Deserialize)]
28pub struct DynGeneratedFileAuxData(pub Arc<dyn GeneratedFileAuxData>);
29unsafe impl salsa::Update for DynGeneratedFileAuxData {
30 unsafe fn maybe_update(old_pointer: *mut Self, new_value: Self) -> bool {
31 let old_aux_data: &mut Self = unsafe { &mut *old_pointer };
32
33 if Arc::ptr_eq(&old_aux_data.0, &new_value.0) {
35 return false;
36 }
37 if GeneratedFileAuxData::eq(&*old_aux_data.0, &*new_value.0) {
39 return false;
40 }
41 *old_aux_data = new_value;
43 true
44 }
45}
46impl DynGeneratedFileAuxData {
47 pub fn new<T: GeneratedFileAuxData + 'static>(aux_data: T) -> Self {
48 DynGeneratedFileAuxData(Arc::new(aux_data))
49 }
50}
51impl Deref for DynGeneratedFileAuxData {
52 type Target = Arc<dyn GeneratedFileAuxData>;
53
54 fn deref(&self) -> &Self::Target {
55 &self.0
56 }
57}
58impl PartialEq for DynGeneratedFileAuxData {
59 fn eq(&self, that: &DynGeneratedFileAuxData) -> bool {
60 self.0.eq(&*that.0)
61 }
62}
63impl Eq for DynGeneratedFileAuxData {}
64impl Hash for DynGeneratedFileAuxData {
65 fn hash<H: Hasher>(&self, state: &mut H) {
66 self.0.hash_value().hash(state);
67 }
68}
69
70pub struct PluginGeneratedFile {
72 pub name: String,
74 pub content: String,
76 pub code_mappings: Vec<CodeMapping>,
79 pub aux_data: Option<DynGeneratedFileAuxData>,
81 pub diagnostics_note: Option<String>,
85 pub is_unhygienic: bool,
88}
89
90#[derive(Default)]
92pub struct PluginResult<'db> {
93 pub code: Option<PluginGeneratedFile>,
95 pub diagnostics: Vec<PluginDiagnostic<'db>>,
97 pub remove_original_item: bool,
99}
100
101#[derive(Clone, Debug, Eq, Hash, PartialEq, salsa::Update)]
103pub struct PluginDiagnostic<'db> {
104 pub stable_ptr: SyntaxStablePtrId<'db>,
106 pub message: String,
107 pub severity: Severity,
109 pub inner_span: Option<(TextWidth, TextWidth)>,
114}
115impl<'db> PluginDiagnostic<'db> {
116 pub fn error(
117 stable_ptr: impl Into<SyntaxStablePtrId<'db>>,
118 message: String,
119 ) -> PluginDiagnostic<'db> {
120 PluginDiagnostic {
121 stable_ptr: stable_ptr.into(),
122 message,
123 severity: Severity::Error,
124 inner_span: None,
125 }
126 }
127
128 pub fn error_with_inner_span(
130 db: &dyn Database,
131 stable_ptr: impl Into<SyntaxStablePtrId<'db>>,
132 inner_span: SyntaxNode<'_>,
133 message: String,
134 ) -> PluginDiagnostic<'db> {
135 let stable_ptr = stable_ptr.into();
136 let offset = inner_span.offset(db) - stable_ptr.lookup(db).offset(db);
137 let width = inner_span.width(db);
138 PluginDiagnostic {
139 stable_ptr,
140 message,
141 severity: Severity::Error,
142 inner_span: Some((offset, width)),
143 }
144 }
145
146 pub fn warning(
147 stable_ptr: impl Into<SyntaxStablePtrId<'db>>,
148 message: String,
149 ) -> PluginDiagnostic<'db> {
150 PluginDiagnostic {
151 stable_ptr: stable_ptr.into(),
152 message,
153 severity: Severity::Warning,
154 inner_span: None,
155 }
156 }
157}
158
159pub struct MacroPluginMetadata<'a> {
162 pub cfg_set: &'a CfgSet,
164 pub declared_derives: &'a OrderedHashSet<SmolStrId<'a>>,
166 pub allowed_features: &'a OrderedHashSet<SmolStrId<'a>>,
168 pub edition: Edition,
170}
171
172pub trait MacroPlugin: std::fmt::Debug + Sync + Send + Any {
175 fn generate_code<'db>(
178 &self,
179 db: &'db dyn Database,
180 item_ast: ast::ModuleItem<'db>,
181 metadata: &MacroPluginMetadata<'_>,
182 ) -> PluginResult<'db>;
183
184 fn declared_attributes<'db>(&self, db: &'db dyn Database) -> Vec<SmolStrId<'db>>;
190
191 fn declared_derives<'db>(&self, _db: &'db dyn Database) -> Vec<SmolStrId<'db>> {
197 Vec::new()
198 }
199
200 fn executable_attributes<'db>(&self, _db: &'db dyn Database) -> Vec<SmolStrId<'db>> {
206 Vec::new()
207 }
208
209 fn phantom_type_attributes<'db>(&self, _db: &'db dyn Database) -> Vec<SmolStrId<'db>> {
213 Vec::new()
214 }
215
216 fn plugin_type_id(&self) -> any::TypeId {
219 self.type_id()
220 }
221}
222
223#[derive(Default)]
225pub struct InlinePluginResult<'db> {
226 pub code: Option<PluginGeneratedFile>,
227 pub diagnostics: Vec<PluginDiagnostic<'db>>,
229}
230
231pub trait InlineMacroExprPlugin: std::fmt::Debug + Sync + Send + Any {
232 fn generate_code<'db>(
236 &self,
237 db: &'db dyn Database,
238 item_ast: &ast::ExprInlineMacro<'db>,
239 metadata: &MacroPluginMetadata<'_>,
240 ) -> InlinePluginResult<'db>;
241
242 fn documentation(&self) -> Option<String> {
244 None
245 }
246
247 fn plugin_type_id(&self) -> any::TypeId {
250 self.type_id()
251 }
252}
253
254pub trait NamedPlugin: Default + 'static {
256 const NAME: &'static str;
257}