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::{ErrorCode, 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 pub error_code: Option<ErrorCode>,
116}
117impl<'db> PluginDiagnostic<'db> {
118 pub fn error(
119 stable_ptr: impl Into<SyntaxStablePtrId<'db>>,
120 message: String,
121 ) -> PluginDiagnostic<'db> {
122 PluginDiagnostic {
123 stable_ptr: stable_ptr.into(),
124 message,
125 severity: Severity::Error,
126 inner_span: None,
127 error_code: None,
128 }
129 }
130
131 pub fn error_with_inner_span(
133 db: &dyn Database,
134 stable_ptr: impl Into<SyntaxStablePtrId<'db>>,
135 inner_span: SyntaxNode<'_>,
136 message: String,
137 ) -> PluginDiagnostic<'db> {
138 let stable_ptr = stable_ptr.into();
139 let offset = inner_span.offset(db) - stable_ptr.lookup(db).offset(db);
140 let width = inner_span.width(db);
141 PluginDiagnostic {
142 stable_ptr,
143 message,
144 severity: Severity::Error,
145 inner_span: Some((offset, width)),
146 error_code: None,
147 }
148 }
149
150 pub fn warning(
151 stable_ptr: impl Into<SyntaxStablePtrId<'db>>,
152 message: String,
153 ) -> PluginDiagnostic<'db> {
154 PluginDiagnostic {
155 stable_ptr: stable_ptr.into(),
156 message,
157 severity: Severity::Warning,
158 inner_span: None,
159 error_code: None,
160 }
161 }
162
163 pub fn with_error_code(self, error_code: ErrorCode) -> PluginDiagnostic<'db> {
165 PluginDiagnostic { error_code: Some(error_code), ..self }
166 }
167}
168
169pub struct MacroPluginMetadata<'a> {
172 pub cfg_set: &'a CfgSet,
174 pub declared_derives: &'a OrderedHashSet<SmolStrId<'a>>,
176 pub allowed_features: &'a OrderedHashSet<SmolStrId<'a>>,
178 pub edition: Edition,
180}
181
182pub trait MacroPlugin: std::fmt::Debug + Sync + Send + Any {
184 fn generate_code<'db>(
187 &self,
188 db: &'db dyn Database,
189 item_ast: ast::ModuleItem<'db>,
190 metadata: &MacroPluginMetadata<'_>,
191 ) -> PluginResult<'db>;
192
193 fn declared_attributes<'db>(&self, db: &'db dyn Database) -> Vec<SmolStrId<'db>>;
199
200 fn declared_derives<'db>(&self, _db: &'db dyn Database) -> Vec<SmolStrId<'db>> {
206 Vec::new()
207 }
208
209 fn executable_attributes<'db>(&self, _db: &'db dyn Database) -> Vec<SmolStrId<'db>> {
215 Vec::new()
216 }
217
218 fn phantom_type_attributes<'db>(&self, _db: &'db dyn Database) -> Vec<SmolStrId<'db>> {
222 Vec::new()
223 }
224
225 fn plugin_type_id(&self) -> any::TypeId {
228 self.type_id()
229 }
230}
231
232#[derive(Default)]
234pub struct InlinePluginResult<'db> {
235 pub code: Option<PluginGeneratedFile>,
236 pub diagnostics: Vec<PluginDiagnostic<'db>>,
238}
239
240pub trait InlineMacroExprPlugin: std::fmt::Debug + Sync + Send + Any {
241 fn generate_code<'db>(
245 &self,
246 db: &'db dyn Database,
247 item_ast: &ast::ExprInlineMacro<'db>,
248 metadata: &MacroPluginMetadata<'_>,
249 ) -> InlinePluginResult<'db>;
250
251 fn documentation(&self) -> Option<String> {
253 None
254 }
255
256 fn plugin_type_id(&self) -> any::TypeId {
259 self.type_id()
260 }
261}
262
263pub trait NamedPlugin: Default + 'static {
265 const NAME: &'static str;
266}