]> git.proxmox.com Git - rustc.git/blobdiff - src/libsyntax/parse/lexer/mod.rs
New upstream version 1.27.1+dfsg1
[rustc.git] / src / libsyntax / parse / lexer / mod.rs
index 6c0e2425d37ad6288ca8757e9edd63ca7580cfd3..22a0261d8c6b15fe9c05b4ce9e2634aa6f69d9b4 100644 (file)
@@ -8,48 +8,92 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use ast;
-use syntax_pos::{self, BytePos, CharPos, Pos, Span};
-use codemap::CodeMap;
-use errors::{FatalError, Handler, DiagnosticBuilder};
-use ext::tt::transcribe::tt_next_token;
-use parse::token::{self, keywords, str_to_ident};
+use ast::{self, Ident};
+use syntax_pos::{self, BytePos, CharPos, Pos, Span, NO_EXPANSION};
+use codemap::{CodeMap, FilePathMapping};
+use errors::{FatalError, DiagnosticBuilder};
+use parse::{token, ParseSess};
 use str::char_at;
-use rustc_unicode::property::Pattern_White_Space;
+use symbol::{Symbol, keywords};
+use core::unicode::property::Pattern_White_Space;
 
 use std::borrow::Cow;
 use std::char;
 use std::mem::replace;
-use std::rc::Rc;
-
-pub use ext::tt::transcribe::{TtReader, new_tt_reader, new_tt_reader_with_doc_flag};
+use rustc_data_structures::sync::Lrc;
 
 pub mod comments;
+mod tokentrees;
 mod unicode_chars;
 
