]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_parse/src/lexer/mod.rs
Rollup merge of #86393 - yerke:add-test-for-issue-52025, r=JohnTitor
[rust.git] / compiler / rustc_parse / src / lexer / mod.rs
1 use rustc_ast::ast::AttrStyle;
2 use rustc_ast::token::{self, CommentKind, Token, TokenKind};
3 use rustc_ast::tokenstream::{Spacing, TokenStream};
4 use rustc_errors::{error_code, Applicability, DiagnosticBuilder, FatalError, PResult};
5 use rustc_lexer::unescape::{self, Mode};
6 use rustc_lexer::{Base, DocStyle, RawStrError};
7 use rustc_session::parse::ParseSess;
8 use rustc_span::symbol::{sym, Symbol};
9 use rustc_span::{BytePos, Pos, Span};
10
11 use tracing::debug;
12
13 mod tokentrees;
14 mod unescape_error_reporting;
15 mod unicode_chars;
16
17 use unescape_error_reporting::{emit_unescape_error, escaped_char};
18
19 #[derive(Clone, Debug)]
20 pub struct UnmatchedBrace {
21     pub expected_delim: token::DelimToken,
22     pub found_delim: Option<token::DelimToken>,
23     pub found_span: Span,
24     pub unclosed_span: Option<Span>,
25     pub candidate_span: Option<Span>,
26 }
27
28 crate fn parse_token_trees<'a>(
29     sess: &'a ParseSess,
30     src: &'a str,
31     start_pos: BytePos,
32     override_span: Option<Span>,
33 ) -> (PResult<'a, TokenStream>, Vec<UnmatchedBrace>) {
34     StringReader { sess, start_pos, pos: start_pos, end_src_index: src.len(), src, override_span }
35         .into_token_trees()
36 }
37
38 struct StringReader<'a> {
39     sess: &'a ParseSess,
40     /// Initial position, read-only.
41     start_pos: BytePos,
42     /// The absolute offset within the source_map of the current character.
43     pos: BytePos,
44     /// Stop reading src at this index.
45     end_src_index: usize,
46     /// Source text to tokenize.
47     src: &'a str,
48     override_span: Option<Span>,
49 }
50
51 impl<'a> StringReader<'a> {
52     fn mk_sp(&self, lo: BytePos, hi: BytePos) -> Span {
53         self.override_span.unwrap_or_else(|| Span::with_root_ctxt(lo, hi))
54     }
55
56     /// Returns the next token, and info about preceding whitespace, if any.
57     fn next_token(&mut self) -> (Spacing, Token) {
58         let mut spacing = Spacing::Joint;
59
60         // Skip `#!` at the start of the file
61         let start_src_index = self.src_index(self.pos);
62         let text: &str = &self.src[start_src_index..self.end_src_index];
63         let is_beginning_of_file = self.pos == self.start_pos;
64         if is_beginning_of_file {
65             if let Some(shebang_len) = rustc_lexer::strip_shebang(text) {
66                 self.pos = self.pos + BytePos::from_usize(shebang_len);
67                 spacing = Spacing::Alone;
68             }
69         }
70
71         // Skip trivial (whitespace & comments) tokens
72         loop {
73             let start_src_index = self.src_index(self.pos);
74             let text: &str = &self.src[start_src_index..self.end_src_index];
75
76             if text.is_empty() {
77                 let span = self.mk_sp(self.pos, self.pos);
78                 return (spacing, Token::new(token::Eof, span));
79             }
80
81             let token = rustc_lexer::first_token(text);
82
83             let start = self.pos;
84             self.pos = self.pos + BytePos::from_usize(token.len);
85
86             debug!("next_token: {:?}({:?})", token.kind, self.str_from(start));
87
88             match self.cook_lexer_token(token.kind, start) {
89                 Some(kind) => {
90                     let span = self.mk_sp(start, self.pos);
91                     return (spacing, Token::new(kind, span));
92                 }
93                 None => spacing = Spacing::Alone,
94             }
95         }
96     }
97
98     /// Report a fatal lexical error with a given span.
99     fn fatal_span(&self, sp: Span, m: &str) -> FatalError {
100         self.sess.span_diagnostic.span_fatal(sp, m)
101     }
102
103     /// Report a lexical error with a given span.
104     fn err_span(&self, sp: Span, m: &str) {
105         self.sess.span_diagnostic.struct_span_err(sp, m).emit();
106     }
107
108     /// Report a fatal error spanning [`from_pos`, `to_pos`).
109     fn fatal_span_(&self, from_pos: BytePos, to_pos: BytePos, m: &str) -> FatalError {
110         self.fatal_span(self.mk_sp(from_pos, to_pos), m)
111     }
112
113     /// Report a lexical error spanning [`from_pos`, `to_pos`).
114     fn err_span_(&self, from_pos: BytePos, to_pos: BytePos, m: &str) {
115         self.err_span(self.mk_sp(from_pos, to_pos), m)
116     }
117
118     fn struct_fatal_span_char(
119         &self,
120         from_pos: BytePos,
121         to_pos: BytePos,
122         m: &str,
123         c: char,
124     ) -> DiagnosticBuilder<'a> {
125         self.sess
126             .span_diagnostic
127             .struct_span_fatal(self.mk_sp(from_pos, to_pos), &format!("{}: {}", m, escaped_char(c)))
128     }
129
130     /// Turns simple `rustc_lexer::TokenKind` enum into a rich
131     /// `rustc_ast::TokenKind`. This turns strings into interned
132     /// symbols and runs additional validation.
133     fn cook_lexer_token(&self, token: rustc_lexer::TokenKind, start: BytePos) -> Option<TokenKind> {
134         Some(match token {
135             rustc_lexer::TokenKind::LineComment { doc_style } => {
136                 // Skip non-doc comments
137                 let doc_style = doc_style?;
138
139                 // Opening delimiter of the length 3 is not included into the symbol.
140                 let content_start = start + BytePos(3);
141                 let content = self.str_from(content_start);
142                 self.cook_doc_comment(content_start, content, CommentKind::Line, doc_style)
143             }
144             rustc_lexer::TokenKind::BlockComment { doc_style, terminated } => {
145                 if !terminated {
146                     let msg = match doc_style {
147                         Some(_) => "unterminated block doc-comment",
148                         None => "unterminated block comment",
149                     };
150                     let last_bpos = self.pos;
151                     self.sess.span_diagnostic.span_fatal_with_code(
152                         self.mk_sp(start, last_bpos),
153                         msg,
154                         error_code!(E0758),
155                     );
156                 }
157
158                 // Skip non-doc comments
159                 let doc_style = doc_style?;
160
161                 // Opening delimiter of the length 3 and closing delimiter of the length 2
162                 // are not included into the symbol.
163                 let content_start = start + BytePos(3);
164                 let content_end = self.pos - BytePos(if terminated { 2 } else { 0 });
165                 let content = self.str_from_to(content_start, content_end);
166                 self.cook_doc_comment(content_start, content, CommentKind::Block, doc_style)
167             }
168             rustc_lexer::TokenKind::Whitespace => return None,
169             rustc_lexer::TokenKind::Ident | rustc_lexer::TokenKind::RawIdent => {
170                 let is_raw_ident = token == rustc_lexer::TokenKind::RawIdent;
171                 let mut ident_start = start;
172                 if is_raw_ident {
173                     ident_start = ident_start + BytePos(2);
174                 }
175                 let sym = nfc_normalize(self.str_from(ident_start));
176                 let span = self.mk_sp(start, self.pos);
177                 self.sess.symbol_gallery.insert(sym, span);
178                 if is_raw_ident {
179                     if !sym.can_be_raw() {
180                         self.err_span(span, &format!("`{}` cannot be a raw identifier", sym));
181                     }
182                     self.sess.raw_identifier_spans.borrow_mut().push(span);
183                 }
184                 token::Ident(sym, is_raw_ident)
185             }
186             rustc_lexer::TokenKind::Literal { kind, suffix_start } => {
187                 let suffix_start = start + BytePos(suffix_start as u32);
188                 let (kind, symbol) = self.cook_lexer_literal(start, suffix_start, kind);
189                 let suffix = if suffix_start < self.pos {
190                     let string = self.str_from(suffix_start);
191                     if string == "_" {
192                         self.sess
193                             .span_diagnostic
194                             .struct_span_warn(
195                                 self.mk_sp(suffix_start, self.pos),
196                                 "underscore literal suffix is not allowed",
197                             )
198                             .warn(
199                                 "this was previously accepted by the compiler but is \
200                                    being phased out; it will become a hard error in \
201                                    a future release!",
202                             )
203                             .note(
204                                 "see issue #42326 \
205                                  <https://github.com/rust-lang/rust/issues/42326> \
206                                  for more information",
207                             )
208                             .emit();
209                         None
210                     } else {
211                         Some(Symbol::intern(string))
212                     }
213                 } else {
214                     None
215                 };
216                 token::Literal(token::Lit { kind, symbol, suffix })
217             }
218             rustc_lexer::TokenKind::Lifetime { starts_with_number } => {
219                 // Include the leading `'` in the real identifier, for macro
220                 // expansion purposes. See #12512 for the gory details of why
221                 // this is necessary.
222                 let lifetime_name = self.str_from(start);
223                 if starts_with_number {
224                     self.err_span_(start, self.pos, "lifetimes cannot start with a number");
225                 }
226                 let ident = Symbol::intern(lifetime_name);
227                 token::Lifetime(ident)
228             }
229             rustc_lexer::TokenKind::Semi => token::Semi,
230             rustc_lexer::TokenKind::Comma => token::Comma,
231             rustc_lexer::TokenKind::Dot => token::Dot,
232             rustc_lexer::TokenKind::OpenParen => token::OpenDelim(token::Paren),
233             rustc_lexer::TokenKind::CloseParen => token::CloseDelim(token::Paren),
234             rustc_lexer::TokenKind::OpenBrace => token::OpenDelim(token::Brace),
235             rustc_lexer::TokenKind::CloseBrace => token::CloseDelim(token::Brace),
236             rustc_lexer::TokenKind::OpenBracket => token::OpenDelim(token::Bracket),
237             rustc_lexer::TokenKind::CloseBracket => token::CloseDelim(token::Bracket),
238             rustc_lexer::TokenKind::At => token::At,
239             rustc_lexer::TokenKind::Pound => token::Pound,
240             rustc_lexer::TokenKind::Tilde => token::Tilde,
241             rustc_lexer::TokenKind::Question => token::Question,
242             rustc_lexer::TokenKind::Colon => token::Colon,
243             rustc_lexer::TokenKind::Dollar => token::Dollar,
244             rustc_lexer::TokenKind::Eq => token::Eq,
245             rustc_lexer::TokenKind::Bang => token::Not,
246             rustc_lexer::TokenKind::Lt => token::Lt,
247             rustc_lexer::TokenKind::Gt => token::Gt,
248             rustc_lexer::TokenKind::Minus => token::BinOp(token::Minus),
249             rustc_lexer::TokenKind::And => token::BinOp(token::And),
250             rustc_lexer::TokenKind::Or => token::BinOp(token::Or),
251             rustc_lexer::TokenKind::Plus => token::BinOp(token::Plus),
252             rustc_lexer::TokenKind::Star => token::BinOp(token::Star),
253             rustc_lexer::TokenKind::Slash => token::BinOp(token::Slash),
254             rustc_lexer::TokenKind::Caret => token::BinOp(token::Caret),
255             rustc_lexer::TokenKind::Percent => token::BinOp(token::Percent),
256
257             rustc_lexer::TokenKind::Unknown => {
258                 let c = self.str_from(start).chars().next().unwrap();
259                 let mut err =
260                     self.struct_fatal_span_char(start, self.pos, "unknown start of token", c);
261                 // FIXME: the lexer could be used to turn the ASCII version of unicode homoglyphs,
262                 // instead of keeping a table in `check_for_substitution`into the token. Ideally,
263                 // this should be inside `rustc_lexer`. However, we should first remove compound
264                 // tokens like `<<` from `rustc_lexer`, and then add fancier error recovery to it,
265                 // as there will be less overall work to do this way.
266                 let token = unicode_chars::check_for_substitution(self, start, c, &mut err);
267                 if c == '\x00' {
268                     err.help("source files must contain UTF-8 encoded text, unexpected null bytes might occur when a different encoding is used");
269                 }
270                 err.emit();
271                 token?
272             }
273         })
274     }
275
276     fn cook_doc_comment(
277         &self,
278         content_start: BytePos,
279         content: &str,
280         comment_kind: CommentKind,
281         doc_style: DocStyle,
282     ) -> TokenKind {
283         if content.contains('\r') {
284             for (idx, _) in content.char_indices().filter(|&(_, c)| c == '\r') {
285                 self.err_span_(
286                     content_start + BytePos(idx as u32),
287                     content_start + BytePos(idx as u32 + 1),
288                     match comment_kind {
289                         CommentKind::Line => "bare CR not allowed in doc-comment",
290                         CommentKind::Block => "bare CR not allowed in block doc-comment",
291                     },
292                 );
293             }
294         }
295
296         let attr_style = match doc_style {
297             DocStyle::Outer => AttrStyle::Outer,
298             DocStyle::Inner => AttrStyle::Inner,
299         };
300
301         token::DocComment(comment_kind, attr_style, Symbol::intern(content))
302     }
303
304     fn cook_lexer_literal(
305         &self,
306         start: BytePos,
307         suffix_start: BytePos,
308         kind: rustc_lexer::LiteralKind,
309     ) -> (token::LitKind, Symbol) {
310         // prefix means `"` or `br"` or `r###"`, ...
311         let (lit_kind, mode, prefix_len, postfix_len) = match kind {
312             rustc_lexer::LiteralKind::Char { terminated } => {
313                 if !terminated {
314                     self.sess.span_diagnostic.span_fatal_with_code(
315                         self.mk_sp(start, suffix_start),
316                         "unterminated character literal",
317                         error_code!(E0762),
318                     )
319                 }
320                 (token::Char, Mode::Char, 1, 1) // ' '
321             }
322             rustc_lexer::LiteralKind::Byte { terminated } => {
323                 if !terminated {
324                     self.sess.span_diagnostic.span_fatal_with_code(
325                         self.mk_sp(start + BytePos(1), suffix_start),
326                         "unterminated byte constant",
327                         error_code!(E0763),
328                     )
329                 }
330                 (token::Byte, Mode::Byte, 2, 1) // b' '
331             }
332             rustc_lexer::LiteralKind::Str { terminated } => {
333                 if !terminated {
334                     self.sess.span_diagnostic.span_fatal_with_code(
335                         self.mk_sp(start, suffix_start),
336                         "unterminated double quote string",
337                         error_code!(E0765),
338                     )
339                 }
340                 (token::Str, Mode::Str, 1, 1) // " "
341             }
342             rustc_lexer::LiteralKind::ByteStr { terminated } => {
343                 if !terminated {
344                     self.sess.span_diagnostic.span_fatal_with_code(
345                         self.mk_sp(start + BytePos(1), suffix_start),
346                         "unterminated double quote byte string",
347                         error_code!(E0766),
348                     )
349                 }
350                 (token::ByteStr, Mode::ByteStr, 2, 1) // b" "
351             }
352             rustc_lexer::LiteralKind::RawStr { n_hashes, err } => {
353                 self.report_raw_str_error(start, err);
354                 let n = u32::from(n_hashes);
355                 (token::StrRaw(n_hashes), Mode::RawStr, 2 + n, 1 + n) // r##" "##
356             }
357             rustc_lexer::LiteralKind::RawByteStr { n_hashes, err } => {
358                 self.report_raw_str_error(start, err);
359                 let n = u32::from(n_hashes);
360                 (token::ByteStrRaw(n_hashes), Mode::RawByteStr, 3 + n, 1 + n) // br##" "##
361             }
362             rustc_lexer::LiteralKind::Int { base, empty_int } => {
363                 return if empty_int {
364                     self.sess
365                         .span_diagnostic
366                         .struct_span_err_with_code(
367                             self.mk_sp(start, suffix_start),
368                             "no valid digits found for number",
369                             error_code!(E0768),
370                         )
371                         .emit();
372                     (token::Integer, sym::integer(0))
373                 } else {
374                     self.validate_int_literal(base, start, suffix_start);
375                     (token::Integer, self.symbol_from_to(start, suffix_start))
376                 };
377             }
378             rustc_lexer::LiteralKind::Float { base, empty_exponent } => {
379                 if empty_exponent {
380                     self.err_span_(start, self.pos, "expected at least one digit in exponent");
381                 }
382
383                 match base {
384                     Base::Hexadecimal => self.err_span_(
385                         start,
386                         suffix_start,
387                         "hexadecimal float literal is not supported",
388                     ),
389                     Base::Octal => {
390                         self.err_span_(start, suffix_start, "octal float literal is not supported")
391                     }
392                     Base::Binary => {
393                         self.err_span_(start, suffix_start, "binary float literal is not supported")
394                     }
395                     _ => (),
396                 }
397
398                 let id = self.symbol_from_to(start, suffix_start);
399                 return (token::Float, id);
400             }
401         };
402         let content_start = start + BytePos(prefix_len);
403         let content_end = suffix_start - BytePos(postfix_len);
404         let id = self.symbol_from_to(content_start, content_end);
405         self.validate_literal_escape(mode, content_start, content_end, prefix_len, postfix_len);
406         (lit_kind, id)
407     }
408
409     #[inline]
410     fn src_index(&self, pos: BytePos) -> usize {
411         (pos - self.start_pos).to_usize()
412     }
413
414     /// Slice of the source text from `start` up to but excluding `self.pos`,
415     /// meaning the slice does not include the character `self.ch`.
416     fn str_from(&self, start: BytePos) -> &str {
417         self.str_from_to(start, self.pos)
418     }
419
420     /// As symbol_from, with an explicit endpoint.
421     fn symbol_from_to(&self, start: BytePos, end: BytePos) -> Symbol {
422         debug!("taking an ident from {:?} to {:?}", start, end);
423         Symbol::intern(self.str_from_to(start, end))
424     }
425
426     /// Slice of the source text spanning from `start` up to but excluding `end`.
427     fn str_from_to(&self, start: BytePos, end: BytePos) -> &str {
428         &self.src[self.src_index(start)..self.src_index(end)]
429     }
430
431     fn report_raw_str_error(&self, start: BytePos, opt_err: Option<RawStrError>) {
432         match opt_err {
433             Some(RawStrError::InvalidStarter { bad_char }) => {
434                 self.report_non_started_raw_string(start, bad_char)
435             }
436             Some(RawStrError::NoTerminator { expected, found, possible_terminator_offset }) => self
437                 .report_unterminated_raw_string(start, expected, possible_terminator_offset, found),
438             Some(RawStrError::TooManyDelimiters { found }) => {
439                 self.report_too_many_hashes(start, found)
440             }
441             None => (),
442         }
443     }
444
445     fn report_non_started_raw_string(&self, start: BytePos, bad_char: char) -> ! {
446         self.struct_fatal_span_char(
447             start,
448             self.pos,
449             "found invalid character; only `#` is allowed in raw string delimitation",
450             bad_char,
451         )
452         .emit();
453         FatalError.raise()
454     }
455
456     fn report_unterminated_raw_string(
457         &self,
458         start: BytePos,
459         n_hashes: usize,
460         possible_offset: Option<usize>,
461         found_terminators: usize,
462     ) -> ! {
463         let mut err = self.sess.span_diagnostic.struct_span_fatal_with_code(
464             self.mk_sp(start, start),
465             "unterminated raw string",
466             error_code!(E0748),
467         );
468
469         err.span_label(self.mk_sp(start, start), "unterminated raw string");
470
471         if n_hashes > 0 {
472             err.note(&format!(
473                 "this raw string should be terminated with `\"{}`",
474                 "#".repeat(n_hashes)
475             ));
476         }
477
478         if let Some(possible_offset) = possible_offset {
479             let lo = start + BytePos(possible_offset as u32);
480             let hi = lo + BytePos(found_terminators as u32);
481             let span = self.mk_sp(lo, hi);
482             err.span_suggestion(
483                 span,
484                 "consider terminating the string here",
485                 "#".repeat(n_hashes),
486                 Applicability::MaybeIncorrect,
487             );
488         }
489
490         err.emit();
491         FatalError.raise()
492     }
493
494     /// Note: It was decided to not add a test case, because it would be too big.
495     /// <https://github.com/rust-lang/rust/pull/50296#issuecomment-392135180>
496     fn report_too_many_hashes(&self, start: BytePos, found: usize) -> ! {
497         self.fatal_span_(
498             start,
499             self.pos,
500             &format!(
501                 "too many `#` symbols: raw strings may be delimited \
502                 by up to 65535 `#` symbols, but found {}",
503                 found
504             ),
505         )
506         .raise();
507     }
508
509     fn validate_literal_escape(
510         &self,
511         mode: Mode,
512         content_start: BytePos,
513         content_end: BytePos,
514         prefix_len: u32,
515         postfix_len: u32,
516     ) {
517         let lit_content = self.str_from_to(content_start, content_end);
518         unescape::unescape_literal(lit_content, mode, &mut |range, result| {
519             // Here we only check for errors. The actual unescaping is done later.
520             if let Err(err) = result {
521                 let span_with_quotes = self
522                     .mk_sp(content_start - BytePos(prefix_len), content_end + BytePos(postfix_len));
523                 let (start, end) = (range.start as u32, range.end as u32);
524                 let lo = content_start + BytePos(start);
525                 let hi = lo + BytePos(end - start);
526                 let span = self.mk_sp(lo, hi);
527                 emit_unescape_error(
528                     &self.sess.span_diagnostic,
529                     lit_content,
530                     span_with_quotes,
531                     span,
532                     mode,
533                     range,
534                     err,
535                 );
536             }
537         });
538     }
539
540     fn validate_int_literal(&self, base: Base, content_start: BytePos, content_end: BytePos) {
541         let base = match base {
542             Base::Binary => 2,
543             Base::Octal => 8,
544             _ => return,
545         };
546         let s = self.str_from_to(content_start + BytePos(2), content_end);
547         for (idx, c) in s.char_indices() {
548             let idx = idx as u32;
549             if c != '_' && c.to_digit(base).is_none() {
550                 let lo = content_start + BytePos(2 + idx);
551                 let hi = content_start + BytePos(2 + idx + c.len_utf8() as u32);
552                 self.err_span_(lo, hi, &format!("invalid digit for a base {} literal", base));
553             }
554         }
555     }
556 }
557
558 pub fn nfc_normalize(string: &str) -> Symbol {
559     use unicode_normalization::{is_nfc_quick, IsNormalized, UnicodeNormalization};
560     match is_nfc_quick(string.chars()) {
561         IsNormalized::Yes => Symbol::intern(string),
562         _ => {
563             let normalized_str: String = string.chars().nfc().collect();
564             Symbol::intern(&normalized_str)
565         }
566     }
567 }