1 // Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 use ast::{self, Ident};
12 use syntax_pos::{self, BytePos, CharPos, Pos, Span, NO_EXPANSION};
13 use codemap::{CodeMap, FilePathMapping};
14 use errors::{FatalError, DiagnosticBuilder};
15 use parse::{token, ParseSess};
17 use symbol::{Symbol, keywords};
18 use core::unicode::property::Pattern_White_Space;
22 use std::mem::replace;
23 use rustc_data_structures::sync::Lrc;
29 #[derive(Clone, PartialEq, Eq, Debug)]
30 pub struct TokenAndSpan {
31 pub tok: token::Token,
35 impl Default for TokenAndSpan {
36 fn default() -> Self {
37 TokenAndSpan { tok: token::Whitespace, sp: syntax_pos::DUMMY_SP }
41 pub struct StringReader<'a> {
42 pub sess: &'a ParseSess,
43 /// The absolute offset within the codemap of the next character to read
44 pub next_pos: BytePos,
45 /// The absolute offset within the codemap of the current character
47 /// The current character (which has been read from self.pos)
49 pub filemap: Lrc<syntax_pos::FileMap>,
50 /// Stop reading src at this index.
51 pub end_src_index: usize,
52 /// Whether to record new-lines and multibyte chars in filemap.
53 /// This is only necessary the first time a filemap is lexed.
54 /// If part of a filemap is being re-lexed, this should be set to false.
55 pub save_new_lines_and_multibyte: bool,
57 pub peek_tok: token::Token,
59 pub fatal_errs: Vec<DiagnosticBuilder<'a>>,
60 // cache a direct reference to the source text, so that we don't have to
61 // retrieve it via `self.filemap.src.as_ref().unwrap()` all the time.
63 /// Stack of open delimiters and their spans. Used for error message.
66 open_braces: Vec<(token::DelimToken, Span)>,
67 pub override_span: Option<Span>,
70 impl<'a> StringReader<'a> {
71 fn mk_sp(&self, lo: BytePos, hi: BytePos) -> Span {
72 unwrap_or!(self.override_span, Span::new(lo, hi, NO_EXPANSION))
74 fn mk_ident(&self, string: &str) -> Ident {
75 let mut ident = Ident::from_str(string);
76 if let Some(span) = self.override_span {
82 fn next_token(&mut self) -> TokenAndSpan where Self: Sized {
83 let res = self.try_next_token();
84 self.unwrap_or_abort(res)
86 fn unwrap_or_abort(&mut self, res: Result<TokenAndSpan, ()>) -> TokenAndSpan {
90 self.emit_fatal_errors();
95 fn try_real_token(&mut self) -> Result<TokenAndSpan, ()> {
96 let mut t = self.try_next_token()?;
99 token::Whitespace | token::Comment | token::Shebang(_) => {
100 t = self.try_next_token()?;
105 self.token = t.tok.clone();
109 pub fn real_token(&mut self) -> TokenAndSpan {
110 let res = self.try_real_token();
111 self.unwrap_or_abort(res)
113 fn is_eof(&self) -> bool {
116 /// Return the next token. EFFECT: advances the string_reader.
117 pub fn try_next_token(&mut self) -> Result<TokenAndSpan, ()> {
118 assert!(self.fatal_errs.is_empty());
119 let ret_val = TokenAndSpan {
120 tok: replace(&mut self.peek_tok, token::Whitespace),
123 self.advance_token()?;
127 fn fail_unterminated_raw_string(&self, pos: BytePos, hash_count: u16) {
128 let mut err = self.struct_span_fatal(pos, pos, "unterminated raw string");
129 err.span_label(self.mk_sp(pos, pos), "unterminated raw string");
131 err.note(&format!("this raw string should be terminated with `\"{}`",
132 "#".repeat(hash_count as usize)));
138 fn fatal(&self, m: &str) -> FatalError {
139 self.fatal_span(self.peek_span, m)
141 pub fn emit_fatal_errors(&mut self) {
142 for err in &mut self.fatal_errs {
145 self.fatal_errs.clear();
147 pub fn peek(&self) -> TokenAndSpan {
148 // FIXME(pcwalton): Bad copy!
150 tok: self.peek_tok.clone(),
156 impl<'a> StringReader<'a> {
157 /// For comments.rs, which hackily pokes into next_pos and ch
158 pub fn new_raw(sess: &'a ParseSess, filemap: Lrc<syntax_pos::FileMap>) -> Self {
159 let mut sr = StringReader::new_raw_internal(sess, filemap);
164 fn new_raw_internal(sess: &'a ParseSess, filemap: Lrc<syntax_pos::FileMap>) -> Self {
165 if filemap.src.is_none() {
166 sess.span_diagnostic.bug(&format!("Cannot lex filemap without source: {}",
170 let src = (*filemap.src.as_ref().unwrap()).clone();
174 next_pos: filemap.start_pos,
175 pos: filemap.start_pos,
178 end_src_index: src.len(),
179 save_new_lines_and_multibyte: true,
180 // dummy values; not read
181 peek_tok: token::Eof,
182 peek_span: syntax_pos::DUMMY_SP,
184 fatal_errs: Vec::new(),
186 span: syntax_pos::DUMMY_SP,
187 open_braces: Vec::new(),
192 pub fn new(sess: &'a ParseSess, filemap: Lrc<syntax_pos::FileMap>) -> Self {
193 let mut sr = StringReader::new_raw(sess, filemap);
194 if sr.advance_token().is_err() {
195 sr.emit_fatal_errors();
201 pub fn retokenize(sess: &'a ParseSess, mut span: Span) -> Self {
202 let begin = sess.codemap().lookup_byte_offset(span.lo());
203 let end = sess.codemap().lookup_byte_offset(span.hi());
205 // Make the range zero-length if the span is invalid.
206 if span.lo() > span.hi() || begin.fm.start_pos != end.fm.start_pos {
207 span = span.shrink_to_lo();
210 let mut sr = StringReader::new_raw_internal(sess, begin.fm);
212 // Seek the lexer to the right byte range.
213 sr.save_new_lines_and_multibyte = false;
214 sr.next_pos = span.lo();
215 sr.end_src_index = sr.src_index(span.hi());
219 if sr.advance_token().is_err() {
220 sr.emit_fatal_errors();
226 pub fn ch_is(&self, c: char) -> bool {
230 /// Report a fatal lexical error with a given span.
231 pub fn fatal_span(&self, sp: Span, m: &str) -> FatalError {
232 self.sess.span_diagnostic.span_fatal(sp, m)
235 /// Report a lexical error with a given span.
236 pub fn err_span(&self, sp: Span, m: &str) {
237 self.sess.span_diagnostic.span_err(sp, m)
241 /// Report a fatal error spanning [`from_pos`, `to_pos`).
242 fn fatal_span_(&self, from_pos: BytePos, to_pos: BytePos, m: &str) -> FatalError {
243 self.fatal_span(self.mk_sp(from_pos, to_pos), m)
246 /// Report a lexical error spanning [`from_pos`, `to_pos`).
247 fn err_span_(&self, from_pos: BytePos, to_pos: BytePos, m: &str) {
248 self.err_span(self.mk_sp(from_pos, to_pos), m)
251 /// Pushes a character to a message string for error reporting
252 fn push_escaped_char_for_msg(m: &mut String, c: char) {
254 '\u{20}'...'\u{7e}' => {
255 // Don't escape \, ' or " for user-facing messages
259 for c in c.escape_default() {
266 /// Report a lexical error spanning [`from_pos`, `to_pos`), appending an
267 /// escaped character to the error message
268 fn fatal_span_char(&self, from_pos: BytePos, to_pos: BytePos, m: &str, c: char) -> FatalError {
269 let mut m = m.to_string();
271 Self::push_escaped_char_for_msg(&mut m, c);
272 self.fatal_span_(from_pos, to_pos, &m[..])
275 fn struct_span_fatal(&self,
279 -> DiagnosticBuilder<'a> {
280 self.sess.span_diagnostic.struct_span_fatal(self.mk_sp(from_pos, to_pos), m)
283 fn struct_fatal_span_char(&self,
288 -> DiagnosticBuilder<'a> {
289 let mut m = m.to_string();
291 Self::push_escaped_char_for_msg(&mut m, c);
292 self.sess.span_diagnostic.struct_span_fatal(self.mk_sp(from_pos, to_pos), &m[..])
295 /// Report a lexical error spanning [`from_pos`, `to_pos`), appending an
296 /// escaped character to the error message
297 fn err_span_char(&self, from_pos: BytePos, to_pos: BytePos, m: &str, c: char) {
298 let mut m = m.to_string();
300 Self::push_escaped_char_for_msg(&mut m, c);
301 self.err_span_(from_pos, to_pos, &m[..]);
303 fn struct_err_span_char(&self,
308 -> DiagnosticBuilder<'a> {
309 let mut m = m.to_string();
311 Self::push_escaped_char_for_msg(&mut m, c);
312 self.sess.span_diagnostic.struct_span_err(self.mk_sp(from_pos, to_pos), &m[..])
315 /// Report a lexical error spanning [`from_pos`, `to_pos`), appending the
316 /// offending string to the error message
317 fn fatal_span_verbose(&self, from_pos: BytePos, to_pos: BytePos, mut m: String) -> FatalError {
319 m.push_str(&self.src[self.src_index(from_pos)..self.src_index(to_pos)]);
320 self.fatal_span_(from_pos, to_pos, &m[..])
323 /// Advance peek_tok and peek_span to refer to the next token, and
324 /// possibly update the interner.
325 fn advance_token(&mut self) -> Result<(), ()> {
326 match self.scan_whitespace_or_comment() {
328 self.peek_span = comment.sp;
329 self.peek_tok = comment.tok;
333 self.peek_tok = token::Eof;
334 self.peek_span = self.mk_sp(self.filemap.end_pos, self.filemap.end_pos);
336 let start_bytepos = self.pos;
337 self.peek_tok = self.next_token_inner()?;
338 self.peek_span = self.mk_sp(start_bytepos, self.pos);
346 fn src_index(&self, pos: BytePos) -> usize {
347 (pos - self.filemap.start_pos).to_usize()
350 /// Calls `f` with a string slice of the source text spanning from `start`
351 /// up to but excluding `self.pos`, meaning the slice does not include
352 /// the character `self.ch`.
353 pub fn with_str_from<T, F>(&self, start: BytePos, f: F) -> T
354 where F: FnOnce(&str) -> T
356 self.with_str_from_to(start, self.pos, f)
359 /// Create a Name from a given offset to the current offset, each
360 /// adjusted 1 towards each other (assumes that on either side there is a
361 /// single-byte delimiter).
362 pub fn name_from(&self, start: BytePos) -> ast::Name {
363 debug!("taking an ident from {:?} to {:?}", start, self.pos);
364 self.with_str_from(start, Symbol::intern)
367 /// As name_from, with an explicit endpoint.
368 pub fn name_from_to(&self, start: BytePos, end: BytePos) -> ast::Name {
369 debug!("taking an ident from {:?} to {:?}", start, end);
370 self.with_str_from_to(start, end, Symbol::intern)
373 /// Calls `f` with a string slice of the source text spanning from `start`
374 /// up to but excluding `end`.
375 fn with_str_from_to<T, F>(&self, start: BytePos, end: BytePos, f: F) -> T
376 where F: FnOnce(&str) -> T
378 f(&self.src[self.src_index(start)..self.src_index(end)])
381 /// Converts CRLF to LF in the given string, raising an error on bare CR.
382 fn translate_crlf<'b>(&self, start: BytePos, s: &'b str, errmsg: &'b str) -> Cow<'b, str> {
385 let ch = char_at(s, i);
386 let next = i + ch.len_utf8();
388 if next < s.len() && char_at(s, next) == '\n' {
389 return translate_crlf_(self, start, s, errmsg, i).into();
391 let pos = start + BytePos(i as u32);
392 let end_pos = start + BytePos(next as u32);
393 self.err_span_(pos, end_pos, errmsg);
399 fn translate_crlf_(rdr: &StringReader,
405 let mut buf = String::with_capacity(s.len());
408 let ch = char_at(s, i);
409 let next = i + ch.len_utf8();
412 buf.push_str(&s[j..i]);
415 if next >= s.len() || char_at(s, next) != '\n' {
416 let pos = start + BytePos(i as u32);
417 let end_pos = start + BytePos(next as u32);
418 rdr.err_span_(pos, end_pos, errmsg);
424 buf.push_str(&s[j..]);
430 /// Advance the StringReader by one character. If a newline is
431 /// discovered, add it to the FileMap's list of line start offsets.
432 pub fn bump(&mut self) {
433 let next_src_index = self.src_index(self.next_pos);
434 if next_src_index < self.end_src_index {
435 let next_ch = char_at(&self.src, next_src_index);
436 let next_ch_len = next_ch.len_utf8();
438 if self.ch.unwrap() == '\n' {
439 if self.save_new_lines_and_multibyte {
440 self.filemap.next_line(self.next_pos);
444 if self.save_new_lines_and_multibyte {
445 self.filemap.record_multibyte_char(self.next_pos, next_ch_len);
448 self.filemap.record_width(self.next_pos, next_ch);
450 self.ch = Some(next_ch);
451 self.pos = self.next_pos;
452 self.next_pos = self.next_pos + Pos::from_usize(next_ch_len);
455 self.pos = self.next_pos;
459 pub fn nextch(&self) -> Option<char> {
460 let next_src_index = self.src_index(self.next_pos);
461 if next_src_index < self.end_src_index {
462 Some(char_at(&self.src, next_src_index))
468 pub fn nextch_is(&self, c: char) -> bool {
469 self.nextch() == Some(c)
472 pub fn nextnextch(&self) -> Option<char> {
473 let next_src_index = self.src_index(self.next_pos);
474 if next_src_index < self.end_src_index {
475 let next_next_src_index =
476 next_src_index + char_at(&self.src, next_src_index).len_utf8();
477 if next_next_src_index < self.end_src_index {
478 return Some(char_at(&self.src, next_next_src_index));
484 pub fn nextnextch_is(&self, c: char) -> bool {
485 self.nextnextch() == Some(c)
488 /// Eats <XID_start><XID_continue>*, if possible.
489 fn scan_optional_raw_name(&mut self) -> Option<ast::Name> {
490 if !ident_start(self.ch) {
493 let start = self.pos;
494 while ident_continue(self.ch) {
498 self.with_str_from(start, |string| {
500 self.sess.span_diagnostic
501 .struct_span_warn(self.mk_sp(start, self.pos),
502 "underscore literal suffix is not allowed")
503 .warn("this was previously accepted by the compiler but is \
504 being phased out; it will become a hard error in \
506 .note("for more information, see issue #42326 \
507 <https://github.com/rust-lang/rust/issues/42326>")
511 Some(Symbol::intern(string))
516 /// PRECONDITION: self.ch is not whitespace
517 /// Eats any kind of comment.
518 fn scan_comment(&mut self) -> Option<TokenAndSpan> {
519 if let Some(c) = self.ch {
520 if c.is_whitespace() {
521 let msg = "called consume_any_line_comment, but there was whitespace";
522 self.sess.span_diagnostic.span_err(self.mk_sp(self.pos, self.pos), msg);
527 match self.nextch() {
532 // line comments starting with "///" or "//!" are doc-comments
533 let doc_comment = (self.ch_is('/') && !self.nextch_is('/')) || self.ch_is('!');
534 let start_bpos = self.pos - BytePos(2);
536 while !self.is_eof() {
537 match self.ch.unwrap() {
540 if self.nextch_is('\n') {
543 } else if doc_comment {
544 self.err_span_(self.pos,
546 "bare CR not allowed in doc-comment");
555 self.with_str_from(start_bpos, |string| {
556 // comments with only more "/"s are not doc comments
557 let tok = if is_doc_comment(string) {
558 token::DocComment(Symbol::intern(string))
565 sp: self.mk_sp(start_bpos, self.pos),
571 sp: self.mk_sp(start_bpos, self.pos),
578 self.scan_block_comment()
582 } else if self.ch_is('#') {
583 if self.nextch_is('!') {
585 // Parse an inner attribute.
586 if self.nextnextch_is('[') {
590 // I guess this is the only way to figure out if
591 // we're at the beginning of the file...
592 let cmap = CodeMap::new(FilePathMapping::empty());
593 cmap.files.borrow_mut().file_maps.push(self.filemap.clone());
594 let loc = cmap.lookup_char_pos_adj(self.pos);
595 debug!("Skipping a shebang");
596 if loc.line == 1 && loc.col == CharPos(0) {
597 // FIXME: Add shebang "token", return it
598 let start = self.pos;
599 while !self.ch_is('\n') && !self.is_eof() {
602 return Some(TokenAndSpan {
603 tok: token::Shebang(self.name_from(start)),
604 sp: self.mk_sp(start, self.pos),
614 /// If there is whitespace, shebang, or a comment, scan it. Otherwise,
616 fn scan_whitespace_or_comment(&mut self) -> Option<TokenAndSpan> {
617 match self.ch.unwrap_or('\0') {
618 // # to handle shebang at start of file -- this is the entry point
619 // for skipping over all "junk"
621 let c = self.scan_comment();
622 debug!("scanning a comment {:?}", c);
625 c if is_pattern_whitespace(Some(c)) => {
626 let start_bpos = self.pos;
627 while is_pattern_whitespace(self.ch) {
630 let c = Some(TokenAndSpan {
631 tok: token::Whitespace,
632 sp: self.mk_sp(start_bpos, self.pos),
634 debug!("scanning whitespace: {:?}", c);
641 /// Might return a sugared-doc-attr
642 fn scan_block_comment(&mut self) -> Option<TokenAndSpan> {
643 // block comments starting with "/**" or "/*!" are doc-comments
644 let is_doc_comment = self.ch_is('*') || self.ch_is('!');
645 let start_bpos = self.pos - BytePos(2);
647 let mut level: isize = 1;
648 let mut has_cr = false;
651 let msg = if is_doc_comment {
652 "unterminated block doc-comment"
654 "unterminated block comment"
656 let last_bpos = self.pos;
657 self.fatal_span_(start_bpos, last_bpos, msg).raise();
659 let n = self.ch.unwrap();
661 '/' if self.nextch_is('*') => {
665 '*' if self.nextch_is('/') => {
677 self.with_str_from(start_bpos, |string| {
678 // but comments with only "*"s between two "/"s are not
679 let tok = if is_block_doc_comment(string) {
680 let string = if has_cr {
681 self.translate_crlf(start_bpos,
683 "bare CR not allowed in block doc-comment")
687 token::DocComment(Symbol::intern(&string[..]))
694 sp: self.mk_sp(start_bpos, self.pos),
699 /// Scan through any digits (base `scan_radix`) or underscores,
700 /// and return how many digits there were.
702 /// `real_radix` represents the true radix of the number we're
703 /// interested in, and errors will be emitted for any digits
704 /// between `real_radix` and `scan_radix`.
705 fn scan_digits(&mut self, real_radix: u32, scan_radix: u32) -> usize {
706 assert!(real_radix <= scan_radix);
711 debug!("skipping a _");
715 match c.and_then(|cc| cc.to_digit(scan_radix)) {
717 debug!("{:?} in scan_digits", c);
718 // check that the hypothetical digit is actually
719 // in range for the true radix
720 if c.unwrap().to_digit(real_radix).is_none() {
721 self.err_span_(self.pos,
723 &format!("invalid digit for a base {} literal", real_radix));
733 /// Lex a LIT_INTEGER or a LIT_FLOAT
734 fn scan_number(&mut self, c: char) -> token::Lit {
737 let start_bpos = self.pos;
742 match self.ch.unwrap_or('\0') {
746 num_digits = self.scan_digits(2, 10);
751 num_digits = self.scan_digits(8, 10);
756 num_digits = self.scan_digits(16, 16);
758 '0'...'9' | '_' | '.' | 'e' | 'E' => {
759 num_digits = self.scan_digits(10, 10) + 1;
763 return token::Integer(self.name_from(start_bpos));
766 } else if c.is_digit(10) {
767 num_digits = self.scan_digits(10, 10) + 1;
773 self.err_span_(start_bpos,
775 "no valid digits found for number");
776 return token::Integer(Symbol::intern("0"));
779 // might be a float, but don't be greedy if this is actually an
780 // integer literal followed by field/method access or a range pattern
781 // (`0..2` and `12.foo()`)
782 if self.ch_is('.') && !self.nextch_is('.') &&
783 !ident_start(self.nextch()) {
784 // might have stuff after the ., and if it does, it needs to start
787 if self.ch.unwrap_or('\0').is_digit(10) {
788 self.scan_digits(10, 10);
789 self.scan_float_exponent();
792 self.check_float_base(start_bpos, pos, base);
793 token::Float(self.name_from(start_bpos))
795 // it might be a float if it has an exponent
796 if self.ch_is('e') || self.ch_is('E') {
797 self.scan_float_exponent();
799 self.check_float_base(start_bpos, pos, base);
800 return token::Float(self.name_from(start_bpos));
802 // but we certainly have an integer!
803 token::Integer(self.name_from(start_bpos))
807 /// Scan over `n_digits` hex digits, stopping at `delim`, reporting an
808 /// error if too many or too few digits are encountered.
809 fn scan_hex_digits(&mut self, n_digits: usize, delim: char, below_0x7f_only: bool) -> bool {
810 debug!("scanning {} digits until {:?}", n_digits, delim);
811 let start_bpos = self.pos;
812 let mut accum_int = 0;
814 let mut valid = true;
815 for _ in 0..n_digits {
817 let last_bpos = self.pos;
818 self.fatal_span_(start_bpos,
820 "unterminated numeric character escape").raise();
822 if self.ch_is(delim) {
823 let last_bpos = self.pos;
824 self.err_span_(start_bpos,
826 "numeric character escape is too short");
830 let c = self.ch.unwrap_or('\x00');
832 accum_int += c.to_digit(16).unwrap_or_else(|| {
833 self.err_span_char(self.pos,
835 "invalid character in numeric character escape",
844 if below_0x7f_only && accum_int >= 0x80 {
845 self.err_span_(start_bpos,
847 "this form of character escape may only be used with characters in \
848 the range [\\x00-\\x7f]");
852 match char::from_u32(accum_int) {
855 let last_bpos = self.pos;
856 self.err_span_(start_bpos, last_bpos, "invalid numeric character escape");
862 /// Scan for a single (possibly escaped) byte or char
863 /// in a byte, (non-raw) byte string, char, or (non-raw) string literal.
864 /// `start` is the position of `first_source_char`, which is already consumed.
866 /// Returns true if there was a valid char/byte, false otherwise.
867 fn scan_char_or_byte(&mut self,
869 first_source_char: char,
873 match first_source_char {
875 // '\X' for some X must be a character constant:
876 let escaped = self.ch;
877 let escaped_pos = self.pos;
880 None => {} // EOF here is an error that will be checked later.
883 'n' | 'r' | 't' | '\\' | '\'' | '"' | '0' => true,
884 'x' => self.scan_byte_escape(delim, !ascii_only),
886 let valid = if self.ch_is('{') {
887 self.scan_unicode_escape(delim) && !ascii_only
889 let span = self.mk_sp(start, self.pos);
890 self.sess.span_diagnostic
891 .struct_span_err(span, "incorrect unicode escape sequence")
893 "format of unicode escape sequences is \
899 self.err_span_(start,
901 "unicode escape sequences cannot be used as a \
902 byte or in a byte string");
907 '\n' if delim == '"' => {
908 self.consume_whitespace();
911 '\r' if delim == '"' && self.ch_is('\n') => {
912 self.consume_whitespace();
917 let mut err = self.struct_err_span_char(escaped_pos,
920 "unknown byte escape"
927 err.span_help(self.mk_sp(escaped_pos, pos),
928 "this is an isolated carriage return; consider \
929 checking your editor and version control \
932 if (e == '{' || e == '}') && !ascii_only {
933 err.span_help(self.mk_sp(escaped_pos, pos),
934 "if used in a formatting string, curly braces \
935 are escaped with `{{` and `}}`");
944 '\t' | '\n' | '\r' | '\'' if delim == '\'' => {
946 self.err_span_char(start,
949 "byte constant must be escaped"
951 "character constant must be escaped"
957 if self.ch_is('\n') {
961 self.err_span_(start,
963 "bare CR not allowed in string, use \\r instead");
968 if ascii_only && first_source_char > '\x7F' {
970 self.err_span_(start,
972 "byte constant must be ASCII. Use a \\xHH escape for a \
981 /// Scan over a `\u{...}` escape
983 /// At this point, we have already seen the `\` and the `u`, the `{` is the current character.
984 /// We will read a hex number (with `_` separators), with 1 to 6 actual digits,
985 /// and pass over the `}`.
986 fn scan_unicode_escape(&mut self, delim: char) -> bool {
987 self.bump(); // past the {
988 let start_bpos = self.pos;
989 let mut valid = true;
991 if let Some('_') = self.ch {
992 // disallow leading `_`
993 self.err_span_(self.pos,
995 "invalid start of unicode escape");
999 let count = self.scan_digits(16, 16);
1002 self.err_span_(start_bpos,
1004 "overlong unicode escape (must have at most 6 hex digits)");
1010 if valid && count == 0 {
1011 self.err_span_(start_bpos,
1013 "empty unicode escape (must have at least 1 hex digit)");
1016 self.bump(); // past the ending `}`
1021 self.err_span_(self.pos,
1023 "unterminated unicode escape (needed a `}`)");
1027 self.err_span_char(start_bpos,
1029 "invalid character in unicode escape",
1035 self.fatal_span_(start_bpos,
1037 "unterminated unicode escape (found EOF)").raise();
1045 /// Scan over a float exponent.
1046 fn scan_float_exponent(&mut self) {
1047 if self.ch_is('e') || self.ch_is('E') {
1049 if self.ch_is('-') || self.ch_is('+') {
1052 if self.scan_digits(10, 10) == 0 {
1053 self.err_span_(self.pos,
1055 "expected at least one digit in exponent")
1060 /// Check that a base is valid for a floating literal, emitting a nice
1061 /// error if it isn't.
1062 fn check_float_base(&mut self, start_bpos: BytePos, last_bpos: BytePos, base: usize) {
1065 self.err_span_(start_bpos,
1067 "hexadecimal float literal is not supported")
1070 self.err_span_(start_bpos,
1072 "octal float literal is not supported")
1075 self.err_span_(start_bpos,
1077 "binary float literal is not supported")
1083 fn binop(&mut self, op: token::BinOpToken) -> token::Token {
1085 if self.ch_is('=') {
1093 /// Return the next token from the string, advances the input past that
1094 /// token, and updates the interner
1095 fn next_token_inner(&mut self) -> Result<token::Token, ()> {
1099 let (is_ident_start, is_raw_ident) =
1100 match (c.unwrap(), self.nextch(), self.nextnextch()) {
1101 // r# followed by an identifier starter is a raw identifier.
1102 // This is an exception to the r# case below.
1103 ('r', Some('#'), x) if ident_start(x) => (true, true),
1104 // r as in r" or r#" is part of a raw string literal.
1105 // b as in b' is part of a byte literal.
1106 // They are not identifiers, and are handled further down.
1107 ('r', Some('"'), _) |
1108 ('r', Some('#'), _) |
1109 ('b', Some('"'), _) |
1110 ('b', Some('\''), _) |
1111 ('b', Some('r'), Some('"')) |
1112 ('b', Some('r'), Some('#')) => (false, false),
1116 let raw_start = self.pos;
1118 // Consume the 'r#' characters.
1123 let start = self.pos;
1124 while ident_continue(self.ch) {
1128 return Ok(self.with_str_from(start, |string| {
1129 // FIXME: perform NFKC normalization here. (Issue #2253)
1130 let ident = self.mk_ident(string);
1131 if is_raw_ident && (ident.is_path_segment_keyword() ||
1132 ident.name == keywords::Underscore.name()) {
1133 self.fatal_span_(raw_start, self.pos,
1134 &format!("`r#{}` is not currently supported.", ident.name)
1138 let span = self.mk_sp(raw_start, self.pos);
1139 self.sess.raw_identifier_spans.borrow_mut().push(span);
1141 token::Ident(ident, is_raw_ident)
1146 if is_dec_digit(c) {
1147 let num = self.scan_number(c.unwrap());
1148 let suffix = self.scan_optional_raw_name();
1149 debug!("next_token_inner: scanned number {:?}, {:?}", num, suffix);
1150 return Ok(token::Literal(num, suffix));
1153 match c.expect("next_token_inner called at EOF") {
1165 if self.ch_is('.') {
1167 if self.ch_is('.') {
1169 Ok(token::DotDotDot)
1170 } else if self.ch_is('=') {
1182 Ok(token::OpenDelim(token::Paren))
1186 Ok(token::CloseDelim(token::Paren))
1190 Ok(token::OpenDelim(token::Brace))
1194 Ok(token::CloseDelim(token::Brace))
1198 Ok(token::OpenDelim(token::Bracket))
1202 Ok(token::CloseDelim(token::Bracket))
1222 if self.ch_is(':') {
1235 // Multi-byte tokens.
1238 if self.ch_is('=') {
1241 } else if self.ch_is('>') {
1250 if self.ch_is('=') {
1259 match self.ch.unwrap_or('\x00') {
1265 Ok(self.binop(token::Shl))
1269 match self.ch.unwrap_or('\x00') {
1282 match self.ch.unwrap_or('\x00') {
1288 Ok(self.binop(token::Shr))
1296 // Either a character constant 'a' OR a lifetime name 'abc
1297 let start_with_quote = self.pos;
1299 let start = self.pos;
1301 // the eof will be picked up by the final `'` check below
1302 let c2 = self.ch.unwrap_or('\x00');
1305 // If the character is an ident start not followed by another single
1306 // quote, then this is a lifetime name:
1307 if ident_start(Some(c2)) && !self.ch_is('\'') {
1308 while ident_continue(self.ch) {
1311 // lifetimes shouldn't end with a single quote
1312 // if we find one, then this is an invalid character literal
1313 if self.ch_is('\'') {
1314 self.fatal_span_verbose(start_with_quote, self.next_pos,
1315 String::from("character literal may only contain one codepoint"))
1320 // Include the leading `'` in the real identifier, for macro
1321 // expansion purposes. See #12512 for the gory details of why
1322 // this is necessary.
1323 let ident = self.with_str_from(start, |lifetime_name| {
1324 self.mk_ident(&format!("'{}", lifetime_name))
1327 return Ok(token::Lifetime(ident));
1330 let valid = self.scan_char_or_byte(start,
1336 if !self.ch_is('\'') {
1340 if self.ch_is('\'') {
1341 let start = self.src_index(start);
1342 let end = self.src_index(self.pos);
1344 let span = self.mk_sp(start_with_quote, self.pos);
1345 self.sess.span_diagnostic
1346 .struct_span_err(span,
1347 "character literal may only contain one codepoint")
1348 .span_suggestion(span,
1349 "if you meant to write a `str` literal, \
1351 format!("\"{}\"", &self.src[start..end]))
1353 return Ok(token::Literal(token::Str_(Symbol::intern("??")), None))
1355 if self.ch_is('\n') || self.is_eof() || self.ch_is('/') {
1356 // Only attempt to infer single line string literals. If we encounter
1357 // a slash, bail out in order to avoid nonsensical suggestion when
1358 // involving comments.
1362 self.fatal_span_verbose(start_with_quote, pos,
1363 String::from("character literal may only contain one codepoint")).raise();
1367 self.name_from(start)
1371 self.bump(); // advance ch past token
1372 let suffix = self.scan_optional_raw_name();
1373 Ok(token::Literal(token::Char(id), suffix))
1377 let lit = match self.ch {
1378 Some('\'') => self.scan_byte(),
1379 Some('"') => self.scan_byte_string(),
1380 Some('r') => self.scan_raw_byte_string(),
1381 _ => unreachable!(), // Should have been a token::Ident above.
1383 let suffix = self.scan_optional_raw_name();
1384 Ok(token::Literal(lit, suffix))
1387 let start_bpos = self.pos;
1388 let mut valid = true;
1390 while !self.ch_is('"') {
1392 let last_bpos = self.pos;
1393 self.fatal_span_(start_bpos,
1395 "unterminated double quote string").raise();
1398 let ch_start = self.pos;
1399 let ch = self.ch.unwrap();
1401 valid &= self.scan_char_or_byte(ch_start,
1407 // adjust for the ASCII " at the start of the literal
1409 self.name_from(start_bpos + BytePos(1))
1411 Symbol::intern("??")
1414 let suffix = self.scan_optional_raw_name();
1415 Ok(token::Literal(token::Str_(id), suffix))
1418 let start_bpos = self.pos;
1420 let mut hash_count: u16 = 0;
1421 while self.ch_is('#') {
1427 self.fail_unterminated_raw_string(start_bpos, hash_count);
1428 } else if !self.ch_is('"') {
1429 let last_bpos = self.pos;
1430 let curr_char = self.ch.unwrap();
1431 self.fatal_span_char(start_bpos,
1433 "found invalid character; only `#` is allowed \
1434 in raw string delimitation",
1438 let content_start_bpos = self.pos;
1439 let mut content_end_bpos;
1440 let mut valid = true;
1443 self.fail_unterminated_raw_string(start_bpos, hash_count);
1445 // if self.ch_is('"') {
1446 // content_end_bpos = self.pos;
1447 // for _ in 0..hash_count {
1449 // if !self.ch_is('#') {
1451 let c = self.ch.unwrap();
1454 content_end_bpos = self.pos;
1455 for _ in 0..hash_count {
1457 if !self.ch_is('#') {
1464 if !self.nextch_is('\n') {
1465 let last_bpos = self.pos;
1466 self.err_span_(start_bpos,
1468 "bare CR not allowed in raw string, use \\r \
1479 self.name_from_to(content_start_bpos, content_end_bpos)
1481 Symbol::intern("??")
1483 let suffix = self.scan_optional_raw_name();
1484 Ok(token::Literal(token::StrRaw(id, hash_count), suffix))
1487 if self.nextch_is('>') {
1492 Ok(self.binop(token::Minus))
1496 if self.nextch_is('&') {
1501 Ok(self.binop(token::And))
1505 match self.nextch() {
1512 Ok(self.binop(token::Or))
1517 Ok(self.binop(token::Plus))
1520 Ok(self.binop(token::Star))
1523 Ok(self.binop(token::Slash))
1526 Ok(self.binop(token::Caret))
1529 Ok(self.binop(token::Percent))
1532 let last_bpos = self.pos;
1533 let bpos = self.next_pos;
1534 let mut err = self.struct_fatal_span_char(last_bpos,
1536 "unknown start of token",
1538 unicode_chars::check_for_substitution(self, c, &mut err);
1539 self.fatal_errs.push(err);
1545 fn consume_whitespace(&mut self) {
1546 while is_pattern_whitespace(self.ch) && !self.is_eof() {
1551 fn read_to_eol(&mut self) -> String {
1552 let mut val = String::new();
1553 while !self.ch_is('\n') && !self.is_eof() {
1554 val.push(self.ch.unwrap());
1557 if self.ch_is('\n') {
1563 fn read_one_line_comment(&mut self) -> String {
1564 let val = self.read_to_eol();
1565 assert!((val.as_bytes()[0] == b'/' && val.as_bytes()[1] == b'/') ||
1566 (val.as_bytes()[0] == b'#' && val.as_bytes()[1] == b'!'));
1570 fn consume_non_eol_whitespace(&mut self) {
1571 while is_pattern_whitespace(self.ch) && !self.ch_is('\n') && !self.is_eof() {
1576 fn peeking_at_comment(&self) -> bool {
1577 (self.ch_is('/') && self.nextch_is('/')) || (self.ch_is('/') && self.nextch_is('*')) ||
1578 // consider shebangs comments, but not inner attributes
1579 (self.ch_is('#') && self.nextch_is('!') && !self.nextnextch_is('['))
1582 fn scan_byte(&mut self) -> token::Lit {
1584 let start = self.pos;
1586 // the eof will be picked up by the final `'` check below
1587 let c2 = self.ch.unwrap_or('\x00');
1590 let valid = self.scan_char_or_byte(start,
1595 if !self.ch_is('\'') {
1596 // Byte offsetting here is okay because the
1597 // character before position `start` are an
1598 // ascii single quote and ascii 'b'.
1600 self.fatal_span_verbose(start - BytePos(2),
1602 "unterminated byte constant".to_string()).raise();
1606 self.name_from(start)
1610 self.bump(); // advance ch past token
1614 fn scan_byte_escape(&mut self, delim: char, below_0x7f_only: bool) -> bool {
1615 self.scan_hex_digits(2, delim, below_0x7f_only)
1618 fn scan_byte_string(&mut self) -> token::Lit {
1620 let start = self.pos;
1621 let mut valid = true;
1623 while !self.ch_is('"') {
1626 self.fatal_span_(start, pos, "unterminated double quote byte string").raise();
1629 let ch_start = self.pos;
1630 let ch = self.ch.unwrap();
1632 valid &= self.scan_char_or_byte(ch_start,
1639 self.name_from(start)
1641 Symbol::intern("??")
1647 fn scan_raw_byte_string(&mut self) -> token::Lit {
1648 let start_bpos = self.pos;
1650 let mut hash_count = 0;
1651 while self.ch_is('#') {
1657 self.fail_unterminated_raw_string(start_bpos, hash_count);
1658 } else if !self.ch_is('"') {
1660 let ch = self.ch.unwrap();
1661 self.fatal_span_char(start_bpos,
1663 "found invalid character; only `#` is allowed in raw \
1664 string delimitation",
1668 let content_start_bpos = self.pos;
1669 let mut content_end_bpos;
1673 self.fail_unterminated_raw_string(start_bpos, hash_count);
1676 content_end_bpos = self.pos;
1677 for _ in 0..hash_count {
1679 if !self.ch_is('#') {
1688 self.err_span_char(pos, pos, "raw byte string must be ASCII", c);
1695 token::ByteStrRaw(self.name_from_to(content_start_bpos, content_end_bpos),
1700 // This tests the character for the unicode property 'PATTERN_WHITE_SPACE' which
1701 // is guaranteed to be forward compatible. http://unicode.org/reports/tr31/#R3
1702 pub fn is_pattern_whitespace(c: Option<char>) -> bool {
1703 c.map_or(false, Pattern_White_Space)
1706 fn in_range(c: Option<char>, lo: char, hi: char) -> bool {
1708 Some(c) => lo <= c && c <= hi,
1713 fn is_dec_digit(c: Option<char>) -> bool {
1714 in_range(c, '0', '9')
1717 pub fn is_doc_comment(s: &str) -> bool {
1718 let res = (s.starts_with("///") && *s.as_bytes().get(3).unwrap_or(&b' ') != b'/') ||
1719 s.starts_with("//!");
1720 debug!("is {:?} a doc comment? {}", s, res);
1724 pub fn is_block_doc_comment(s: &str) -> bool {
1725 // Prevent `/**/` from being parsed as a doc comment
1726 let res = ((s.starts_with("/**") && *s.as_bytes().get(3).unwrap_or(&b' ') != b'*') ||
1727 s.starts_with("/*!")) && s.len() >= 5;
1728 debug!("is {:?} a doc comment? {}", s, res);
1732 fn ident_start(c: Option<char>) -> bool {
1735 None => return false,
1738 (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || (c > '\x7f' && c.is_xid_start())
1741 fn ident_continue(c: Option<char>) -> bool {
1744 None => return false,
1747 (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' ||
1748 (c > '\x7f' && c.is_xid_continue())
1751 // The string is a valid identifier or a lifetime identifier.
1752 pub fn is_valid_ident(s: &str) -> bool {
1753 let mut chars = s.chars();
1754 ident_start(chars.next()) && chars.all(|ch| ident_continue(Some(ch)))
1761 use ast::{Ident, CrateConfig};
1763 use syntax_pos::{BytePos, Span, NO_EXPANSION};
1764 use codemap::CodeMap;
1766 use feature_gate::UnstableFeatures;
1768 use std::collections::HashSet;
1770 use std::path::PathBuf;
1771 use diagnostics::plugin::ErrorMap;
1772 use rustc_data_structures::sync::Lock;
1774 fn mk_sess(cm: Lrc<CodeMap>) -> ParseSess {
1775 let emitter = errors::emitter::EmitterWriter::new(Box::new(io::sink()),
1780 span_diagnostic: errors::Handler::with_emitter(true, false, Box::new(emitter)),
1781 unstable_features: UnstableFeatures::from_environment(),
1782 config: CrateConfig::new(),
1783 included_mod_stack: Lock::new(Vec::new()),
1785 missing_fragment_specifiers: Lock::new(HashSet::new()),
1786 raw_identifier_spans: Lock::new(Vec::new()),
1787 registered_diagnostics: Lock::new(ErrorMap::new()),
1788 non_modrs_mods: Lock::new(vec![]),
1792 // open a string reader for the given string
1793 fn setup<'a>(cm: &CodeMap,
1794 sess: &'a ParseSess,
1796 -> StringReader<'a> {
1797 let fm = cm.new_filemap(PathBuf::from("zebra.rs").into(), teststr);
1798 StringReader::new(sess, fm)
1804 let cm = Lrc::new(CodeMap::new(FilePathMapping::empty()));
1805 let sh = mk_sess(cm.clone());
1806 let mut string_reader = setup(&cm,
1808 "/* my source file */ fn main() { println!(\"zebra\"); }\n"
1810 let id = Ident::from_str("fn");
1811 assert_eq!(string_reader.next_token().tok, token::Comment);
1812 assert_eq!(string_reader.next_token().tok, token::Whitespace);
1813 let tok1 = string_reader.next_token();
1814 let tok2 = TokenAndSpan {
1815 tok: token::Ident(id, false),
1816 sp: Span::new(BytePos(21), BytePos(23), NO_EXPANSION),
1818 assert_eq!(tok1, tok2);
1819 assert_eq!(string_reader.next_token().tok, token::Whitespace);
1820 // the 'main' id is already read:
1821 assert_eq!(string_reader.pos.clone(), BytePos(28));
1822 // read another token:
1823 let tok3 = string_reader.next_token();
1824 let tok4 = TokenAndSpan {
1825 tok: mk_ident("main"),
1826 sp: Span::new(BytePos(24), BytePos(28), NO_EXPANSION),
1828 assert_eq!(tok3, tok4);
1829 // the lparen is already read:
1830 assert_eq!(string_reader.pos.clone(), BytePos(29))
1834 // check that the given reader produces the desired stream
1835 // of tokens (stop checking after exhausting the expected vec)
1836 fn check_tokenization(mut string_reader: StringReader, expected: Vec<token::Token>) {
1837 for expected_tok in &expected {
1838 assert_eq!(&string_reader.next_token().tok, expected_tok);
1842 // make the identifier by looking up the string in the interner
1843 fn mk_ident(id: &str) -> token::Token {
1844 token::Token::from_ast_ident(Ident::from_str(id))
1848 fn doublecolonparsing() {
1850 let cm = Lrc::new(CodeMap::new(FilePathMapping::empty()));
1851 let sh = mk_sess(cm.clone());
1852 check_tokenization(setup(&cm, &sh, "a b".to_string()),
1853 vec![mk_ident("a"), token::Whitespace, mk_ident("b")]);
1860 let cm = Lrc::new(CodeMap::new(FilePathMapping::empty()));
1861 let sh = mk_sess(cm.clone());
1862 check_tokenization(setup(&cm, &sh, "a::b".to_string()),
1863 vec![mk_ident("a"), token::ModSep, mk_ident("b")]);
1870 let cm = Lrc::new(CodeMap::new(FilePathMapping::empty()));
1871 let sh = mk_sess(cm.clone());
1872 check_tokenization(setup(&cm, &sh, "a ::b".to_string()),
1873 vec![mk_ident("a"), token::Whitespace, token::ModSep, mk_ident("b")]);
1880 let cm = Lrc::new(CodeMap::new(FilePathMapping::empty()));
1881 let sh = mk_sess(cm.clone());
1882 check_tokenization(setup(&cm, &sh, "a:: b".to_string()),
1883 vec![mk_ident("a"), token::ModSep, token::Whitespace, mk_ident("b")]);
1890 let cm = Lrc::new(CodeMap::new(FilePathMapping::empty()));
1891 let sh = mk_sess(cm.clone());
1892 assert_eq!(setup(&cm, &sh, "'a'".to_string()).next_token().tok,
1893 token::Literal(token::Char(Symbol::intern("a")), None));
1898 fn character_space() {
1900 let cm = Lrc::new(CodeMap::new(FilePathMapping::empty()));
1901 let sh = mk_sess(cm.clone());
1902 assert_eq!(setup(&cm, &sh, "' '".to_string()).next_token().tok,
1903 token::Literal(token::Char(Symbol::intern(" ")), None));
1908 fn character_escaped() {
1910 let cm = Lrc::new(CodeMap::new(FilePathMapping::empty()));
1911 let sh = mk_sess(cm.clone());
1912 assert_eq!(setup(&cm, &sh, "'\\n'".to_string()).next_token().tok,
1913 token::Literal(token::Char(Symbol::intern("\\n")), None));
1918 fn lifetime_name() {
1920 let cm = Lrc::new(CodeMap::new(FilePathMapping::empty()));
1921 let sh = mk_sess(cm.clone());
1922 assert_eq!(setup(&cm, &sh, "'abc".to_string()).next_token().tok,
1923 token::Lifetime(Ident::from_str("'abc")));
1930 let cm = Lrc::new(CodeMap::new(FilePathMapping::empty()));
1931 let sh = mk_sess(cm.clone());
1932 assert_eq!(setup(&cm, &sh, "r###\"\"#a\\b\x00c\"\"###".to_string())
1935 token::Literal(token::StrRaw(Symbol::intern("\"#a\\b\x00c\""), 3), None));
1940 fn literal_suffixes() {
1942 let cm = Lrc::new(CodeMap::new(FilePathMapping::empty()));
1943 let sh = mk_sess(cm.clone());
1945 ($input: expr, $tok_type: ident, $tok_contents: expr) => {{
1946 assert_eq!(setup(&cm, &sh, format!("{}suffix", $input)).next_token().tok,
1947 token::Literal(token::$tok_type(Symbol::intern($tok_contents)),
1948 Some(Symbol::intern("suffix"))));
1949 // with a whitespace separator:
1950 assert_eq!(setup(&cm, &sh, format!("{} suffix", $input)).next_token().tok,
1951 token::Literal(token::$tok_type(Symbol::intern($tok_contents)),
1956 test!("'a'", Char, "a");
1957 test!("b'a'", Byte, "a");
1958 test!("\"a\"", Str_, "a");
1959 test!("b\"a\"", ByteStr, "a");
1960 test!("1234", Integer, "1234");
1961 test!("0b101", Integer, "0b101");
1962 test!("0xABC", Integer, "0xABC");
1963 test!("1.0", Float, "1.0");
1964 test!("1.0e10", Float, "1.0e10");
1966 assert_eq!(setup(&cm, &sh, "2us".to_string()).next_token().tok,
1967 token::Literal(token::Integer(Symbol::intern("2")),
1968 Some(Symbol::intern("us"))));
1969 assert_eq!(setup(&cm, &sh, "r###\"raw\"###suffix".to_string()).next_token().tok,
1970 token::Literal(token::StrRaw(Symbol::intern("raw"), 3),
1971 Some(Symbol::intern("suffix"))));
1972 assert_eq!(setup(&cm, &sh, "br###\"raw\"###suffix".to_string()).next_token().tok,
1973 token::Literal(token::ByteStrRaw(Symbol::intern("raw"), 3),
1974 Some(Symbol::intern("suffix"))));
1979 fn line_doc_comments() {
1980 assert!(is_doc_comment("///"));
1981 assert!(is_doc_comment("/// blah"));
1982 assert!(!is_doc_comment("////"));
1986 fn nested_block_comments() {
1988 let cm = Lrc::new(CodeMap::new(FilePathMapping::empty()));
1989 let sh = mk_sess(cm.clone());
1990 let mut lexer = setup(&cm, &sh, "/* /* */ */'a'".to_string());
1991 match lexer.next_token().tok {
1992 token::Comment => {}
1993 _ => panic!("expected a comment!"),
1995 assert_eq!(lexer.next_token().tok,
1996 token::Literal(token::Char(Symbol::intern("a")), None));
2001 fn crlf_comments() {
2003 let cm = Lrc::new(CodeMap::new(FilePathMapping::empty()));
2004 let sh = mk_sess(cm.clone());
2005 let mut lexer = setup(&cm, &sh, "// test\r\n/// test\r\n".to_string());
2006 let comment = lexer.next_token();
2007 assert_eq!(comment.tok, token::Comment);
2008 assert_eq!((comment.sp.lo(), comment.sp.hi()), (BytePos(0), BytePos(7)));
2009 assert_eq!(lexer.next_token().tok, token::Whitespace);
2010 assert_eq!(lexer.next_token().tok,
2011 token::DocComment(Symbol::intern("/// test")));