weak_true_proc_macro/
lib.rs

1use std::mem::replace;
2
3use proc_macro::TokenStream;
4use quote::ToTokens;
5use syn::{
6    parse_quote_spanned,
7    spanned::Spanned,
8    visit_mut::{
9        visit_expr_if_mut, visit_expr_match_mut, visit_expr_while_mut,
10        VisitMut,
11    },
12    BinOp, Error, Expr, ExprBinary, ExprIf, ExprMatch, ExprUnary, ExprWhile,
13    ImplItem, UnOp,
14};
15
16fn weak(expr: &mut Expr) {
17    let local = replace(expr, Expr::PLACEHOLDER);
18
19    *expr = parse_quote_spanned! { expr.span() =>
20        ::weak_true::WeakTrue::weak_true(&#local)
21    };
22}
23
24fn try_weak_true_cond(expr: &mut Expr) -> bool {
25    match expr {
26        Expr::Binary(ExprBinary {
27            op: BinOp::And(_) | BinOp::Or(_),
28            left,
29            right,
30            ..
31        }) => {
32            weak_true_cond(left);
33            weak_true_cond(right);
34            true
35        },
36        Expr::Unary(ExprUnary { op: UnOp::Not(_), expr, .. }) => {
37            weak_true_cond(expr);
38            true
39        },
40        _ => false,
41    }
42}
43
44fn weak_true_cond(expr: &mut Expr) {
45    if !try_weak_true_cond(expr) {
46        weak(expr);
47    }
48}
49
50struct Visitor;
51
52impl VisitMut for Visitor {
53    fn visit_expr_if_mut(&mut self, node: &mut ExprIf) {
54        weak_true_cond(&mut node.cond);
55        visit_expr_if_mut(self, node);
56    }
57    fn visit_expr_while_mut(&mut self, node: &mut ExprWhile) {
58        weak_true_cond(&mut node.cond);
59        visit_expr_while_mut(self, node);
60    }
61    fn visit_expr_match_mut(&mut self, node: &mut ExprMatch) {
62        for arm in &mut node.arms {
63            if let Some((_, guard)) = &mut arm.guard {
64                weak_true_cond(guard);
65            }
66        }
67        visit_expr_match_mut(self, node);
68    }
69}
70
71#[proc_macro_attribute]
72pub fn weak_true(attr: TokenStream, item: TokenStream) -> TokenStream {
73    if let Some(tt) = attr.into_iter().next() {
74        return Error::new(tt.span().into(), "invalid token")
75            .into_compile_error()
76            .into();
77    }
78
79    let mut item = match syn::parse::<ImplItem>(item) {
80        Ok(item) => item,
81        Err(e) => return e.into_compile_error().into(),
82    };
83
84    Visitor.visit_impl_item_mut(&mut item);
85
86    item.into_token_stream().into()
87}