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