]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/parse/mod.rs
don't amplify errors in format! with bad literals
[rust.git] / src / libsyntax / parse / mod.rs
1 //! The main parser interface.
2
3 use crate::ast::{self, CrateConfig, NodeId};
4 use crate::early_buffered_lints::{BufferedEarlyLint, BufferedEarlyLintId};
5 use crate::source_map::{SourceMap, FilePathMapping};
6 use crate::feature_gate::UnstableFeatures;
7 use crate::parse::parser::Parser;
8 use crate::symbol::Symbol;
9 use crate::syntax::parse::parser::emit_unclosed_delims;
10 use crate::tokenstream::{TokenStream, TokenTree};
11 use crate::diagnostics::plugin::ErrorMap;
12 use crate::print::pprust::token_to_string;
13
14 use errors::{FatalError, Level, Handler, ColorConfig, Diagnostic, DiagnosticBuilder};
15 use rustc_data_structures::sync::{Lrc, Lock};
16 use syntax_pos::{Span, SourceFile, FileName, MultiSpan};
17 use log::debug;
18
19 use rustc_data_structures::fx::FxHashSet;
20 use std::borrow::Cow;
21 use std::path::{Path, PathBuf};
22 use std::str;
23
24 pub type PResult<'a, T> = Result<T, DiagnosticBuilder<'a>>;
25
26 #[macro_use]
27 pub mod parser;
28
29 pub mod lexer;
30 pub mod token;
31 pub mod attr;
32
33 pub mod classify;
34
35 pub(crate) mod unescape;
36 use unescape::{unescape_str, unescape_char, unescape_byte_str, unescape_byte};
37
38 pub(crate) mod unescape_error_reporting;
39
40 /// Info about a parsing session.
41 pub struct ParseSess {
42     pub span_diagnostic: Handler,
43     pub unstable_features: UnstableFeatures,
44     pub config: CrateConfig,
45     pub missing_fragment_specifiers: Lock<FxHashSet<Span>>,
46     /// Places where raw identifiers were used. This is used for feature-gating raw identifiers.
47     pub raw_identifier_spans: Lock<Vec<Span>>,
48     /// The registered diagnostics codes.
49     crate registered_diagnostics: Lock<ErrorMap>,
50     /// Used to determine and report recursive module inclusions.
51     included_mod_stack: Lock<Vec<PathBuf>>,
52     source_map: Lrc<SourceMap>,
53     pub buffered_lints: Lock<Vec<BufferedEarlyLint>>,
54 }
55
56 impl ParseSess {
57     pub fn new(file_path_mapping: FilePathMapping) -> Self {
58         let cm = Lrc::new(SourceMap::new(file_path_mapping));
59         let handler = Handler::with_tty_emitter(ColorConfig::Auto,
60                                                 true,
61                                                 None,
62                                                 Some(cm.clone()));
63         ParseSess::with_span_handler(handler, cm)
64     }
65
66     pub fn with_span_handler(handler: Handler, source_map: Lrc<SourceMap>) -> ParseSess {
67         ParseSess {
68             span_diagnostic: handler,
69             unstable_features: UnstableFeatures::from_environment(),
70             config: FxHashSet::default(),
71             missing_fragment_specifiers: Lock::new(FxHashSet::default()),
72             raw_identifier_spans: Lock::new(Vec::new()),
73             registered_diagnostics: Lock::new(ErrorMap::new()),
74             included_mod_stack: Lock::new(vec![]),
75             source_map,
76             buffered_lints: Lock::new(vec![]),
77         }
78     }
79
80     #[inline]
81     pub fn source_map(&self) -> &SourceMap {
82         &self.source_map
83     }
84
85     pub fn buffer_lint<S: Into<MultiSpan>>(&self,
86         lint_id: BufferedEarlyLintId,
87         span: S,
88         id: NodeId,
89         msg: &str,
90     ) {
91         self.buffered_lints.with_lock(|buffered_lints| {
92             buffered_lints.push(BufferedEarlyLint{
93                 span: span.into(),
94                 id,
95                 msg: msg.into(),
96                 lint_id,
97             });
98         });
99     }
100 }
101
102 #[derive(Clone)]
103 pub struct Directory<'a> {
104     pub path: Cow<'a, Path>,
105     pub ownership: DirectoryOwnership,
106 }
107
108 #[derive(Copy, Clone)]
109 pub enum DirectoryOwnership {
110     Owned {
111         // None if `mod.rs`, `Some("foo")` if we're in `foo.rs`
112         relative: Option<ast::Ident>,
113     },
114     UnownedViaBlock,
115     UnownedViaMod(bool /* legacy warnings? */),
116 }
117
118 // a bunch of utility functions of the form parse_<thing>_from_<source>
119 // where <thing> includes crate, expr, item, stmt, tts, and one that
120 // uses a HOF to parse anything, and <source> includes file and
121 // source_str.
122
123 pub fn parse_crate_from_file<'a>(input: &Path, sess: &'a ParseSess) -> PResult<'a, ast::Crate> {
124     let mut parser = new_parser_from_file(sess, input);
125     parser.parse_crate_mod()
126 }
127
128 pub fn parse_crate_attrs_from_file<'a>(input: &Path, sess: &'a ParseSess)
129                                        -> PResult<'a, Vec<ast::Attribute>> {
130     let mut parser = new_parser_from_file(sess, input);
131     parser.parse_inner_attributes()
132 }
133
134 pub fn parse_crate_from_source_str(name: FileName, source: String, sess: &ParseSess)
135                                        -> PResult<'_, ast::Crate> {
136     new_parser_from_source_str(sess, name, source).parse_crate_mod()
137 }
138
139 pub fn parse_crate_attrs_from_source_str(name: FileName, source: String, sess: &ParseSess)
140                                              -> PResult<'_, Vec<ast::Attribute>> {
141     new_parser_from_source_str(sess, name, source).parse_inner_attributes()
142 }
143
144 pub fn parse_stream_from_source_str(
145     name: FileName,
146     source: String,
147     sess: &ParseSess,
148     override_span: Option<Span>,
149 ) -> TokenStream {
150     let (stream, mut errors) = source_file_to_stream(
151         sess,
152         sess.source_map().new_source_file(name, source),
153         override_span,
154     );
155     emit_unclosed_delims(&mut errors, &sess.span_diagnostic);
156     stream
157 }
158
159 /// Creates a new parser from a source string.
160 pub fn new_parser_from_source_str(sess: &ParseSess, name: FileName, source: String) -> Parser<'_> {
161     panictry_buffer!(&sess.span_diagnostic, maybe_new_parser_from_source_str(sess, name, source))
162 }
163
164 /// Creates a new parser from a source string. Returns any buffered errors from lexing the initial
165 /// token stream.
166 pub fn maybe_new_parser_from_source_str(sess: &ParseSess, name: FileName, source: String)
167     -> Result<Parser<'_>, Vec<Diagnostic>>
168 {
169     let mut parser = maybe_source_file_to_parser(sess,
170                                                  sess.source_map().new_source_file(name, source))?;
171     parser.recurse_into_file_modules = false;
172     Ok(parser)
173 }
174
175 /// Creates a new parser, handling errors as appropriate
176 /// if the file doesn't exist
177 pub fn new_parser_from_file<'a>(sess: &'a ParseSess, path: &Path) -> Parser<'a> {
178     source_file_to_parser(sess, file_to_source_file(sess, path, None))
179 }
180
181 /// Creates a new parser, returning buffered diagnostics if the file doesn't
182 /// exist or from lexing the initial token stream.
183 pub fn maybe_new_parser_from_file<'a>(sess: &'a ParseSess, path: &Path)
184     -> Result<Parser<'a>, Vec<Diagnostic>> {
185     let file = try_file_to_source_file(sess, path, None).map_err(|db| vec![db])?;
186     maybe_source_file_to_parser(sess, file)
187 }
188
189 /// Given a session, a crate config, a path, and a span, add
190 /// the file at the given path to the source_map, and return a parser.
191 /// On an error, use the given span as the source of the problem.
192 pub fn new_sub_parser_from_file<'a>(sess: &'a ParseSess,
193                                     path: &Path,
194                                     directory_ownership: DirectoryOwnership,
195                                     module_name: Option<String>,
196                                     sp: Span) -> Parser<'a> {
197     let mut p = source_file_to_parser(sess, file_to_source_file(sess, path, Some(sp)));
198     p.directory.ownership = directory_ownership;
199     p.root_module_name = module_name;
200     p
201 }
202
203 /// Given a source_file and config, return a parser
204 fn source_file_to_parser(sess: &ParseSess, source_file: Lrc<SourceFile>) -> Parser<'_> {
205     panictry_buffer!(&sess.span_diagnostic,
206                      maybe_source_file_to_parser(sess, source_file))
207 }
208
209 /// Given a source_file and config, return a parser. Returns any buffered errors from lexing the
210 /// initial token stream.
211 fn maybe_source_file_to_parser(
212     sess: &ParseSess,
213     source_file: Lrc<SourceFile>,
214 ) -> Result<Parser<'_>, Vec<Diagnostic>> {
215     let end_pos = source_file.end_pos;
216     let (stream, unclosed_delims) = maybe_file_to_stream(sess, source_file, None)?;
217     let mut parser = stream_to_parser(sess, stream);
218     parser.unclosed_delims = unclosed_delims;
219     if parser.token == token::Eof && parser.span.is_dummy() {
220         parser.span = Span::new(end_pos, end_pos, parser.span.ctxt());
221     }
222
223     Ok(parser)
224 }
225
226 // must preserve old name for now, because quote! from the *existing*
227 // compiler expands into it
228 pub fn new_parser_from_tts(sess: &ParseSess, tts: Vec<TokenTree>) -> Parser<'_> {
229     stream_to_parser(sess, tts.into_iter().collect())
230 }
231
232
233 // base abstractions
234
235 /// Given a session and a path and an optional span (for error reporting),
236 /// add the path to the session's source_map and return the new source_file or
237 /// error when a file can't be read.
238 fn try_file_to_source_file(sess: &ParseSess, path: &Path, spanopt: Option<Span>)
239                    -> Result<Lrc<SourceFile>, Diagnostic> {
240     sess.source_map().load_file(path)
241     .map_err(|e| {
242         let msg = format!("couldn't read {}: {}", path.display(), e);
243         let mut diag = Diagnostic::new(Level::Fatal, &msg);
244         if let Some(sp) = spanopt {
245             diag.set_span(sp);
246         }
247         diag
248     })
249 }
250
251 /// Given a session and a path and an optional span (for error reporting),
252 /// add the path to the session's `source_map` and return the new `source_file`.
253 fn file_to_source_file(sess: &ParseSess, path: &Path, spanopt: Option<Span>)
254                    -> Lrc<SourceFile> {
255     match try_file_to_source_file(sess, path, spanopt) {
256         Ok(source_file) => source_file,
257         Err(d) => {
258             DiagnosticBuilder::new_diagnostic(&sess.span_diagnostic, d).emit();
259             FatalError.raise();
260         }
261     }
262 }
263
264 /// Given a source_file, produces a sequence of token trees.
265 pub fn source_file_to_stream(
266     sess: &ParseSess,
267     source_file: Lrc<SourceFile>,
268     override_span: Option<Span>,
269 ) -> (TokenStream, Vec<lexer::UnmatchedBrace>) {
270     panictry_buffer!(&sess.span_diagnostic, maybe_file_to_stream(sess, source_file, override_span))
271 }
272
273 /// Given a source file, produces a sequence of token trees. Returns any buffered errors from
274 /// parsing the token tream.
275 pub fn maybe_file_to_stream(
276     sess: &ParseSess,
277     source_file: Lrc<SourceFile>,
278     override_span: Option<Span>,
279 ) -> Result<(TokenStream, Vec<lexer::UnmatchedBrace>), Vec<Diagnostic>> {
280     let mut srdr = lexer::StringReader::new_or_buffered_errs(sess, source_file, override_span)?;
281     srdr.real_token();
282
283     match srdr.parse_all_token_trees() {
284         Ok(stream) => Ok((stream, srdr.unmatched_braces)),
285         Err(err) => {
286             let mut buffer = Vec::with_capacity(1);
287             err.buffer(&mut buffer);
288             // Not using `emit_unclosed_delims` to use `db.buffer`
289             for unmatched in srdr.unmatched_braces {
290                 let mut db = sess.span_diagnostic.struct_span_err(unmatched.found_span, &format!(
291                     "incorrect close delimiter: `{}`",
292                     token_to_string(&token::Token::CloseDelim(unmatched.found_delim)),
293                 ));
294                 db.span_label(unmatched.found_span, "incorrect close delimiter");
295                 if let Some(sp) = unmatched.candidate_span {
296                     db.span_label(sp, "close delimiter possibly meant for this");
297                 }
298                 if let Some(sp) = unmatched.unclosed_span {
299                     db.span_label(sp, "un-closed delimiter");
300                 }
301                 db.buffer(&mut buffer);
302             }
303             Err(buffer)
304         }
305     }
306 }
307
308 /// Given stream and the `ParseSess`, produces a parser.
309 pub fn stream_to_parser(sess: &ParseSess, stream: TokenStream) -> Parser<'_> {
310     Parser::new(sess, stream, None, true, false)
311 }
312
313 /// Parses a string representing a raw string literal into its final form. The
314 /// only operation this does is convert embedded CRLF into a single LF.
315 fn raw_str_lit(lit: &str) -> String {
316     debug!("raw_str_lit: given {}", lit.escape_default());
317     let mut res = String::with_capacity(lit.len());
318
319     let mut chars = lit.chars().peekable();
320     while let Some(c) = chars.next() {
321         if c == '\r' {
322             if *chars.peek().unwrap() != '\n' {
323                 panic!("lexer accepted bare CR");
324             }
325             chars.next();
326             res.push('\n');
327         } else {
328             res.push(c);
329         }
330     }
331
332     res.shrink_to_fit();
333     res
334 }
335
336 // check if `s` looks like i32 or u1234 etc.
337 fn looks_like_width_suffix(first_chars: &[char], s: &str) -> bool {
338     s.starts_with(first_chars) && s[1..].chars().all(|c| c.is_ascii_digit())
339 }
340
341 macro_rules! err {
342     ($opt_diag:expr, |$span:ident, $diag:ident| $($body:tt)*) => {
343         match $opt_diag {
344             Some(($span, $diag)) => { $($body)* }
345             None => return None,
346         }
347     }
348 }
349
350 crate fn lit_token(lit: token::Lit, suf: Option<Symbol>, diag: Option<(Span, &Handler)>)
351                  -> (bool /* suffix illegal? */, Option<ast::LitKind>) {
352     use ast::LitKind;
353
354     match lit {
355         token::Byte(i) => {
356             let lit_kind = match unescape_byte(&i.as_str()) {
357                 Ok(c) => LitKind::Byte(c),
358                 Err(_) => LitKind::Err(i),
359             };
360             (true, Some(lit_kind))
361         },
362         token::Char(i) => {
363             let lit_kind = match unescape_char(&i.as_str()) {
364                 Ok(c) => LitKind::Char(c),
365                 Err(_) => LitKind::Err(i),
366             };
367             (true, Some(lit_kind))
368         },
369         token::Err(i) => (true, Some(LitKind::Err(i))),
370
371         // There are some valid suffixes for integer and float literals,
372         // so all the handling is done internally.
373         token::Integer(s) => (false, integer_lit(&s.as_str(), suf, diag)),
374         token::Float(s) => (false, float_lit(&s.as_str(), suf, diag)),
375
376         token::Str_(mut sym) => {
377             // If there are no characters requiring special treatment we can
378             // reuse the symbol from the Token. Otherwise, we must generate a
379             // new symbol because the string in the LitKind is different to the
380             // string in the Token.
381             let mut has_error = false;
382             let s = &sym.as_str();
383             if s.as_bytes().iter().any(|&c| c == b'\\' || c == b'\r') {
384                 let mut buf = String::with_capacity(s.len());
385                 unescape_str(s, &mut |_, unescaped_char| {
386                     match unescaped_char {
387                         Ok(c) => buf.push(c),
388                         Err(_) => has_error = true,
389                     }
390                 });
391                 if has_error {
392                     return (true, Some(LitKind::Err(sym)));
393                 }
394                 sym = Symbol::intern(&buf)
395             }
396
397             (true, Some(LitKind::Str(sym, ast::StrStyle::Cooked)))
398         }
399         token::StrRaw(mut sym, n) => {
400             // Ditto.
401             let s = &sym.as_str();
402             if s.contains('\r') {
403                 sym = Symbol::intern(&raw_str_lit(s));
404             }
405             (true, Some(LitKind::Str(sym, ast::StrStyle::Raw(n))))
406         }
407         token::ByteStr(i) => {
408             let s = &i.as_str();
409             let mut buf = Vec::with_capacity(s.len());
410             let mut has_error = false;
411             unescape_byte_str(s, &mut |_, unescaped_byte| {
412                 match unescaped_byte {
413                     Ok(c) => buf.push(c),
414                     Err(_) => has_error = true,
415                 }
416             });
417             if has_error {
418                 return (true, Some(LitKind::Err(i)));
419             }
420             buf.shrink_to_fit();
421             (true, Some(LitKind::ByteStr(Lrc::new(buf))))
422         }
423         token::ByteStrRaw(i, _) => {
424             (true, Some(LitKind::ByteStr(Lrc::new(i.to_string().into_bytes()))))
425         }
426     }
427 }
428
429 fn filtered_float_lit(data: Symbol, suffix: Option<Symbol>, diag: Option<(Span, &Handler)>)
430                       -> Option<ast::LitKind> {
431     debug!("filtered_float_lit: {}, {:?}", data, suffix);
432     let suffix = match suffix {
433         Some(suffix) => suffix,
434         None => return Some(ast::LitKind::FloatUnsuffixed(data)),
435     };
436
437     Some(match &*suffix.as_str() {
438         "f32" => ast::LitKind::Float(data, ast::FloatTy::F32),
439         "f64" => ast::LitKind::Float(data, ast::FloatTy::F64),
440         suf => {
441             err!(diag, |span, diag| {
442                 if suf.len() >= 2 && looks_like_width_suffix(&['f'], suf) {
443                     // if it looks like a width, lets try to be helpful.
444                     let msg = format!("invalid width `{}` for float literal", &suf[1..]);
445                     diag.struct_span_err(span, &msg).help("valid widths are 32 and 64").emit()
446                 } else {
447                     let msg = format!("invalid suffix `{}` for float literal", suf);
448                     diag.struct_span_err(span, &msg)
449                         .span_label(span, format!("invalid suffix `{}`", suf))
450                         .help("valid suffixes are `f32` and `f64`")
451                         .emit();
452                 }
453             });
454
455             ast::LitKind::FloatUnsuffixed(data)
456         }
457     })
458 }
459 fn float_lit(s: &str, suffix: Option<Symbol>, diag: Option<(Span, &Handler)>)
460                  -> Option<ast::LitKind> {
461     debug!("float_lit: {:?}, {:?}", s, suffix);
462     // FIXME #2252: bounds checking float literals is deferred until trans
463
464     // Strip underscores without allocating a new String unless necessary.
465     let s2;
466     let s = if s.chars().any(|c| c == '_') {
467         s2 = s.chars().filter(|&c| c != '_').collect::<String>();
468         &s2
469     } else {
470         s
471     };
472
473     filtered_float_lit(Symbol::intern(s), suffix, diag)
474 }
475
476 fn integer_lit(s: &str, suffix: Option<Symbol>, diag: Option<(Span, &Handler)>)
477                    -> Option<ast::LitKind> {
478     // s can only be ascii, byte indexing is fine
479
480     // Strip underscores without allocating a new String unless necessary.
481     let s2;
482     let mut s = if s.chars().any(|c| c == '_') {
483         s2 = s.chars().filter(|&c| c != '_').collect::<String>();
484         &s2
485     } else {
486         s
487     };
488
489     debug!("integer_lit: {}, {:?}", s, suffix);
490
491     let mut base = 10;
492     let orig = s;
493     let mut ty = ast::LitIntType::Unsuffixed;
494
495     if s.starts_with('0') && s.len() > 1 {
496         match s.as_bytes()[1] {
497             b'x' => base = 16,
498             b'o' => base = 8,
499             b'b' => base = 2,
500             _ => { }
501         }
502     }
503
504     // 1f64 and 2f32 etc. are valid float literals.
505     if let Some(suf) = suffix {
506         if looks_like_width_suffix(&['f'], &suf.as_str()) {
507             let err = match base {
508                 16 => Some("hexadecimal float literal is not supported"),
509                 8 => Some("octal float literal is not supported"),
510                 2 => Some("binary float literal is not supported"),
511                 _ => None,
512             };
513             if let Some(err) = err {
514                 err!(diag, |span, diag| {
515                     diag.struct_span_err(span, err)
516                         .span_label(span, "not supported")
517                         .emit();
518                 });
519             }
520             return filtered_float_lit(Symbol::intern(s), Some(suf), diag)
521         }
522     }
523
524     if base != 10 {
525         s = &s[2..];
526     }
527
528     if let Some(suf) = suffix {
529         if suf.as_str().is_empty() {
530             err!(diag, |span, diag| diag.span_bug(span, "found empty literal suffix in Some"));
531         }
532         ty = match &*suf.as_str() {
533             "isize" => ast::LitIntType::Signed(ast::IntTy::Isize),
534             "i8"  => ast::LitIntType::Signed(ast::IntTy::I8),
535             "i16" => ast::LitIntType::Signed(ast::IntTy::I16),
536             "i32" => ast::LitIntType::Signed(ast::IntTy::I32),
537             "i64" => ast::LitIntType::Signed(ast::IntTy::I64),
538             "i128" => ast::LitIntType::Signed(ast::IntTy::I128),
539             "usize" => ast::LitIntType::Unsigned(ast::UintTy::Usize),
540             "u8"  => ast::LitIntType::Unsigned(ast::UintTy::U8),
541             "u16" => ast::LitIntType::Unsigned(ast::UintTy::U16),
542             "u32" => ast::LitIntType::Unsigned(ast::UintTy::U32),
543             "u64" => ast::LitIntType::Unsigned(ast::UintTy::U64),
544             "u128" => ast::LitIntType::Unsigned(ast::UintTy::U128),
545             suf => {
546                 // i<digits> and u<digits> look like widths, so lets
547                 // give an error message along those lines
548                 err!(diag, |span, diag| {
549                     if looks_like_width_suffix(&['i', 'u'], suf) {
550                         let msg = format!("invalid width `{}` for integer literal", &suf[1..]);
551                         diag.struct_span_err(span, &msg)
552                             .help("valid widths are 8, 16, 32, 64 and 128")
553                             .emit();
554                     } else {
555                         let msg = format!("invalid suffix `{}` for numeric literal", suf);
556                         diag.struct_span_err(span, &msg)
557                             .span_label(span, format!("invalid suffix `{}`", suf))
558                             .help("the suffix must be one of the integral types \
559                                    (`u32`, `isize`, etc)")
560                             .emit();
561                     }
562                 });
563
564                 ty
565             }
566         }
567     }
568
569     debug!("integer_lit: the type is {:?}, base {:?}, the new string is {:?}, the original \
570            string was {:?}, the original suffix was {:?}", ty, base, s, orig, suffix);
571
572     Some(match u128::from_str_radix(s, base) {
573         Ok(r) => ast::LitKind::Int(r, ty),
574         Err(_) => {
575             // small bases are lexed as if they were base 10, e.g, the string
576             // might be `0b10201`. This will cause the conversion above to fail,
577             // but these cases have errors in the lexer: we don't want to emit
578             // two errors, and we especially don't want to emit this error since
579             // it isn't necessarily true.
580             let already_errored = base < 10 &&
581                 s.chars().any(|c| c.to_digit(10).map_or(false, |d| d >= base));
582
583             if !already_errored {
584                 err!(diag, |span, diag| diag.span_err(span, "int literal is too large"));
585             }
586             ast::LitKind::Int(0, ty)
587         }
588     })
589 }
590
591 /// A sequence separator.
592 pub struct SeqSep {
593     /// The seperator token.
594     pub sep: Option<token::Token>,
595     /// `true` if a trailing separator is allowed.
596     pub trailing_sep_allowed: bool,
597 }
598
599 impl SeqSep {
600     pub fn trailing_allowed(t: token::Token) -> SeqSep {
601         SeqSep {
602             sep: Some(t),
603             trailing_sep_allowed: true,
604         }
605     }
606
607     pub fn none() -> SeqSep {
608         SeqSep {
609             sep: None,
610             trailing_sep_allowed: false,
611         }
612     }
613 }
614
615 #[cfg(test)]
616 mod tests {
617     use super::*;
618     use crate::ast::{self, Ident, PatKind};
619     use crate::attr::first_attr_value_str_by_name;
620     use crate::ptr::P;
621     use crate::print::pprust::item_to_string;
622     use crate::tokenstream::{DelimSpan, TokenTree};
623     use crate::util::parser_testing::string_to_stream;
624     use crate::util::parser_testing::{string_to_expr, string_to_item};
625     use crate::with_globals;
626     use syntax_pos::{Span, BytePos, Pos, NO_EXPANSION};
627
628     /// Parses an item.
629     ///
630     /// Returns `Ok(Some(item))` when successful, `Ok(None)` when no item was found, and `Err`
631     /// when a syntax error occurred.
632     fn parse_item_from_source_str(name: FileName, source: String, sess: &ParseSess)
633                                         -> PResult<'_, Option<P<ast::Item>>> {
634         new_parser_from_source_str(sess, name, source).parse_item()
635     }
636
637     // produce a syntax_pos::span
638     fn sp(a: u32, b: u32) -> Span {
639         Span::new(BytePos(a), BytePos(b), NO_EXPANSION)
640     }
641
642     #[should_panic]
643     #[test] fn bad_path_expr_1() {
644         with_globals(|| {
645             string_to_expr("::abc::def::return".to_string());
646         })
647     }
648
649     // check the token-tree-ization of macros
650     #[test]
651     fn string_to_tts_macro () {
652         with_globals(|| {
653             let tts: Vec<_> =
654                 string_to_stream("macro_rules! zip (($a)=>($a))".to_string()).trees().collect();
655             let tts: &[TokenTree] = &tts[..];
656
657             match (tts.len(), tts.get(0), tts.get(1), tts.get(2), tts.get(3)) {
658                 (
659                     4,
660                     Some(&TokenTree::Token(_, token::Ident(name_macro_rules, false))),
661                     Some(&TokenTree::Token(_, token::Not)),
662                     Some(&TokenTree::Token(_, token::Ident(name_zip, false))),
663                     Some(&TokenTree::Delimited(_, macro_delim, ref macro_tts)),
664                 )
665                 if name_macro_rules.name == "macro_rules"
666                 && name_zip.name == "zip" => {
667                     let tts = &macro_tts.trees().collect::<Vec<_>>();
668                     match (tts.len(), tts.get(0), tts.get(1), tts.get(2)) {
669                         (
670                             3,
671                             Some(&TokenTree::Delimited(_, first_delim, ref first_tts)),
672                             Some(&TokenTree::Token(_, token::FatArrow)),
673                             Some(&TokenTree::Delimited(_, second_delim, ref second_tts)),
674                         )
675                         if macro_delim == token::Paren => {
676                             let tts = &first_tts.trees().collect::<Vec<_>>();
677                             match (tts.len(), tts.get(0), tts.get(1)) {
678                                 (
679                                     2,
680                                     Some(&TokenTree::Token(_, token::Dollar)),
681                                     Some(&TokenTree::Token(_, token::Ident(ident, false))),
682                                 )
683                                 if first_delim == token::Paren && ident.name == "a" => {},
684                                 _ => panic!("value 3: {:?} {:?}", first_delim, first_tts),
685                             }
686                             let tts = &second_tts.trees().collect::<Vec<_>>();
687                             match (tts.len(), tts.get(0), tts.get(1)) {
688                                 (
689                                     2,
690                                     Some(&TokenTree::Token(_, token::Dollar)),
691                                     Some(&TokenTree::Token(_, token::Ident(ident, false))),
692                                 )
693                                 if second_delim == token::Paren && ident.name == "a" => {},
694                                 _ => panic!("value 4: {:?} {:?}", second_delim, second_tts),
695                             }
696                         },
697                         _ => panic!("value 2: {:?} {:?}", macro_delim, macro_tts),
698                     }
699                 },
700                 _ => panic!("value: {:?}",tts),
701             }
702         })
703     }
704
705     #[test]
706     fn string_to_tts_1() {
707         with_globals(|| {
708             let tts = string_to_stream("fn a (b : i32) { b; }".to_string());
709
710             let expected = TokenStream::new(vec![
711                 TokenTree::Token(sp(0, 2), token::Ident(Ident::from_str("fn"), false)).into(),
712                 TokenTree::Token(sp(3, 4), token::Ident(Ident::from_str("a"), false)).into(),
713                 TokenTree::Delimited(
714                     DelimSpan::from_pair(sp(5, 6), sp(13, 14)),
715                     token::DelimToken::Paren,
716                     TokenStream::new(vec![
717                         TokenTree::Token(sp(6, 7),
718                                          token::Ident(Ident::from_str("b"), false)).into(),
719                         TokenTree::Token(sp(8, 9), token::Colon).into(),
720                         TokenTree::Token(sp(10, 13),
721                                          token::Ident(Ident::from_str("i32"), false)).into(),
722                     ]).into(),
723                 ).into(),
724                 TokenTree::Delimited(
725                     DelimSpan::from_pair(sp(15, 16), sp(20, 21)),
726                     token::DelimToken::Brace,
727                     TokenStream::new(vec![
728                         TokenTree::Token(sp(17, 18),
729                                          token::Ident(Ident::from_str("b"), false)).into(),
730                         TokenTree::Token(sp(18, 19), token::Semi).into(),
731                     ]).into(),
732                 ).into()
733             ]);
734
735             assert_eq!(tts, expected);
736         })
737     }
738
739     #[test] fn parse_use() {
740         with_globals(|| {
741             let use_s = "use foo::bar::baz;";
742             let vitem = string_to_item(use_s.to_string()).unwrap();
743             let vitem_s = item_to_string(&vitem);
744             assert_eq!(&vitem_s[..], use_s);
745
746             let use_s = "use foo::bar as baz;";
747             let vitem = string_to_item(use_s.to_string()).unwrap();
748             let vitem_s = item_to_string(&vitem);
749             assert_eq!(&vitem_s[..], use_s);
750         })
751     }
752
753     #[test] fn parse_extern_crate() {
754         with_globals(|| {
755             let ex_s = "extern crate foo;";
756             let vitem = string_to_item(ex_s.to_string()).unwrap();
757             let vitem_s = item_to_string(&vitem);
758             assert_eq!(&vitem_s[..], ex_s);
759
760             let ex_s = "extern crate foo as bar;";
761             let vitem = string_to_item(ex_s.to_string()).unwrap();
762             let vitem_s = item_to_string(&vitem);
763             assert_eq!(&vitem_s[..], ex_s);
764         })
765     }
766
767     fn get_spans_of_pat_idents(src: &str) -> Vec<Span> {
768         let item = string_to_item(src.to_string()).unwrap();
769
770         struct PatIdentVisitor {
771             spans: Vec<Span>
772         }
773         impl<'a> crate::visit::Visitor<'a> for PatIdentVisitor {
774             fn visit_pat(&mut self, p: &'a ast::Pat) {
775                 match p.node {
776                     PatKind::Ident(_ , ref spannedident, _) => {
777                         self.spans.push(spannedident.span.clone());
778                     }
779                     _ => {
780                         crate::visit::walk_pat(self, p);
781                     }
782                 }
783             }
784         }
785         let mut v = PatIdentVisitor { spans: Vec::new() };
786         crate::visit::walk_item(&mut v, &item);
787         return v.spans;
788     }
789
790     #[test] fn span_of_self_arg_pat_idents_are_correct() {
791         with_globals(|| {
792
793             let srcs = ["impl z { fn a (&self, &myarg: i32) {} }",
794                         "impl z { fn a (&mut self, &myarg: i32) {} }",
795                         "impl z { fn a (&'a self, &myarg: i32) {} }",
796                         "impl z { fn a (self, &myarg: i32) {} }",
797                         "impl z { fn a (self: Foo, &myarg: i32) {} }",
798                         ];
799
800             for &src in &srcs {
801                 let spans = get_spans_of_pat_idents(src);
802                 let (lo, hi) = (spans[0].lo(), spans[0].hi());
803                 assert!("self" == &src[lo.to_usize()..hi.to_usize()],
804                         "\"{}\" != \"self\". src=\"{}\"",
805                         &src[lo.to_usize()..hi.to_usize()], src)
806             }
807         })
808     }
809
810     #[test] fn parse_exprs () {
811         with_globals(|| {
812             // just make sure that they parse....
813             string_to_expr("3 + 4".to_string());
814             string_to_expr("a::z.froob(b,&(987+3))".to_string());
815         })
816     }
817
818     #[test] fn attrs_fix_bug () {
819         with_globals(|| {
820             string_to_item("pub fn mk_file_writer(path: &Path, flags: &[FileFlag])
821                    -> Result<Box<Writer>, String> {
822     #[cfg(windows)]
823     fn wb() -> c_int {
824       (O_WRONLY | libc::consts::os::extra::O_BINARY) as c_int
825     }
826
827     #[cfg(unix)]
828     fn wb() -> c_int { O_WRONLY as c_int }
829
830     let mut fflags: c_int = wb();
831 }".to_string());
832         })
833     }
834
835     #[test] fn crlf_doc_comments() {
836         with_globals(|| {
837             let sess = ParseSess::new(FilePathMapping::empty());
838
839             let name_1 = FileName::Custom("crlf_source_1".to_string());
840             let source = "/// doc comment\r\nfn foo() {}".to_string();
841             let item = parse_item_from_source_str(name_1, source, &sess)
842                 .unwrap().unwrap();
843             let doc = first_attr_value_str_by_name(&item.attrs, "doc").unwrap();
844             assert_eq!(doc, "/// doc comment");
845
846             let name_2 = FileName::Custom("crlf_source_2".to_string());
847             let source = "/// doc comment\r\n/// line 2\r\nfn foo() {}".to_string();
848             let item = parse_item_from_source_str(name_2, source, &sess)
849                 .unwrap().unwrap();
850             let docs = item.attrs.iter().filter(|a| a.path == "doc")
851                         .map(|a| a.value_str().unwrap().to_string()).collect::<Vec<_>>();
852             let b: &[_] = &["/// doc comment".to_string(), "/// line 2".to_string()];
853             assert_eq!(&docs[..], b);
854
855             let name_3 = FileName::Custom("clrf_source_3".to_string());
856             let source = "/** doc comment\r\n *  with CRLF */\r\nfn foo() {}".to_string();
857             let item = parse_item_from_source_str(name_3, source, &sess).unwrap().unwrap();
858             let doc = first_attr_value_str_by_name(&item.attrs, "doc").unwrap();
859             assert_eq!(doc, "/** doc comment\n *  with CRLF */");
860         });
861     }
862
863     #[test]
864     fn ttdelim_span() {
865         fn parse_expr_from_source_str(
866             name: FileName, source: String, sess: &ParseSess
867         ) -> PResult<'_, P<ast::Expr>> {
868             new_parser_from_source_str(sess, name, source).parse_expr()
869         }
870
871         with_globals(|| {
872             let sess = ParseSess::new(FilePathMapping::empty());
873             let expr = parse_expr_from_source_str(PathBuf::from("foo").into(),
874                 "foo!( fn main() { body } )".to_string(), &sess).unwrap();
875
876             let tts: Vec<_> = match expr.node {
877                 ast::ExprKind::Mac(ref mac) => mac.node.stream().trees().collect(),
878                 _ => panic!("not a macro"),
879             };
880
881             let span = tts.iter().rev().next().unwrap().span();
882
883             match sess.source_map().span_to_snippet(span) {
884                 Ok(s) => assert_eq!(&s[..], "{ body }"),
885                 Err(_) => panic!("could not get snippet"),
886             }
887         });
888     }
889
890     // This tests that when parsing a string (rather than a file) we don't try
891     // and read in a file for a module declaration and just parse a stub.
892     // See `recurse_into_file_modules` in the parser.
893     #[test]
894     fn out_of_line_mod() {
895         with_globals(|| {
896             let sess = ParseSess::new(FilePathMapping::empty());
897             let item = parse_item_from_source_str(
898                 PathBuf::from("foo").into(),
899                 "mod foo { struct S; mod this_does_not_exist; }".to_owned(),
900                 &sess,
901             ).unwrap().unwrap();
902
903             if let ast::ItemKind::Mod(ref m) = item.node {
904                 assert!(m.items.len() == 2);
905             } else {
906                 panic!();
907             }
908         });
909     }
910 }