1use crate::{
2 Str,
3 configuration::{ANY, Tag, TagType, doc_option, env::need_check, get_tag, get_tag_opt},
4};
5use indexmap::IndexMap;
6use proc_macro2::TokenStream;
7use quote::quote;
8use serde::{Deserialize, Serialize};
9use std::fmt;
10use syn::{
11 parse::{Parse, ParseStream},
12 punctuated::Punctuated,
13 token::{Brace, Paren},
14 *,
15};
16
17mod utils;
18
19#[cfg(test)]
20mod tests;
21
22#[derive(Debug)]
23pub struct SafetyAttr {
24 pub attr: Attribute,
25 pub args: SafetyAttrArgs,
26}
27
28impl Parse for SafetyAttr {
29 fn parse(input: ParseStream) -> Result<Self> {
30 let mut attrs = Attribute::parse_outer(input)?;
31
32 if attrs.len() != 1 {
33 return Err(syn::Error::new(
34 input.span(),
35 "Given input must be a single #[safety] attribute.",
36 ));
37 }
38 let attr = attrs.remove(0);
39 drop(attrs);
40
41 let args = attr.parse_args()?;
45 Ok(SafetyAttr { attr, args })
46 }
47}
48
49pub fn parse_attr_and_get_properties(attr: &str) -> Box<[PropertiesAndReason]> {
51 let Ok(attr) = parse_str::<SafetyAttr>(attr) else { return Box::new([]) };
52 attr.args.args.into_iter().collect()
53}
54
55#[derive(Debug)]
56pub struct SafetyAttrArgs {
57 pub args: Punctuated<PropertiesAndReason, Token![;]>,
58}
59
60impl Parse for SafetyAttrArgs {
61 fn parse(input: ParseStream) -> Result<Self> {
62 Ok(SafetyAttrArgs { args: Punctuated::parse_terminated(input)? })
63 }
64}
65
66impl SafetyAttrArgs {
67 pub fn property_reason(&self) -> impl Iterator<Item = (&Property, Option<&str>)> {
68 self.args.iter().flat_map(|arg| arg.tags.iter().map(|prop| (prop, arg.desc.as_deref())))
69 }
70}
71
72#[derive(Debug, Deserialize, Serialize)]
73pub struct PropertiesAndReason {
74 pub tags: Box<[Property]>,
75 pub desc: Option<Str>,
76}
77
78impl Parse for PropertiesAndReason {
79 fn parse(input: ParseStream) -> Result<Self> {
80 let mut tags = Vec::<Property>::new();
81 let mut desc = None;
82
83 while !input.cursor().eof() {
84 let tag: TagNameType = input.parse()?;
85 if need_check() {
86 tag.check_type();
87 }
88 let args = if input.peek(Paren) {
89 let content;
90 parenthesized!(content in input);
91 let args = Punctuated::<Expr, Token![,]>::parse_terminated(&content)?;
92 args.into_iter().collect()
93 } else if input.peek(Brace) {
94 let content;
95 braced!(content in input);
96 let args = Punctuated::<Expr, Token![,]>::parse_terminated(&content)?;
97 args.into_iter().collect()
98 } else {
99 Default::default()
100 };
101 tags.push(Property { tag, args });
102
103 if input.peek(Token![,]) {
104 let _: Token![,] = input.parse()?;
106 }
107 if input.peek(Token![:]) {
108 let _: Token![:] = input.parse()?;
109 let s: LitStr = input.parse()?;
111 desc = Some(s.value().into());
112 break;
113 }
114 if input.peek(Token![;]) {
115 break;
117 }
118 }
119 Ok(PropertiesAndReason { tags: tags.into(), desc })
120 }
121}
122
123impl PropertiesAndReason {
124 pub fn gen_doc(&self) -> TokenStream {
132 let mut ts = TokenStream::default();
133
134 if doc_option().heading_safety_title && self.need_gen_doc() {
135 ts.extend(quote! { #[doc = "# Safety\n\n"] });
136 }
137
138 if let Some(desc) = self.desc.as_deref() {
139 ts.extend(quote! { #[doc = #desc] });
140 }
141
142 let heading_tag = doc_option().heading_tag;
143
144 for tag in &self.tags {
145 let name = tag.tag.name();
146 let tokens = match (heading_tag, tag.gen_doc()) {
147 (true, None) => quote! { #[doc = concat!("* ", #name)] },
148 (true, Some(desc)) => quote! { #[doc = concat!("* ", #name, ": ", #desc)] },
149 (false, None) => quote! {},
150 (false, Some(desc)) => quote! { #[doc = concat!("* ", #desc)] },
151 };
152 ts.extend(tokens);
153 }
154 ts
155 }
156
157 pub fn gen_hover_doc(&self) -> Box<str> {
159 use std::fmt::Write;
160
161 let mut doc = String::with_capacity(512);
162
163 if let Some(desc) = &self.desc {
164 doc.push_str(desc);
165 doc.push_str("\n\n");
166 }
167
168 for tag in &self.tags {
169 let name = tag.tag.name();
170 if let Some(desc) = tag.gen_doc() {
171 _ = writeln!(&mut doc, "* {name}: {desc}");
172 } else {
173 _ = writeln!(&mut doc, "* {name}");
174 }
175 }
176
177 doc.into()
178 }
179
180 fn gen_sp_in_any_doc(&self) -> String {
181 let mut doc = String::new();
182 let heading_tag = doc_option().heading_tag;
183
184 for tag in &self.tags {
185 let name = tag.tag.name();
186 let item = match (heading_tag, tag.gen_doc()) {
187 (true, None) => format!(" * {name}"),
188 (true, Some(desc)) => format!(" * {name}: {desc}"),
189 (false, None) => String::new(),
190 (false, Some(desc)) => format!(" * {desc}"),
191 };
192 doc.push_str(&item);
193 doc.push('\n');
194 }
195 doc.pop();
196 doc
197 }
198
199 pub fn need_gen_doc(&self) -> bool {
200 self.desc.is_some() || !self.tags.is_empty()
201 }
202
203 pub fn new_single_sp(name: &str) -> Self {
205 PropertiesAndReason {
206 tags: Box::new([Property {
207 tag: TagNameType { typ: None, name: name.into() },
208 args: Box::default(),
209 }]),
210 desc: None,
211 }
212 }
213
214 pub fn parse_sp_str(s: &str) -> syn::Result<Self> {
218 parse_str(s)
219 }
220}
221
222#[derive(Clone, Deserialize, Serialize)]
223pub struct Property {
224 pub tag: TagNameType,
226 #[serde(deserialize_with = "utils::deserialize_str_to_expr")]
228 #[serde(serialize_with = "utils::serialize_expr_to_str")]
229 pub args: Box<[Expr]>,
230}
231
232impl fmt::Debug for Property {
233 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
234 let args = &*self.args;
235 let args = quote! { #(#args),* }.to_string();
236 f.debug_struct("Property").field("tag", &self.tag).field("args", &args).finish()
237 }
238}
239
240impl Property {
241 pub fn gen_doc(&self) -> Option<String> {
244 let name = self.tag.name();
245
246 if name == ANY {
247 if self.args.is_empty() {
248 return None;
249 }
250 let mut doc =
251 "Only one of the following properties requires being satisfied:\n".to_owned();
252 for prop in utils::parse_args_in_any_tag(&self.args) {
254 doc.push_str(&prop.gen_sp_in_any_doc());
255 }
256 return Some(doc);
257 }
258
259 let defined_tag = get_tag_opt(name)?;
260 let args_len = self.args.len().min(defined_tag.args.len());
262
263 let defined_args = defined_tag.args[..args_len].iter().map(|s| &**s);
265 let input_args = self.args[..args_len].iter().map(utils::expr_to_string);
266 let mut map_defined_arg_input_arg: IndexMap<_, _> = defined_args.zip(input_args).collect();
267 for defined_arg in &defined_tag.args {
269 if !map_defined_arg_input_arg.contains_key(&**defined_arg) {
270 map_defined_arg_input_arg.insert(defined_arg, String::new());
271 }
272 }
273
274 defined_tag.desc.as_deref().map(|desc| utils::template(desc, &map_defined_arg_input_arg))
275 }
276
277 pub fn args_in_any_tag(&self) -> Option<Vec<PropertiesAndReason>> {
279 (self.tag.name() == ANY && !self.args.is_empty())
280 .then(|| utils::parse_args_in_any_tag(&self.args))
281 }
282
283 pub fn args_as_string(&self) -> Box<[String]> {
284 self.args.iter().map(utils::expr_to_string).collect()
285 }
286}
287
288#[derive(Clone, Debug, Deserialize, Serialize)]
290pub struct TagNameType {
291 typ: Option<TagType>,
295 name: Str,
297}
298
299impl Parse for TagNameType {
300 fn parse(input: ParseStream) -> Result<Self> {
301 let ident: Ident = input.parse()?;
302 let first = ident.to_string();
303 Ok(if input.peek(Token![.]) {
304 let _: Token![.] = input.parse()?;
305 let second: Ident = input.parse()?;
306 let name = second.to_string().into();
307 TagNameType { name, typ: Some(TagType::new(&first)) }
308 } else {
309 TagNameType { name: first.into(), typ: None }
310 })
311 }
312}
313
314impl TagNameType {
315 pub fn name(&self) -> &str {
316 &self.name
317 }
318
319 pub fn typ(&self) -> Option<TagType> {
323 self.typ
324 }
325
326 pub fn name_type(&self) -> (&str, Option<TagType>) {
327 (&self.name, self.typ)
328 }
329
330 pub fn check_type(&self) {
332 let (name, typ) = self.name_type();
333 if name == ANY {
334 return;
336 }
337 let defined_types = &get_tag(name).types;
338 if let Some(typ) = typ {
339 assert!(
340 defined_types.contains(&typ),
341 "For tag {name:?}, defined_types is {defined_types:?}, \
342 while user's {typ:?} doesn't exist."
343 );
344 } else {
345 assert_eq!(
346 defined_types.len(),
347 1,
348 "For tag {name:?} without explicit type, \
349 the default type is the single defined type.\n\
350 But defined_types has multiple types: {defined_types:?}.\n\
351 So choose a type to be `type.{name}`."
352 );
353 }
354 }
355
356 pub fn get_spec(&self) -> Option<&'static Tag> {
358 get_tag_opt(&self.name)
359 }
360}