weak_true_proc_macro/
lib.rs1use 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}