1use std::ops::Range;
2
3use diagnostics::make_unclosed_delims_error;
4use rustc_ast::ast::{self, AttrStyle};
5use rustc_ast::token::{self, CommentKind, Delimiter, IdentIsRaw, Token, TokenKind};
6use rustc_ast::tokenstream::TokenStream;
7use rustc_ast::util::unicode::contains_text_flow_control_chars;
8use rustc_errors::codes::*;
9use rustc_errors::{Applicability, Diag, DiagCtxtHandle, StashKey};
10use rustc_lexer::{
11 Base, Cursor, DocStyle, FrontmatterAllowed, LiteralKind, RawStrError, is_whitespace,
12};
13use rustc_literal_escaper::{EscapeError, Mode, unescape_mixed, unescape_unicode};
14use rustc_session::lint::BuiltinLintDiag;
15use rustc_session::lint::builtin::{
16 RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX, RUST_2024_GUARDED_STRING_INCOMPATIBLE_SYNTAX,
17 TEXT_DIRECTION_CODEPOINT_IN_COMMENT,
18};
19use rustc_session::parse::ParseSess;
20use rustc_span::{BytePos, Pos, Span, Symbol, sym};
21use tracing::debug;
22
23use crate::errors;
24use crate::lexer::diagnostics::TokenTreeDiagInfo;
25use crate::lexer::unicode_chars::UNICODE_ARRAY;
26
27mod diagnostics;
28mod tokentrees;
29mod unescape_error_reporting;
30mod unicode_chars;
31
32use unescape_error_reporting::{emit_unescape_error, escaped_char};
33
34#[cfg(target_pointer_width = "64")]
39rustc_data_structures::static_assert_size!(rustc_lexer::Token, 12);
40
41#[derive(Clone, Debug)]
42pub(crate) struct UnmatchedDelim {
43 pub found_delim: Option<Delimiter>,
44 pub found_span: Span,
45 pub unclosed_span: Option<Span>,
46 pub candidate_span: Option<Span>,
47}
48
49pub(crate) fn lex_token_trees<'psess, 'src>(
50 psess: &'psess ParseSess,
51 mut src: &'src str,
52 mut start_pos: BytePos,
53 override_span: Option<Span>,
54) -> Result<TokenStream, Vec<Diag<'psess>>> {
55 if let Some(shebang_len) = rustc_lexer::strip_shebang(src) {
57 src = &src[shebang_len..];
58 start_pos = start_pos + BytePos::from_usize(shebang_len);
59 }
60
61 let cursor = Cursor::new(src, FrontmatterAllowed::Yes);
62 let mut lexer = Lexer {
63 psess,
64 start_pos,
65 pos: start_pos,
66 src,
67 cursor,
68 override_span,
69 nbsp_is_whitespace: false,
70 last_lifetime: None,
71 token: Token::dummy(),
72 diag_info: TokenTreeDiagInfo::default(),
73 };
74 let res = lexer.lex_token_trees(false);
75
76 let mut unmatched_delims: Vec<_> = lexer
77 .diag_info
78 .unmatched_delims
79 .into_iter()
80 .filter_map(|unmatched_delim| make_unclosed_delims_error(unmatched_delim, psess))
81 .collect();
82
83 match res {
84 Ok((_open_spacing, stream)) => {
85 if unmatched_delims.is_empty() {
86 Ok(stream)
87 } else {
88 Err(unmatched_delims)
90 }
91 }
92 Err(errs) => {
93 unmatched_delims.extend(errs);
96 Err(unmatched_delims)
97 }
98 }
99}
100
101struct Lexer<'psess, 'src> {
102 psess: &'psess ParseSess,
103 start_pos: BytePos,
105 pos: BytePos,
107 src: &'src str,
109 cursor: Cursor<'src>,
111 override_span: Option<Span>,
112 nbsp_is_whitespace: bool,
116
117 last_lifetime: Option<Span>,
120
121 token: Token,
123
124 diag_info: TokenTreeDiagInfo,
125}
126
127impl<'psess, 'src> Lexer<'psess, 'src> {
128 fn dcx(&self) -> DiagCtxtHandle<'psess> {
129 self.psess.dcx()
130 }
131
132 fn mk_sp(&self, lo: BytePos, hi: BytePos) -> Span {
133 self.override_span.unwrap_or_else(|| Span::with_root_ctxt(lo, hi))
134 }
135
136 fn next_token_from_cursor(&mut self) -> (Token, bool) {
139 let mut preceded_by_whitespace = false;
140 let mut swallow_next_invalid = 0;
141 loop {
143 let str_before = self.cursor.as_str();
144 let token = self.cursor.advance_token();
145 let start = self.pos;
146 self.pos = self.pos + BytePos(token.len);
147
148 debug!("next_token: {:?}({:?})", token.kind, self.str_from(start));
149
150 if let rustc_lexer::TokenKind::Semi
151 | rustc_lexer::TokenKind::LineComment { .. }
152 | rustc_lexer::TokenKind::BlockComment { .. }
153 | rustc_lexer::TokenKind::CloseParen
154 | rustc_lexer::TokenKind::CloseBrace
155 | rustc_lexer::TokenKind::CloseBracket = token.kind
156 {
157 self.last_lifetime = None;
160 }
161
162 let kind = match token.kind {
166 rustc_lexer::TokenKind::LineComment { doc_style } => {
167 let Some(doc_style) = doc_style else {
169 self.lint_unicode_text_flow(start);
170 preceded_by_whitespace = true;
171 continue;
172 };
173
174 let content_start = start + BytePos(3);
176 let content = self.str_from(content_start);
177 self.cook_doc_comment(content_start, content, CommentKind::Line, doc_style)
178 }
179 rustc_lexer::TokenKind::BlockComment { doc_style, terminated } => {
180 if !terminated {
181 self.report_unterminated_block_comment(start, doc_style);
182 }
183
184 let Some(doc_style) = doc_style else {
186 self.lint_unicode_text_flow(start);
187 preceded_by_whitespace = true;
188 continue;
189 };
190
191 let content_start = start + BytePos(3);
194 let content_end = self.pos - BytePos(if terminated { 2 } else { 0 });
195 let content = self.str_from_to(content_start, content_end);
196 self.cook_doc_comment(content_start, content, CommentKind::Block, doc_style)
197 }
198 rustc_lexer::TokenKind::Frontmatter { has_invalid_preceding_whitespace, invalid_infostring } => {
199 self.validate_frontmatter(start, has_invalid_preceding_whitespace, invalid_infostring);
200 preceded_by_whitespace = true;
201 continue;
202 }
203 rustc_lexer::TokenKind::Whitespace => {
204 preceded_by_whitespace = true;
205 continue;
206 }
207 rustc_lexer::TokenKind::Ident => self.ident(start),
208 rustc_lexer::TokenKind::RawIdent => {
209 let sym = nfc_normalize(self.str_from(start + BytePos(2)));
210 let span = self.mk_sp(start, self.pos);
211 self.psess.symbol_gallery.insert(sym, span);
212 if !sym.can_be_raw() {
213 self.dcx().emit_err(errors::CannotBeRawIdent { span, ident: sym });
214 }
215 self.psess.raw_identifier_spans.push(span);
216 token::Ident(sym, IdentIsRaw::Yes)
217 }
218 rustc_lexer::TokenKind::UnknownPrefix => {
219 self.report_unknown_prefix(start);
220 self.ident(start)
221 }
222 rustc_lexer::TokenKind::UnknownPrefixLifetime => {
223 self.report_unknown_prefix(start);
224 let lifetime_name = self.str_from(start);
228 self.last_lifetime = Some(self.mk_sp(start, start + BytePos(1)));
229 let ident = Symbol::intern(lifetime_name);
230 token::Lifetime(ident, IdentIsRaw::No)
231 }
232 rustc_lexer::TokenKind::InvalidIdent
233 if !UNICODE_ARRAY.iter().any(|&(c, _, _)| {
236 let sym = self.str_from(start);
237 sym.chars().count() == 1 && c == sym.chars().next().unwrap()
238 }) =>
239 {
240 let sym = nfc_normalize(self.str_from(start));
241 let span = self.mk_sp(start, self.pos);
242 self.psess
243 .bad_unicode_identifiers
244 .borrow_mut()
245 .entry(sym)
246 .or_default()
247 .push(span);
248 token::Ident(sym, IdentIsRaw::No)
249 }
250 rustc_lexer::TokenKind::Literal {
253 kind: kind @ (LiteralKind::CStr { .. } | LiteralKind::RawCStr { .. }),
254 suffix_start: _,
255 } if !self.mk_sp(start, self.pos).edition().at_least_rust_2021() => {
256 let prefix_len = match kind {
257 LiteralKind::CStr { .. } => 1,
258 LiteralKind::RawCStr { .. } => 2,
259 _ => unreachable!(),
260 };
261
262 let lit_start = start + BytePos(prefix_len);
265 self.pos = lit_start;
266 self.cursor = Cursor::new(&str_before[prefix_len as usize..], FrontmatterAllowed::No);
267 self.report_unknown_prefix(start);
268 let prefix_span = self.mk_sp(start, lit_start);
269 return (Token::new(self.ident(start), prefix_span), preceded_by_whitespace);
270 }
271 rustc_lexer::TokenKind::GuardedStrPrefix => {
272 self.maybe_report_guarded_str(start, str_before)
273 }
274 rustc_lexer::TokenKind::Literal { kind, suffix_start } => {
275 let suffix_start = start + BytePos(suffix_start);
276 let (kind, symbol) = self.cook_lexer_literal(start, suffix_start, kind);
277 let suffix = if suffix_start < self.pos {
278 let string = self.str_from(suffix_start);
279 if string == "_" {
280 self.dcx().emit_err(errors::UnderscoreLiteralSuffix {
281 span: self.mk_sp(suffix_start, self.pos),
282 });
283 None
284 } else {
285 Some(Symbol::intern(string))
286 }
287 } else {
288 None
289 };
290 token::Literal(token::Lit { kind, symbol, suffix })
291 }
292 rustc_lexer::TokenKind::Lifetime { starts_with_number } => {
293 let lifetime_name = self.str_from(start);
297 self.last_lifetime = Some(self.mk_sp(start, start + BytePos(1)));
298 if starts_with_number {
299 let span = self.mk_sp(start, self.pos);
300 self.dcx()
301 .struct_err("lifetimes cannot start with a number")
302 .with_span(span)
303 .stash(span, StashKey::LifetimeIsChar);
304 }
305 let ident = Symbol::intern(lifetime_name);
306 token::Lifetime(ident, IdentIsRaw::No)
307 }
308 rustc_lexer::TokenKind::RawLifetime => {
309 self.last_lifetime = Some(self.mk_sp(start, start + BytePos(1)));
310
311 let ident_start = start + BytePos(3);
312 let prefix_span = self.mk_sp(start, ident_start);
313
314 if prefix_span.at_least_rust_2021() {
315 if self.cursor.as_str().starts_with('\'') {
321 let lit_span = self.mk_sp(start, self.pos + BytePos(1));
322 let contents = self.str_from_to(start + BytePos(1), self.pos);
323 emit_unescape_error(
324 self.dcx(),
325 contents,
326 lit_span,
327 lit_span,
328 Mode::Char,
329 0..contents.len(),
330 EscapeError::MoreThanOneChar,
331 )
332 .expect("expected error");
333 }
334
335 let span = self.mk_sp(start, self.pos);
336
337 let lifetime_name_without_tick =
338 Symbol::intern(&self.str_from(ident_start));
339 if !lifetime_name_without_tick.can_be_raw() {
340 self.dcx().emit_err(
341 errors::CannotBeRawLifetime {
342 span,
343 ident: lifetime_name_without_tick
344 }
345 );
346 }
347
348 let mut lifetime_name =
350 String::with_capacity(lifetime_name_without_tick.as_str().len() + 1);
351 lifetime_name.push('\'');
352 lifetime_name += lifetime_name_without_tick.as_str();
353 let sym = Symbol::intern(&lifetime_name);
354
355 self.psess.raw_identifier_spans.push(span);
357
358 token::Lifetime(sym, IdentIsRaw::Yes)
359 } else {
360 self.psess.buffer_lint(
362 RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX,
363 prefix_span,
364 ast::CRATE_NODE_ID,
365 BuiltinLintDiag::RawPrefix(prefix_span),
366 );
367
368 let lt_start = start + BytePos(2);
370 self.pos = lt_start;
371 self.cursor = Cursor::new(&str_before[2 as usize..], FrontmatterAllowed::No);
372
373 let lifetime_name = self.str_from(start);
374 let ident = Symbol::intern(lifetime_name);
375 token::Lifetime(ident, IdentIsRaw::No)
376 }
377 }
378 rustc_lexer::TokenKind::Semi => token::Semi,
379 rustc_lexer::TokenKind::Comma => token::Comma,
380 rustc_lexer::TokenKind::Dot => token::Dot,
381 rustc_lexer::TokenKind::OpenParen => token::OpenParen,
382 rustc_lexer::TokenKind::CloseParen => token::CloseParen,
383 rustc_lexer::TokenKind::OpenBrace => token::OpenBrace,
384 rustc_lexer::TokenKind::CloseBrace => token::CloseBrace,
385 rustc_lexer::TokenKind::OpenBracket => token::OpenBracket,
386 rustc_lexer::TokenKind::CloseBracket => token::CloseBracket,
387 rustc_lexer::TokenKind::At => token::At,
388 rustc_lexer::TokenKind::Pound => token::Pound,
389 rustc_lexer::TokenKind::Tilde => token::Tilde,
390 rustc_lexer::TokenKind::Question => token::Question,
391 rustc_lexer::TokenKind::Colon => token::Colon,
392 rustc_lexer::TokenKind::Dollar => token::Dollar,
393 rustc_lexer::TokenKind::Eq => token::Eq,
394 rustc_lexer::TokenKind::Bang => token::Bang,
395 rustc_lexer::TokenKind::Lt => token::Lt,
396 rustc_lexer::TokenKind::Gt => token::Gt,
397 rustc_lexer::TokenKind::Minus => token::Minus,
398 rustc_lexer::TokenKind::And => token::And,
399 rustc_lexer::TokenKind::Or => token::Or,
400 rustc_lexer::TokenKind::Plus => token::Plus,
401 rustc_lexer::TokenKind::Star => token::Star,
402 rustc_lexer::TokenKind::Slash => token::Slash,
403 rustc_lexer::TokenKind::Caret => token::Caret,
404 rustc_lexer::TokenKind::Percent => token::Percent,
405
406 rustc_lexer::TokenKind::Unknown | rustc_lexer::TokenKind::InvalidIdent => {
407 if swallow_next_invalid > 0 {
409 swallow_next_invalid -= 1;
410 continue;
411 }
412 let mut it = self.str_from_to_end(start).chars();
413 let c = it.next().unwrap();
414 if c == '\u{00a0}' {
415 if self.nbsp_is_whitespace {
419 preceded_by_whitespace = true;
420 continue;
421 }
422 self.nbsp_is_whitespace = true;
423 }
424 let repeats = it.take_while(|c1| *c1 == c).count();
425 let (token, sugg) =
432 unicode_chars::check_for_substitution(self, start, c, repeats + 1);
433 self.dcx().emit_err(errors::UnknownTokenStart {
434 span: self.mk_sp(start, self.pos + Pos::from_usize(repeats * c.len_utf8())),
435 escaped: escaped_char(c),
436 sugg,
437 null: if c == '\x00' { Some(errors::UnknownTokenNull) } else { None },
438 repeat: if repeats > 0 {
439 swallow_next_invalid = repeats;
440 Some(errors::UnknownTokenRepeat { repeats })
441 } else {
442 None
443 },
444 });
445
446 if let Some(token) = token {
447 token
448 } else {
449 preceded_by_whitespace = true;
450 continue;
451 }
452 }
453 rustc_lexer::TokenKind::Eof => token::Eof,
454 };
455 let span = self.mk_sp(start, self.pos);
456 return (Token::new(kind, span), preceded_by_whitespace);
457 }
458 }
459
460 fn ident(&self, start: BytePos) -> TokenKind {
461 let sym = nfc_normalize(self.str_from(start));
462 let span = self.mk_sp(start, self.pos);
463 self.psess.symbol_gallery.insert(sym, span);
464 token::Ident(sym, IdentIsRaw::No)
465 }
466
467 fn lint_unicode_text_flow(&self, start: BytePos) {
470 let content_start = start + BytePos(2);
472 let content = self.str_from(content_start);
473 if contains_text_flow_control_chars(content) {
474 let span = self.mk_sp(start, self.pos);
475 self.psess.buffer_lint(
476 TEXT_DIRECTION_CODEPOINT_IN_COMMENT,
477 span,
478 ast::CRATE_NODE_ID,
479 BuiltinLintDiag::UnicodeTextFlow(span, content.to_string()),
480 );
481 }
482 }
483
484 fn validate_frontmatter(
485 &self,
486 start: BytePos,
487 has_invalid_preceding_whitespace: bool,
488 invalid_infostring: bool,
489 ) {
490 let s = self.str_from(start);
491 let real_start = s.find("---").unwrap();
492 let frontmatter_opening_pos = BytePos(real_start as u32) + start;
493 let s_new = &s[real_start..];
494 let within = s_new.trim_start_matches('-');
495 let len_opening = s_new.len() - within.len();
496
497 let frontmatter_opening_end_pos = frontmatter_opening_pos + BytePos(len_opening as u32);
498 if has_invalid_preceding_whitespace {
499 let line_start =
500 BytePos(s[..real_start].rfind("\n").map_or(0, |i| i as u32 + 1)) + start;
501 let span = self.mk_sp(line_start, frontmatter_opening_end_pos);
502 let label_span = self.mk_sp(line_start, frontmatter_opening_pos);
503 self.dcx().emit_err(errors::FrontmatterInvalidOpeningPrecedingWhitespace {
504 span,
505 note_span: label_span,
506 });
507 }
508
509 if invalid_infostring {
510 let line_end = s[real_start..].find('\n').unwrap_or(s[real_start..].len());
511 let span = self.mk_sp(
512 frontmatter_opening_end_pos,
513 frontmatter_opening_pos + BytePos(line_end as u32),
514 );
515 self.dcx().emit_err(errors::FrontmatterInvalidInfostring { span });
516 }
517
518 let last_line_start = within.rfind('\n').map_or(0, |i| i + 1);
519 let last_line = &within[last_line_start..];
520 let last_line_trimmed = last_line.trim_start_matches(is_whitespace);
521 let last_line_start_pos = frontmatter_opening_end_pos + BytePos(last_line_start as u32);
522
523 let frontmatter_span = self.mk_sp(frontmatter_opening_pos, self.pos);
524 self.psess.gated_spans.gate(sym::frontmatter, frontmatter_span);
525
526 if !last_line_trimmed.starts_with("---") {
527 let label_span = self.mk_sp(frontmatter_opening_pos, frontmatter_opening_end_pos);
528 self.dcx().emit_err(errors::FrontmatterUnclosed {
529 span: frontmatter_span,
530 note_span: label_span,
531 });
532 return;
533 }
534
535 if last_line_trimmed.len() != last_line.len() {
536 let line_end = last_line_start_pos + BytePos(last_line.len() as u32);
537 let span = self.mk_sp(last_line_start_pos, line_end);
538 let whitespace_end =
539 last_line_start_pos + BytePos((last_line.len() - last_line_trimmed.len()) as u32);
540 let label_span = self.mk_sp(last_line_start_pos, whitespace_end);
541 self.dcx().emit_err(errors::FrontmatterInvalidClosingPrecedingWhitespace {
542 span,
543 note_span: label_span,
544 });
545 }
546
547 let rest = last_line_trimmed.trim_start_matches('-');
548 let len_close = last_line_trimmed.len() - rest.len();
549 if len_close != len_opening {
550 let span = self.mk_sp(frontmatter_opening_pos, self.pos);
551 let opening = self.mk_sp(frontmatter_opening_pos, frontmatter_opening_end_pos);
552 let last_line_close_pos = last_line_start_pos + BytePos(len_close as u32);
553 let close = self.mk_sp(last_line_start_pos, last_line_close_pos);
554 self.dcx().emit_err(errors::FrontmatterLengthMismatch {
555 span,
556 opening,
557 close,
558 len_opening,
559 len_close,
560 });
561 }
562
563 if !rest.trim_matches(is_whitespace).is_empty() {
564 let span = self.mk_sp(last_line_start_pos, self.pos);
565 self.dcx().emit_err(errors::FrontmatterExtraCharactersAfterClose { span });
566 }
567 }
568
569 fn cook_doc_comment(
570 &self,
571 content_start: BytePos,
572 content: &str,
573 comment_kind: CommentKind,
574 doc_style: DocStyle,
575 ) -> TokenKind {
576 if content.contains('\r') {
577 for (idx, _) in content.char_indices().filter(|&(_, c)| c == '\r') {
578 let span = self.mk_sp(
579 content_start + BytePos(idx as u32),
580 content_start + BytePos(idx as u32 + 1),
581 );
582 let block = matches!(comment_kind, CommentKind::Block);
583 self.dcx().emit_err(errors::CrDocComment { span, block });
584 }
585 }
586
587 let attr_style = match doc_style {
588 DocStyle::Outer => AttrStyle::Outer,
589 DocStyle::Inner => AttrStyle::Inner,
590 };
591
592 token::DocComment(comment_kind, attr_style, Symbol::intern(content))
593 }
594
595 fn cook_lexer_literal(
596 &self,
597 start: BytePos,
598 end: BytePos,
599 kind: rustc_lexer::LiteralKind,
600 ) -> (token::LitKind, Symbol) {
601 match kind {
602 rustc_lexer::LiteralKind::Char { terminated } => {
603 if !terminated {
604 let mut err = self
605 .dcx()
606 .struct_span_fatal(self.mk_sp(start, end), "unterminated character literal")
607 .with_code(E0762);
608 if let Some(lt_sp) = self.last_lifetime {
609 err.multipart_suggestion(
610 "if you meant to write a string literal, use double quotes",
611 vec![
612 (lt_sp, "\"".to_string()),
613 (self.mk_sp(start, start + BytePos(1)), "\"".to_string()),
614 ],
615 Applicability::MaybeIncorrect,
616 );
617 }
618 err.emit()
619 }
620 self.cook_unicode(token::Char, Mode::Char, start, end, 1, 1) }
622 rustc_lexer::LiteralKind::Byte { terminated } => {
623 if !terminated {
624 self.dcx()
625 .struct_span_fatal(
626 self.mk_sp(start + BytePos(1), end),
627 "unterminated byte constant",
628 )
629 .with_code(E0763)
630 .emit()
631 }
632 self.cook_unicode(token::Byte, Mode::Byte, start, end, 2, 1) }
634 rustc_lexer::LiteralKind::Str { terminated } => {
635 if !terminated {
636 self.dcx()
637 .struct_span_fatal(
638 self.mk_sp(start, end),
639 "unterminated double quote string",
640 )
641 .with_code(E0765)
642 .emit()
643 }
644 self.cook_unicode(token::Str, Mode::Str, start, end, 1, 1) }
646 rustc_lexer::LiteralKind::ByteStr { terminated } => {
647 if !terminated {
648 self.dcx()
649 .struct_span_fatal(
650 self.mk_sp(start + BytePos(1), end),
651 "unterminated double quote byte string",
652 )
653 .with_code(E0766)
654 .emit()
655 }
656 self.cook_unicode(token::ByteStr, Mode::ByteStr, start, end, 2, 1) }
658 rustc_lexer::LiteralKind::CStr { terminated } => {
659 if !terminated {
660 self.dcx()
661 .struct_span_fatal(
662 self.mk_sp(start + BytePos(1), end),
663 "unterminated C string",
664 )
665 .with_code(E0767)
666 .emit()
667 }
668 self.cook_mixed(token::CStr, Mode::CStr, start, end, 2, 1) }
670 rustc_lexer::LiteralKind::RawStr { n_hashes } => {
671 if let Some(n_hashes) = n_hashes {
672 let n = u32::from(n_hashes);
673 let kind = token::StrRaw(n_hashes);
674 self.cook_unicode(kind, Mode::RawStr, start, end, 2 + n, 1 + n) } else {
676 self.report_raw_str_error(start, 1);
677 }
678 }
679 rustc_lexer::LiteralKind::RawByteStr { n_hashes } => {
680 if let Some(n_hashes) = n_hashes {
681 let n = u32::from(n_hashes);
682 let kind = token::ByteStrRaw(n_hashes);
683 self.cook_unicode(kind, Mode::RawByteStr, start, end, 3 + n, 1 + n) } else {
685 self.report_raw_str_error(start, 2);
686 }
687 }
688 rustc_lexer::LiteralKind::RawCStr { n_hashes } => {
689 if let Some(n_hashes) = n_hashes {
690 let n = u32::from(n_hashes);
691 let kind = token::CStrRaw(n_hashes);
692 self.cook_unicode(kind, Mode::RawCStr, start, end, 3 + n, 1 + n) } else {
694 self.report_raw_str_error(start, 2);
695 }
696 }
697 rustc_lexer::LiteralKind::Int { base, empty_int } => {
698 let mut kind = token::Integer;
699 if empty_int {
700 let span = self.mk_sp(start, end);
701 let guar = self.dcx().emit_err(errors::NoDigitsLiteral { span });
702 kind = token::Err(guar);
703 } else if matches!(base, Base::Binary | Base::Octal) {
704 let base = base as u32;
705 let s = self.str_from_to(start + BytePos(2), end);
706 for (idx, c) in s.char_indices() {
707 let span = self.mk_sp(
708 start + BytePos::from_usize(2 + idx),
709 start + BytePos::from_usize(2 + idx + c.len_utf8()),
710 );
711 if c != '_' && c.to_digit(base).is_none() {
712 let guar =
713 self.dcx().emit_err(errors::InvalidDigitLiteral { span, base });
714 kind = token::Err(guar);
715 }
716 }
717 }
718 (kind, self.symbol_from_to(start, end))
719 }
720 rustc_lexer::LiteralKind::Float { base, empty_exponent } => {
721 let mut kind = token::Float;
722 if empty_exponent {
723 let span = self.mk_sp(start, self.pos);
724 let guar = self.dcx().emit_err(errors::EmptyExponentFloat { span });
725 kind = token::Err(guar);
726 }
727 let base = match base {
728 Base::Hexadecimal => Some("hexadecimal"),
729 Base::Octal => Some("octal"),
730 Base::Binary => Some("binary"),
731 _ => None,
732 };
733 if let Some(base) = base {
734 let span = self.mk_sp(start, end);
735 let guar =
736 self.dcx().emit_err(errors::FloatLiteralUnsupportedBase { span, base });
737 kind = token::Err(guar)
738 }
739 (kind, self.symbol_from_to(start, end))
740 }
741 }
742 }
743
744 #[inline]
745 fn src_index(&self, pos: BytePos) -> usize {
746 (pos - self.start_pos).to_usize()
747 }
748
749 fn str_from(&self, start: BytePos) -> &'src str {
752 self.str_from_to(start, self.pos)
753 }
754
755 fn symbol_from_to(&self, start: BytePos, end: BytePos) -> Symbol {
757 debug!("taking an ident from {:?} to {:?}", start, end);
758 Symbol::intern(self.str_from_to(start, end))
759 }
760
761 fn str_from_to(&self, start: BytePos, end: BytePos) -> &'src str {
763 &self.src[self.src_index(start)..self.src_index(end)]
764 }
765
766 fn str_from_to_end(&self, start: BytePos) -> &'src str {
768 &self.src[self.src_index(start)..]
769 }
770
771 fn report_raw_str_error(&self, start: BytePos, prefix_len: u32) -> ! {
772 match rustc_lexer::validate_raw_str(self.str_from(start), prefix_len) {
773 Err(RawStrError::InvalidStarter { bad_char }) => {
774 self.report_non_started_raw_string(start, bad_char)
775 }
776 Err(RawStrError::NoTerminator { expected, found, possible_terminator_offset }) => self
777 .report_unterminated_raw_string(start, expected, possible_terminator_offset, found),
778 Err(RawStrError::TooManyDelimiters { found }) => {
779 self.report_too_many_hashes(start, found)
780 }
781 Ok(()) => panic!("no error found for supposedly invalid raw string literal"),
782 }
783 }
784
785 fn report_non_started_raw_string(&self, start: BytePos, bad_char: char) -> ! {
786 self.dcx()
787 .struct_span_fatal(
788 self.mk_sp(start, self.pos),
789 format!(
790 "found invalid character; only `#` is allowed in raw string delimitation: {}",
791 escaped_char(bad_char)
792 ),
793 )
794 .emit()
795 }
796
797 fn report_unterminated_raw_string(
798 &self,
799 start: BytePos,
800 n_hashes: u32,
801 possible_offset: Option<u32>,
802 found_terminators: u32,
803 ) -> ! {
804 let mut err =
805 self.dcx().struct_span_fatal(self.mk_sp(start, start), "unterminated raw string");
806 err.code(E0748);
807 err.span_label(self.mk_sp(start, start), "unterminated raw string");
808
809 if n_hashes > 0 {
810 err.note(format!(
811 "this raw string should be terminated with `\"{}`",
812 "#".repeat(n_hashes as usize)
813 ));
814 }
815
816 if let Some(possible_offset) = possible_offset {
817 let lo = start + BytePos(possible_offset);
818 let hi = lo + BytePos(found_terminators);
819 let span = self.mk_sp(lo, hi);
820 err.span_suggestion(
821 span,
822 "consider terminating the string here",
823 "#".repeat(n_hashes as usize),
824 Applicability::MaybeIncorrect,
825 );
826 }
827
828 err.emit()
829 }
830
831 fn report_unterminated_block_comment(&self, start: BytePos, doc_style: Option<DocStyle>) {
832 let msg = match doc_style {
833 Some(_) => "unterminated block doc-comment",
834 None => "unterminated block comment",
835 };
836 let last_bpos = self.pos;
837 let mut err = self.dcx().struct_span_fatal(self.mk_sp(start, last_bpos), msg);
838 err.code(E0758);
839 let mut nested_block_comment_open_idxs = vec![];
840 let mut last_nested_block_comment_idxs = None;
841 let mut content_chars = self.str_from(start).char_indices().peekable();
842
843 while let Some((idx, current_char)) = content_chars.next() {
844 match content_chars.peek() {
845 Some((_, '*')) if current_char == '/' => {
846 nested_block_comment_open_idxs.push(idx);
847 }
848 Some((_, '/')) if current_char == '*' => {
849 last_nested_block_comment_idxs =
850 nested_block_comment_open_idxs.pop().map(|open_idx| (open_idx, idx));
851 }
852 _ => {}
853 };
854 }
855
856 if let Some((nested_open_idx, nested_close_idx)) = last_nested_block_comment_idxs {
857 err.span_label(self.mk_sp(start, start + BytePos(2)), msg)
858 .span_label(
859 self.mk_sp(
860 start + BytePos(nested_open_idx as u32),
861 start + BytePos(nested_open_idx as u32 + 2),
862 ),
863 "...as last nested comment starts here, maybe you want to close this instead?",
864 )
865 .span_label(
866 self.mk_sp(
867 start + BytePos(nested_close_idx as u32),
868 start + BytePos(nested_close_idx as u32 + 2),
869 ),
870 "...and last nested comment terminates here.",
871 );
872 }
873
874 err.emit();
875 }
876
877 fn report_unknown_prefix(&self, start: BytePos) {
882 let prefix_span = self.mk_sp(start, self.pos);
883 let prefix = self.str_from_to(start, self.pos);
884 let expn_data = prefix_span.ctxt().outer_expn_data();
885
886 if expn_data.edition.at_least_rust_2021() {
887 let sugg = if prefix == "rb" {
889 Some(errors::UnknownPrefixSugg::UseBr(prefix_span))
890 } else if prefix == "rc" {
891 Some(errors::UnknownPrefixSugg::UseCr(prefix_span))
892 } else if expn_data.is_root() {
893 if self.cursor.first() == '\''
894 && let Some(start) = self.last_lifetime
895 && self.cursor.third() != '\''
896 && let end = self.mk_sp(self.pos, self.pos + BytePos(1))
897 && !self.psess.source_map().is_multiline(start.until(end))
898 {
899 Some(errors::UnknownPrefixSugg::MeantStr { start, end })
903 } else {
904 Some(errors::UnknownPrefixSugg::Whitespace(prefix_span.shrink_to_hi()))
905 }
906 } else {
907 None
908 };
909 self.dcx().emit_err(errors::UnknownPrefix { span: prefix_span, prefix, sugg });
910 } else {
911 self.psess.buffer_lint(
913 RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX,
914 prefix_span,
915 ast::CRATE_NODE_ID,
916 BuiltinLintDiag::ReservedPrefix(prefix_span, prefix.to_string()),
917 );
918 }
919 }
920
921 fn maybe_report_guarded_str(&mut self, start: BytePos, str_before: &'src str) -> TokenKind {
928 let span = self.mk_sp(start, self.pos);
929 let edition2024 = span.edition().at_least_rust_2024();
930
931 let space_pos = start + BytePos(1);
932 let space_span = self.mk_sp(space_pos, space_pos);
933
934 let mut cursor = Cursor::new(str_before, FrontmatterAllowed::No);
935
936 let (is_string, span, unterminated) = match cursor.guarded_double_quoted_string() {
937 Some(rustc_lexer::GuardedStr { n_hashes, terminated, token_len }) => {
938 let end = start + BytePos(token_len);
939 let span = self.mk_sp(start, end);
940 let str_start = start + BytePos(n_hashes);
941
942 if edition2024 {
943 self.cursor = cursor;
944 self.pos = end;
945 }
946
947 let unterminated = if terminated { None } else { Some(str_start) };
948
949 (true, span, unterminated)
950 }
951 None => {
952 debug_assert_eq!(self.str_from_to(start, start + BytePos(2)), "##");
954
955 (false, span, None)
956 }
957 };
958 if edition2024 {
959 if let Some(str_start) = unterminated {
960 self.dcx()
962 .struct_span_fatal(
963 self.mk_sp(str_start, self.pos),
964 "unterminated double quote string",
965 )
966 .with_code(E0765)
967 .emit()
968 }
969
970 let sugg = if span.from_expansion() {
971 None
972 } else {
973 Some(errors::GuardedStringSugg(space_span))
974 };
975
976 let err = if is_string {
978 self.dcx().emit_err(errors::ReservedString { span, sugg })
979 } else {
980 self.dcx().emit_err(errors::ReservedMultihash { span, sugg })
981 };
982
983 token::Literal(token::Lit {
984 kind: token::Err(err),
985 symbol: self.symbol_from_to(start, self.pos),
986 suffix: None,
987 })
988 } else {
989 self.psess.buffer_lint(
991 RUST_2024_GUARDED_STRING_INCOMPATIBLE_SYNTAX,
992 span,
993 ast::CRATE_NODE_ID,
994 BuiltinLintDiag::ReservedString { is_string, suggestion: space_span },
995 );
996
997 self.pos = start + BytePos(1);
1000 self.cursor = Cursor::new(&str_before[1..], FrontmatterAllowed::No);
1001 token::Pound
1002 }
1003 }
1004
1005 fn report_too_many_hashes(&self, start: BytePos, num: u32) -> ! {
1006 self.dcx().emit_fatal(errors::TooManyHashes { span: self.mk_sp(start, self.pos), num });
1007 }
1008
1009 fn cook_common(
1010 &self,
1011 mut kind: token::LitKind,
1012 mode: Mode,
1013 start: BytePos,
1014 end: BytePos,
1015 prefix_len: u32,
1016 postfix_len: u32,
1017 unescape: fn(&str, Mode, &mut dyn FnMut(Range<usize>, Result<(), EscapeError>)),
1018 ) -> (token::LitKind, Symbol) {
1019 let content_start = start + BytePos(prefix_len);
1020 let content_end = end - BytePos(postfix_len);
1021 let lit_content = self.str_from_to(content_start, content_end);
1022 unescape(lit_content, mode, &mut |range, result| {
1023 if let Err(err) = result {
1025 let span_with_quotes = self.mk_sp(start, end);
1026 let (start, end) = (range.start as u32, range.end as u32);
1027 let lo = content_start + BytePos(start);
1028 let hi = lo + BytePos(end - start);
1029 let span = self.mk_sp(lo, hi);
1030 let is_fatal = err.is_fatal();
1031 if let Some(guar) = emit_unescape_error(
1032 self.dcx(),
1033 lit_content,
1034 span_with_quotes,
1035 span,
1036 mode,
1037 range,
1038 err,
1039 ) {
1040 assert!(is_fatal);
1041 kind = token::Err(guar);
1042 }
1043 }
1044 });
1045
1046 let sym = if !matches!(kind, token::Err(_)) {
1049 Symbol::intern(lit_content)
1050 } else {
1051 self.symbol_from_to(start, end)
1052 };
1053 (kind, sym)
1054 }
1055
1056 fn cook_unicode(
1057 &self,
1058 kind: token::LitKind,
1059 mode: Mode,
1060 start: BytePos,
1061 end: BytePos,
1062 prefix_len: u32,
1063 postfix_len: u32,
1064 ) -> (token::LitKind, Symbol) {
1065 self.cook_common(kind, mode, start, end, prefix_len, postfix_len, |src, mode, callback| {
1066 unescape_unicode(src, mode, &mut |span, result| callback(span, result.map(drop)))
1067 })
1068 }
1069
1070 fn cook_mixed(
1071 &self,
1072 kind: token::LitKind,
1073 mode: Mode,
1074 start: BytePos,
1075 end: BytePos,
1076 prefix_len: u32,
1077 postfix_len: u32,
1078 ) -> (token::LitKind, Symbol) {
1079 self.cook_common(kind, mode, start, end, prefix_len, postfix_len, |src, mode, callback| {
1080 unescape_mixed(src, mode, &mut |span, result| callback(span, result.map(drop)))
1081 })
1082 }
1083}
1084
1085pub fn nfc_normalize(string: &str) -> Symbol {
1086 use unicode_normalization::{IsNormalized, UnicodeNormalization, is_nfc_quick};
1087 match is_nfc_quick(string.chars()) {
1088 IsNormalized::Yes => Symbol::intern(string),
1089 _ => {
1090 let normalized_str: String = string.chars().nfc().collect();
1091 Symbol::intern(&normalized_str)
1092 }
1093 }
1094}