1use syn::{
2 bracketed,
3 parse::{self, ParseStream},
4 punctuated::Punctuated,
5 spanned::Spanned,
6 Abi, AttrStyle, Attribute, Expr, FnArg, ForeignItemFn, Ident, ItemFn, Pat, PatType, Path,
7 PathArguments, ReturnType, Token, Type, Visibility,
8};
9
10use crate::{
11 ast::{Access, Local, LocalResources, SharedResources, TaskLocal},
12 Map,
13};
14
15pub fn abi_is_rust(abi: &Abi) -> bool {
16 match &abi.name {
17 None => true,
18 Some(s) => s.value() == "Rust",
19 }
20}
21
22pub fn attr_eq(attr: &Attribute, name: &str) -> bool {
23 attr.style == AttrStyle::Outer && attr.path.segments.len() == 1 && {
24 let segment = attr.path.segments.first().unwrap();
25 segment.arguments == PathArguments::None && *segment.ident.to_string() == *name
26 }
27}
28
29pub fn check_fn_signature(item: &ItemFn) -> bool {
39 item.vis == Visibility::Inherited
40 && item.sig.constness.is_none()
41 && item.sig.asyncness.is_none()
42 && item.sig.abi.is_none()
43 && item.sig.unsafety.is_none()
44 && item.sig.generics.params.is_empty()
45 && item.sig.generics.where_clause.is_none()
46 && item.sig.variadic.is_none()
47}
48
49#[allow(dead_code)]
50pub fn check_foreign_fn_signature(item: &ForeignItemFn) -> bool {
51 item.vis == Visibility::Inherited
52 && item.sig.constness.is_none()
53 && item.sig.asyncness.is_none()
54 && item.sig.abi.is_none()
55 && item.sig.unsafety.is_none()
56 && item.sig.generics.params.is_empty()
57 && item.sig.generics.where_clause.is_none()
58 && item.sig.variadic.is_none()
59}
60
61pub struct FilterAttrs {
62 pub cfgs: Vec<Attribute>,
63 pub docs: Vec<Attribute>,
64 pub attrs: Vec<Attribute>,
65}
66
67pub fn filter_attributes(input_attrs: Vec<Attribute>) -> FilterAttrs {
68 let mut cfgs = vec![];
69 let mut docs = vec![];
70 let mut attrs = vec![];
71
72 for attr in input_attrs {
73 if attr_eq(&attr, "cfg") {
74 cfgs.push(attr);
75 } else if attr_eq(&attr, "doc") {
76 docs.push(attr);
77 } else {
78 attrs.push(attr);
79 }
80 }
81
82 FilterAttrs { cfgs, docs, attrs }
83}
84
85pub fn extract_lock_free(attrs: &mut Vec<Attribute>) -> parse::Result<bool> {
86 if let Some(pos) = attrs.iter().position(|attr| attr_eq(attr, "lock_free")) {
87 attrs.remove(pos);
88 Ok(true)
89 } else {
90 Ok(false)
91 }
92}
93
94pub fn parse_shared_resources(content: ParseStream<'_>) -> parse::Result<SharedResources> {
95 let inner;
96 bracketed!(inner in content);
97
98 let mut resources = Map::new();
99 for e in inner.call(Punctuated::<Expr, Token![,]>::parse_terminated)? {
100 let err = Err(parse::Error::new(
101 e.span(),
102 "identifier appears more than once in list",
103 ));
104 let (access, path) = match e {
105 Expr::Path(e) => (Access::Exclusive, e.path),
106
107 Expr::Reference(ref r) if r.mutability.is_none() => match &*r.expr {
108 Expr::Path(e) => (Access::Shared, e.path.clone()),
109
110 _ => return err,
111 },
112
113 _ => return err,
114 };
115
116 let ident = extract_resource_name_ident(path)?;
117
118 if resources.contains_key(&ident) {
119 return Err(parse::Error::new(
120 ident.span(),
121 "resource appears more than once in list",
122 ));
123 }
124
125 resources.insert(ident, access);
126 }
127
128 Ok(resources)
129}
130
131fn extract_resource_name_ident(path: Path) -> parse::Result<Ident> {
132 if path.leading_colon.is_some()
133 || path.segments.len() != 1
134 || path.segments[0].arguments != PathArguments::None
135 {
136 Err(parse::Error::new(
137 path.span(),
138 "resource must be an identifier, not a path",
139 ))
140 } else {
141 Ok(path.segments[0].ident.clone())
142 }
143}
144
145pub fn parse_local_resources(content: ParseStream<'_>) -> parse::Result<LocalResources> {
146 let inner;
147 bracketed!(inner in content);
148
149 let mut resources = Map::new();
150
151 for e in inner.call(Punctuated::<Expr, Token![,]>::parse_terminated)? {
152 let err = Err(parse::Error::new(
153 e.span(),
154 "identifier appears more than once in list",
155 ));
156
157 let (name, local) = match e {
158 Expr::Path(path) => {
160 if !path.attrs.is_empty() {
161 return Err(parse::Error::new(
162 path.span(),
163 "attributes are not supported here",
164 ));
165 }
166
167 let ident = extract_resource_name_ident(path.path)?;
168 (ident, TaskLocal::External)
171 }
172
173 Expr::Assign(e) => {
175 let (name, ty, cfgs, attrs) = match *e.left {
176 Expr::Type(t) => {
177 let (name, cfgs, attrs) = match *t.expr {
179 Expr::Path(path) => {
180 let name = extract_resource_name_ident(path.path)?;
181 let FilterAttrs { cfgs, attrs, .. } = filter_attributes(path.attrs);
182
183 (name, cfgs, attrs)
184 }
185 _ => return err,
186 };
187
188 let ty = t.ty;
189
190 match &*ty {
192 Type::Array(_) => {}
193 Type::Path(_) => {}
194 Type::Ptr(_) => {}
195 Type::Tuple(_) => {}
196 _ => return Err(parse::Error::new(
197 ty.span(),
198 "unsupported type, must be an array, tuple, pointer or type path",
199 )),
200 };
201
202 (name, ty, cfgs, attrs)
203 }
204 e => return Err(parse::Error::new(e.span(), "malformed, expected a type")),
205 };
206
207 let expr = e.right; (
210 name,
211 TaskLocal::Declared(Local {
212 attrs,
213 cfgs,
214 ty,
215 expr,
216 }),
217 )
218 }
219
220 expr => {
221 return Err(parse::Error::new(
222 expr.span(),
223 "malformed, expected 'IDENT: TYPE = EXPR'",
224 ))
225 }
226 };
227
228 resources.insert(name, local);
229 }
230
231 Ok(resources)
232}
233
234type ParseInputResult = Option<(Box<Pat>, Result<Vec<PatType>, FnArg>)>;
235
236pub fn parse_inputs(inputs: Punctuated<FnArg, Token![,]>, name: &str) -> ParseInputResult {
237 let mut inputs = inputs.into_iter();
238
239 match inputs.next() {
240 Some(FnArg::Typed(first)) => {
241 if type_is_path(&first.ty, &[name, "Context"]) {
242 let rest = inputs
243 .map(|arg| match arg {
244 FnArg::Typed(arg) => Ok(arg),
245 _ => Err(arg),
246 })
247 .collect::<Result<Vec<_>, _>>();
248
249 Some((first.pat, rest))
250 } else {
251 None
252 }
253 }
254
255 _ => None,
256 }
257}
258
259pub fn type_is_bottom(ty: &ReturnType) -> bool {
260 if let ReturnType::Type(_, ty) = ty {
261 matches!(**ty, Type::Never(_))
262 } else {
263 false
264 }
265}
266
267fn extract_init_resource_name_ident(ty: Type) -> Result<Ident, ()> {
268 match ty {
269 Type::Path(path) => {
270 let path = path.path;
271
272 if path.leading_colon.is_some()
273 || path.segments.len() != 1
274 || path.segments[0].arguments != PathArguments::None
275 {
276 Err(())
277 } else {
278 Ok(path.segments[0].ident.clone())
279 }
280 }
281 _ => Err(()),
282 }
283}
284
285pub fn type_is_init_return(ty: &ReturnType, name: &str) -> Result<(Ident, Ident), ()> {
287 match ty {
288 ReturnType::Default => Err(()),
289
290 ReturnType::Type(_, ty) => match &**ty {
291 Type::Tuple(t) => {
292 if t.elems.len() == 3 && type_is_path(&t.elems[2], &[name, "Monotonics"]) {
298 return Ok((
299 extract_init_resource_name_ident(t.elems[0].clone())?,
300 extract_init_resource_name_ident(t.elems[1].clone())?,
301 ));
302 }
303
304 Err(())
305 }
306
307 _ => Err(()),
308 },
309 }
310}
311
312pub fn type_is_path(ty: &Type, segments: &[&str]) -> bool {
313 match ty {
314 Type::Path(tpath) if tpath.qself.is_none() => {
315 tpath.path.segments.len() == segments.len()
316 && tpath
317 .path
318 .segments
319 .iter()
320 .zip(segments)
321 .all(|(lhs, rhs)| lhs.ident == **rhs)
322 }
323
324 _ => false,
325 }
326}
327
328pub fn type_is_unit(ty: &ReturnType) -> bool {
329 if let ReturnType::Type(_, ty) = ty {
330 if let Type::Tuple(ref tuple) = **ty {
331 tuple.elems.is_empty()
332 } else {
333 false
334 }
335 } else {
336 true
337 }
338}