safety_parser/safety/
mod.rs

1use crate::{
2    Str,
3    configuration::{ANY, TagType, config_exists, doc_option, get_tag},
4};
5use indexmap::IndexMap;
6use proc_macro2::TokenStream;
7use quote::quote;
8use syn::{
9    parse::{Parse, ParseStream},
10    punctuated::Punctuated,
11    token::{Brace, Paren},
12    *,
13};
14
15mod utils;
16
17#[cfg(test)]
18mod tests;
19
20#[derive(Debug)]
21pub struct SafetyAttr {
22    pub attr: Attribute,
23    pub args: SafetyAttrArgs,
24}
25
26impl Parse for SafetyAttr {
27    fn parse(input: ParseStream) -> Result<Self> {
28        let mut attrs = Attribute::parse_outer(input)?;
29
30        assert!(attrs.len() == 1, "Given input must be a single #[safety] attribute.");
31        let attr = attrs.remove(0);
32        drop(attrs);
33
34        // We don't check attribute name. Normally, it's #[safety { ... }],
35        // but it can be #[path::to::safety {}], or #[reexported {}], or #[rapx::inner {}].
36
37        let args = attr.parse_args()?;
38        Ok(SafetyAttr { attr, args })
39    }
40}
41
42/// Parse a full attribute such as `#[rapx::inner { ... }]` to get properties.
43pub fn parse_attr_and_get_properties(attr: &str) -> Box<[PropertiesAndReason]> {
44    let attr: SafetyAttr = parse_str(attr)
45        .unwrap_or_else(|e| panic!("Failed to parse {attr:?} as a safety attribute:\n{e}"));
46    attr.args.args.into_iter().collect()
47}
48
49#[derive(Debug)]
50pub struct SafetyAttrArgs {
51    pub args: Punctuated<PropertiesAndReason, Token![;]>,
52}
53
54impl Parse for SafetyAttrArgs {
55    fn parse(input: ParseStream) -> Result<Self> {
56        Ok(SafetyAttrArgs { args: Punctuated::parse_terminated(input)? })
57    }
58}
59
60impl SafetyAttrArgs {
61    pub fn property_reason(&self) -> impl Iterator<Item = (&Property, Option<&str>)> {
62        self.args.iter().flat_map(|arg| arg.tags.iter().map(|prop| (prop, arg.desc.as_deref())))
63    }
64}
65
66#[derive(Debug)]
67pub struct PropertiesAndReason {
68    pub tags: Box<[Property]>,
69    pub desc: Option<Str>,
70}
71
72impl Parse for PropertiesAndReason {
73    fn parse(input: ParseStream) -> Result<Self> {
74        let mut tags = Vec::<Property>::new();
75        let mut desc = None;
76
77        while !input.cursor().eof() {
78            let tag: TagNameType = input.parse()?;
79            if config_exists() {
80                tag.check_type();
81            }
82            let args = if input.peek(Paren) {
83                let content;
84                parenthesized!(content in input);
85                let args = Punctuated::<Expr, Token![,]>::parse_terminated(&content)?;
86                args.into_iter().collect()
87            } else if input.peek(Brace) {
88                let content;
89                braced!(content in input);
90                let args = Punctuated::<Expr, Token![,]>::parse_terminated(&content)?;
91                args.into_iter().collect()
92            } else {
93                Default::default()
94            };
95            tags.push(Property { tag, args });
96
97            if input.peek(Token![,]) {
98                // consume `,` in multiple tags
99                let _: Token![,] = input.parse()?;
100            }
101            if input.peek(Token![:]) {
102                let _: Token![:] = input.parse()?;
103                // `:` isn't in args, thus parse desc
104                let s: LitStr = input.parse()?;
105                desc = Some(s.value().into());
106                break;
107            }
108            if input.peek(Token![;]) {
109                // new grouped SPs
110                break;
111            }
112        }
113        Ok(PropertiesAndReason { tags: tags.into(), desc })
114    }
115}
116
117impl PropertiesAndReason {
118    /// Generate
119    ///
120    /// ```text
121    /// /// Grouped desc
122    /// /// * SP1: desc
123    /// /// * SP2: desc
124    /// ```
125    pub fn gen_doc(&self) -> TokenStream {
126        let mut ts = TokenStream::default();
127
128        if doc_option().heading_safety_title && self.need_gen_doc() {
129            ts.extend(quote! { #[doc = "# Safety\n\n"] });
130        }
131
132        if let Some(desc) = self.desc.as_deref() {
133            ts.extend(quote! { #[doc = #desc] });
134        }
135
136        let heading_tag = doc_option().heading_tag;
137
138        for tag in &self.tags {
139            let name = tag.tag.name();
140            let tokens = match (heading_tag, tag.gen_doc()) {
141                (true, None) => quote! { #[doc = concat!("* ", #name)] },
142                (true, Some(desc)) => quote! { #[doc = concat!("* ", #name, ": ", #desc)] },
143                (false, None) => quote! {},
144                (false, Some(desc)) => quote! { #[doc = concat!("* ", #desc)] },
145            };
146            ts.extend(tokens);
147        }
148        ts
149    }
150
151    fn gen_sp_in_any_doc(&self) -> String {
152        let mut doc = String::new();
153        let heading_tag = doc_option().heading_tag;
154
155        for tag in &self.tags {
156            let name = tag.tag.name();
157            let item = match (heading_tag, tag.gen_doc()) {
158                (true, None) => format!("    * {name}"),
159                (true, Some(desc)) => format!("    * {name}: {desc}"),
160                (false, None) => String::new(),
161                (false, Some(desc)) => format!("    * {desc}"),
162            };
163            doc.push_str(&item);
164            doc.push('\n');
165        }
166        doc.pop();
167        doc
168    }
169
170    pub fn need_gen_doc(&self) -> bool {
171        self.desc.is_some() || !self.tags.is_empty()
172    }
173}
174
175#[derive(Debug)]
176pub struct Property {
177    /// `SP` or `type.SP`. The type of single `SP` is unkown until queried from definition.
178    pub tag: TagNameType,
179    /// Args in `SP(args)` such as `arg1, arg2`.
180    pub args: Box<[Expr]>,
181}
182
183impl Property {
184    /// Generate `#[doc]` for this property from its desc string interpolation.
185    /// None means SP is not defined with desc, thus nothing to generate.
186    pub fn gen_doc(&self) -> Option<String> {
187        let name = self.tag.name();
188
189        if name == ANY {
190            if self.args.is_empty() {
191                return None;
192            }
193            let mut doc =
194                "Only one of the following properties requires being satisfied:\n".to_owned();
195            // validate SPs in `any(SP1, SP2, ...)` exist
196            for prop in utils::parse_args_in_any_tag(&self.args) {
197                doc.push_str(&prop.gen_sp_in_any_doc());
198            }
199            return Some(doc);
200        }
201
202        let defined_tag = get_tag(name);
203        // NOTE: this tolerates missing args, but position matters.
204        let args_len = self.args.len().min(defined_tag.args.len());
205
206        // map defined arg names to user inputs
207        let defined_args = defined_tag.args[..args_len].iter().map(|s| &**s);
208        let input_args = self.args[..args_len].iter().map(utils::expr_to_string);
209        let mut map_defined_arg_input_arg: IndexMap<_, _> = defined_args.zip(input_args).collect();
210        // if input arg is missing, defined arg will be an empty string
211        for defined_arg in &defined_tag.args {
212            if !map_defined_arg_input_arg.contains_key(&**defined_arg) {
213                map_defined_arg_input_arg.insert(defined_arg, String::new());
214            }
215        }
216
217        defined_tag.desc.as_deref().map(|desc| utils::template(desc, &map_defined_arg_input_arg))
218    }
219
220    /// SPs in `any` tag. None means the tag is not `any` or empty args.
221    pub fn args_in_any_tag(&self) -> Option<Vec<PropertiesAndReason>> {
222        (self.tag.name() == ANY && !self.args.is_empty())
223            .then(|| utils::parse_args_in_any_tag(&self.args))
224    }
225}
226
227/// Typed SP: `type.SP`
228#[derive(Debug)]
229pub struct TagNameType {
230    /// Default tag type is the one in single defined_types.
231    //
232    /// Deserialization will fill the default tag type as Precond.
233    typ: Option<TagType>,
234    /// Single ident string.
235    name: Str,
236}
237
238impl Parse for TagNameType {
239    fn parse(input: ParseStream) -> Result<Self> {
240        let ident: Ident = input.parse()?;
241        let first = ident.to_string();
242        Ok(if input.peek(Token![.]) {
243            let _: Token![.] = input.parse()?;
244            let second: Ident = input.parse()?;
245            let name = second.to_string().into();
246            TagNameType { name, typ: Some(TagType::new(&first)) }
247        } else {
248            TagNameType { name: first.into(), typ: None }
249        })
250    }
251}
252
253impl TagNameType {
254    pub fn name(&self) -> &str {
255        &self.name
256    }
257
258    // FIXME: no pinned default tag, because we want default tag to be
259    // the one in single defined_types. Deserialization will fill the
260    // default tag type as Precond.
261    pub fn typ(&self) -> Option<TagType> {
262        self.typ
263    }
264
265    pub fn name_type(&self) -> (&str, Option<TagType>) {
266        (&self.name, self.typ)
267    }
268
269    /// Check if the tag in macro is wrongly specified.
270    pub fn check_type(&self) {
271        let (name, typ) = self.name_type();
272        if name == ANY {
273            // FIXME: check SP args here
274            return;
275        }
276        let defined_types = &get_tag(name).types;
277        if let Some(typ) = typ {
278            assert!(
279                defined_types.contains(&typ),
280                "For tag {name:?}, defined_types is {defined_types:?}, \
281                 while user's {typ:?} doesn't exist."
282            );
283        } else {
284            assert_eq!(
285                defined_types.len(),
286                1,
287                "For tag {name:?} without explicit type, \
288                 the default type is the single defined type.\n\
289                 But defined_types has multiple types: {defined_types:?}.\n\
290                 So choose a type to be `type.{name}`."
291            );
292        }
293    }
294}