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