use quote::quote; use syn::visit_mut::{self, VisitMut}; use syn::{parse_quote, Attribute, Expr, ExprMatch, ItemFn, Local}; use crate::parse::Input; pub fn check(input: &mut ItemFn) { Checker.visit_item_fn_mut(input); } struct Checker; impl VisitMut for Checker { fn visit_expr_mut(&mut self, expr: &mut Expr) { visit_mut::visit_expr_mut(self, expr); let expr_match = match expr { Expr::Match(expr) => expr, _ => return, }; if !take_sorted_attr(&mut expr_match.attrs) { return; } let input = expr_match.clone(); check_and_insert_error(input, expr); } fn visit_local_mut(&mut self, local: &mut Local) { visit_mut::visit_local_mut(self, local); let init = match &local.init { Some((_, init)) => init, None => return, }; let expr_match = match init.as_ref() { Expr::Match(expr) => expr, _ => return, }; if !take_sorted_attr(&mut local.attrs) { return; } let input = expr_match.clone(); let expr = local.init.as_mut().unwrap().1.as_mut(); check_and_insert_error(input, expr); } } fn take_sorted_attr(attrs: &mut Vec) -> bool { for i in 0..attrs.len() { let path = &attrs[i].path; let path = quote!(#path).to_string(); if path == "sorted" || path == "remain :: sorted" { attrs.remove(i); return true; } } false } fn check_and_insert_error(input: ExprMatch, out: &mut Expr) { let mut input = Input::Match(input); *out = match crate::check::sorted(&mut input) { Ok(_) => parse_quote!(#input), Err(err) => { let err = err.to_compile_error(); parse_quote!({ #err #input }) } }; }