overflow 0.1.0

Proc macros for changing the overflow behavior of math expressions
Documentation
use quote::quote;
use syn::{spanned::Spanned, BinOp, Expr, ExprBinary, ExprUnary, Ident, UnOp};

pub fn transform_expr(mut expr: Expr) -> proc_macro2::TokenStream {
    match expr {
        Expr::Unary(unary) => transform_unary(unary),
        Expr::Binary(binary) => transform_binary(binary),
        Expr::MethodCall(ref mut mc) if mc.method == "pow" => {
            mc.method = syn::Ident::new("wrapping_pow", mc.method.span());
            quote! { #mc }
        }
        Expr::MethodCall(ref mut mc) if mc.method == "abs" => {
            mc.method = syn::Ident::new("wrapping_abs", mc.method.span());
            quote! { #mc }
        }
        Expr::Paren(p) => {
            let expr = transform_expr(*p.expr);
            quote! {
                (#expr)
            }
        }
        _ => quote! { #expr },
    }
}

fn transform_unary(unary: ExprUnary) -> proc_macro2::TokenStream {
    let expr = transform_expr(*unary.expr);
    let op = unary.op;
    match op {
        UnOp::Neg(_) => {
            quote! {
                #expr.wrapping_neg()
            }
        }
        _ => quote! { #expr },
    }
}

fn transform_binary(binary: ExprBinary) -> proc_macro2::TokenStream {
    let left = transform_expr(*binary.left);
    let right = transform_expr(*binary.right);
    let op = binary.op;
    let method_name = match op {
        BinOp::Add(_) => Some("wrapping_add"),
        BinOp::Sub(_) => Some("wrapping_sub"),
        BinOp::Mul(_) => Some("wrapping_mul"),
        BinOp::Div(_) => Some("wrapping_div"),
        BinOp::Rem(_) => Some("wrapping_rem"),
        BinOp::Shl(_) => Some("wrapping_shl"),
        BinOp::Shr(_) => Some("wrapping_shr"),
        _ => None,
    };

    method_name
        .map(|method_name| {
            let method_name = Ident::new(method_name, op.span());
            quote! {
                #left.#method_name(#right)
            }
        })
        .unwrap_or_else(|| {
            quote! { #left #op #right }
        })
}