4 #![crate_type = "proc-macro"]
6 #![feature(proc_macro_expand, proc_macro_span)]
8 extern crate proc_macro;
11 use std::str::FromStr;
13 // Flatten the TokenStream, removing any toplevel `Delimiter::None`s for
15 fn flatten(ts: TokenStream) -> Vec<TokenTree> {
17 .flat_map(|tt| match &tt {
18 TokenTree::Group(group) if group.delimiter() == Delimiter::None => {
19 flatten(group.stream())
26 // Assert that two TokenStream values are roughly equal to one-another.
27 fn assert_ts_eq(lhs: &TokenStream, rhs: &TokenStream) {
28 let ltts = flatten(lhs.clone());
29 let rtts = flatten(rhs.clone());
31 if ltts.len() != rtts.len() {
33 "expected the same number of tts ({} == {})\nlhs:\n{:#?}\nrhs:\n{:#?}",
41 for (ltt, rtt) in ltts.iter().zip(&rtts) {
43 (TokenTree::Group(l), TokenTree::Group(r)) => {
47 "expected delimiters to match for {:?} and {:?}",
51 assert_ts_eq(&l.stream(), &r.stream());
53 (TokenTree::Punct(l), TokenTree::Punct(r)) => assert_eq!(
54 (l.as_char(), l.spacing()),
55 (r.as_char(), r.spacing()),
56 "expected punct to match for {:?} and {:?}",
60 (TokenTree::Ident(l), TokenTree::Ident(r)) => assert_eq!(
63 "expected ident to match for {:?} and {:?}",
67 (TokenTree::Literal(l), TokenTree::Literal(r)) => assert_eq!(
70 "expected literal to match for {:?} and {:?}",
74 (l, r) => panic!("expected type to match for {:?} and {:?}", l, r),
80 pub fn expand_expr_is(input: TokenStream) -> TokenStream {
81 let mut iter = input.into_iter();
82 let mut expected_tts = Vec::new();
85 Some(TokenTree::Punct(p)) if p.as_char() == ',' => break p,
86 Some(tt) => expected_tts.push(tt),
87 None => panic!("expected comma"),
91 // Make sure that `Ident` and `Literal` objects from this proc-macro's
92 // environment are not invalidated when `expand_expr` recursively invokes
93 // another macro by taking a local copy, and checking it after the fact.
94 let pre_expand_span = comma.span();
95 let pre_expand_ident = Ident::new("ident", comma.span());
96 let pre_expand_literal = Literal::string("literal");
97 let pre_expand_call_site = Span::call_site();
99 let expected = expected_tts.into_iter().collect::<TokenStream>();
100 let expanded = iter.collect::<TokenStream>().expand_expr().expect("expand_expr failed");
102 expected.to_string() == expanded.to_string(),
103 "assert failed\nexpected: `{}`\nexpanded: `{}`",
104 expected.to_string(),
108 // Also compare the raw tts to make sure they line up.
109 assert_ts_eq(&expected, &expanded);
111 assert!(comma.span().eq(&pre_expand_span), "pre-expansion span is still equal");
112 assert_eq!(pre_expand_ident.to_string(), "ident", "pre-expansion identifier is still valid");
114 pre_expand_literal.to_string(),
116 "pre-expansion literal is still valid"
118 assert!(Span::call_site().eq(&pre_expand_call_site), "pre-expansion call-site is still equal");
124 pub fn expand_expr_fail(input: TokenStream) -> TokenStream {
125 match input.expand_expr() {
126 Ok(ts) => panic!("expand_expr unexpectedly succeeded: `{}`", ts),
127 Err(_) => TokenStream::new(),
132 pub fn check_expand_expr_file(ts: TokenStream) -> TokenStream {
133 // Check that the passed in `file!()` invocation and a parsed `file!`
134 // invocation expand to the same literal.
135 let input_t = ts.expand_expr().expect("expand_expr failed on macro input").to_string();
136 let parse_t = TokenStream::from_str("file!{}")
139 .expect("expand_expr failed on internal macro")
141 assert_eq!(input_t, parse_t);
143 // Check that the literal matches `Span::call_site().source_file().path()`
145 Literal::string(&Span::call_site().source_file().path().to_string_lossy()).to_string();
146 assert_eq!(input_t, expect_t);
152 pub fn recursive_expand(_: TokenStream) -> TokenStream {
153 // Recursively call until we hit the recursion limit and get an error.
155 // NOTE: This doesn't panic if expansion fails because that'll cause a very
156 // large number of errors to fill the output.
157 TokenStream::from_str("recursive_expand!{}")
160 .unwrap_or(std::iter::once(TokenTree::Literal(Literal::u32_suffixed(0))).collect())
164 pub fn echo_pm(input: TokenStream) -> TokenStream {