]> git.lizzy.rs Git - rust.git/blob - src/test/ui/proc-macro/auxiliary/expand-expr.rs
normalize stderr
[rust.git] / src / test / ui / proc-macro / auxiliary / expand-expr.rs
1 // force-host
2 // no-prefer-dynamic
3
4 #![crate_type = "proc-macro"]
5 #![deny(warnings)]
6 #![feature(proc_macro_expand, proc_macro_span)]
7
8 extern crate proc_macro;
9
10 use proc_macro::*;
11 use std::str::FromStr;
12
13 // Flatten the TokenStream, removing any toplevel `Delimiter::None`s for
14 // comparison.
15 fn flatten(ts: TokenStream) -> Vec<TokenTree> {
16     ts.into_iter()
17         .flat_map(|tt| match &tt {
18             TokenTree::Group(group) if group.delimiter() == Delimiter::None => {
19                 flatten(group.stream())
20             }
21             _ => vec![tt],
22         })
23         .collect()
24 }
25
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());
30
31     if ltts.len() != rtts.len() {
32         panic!(
33             "expected the same number of tts ({} == {})\nlhs:\n{:#?}\nrhs:\n{:#?}",
34             ltts.len(),
35             rtts.len(),
36             lhs,
37             rhs
38         )
39     }
40
41     for (ltt, rtt) in ltts.iter().zip(&rtts) {
42         match (ltt, rtt) {
43             (TokenTree::Group(l), TokenTree::Group(r)) => {
44                 assert_eq!(
45                     l.delimiter(),
46                     r.delimiter(),
47                     "expected delimiters to match for {:?} and {:?}",
48                     l,
49                     r
50                 );
51                 assert_ts_eq(&l.stream(), &r.stream());
52             }
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 {:?}",
57                 l,
58                 r
59             ),
60             (TokenTree::Ident(l), TokenTree::Ident(r)) => assert_eq!(
61                 l.to_string(),
62                 r.to_string(),
63                 "expected ident to match for {:?} and {:?}",
64                 l,
65                 r
66             ),
67             (TokenTree::Literal(l), TokenTree::Literal(r)) => assert_eq!(
68                 l.to_string(),
69                 r.to_string(),
70                 "expected literal to match for {:?} and {:?}",
71                 l,
72                 r
73             ),
74             (l, r) => panic!("expected type to match for {:?} and {:?}", l, r),
75         }
76     }
77 }
78
79 #[proc_macro]
80 pub fn expand_expr_is(input: TokenStream) -> TokenStream {
81     let mut iter = input.into_iter();
82     let mut expected_tts = Vec::new();
83     let comma = loop {
84         match iter.next() {
85             Some(TokenTree::Punct(p)) if p.as_char() == ',' => break p,
86             Some(tt) => expected_tts.push(tt),
87             None => panic!("expected comma"),
88         }
89     };
90
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();
98
99     let expected = expected_tts.into_iter().collect::<TokenStream>();
100     let expanded = iter.collect::<TokenStream>().expand_expr().expect("expand_expr failed");
101     assert!(
102         expected.to_string() == expanded.to_string(),
103         "assert failed\nexpected: `{}`\nexpanded: `{}`",
104         expected.to_string(),
105         expanded.to_string()
106     );
107
108     // Also compare the raw tts to make sure they line up.
109     assert_ts_eq(&expected, &expanded);
110
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");
113     assert_eq!(
114         pre_expand_literal.to_string(),
115         "\"literal\"",
116         "pre-expansion literal is still valid"
117     );
118     assert!(Span::call_site().eq(&pre_expand_call_site), "pre-expansion call-site is still equal");
119
120     TokenStream::new()
121 }
122
123 #[proc_macro]
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(),
128     }
129 }
130
131 #[proc_macro]
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!{}")
137         .unwrap()
138         .expand_expr()
139         .expect("expand_expr failed on internal macro")
140         .to_string();
141     assert_eq!(input_t, parse_t);
142
143     // Check that the literal matches `Span::call_site().source_file().path()`
144     let expect_t =
145         Literal::string(&Span::call_site().source_file().path().to_string_lossy()).to_string();
146     assert_eq!(input_t, expect_t);
147
148     TokenStream::new()
149 }
150
151 #[proc_macro]
152 pub fn recursive_expand(_: TokenStream) -> TokenStream {
153     // Recursively call until we hit the recursion limit and get an error.
154     //
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!{}")
158         .unwrap()
159         .expand_expr()
160         .unwrap_or(std::iter::once(TokenTree::Literal(Literal::u32_suffixed(0))).collect())
161 }
162
163 #[proc_macro]
164 pub fn echo_pm(input: TokenStream) -> TokenStream {
165     input
166 }