1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#![crate_type = "dylib"]
#![feature(plugin_registrar, rustc_private)]
extern crate rustc_plugin;
extern crate syntax;
use rustc_plugin::Registry;
use syntax::ast::{BinOpKind, Expr, ExprKind, Ident, Mac, UnOp};
use syntax::codemap::Span;
use syntax::ext::base::{DummyResult, ExtCtxt, MacEager, MacResult};
use syntax::ext::build::AstBuilder;
use syntax::fold::{self, Folder};
use syntax::parse;
use syntax::parse::token::DelimToken;
use syntax::ptr::P;
use syntax::tokenstream::{Delimited, TokenStream, TokenTree};
struct WrappingFolder<'cx, 'a: 'cx> {
cx: &'cx ExtCtxt<'a>,
}
impl<'cx, 'a> Folder for WrappingFolder<'cx, 'a> {
fn fold_mac(&mut self, mac: Mac) -> Mac {
self.cx.span_err(mac.span, "cannot call nested macros in a `wrapping!` block");
mac
}
fn fold_expr(&mut self, expr: P<Expr>) -> P<Expr> {
expr.map(|expr| { match expr.node {
ExprKind::Unary(UnOp::Neg, inner) => {
let inner = self.fold_expr(inner);
let method = Ident::from_str("wrapping_neg");
self.cx.expr_method_call(expr.span, inner, method, vec![])
.and_then(|e| e)
}
ExprKind::Binary(op, left, right) => {
let left = self.fold_expr(left);
let right = self.fold_expr(right);
match wrapping_method(op.node) {
Some(method) => self.cx.expr_method_call(
expr.span, left, method, vec![right]).and_then(|e| e),
None =>
Expr {
node: ExprKind::Binary(op, left, right),
..expr
},
}
},
ExprKind::AssignOp(op, target, source) => {
let source = self.fold_expr(source);
Expr {
node: match wrapping_method(op.node) {
Some(method) => {
let call = self.cx.expr_method_call(
expr.span, target.clone(), method, vec![source]);
ExprKind::Assign(target, call)
},
None => ExprKind::AssignOp(op, target, source),
},
..expr
}
},
_ => fold::noop_fold_expr(expr, self),
}})
}
}
fn wrapping_method(op: BinOpKind) -> Option<Ident> {
Some(Ident::from_str(match op {
BinOpKind::Add => "wrapping_add",
BinOpKind::Sub => "wrapping_sub",
BinOpKind::Mul => "wrapping_mul",
BinOpKind::Div => "wrapping_div",
BinOpKind::Rem => "wrapping_rem",
BinOpKind::Shl => "wrapping_shl",
BinOpKind::Shr => "wrapping_shr",
_ => return None,
}))
}
fn expand_wrapping<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> Box<MacResult + 'cx> {
let block = TokenTree::Delimited(sp, Delimited {
delim: DelimToken::Brace,
tts: tts.iter().cloned().collect::<TokenStream>().into(),
});
let mut parser = parse::stream_to_parser(cx.parse_sess, block.into());
match parser.parse_block() {
Ok(block) => {
let block = WrappingFolder { cx: cx }.fold_block(block);
MacEager::expr(cx.expr_block(block))
},
Err(mut e) => {
e.emit();
DummyResult::expr(sp)
},
}
}
#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
reg.register_macro("wrapping", expand_wrapping);
}