]> git.lizzy.rs Git - rust.git/commitdiff
Rollup merge of #53373 - estebank:unclosed, r=petrochenkov
authorkennytm <kennytm@gmail.com>
Thu, 16 Aug 2018 16:13:24 +0000 (00:13 +0800)
committerGitHub <noreply@github.com>
Thu, 16 Aug 2018 16:13:24 +0000 (00:13 +0800)
Tweak unclosed delimiter parser error

1  2 
src/libsyntax/parse/lexer/mod.rs
src/libsyntax/parse/lexer/tokentrees.rs

index 5913c63bfaa5f6f238c0bbff004d3685deb98a6d,ffa6f65dc027a03132a6b3956a5b9b827526d687..bdf25618f474eda00f8d2bb87dab4604b86718f0
@@@ -67,29 -67,30 +67,30 @@@ pub struct StringReader<'a> 
      span_src_raw: Span,
      open_braces: Vec<(token::DelimToken, Span)>,
      crate override_span: Option<Span>,
+     last_unclosed_found_span: Option<Span>,
  }
  
  impl<'a> StringReader<'a> {
      fn mk_sp(&self, lo: BytePos, hi: BytePos) -> Span {
          self.mk_sp_and_raw(lo, hi).0
      }
 +
      fn mk_sp_and_raw(&self, lo: BytePos, hi: BytePos) -> (Span, Span) {
          let raw = Span::new(lo, hi, NO_EXPANSION);
 -        let real = unwrap_or!(self.override_span, raw);
 +        let real = self.override_span.unwrap_or(raw);
 +
          (real, raw)
      }
 +
      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)
 -    }
      fn unwrap_or_abort(&mut self, res: Result<TokenAndSpan, ()>) -> TokenAndSpan {
          match res {
              Ok(tok) => tok,
              }
          }
      }
 +
 +    fn next_token(&mut self) -> TokenAndSpan where Self: Sized {
 +        let res = self.try_next_token();
 +        self.unwrap_or_abort(res)
 +    }
 +
 +    /// Return the next token. EFFECT: advances the string_reader.
 +    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::Whitespace),
 +            sp: self.peek_span,
 +        };
 +        self.advance_token()?;
 +        self.span_src_raw = self.peek_span_src_raw;
 +
 +        Ok(ret_val)
 +    }
 +
      fn try_real_token(&mut self) -> Result<TokenAndSpan, ()> {
          let mut t = self.try_next_token()?;
          loop {
                  _ => break,
              }
          }
 +
          self.token = t.tok.clone();
          self.span = t.sp;
 +
          Ok(t)
      }
 +
      pub fn real_token(&mut self) -> TokenAndSpan {
          let res = self.try_real_token();
          self.unwrap_or_abort(res)
      }
 +
 +    #[inline]
      fn is_eof(&self) -> bool {
          self.ch.is_none()
      }
 -    /// Return the next token. EFFECT: advances the string_reader.
 -    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::Whitespace),
 -            sp: self.peek_span,
 -        };
 -        self.advance_token()?;
 -        self.span_src_raw = self.peek_span_src_raw;
 -        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)
      }
 +
      pub fn emit_fatal_errors(&mut self) {
          for err in &mut self.fatal_errs {
              err.emit();
          }
 +
          self.fatal_errs.clear();
      }
 +
      pub fn peek(&self) -> TokenAndSpan {
          // FIXME(pcwalton): Bad copy!
          TokenAndSpan {
      }
  
      /// For comments.rs, which hackily pokes into next_pos and ch
 -    fn new_raw(sess: &'a ParseSess, filemap: Lrc<syntax_pos::FileMap>,
 -                   override_span: Option<Span>) -> Self {
 +    fn new_raw(sess: &'a ParseSess, filemap: Lrc<syntax_pos::FileMap>, override_span: Option<Span>)
 +        -> Self
 +    {
          let mut sr = StringReader::new_raw_internal(sess, filemap, override_span);
          sr.bump();
 +
          sr
      }
  
      fn new_raw_internal(sess: &'a ParseSess, filemap: Lrc<syntax_pos::FileMap>,
 -                        override_span: Option<Span>) -> Self {
 +        override_span: Option<Span>) -> Self
 +    {
          if filemap.src.is_none() {
              sess.span_diagnostic.bug(&format!("Cannot lex filemap without source: {}",
                                                filemap.name));
              span_src_raw: syntax_pos::DUMMY_SP,
              open_braces: Vec::new(),
              override_span,
+             last_unclosed_found_span: None,
          }
      }
  
      pub fn new(sess: &'a ParseSess, filemap: Lrc<syntax_pos::FileMap>, override_span: Option<Span>)
 -               -> Self {
 +        -> Self
 +    {
          let mut sr = StringReader::new_raw(sess, filemap, override_span);
          if sr.advance_token().is_err() {
              sr.emit_fatal_errors();
              FatalError.raise();
          }
 +
          sr
      }
  
              sr.emit_fatal_errors();
              FatalError.raise();
          }
 +
          sr
      }
  
 +    #[inline]
      fn ch_is(&self, c: char) -> bool {
          self.ch == Some(c)
      }
          let mut m = m.to_string();
          m.push_str(": ");
          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> {
 +    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,
 -                              m: &str,
 -                              c: char)
 -                              -> DiagnosticBuilder<'a> {
 +    fn struct_fatal_span_char(&self, from_pos: BytePos, to_pos: BytePos, m: &str, c: char)
 +        -> DiagnosticBuilder<'a>
 +    {
          let mut m = m.to_string();
          m.push_str(": ");
          Self::push_escaped_char_for_msg(&mut m, c);
 +
          self.sess.span_diagnostic.struct_span_fatal(self.mk_sp(from_pos, to_pos), &m[..])
      }
  
          Self::push_escaped_char_for_msg(&mut m, c);
          self.err_span_(from_pos, to_pos, &m[..]);
      }
 -    fn struct_err_span_char(&self,
 -                            from_pos: BytePos,
 -                            to_pos: BytePos,
 -                            m: &str,
 -                            c: char)
 -                            -> DiagnosticBuilder<'a> {
 +
 +    fn struct_err_span_char(&self, from_pos: BytePos, to_pos: BytePos, m: &str, c: char)
 +        -> DiagnosticBuilder<'a>
 +    {
          let mut m = m.to_string();
          m.push_str(": ");
          Self::push_escaped_char_for_msg(&mut m, c);
 +
          self.sess.span_diagnostic.struct_span_err(self.mk_sp(from_pos, to_pos), &m[..])
      }
  
      fn fatal_span_verbose(&self, from_pos: BytePos, to_pos: BytePos, mut m: String) -> FatalError {
          m.push_str(": ");
          m.push_str(&self.src[self.src_index(from_pos)..self.src_index(to_pos)]);
 +
          self.fatal_span_(from_pos, to_pos, &m[..])
      }
  
                  };
              }
          }
 +
          Ok(())
      }
  
          }
      }
  
 +    #[inline]
      fn nextch_is(&self, c: char) -> bool {
          self.nextch() == Some(c)
      }
          None
      }
  
 +    #[inline]
      fn nextnextch_is(&self, c: char) -> bool {
          self.nextnextch() == Some(c)
      }
          if !ident_start(self.ch) {
              return None;
          }
 +
          let start = self.pos;
          self.bump();
 +
          while ident_continue(self.ch) {
              self.bump();
          }
      fn scan_digits(&mut self, real_radix: u32, scan_radix: u32) -> usize {
          assert!(real_radix <= scan_radix);
          let mut len = 0;
 +
          loop {
              let c = self.ch;
              if c == Some('_') {
  
      /// Lex a LIT_INTEGER or a LIT_FLOAT
      fn scan_number(&mut self, c: char) -> token::Lit {
 -        let num_digits;
          let mut base = 10;
          let start_bpos = self.pos;
 -
          self.bump();
  
 -        if c == '0' {
 +        let num_digits = if c == '0' {
              match self.ch.unwrap_or('\0') {
                  'b' => {
                      self.bump();
                      base = 2;
 -                    num_digits = self.scan_digits(2, 10);
 +                    self.scan_digits(2, 10)
                  }
                  'o' => {
                      self.bump();
                      base = 8;
 -                    num_digits = self.scan_digits(8, 10);
 +                    self.scan_digits(8, 10)
                  }
                  'x' => {
                      self.bump();
                      base = 16;
 -                    num_digits = self.scan_digits(16, 16);
 +                    self.scan_digits(16, 16)
                  }
                  '0'..='9' | '_' | '.' | 'e' | 'E' => {
 -                    num_digits = self.scan_digits(10, 10) + 1;
 +                    self.scan_digits(10, 10) + 1
                  }
                  _ => {
                      // just a 0
                  }
              }
          } else if c.is_digit(10) {
 -            num_digits = self.scan_digits(10, 10) + 1;
 +            self.scan_digits(10, 10) + 1
          } else {
 -            num_digits = 0;
 -        }
 +            0
 +        };
  
          if num_digits == 0 {
 -            self.err_span_(start_bpos,
 -                           self.pos,
 -                           "no valid digits found for number");
 +            self.err_span_(start_bpos, self.pos, "no valid digits found for number");
 +
              return token::Integer(Symbol::intern("0"));
          }
  
              }
              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
                           first_source_char: char,
                           ascii_only: bool,
                           delim: char)
 -                         -> bool {
 +                         -> bool
 +    {
          match first_source_char {
              '\\' => {
                  // '\X' for some X must be a character constant:
                             "overlong unicode escape (must have at most 6 hex digits)");
              valid = false;
          }
 +
          loop {
              match self.ch {
                  Some('}') => {
              }
              self.bump();
          }
 +
          valid
      }
  
      fn scan_float_exponent(&mut self) {
          if self.ch_is('e') || self.ch_is('E') {
              self.bump();
 +
              if self.ch_is('-') || self.ch_is('+') {
                  self.bump();
              }
 +
              if self.scan_digits(10, 10) == 0 {
                  let mut err = self.struct_span_fatal(
                      self.pos, self.next_pos,
                      ('b', Some('r'), Some('#')) => (false, false),
                      _ => (true, false),
                  };
 +
              if is_ident_start {
                  let raw_start = self.pos;
                  if is_raw_ident {
  
                  let start = self.pos;
                  self.bump();
 +
                  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 && (ident.is_path_segment_keyword() ||
                                          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)
                  }));
              }
                      return Ok(token::Lifetime(ident));
                  }
  
 -                let valid = self.scan_char_or_byte(start,
 -                                                   c2,
 -                                                   // ascii_only =
 -                                                   false,
 -                                                   '\'');
 +                let valid = self.scan_char_or_byte(start, c2, /* ascii_only */ false, '\'');
  
                  if !self.ch_is('\'') {
                      let pos = self.pos;
 +
                      loop {
                          self.bump();
                          if self.ch_is('\'') {
                              break;
                          }
                      }
 +
                      self.fatal_span_verbose(start_with_quote, pos,
                          String::from("character literal may only contain one codepoint")).raise();
                  }
                  } else {
                      Symbol::intern("0")
                  };
 +
                  self.bump(); // advance ch past token
                  let suffix = self.scan_optional_raw_name();
 +
                  Ok(token::Literal(token::Char(id), suffix))
              }
              'b' => {
                      _ => unreachable!(),  // Should have been a token::Ident above.
                  };
                  let suffix = self.scan_optional_raw_name();
 +
                  Ok(token::Literal(lit, suffix))
              }
              '"' => {
                  let start_bpos = self.pos;
                  let mut valid = true;
                  self.bump();
 +
                  while !self.ch_is('"') {
                      if self.is_eof() {
                          let last_bpos = self.pos;
                      let ch_start = self.pos;
                      let ch = self.ch.unwrap();
                      self.bump();
 -                    valid &= self.scan_char_or_byte(ch_start,
 -                                                    ch,
 -                                                    // ascii_only =
 -                                                    false,
 -                                                    '"');
 +                    valid &= self.scan_char_or_byte(ch_start, ch, /* ascii_only */ false, '"');
                  }
                  // adjust for the ASCII " at the start of the literal
                  let id = if valid {
                  };
                  self.bump();
                  let suffix = self.scan_optional_raw_name();
 +
                  Ok(token::Literal(token::Str_(id), suffix))
              }
              'r' => {
                      }
                      self.bump();
                  }
 +
                  self.bump();
                  let id = if valid {
                      self.name_from_to(content_start_bpos, content_end_bpos)
                      Symbol::intern("??")
                  };
                  let suffix = self.scan_optional_raw_name();
 +
                  Ok(token::Literal(token::StrRaw(id, hash_count), suffix))
              }
              '-' => {
                                                            c);
                  unicode_chars::check_for_substitution(self, c, &mut err);
                  self.fatal_errs.push(err);
 +
                  Err(())
              }
          }
              val.push(self.ch.unwrap());
              self.bump();
          }
 +
          if self.ch_is('\n') {
              self.bump();
          }
 +
          val
      }
  
              Symbol::intern("?")
          };
          self.bump(); // advance ch past token
 +
          token::Byte(id)
      }
  
 +    #[inline]
      fn scan_byte_escape(&mut self, delim: char, below_0x7f_only: bool) -> bool {
          self.scan_hex_digits(2, delim, below_0x7f_only)
      }
                                              true,
                                              '"');
          }
 +
          let id = if valid {
              self.name_from(start)
          } else {
              Symbol::intern("??")
          };
          self.bump();
 +
          token::ByteStr(id)
      }
  
              }
              self.bump();
          }
 +
          self.bump();
 -        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)
      }
  }
  
  // This tests the character for the unicode property 'PATTERN_WHITE_SPACE' which
  // is guaranteed to be forward compatible. http://unicode.org/reports/tr31/#R3
 +#[inline]
  crate fn is_pattern_whitespace(c: Option<char>) -> bool {
      c.map_or(false, Pattern_White_Space)
  }
  
 +#[inline]
  fn in_range(c: Option<char>, lo: char, hi: char) -> bool {
 -    match c {
 -        Some(c) => lo <= c && c <= hi,
 -        _ => false,
 -    }
 +    c.map_or(false, |c| lo <= c && c <= hi)
  }
  
 +#[inline]
  fn is_dec_digit(c: Option<char>) -> bool {
      in_range(c, '0', '9')
  }
