safety_parser/safety/
mod.rs1use crate::{
2 Str,
3 configuration::{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::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 let args = attr.parse_args()?;
38 Ok(SafetyAttr { attr, args })
39 }
40}
41
42pub 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 sp = if input.peek(Paren) {
83 let content;
84 parenthesized!(content in input);
85 let args = Punctuated::<Expr, Token![,]>::parse_terminated(&content)?;
86 let args = args.into_iter().collect();
87 Property { tag, args }
88 } else {
89 Property { tag, args: Default::default() }
90 };
91 tags.push(sp);
92
93 if input.peek(Token![,]) {
94 let _: Token![,] = input.parse()?;
96 }
97 if input.peek(Token![:]) {
98 let _: Token![:] = input.parse()?;
99 let s: LitStr = input.parse()?;
101 desc = Some(s.value().into());
102 break;
103 }
104 if input.peek(Token![;]) {
105 break;
107 }
108 }
109 Ok(PropertiesAndReason { tags: tags.into(), desc })
110 }
111}
112
113impl PropertiesAndReason {
114 pub fn gen_doc(&self) -> TokenStream {
122 let mut ts = TokenStream::default();
123
124 if doc_option().heading_safety_title && self.need_gen_doc() {
125 ts.extend(quote! { #[doc = "# Safety\n\n"] });
126 }
127
128 if let Some(desc) = self.desc.as_deref() {
129 ts.extend(quote! { #[doc = #desc] });
130 }
131
132 let heading_tag = doc_option().heading_tag;
133
134 for tag in &self.tags {
135 let name = tag.tag.name();
136 let tokens = match (heading_tag, tag.gen_doc()) {
137 (true, None) => quote! { #[doc = concat!("* ", #name)] },
138 (true, Some(desc)) => quote! { #[doc = concat!("* ", #name, ": ", #desc)] },
139 (false, None) => quote! {},
140 (false, Some(desc)) => quote! { #[doc = concat!("* ", #desc)] },
141 };
142 ts.extend(tokens);
143 }
144 ts
145 }
146
147 pub fn need_gen_doc(&self) -> bool {
148 self.desc.is_some() || !self.tags.is_empty()
149 }
150}
151
152#[derive(Debug)]
153pub struct Property {
154 pub tag: TagNameType,
156 pub args: Box<[Expr]>,
158}
159
160impl Property {
161 pub fn gen_doc(&self) -> Option<String> {
164 let defined_tag = get_tag(self.tag.name());
165 let args_len = self.args.len().min(defined_tag.args.len());
167
168 let defined_args = defined_tag.args[..args_len].iter().map(|s| &**s);
170 let input_args = self.args[..args_len].iter().map(utils::expr_to_string);
171 let mut map_defined_arg_input_arg: IndexMap<_, _> = defined_args.zip(input_args).collect();
172 for defined_arg in &defined_tag.args {
174 if !map_defined_arg_input_arg.contains_key(&**defined_arg) {
175 map_defined_arg_input_arg.insert(defined_arg, String::new());
176 }
177 }
178
179 defined_tag.desc.as_deref().map(|desc| utils::template(desc, &map_defined_arg_input_arg))
180 }
181}
182
183#[derive(Debug)]
185pub struct TagNameType {
186 typ: Option<TagType>,
190 name: Str,
192}
193
194impl Parse for TagNameType {
195 fn parse(input: ParseStream) -> Result<Self> {
196 let ident: Ident = input.parse()?;
197 let first = ident.to_string();
198 Ok(if input.peek(Token![.]) {
199 let _: Token![.] = input.parse()?;
200 let second: Ident = input.parse()?;
201 let name = second.to_string().into();
202 TagNameType { name, typ: Some(TagType::new(&first)) }
203 } else {
204 TagNameType { name: first.into(), typ: None }
205 })
206 }
207}
208
209impl TagNameType {
210 pub fn name(&self) -> &str {
211 &self.name
212 }
213
214 pub fn typ(&self) -> Option<TagType> {
218 self.typ
219 }
220
221 pub fn name_type(&self) -> (&str, Option<TagType>) {
222 (&self.name, self.typ)
223 }
224
225 pub fn check_type(&self) {
227 let (name, typ) = self.name_type();
228 let defined_types = &get_tag(name).types;
229 if let Some(typ) = typ {
230 assert!(
231 defined_types.contains(&typ),
232 "For tag {name:?}, defined_types is {defined_types:?}, \
233 while user's {typ:?} doesn't exist."
234 );
235 } else {
236 assert_eq!(
237 defined_types.len(),
238 1,
239 "For tag {name:?} without explicit type, \
240 the default type is the single defined type.\n\
241 But defined_types has multiple types: {defined_types:?}.\n\
242 So choose a type to be `type.{name}`."
243 );
244 }
245 }
246}