]>
Commit | Line | Data |
---|---|---|
064997fb FG |
1 | //! This module add real world mbe example for benchmark tests |
2 | ||
3 | use rustc_hash::FxHashMap; | |
4 | use syntax::{ | |
5 | ast::{self, HasName}, | |
6 | AstNode, SmolStr, | |
7 | }; | |
8 | use test_utils::{bench, bench_fixture, skip_slow_tests}; | |
9 | ||
10 | use crate::{ | |
2b03887a | 11 | parser::{MetaVarKind, Op, RepeatKind, Separator}, |
9ffffee4 | 12 | syntax_node_to_token_tree, tt, DeclarativeMacro, |
064997fb FG |
13 | }; |
14 | ||
15 | #[test] | |
16 | fn benchmark_parse_macro_rules() { | |
17 | if skip_slow_tests() { | |
18 | return; | |
19 | } | |
20 | let rules = macro_rules_fixtures_tt(); | |
21 | let hash: usize = { | |
22 | let _pt = bench("mbe parse macro rules"); | |
23 | rules.values().map(|it| DeclarativeMacro::parse_macro_rules(it).unwrap().rules.len()).sum() | |
24 | }; | |
25 | assert_eq!(hash, 1144); | |
26 | } | |
27 | ||
28 | #[test] | |
29 | fn benchmark_expand_macro_rules() { | |
30 | if skip_slow_tests() { | |
31 | return; | |
32 | } | |
33 | let rules = macro_rules_fixtures(); | |
34 | let invocations = invocation_fixtures(&rules); | |
35 | ||
36 | let hash: usize = { | |
37 | let _pt = bench("mbe expand macro rules"); | |
38 | invocations | |
39 | .into_iter() | |
40 | .map(|(id, tt)| { | |
41 | let res = rules[&id].expand(&tt); | |
42 | assert!(res.err.is_none()); | |
43 | res.value.token_trees.len() | |
44 | }) | |
45 | .sum() | |
46 | }; | |
47 | assert_eq!(hash, 69413); | |
48 | } | |
49 | ||
50 | fn macro_rules_fixtures() -> FxHashMap<String, DeclarativeMacro> { | |
51 | macro_rules_fixtures_tt() | |
52 | .into_iter() | |
53 | .map(|(id, tt)| (id, DeclarativeMacro::parse_macro_rules(&tt).unwrap())) | |
54 | .collect() | |
55 | } | |
56 | ||
57 | fn macro_rules_fixtures_tt() -> FxHashMap<String, tt::Subtree> { | |
58 | let fixture = bench_fixture::numerous_macro_rules(); | |
59 | let source_file = ast::SourceFile::parse(&fixture).ok().unwrap(); | |
60 | ||
61 | source_file | |
62 | .syntax() | |
63 | .descendants() | |
64 | .filter_map(ast::MacroRules::cast) | |
65 | .map(|rule| { | |
66 | let id = rule.name().unwrap().to_string(); | |
67 | let (def_tt, _) = syntax_node_to_token_tree(rule.token_tree().unwrap().syntax()); | |
68 | (id, def_tt) | |
69 | }) | |
70 | .collect() | |
71 | } | |
72 | ||
73 | /// Generate random invocation fixtures from rules | |
74 | fn invocation_fixtures(rules: &FxHashMap<String, DeclarativeMacro>) -> Vec<(String, tt::Subtree)> { | |
75 | let mut seed = 123456789; | |
76 | let mut res = Vec::new(); | |
77 | ||
78 | for (name, it) in rules { | |
79 | for rule in &it.rules { | |
80 | // Generate twice | |
81 | for _ in 0..2 { | |
82 | // The input are generated by filling the `Op` randomly. | |
83 | // However, there are some cases generated are ambiguous for expanding, for example: | |
84 | // ```rust | |
85 | // macro_rules! m { | |
86 | // ($($t:ident),* as $ty:ident) => {} | |
87 | // } | |
88 | // m!(as u32); // error: local ambiguity: multiple parsing options: built-in NTs ident ('t') or 1 other option. | |
89 | // ``` | |
90 | // | |
91 | // So we just skip any error cases and try again | |
92 | let mut try_cnt = 0; | |
93 | loop { | |
9ffffee4 FG |
94 | let mut subtree = tt::Subtree { |
95 | delimiter: tt::Delimiter { | |
96 | open: tt::TokenId::UNSPECIFIED, | |
97 | close: tt::TokenId::UNSPECIFIED, | |
98 | kind: tt::DelimiterKind::Invisible, | |
99 | }, | |
100 | token_trees: vec![], | |
101 | }; | |
064997fb FG |
102 | for op in rule.lhs.iter() { |
103 | collect_from_op(op, &mut subtree, &mut seed); | |
104 | } | |
105 | if it.expand(&subtree).err.is_none() { | |
106 | res.push((name.clone(), subtree)); | |
107 | break; | |
108 | } | |
109 | try_cnt += 1; | |
110 | if try_cnt > 100 { | |
9c376795 | 111 | panic!("invocaton fixture {name} cannot be generated.\n"); |
064997fb FG |
112 | } |
113 | } | |
114 | } | |
115 | } | |
116 | } | |
117 | return res; | |
118 | ||
119 | fn collect_from_op(op: &Op, parent: &mut tt::Subtree, seed: &mut usize) { | |
120 | return match op { | |
2b03887a FG |
121 | Op::Var { kind, .. } => match kind.as_ref() { |
122 | Some(MetaVarKind::Ident) => parent.token_trees.push(make_ident("foo")), | |
123 | Some(MetaVarKind::Ty) => parent.token_trees.push(make_ident("Foo")), | |
124 | Some(MetaVarKind::Tt) => parent.token_trees.push(make_ident("foo")), | |
125 | Some(MetaVarKind::Vis) => parent.token_trees.push(make_ident("pub")), | |
126 | Some(MetaVarKind::Pat) => parent.token_trees.push(make_ident("foo")), | |
127 | Some(MetaVarKind::Path) => parent.token_trees.push(make_ident("foo")), | |
128 | Some(MetaVarKind::Literal) => parent.token_trees.push(make_literal("1")), | |
129 | Some(MetaVarKind::Expr) => parent.token_trees.push(make_ident("foo")), | |
130 | Some(MetaVarKind::Lifetime) => { | |
064997fb FG |
131 | parent.token_trees.push(make_punct('\'')); |
132 | parent.token_trees.push(make_ident("a")); | |
133 | } | |
2b03887a | 134 | Some(MetaVarKind::Block) => { |
064997fb FG |
135 | parent.token_trees.push(make_subtree(tt::DelimiterKind::Brace, None)) |
136 | } | |
2b03887a | 137 | Some(MetaVarKind::Item) => { |
064997fb FG |
138 | parent.token_trees.push(make_ident("fn")); |
139 | parent.token_trees.push(make_ident("foo")); | |
140 | parent.token_trees.push(make_subtree(tt::DelimiterKind::Parenthesis, None)); | |
141 | parent.token_trees.push(make_subtree(tt::DelimiterKind::Brace, None)); | |
142 | } | |
2b03887a | 143 | Some(MetaVarKind::Meta) => { |
064997fb FG |
144 | parent.token_trees.push(make_ident("foo")); |
145 | parent.token_trees.push(make_subtree(tt::DelimiterKind::Parenthesis, None)); | |
146 | } | |
147 | ||
148 | None => (), | |
9c376795 | 149 | Some(kind) => panic!("Unhandled kind {kind:?}"), |
064997fb | 150 | }, |
9c376795 FG |
151 | Op::Literal(it) => parent.token_trees.push(tt::Leaf::from(it.clone()).into()), |
152 | Op::Ident(it) => parent.token_trees.push(tt::Leaf::from(it.clone()).into()), | |
153 | Op::Punct(puncts) => { | |
154 | for punct in puncts { | |
9ffffee4 | 155 | parent.token_trees.push(tt::Leaf::from(*punct).into()); |
9c376795 FG |
156 | } |
157 | } | |
064997fb FG |
158 | Op::Repeat { tokens, kind, separator } => { |
159 | let max = 10; | |
160 | let cnt = match kind { | |
161 | RepeatKind::ZeroOrMore => rand(seed) % max, | |
162 | RepeatKind::OneOrMore => 1 + rand(seed) % max, | |
163 | RepeatKind::ZeroOrOne => rand(seed) % 2, | |
164 | }; | |
165 | for i in 0..cnt { | |
166 | for it in tokens.iter() { | |
167 | collect_from_op(it, parent, seed); | |
168 | } | |
169 | if i + 1 != cnt { | |
170 | if let Some(sep) = separator { | |
171 | match sep { | |
172 | Separator::Literal(it) => { | |
173 | parent.token_trees.push(tt::Leaf::Literal(it.clone()).into()) | |
174 | } | |
175 | Separator::Ident(it) => { | |
176 | parent.token_trees.push(tt::Leaf::Ident(it.clone()).into()) | |
177 | } | |
178 | Separator::Puncts(puncts) => { | |
179 | for it in puncts { | |
180 | parent.token_trees.push(tt::Leaf::Punct(*it).into()) | |
181 | } | |
182 | } | |
183 | }; | |
184 | } | |
185 | } | |
186 | } | |
187 | } | |
188 | Op::Subtree { tokens, delimiter } => { | |
189 | let mut subtree = tt::Subtree { delimiter: *delimiter, token_trees: Vec::new() }; | |
190 | tokens.iter().for_each(|it| { | |
191 | collect_from_op(it, &mut subtree, seed); | |
192 | }); | |
193 | parent.token_trees.push(subtree.into()); | |
194 | } | |
195 | Op::Ignore { .. } | Op::Index { .. } => {} | |
196 | }; | |
197 | ||
198 | // Simple linear congruential generator for determistic result | |
199 | fn rand(seed: &mut usize) -> usize { | |
200 | let a = 1664525; | |
201 | let c = 1013904223; | |
202 | *seed = usize::wrapping_add(usize::wrapping_mul(*seed, a), c); | |
203 | *seed | |
204 | } | |
205 | fn make_ident(ident: &str) -> tt::TokenTree { | |
9ffffee4 FG |
206 | tt::Leaf::Ident(tt::Ident { |
207 | span: tt::TokenId::unspecified(), | |
208 | text: SmolStr::new(ident), | |
209 | }) | |
210 | .into() | |
064997fb FG |
211 | } |
212 | fn make_punct(char: char) -> tt::TokenTree { | |
213 | tt::Leaf::Punct(tt::Punct { | |
9ffffee4 | 214 | span: tt::TokenId::unspecified(), |
064997fb FG |
215 | char, |
216 | spacing: tt::Spacing::Alone, | |
217 | }) | |
218 | .into() | |
219 | } | |
220 | fn make_literal(lit: &str) -> tt::TokenTree { | |
221 | tt::Leaf::Literal(tt::Literal { | |
9ffffee4 | 222 | span: tt::TokenId::unspecified(), |
064997fb FG |
223 | text: SmolStr::new(lit), |
224 | }) | |
225 | .into() | |
226 | } | |
227 | fn make_subtree( | |
228 | kind: tt::DelimiterKind, | |
229 | token_trees: Option<Vec<tt::TokenTree>>, | |
230 | ) -> tt::TokenTree { | |
231 | tt::Subtree { | |
9ffffee4 FG |
232 | delimiter: tt::Delimiter { |
233 | open: tt::TokenId::unspecified(), | |
234 | close: tt::TokenId::unspecified(), | |
235 | kind, | |
236 | }, | |
064997fb FG |
237 | token_trees: token_trees.unwrap_or_default(), |
238 | } | |
239 | .into() | |
240 | } | |
241 | } | |
242 | } |