safety_parser/safety/
mod.rs1use 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 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 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 let _: Token![,] = input.parse()?;
100 }
101 if input.peek(Token![:]) {
102 let _: Token![:] = input.parse()?;
103 let s: LitStr = input.parse()?;
105 desc = Some(s.value().into());
106 break;
107 }
108 if input.peek(Token![;]) {
109 break;
111 }
112 }
113 Ok(PropertiesAndReason { tags: tags.into(), desc })
114 }
115}
116
117impl PropertiesAndReason {
118 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 pub tag: TagNameType,
179 pub args: Box<[Expr]>,
181}
182
183impl Property {
184 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 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 let args_len = self.args.len().min(defined_tag.args.len());
205
206 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 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 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#[derive(Debug)]
229pub struct TagNameType {
230 typ: Option<TagType>,
234 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 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 pub fn check_type(&self) {
271 let (name, typ) = self.name_type();
272 if name == ANY {
273 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}