-pub trait Reader {
-    fn is_eof(&self) -> bool;
-    fn try_next_token(&mut self) -> Result<TokenAndSpan, ()>;
+#[derive(Clone, PartialEq, Eq, Debug)]
+pub struct TokenAndSpan {
+    pub tok: token::Token,
+    pub sp: Span,
+}
+
+impl Default for TokenAndSpan {
+    fn default() -> Self {
+        TokenAndSpan { tok: token::Whitespace, sp: syntax_pos::DUMMY_SP }
+    }
+}
+
+pub struct StringReader<'a> {
+    pub sess: &'a ParseSess,
+    /// The absolute offset within the codemap of the next character to read
+    pub next_pos: BytePos,
+    /// The absolute offset within the codemap of the current character
+    pub pos: BytePos,
+    /// The column of the next character to read
+    pub col: CharPos,
+    /// The current character (which has been read from self.pos)
+    pub ch: Option<char>,
+    pub filemap: Lrc<syntax_pos::FileMap>,
+    /// If Some, stop reading the source at this position (inclusive).
+    pub terminator: Option<BytePos>,
+    /// Whether to record new-lines and multibyte chars in filemap.
+    /// This is only necessary the first time a filemap is lexed.
+    /// If part of a filemap is being re-lexed, this should be set to false.
+    pub save_new_lines_and_multibyte: bool,
+    // cached:
+    pub peek_tok: token::Token,
+    pub peek_span: Span,
+    pub fatal_errs: Vec<DiagnosticBuilder<'a>>,
+    // cache a direct reference to the source text, so that we don't have to
+    // retrieve it via `self.filemap.src.as_ref().unwrap()` all the time.
+    source_text: Lrc<String>,
+    /// Stack of open delimiters and their spans. Used for error message.
+    token: token::Token,
+    span: Span,
+    open_braces: Vec<(token::DelimToken, Span)>,
+    pub override_span: Option<Span>,
+}
+
+impl<'a> StringReader<'a> {
+    fn mk_sp(&self, lo: BytePos, hi: BytePos) -> Span {
+        unwrap_or!(self.override_span, Span::new(lo, hi, NO_EXPANSION))
+    }
+    fn mk_ident(&self, string: &str) -> Ident {
+        let mut ident = Ident::from_str(string);
+        if let Some(span) = self.override_span {
+            ident.span = span;
+        }
+        ident
+    }
+
     fn next_token(&mut self) -> TokenAndSpan where Self: Sized {
         let res = self.try_next_token();
         self.unwrap_or_abort(res)
     }
-    /// Report a fatal error with the current span.
-    fn fatal(&self, &str) -> FatalError;
-    /// Report a non-fatal error with the current span.
-    fn err(&self, &str);
-    fn emit_fatal_errors(&mut self);
     fn unwrap_or_abort(&mut self, res: Result<TokenAndSpan, ()>) -> TokenAndSpan {
         match res {
             Ok(tok) => tok,
             Err(_) => {
                 self.emit_fatal_errors();
-                panic!(FatalError);
+                FatalError.raise();
             }
         }
     }
-    fn peek(&self) -> TokenAndSpan;
-    /// Get a token the parser cares about.
     fn try_real_token(&mut self) -> Result<TokenAndSpan, ()> {
         let mut t = self.try_next_token()?;
         loop {
@@ -60,80 +104,56 @@ pub trait Reader {
                 _ => break,
             }
         }
+        self.token = t.tok.clone();
+        self.span = t.sp;
         Ok(t)
     }
-    fn real_token(&mut self) -> TokenAndSpan {
+    pub fn real_token(&mut self) -> TokenAndSpan {
         let res = self.try_real_token();
         self.unwrap_or_abort(res)
     }
-}
-
-#[derive(Clone, PartialEq, Eq, Debug)]
-pub struct TokenAndSpan {
-    pub tok: token::Token,
-    pub sp: Span,
-}
-
-pub struct StringReader<'a> {
-    pub span_diagnostic: &'a Handler,
-    /// The absolute offset within the codemap of the next character to read
-    pub pos: BytePos,
-    /// The absolute offset within the codemap of the last character read(curr)
-    pub last_pos: BytePos,
-    /// The column of the next character to read
-    pub col: CharPos,
-    /// The last character to be read
-    pub curr: Option<char>,
-    pub filemap: Rc<syntax_pos::FileMap>,
-    /// If Some, stop reading the source at this position (inclusive).
-    pub terminator: Option<BytePos>,
-    /// Whether to record new-lines in filemap. This is only necessary the first
-    /// time a filemap is lexed. If part of a filemap is being re-lexed, this
-    /// should be set to false.
-    pub save_new_lines: bool,
-    // cached:
-    pub peek_tok: token::Token,
-    pub peek_span: Span,
-    pub fatal_errs: Vec<DiagnosticBuilder<'a>>,
-    // cache a direct reference to the source text, so that we don't have to
-    // retrieve it via `self.filemap.src.as_ref().unwrap()` all the time.
-    source_text: Rc<String>,
-}
-
-impl<'a> Reader for StringReader<'a> {
     fn is_eof(&self) -> bool {
-        if self.curr.is_none() {
+        if self.ch.is_none() {
             return true;
         }
 
         match self.terminator {
-            Some(t) => self.pos > t,
+            Some(t) => self.next_pos > t,
             None => false,
         }
     }
     /// Return the next token. EFFECT: advances the string_reader.
-    fn try_next_token(&mut self) -> Result<TokenAndSpan, ()> {
+    pub fn try_next_token(&mut self) -> Result<TokenAndSpan, ()> {
         assert!(self.fatal_errs.is_empty());
         let ret_val = TokenAndSpan {
-            tok: replace(&mut self.peek_tok, token::Underscore),
+            tok: replace(&mut self.peek_tok, token::Whitespace),
             sp: self.peek_span,
         };
         self.advance_token()?;
         Ok(ret_val)
     }
+
+    fn fail_unterminated_raw_string(&self, pos: BytePos, hash_count: u16) {
+        let mut err = self.struct_span_fatal(pos, pos, "unterminated raw string");
+        err.span_label(self.mk_sp(pos, pos), "unterminated raw string");
+        if hash_count > 0 {
+            err.note(&format!("this raw string should be terminated with `\"{}`",
+                              "#".repeat(hash_count as usize)));
+        }
+        err.emit();
+        FatalError.raise();
+    }
+
     fn fatal(&self, m: &str) -> FatalError {
         self.fatal_span(self.peek_span, m)
     }
-    fn err(&self, m: &str) {
-        self.err_span(self.peek_span, m)
-    }
-    fn emit_fatal_errors(&mut self) {
+    pub fn emit_fatal_errors(&mut self) {
         for err in &mut self.fatal_errs {
             err.emit();
         }
         self.fatal_errs.clear();
     }
-    fn peek(&self) -> TokenAndSpan {
+    pub fn peek(&self) -> TokenAndSpan {
         // FIXME(pcwalton): Bad copy!
         TokenAndSpan {
             tok: self.peek_tok.clone(),
@@ -142,108 +162,115 @@ impl<'a> Reader for StringReader<'a> {
     }
 }
 
-impl<'a> Reader for TtReader<'a> {
-    fn is_eof(&self) -> bool {
-        self.cur_tok == token::Eof
-    }
-    fn try_next_token(&mut self) -> Result<TokenAndSpan, ()> {
-        assert!(self.fatal_errs.is_empty());
-        let r = tt_next_token(self);
-        debug!("TtReader: r={:?}", r);
-        Ok(r)
-    }
-    fn fatal(&self, m: &str) -> FatalError {
-        self.sp_diag.span_fatal(self.cur_span, m)
-    }
-    fn err(&self, m: &str) {
-        self.sp_diag.span_err(self.cur_span, m);
-    }
-    fn emit_fatal_errors(&mut self) {
-        for err in &mut self.fatal_errs {
-            err.emit();
-        }
-        self.fatal_errs.clear();
-    }
-    fn peek(&self) -> TokenAndSpan {
-        TokenAndSpan {
-            tok: self.cur_tok.clone(),
-            sp: self.cur_span,
-        }
-    }
-}
-
 impl<'a> StringReader<'a> {
-    /// For comments.rs, which hackily pokes into pos and curr
-    pub fn new_raw<'b>(span_diagnostic: &'b Handler,
-                       filemap: Rc<syntax_pos::FileMap>)
-                       -> StringReader<'b> {
-        let mut sr = StringReader::new_raw_internal(span_diagnostic, filemap);
+    /// For comments.rs, which hackily pokes into next_pos and ch
+    pub fn new_raw(sess: &'a ParseSess, filemap: Lrc<syntax_pos::FileMap>) -> Self {
+        let mut sr = StringReader::new_raw_internal(sess, filemap);
         sr.bump();
         sr
     }
 
-    fn new_raw_internal<'b>(span_diagnostic: &'b Handler,
-                            filemap: Rc<syntax_pos::FileMap>)
-                            -> StringReader<'b> {
+    fn new_raw_internal(sess: &'a ParseSess, filemap: Lrc<syntax_pos::FileMap>) -> Self {
         if filemap.src.is_none() {
-            span_diagnostic.bug(&format!("Cannot lex filemap \
-                                          without source: {}",
-                                         filemap.name)[..]);
+            sess.span_diagnostic.bug(&format!("Cannot lex filemap without source: {}",
+                                              filemap.name));
         }
 
         let source_text = (*filemap.src.as_ref().unwrap()).clone();
 
         StringReader {
-            span_diagnostic: span_diagnostic,
+            sess,
+            next_pos: filemap.start_pos,
             pos: filemap.start_pos,
-            last_pos: filemap.start_pos,
             col: CharPos(0),
-            curr: Some('\n'),
-            filemap: filemap,
+            ch: Some('\n'),
+            filemap,
             terminator: None,
-            save_new_lines: true,
+            save_new_lines_and_multibyte: true,
             // dummy values; not read
             peek_tok: token::Eof,
             peek_span: syntax_pos::DUMMY_SP,
-            source_text: source_text,
+            source_text,
             fatal_errs: Vec::new(),
+            token: token::Eof,
+            span: syntax_pos::DUMMY_SP,
+            open_braces: Vec::new(),
+            override_span: None,
         }
     }
 
-    pub fn new<'b>(span_diagnostic: &'b Handler,
-                   filemap: Rc<syntax_pos::FileMap>)
-                   -> StringReader<'b> {
-        let mut sr = StringReader::new_raw(span_diagnostic, filemap);
-        if let Err(_) = sr.advance_token() {
+    pub fn new(sess: &'a ParseSess, filemap: Lrc<syntax_pos::FileMap>) -> Self {
+        let mut sr = StringReader::new_raw(sess, filemap);
+        if sr.advance_token().is_err() {
             sr.emit_fatal_errors();
-            panic!(FatalError);
+            FatalError.raise();
         }
         sr
     }
 
-    pub fn curr_is(&self, c: char) -> bool {
-        self.curr == Some(c)
+    pub fn retokenize(sess: &'a ParseSess, mut span: Span) -> Self {
+        let begin = sess.codemap().lookup_byte_offset(span.lo());
+        let end = sess.codemap().lookup_byte_offset(span.hi());
+
+        // Make the range zero-length if the span is invalid.
+        if span.lo() > span.hi() || begin.fm.start_pos != end.fm.start_pos {
+            span = span.shrink_to_lo();
+        }
+
+        let mut sr = StringReader::new_raw_internal(sess, begin.fm);
+
+        // Seek the lexer to the right byte range.
+        sr.save_new_lines_and_multibyte = false;
+        sr.next_pos = span.lo();
+        sr.terminator = Some(span.hi());
+
+        sr.bump();
+
+        if sr.advance_token().is_err() {
+            sr.emit_fatal_errors();
+            FatalError.raise();
+        }
+        sr
+    }
+
+    pub fn ch_is(&self, c: char) -> bool {
+        self.ch == Some(c)
     }
 
     /// Report a fatal lexical error with a given span.
     pub fn fatal_span(&self, sp: Span, m: &str) -> FatalError {
-        self.span_diagnostic.span_fatal(sp, m)
+        self.sess.span_diagnostic.span_fatal(sp, m)
     }
 
     /// Report a lexical error with a given span.
     pub fn err_span(&self, sp: Span, m: &str) {
-        self.span_diagnostic.span_err(sp, m)
+        self.sess.span_diagnostic.span_err(sp, m)
     }
 
 
     /// Report a fatal error spanning [`from_pos`, `to_pos`).
     fn fatal_span_(&self, from_pos: BytePos, to_pos: BytePos, m: &str) -> FatalError {
-        self.fatal_span(syntax_pos::mk_sp(from_pos, to_pos), m)
+        self.fatal_span(self.mk_sp(from_pos, to_pos), m)
     }
 
     /// Report a lexical error spanning [`from_pos`, `to_pos`).
     fn err_span_(&self, from_pos: BytePos, to_pos: BytePos, m: &str) {
-        self.err_span(syntax_pos::mk_sp(from_pos, to_pos), m)
+        self.err_span(self.mk_sp(from_pos, to_pos), m)
+    }
+
+    /// Pushes a character to a message string for error reporting
+    fn push_escaped_char_for_msg(m: &mut String, c: char) {
+        match c {
+            '\u{20}'...'\u{7e}' => {
+                // Don't escape \, ' or " for user-facing messages
+                m.push(c);
+            }
+            _ => {
+                for c in c.escape_default() {
+                    m.push(c);
+                }
+            }
+        }
     }
 
     /// Report a lexical error spanning [`from_pos`, `to_pos`), appending an
@@ -251,11 +278,18 @@ impl<'a> StringReader<'a> {
     fn fatal_span_char(&self, from_pos: BytePos, to_pos: BytePos, m: &str, c: char) -> FatalError {
         let mut m = m.to_string();
         m.push_str(": ");
-        for c in c.escape_default() {
-            m.push(c)
-        }
+        Self::push_escaped_char_for_msg(&mut m, c);
         self.fatal_span_(from_pos, to_pos, &m[..])
     }
+
+    fn struct_span_fatal(&self,
+                         from_pos: BytePos,
+                         to_pos: BytePos,
+                         m: &str)
+                         -> DiagnosticBuilder<'a> {
+        self.sess.span_diagnostic.struct_span_fatal(self.mk_sp(from_pos, to_pos), m)
+    }
+
     fn struct_fatal_span_char(&self,
                               from_pos: BytePos,
                               to_pos: BytePos,
@@ -264,10 +298,8 @@ impl<'a> StringReader<'a> {
                               -> DiagnosticBuilder<'a> {
         let mut m = m.to_string();
         m.push_str(": ");
-        for c in c.escape_default() {
-            m.push(c)
-        }
-        self.span_diagnostic.struct_span_fatal(syntax_pos::mk_sp(from_pos, to_pos), &m[..])
+        Self::push_escaped_char_for_msg(&mut m, c);
+        self.sess.span_diagnostic.struct_span_fatal(self.mk_sp(from_pos, to_pos), &m[..])
     }
 
     /// Report a lexical error spanning [`from_pos`, `to_pos`), appending an
@@ -275,9 +307,7 @@ impl<'a> StringReader<'a> {
     fn err_span_char(&self, from_pos: BytePos, to_pos: BytePos, m: &str, c: char) {
         let mut m = m.to_string();
         m.push_str(": ");
-        for c in c.escape_default() {
-            m.push(c)
-        }
+        Self::push_escaped_char_for_msg(&mut m, c);
         self.err_span_(from_pos, to_pos, &m[..]);
     }
     fn struct_err_span_char(&self,
@@ -288,10 +318,8 @@ impl<'a> StringReader<'a> {
                             -> DiagnosticBuilder<'a> {
         let mut m = m.to_string();
         m.push_str(": ");
-        for c in c.escape_default() {
-            m.push(c)
-        }
-        self.span_diagnostic.struct_span_err(syntax_pos::mk_sp(from_pos, to_pos), &m[..])
+        Self::push_escaped_char_for_msg(&mut m, c);
+        self.sess.span_diagnostic.struct_span_err(self.mk_sp(from_pos, to_pos), &m[..])
     }
 
     /// Report a lexical error spanning [`from_pos`, `to_pos`), appending the
@@ -315,11 +343,11 @@ impl<'a> StringReader<'a> {
             None => {
                 if self.is_eof() {
                     self.peek_tok = token::Eof;
-                    self.peek_span = syntax_pos::mk_sp(self.filemap.end_pos, self.filemap.end_pos);
+                    self.peek_span = self.mk_sp(self.filemap.end_pos, self.filemap.end_pos);
                 } else {
-                    let start_bytepos = self.last_pos;
+                    let start_bytepos = self.pos;
                     self.peek_tok = self.next_token_inner()?;
-                    self.peek_span = syntax_pos::mk_sp(start_bytepos, self.last_pos);
+                    self.peek_span = self.mk_sp(start_bytepos, self.pos);
                 };
             }
         }
@@ -331,26 +359,26 @@ impl<'a> StringReader<'a> {
     }
 
     /// Calls `f` with a string slice of the source text spanning from `start`
-    /// up to but excluding `self.last_pos`, meaning the slice does not include
-    /// the character `self.curr`.
+    /// up to but excluding `self.pos`, meaning the slice does not include
+    /// the character `self.ch`.
     pub fn with_str_from<T, F>(&self, start: BytePos, f: F) -> T
         where F: FnOnce(&str) -> T
     {
-        self.with_str_from_to(start, self.last_pos, f)
+        self.with_str_from_to(start, self.pos, f)
     }
 
     /// Create a Name from a given offset to the current offset, each
     /// adjusted 1 towards each other (assumes that on either side there is a
     /// single-byte delimiter).
     pub fn name_from(&self, start: BytePos) -> ast::Name {
-        debug!("taking an ident from {:?} to {:?}", start, self.last_pos);
-        self.with_str_from(start, token::intern)
+        debug!("taking an ident from {:?} to {:?}", start, self.pos);
+        self.with_str_from(start, Symbol::intern)
     }
 
     /// As name_from, with an explicit endpoint.
     pub fn name_from_to(&self, start: BytePos, end: BytePos) -> ast::Name {
         debug!("taking an ident from {:?} to {:?}", start, end);
-        self.with_str_from_to(start, end, token::intern)
+        self.with_str_from_to(start, end, Symbol::intern)
     }
 
     /// Calls `f` with a string slice of the source text spanning from `start`
@@ -414,34 +442,41 @@ impl<'a> StringReader<'a> {
     /// Advance the StringReader by one character. If a newline is
     /// discovered, add it to the FileMap's list of line start offsets.
     pub fn bump(&mut self) {
-        self.last_pos = self.pos;
-        let current_byte_offset = self.byte_offset(self.pos).to_usize();
-        if current_byte_offset < self.source_text.len() {
-            assert!(self.curr.is_some());
-            let last_char = self.curr.unwrap();
-            let ch = char_at(&self.source_text, current_byte_offset);
-            let next = current_byte_offset + ch.len_utf8();
-            let byte_offset_diff = next - current_byte_offset;
-            self.pos = self.pos + Pos::from_usize(byte_offset_diff);
-            self.curr = Some(ch);
-            self.col = self.col + CharPos(1);
-            if last_char == '\n' {
-                if self.save_new_lines {
-                    self.filemap.next_line(self.last_pos);
+        let new_pos = self.next_pos;
+        let new_byte_offset = self.byte_offset(new_pos).to_usize();
+        let end = self.terminator.map_or(self.source_text.len(), |t| {
+            self.byte_offset(t).to_usize()
+        });
+        if new_byte_offset < end {
+            let old_ch_is_newline = self.ch.unwrap() == '\n';
+            let new_ch = char_at(&self.source_text, new_byte_offset);
+            let new_ch_len = new_ch.len_utf8();
+
+            self.ch = Some(new_ch);
+            self.pos = new_pos;
+            self.next_pos = new_pos + Pos::from_usize(new_ch_len);
+            if old_ch_is_newline {
+                if self.save_new_lines_and_multibyte {
+                    self.filemap.next_line(self.pos);
                 }
                 self.col = CharPos(0);
+            } else {
+                self.col = self.col + CharPos(1);
             }
-
-            if byte_offset_diff > 1 {
-                self.filemap.record_multibyte_char(self.last_pos, byte_offset_diff);
+            if new_ch_len > 1 {
+                if self.save_new_lines_and_multibyte {
+                    self.filemap.record_multibyte_char(self.pos, new_ch_len);
+                }
             }
+            self.filemap.record_width(self.pos, new_ch);
         } else {
-            self.curr = None;
+            self.ch = None;
+            self.pos = new_pos;
         }
     }
 
     pub fn nextch(&self) -> Option<char> {
-        let offset = self.byte_offset(self.pos).to_usize();
+        let offset = self.byte_offset(self.next_pos).to_usize();
         if offset < self.source_text.len() {
             Some(char_at(&self.source_text, offset))
         } else {
@@ -454,7 +489,7 @@ impl<'a> StringReader<'a> {
     }
 
     pub fn nextnextch(&self) -> Option<char> {
-        let offset = self.byte_offset(self.pos).to_usize();
+        let offset = self.byte_offset(self.next_pos).to_usize();
         let s = &self.source_text[..];
         if offset >= s.len() {
             return None;
@@ -473,58 +508,62 @@ impl<'a> StringReader<'a> {
 
     /// Eats <XID_start><XID_continue>*, if possible.
     fn scan_optional_raw_name(&mut self) -> Option<ast::Name> {
-        if !ident_start(self.curr) {
+        if !ident_start(self.ch) {
             return None;
         }
-        let start = self.last_pos;
-        while ident_continue(self.curr) {
+        let start = self.pos;
+        while ident_continue(self.ch) {
             self.bump();
         }
 
         self.with_str_from(start, |string| {
             if string == "_" {
+                self.sess.span_diagnostic
+                    .struct_span_warn(self.mk_sp(start, self.pos),
+                                      "underscore literal suffix is not allowed")
+                    .warn("this was previously accepted by the compiler but is \
+                          being phased out; it will become a hard error in \
+                          a future release!")
+                    .note("for more information, see issue #42326 \
+                          <https://github.com/rust-lang/rust/issues/42326>")
+                    .emit();
                 None
             } else {
-                Some(token::intern(string))
+                Some(Symbol::intern(string))
             }
         })
     }
 
-    /// PRECONDITION: self.curr is not whitespace
+    /// PRECONDITION: self.ch is not whitespace
     /// Eats any kind of comment.
     fn scan_comment(&mut self) -> Option<TokenAndSpan> {
-        if let Some(c) = self.curr {
+        if let Some(c) = self.ch {
             if c.is_whitespace() {
-                self.span_diagnostic.span_err(syntax_pos::mk_sp(self.last_pos, self.last_pos),
-                                              "called consume_any_line_comment, but there \
-                                               was whitespace");
+                let msg = "called consume_any_line_comment, but there was whitespace";
+                self.sess.span_diagnostic.span_err(self.mk_sp(self.pos, self.pos), msg);
             }
         }
 
-        if self.curr_is('/') {
+        if self.ch_is('/') {
             match self.nextch() {
                 Some('/') => {
                     self.bump();
                     self.bump();
 
                     // line comments starting with "///" or "//!" are doc-comments
-                    let doc_comment = self.curr_is('/') || self.curr_is('!');
-                    let start_bpos = if doc_comment {
-                        self.pos - BytePos(3)
-                    } else {
-                        self.last_pos - BytePos(2)
-                    };
+                    let doc_comment = (self.ch_is('/') && !self.nextch_is('/')) || self.ch_is('!');
+                    let start_bpos = self.pos - BytePos(2);
 
                     while !self.is_eof() {
-                        match self.curr.unwrap() {
+                        match self.ch.unwrap() {
                             '\n' => break,
                             '\r' => {
                                 if self.nextch_is('\n') {
                                     // CRLF
                                     break;
                                 } else if doc_comment {
-                                    self.err_span_(self.last_pos,
-                                                   self.pos,
+                                    self.err_span_(self.pos,
+                                                   self.next_pos,
                                                    "bare CR not allowed in doc-comment");
                                 }
                             }
@@ -533,26 +572,26 @@ impl<'a> StringReader<'a> {
                         self.bump();
                     }
 
-                    return if doc_comment {
+                    if doc_comment {
                         self.with_str_from(start_bpos, |string| {
                             // comments with only more "/"s are not doc comments
                             let tok = if is_doc_comment(string) {
-                                token::DocComment(token::intern(string))
+                                token::DocComment(Symbol::intern(string))
                             } else {
                                 token::Comment
                             };
 
                             Some(TokenAndSpan {
-                                tok: tok,
-                                sp: syntax_pos::mk_sp(start_bpos, self.last_pos),
+                                tok,
+                                sp: self.mk_sp(start_bpos, self.pos),
                             })
                         })
                     } else {
                         Some(TokenAndSpan {
                             tok: token::Comment,
-                            sp: syntax_pos::mk_sp(start_bpos, self.last_pos),
+                            sp: self.mk_sp(start_bpos, self.pos),
                         })
-                    };
+                    }
                 }
                 Some('*') => {
                     self.bump();
@@ -561,7 +600,7 @@ impl<'a> StringReader<'a> {
                 }
                 _ => None,
             }
-        } else if self.curr_is('#') {
+        } else if self.ch_is('#') {
             if self.nextch_is('!') {
 
                 // Parse an inner attribute.
@@ -571,19 +610,19 @@ impl<'a> StringReader<'a> {
 
                 // I guess this is the only way to figure out if
                 // we're at the beginning of the file...
-                let cmap = CodeMap::new();
-                cmap.files.borrow_mut().push(self.filemap.clone());
-                let loc = cmap.lookup_char_pos_adj(self.last_pos);
+                let cmap = CodeMap::new(FilePathMapping::empty());
+                cmap.files.borrow_mut().file_maps.push(self.filemap.clone());
+                let loc = cmap.lookup_char_pos_adj(self.pos);
                 debug!("Skipping a shebang");
                 if loc.line == 1 && loc.col == CharPos(0) {
                     // FIXME: Add shebang "token", return it
-                    let start = self.last_pos;
-                    while !self.curr_is('\n') && !self.is_eof() {
+                    let start = self.pos;
+                    while !self.ch_is('\n') && !self.is_eof() {
                         self.bump();
                     }
                     return Some(TokenAndSpan {
                         tok: token::Shebang(self.name_from(start)),
-                        sp: syntax_pos::mk_sp(start, self.last_pos),
+                        sp: self.mk_sp(start, self.pos),
                     });
                 }
             }
@@ -596,7 +635,7 @@ impl<'a> StringReader<'a> {
     /// If there is whitespace, shebang, or a comment, scan it. Otherwise,
     /// return None.
     fn scan_whitespace_or_comment(&mut self) -> Option<TokenAndSpan> {
-        match self.curr.unwrap_or('\0') {
+        match self.ch.unwrap_or('\0') {
             // # to handle shebang at start of file -- this is the entry point
             // for skipping over all "junk"
             '/' | '#' => {
@@ -605,13 +644,13 @@ impl<'a> StringReader<'a> {
                 c
             },
             c if is_pattern_whitespace(Some(c)) => {
-                let start_bpos = self.last_pos;
-                while is_pattern_whitespace(self.curr) {
+                let start_bpos = self.pos;
+                while is_pattern_whitespace(self.ch) {
                     self.bump();
                 }
                 let c = Some(TokenAndSpan {
                     tok: token::Whitespace,
-                    sp: syntax_pos::mk_sp(start_bpos, self.last_pos),
+                    sp: self.mk_sp(start_bpos, self.pos),
                 });
                 debug!("scanning whitespace: {:?}", c);
                 c
@@ -623,8 +662,8 @@ impl<'a> StringReader<'a> {
     /// Might return a sugared-doc-attr
     fn scan_block_comment(&mut self) -> Option<TokenAndSpan> {
         // block comments starting with "/**" or "/*!" are doc-comments
-        let is_doc_comment = self.curr_is('*') || self.curr_is('!');
-        let start_bpos = self.last_pos - BytePos(2);
+        let is_doc_comment = self.ch_is('*') || self.ch_is('!');
+        let start_bpos = self.pos - BytePos(2);
 
         let mut level: isize = 1;
         let mut has_cr = false;
@@ -635,10 +674,10 @@ impl<'a> StringReader<'a> {
                 } else {
                     "unterminated block comment"
                 };
-                let last_bpos = self.last_pos;
-                panic!(self.fatal_span_(start_bpos, last_bpos, msg));
+                let last_bpos = self.pos;
+                self.fatal_span_(start_bpos, last_bpos, msg).raise();
             }
-            let n = self.curr.unwrap();
+            let n = self.ch.unwrap();
             match n {
                 '/' if self.nextch_is('*') => {
                     level += 1;
@@ -666,14 +705,14 @@ impl<'a> StringReader<'a> {
                 } else {
                     string.into()
                 };
-                token::DocComment(token::intern(&string[..]))
+                token::DocComment(Symbol::intern(&string[..]))
             } else {
                 token::Comment
             };
 
             Some(TokenAndSpan {
-                tok: tok,
-                sp: syntax_pos::mk_sp(start_bpos, self.last_pos),
+                tok,
+                sp: self.mk_sp(start_bpos, self.pos),
             })
         })
     }
@@ -688,7 +727,7 @@ impl<'a> StringReader<'a> {
         assert!(real_radix <= scan_radix);
         let mut len = 0;
         loop {
-            let c = self.curr;
+            let c = self.ch;
             if c == Some('_') {
                 debug!("skipping a _");
                 self.bump();
@@ -700,8 +739,8 @@ impl<'a> StringReader<'a> {
                     // check that the hypothetical digit is actually
                     // in range for the true radix
                     if c.unwrap().to_digit(real_radix).is_none() {
-                        self.err_span_(self.last_pos,
-                                       self.pos,
+                        self.err_span_(self.pos,
+                                       self.next_pos,
                                        &format!("invalid digit for a base {} literal", real_radix));
                     }
                     len += 1;
@@ -716,12 +755,12 @@ impl<'a> StringReader<'a> {
     fn scan_number(&mut self, c: char) -> token::Lit {
         let num_digits;
         let mut base = 10;
-        let start_bpos = self.last_pos;
+        let start_bpos = self.pos;
 
         self.bump();
 
         if c == '0' {
-            match self.curr.unwrap_or('\0') {
+            match self.ch.unwrap_or('\0') {
                 'b' => {
                     self.bump();
                     base = 2;
@@ -737,7 +776,7 @@ impl<'a> StringReader<'a> {
                     base = 16;
                     num_digits = self.scan_digits(16, 16);
                 }
-                '0'...'9' | '_' | '.' => {
+                '0'...'9' | '_' | '.' | 'e' | 'E' => {
                     num_digits = self.scan_digits(10, 10) + 1;
                 }
                 _ => {
@@ -753,38 +792,36 @@ impl<'a> StringReader<'a> {
 
         if num_digits == 0 {
             self.err_span_(start_bpos,
-                           self.last_pos,
+                           self.pos,
                            "no valid digits found for number");
-            return token::Integer(token::intern("0"));
+            return token::Integer(Symbol::intern("0"));
         }
 
         // might be a float, but don't be greedy if this is actually an
         // integer literal followed by field/method access or a range pattern
         // (`0..2` and `12.foo()`)
-        if self.curr_is('.') && !self.nextch_is('.') &&
-           !self.nextch()
-                .unwrap_or('\0')
-                .is_xid_start() {
+        if self.ch_is('.') && !self.nextch_is('.') &&
+           !ident_start(self.nextch()) {
             // might have stuff after the ., and if it does, it needs to start
             // with a number
             self.bump();
-            if self.curr.unwrap_or('\0').is_digit(10) {
+            if self.ch.unwrap_or('\0').is_digit(10) {
                 self.scan_digits(10, 10);
                 self.scan_float_exponent();
             }
-            let last_pos = self.last_pos;
-            self.check_float_base(start_bpos, last_pos, base);
-            return token::Float(self.name_from(start_bpos));
+            let pos = self.pos;
+            self.check_float_base(start_bpos, pos, base);
+            token::Float(self.name_from(start_bpos))
         } else {
             // it might be a float if it has an exponent
-            if self.curr_is('e') || self.curr_is('E') {
+            if self.ch_is('e') || self.ch_is('E') {
                 self.scan_float_exponent();
-                let last_pos = self.last_pos;
-                self.check_float_base(start_bpos, last_pos, base);
+                let pos = self.pos;
+                self.check_float_base(start_bpos, pos, base);
                 return token::Float(self.name_from(start_bpos));
             }
             // but we certainly have an integer!
-            return token::Integer(self.name_from(start_bpos));
+            token::Integer(self.name_from(start_bpos))
         }
     }
 
@@ -792,30 +829,30 @@ impl<'a> StringReader<'a> {
     /// error if too many or too few digits are encountered.
     fn scan_hex_digits(&mut self, n_digits: usize, delim: char, below_0x7f_only: bool) -> bool {
         debug!("scanning {} digits until {:?}", n_digits, delim);
-        let start_bpos = self.last_pos;
+        let start_bpos = self.pos;
         let mut accum_int = 0;
 
         let mut valid = true;
         for _ in 0..n_digits {
             if self.is_eof() {
-                let last_bpos = self.last_pos;
-                panic!(self.fatal_span_(start_bpos,
-                                        last_bpos,
-                                        "unterminated numeric character escape"));
+                let last_bpos = self.pos;
+                self.fatal_span_(start_bpos,
+                                 last_bpos,
+                                 "unterminated numeric character escape").raise();
             }
-            if self.curr_is(delim) {
-                let last_bpos = self.last_pos;
+            if self.ch_is(delim) {
+                let last_bpos = self.pos;
                 self.err_span_(start_bpos,
                                last_bpos,
                                "numeric character escape is too short");
                 valid = false;
                 break;
             }
-            let c = self.curr.unwrap_or('\x00');
+            let c = self.ch.unwrap_or('\x00');
             accum_int *= 16;
             accum_int += c.to_digit(16).unwrap_or_else(|| {
-                self.err_span_char(self.last_pos,
-                                   self.pos,
+                self.err_span_char(self.pos,
+                                   self.next_pos,
                                    "invalid character in numeric character escape",
                                    c);
 
@@ -827,7 +864,7 @@ impl<'a> StringReader<'a> {
 
         if below_0x7f_only && accum_int >= 0x80 {
             self.err_span_(start_bpos,
-                           self.last_pos,
+                           self.pos,
                            "this form of character escape may only be used with characters in \
                             the range [\\x00-\\x7f]");
             valid = false;
@@ -836,7 +873,7 @@ impl<'a> StringReader<'a> {
         match char::from_u32(accum_int) {
             Some(_) => valid,
             None => {
-                let last_bpos = self.last_pos;
+                let last_bpos = self.pos;
                 self.err_span_(start_bpos, last_bpos, "invalid numeric character escape");
                 false
             }
@@ -857,8 +894,8 @@ impl<'a> StringReader<'a> {
         match first_source_char {
             '\\' => {
                 // '\X' for some X must be a character constant:
-                let escaped = self.curr;
-                let escaped_pos = self.last_pos;
+                let escaped = self.ch;
+                let escaped_pos = self.pos;
                 self.bump();
                 match escaped {
                     None => {}  // EOF here is an error that will be checked later.
@@ -867,11 +904,11 @@ impl<'a> StringReader<'a> {
                             'n' | 'r' | 't' | '\\' | '\'' | '"' | '0' => true,
                             'x' => self.scan_byte_escape(delim, !ascii_only),
                             'u' => {
-                                let valid = if self.curr_is('{') {
+                                let valid = if self.ch_is('{') {
                                     self.scan_unicode_escape(delim) && !ascii_only
                                 } else {
-                                    let span = syntax_pos::mk_sp(start, self.last_pos);
-                                    self.span_diagnostic
+                                    let span = self.mk_sp(start, self.pos);
+                                    self.sess.span_diagnostic
                                         .struct_span_err(span, "incorrect unicode escape sequence")
                                         .span_help(span,
                                                    "format of unicode escape sequences is \
@@ -881,7 +918,7 @@ impl<'a> StringReader<'a> {
                                 };
                                 if ascii_only {
                                     self.err_span_(start,
-                                                   self.last_pos,
+                                                   self.pos,
                                                    "unicode escape sequences cannot be used as a \
                                                     byte or in a byte string");
                                 }
@@ -892,14 +929,14 @@ impl<'a> StringReader<'a> {
                                 self.consume_whitespace();
                                 true
                             }
-                            '\r' if delim == '"' && self.curr_is('\n') => {
+                            '\r' if delim == '"' && self.ch_is('\n') => {
                                 self.consume_whitespace();
                                 true
                             }
                             c => {
-                                let last_pos = self.last_pos;
+                                let pos = self.pos;
                                 let mut err = self.struct_err_span_char(escaped_pos,
-                                                                        last_pos,
+                                                                        pos,
                                                                         if ascii_only {
                                                                             "unknown byte escape"
                                                                         } else {
@@ -908,13 +945,13 @@ impl<'a> StringReader<'a> {
                                                                         },
                                                                         c);
                                 if e == '\r' {
-                                    err.span_help(syntax_pos::mk_sp(escaped_pos, last_pos),
+                                    err.span_help(self.mk_sp(escaped_pos, pos),
                                                   "this is an isolated carriage return; consider \
                                                    checking your editor and version control \
                                                    settings");
                                 }
                                 if (e == '{' || e == '}') && !ascii_only {
-                                    err.span_help(syntax_pos::mk_sp(escaped_pos, last_pos),
+                                    err.span_help(self.mk_sp(escaped_pos, pos),
                                                   "if used in a formatting string, curly braces \
                                                    are escaped with `{{` and `}}`");
                                 }
@@ -926,9 +963,9 @@ impl<'a> StringReader<'a> {
                 }
             }
             '\t' | '\n' | '\r' | '\'' if delim == '\'' => {
-                let last_pos = self.last_pos;
+                let pos = self.pos;
                 self.err_span_char(start,
-                                   last_pos,
+                                   pos,
                                    if ascii_only {
                                        "byte constant must be escaped"
                                    } else {
@@ -938,21 +975,21 @@ impl<'a> StringReader<'a> {
                 return false;
             }
             '\r' => {
-                if self.curr_is('\n') {
+                if self.ch_is('\n') {
                     self.bump();
                     return true;
                 } else {
                     self.err_span_(start,
-                                   self.last_pos,
+                                   self.pos,
                                    "bare CR not allowed in string, use \\r instead");
                     return false;
                 }
             }
             _ => {
                 if ascii_only && first_source_char > '\x7F' {
-                    let last_pos = self.last_pos;
+                    let pos = self.pos;
                     self.err_span_(start,
-                                   last_pos,
+                                   pos,
                                    "byte constant must be ASCII. Use a \\xHH escape for a \
                                     non-ASCII byte");
                     return false;
@@ -962,73 +999,80 @@ impl<'a> StringReader<'a> {
         true
     }
 
-    /// Scan over a \u{...} escape
+    /// Scan over a `\u{...}` escape
     ///
-    /// At this point, we have already seen the \ and the u, the { is the current character. We
-    /// will read at least one digit, and up to 6, and pass over the }.
+    /// At this point, we have already seen the `\` and the `u`, the `{` is the current character.
+    /// We will read a hex number (with `_` separators), with 1 to 6 actual digits,
+    /// and pass over the `}`.
     fn scan_unicode_escape(&mut self, delim: char) -> bool {
         self.bump(); // past the {
-        let start_bpos = self.last_pos;
-        let mut count = 0;
-        let mut accum_int = 0;
+        let start_bpos = self.pos;
         let mut valid = true;
 
-        while !self.curr_is('}') && count <= 6 {
-            let c = match self.curr {
-                Some(c) => c,
-                None => {
-                    panic!(self.fatal_span_(start_bpos,
-                                            self.last_pos,
-                                            "unterminated unicode escape (found EOF)"));
-                }
-            };
-            accum_int *= 16;
-            accum_int += c.to_digit(16).unwrap_or_else(|| {
-                if c == delim {
-                    panic!(self.fatal_span_(self.last_pos,
-                                            self.pos,
-                                            "unterminated unicode escape (needed a `}`)"));
-                } else {
-                    self.err_span_char(self.last_pos,
-                                       self.pos,
-                                       "invalid character in unicode escape",
-                                       c);
-                }
-                valid = false;
-                0
-            });
-            self.bump();
-            count += 1;
+        if let Some('_') = self.ch {
+            // disallow leading `_`
+            self.err_span_(self.pos,
+                           self.next_pos,
+                           "invalid start of unicode escape");
+            valid = false;
         }
 
+        let count = self.scan_digits(16, 16);
+
         if count > 6 {
             self.err_span_(start_bpos,
-                           self.last_pos,
-                           "overlong unicode escape (can have at most 6 hex digits)");
+                           self.pos,
+                           "overlong unicode escape (must have at most 6 hex digits)");
             valid = false;
         }
-
-        if valid && (char::from_u32(accum_int).is_none() || count == 0) {
-            self.err_span_(start_bpos,
-                           self.last_pos,
-                           "invalid unicode character escape");
-            valid = false;
+        loop {
+            match self.ch {
+                Some('}') => {
+                    if valid && count == 0 {
+                        self.err_span_(start_bpos,
+                                       self.pos,
+                                       "empty unicode escape (must have at least 1 hex digit)");
+                        valid = false;
+                    }
+                    self.bump(); // past the ending `}`
+                    break;
+                },
+                Some(c) => {
+                    if c == delim {
+                        self.err_span_(self.pos,
+                                       self.pos,
+                                       "unterminated unicode escape (needed a `}`)");
+                        valid = false;
+                        break;
+                    } else if valid {
+                        self.err_span_char(start_bpos,
+                                           self.pos,
+                                           "invalid character in unicode escape",
+                                           c);
+                        valid = false;
+                    }
+                },
+                None => {
+                    self.fatal_span_(start_bpos,
+                                     self.pos,
+                                     "unterminated unicode escape (found EOF)").raise();
+                }
+            }
+            self.bump();
         }
-
-        self.bump(); // past the ending }
         valid
     }
 
     /// Scan over a float exponent.
     fn scan_float_exponent(&mut self) {
-        if self.curr_is('e') || self.curr_is('E') {
+        if self.ch_is('e') || self.ch_is('E') {
             self.bump();
-            if self.curr_is('-') || self.curr_is('+') {
+            if self.ch_is('-') || self.ch_is('+') {
                 self.bump();
             }
             if self.scan_digits(10, 10) == 0 {
-                self.err_span_(self.last_pos,
-                               self.pos,
+                self.err_span_(self.pos,
+                               self.next_pos,
                                "expected at least one digit in exponent")
             }
         }
@@ -1059,44 +1103,65 @@ impl<'a> StringReader<'a> {
 
     fn binop(&mut self, op: token::BinOpToken) -> token::Token {
         self.bump();
-        if self.curr_is('=') {
+        if self.ch_is('=') {
             self.bump();
-            return token::BinOpEq(op);
+            token::BinOpEq(op)
         } else {
-            return token::BinOp(op);
+            token::BinOp(op)
         }
     }
 
     /// Return the next token from the string, advances the input past that
     /// token, and updates the interner
     fn next_token_inner(&mut self) -> Result<token::Token, ()> {
-        let c = self.curr;
-        if ident_start(c) &&
-           match (c.unwrap(), self.nextch(), self.nextnextch()) {
-            // Note: r as in r" or r#" is part of a raw string literal,
-            // b as in b' is part of a byte literal.
-            // They are not identifiers, and are handled further down.
-            ('r', Some('"'), _) |
-            ('r', Some('#'), _) |
-            ('b', Some('"'), _) |
-            ('b', Some('\''), _) |
-            ('b', Some('r'), Some('"')) |
-            ('b', Some('r'), Some('#')) => false,
-            _ => true,
-        } {
-            let start = self.last_pos;
-            while ident_continue(self.curr) {
-                self.bump();
-            }
+        let c = self.ch;
+
+        if ident_start(c) {
+            let (is_ident_start, is_raw_ident) =
+                match (c.unwrap(), self.nextch(), self.nextnextch()) {
+                    // r# followed by an identifier starter is a raw identifier.
+                    // This is an exception to the r# case below.
+                    ('r', Some('#'), x) if ident_start(x) => (true, true),
+                    // r as in r" or r#" is part of a raw string literal.
+                    // b as in b' is part of a byte literal.
+                    // They are not identifiers, and are handled further down.
+                    ('r', Some('"'), _) |
+                    ('r', Some('#'), _) |
+                    ('b', Some('"'), _) |
+                    ('b', Some('\''), _) |
+                    ('b', Some('r'), Some('"')) |
+                    ('b', Some('r'), Some('#')) => (false, false),
+                    _ => (true, false),
+                };
+            if is_ident_start {
+                let raw_start = self.pos;
+                if is_raw_ident {
+                    // Consume the 'r#' characters.
+                    self.bump();
+                    self.bump();
+                }
 
-            return Ok(self.with_str_from(start, |string| {
-                if string == "_" {
-                    token::Underscore
-                } else {
-                    // FIXME: perform NFKC normalization here. (Issue #2253)
-                    token::Ident(str_to_ident(string))
+                let start = self.pos;
+                while ident_continue(self.ch) {
+                    self.bump();
                 }
-            }));
+
+                return Ok(self.with_str_from(start, |string| {
+                    // FIXME: perform NFKC normalization here. (Issue #2253)
+                    let ident = self.mk_ident(string);
+                    if is_raw_ident && (token::is_path_segment_keyword(ident) ||
+                                        ident.name == keywords::Underscore.name()) {
+                        self.fatal_span_(raw_start, self.pos,
+                            &format!("`r#{}` is not currently supported.", ident.name)
+                        ).raise();
+                    }
+                    if is_raw_ident {
+                        let span = self.mk_sp(raw_start, self.pos);
+                        self.sess.raw_identifier_spans.borrow_mut().push(span);
+                    }
+                    token::Ident(ident, is_raw_ident)
+                }));
+            }
         }
 
         if is_dec_digit(c) {
@@ -1110,163 +1175,166 @@ impl<'a> StringReader<'a> {
             // One-byte tokens.
             ';' => {
                 self.bump();
-                return Ok(token::Semi);
+                Ok(token::Semi)
             }
             ',' => {
                 self.bump();
-                return Ok(token::Comma);
+                Ok(token::Comma)
             }
             '.' => {
                 self.bump();
-                return if self.curr_is('.') {
+                if self.ch_is('.') {
                     self.bump();
-                    if self.curr_is('.') {
+                    if self.ch_is('.') {
                         self.bump();
                         Ok(token::DotDotDot)
+                    } else if self.ch_is('=') {
+                        self.bump();
+                        Ok(token::DotDotEq)
                     } else {
                         Ok(token::DotDot)
                     }
                 } else {
                     Ok(token::Dot)
-                };
+                }
             }
             '(' => {
                 self.bump();
-                return Ok(token::OpenDelim(token::Paren));
+                Ok(token::OpenDelim(token::Paren))
             }
             ')' => {
                 self.bump();
-                return Ok(token::CloseDelim(token::Paren));
+                Ok(token::CloseDelim(token::Paren))
             }
             '{' => {
                 self.bump();
-                return Ok(token::OpenDelim(token::Brace));
+                Ok(token::OpenDelim(token::Brace))
             }
             '}' => {
                 self.bump();
-                return Ok(token::CloseDelim(token::Brace));
+                Ok(token::CloseDelim(token::Brace))
             }
             '[' => {
                 self.bump();
-                return Ok(token::OpenDelim(token::Bracket));
+                Ok(token::OpenDelim(token::Bracket))
             }
             ']' => {
                 self.bump();
-                return Ok(token::CloseDelim(token::Bracket));
+                Ok(token::CloseDelim(token::Bracket))
             }
             '@' => {
                 self.bump();
-                return Ok(token::At);
+                Ok(token::At)
             }
             '#' => {
                 self.bump();
-                return Ok(token::Pound);
+                Ok(token::Pound)
             }
             '~' => {
                 self.bump();
-                return Ok(token::Tilde);
+                Ok(token::Tilde)
             }
             '?' => {
                 self.bump();
-                return Ok(token::Question);
+                Ok(token::Question)
             }
             ':' => {
                 self.bump();
-                if self.curr_is(':') {
+                if self.ch_is(':') {
                     self.bump();
-                    return Ok(token::ModSep);
+                    Ok(token::ModSep)
                 } else {
-                    return Ok(token::Colon);
+                    Ok(token::Colon)
                 }
             }
 
             '$' => {
                 self.bump();
-                return Ok(token::Dollar);
+                Ok(token::Dollar)
             }
 
             // Multi-byte tokens.
             '=' => {
                 self.bump();
-                if self.curr_is('=') {
+                if self.ch_is('=') {
                     self.bump();
-                    return Ok(token::EqEq);
-                } else if self.curr_is('>') {
+                    Ok(token::EqEq)
+                } else if self.ch_is('>') {
                     self.bump();
-                    return Ok(token::FatArrow);
+                    Ok(token::FatArrow)
                 } else {
-                    return Ok(token::Eq);
+                    Ok(token::Eq)
                 }
             }
             '!' => {
                 self.bump();
-                if self.curr_is('=') {
+                if self.ch_is('=') {
                     self.bump();
-                    return Ok(token::Ne);
+                    Ok(token::Ne)
                 } else {
-                    return Ok(token::Not);
+                    Ok(token::Not)
                 }
             }
             '<' => {
                 self.bump();
-                match self.curr.unwrap_or('\x00') {
+                match self.ch.unwrap_or('\x00') {
                     '=' => {
                         self.bump();
-                        return Ok(token::Le);
+                        Ok(token::Le)
                     }
                     '<' => {
-                        return Ok(self.binop(token::Shl));
+                        Ok(self.binop(token::Shl))
                     }
                     '-' => {
                         self.bump();
-                        match self.curr.unwrap_or('\x00') {
+                        match self.ch.unwrap_or('\x00') {
                             _ => {
-                                return Ok(token::LArrow);
+                                Ok(token::LArrow)
                             }
                         }
                     }
                     _ => {
-                        return Ok(token::Lt);
+                        Ok(token::Lt)
                     }
                 }
             }
             '>' => {
                 self.bump();
-                match self.curr.unwrap_or('\x00') {
+                match self.ch.unwrap_or('\x00') {
                     '=' => {
                         self.bump();
-                        return Ok(token::Ge);
+                        Ok(token::Ge)
                     }
                     '>' => {
-                        return Ok(self.binop(token::Shr));
+                        Ok(self.binop(token::Shr))
                     }
                     _ => {
-                        return Ok(token::Gt);
+                        Ok(token::Gt)
                     }
                 }
             }
             '\'' => {
                 // Either a character constant 'a' OR a lifetime name 'abc
-                let start_with_quote = self.last_pos;
+                let start_with_quote = self.pos;
                 self.bump();
-                let start = self.last_pos;
+                let start = self.pos;
 
                 // the eof will be picked up by the final `'` check below
-                let c2 = self.curr.unwrap_or('\x00');
+                let c2 = self.ch.unwrap_or('\x00');
                 self.bump();
 
                 // If the character is an ident start not followed by another single
                 // quote, then this is a lifetime name:
-                if ident_start(Some(c2)) && !self.curr_is('\'') {
-                    while ident_continue(self.curr) {
+                if ident_start(Some(c2)) && !self.ch_is('\'') {
+                    while ident_continue(self.ch) {
                         self.bump();
                     }
                     // lifetimes shouldn't end with a single quote
                     // if we find one, then this is an invalid character literal
-                    if self.curr_is('\'') {
-                        panic!(self.fatal_span_verbose(
-                               start_with_quote, self.pos,
-                               String::from("character literal may only contain one codepoint")));
+                    if self.ch_is('\'') {
+                        self.fatal_span_verbose(start_with_quote, self.next_pos,
+                                String::from("character literal may only contain one codepoint"))
+                            .raise();
 
                     }
 
@@ -1274,21 +1342,9 @@ impl<'a> StringReader<'a> {
                     // expansion purposes. See #12512 for the gory details of why
                     // this is necessary.
                     let ident = self.with_str_from(start, |lifetime_name| {
-                        str_to_ident(&format!("'{}", lifetime_name))
+                        self.mk_ident(&format!("'{}", lifetime_name))
                     });
 
-                    // Conjure up a "keyword checking ident" to make sure that
-                    // the lifetime name is not a keyword.
-                    let keyword_checking_ident = self.with_str_from(start, |lifetime_name| {
-                        str_to_ident(lifetime_name)
-                    });
-                    let keyword_checking_token = &token::Ident(keyword_checking_ident);
-                    let last_bpos = self.last_pos;
-                    if keyword_checking_token.is_any_keyword() &&
-                       !keyword_checking_token.is_keyword(keywords::Static) {
-                        self.err_span_(start, last_bpos, "lifetimes cannot use keyword names");
-                    }
-
                     return Ok(token::Lifetime(ident));
                 }
 
@@ -1298,46 +1354,71 @@ impl<'a> StringReader<'a> {
                                                    false,
                                                    '\'');
 
-                if !self.curr_is('\'') {
-                    panic!(self.fatal_span_verbose(
-                           start_with_quote, self.last_pos,
-                           String::from("character literal may only contain one codepoint")));
+                if !self.ch_is('\'') {
+                    let pos = self.pos;
+                    loop {
+                        self.bump();
+                        if self.ch_is('\'') {
+                            let start = self.byte_offset(start).to_usize();
+                            let end = self.byte_offset(self.pos).to_usize();
+                            self.bump();
+                            let span = self.mk_sp(start_with_quote, self.pos);
+                            self.sess.span_diagnostic
+                                .struct_span_err(span,
+                                                 "character literal may only contain one codepoint")
+                                .span_suggestion(span,
+                                                 "if you meant to write a `str` literal, \
+                                                  use double quotes",
+                                                 format!("\"{}\"",
+                                                         &self.source_text[start..end]))
+                                .emit();
+                            return Ok(token::Literal(token::Str_(Symbol::intern("??")), None))
+                        }
+                        if self.ch_is('\n') || self.is_eof() || self.ch_is('/') {
+                            // Only attempt to infer single line string literals. If we encounter
+                            // a slash, bail out in order to avoid nonsensical suggestion when
+                            // involving comments.
+                            break;
+                        }
+                    }
+                    self.fatal_span_verbose(start_with_quote, pos,
+                        String::from("character literal may only contain one codepoint")).raise();
                 }
 
                 let id = if valid {
                     self.name_from(start)
                 } else {
-                    token::intern("0")
+                    Symbol::intern("0")
                 };
-                self.bump(); // advance curr past token
+                self.bump(); // advance ch past token
                 let suffix = self.scan_optional_raw_name();
-                return Ok(token::Literal(token::Char(id), suffix));
+                Ok(token::Literal(token::Char(id), suffix))
             }
             'b' => {
                 self.bump();
-                let lit = match self.curr {
+                let lit = match self.ch {
                     Some('\'') => self.scan_byte(),
                     Some('"') => self.scan_byte_string(),
                     Some('r') => self.scan_raw_byte_string(),
                     _ => unreachable!(),  // Should have been a token::Ident above.
                 };
                 let suffix = self.scan_optional_raw_name();
-                return Ok(token::Literal(lit, suffix));
+                Ok(token::Literal(lit, suffix))
             }
             '"' => {
-                let start_bpos = self.last_pos;
+                let start_bpos = self.pos;
                 let mut valid = true;
                 self.bump();
-                while !self.curr_is('"') {
+                while !self.ch_is('"') {
                     if self.is_eof() {
-                        let last_bpos = self.last_pos;
-                        panic!(self.fatal_span_(start_bpos,
-                                                last_bpos,
-                                                "unterminated double quote string"));
+                        let last_bpos = self.pos;
+                        self.fatal_span_(start_bpos,
+                                         last_bpos,
+                                         "unterminated double quote string").raise();
                     }
 
-                    let ch_start = self.last_pos;
-                    let ch = self.curr.unwrap();
+                    let ch_start = self.pos;
+                    let ch = self.ch.unwrap();
                     self.bump();
                     valid &= self.scan_char_or_byte(ch_start,
                                                     ch,
@@ -1349,55 +1430,53 @@ impl<'a> StringReader<'a> {
                 let id = if valid {
                     self.name_from(start_bpos + BytePos(1))
                 } else {
-                    token::intern("??")
+                    Symbol::intern("??")
                 };
                 self.bump();
                 let suffix = self.scan_optional_raw_name();
-                return Ok(token::Literal(token::Str_(id), suffix));
+                Ok(token::Literal(token::Str_(id), suffix))
             }
             'r' => {
-                let start_bpos = self.last_pos;
+                let start_bpos = self.pos;
                 self.bump();
-                let mut hash_count = 0;
-                while self.curr_is('#') {
+                let mut hash_count: u16 = 0;
+                while self.ch_is('#') {
                     self.bump();
                     hash_count += 1;
                 }
 
                 if self.is_eof() {
-                    let last_bpos = self.last_pos;
-                    panic!(self.fatal_span_(start_bpos, last_bpos, "unterminated raw string"));
-                } else if !self.curr_is('"') {
-                    let last_bpos = self.last_pos;
-                    let curr_char = self.curr.unwrap();
-                    panic!(self.fatal_span_char(start_bpos,
-                                                last_bpos,
-                                                "found invalid character; only `#` is allowed \
-                                                 in raw string delimitation",
-                                                curr_char));
+                    self.fail_unterminated_raw_string(start_bpos, hash_count);
+                } else if !self.ch_is('"') {
+                    let last_bpos = self.pos;
+                    let curr_char = self.ch.unwrap();
+                    self.fatal_span_char(start_bpos,
+                                         last_bpos,
+                                         "found invalid character; only `#` is allowed \
+                                         in raw string delimitation",
+                                         curr_char).raise();
                 }
                 self.bump();
-                let content_start_bpos = self.last_pos;
+                let content_start_bpos = self.pos;
                 let mut content_end_bpos;
                 let mut valid = true;
                 'outer: loop {
                     if self.is_eof() {
-                        let last_bpos = self.last_pos;
-                        panic!(self.fatal_span_(start_bpos, last_bpos, "unterminated raw string"));
+                        self.fail_unterminated_raw_string(start_bpos, hash_count);
                     }
-                    // if self.curr_is('"') {
-                    // content_end_bpos = self.last_pos;
+                    // if self.ch_is('"') {
+                    // content_end_bpos = self.pos;
                     // for _ in 0..hash_count {
                     // self.bump();
-                    // if !self.curr_is('#') {
+                    // if !self.ch_is('#') {
                     // continue 'outer;
-                    let c = self.curr.unwrap();
+                    let c = self.ch.unwrap();
                     match c {
                         '"' => {
-                            content_end_bpos = self.last_pos;
+                            content_end_bpos = self.pos;
                             for _ in 0..hash_count {
                                 self.bump();
-                                if !self.curr_is('#') {
+                                if !self.ch_is('#') {
                                     continue 'outer;
                                 }
                             }
@@ -1405,7 +1484,7 @@ impl<'a> StringReader<'a> {
                         }
                         '\r' => {
                             if !self.nextch_is('\n') {
-                                let last_bpos = self.last_pos;
+                                let last_bpos = self.pos;
                                 self.err_span_(start_bpos,
                                                last_bpos,
                                                "bare CR not allowed in raw string, use \\r \
@@ -1421,27 +1500,27 @@ impl<'a> StringReader<'a> {
                 let id = if valid {
                     self.name_from_to(content_start_bpos, content_end_bpos)
                 } else {
-                    token::intern("??")
+                    Symbol::intern("??")
                 };
                 let suffix = self.scan_optional_raw_name();
-                return Ok(token::Literal(token::StrRaw(id, hash_count), suffix));
+                Ok(token::Literal(token::StrRaw(id, hash_count), suffix))
             }
             '-' => {
                 if self.nextch_is('>') {
                     self.bump();
                     self.bump();
-                    return Ok(token::RArrow);
+                    Ok(token::RArrow)
                 } else {
-                    return Ok(self.binop(token::Minus));
+                    Ok(self.binop(token::Minus))
                 }
             }
             '&' => {
                 if self.nextch_is('&') {
                     self.bump();
                     self.bump();
-                    return Ok(token::AndAnd);
+                    Ok(token::AndAnd)
                 } else {
-                    return Ok(self.binop(token::And));
+                    Ok(self.binop(token::And))
                 }
             }
             '|' => {
@@ -1449,36 +1528,36 @@ impl<'a> StringReader<'a> {
                     Some('|') => {
                         self.bump();
                         self.bump();
-                        return Ok(token::OrOr);
+                        Ok(token::OrOr)
                     }
                     _ => {
-                        return Ok(self.binop(token::Or));
+                        Ok(self.binop(token::Or))
                     }
                 }
             }
             '+' => {
-                return Ok(self.binop(token::Plus));
+                Ok(self.binop(token::Plus))
             }
             '*' => {
-                return Ok(self.binop(token::Star));
+                Ok(self.binop(token::Star))
             }
             '/' => {
-                return Ok(self.binop(token::Slash));
+                Ok(self.binop(token::Slash))
             }
             '^' => {
-                return Ok(self.binop(token::Caret));
+                Ok(self.binop(token::Caret))
             }
             '%' => {
-                return Ok(self.binop(token::Percent));
+                Ok(self.binop(token::Percent))
             }
             c => {
-                let last_bpos = self.last_pos;
-                let bpos = self.pos;
+                let last_bpos = self.pos;
+                let bpos = self.next_pos;
                 let mut err = self.struct_fatal_span_char(last_bpos,
                                                           bpos,
                                                           "unknown start of token",
                                                           c);
-                unicode_chars::check_for_substitution(&self, c, &mut err);
+                unicode_chars::check_for_substitution(self, c, &mut err);
                 self.fatal_errs.push(err);
                 Err(())
             }
@@ -1486,48 +1565,48 @@ impl<'a> StringReader<'a> {
     }
 
     fn consume_whitespace(&mut self) {
-        while is_pattern_whitespace(self.curr) && !self.is_eof() {
+        while is_pattern_whitespace(self.ch) && !self.is_eof() {
             self.bump();
         }
     }
 
     fn read_to_eol(&mut self) -> String {
         let mut val = String::new();
-        while !self.curr_is('\n') && !self.is_eof() {
-            val.push(self.curr.unwrap());
+        while !self.ch_is('\n') && !self.is_eof() {
+            val.push(self.ch.unwrap());
             self.bump();
         }
-        if self.curr_is('\n') {
+        if self.ch_is('\n') {
             self.bump();
         }
-        return val;
+        val
     }
 
     fn read_one_line_comment(&mut self) -> String {
         let val = self.read_to_eol();
         assert!((val.as_bytes()[0] == b'/' && val.as_bytes()[1] == b'/') ||
                 (val.as_bytes()[0] == b'#' && val.as_bytes()[1] == b'!'));
-        return val;
+        val
     }
 
     fn consume_non_eol_whitespace(&mut self) {
-        while is_pattern_whitespace(self.curr) && !self.curr_is('\n') && !self.is_eof() {
+        while is_pattern_whitespace(self.ch) && !self.ch_is('\n') && !self.is_eof() {
             self.bump();
         }
     }
 
     fn peeking_at_comment(&self) -> bool {
-        (self.curr_is('/') && self.nextch_is('/')) || (self.curr_is('/') && self.nextch_is('*')) ||
+        (self.ch_is('/') && self.nextch_is('/')) || (self.ch_is('/') && self.nextch_is('*')) ||
         // consider shebangs comments, but not inner attributes
-        (self.curr_is('#') && self.nextch_is('!') && !self.nextnextch_is('['))
+        (self.ch_is('#') && self.nextch_is('!') && !self.nextnextch_is('['))
     }
 
     fn scan_byte(&mut self) -> token::Lit {
         self.bump();
-        let start = self.last_pos;
+        let start = self.pos;
 
         // the eof will be picked up by the final `'` check below
-        let c2 = self.curr.unwrap_or('\x00');
+        let c2 = self.ch.unwrap_or('\x00');
         self.bump();
 
         let valid = self.scan_char_or_byte(start,
@@ -1535,23 +1614,23 @@ impl<'a> StringReader<'a> {
                                            // ascii_only =
                                            true,
                                            '\'');
-        if !self.curr_is('\'') {
+        if !self.ch_is('\'') {
             // Byte offsetting here is okay because the
             // character before position `start` are an
             // ascii single quote and ascii 'b'.
-            let last_pos = self.last_pos;
-            panic!(self.fatal_span_verbose(start - BytePos(2),
-                                           last_pos,
-                                           "unterminated byte constant".to_string()));
+            let pos = self.pos;
+            self.fatal_span_verbose(start - BytePos(2),
+                                    pos,
+                                    "unterminated byte constant".to_string()).raise();
         }
 
         let id = if valid {
             self.name_from(start)
         } else {
-            token::intern("?")
+            Symbol::intern("?")
         };
-        self.bump(); // advance curr past token
-        return token::Byte(id);
+        self.bump(); // advance ch past token
+        token::Byte(id)
     }
 
     fn scan_byte_escape(&mut self, delim: char, below_0x7f_only: bool) -> bool {
@@ -1560,17 +1639,17 @@ impl<'a> StringReader<'a> {
 
     fn scan_byte_string(&mut self) -> token::Lit {
         self.bump();
-        let start = self.last_pos;
+        let start = self.pos;
         let mut valid = true;
 
-        while !self.curr_is('"') {
+        while !self.ch_is('"') {
             if self.is_eof() {
-                let last_pos = self.last_pos;
-                panic!(self.fatal_span_(start, last_pos, "unterminated double quote byte string"));
+                let pos = self.pos;
+                self.fatal_span_(start, pos, "unterminated double quote byte string").raise();
             }
 
-            let ch_start = self.last_pos;
-            let ch = self.curr.unwrap();
+            let ch_start = self.pos;
+            let ch = self.ch.unwrap();
             self.bump();
             valid &= self.scan_char_or_byte(ch_start,
                                             ch,
@@ -1581,47 +1660,45 @@ impl<'a> StringReader<'a> {
         let id = if valid {
             self.name_from(start)
         } else {
-            token::intern("??")
+            Symbol::intern("??")
         };
         self.bump();
-        return token::ByteStr(id);
+        token::ByteStr(id)
     }
 
     fn scan_raw_byte_string(&mut self) -> token::Lit {
-        let start_bpos = self.last_pos;
+        let start_bpos = self.pos;
         self.bump();
         let mut hash_count = 0;
-        while self.curr_is('#') {
+        while self.ch_is('#') {
             self.bump();
             hash_count += 1;
         }
 
         if self.is_eof() {
-            let last_pos = self.last_pos;
-            panic!(self.fatal_span_(start_bpos, last_pos, "unterminated raw string"));
-        } else if !self.curr_is('"') {
-            let last_pos = self.last_pos;
-            let ch = self.curr.unwrap();
-            panic!(self.fatal_span_char(start_bpos,
-                                        last_pos,
+            self.fail_unterminated_raw_string(start_bpos, hash_count);
+        } else if !self.ch_is('"') {
+            let pos = self.pos;
+            let ch = self.ch.unwrap();
+            self.fatal_span_char(start_bpos,
+                                        pos,
                                         "found invalid character; only `#` is allowed in raw \
                                          string delimitation",
-                                        ch));
+                                        ch).raise();
         }
         self.bump();
-        let content_start_bpos = self.last_pos;
+        let content_start_bpos = self.pos;
         let mut content_end_bpos;
         'outer: loop {
-            match self.curr {
+            match self.ch {
                 None => {
-                    let last_pos = self.last_pos;
-                    panic!(self.fatal_span_(start_bpos, last_pos, "unterminated raw string"))
+                    self.fail_unterminated_raw_string(start_bpos, hash_count);
                 }
                 Some('"') => {
-                    content_end_bpos = self.last_pos;
+                    content_end_bpos = self.pos;
                     for _ in 0..hash_count {
                         self.bump();
-                        if !self.curr_is('#') {
+                        if !self.ch_is('#') {
                             continue 'outer;
                         }
                     }
@@ -1629,16 +1706,16 @@ impl<'a> StringReader<'a> {
                 }
                 Some(c) => {
                     if c > '\x7F' {
-                        let last_pos = self.last_pos;
-                        self.err_span_char(last_pos, last_pos, "raw byte string must be ASCII", c);
+                        let pos = self.pos;
+                        self.err_span_char(pos, pos, "raw byte string must be ASCII", c);
                     }
                 }
             }
             self.bump();
         }
         self.bump();
-        return token::ByteStrRaw(self.name_from_to(content_start_bpos, content_end_bpos),
-                                 hash_count);
+        token::ByteStrRaw(self.name_from_to(content_start_bpos, content_end_bpos),
+                                 hash_count)
     }
 }
 
@@ -1656,7 +1733,7 @@ fn in_range(c: Option<char>, lo: char, hi: char) -> bool {
 }
 
 fn is_dec_digit(c: Option<char>) -> bool {
-    return in_range(c, '0', '9');
+    in_range(c, '0', '9')
 }
 
 pub fn is_doc_comment(s: &str) -> bool {
@@ -1697,67 +1774,77 @@ fn ident_continue(c: Option<char>) -> bool {
 mod tests {
     use super::*;
 
+    use ast::{Ident, CrateConfig};
+    use symbol::Symbol;
     use syntax_pos::{BytePos, Span, NO_EXPANSION};
     use codemap::CodeMap;
     use errors;
+    use feature_gate::UnstableFeatures;
     use parse::token;
-    use parse::token::str_to_ident;
+    use std::collections::HashSet;
     use std::io;
-    use std::rc::Rc;
-
-    fn mk_sh(cm: Rc<CodeMap>) -> errors::Handler {
-        // FIXME (#22405): Replace `Box::new` with `box` here when/if possible.
+    use std::path::PathBuf;
+    use diagnostics::plugin::ErrorMap;
+    use rustc_data_structures::sync::Lock;
+    use with_globals;
+    fn mk_sess(cm: Lrc<CodeMap>) -> ParseSess {
         let emitter = errors::emitter::EmitterWriter::new(Box::new(io::sink()),
-                                                Some(cm));
-        errors::Handler::with_emitter(true, false, Box::new(emitter))
+                                                          Some(cm.clone()),
+                                                          false,
+                                                          false);
+        ParseSess {
+            span_diagnostic: errors::Handler::with_emitter(true, false, Box::new(emitter)),
+            unstable_features: UnstableFeatures::from_environment(),
+            config: CrateConfig::new(),
+            included_mod_stack: Lock::new(Vec::new()),
+            code_map: cm,
+            missing_fragment_specifiers: Lock::new(HashSet::new()),
+            raw_identifier_spans: Lock::new(Vec::new()),
+            registered_diagnostics: Lock::new(ErrorMap::new()),
+            non_modrs_mods: Lock::new(vec![]),
+        }
     }
 
     // open a string reader for the given string
     fn setup<'a>(cm: &CodeMap,
-                 span_handler: &'a errors::Handler,
+                 sess: &'a ParseSess,
                  teststr: String)
                  -> StringReader<'a> {
-        let fm = cm.new_filemap("zebra.rs".to_string(), None, teststr);
-        StringReader::new(span_handler, fm)
+        let fm = cm.new_filemap(PathBuf::from("zebra.rs").into(), teststr);
+        StringReader::new(sess, fm)
     }
 
     #[test]
     fn t1() {
-        let cm = Rc::new(CodeMap::new());
-        let sh = mk_sh(cm.clone());
-        let mut string_reader = setup(&cm,
-                                      &sh,
-                                      "/* my source file */ fn main() { println!(\"zebra\"); }\n"
-                                          .to_string());
-        let id = str_to_ident("fn");
-        assert_eq!(string_reader.next_token().tok, token::Comment);
-        assert_eq!(string_reader.next_token().tok, token::Whitespace);
-        let tok1 = string_reader.next_token();
-        let tok2 = TokenAndSpan {
-            tok: token::Ident(id),
-            sp: Span {
-                lo: BytePos(21),
-                hi: BytePos(23),
-                expn_id: NO_EXPANSION,
-            },
-        };
-        assert_eq!(tok1, tok2);
-        assert_eq!(string_reader.next_token().tok, token::Whitespace);
-        // the 'main' id is already read:
-        assert_eq!(string_reader.last_pos.clone(), BytePos(28));
-        // read another token:
-        let tok3 = string_reader.next_token();
-        let tok4 = TokenAndSpan {
-            tok: token::Ident(str_to_ident("main")),
-            sp: Span {
-                lo: BytePos(24),
-                hi: BytePos(28),
-                expn_id: NO_EXPANSION,
-            },
-        };
-        assert_eq!(tok3, tok4);
-        // the lparen is already read:
-        assert_eq!(string_reader.last_pos.clone(), BytePos(29))
+        with_globals(|| {
+            let cm = Lrc::new(CodeMap::new(FilePathMapping::empty()));
+            let sh = mk_sess(cm.clone());
+            let mut string_reader = setup(&cm,
+                                        &sh,
+                                        "/* my source file */ fn main() { println!(\"zebra\"); }\n"
+                                            .to_string());
+            let id = Ident::from_str("fn");
+            assert_eq!(string_reader.next_token().tok, token::Comment);
+            assert_eq!(string_reader.next_token().tok, token::Whitespace);
+            let tok1 = string_reader.next_token();
+            let tok2 = TokenAndSpan {
+                tok: token::Ident(id, false),
+                sp: Span::new(BytePos(21), BytePos(23), NO_EXPANSION),
+            };
+            assert_eq!(tok1, tok2);
+            assert_eq!(string_reader.next_token().tok, token::Whitespace);
+            // the 'main' id is already read:
+            assert_eq!(string_reader.pos.clone(), BytePos(28));
+            // read another token:
+            let tok3 = string_reader.next_token();
+            let tok4 = TokenAndSpan {
+                tok: mk_ident("main"),
+                sp: Span::new(BytePos(24), BytePos(28), NO_EXPANSION),
+            };
+            assert_eq!(tok3, tok4);
+            // the lparen is already read:
+            assert_eq!(string_reader.pos.clone(), BytePos(29))
+        })
     }
 
     // check that the given reader produces the desired stream
@@ -1770,118 +1857,138 @@ mod tests {
 
     // make the identifier by looking up the string in the interner
     fn mk_ident(id: &str) -> token::Token {
-        token::Ident(str_to_ident(id))
+        token::Token::from_ast_ident(Ident::from_str(id))
     }
 
     #[test]
     fn doublecolonparsing() {
-        let cm = Rc::new(CodeMap::new());
-        let sh = mk_sh(cm.clone());
-        check_tokenization(setup(&cm, &sh, "a b".to_string()),
-                           vec![mk_ident("a"), token::Whitespace, mk_ident("b")]);
+        with_globals(|| {
+            let cm = Lrc::new(CodeMap::new(FilePathMapping::empty()));
+            let sh = mk_sess(cm.clone());
+            check_tokenization(setup(&cm, &sh, "a b".to_string()),
+                            vec![mk_ident("a"), token::Whitespace, mk_ident("b")]);
+        })
     }
 
     #[test]
     fn dcparsing_2() {
-        let cm = Rc::new(CodeMap::new());
-        let sh = mk_sh(cm.clone());
-        check_tokenization(setup(&cm, &sh, "a::b".to_string()),
-                           vec![mk_ident("a"), token::ModSep, mk_ident("b")]);
+        with_globals(|| {
+            let cm = Lrc::new(CodeMap::new(FilePathMapping::empty()));
+            let sh = mk_sess(cm.clone());
+            check_tokenization(setup(&cm, &sh, "a::b".to_string()),
+                            vec![mk_ident("a"), token::ModSep, mk_ident("b")]);
+        })
     }
 
     #[test]
     fn dcparsing_3() {
-        let cm = Rc::new(CodeMap::new());
-        let sh = mk_sh(cm.clone());
-        check_tokenization(setup(&cm, &sh, "a ::b".to_string()),
-                           vec![mk_ident("a"), token::Whitespace, token::ModSep, mk_ident("b")]);
+        with_globals(|| {
+            let cm = Lrc::new(CodeMap::new(FilePathMapping::empty()));
+            let sh = mk_sess(cm.clone());
+            check_tokenization(setup(&cm, &sh, "a ::b".to_string()),
+                            vec![mk_ident("a"), token::Whitespace, token::ModSep, mk_ident("b")]);
+        })
     }
 
     #[test]
     fn dcparsing_4() {
-        let cm = Rc::new(CodeMap::new());
-        let sh = mk_sh(cm.clone());
-        check_tokenization(setup(&cm, &sh, "a:: b".to_string()),
-                           vec![mk_ident("a"), token::ModSep, token::Whitespace, mk_ident("b")]);
+        with_globals(|| {
+            let cm = Lrc::new(CodeMap::new(FilePathMapping::empty()));
+            let sh = mk_sess(cm.clone());
+            check_tokenization(setup(&cm, &sh, "a:: b".to_string()),
+                            vec![mk_ident("a"), token::ModSep, token::Whitespace, mk_ident("b")]);
+        })
     }
 
     #[test]
     fn character_a() {
-        let cm = Rc::new(CodeMap::new());
-        let sh = mk_sh(cm.clone());
-        assert_eq!(setup(&cm, &sh, "'a'".to_string()).next_token().tok,
-                   token::Literal(token::Char(token::intern("a")), None));
+        with_globals(|| {
+            let cm = Lrc::new(CodeMap::new(FilePathMapping::empty()));
+            let sh = mk_sess(cm.clone());
+            assert_eq!(setup(&cm, &sh, "'a'".to_string()).next_token().tok,
+                    token::Literal(token::Char(Symbol::intern("a")), None));
+        })
     }
 
     #[test]
     fn character_space() {
-        let cm = Rc::new(CodeMap::new());
-        let sh = mk_sh(cm.clone());
-        assert_eq!(setup(&cm, &sh, "' '".to_string()).next_token().tok,
-                   token::Literal(token::Char(token::intern(" ")), None));
+        with_globals(|| {
+            let cm = Lrc::new(CodeMap::new(FilePathMapping::empty()));
+            let sh = mk_sess(cm.clone());
+            assert_eq!(setup(&cm, &sh, "' '".to_string()).next_token().tok,
+                    token::Literal(token::Char(Symbol::intern(" ")), None));
+        })
     }
 
     #[test]
     fn character_escaped() {
-        let cm = Rc::new(CodeMap::new());
-        let sh = mk_sh(cm.clone());
-        assert_eq!(setup(&cm, &sh, "'\\n'".to_string()).next_token().tok,
-                   token::Literal(token::Char(token::intern("\\n")), None));
+        with_globals(|| {
+            let cm = Lrc::new(CodeMap::new(FilePathMapping::empty()));
+            let sh = mk_sess(cm.clone());
+            assert_eq!(setup(&cm, &sh, "'\\n'".to_string()).next_token().tok,
+                    token::Literal(token::Char(Symbol::intern("\\n")), None));
+        })
     }
 
     #[test]
     fn lifetime_name() {
-        let cm = Rc::new(CodeMap::new());
-        let sh = mk_sh(cm.clone());
-        assert_eq!(setup(&cm, &sh, "'abc".to_string()).next_token().tok,
-                   token::Lifetime(token::str_to_ident("'abc")));
+        with_globals(|| {
+            let cm = Lrc::new(CodeMap::new(FilePathMapping::empty()));
+            let sh = mk_sess(cm.clone());
+            assert_eq!(setup(&cm, &sh, "'abc".to_string()).next_token().tok,
+                    token::Lifetime(Ident::from_str("'abc")));
+        })
     }
 
     #[test]
     fn raw_string() {
-        let cm = Rc::new(CodeMap::new());
-        let sh = mk_sh(cm.clone());
-        assert_eq!(setup(&cm, &sh, "r###\"\"#a\\b\x00c\"\"###".to_string())
-                       .next_token()
-                       .tok,
-                   token::Literal(token::StrRaw(token::intern("\"#a\\b\x00c\""), 3), None));
+        with_globals(|| {
+            let cm = Lrc::new(CodeMap::new(FilePathMapping::empty()));
+            let sh = mk_sess(cm.clone());
+            assert_eq!(setup(&cm, &sh, "r###\"\"#a\\b\x00c\"\"###".to_string())
+                        .next_token()
+                        .tok,
+                    token::Literal(token::StrRaw(Symbol::intern("\"#a\\b\x00c\""), 3), None));
+        })
     }
 
     #[test]
     fn literal_suffixes() {
-        let cm = Rc::new(CodeMap::new());
-        let sh = mk_sh(cm.clone());
-        macro_rules! test {
-            ($input: expr, $tok_type: ident, $tok_contents: expr) => {{
-                assert_eq!(setup(&cm, &sh, format!("{}suffix", $input)).next_token().tok,
-                           token::Literal(token::$tok_type(token::intern($tok_contents)),
-                                          Some(token::intern("suffix"))));
-                // with a whitespace separator:
-                assert_eq!(setup(&cm, &sh, format!("{} suffix", $input)).next_token().tok,
-                           token::Literal(token::$tok_type(token::intern($tok_contents)),
-                                          None));
-            }}
-        }
-
-        test!("'a'", Char, "a");
-        test!("b'a'", Byte, "a");
-        test!("\"a\"", Str_, "a");
-        test!("b\"a\"", ByteStr, "a");
-        test!("1234", Integer, "1234");
-        test!("0b101", Integer, "0b101");
-        test!("0xABC", Integer, "0xABC");
-        test!("1.0", Float, "1.0");
-        test!("1.0e10", Float, "1.0e10");
-
-        assert_eq!(setup(&cm, &sh, "2us".to_string()).next_token().tok,
-                   token::Literal(token::Integer(token::intern("2")),
-                                  Some(token::intern("us"))));
-        assert_eq!(setup(&cm, &sh, "r###\"raw\"###suffix".to_string()).next_token().tok,
-                   token::Literal(token::StrRaw(token::intern("raw"), 3),
-                                  Some(token::intern("suffix"))));
-        assert_eq!(setup(&cm, &sh, "br###\"raw\"###suffix".to_string()).next_token().tok,
-                   token::Literal(token::ByteStrRaw(token::intern("raw"), 3),
-                                  Some(token::intern("suffix"))));
+        with_globals(|| {
+            let cm = Lrc::new(CodeMap::new(FilePathMapping::empty()));
+            let sh = mk_sess(cm.clone());
+            macro_rules! test {
+                ($input: expr, $tok_type: ident, $tok_contents: expr) => {{
+                    assert_eq!(setup(&cm, &sh, format!("{}suffix", $input)).next_token().tok,
+                            token::Literal(token::$tok_type(Symbol::intern($tok_contents)),
+                                            Some(Symbol::intern("suffix"))));
+                    // with a whitespace separator:
+                    assert_eq!(setup(&cm, &sh, format!("{} suffix", $input)).next_token().tok,
+                            token::Literal(token::$tok_type(Symbol::intern($tok_contents)),
+                                            None));
+                }}
+            }
+
+            test!("'a'", Char, "a");
+            test!("b'a'", Byte, "a");
+            test!("\"a\"", Str_, "a");
+            test!("b\"a\"", ByteStr, "a");
+            test!("1234", Integer, "1234");
+            test!("0b101", Integer, "0b101");
+            test!("0xABC", Integer, "0xABC");
+            test!("1.0", Float, "1.0");
+            test!("1.0e10", Float, "1.0e10");
+
+            assert_eq!(setup(&cm, &sh, "2us".to_string()).next_token().tok,
+                    token::Literal(token::Integer(Symbol::intern("2")),
+                                    Some(Symbol::intern("us"))));
+            assert_eq!(setup(&cm, &sh, "r###\"raw\"###suffix".to_string()).next_token().tok,
+                    token::Literal(token::StrRaw(Symbol::intern("raw"), 3),
+                                    Some(Symbol::intern("suffix"))));
+            assert_eq!(setup(&cm, &sh, "br###\"raw\"###suffix".to_string()).next_token().tok,
+                    token::Literal(token::ByteStrRaw(Symbol::intern("raw"), 3),
+                                    Some(Symbol::intern("suffix"))));
+        })
     }
 
     #[test]
@@ -1893,27 +2000,31 @@ mod tests {
 
     #[test]
     fn nested_block_comments() {
-        let cm = Rc::new(CodeMap::new());
-        let sh = mk_sh(cm.clone());
-        let mut lexer = setup(&cm, &sh, "/* /* */ */'a'".to_string());
-        match lexer.next_token().tok {
-            token::Comment => {}
-            _ => panic!("expected a comment!"),
-        }
-        assert_eq!(lexer.next_token().tok,
-                   token::Literal(token::Char(token::intern("a")), None));
+        with_globals(|| {
+            let cm = Lrc::new(CodeMap::new(FilePathMapping::empty()));
+            let sh = mk_sess(cm.clone());
+            let mut lexer = setup(&cm, &sh, "/* /* */ */'a'".to_string());
+            match lexer.next_token().tok {
+                token::Comment => {}
+                _ => panic!("expected a comment!"),
+            }
+            assert_eq!(lexer.next_token().tok,
+                    token::Literal(token::Char(Symbol::intern("a")), None));
+        })
     }
 
     #[test]
     fn crlf_comments() {
-        let cm = Rc::new(CodeMap::new());
-        let sh = mk_sh(cm.clone());
-        let mut lexer = setup(&cm, &sh, "// test\r\n/// test\r\n".to_string());
-        let comment = lexer.next_token();
-        assert_eq!(comment.tok, token::Comment);
-        assert_eq!(comment.sp, ::syntax_pos::mk_sp(BytePos(0), BytePos(7)));
-        assert_eq!(lexer.next_token().tok, token::Whitespace);
-        assert_eq!(lexer.next_token().tok,
-                   token::DocComment(token::intern("/// test")));
+        with_globals(|| {
+            let cm = Lrc::new(CodeMap::new(FilePathMapping::empty()));
+            let sh = mk_sess(cm.clone());
+            let mut lexer = setup(&cm, &sh, "// test\r\n/// test\r\n".to_string());
+            let comment = lexer.next_token();
+            assert_eq!(comment.tok, token::Comment);
+            assert_eq!((comment.sp.lo(), comment.sp.hi()), (BytePos(0), BytePos(7)));
+            assert_eq!(lexer.next_token().tok, token::Whitespace);
+            assert_eq!(lexer.next_token().tok,
+                    token::DocComment(Symbol::intern("/// test")));
+        })
     }
 }