index 1e7855e68ddc64efa04f6f738e11ae63e2b06881,af8ac3895c2736b285a810b06c221ab56c350dbf..e2fd7faf90387e856007ccc7fe8368409ff15044
@@@ -17,11 -17,9 +17,11 @@@ impl<'a> StringReader<'a> 
      // Parse a stream of tokens into a list of `TokenTree`s, up to an `Eof`.
      crate fn parse_all_token_trees(&mut self) -> PResult<'a, TokenStream> {
          let mut tts = Vec::new();
 +
          while self.token != token::Eof {
              tts.push(self.parse_token_tree()?);
          }
 +
          Ok(TokenStream::concat(tts))
      }
  
@@@ -32,7 -30,6 +32,7 @@@
              if let token::CloseDelim(..) = self.token {
                  return TokenStream::concat(tts);
              }
 +
              match self.parse_token_tree() {
                  Ok(tree) => tts.push(tree),
                  Err(mut e) => {
@@@ -51,7 -48,6 +51,7 @@@
                  for &(_, sp) in &self.open_braces {
                      err.span_help(sp, "did you mean to close this delimiter?");
                  }
 +
                  Err(err)
              },
              token::OpenDelim(delim) => {
                      // Incorrect delimiter.
                      token::CloseDelim(other) => {
                          let token_str = token_to_string(&self.token);
-                         let msg = format!("incorrect close delimiter: `{}`", token_str);
-                         let mut err = self.sess.span_diagnostic.struct_span_err(self.span, &msg);
-                         // This is a conservative error: only report the last unclosed delimiter.
-                         // The previous unclosed delimiters could actually be closed! The parser
-                         // just hasn't gotten to them yet.
-                         if let Some(&(_, sp)) = self.open_braces.last() {
-                             err.span_note(sp, "unclosed delimiter");
-                         };
-                         err.emit();
+                         if self.last_unclosed_found_span != Some(self.span) {
+                             // do not complain about the same unclosed delimiter multiple times
+                             self.last_unclosed_found_span = Some(self.span);
+                             let msg = format!("incorrect close delimiter: `{}`", token_str);
+                             let mut err = self.sess.span_diagnostic.struct_span_err(
+                                 self.span,
+                                 &msg,
+                             );
+                             err.span_label(self.span, "incorrect close delimiter");
+                             // This is a conservative error: only report the last unclosed
+                             // delimiter. The previous unclosed delimiters could actually be
+                             // closed! The parser just hasn't gotten to them yet.
+                             if let Some(&(_, sp)) = self.open_braces.last() {
+                                 err.span_label(sp, "unclosed delimiter");
+                             };
+                             err.emit();
+                         }
                          self.open_braces.pop().unwrap();
  
                          // If the incorrect delimiter matches an earlier opening
                  // matching opening delimiter).
                  let token_str = token_to_string(&self.token);
                  let msg = format!("unexpected close delimiter: `{}`", token_str);
-                 let err = self.sess.span_diagnostic.struct_span_err(self.span, &msg);
+                 let mut err = self.sess.span_diagnostic.struct_span_err(self.span, &msg);
+                 err.span_label(self.span, "unexpected close delimiter");
                  Err(err)
              },
              _ => {
                  let raw = self.span_src_raw;
                  self.real_token();
                  let is_joint = raw.hi() == self.span_src_raw.lo() && token::is_op(&self.token);
 +
                  Ok(if is_joint { tt.joint() } else { tt.into() })
              }
          }