1 use quote::quote;
2 use syn::visit_mut::{self, VisitMut};
3 use syn::{parse_quote, Attribute, Expr, ExprMatch, ItemFn, Local};
4 
5 use crate::parse::Input;
6 
check(input: &mut ItemFn)7 pub fn check(input: &mut ItemFn) {
8     Checker.visit_item_fn_mut(input);
9 }
10 
11 struct Checker;
12 
13 impl VisitMut for Checker {
visit_expr_mut(&mut self, expr: &mut Expr)14     fn visit_expr_mut(&mut self, expr: &mut Expr) {
15         visit_mut::visit_expr_mut(self, expr);
16 
17         let expr_match = match expr {
18             Expr::Match(expr) => expr,
19             _ => return,
20         };
21 
22         if !take_sorted_attr(&mut expr_match.attrs) {
23             return;
24         }
25 
26         let input = expr_match.clone();
27         check_and_insert_error(input, expr);
28     }
29 
visit_local_mut(&mut self, local: &mut Local)30     fn visit_local_mut(&mut self, local: &mut Local) {
31         visit_mut::visit_local_mut(self, local);
32 
33         let init = match &local.init {
34             Some(init) => init,
35             None => return,
36         };
37 
38         let expr_match = match init.expr.as_ref() {
39             Expr::Match(expr) => expr,
40             _ => return,
41         };
42 
43         if !take_sorted_attr(&mut local.attrs) {
44             return;
45         }
46 
47         let input = expr_match.clone();
48         let expr = local.init.as_mut().unwrap().expr.as_mut();
49         check_and_insert_error(input, expr);
50     }
51 }
52 
take_sorted_attr(attrs: &mut Vec<Attribute>) -> bool53 fn take_sorted_attr(attrs: &mut Vec<Attribute>) -> bool {
54     for i in 0..attrs.len() {
55         let path = &attrs[i].path();
56         let path = quote!(#path).to_string();
57         if path == "sorted" || path == "remain :: sorted" {
58             attrs.remove(i);
59             return true;
60         }
61     }
62 
63     false
64 }
65 
check_and_insert_error(input: ExprMatch, out: &mut Expr)66 fn check_and_insert_error(input: ExprMatch, out: &mut Expr) {
67     let mut input = Input::Match(input);
68 
69     *out = match crate::check::sorted(&mut input) {
70         Ok(()) => parse_quote!(#input),
71         Err(err) => {
72             let err = err.to_compile_error();
73             parse_quote!({
74                 #err
75                 #input
76             })
77         }
78     };
79 }
80