cairo_lang_plugins/plugins/
external_attributes_validation.rs1use cairo_lang_defs::plugin::{MacroPlugin, MacroPluginMetadata, PluginDiagnostic, PluginResult};
2use cairo_lang_syntax::attribute::structured::{AttributeArgVariant, AttributeStructurize};
3use cairo_lang_syntax::node::db::SyntaxGroup;
4use cairo_lang_syntax::node::helpers::QueryAttrs;
5use cairo_lang_syntax::node::{Terminal, TypedSyntaxNode, ast};
6
7#[derive(Debug, Default)]
8#[non_exhaustive]
9pub struct ExternalAttributesValidationPlugin;
10
11const DOC_ATTR: &str = "doc";
12const HIDDEN_ATTR: &str = "hidden";
13
14impl MacroPlugin for ExternalAttributesValidationPlugin {
15 fn generate_code(
16 &self,
17 db: &dyn SyntaxGroup,
18 item_ast: ast::ModuleItem,
19 _metadata: &MacroPluginMetadata<'_>,
20 ) -> PluginResult {
21 match get_diagnostics(db, &item_ast) {
22 Some(diagnostics) => {
23 PluginResult { code: None, remove_original_item: false, diagnostics }
24 }
25 None => PluginResult::default(),
26 }
27 }
28
29 fn declared_attributes(&self) -> Vec<String> {
30 vec![DOC_ATTR.to_string()]
31 }
32}
33
34fn get_diagnostics<Item: QueryAttrs>(
35 db: &dyn SyntaxGroup,
36 item: &Item,
37) -> Option<Vec<PluginDiagnostic>> {
38 let mut diagnostics: Vec<PluginDiagnostic> = Vec::new();
39 item.query_attr(db, DOC_ATTR).into_iter().for_each(|attr| {
40 let args = attr.clone().structurize(db).args;
41 if args.is_empty() {
42 diagnostics.push(PluginDiagnostic::error(
43 attr.stable_ptr(),
44 format!("Expected arguments. Supported args: {}", HIDDEN_ATTR),
45 ));
46 return;
47 }
48 args.iter().for_each(|arg| match &arg.variant {
49 AttributeArgVariant::Unnamed(value) => {
50 let ast::Expr::Path(path) = value else {
51 diagnostics.push(PluginDiagnostic::error(
52 value,
53 format!("Expected identifier. Supported identifiers: {}", HIDDEN_ATTR),
54 ));
55 return;
56 };
57 let [ast::PathSegment::Simple(segment)] = &path.elements(db)[..] else {
58 diagnostics.push(PluginDiagnostic::error(
59 path,
60 "Wrong type of argument. Currently only #[doc(hidden)] is supported."
61 .to_owned(),
62 ));
63 return;
64 };
65 if segment.ident(db).text(db) != HIDDEN_ATTR {
66 diagnostics.push(PluginDiagnostic::error(
67 path,
68 "Wrong type of argument. Currently only #[doc(hidden)] is supported."
69 .to_owned(),
70 ));
71 }
72 }
73 _ => diagnostics.push(PluginDiagnostic::error(
74 &arg.arg,
75 format!("This argument is not supported. Supported args: {}", HIDDEN_ATTR),
76 )),
77 });
78 });
79 if diagnostics.is_empty() { None } else { Some(diagnostics) }
80}