]>
Commit | Line | Data |
---|---|---|
416331ca | 1 | use crate::tests::{matches_codepattern, string_to_stream, with_error_checking_parse}; |
60c5eb7d | 2 | |
74b04a01 | 3 | use rustc_ast::ptr::P; |
04454e1e | 4 | use rustc_ast::token::{self, Delimiter, Token}; |
74b04a01 XL |
5 | use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree}; |
6 | use rustc_ast::visit; | |
3dfed10e | 7 | use rustc_ast::{self as ast, PatKind}; |
74b04a01 | 8 | use rustc_ast_pretty::pprust::item_to_string; |
dfeec247 | 9 | use rustc_errors::PResult; |
60c5eb7d | 10 | use rustc_parse::new_parser_from_source_str; |
5869c6ff | 11 | use rustc_parse::parser::ForceCollect; |
74b04a01 | 12 | use rustc_session::parse::ParseSess; |
136023e0 | 13 | use rustc_span::create_default_session_globals_then; |
dfeec247 XL |
14 | use rustc_span::source_map::FilePathMapping; |
15 | use rustc_span::symbol::{kw, sym, Symbol}; | |
16 | use rustc_span::{BytePos, FileName, Pos, Span}; | |
416331ca XL |
17 | |
18 | use std::path::PathBuf; | |
19 | ||
60c5eb7d XL |
20 | fn sess() -> ParseSess { |
21 | ParseSess::new(FilePathMapping::empty()) | |
22 | } | |
23 | ||
416331ca XL |
24 | /// Parses an item. |
25 | /// | |
26 | /// Returns `Ok(Some(item))` when successful, `Ok(None)` when no item was found, and `Err` | |
27 | /// when a syntax error occurred. | |
dfeec247 XL |
28 | fn parse_item_from_source_str( |
29 | name: FileName, | |
30 | source: String, | |
31 | sess: &ParseSess, | |
32 | ) -> PResult<'_, Option<P<ast::Item>>> { | |
5869c6ff | 33 | new_parser_from_source_str(sess, name, source).parse_item(ForceCollect::No) |
416331ca XL |
34 | } |
35 | ||
dfeec247 | 36 | // Produces a `rustc_span::span`. |
416331ca | 37 | fn sp(a: u32, b: u32) -> Span { |
e1599b0c | 38 | Span::with_root_ctxt(BytePos(a), BytePos(b)) |
416331ca XL |
39 | } |
40 | ||
e1599b0c | 41 | /// Parses a string, return an expression. |
dfeec247 | 42 | fn string_to_expr(source_str: String) -> P<ast::Expr> { |
60c5eb7d | 43 | with_error_checking_parse(source_str, &sess(), |p| p.parse_expr()) |
416331ca XL |
44 | } |
45 | ||
e1599b0c | 46 | /// Parses a string, returns an item. |
dfeec247 | 47 | fn string_to_item(source_str: String) -> Option<P<ast::Item>> { |
5869c6ff | 48 | with_error_checking_parse(source_str, &sess(), |p| p.parse_item(ForceCollect::No)) |
416331ca XL |
49 | } |
50 | ||
51 | #[should_panic] | |
dfeec247 XL |
52 | #[test] |
53 | fn bad_path_expr_1() { | |
136023e0 | 54 | create_default_session_globals_then(|| { |
416331ca XL |
55 | string_to_expr("::abc::def::return".to_string()); |
56 | }) | |
57 | } | |
58 | ||
e1599b0c | 59 | // Checks the token-tree-ization of macros. |
416331ca | 60 | #[test] |
dfeec247 | 61 | fn string_to_tts_macro() { |
136023e0 | 62 | create_default_session_globals_then(|| { |
416331ca | 63 | let tts: Vec<_> = |
923072b8 | 64 | string_to_stream("macro_rules! zip (($a)=>($a))".to_string()).into_trees().collect(); |
416331ca XL |
65 | let tts: &[TokenTree] = &tts[..]; |
66 | ||
67 | match tts { | |
a2a8927a XL |
68 | [ |
69 | TokenTree::Token(Token { kind: token::Ident(name_macro_rules, false), .. }), | |
70 | TokenTree::Token(Token { kind: token::Not, .. }), | |
71 | TokenTree::Token(Token { kind: token::Ident(name_zip, false), .. }), | |
72 | TokenTree::Delimited(_, macro_delim, macro_tts), | |
73 | ] if name_macro_rules == &kw::MacroRules && name_zip.as_str() == "zip" => { | |
416331ca XL |
74 | let tts = ¯o_tts.trees().collect::<Vec<_>>(); |
75 | match &tts[..] { | |
a2a8927a XL |
76 | [ |
77 | TokenTree::Delimited(_, first_delim, first_tts), | |
78 | TokenTree::Token(Token { kind: token::FatArrow, .. }), | |
79 | TokenTree::Delimited(_, second_delim, second_tts), | |
04454e1e | 80 | ] if macro_delim == &Delimiter::Parenthesis => { |
416331ca XL |
81 | let tts = &first_tts.trees().collect::<Vec<_>>(); |
82 | match &tts[..] { | |
a2a8927a XL |
83 | [ |
84 | TokenTree::Token(Token { kind: token::Dollar, .. }), | |
85 | TokenTree::Token(Token { kind: token::Ident(name, false), .. }), | |
04454e1e FG |
86 | ] if first_delim == &Delimiter::Parenthesis && name.as_str() == "a" => { |
87 | } | |
416331ca XL |
88 | _ => panic!("value 3: {:?} {:?}", first_delim, first_tts), |
89 | } | |
90 | let tts = &second_tts.trees().collect::<Vec<_>>(); | |
91 | match &tts[..] { | |
a2a8927a XL |
92 | [ |
93 | TokenTree::Token(Token { kind: token::Dollar, .. }), | |
94 | TokenTree::Token(Token { kind: token::Ident(name, false), .. }), | |
04454e1e FG |
95 | ] if second_delim == &Delimiter::Parenthesis |
96 | && name.as_str() == "a" => {} | |
416331ca XL |
97 | _ => panic!("value 4: {:?} {:?}", second_delim, second_tts), |
98 | } | |
dfeec247 | 99 | } |
416331ca XL |
100 | _ => panic!("value 2: {:?} {:?}", macro_delim, macro_tts), |
101 | } | |
dfeec247 XL |
102 | } |
103 | _ => panic!("value: {:?}", tts), | |
416331ca XL |
104 | } |
105 | }) | |
106 | } | |
107 | ||
108 | #[test] | |
109 | fn string_to_tts_1() { | |
136023e0 | 110 | create_default_session_globals_then(|| { |
416331ca XL |
111 | let tts = string_to_stream("fn a (b : i32) { b; }".to_string()); |
112 | ||
113 | let expected = TokenStream::new(vec![ | |
114 | TokenTree::token(token::Ident(kw::Fn, false), sp(0, 2)).into(), | |
f9f354fc | 115 | TokenTree::token(token::Ident(Symbol::intern("a"), false), sp(3, 4)).into(), |
416331ca XL |
116 | TokenTree::Delimited( |
117 | DelimSpan::from_pair(sp(5, 6), sp(13, 14)), | |
04454e1e | 118 | Delimiter::Parenthesis, |
416331ca | 119 | TokenStream::new(vec![ |
f9f354fc | 120 | TokenTree::token(token::Ident(Symbol::intern("b"), false), sp(6, 7)).into(), |
416331ca XL |
121 | TokenTree::token(token::Colon, sp(8, 9)).into(), |
122 | TokenTree::token(token::Ident(sym::i32, false), sp(10, 13)).into(), | |
dfeec247 XL |
123 | ]) |
124 | .into(), | |
125 | ) | |
126 | .into(), | |
416331ca XL |
127 | TokenTree::Delimited( |
128 | DelimSpan::from_pair(sp(15, 16), sp(20, 21)), | |
04454e1e | 129 | Delimiter::Brace, |
416331ca | 130 | TokenStream::new(vec![ |
f9f354fc | 131 | TokenTree::token(token::Ident(Symbol::intern("b"), false), sp(17, 18)).into(), |
416331ca | 132 | TokenTree::token(token::Semi, sp(18, 19)).into(), |
dfeec247 XL |
133 | ]) |
134 | .into(), | |
135 | ) | |
136 | .into(), | |
416331ca XL |
137 | ]); |
138 | ||
139 | assert_eq!(tts, expected); | |
140 | }) | |
141 | } | |
142 | ||
dfeec247 XL |
143 | #[test] |
144 | fn parse_use() { | |
136023e0 | 145 | create_default_session_globals_then(|| { |
416331ca XL |
146 | let use_s = "use foo::bar::baz;"; |
147 | let vitem = string_to_item(use_s.to_string()).unwrap(); | |
148 | let vitem_s = item_to_string(&vitem); | |
149 | assert_eq!(&vitem_s[..], use_s); | |
150 | ||
151 | let use_s = "use foo::bar as baz;"; | |
152 | let vitem = string_to_item(use_s.to_string()).unwrap(); | |
153 | let vitem_s = item_to_string(&vitem); | |
154 | assert_eq!(&vitem_s[..], use_s); | |
155 | }) | |
156 | } | |
157 | ||
dfeec247 XL |
158 | #[test] |
159 | fn parse_extern_crate() { | |
136023e0 | 160 | create_default_session_globals_then(|| { |
416331ca XL |
161 | let ex_s = "extern crate foo;"; |
162 | let vitem = string_to_item(ex_s.to_string()).unwrap(); | |
163 | let vitem_s = item_to_string(&vitem); | |
164 | assert_eq!(&vitem_s[..], ex_s); | |
165 | ||
166 | let ex_s = "extern crate foo as bar;"; | |
167 | let vitem = string_to_item(ex_s.to_string()).unwrap(); | |
168 | let vitem_s = item_to_string(&vitem); | |
169 | assert_eq!(&vitem_s[..], ex_s); | |
170 | }) | |
171 | } | |
172 | ||
173 | fn get_spans_of_pat_idents(src: &str) -> Vec<Span> { | |
174 | let item = string_to_item(src.to_string()).unwrap(); | |
175 | ||
176 | struct PatIdentVisitor { | |
dfeec247 | 177 | spans: Vec<Span>, |
416331ca | 178 | } |
60c5eb7d | 179 | impl<'a> visit::Visitor<'a> for PatIdentVisitor { |
416331ca | 180 | fn visit_pat(&mut self, p: &'a ast::Pat) { |
e74abb32 | 181 | match p.kind { |
dfeec247 | 182 | PatKind::Ident(_, ref ident, _) => { |
e1599b0c | 183 | self.spans.push(ident.span.clone()); |
416331ca XL |
184 | } |
185 | _ => { | |
60c5eb7d | 186 | visit::walk_pat(self, p); |
416331ca XL |
187 | } |
188 | } | |
189 | } | |
190 | } | |
191 | let mut v = PatIdentVisitor { spans: Vec::new() }; | |
60c5eb7d | 192 | visit::walk_item(&mut v, &item); |
416331ca XL |
193 | return v.spans; |
194 | } | |
195 | ||
dfeec247 XL |
196 | #[test] |
197 | fn span_of_self_arg_pat_idents_are_correct() { | |
136023e0 | 198 | create_default_session_globals_then(|| { |
dfeec247 XL |
199 | let srcs = [ |
200 | "impl z { fn a (&self, &myarg: i32) {} }", | |
201 | "impl z { fn a (&mut self, &myarg: i32) {} }", | |
202 | "impl z { fn a (&'a self, &myarg: i32) {} }", | |
203 | "impl z { fn a (self, &myarg: i32) {} }", | |
204 | "impl z { fn a (self: Foo, &myarg: i32) {} }", | |
205 | ]; | |
416331ca | 206 | |
136023e0 | 207 | for src in srcs { |
416331ca XL |
208 | let spans = get_spans_of_pat_idents(src); |
209 | let (lo, hi) = (spans[0].lo(), spans[0].hi()); | |
dfeec247 XL |
210 | assert!( |
211 | "self" == &src[lo.to_usize()..hi.to_usize()], | |
212 | "\"{}\" != \"self\". src=\"{}\"", | |
213 | &src[lo.to_usize()..hi.to_usize()], | |
214 | src | |
215 | ) | |
416331ca XL |
216 | } |
217 | }) | |
218 | } | |
219 | ||
dfeec247 XL |
220 | #[test] |
221 | fn parse_exprs() { | |
136023e0 | 222 | create_default_session_globals_then(|| { |
416331ca XL |
223 | // just make sure that they parse.... |
224 | string_to_expr("3 + 4".to_string()); | |
225 | string_to_expr("a::z.froob(b,&(987+3))".to_string()); | |
226 | }) | |
227 | } | |
228 | ||
dfeec247 XL |
229 | #[test] |
230 | fn attrs_fix_bug() { | |
136023e0 | 231 | create_default_session_globals_then(|| { |
dfeec247 XL |
232 | string_to_item( |
233 | "pub fn mk_file_writer(path: &Path, flags: &[FileFlag]) | |
416331ca XL |
234 | -> Result<Box<Writer>, String> { |
235 | #[cfg(windows)] | |
236 | fn wb() -> c_int { | |
237 | (O_WRONLY | libc::consts::os::extra::O_BINARY) as c_int | |
238 | } | |
239 | ||
240 | #[cfg(unix)] | |
241 | fn wb() -> c_int { O_WRONLY as c_int } | |
242 | ||
243 | let mut fflags: c_int = wb(); | |
dfeec247 XL |
244 | }" |
245 | .to_string(), | |
246 | ); | |
416331ca XL |
247 | }) |
248 | } | |
249 | ||
dfeec247 XL |
250 | #[test] |
251 | fn crlf_doc_comments() { | |
136023e0 | 252 | create_default_session_globals_then(|| { |
60c5eb7d | 253 | let sess = sess(); |
416331ca XL |
254 | |
255 | let name_1 = FileName::Custom("crlf_source_1".to_string()); | |
256 | let source = "/// doc comment\r\nfn foo() {}".to_string(); | |
dfeec247 XL |
257 | let item = parse_item_from_source_str(name_1, source, &sess).unwrap().unwrap(); |
258 | let doc = item.attrs.iter().filter_map(|at| at.doc_str()).next().unwrap(); | |
3dfed10e | 259 | assert_eq!(doc.as_str(), " doc comment"); |
416331ca XL |
260 | |
261 | let name_2 = FileName::Custom("crlf_source_2".to_string()); | |
262 | let source = "/// doc comment\r\n/// line 2\r\nfn foo() {}".to_string(); | |
dfeec247 XL |
263 | let item = parse_item_from_source_str(name_2, source, &sess).unwrap().unwrap(); |
264 | let docs = item.attrs.iter().filter_map(|at| at.doc_str()).collect::<Vec<_>>(); | |
3dfed10e | 265 | let b: &[_] = &[Symbol::intern(" doc comment"), Symbol::intern(" line 2")]; |
416331ca XL |
266 | assert_eq!(&docs[..], b); |
267 | ||
268 | let name_3 = FileName::Custom("clrf_source_3".to_string()); | |
269 | let source = "/** doc comment\r\n * with CRLF */\r\nfn foo() {}".to_string(); | |
270 | let item = parse_item_from_source_str(name_3, source, &sess).unwrap().unwrap(); | |
dfeec247 | 271 | let doc = item.attrs.iter().filter_map(|at| at.doc_str()).next().unwrap(); |
3dfed10e | 272 | assert_eq!(doc.as_str(), " doc comment\n * with CRLF "); |
416331ca XL |
273 | }); |
274 | } | |
275 | ||
276 | #[test] | |
277 | fn ttdelim_span() { | |
278 | fn parse_expr_from_source_str( | |
dfeec247 XL |
279 | name: FileName, |
280 | source: String, | |
281 | sess: &ParseSess, | |
416331ca XL |
282 | ) -> PResult<'_, P<ast::Expr>> { |
283 | new_parser_from_source_str(sess, name, source).parse_expr() | |
284 | } | |
285 | ||
136023e0 | 286 | create_default_session_globals_then(|| { |
60c5eb7d | 287 | let sess = sess(); |
dfeec247 XL |
288 | let expr = parse_expr_from_source_str( |
289 | PathBuf::from("foo").into(), | |
290 | "foo!( fn main() { body } )".to_string(), | |
291 | &sess, | |
292 | ) | |
293 | .unwrap(); | |
416331ca | 294 | |
e74abb32 | 295 | let tts: Vec<_> = match expr.kind { |
923072b8 | 296 | ast::ExprKind::MacCall(ref mac) => mac.args.inner_tokens().into_trees().collect(), |
416331ca XL |
297 | _ => panic!("not a macro"), |
298 | }; | |
299 | ||
300 | let span = tts.iter().rev().next().unwrap().span(); | |
301 | ||
302 | match sess.source_map().span_to_snippet(span) { | |
303 | Ok(s) => assert_eq!(&s[..], "{ body }"), | |
304 | Err(_) => panic!("could not get snippet"), | |
305 | } | |
306 | }); | |
307 | } | |
308 | ||
309 | // This tests that when parsing a string (rather than a file) we don't try | |
310 | // and read in a file for a module declaration and just parse a stub. | |
311 | // See `recurse_into_file_modules` in the parser. | |
312 | #[test] | |
313 | fn out_of_line_mod() { | |
136023e0 | 314 | create_default_session_globals_then(|| { |
416331ca XL |
315 | let item = parse_item_from_source_str( |
316 | PathBuf::from("foo").into(), | |
317 | "mod foo { struct S; mod this_does_not_exist; }".to_owned(), | |
60c5eb7d | 318 | &sess(), |
dfeec247 XL |
319 | ) |
320 | .unwrap() | |
321 | .unwrap(); | |
416331ca | 322 | |
6a06907d XL |
323 | if let ast::ItemKind::Mod(_, ref mod_kind) = item.kind { |
324 | assert!(matches!(mod_kind, ast::ModKind::Loaded(items, ..) if items.len() == 2)); | |
416331ca XL |
325 | } else { |
326 | panic!(); | |
327 | } | |
328 | }); | |
329 | } | |
330 | ||
331 | #[test] | |
332 | fn eqmodws() { | |
dfeec247 XL |
333 | assert_eq!(matches_codepattern("", ""), true); |
334 | assert_eq!(matches_codepattern("", "a"), false); | |
335 | assert_eq!(matches_codepattern("a", ""), false); | |
336 | assert_eq!(matches_codepattern("a", "a"), true); | |
337 | assert_eq!(matches_codepattern("a b", "a \n\t\r b"), true); | |
338 | assert_eq!(matches_codepattern("a b ", "a \n\t\r b"), true); | |
339 | assert_eq!(matches_codepattern("a b", "a \n\t\r b "), false); | |
340 | assert_eq!(matches_codepattern("a b", "a b"), true); | |
341 | assert_eq!(matches_codepattern("ab", "a b"), false); | |
342 | assert_eq!(matches_codepattern("a b", "ab"), true); | |
343 | assert_eq!(matches_codepattern(" a b", "ab"), true); | |
416331ca XL |
344 | } |
345 | ||
346 | #[test] | |
347 | fn pattern_whitespace() { | |
dfeec247 XL |
348 | assert_eq!(matches_codepattern("", "\x0C"), false); |
349 | assert_eq!(matches_codepattern("a b ", "a \u{0085}\n\t\r b"), true); | |
350 | assert_eq!(matches_codepattern("a b", "a \u{0085}\n\t\r b "), false); | |
416331ca XL |
351 | } |
352 | ||
353 | #[test] | |
354 | fn non_pattern_whitespace() { | |
355 | // These have the property 'White_Space' but not 'Pattern_White_Space' | |
dfeec247 XL |
356 | assert_eq!(matches_codepattern("a b", "a\u{2002}b"), false); |
357 | assert_eq!(matches_codepattern("a b", "a\u{2002}b"), false); | |
358 | assert_eq!(matches_codepattern("\u{205F}a b", "ab"), false); | |
359 | assert_eq!(matches_codepattern("a \u{3000}b", "ab"), false); | |
416331ca | 360 | } |