1use proc_macro2::Span;
2use syn::{
3 Attribute, Expr, Ident, Meta, Pat, Token,
4 parse::{Parse, ParseStream, Result},
5 parse_quote,
6 punctuated::Punctuated,
7 spanned::Spanned,
8};
9
10use crate::{Capture, Condition, PostCondition, Spec};
11
12#[cfg(test)]
13mod tests;
14
15impl Parse for Spec {
16 fn parse(input: ParseStream) -> Result<Self> {
17 let args = Punctuated::<SpecArg, Token![,]>::parse_terminated(input)?;
18
19 let mut last_arg_order: Option<ArgOrder> = None;
20 let mut requires: Vec<Condition> = vec![];
21 let mut maintains: Vec<Condition> = vec![];
22 let mut captures: Vec<Capture> = vec![];
23 let mut binds_pattern: Option<Pat> = None;
24 let mut ensures: Vec<PostCondition> = vec![];
25
26 for arg in args {
27 let current_arg_order = arg.get_order();
28 if let Some(last_order) = last_arg_order {
29 if current_arg_order < last_order {
30 return Err(syn::Error::new(
31 arg.get_keyword_span(),
32 "parameters are out of order: their order must be `requires`, `maintains`, `captures`, `binds`, `ensures`",
33 ));
34 }
35 }
36 last_arg_order = Some(current_arg_order);
37
38 match arg {
39 SpecArg::Requires { cfg, expr, .. } => {
40 if let Expr::Array(conditions) = expr {
41 requires.extend(conditions.elems.into_iter().map(|expr| Condition {
42 expr,
43 cfg: cfg.clone(),
44 }));
45 } else {
46 requires.push(Condition { expr, cfg });
47 }
48 }
49 SpecArg::Maintains { cfg, expr, .. } => {
50 if let Expr::Array(conditions) = expr {
51 maintains.extend(conditions.elems.into_iter().map(|expr| Condition {
52 expr,
53 cfg: cfg.clone(),
54 }));
55 } else {
56 maintains.push(Condition { expr, cfg });
57 }
58 }
59 SpecArg::Captures { keyword, expr } => {
60 if !captures.is_empty() {
61 return Err(syn::Error::new(
62 keyword.span(),
63 "at most one `captures` parameter is allowed; to capture multiple values, use a list: `captures: [expr1, expr2, ...]`",
64 ));
65 }
66 if let Expr::Array(array) = expr {
67 captures.extend(interpret_array_as_captures(array)?);
68 } else {
69 captures.push(interpret_expr_as_capture(expr)?);
70 }
71 }
72 SpecArg::Binds { keyword, pattern } => {
73 if binds_pattern.is_some() {
74 return Err(syn::Error::new(
75 keyword.span(),
76 "multiple `binds` parameters are not allowed",
77 ));
78 }
79 binds_pattern = Some(pattern);
80 }
81 SpecArg::Ensures { cfg, expr, .. } => {
82 let default_pattern = binds_pattern.clone().unwrap_or(parse_quote! { output });
83
84 if let Expr::Array(conditions) = expr {
85 for expr in conditions.elems {
86 ensures.push(PostCondition {
87 closure: interpret_expr_as_postcondition(
88 expr,
89 default_pattern.clone(),
90 )?,
91 cfg: cfg.clone(),
92 });
93 }
94 } else {
95 ensures.push(PostCondition {
96 closure: interpret_expr_as_postcondition(expr, default_pattern)?,
97 cfg,
98 });
99 }
100 }
101 }
102 }
103
104 Ok(Spec {
105 requires,
106 maintains,
107 captures,
108 ensures,
109 })
110 }
111}
112
113#[derive(PartialEq, PartialOrd, Clone, Copy, Debug)]
114enum ArgOrder {
115 Requires,
116 Maintains,
117 Captures,
118 Binds,
119 Ensures,
120}
121
122enum SpecArg {
124 Requires {
125 keyword: kw::requires,
126 cfg: Option<Meta>,
127 expr: Expr,
128 },
129 Ensures {
130 keyword: kw::ensures,
131 cfg: Option<Meta>,
132 expr: Expr,
133 },
134 Maintains {
135 keyword: kw::maintains,
136 cfg: Option<Meta>,
137 expr: Expr,
138 },
139 Captures {
140 keyword: kw::captures,
141 expr: Expr,
142 },
143 Binds {
144 keyword: kw::binds,
145 pattern: Pat,
146 },
147}
148
149impl SpecArg {
150 fn get_order(&self) -> ArgOrder {
151 match self {
152 SpecArg::Requires { .. } => ArgOrder::Requires,
153 SpecArg::Maintains { .. } => ArgOrder::Maintains,
154 SpecArg::Captures { .. } => ArgOrder::Captures,
155 SpecArg::Binds { .. } => ArgOrder::Binds,
156 SpecArg::Ensures { .. } => ArgOrder::Ensures,
157 }
158 }
159
160 fn get_keyword_span(&self) -> Span {
161 match self {
162 SpecArg::Requires { keyword, .. } => keyword.span,
163 SpecArg::Ensures { keyword, .. } => keyword.span,
164 SpecArg::Maintains { keyword, .. } => keyword.span,
165 SpecArg::Captures { keyword, .. } => keyword.span,
166 SpecArg::Binds { keyword, .. } => keyword.span,
167 }
168 }
169}
170
171impl Parse for SpecArg {
172 fn parse(input: ParseStream) -> Result<Self> {
173 let attrs = input.call(Attribute::parse_outer)?;
174 let cfg = parse_cfg_attribute(&attrs)?;
175
176 let lookahead = input.lookahead1();
177 if lookahead.peek(kw::captures) {
178 if cfg.is_some() {
179 return Err(syn::Error::new(
180 attrs[0].span(),
181 "`cfg` attribute is not supported on `captures`",
182 ));
183 }
184
185 let keyword = input.parse::<kw::captures>()?;
187 input.parse::<Token![:]>()?;
188 Ok(SpecArg::Captures {
189 keyword,
190 expr: input.parse()?,
191 })
192 } else if lookahead.peek(kw::binds) {
193 if cfg.is_some() {
194 return Err(syn::Error::new(
195 attrs[0].span(),
196 "`cfg` attribute is not supported on `binds`",
197 ));
198 }
199
200 let keyword = input.parse::<kw::binds>()?;
202 input.parse::<Token![:]>()?;
203 Ok(SpecArg::Binds {
204 keyword,
205 pattern: Pat::parse_single(input)?,
206 })
207 } else if lookahead.peek(kw::requires) {
208 let keyword = input.parse::<kw::requires>()?;
210 input.parse::<Token![:]>()?;
211 Ok(SpecArg::Requires {
212 keyword,
213 cfg,
214 expr: input.parse()?,
215 })
216 } else if lookahead.peek(kw::maintains) {
217 let keyword = input.parse::<kw::maintains>()?;
219 input.parse::<Token![:]>()?;
220 Ok(SpecArg::Maintains {
221 keyword,
222 cfg,
223 expr: input.parse()?,
224 })
225 } else if lookahead.peek(kw::ensures) {
226 let keyword = input.parse::<kw::ensures>()?;
228 input.parse::<Token![:]>()?;
229 Ok(SpecArg::Ensures {
230 keyword,
231 cfg,
232 expr: input.parse()?,
233 })
234 } else {
235 Err(lookahead.error())
236 }
237 }
238}
239
240fn interpret_array_as_captures(array: syn::ExprArray) -> Result<Vec<Capture>> {
242 let mut bindings = Vec::new();
243
244 for elem in array.elems {
245 bindings.push(interpret_expr_as_capture(elem)?);
248 }
249
250 Ok(bindings)
251}
252
253fn interpret_expr_as_capture(expr: Expr) -> Result<Capture> {
255 match expr {
256 Expr::Path(ref path)
258 if path.path.segments.len() == 1
259 && path.path.leading_colon.is_none()
260 && path.attrs.is_empty()
261 && path.qself.is_none() =>
262 {
263 let ident = &path.path.segments[0].ident;
264 let alias = Ident::new(&format!("old_{}", ident), ident.span());
265 Ok(Capture { expr, alias })
266 }
267 Expr::Cast(cast) => {
269 if let syn::Type::Path(ref type_path) = *cast.ty {
271 if type_path.path.segments.len() == 1
272 && type_path.path.leading_colon.is_none()
273 && type_path.qself.is_none()
274 {
275 let alias = type_path.path.segments[0].ident.clone();
276 return Ok(Capture {
277 expr: *cast.expr,
278 alias,
279 });
280 }
281 }
282 Err(syn::Error::new_spanned(
283 cast,
284 "alias must be a simple identifier",
285 ))
286 }
287 _ => Err(syn::Error::new_spanned(
289 expr,
290 "complex expressions require an explicit alias using `as`",
291 )),
292 }
293}
294
295fn interpret_expr_as_postcondition(expr: Expr, default_binding: Pat) -> Result<syn::ExprClosure> {
296 match expr {
297 Expr::Closure(closure) => {
299 if closure.inputs.len() == 1 {
300 Ok(closure)
301 } else {
302 Err(syn::Error::new_spanned(
303 closure.or1_token,
304 format!(
305 "postcondition closure must have exactly one argument, found {}",
306 closure.inputs.len()
307 ),
308 ))
309 }
310 }
311 expr => Ok(syn::ExprClosure {
313 attrs: vec![],
314 lifetimes: None,
315 constness: None,
316 movability: None,
317 asyncness: None,
318 capture: None,
319 or1_token: Default::default(),
320 inputs: syn::punctuated::Punctuated::from_iter([default_binding]),
321 or2_token: Default::default(),
322 output: syn::ReturnType::Default,
323 body: Box::new(expr),
324 }),
325 }
326}
327
328fn parse_cfg_attribute(attrs: &[Attribute]) -> Result<Option<Meta>> {
329 let mut cfg_attrs: Vec<Meta> = vec![];
330
331 for attr in attrs {
332 if attr.path().is_ident("cfg") {
333 cfg_attrs.push(attr.parse_args()?);
334 } else {
335 return Err(syn::Error::new(
336 attr.span(),
337 "unsupported attribute; only `cfg` is allowed",
338 ));
339 }
340 }
341
342 if cfg_attrs.len() > 1 {
343 return Err(syn::Error::new(
344 cfg_attrs[1].span(),
345 "multiple `cfg` attributes are not supported",
346 ));
347 }
348
349 Ok(cfg_attrs.pop())
350}
351
352mod kw {
355 syn::custom_keyword!(requires);
356 syn::custom_keyword!(maintains);
357 syn::custom_keyword!(captures);
358 syn::custom_keyword!(binds);
359 syn::custom_keyword!(ensures);
360}