mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 16:24:46 +00:00
Introduce rustc_lexer
The idea here is to make a reusable library out of the existing rust-lexer, by separating out pure lexing and rustc-specific concerns, like spans, error reporting an interning. So, rustc_lexer operates directly on `&str`, produces simple tokens which are a pair of type-tag and a bit of original text, and does not report errors, instead storing them as flags on the token.
This commit is contained in:
parent
95b1fe560d
commit
395ee0b79f
@ -2972,6 +2972,13 @@ dependencies = [
|
|||||||
"tempfile 3.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"tempfile 3.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc_lexer"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc_lint"
|
name = "rustc_lint"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
@ -3622,6 +3629,7 @@ dependencies = [
|
|||||||
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rustc_data_structures 0.0.0",
|
"rustc_data_structures 0.0.0",
|
||||||
"rustc_errors 0.0.0",
|
"rustc_errors 0.0.0",
|
||||||
|
"rustc_lexer 0.1.0",
|
||||||
"rustc_macros 0.1.0",
|
"rustc_macros 0.1.0",
|
||||||
"rustc_target 0.0.0",
|
"rustc_target 0.0.0",
|
||||||
"scoped-tls 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"scoped-tls 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
9
src/librustc_lexer/Cargo.toml
Normal file
9
src/librustc_lexer/Cargo.toml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
[package]
|
||||||
|
authors = ["The Rust Project Developers"]
|
||||||
|
name = "rustc_lexer"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# Note that this crate purposefully does not depend on other rustc crates
|
||||||
|
[dependencies]
|
||||||
|
unicode-xid = { version = "0.1.0", optional = true }
|
57
src/librustc_lexer/src/cursor.rs
Normal file
57
src/librustc_lexer/src/cursor.rs
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
use std::str::Chars;
|
||||||
|
|
||||||
|
pub(crate) struct Cursor<'a> {
|
||||||
|
initial_len: usize,
|
||||||
|
chars: Chars<'a>,
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
prev: char,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) const EOF_CHAR: char = '\0';
|
||||||
|
|
||||||
|
impl<'a> Cursor<'a> {
|
||||||
|
pub(crate) fn new(input: &'a str) -> Cursor<'a> {
|
||||||
|
Cursor {
|
||||||
|
initial_len: input.len(),
|
||||||
|
chars: input.chars(),
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
prev: EOF_CHAR,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// For debug assertions only
|
||||||
|
pub(crate) fn prev(&self) -> char {
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
{
|
||||||
|
self.prev
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
{
|
||||||
|
'\0'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub(crate) fn nth_char(&self, n: usize) -> char {
|
||||||
|
self.chars().nth(n).unwrap_or(EOF_CHAR)
|
||||||
|
}
|
||||||
|
pub(crate) fn is_eof(&self) -> bool {
|
||||||
|
self.chars.as_str().is_empty()
|
||||||
|
}
|
||||||
|
pub(crate) fn len_consumed(&self) -> usize {
|
||||||
|
self.initial_len - self.chars.as_str().len()
|
||||||
|
}
|
||||||
|
/// Returns an iterator over the remaining characters.
|
||||||
|
fn chars(&self) -> Chars<'a> {
|
||||||
|
self.chars.clone()
|
||||||
|
}
|
||||||
|
/// Moves to the next character.
|
||||||
|
pub(crate) fn bump(&mut self) -> Option<char> {
|
||||||
|
let c = self.chars.next()?;
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
{
|
||||||
|
self.prev = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(c)
|
||||||
|
}
|
||||||
|
}
|
710
src/librustc_lexer/src/lib.rs
Normal file
710
src/librustc_lexer/src/lib.rs
Normal file
@ -0,0 +1,710 @@
|
|||||||
|
// We want to be able to build this crate with a stable compiler, so feature
|
||||||
|
// flags should optional.
|
||||||
|
#![cfg_attr(not(feature = "unicode-xid"), feature(rustc_private))]
|
||||||
|
#![cfg_attr(not(feature = "unicode-xid"), feature(unicode_internals))]
|
||||||
|
|
||||||
|
mod cursor;
|
||||||
|
|
||||||
|
use crate::cursor::{Cursor, EOF_CHAR};
|
||||||
|
|
||||||
|
pub struct Token {
|
||||||
|
pub kind: TokenKind,
|
||||||
|
pub len: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub enum TokenKind {
|
||||||
|
LineComment,
|
||||||
|
BlockComment { terminated: bool },
|
||||||
|
Whitespace,
|
||||||
|
Ident,
|
||||||
|
RawIdent,
|
||||||
|
Literal { kind: LiteralKind, suffix_start: usize },
|
||||||
|
Lifetime { starts_with_number: bool },
|
||||||
|
Semi,
|
||||||
|
Comma,
|
||||||
|
DotDotDot,
|
||||||
|
DotDotEq,
|
||||||
|
DotDot,
|
||||||
|
Dot,
|
||||||
|
OpenParen,
|
||||||
|
CloseParen,
|
||||||
|
OpenBrace,
|
||||||
|
CloseBrace,
|
||||||
|
OpenBracket,
|
||||||
|
CloseBracket,
|
||||||
|
At,
|
||||||
|
Pound,
|
||||||
|
Tilde,
|
||||||
|
Question,
|
||||||
|
ColonColon,
|
||||||
|
Colon,
|
||||||
|
Dollar,
|
||||||
|
EqEq,
|
||||||
|
Eq,
|
||||||
|
FatArrow,
|
||||||
|
Ne,
|
||||||
|
Not,
|
||||||
|
Le,
|
||||||
|
LArrow,
|
||||||
|
Lt,
|
||||||
|
ShlEq,
|
||||||
|
Shl,
|
||||||
|
Ge,
|
||||||
|
Gt,
|
||||||
|
ShrEq,
|
||||||
|
Shr,
|
||||||
|
RArrow,
|
||||||
|
Minus,
|
||||||
|
MinusEq,
|
||||||
|
And,
|
||||||
|
AndAnd,
|
||||||
|
AndEq,
|
||||||
|
Or,
|
||||||
|
OrOr,
|
||||||
|
OrEq,
|
||||||
|
PlusEq,
|
||||||
|
Plus,
|
||||||
|
StarEq,
|
||||||
|
Star,
|
||||||
|
SlashEq,
|
||||||
|
Slash,
|
||||||
|
CaretEq,
|
||||||
|
Caret,
|
||||||
|
PercentEq,
|
||||||
|
Percent,
|
||||||
|
Unknown,
|
||||||
|
}
|
||||||
|
use self::TokenKind::*;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub enum LiteralKind {
|
||||||
|
Int { base: Base, empty_int: bool },
|
||||||
|
Float { base: Base, empty_exponent: bool },
|
||||||
|
Char { terminated: bool },
|
||||||
|
Byte { terminated: bool },
|
||||||
|
Str { terminated: bool },
|
||||||
|
ByteStr { terminated: bool },
|
||||||
|
RawStr { n_hashes: usize, started: bool, terminated: bool },
|
||||||
|
RawByteStr { n_hashes: usize, started: bool, terminated: bool },
|
||||||
|
}
|
||||||
|
use self::LiteralKind::*;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub enum Base {
|
||||||
|
Binary,
|
||||||
|
Octal,
|
||||||
|
Hexadecimal,
|
||||||
|
Decimal,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Token {
|
||||||
|
fn new(kind: TokenKind, len: usize) -> Token {
|
||||||
|
Token { kind, len }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn strip_shebang(input: &str) -> Option<usize> {
|
||||||
|
debug_assert!(!input.is_empty());
|
||||||
|
if !input.starts_with("#!") || input.starts_with("#![") {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(input.find('\n').unwrap_or(input.len()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn first_token(input: &str) -> Token {
|
||||||
|
debug_assert!(!input.is_empty());
|
||||||
|
Cursor::new(input).advance_token()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tokenize(mut input: &str) -> impl Iterator<Item = Token> + '_ {
|
||||||
|
std::iter::from_fn(move || {
|
||||||
|
if input.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let token = first_token(input);
|
||||||
|
input = &input[token.len..];
|
||||||
|
Some(token)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Cursor<'_> {
|
||||||
|
fn advance_token(&mut self) -> Token {
|
||||||
|
let first_char = self.bump().unwrap();
|
||||||
|
let token_kind = match first_char {
|
||||||
|
'/' => match self.nth_char(0) {
|
||||||
|
'/' => self.line_comment(),
|
||||||
|
'*' => self.block_comment(),
|
||||||
|
_ => {
|
||||||
|
if self.eat_assign() {
|
||||||
|
SlashEq
|
||||||
|
} else {
|
||||||
|
Slash
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
c if character_properties::is_whitespace(c) => self.whitespace(),
|
||||||
|
'r' => match (self.nth_char(0), self.nth_char(1)) {
|
||||||
|
('#', c1) if character_properties::is_id_start(c1) => self.raw_ident(),
|
||||||
|
('#', _) | ('"', _) => {
|
||||||
|
let (n_hashes, started, terminated) = self.raw_double_quoted_string();
|
||||||
|
let suffix_start = self.len_consumed();
|
||||||
|
if terminated {
|
||||||
|
self.eat_literal_suffix();
|
||||||
|
}
|
||||||
|
let kind = RawStr { n_hashes, started, terminated };
|
||||||
|
Literal { kind, suffix_start }
|
||||||
|
}
|
||||||
|
_ => self.ident(),
|
||||||
|
},
|
||||||
|
'b' => match (self.nth_char(0), self.nth_char(1)) {
|
||||||
|
('\'', _) => {
|
||||||
|
self.bump();
|
||||||
|
let terminated = self.single_quoted_string();
|
||||||
|
let suffix_start = self.len_consumed();
|
||||||
|
if terminated {
|
||||||
|
self.eat_literal_suffix();
|
||||||
|
}
|
||||||
|
let kind = Byte { terminated };
|
||||||
|
Literal { kind, suffix_start }
|
||||||
|
}
|
||||||
|
('"', _) => {
|
||||||
|
self.bump();
|
||||||
|
let terminated = self.double_quoted_string();
|
||||||
|
let suffix_start = self.len_consumed();
|
||||||
|
if terminated {
|
||||||
|
self.eat_literal_suffix();
|
||||||
|
}
|
||||||
|
let kind = ByteStr { terminated };
|
||||||
|
Literal { kind, suffix_start }
|
||||||
|
}
|
||||||
|
('r', '"') | ('r', '#') => {
|
||||||
|
self.bump();
|
||||||
|
let (n_hashes, started, terminated) = self.raw_double_quoted_string();
|
||||||
|
let suffix_start = self.len_consumed();
|
||||||
|
if terminated {
|
||||||
|
self.eat_literal_suffix();
|
||||||
|
}
|
||||||
|
let kind = RawByteStr { n_hashes, started, terminated };
|
||||||
|
Literal { kind, suffix_start }
|
||||||
|
}
|
||||||
|
_ => self.ident(),
|
||||||
|
},
|
||||||
|
c if character_properties::is_id_start(c) => self.ident(),
|
||||||
|
c @ '0'..='9' => {
|
||||||
|
let literal_kind = self.number(c);
|
||||||
|
let suffix_start = self.len_consumed();
|
||||||
|
self.eat_literal_suffix();
|
||||||
|
TokenKind::Literal { kind: literal_kind, suffix_start }
|
||||||
|
}
|
||||||
|
';' => Semi,
|
||||||
|
',' => Comma,
|
||||||
|
'.' => {
|
||||||
|
if self.nth_char(0) == '.' {
|
||||||
|
self.bump();
|
||||||
|
if self.nth_char(0) == '.' {
|
||||||
|
self.bump();
|
||||||
|
DotDotDot
|
||||||
|
} else if self.nth_char(0) == '=' {
|
||||||
|
self.bump();
|
||||||
|
DotDotEq
|
||||||
|
} else {
|
||||||
|
DotDot
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Dot
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'(' => OpenParen,
|
||||||
|
')' => CloseParen,
|
||||||
|
'{' => OpenBrace,
|
||||||
|
'}' => CloseBrace,
|
||||||
|
'[' => OpenBracket,
|
||||||
|
']' => CloseBracket,
|
||||||
|
'@' => At,
|
||||||
|
'#' => Pound,
|
||||||
|
'~' => Tilde,
|
||||||
|
'?' => Question,
|
||||||
|
':' => {
|
||||||
|
if self.nth_char(0) == ':' {
|
||||||
|
self.bump();
|
||||||
|
ColonColon
|
||||||
|
} else {
|
||||||
|
Colon
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'$' => Dollar,
|
||||||
|
'=' => {
|
||||||
|
if self.nth_char(0) == '=' {
|
||||||
|
self.bump();
|
||||||
|
EqEq
|
||||||
|
} else if self.nth_char(0) == '>' {
|
||||||
|
self.bump();
|
||||||
|
FatArrow
|
||||||
|
} else {
|
||||||
|
Eq
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'!' => {
|
||||||
|
if self.nth_char(0) == '=' {
|
||||||
|
self.bump();
|
||||||
|
Ne
|
||||||
|
} else {
|
||||||
|
Not
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'<' => match self.nth_char(0) {
|
||||||
|
'=' => {
|
||||||
|
self.bump();
|
||||||
|
Le
|
||||||
|
}
|
||||||
|
'<' => {
|
||||||
|
self.bump();
|
||||||
|
if self.eat_assign() { ShlEq } else { Shl }
|
||||||
|
}
|
||||||
|
'-' => {
|
||||||
|
self.bump();
|
||||||
|
LArrow
|
||||||
|
}
|
||||||
|
_ => Lt,
|
||||||
|
},
|
||||||
|
'>' => match self.nth_char(0) {
|
||||||
|
'=' => {
|
||||||
|
self.bump();
|
||||||
|
Ge
|
||||||
|
}
|
||||||
|
'>' => {
|
||||||
|
self.bump();
|
||||||
|
if self.eat_assign() { ShrEq } else { Shr }
|
||||||
|
}
|
||||||
|
_ => Gt,
|
||||||
|
},
|
||||||
|
'-' => {
|
||||||
|
if self.nth_char(0) == '>' {
|
||||||
|
self.bump();
|
||||||
|
RArrow
|
||||||
|
} else {
|
||||||
|
if self.eat_assign() { MinusEq } else { Minus }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'&' => {
|
||||||
|
if self.nth_char(0) == '&' {
|
||||||
|
self.bump();
|
||||||
|
AndAnd
|
||||||
|
} else {
|
||||||
|
if self.eat_assign() { AndEq } else { And }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'|' => {
|
||||||
|
if self.nth_char(0) == '|' {
|
||||||
|
self.bump();
|
||||||
|
OrOr
|
||||||
|
} else {
|
||||||
|
if self.eat_assign() { OrEq } else { Or }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'+' => {
|
||||||
|
if self.eat_assign() {
|
||||||
|
PlusEq
|
||||||
|
} else {
|
||||||
|
Plus
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'*' => {
|
||||||
|
if self.eat_assign() {
|
||||||
|
StarEq
|
||||||
|
} else {
|
||||||
|
Star
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'^' => {
|
||||||
|
if self.eat_assign() {
|
||||||
|
CaretEq
|
||||||
|
} else {
|
||||||
|
Caret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'%' => {
|
||||||
|
if self.eat_assign() {
|
||||||
|
PercentEq
|
||||||
|
} else {
|
||||||
|
Percent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'\'' => self.lifetime_or_char(),
|
||||||
|
'"' => {
|
||||||
|
let terminated = self.double_quoted_string();
|
||||||
|
let suffix_start = self.len_consumed();
|
||||||
|
if terminated {
|
||||||
|
self.eat_literal_suffix();
|
||||||
|
}
|
||||||
|
let kind = Str { terminated };
|
||||||
|
Literal { kind, suffix_start }
|
||||||
|
}
|
||||||
|
_ => Unknown,
|
||||||
|
};
|
||||||
|
Token::new(token_kind, self.len_consumed())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn line_comment(&mut self) -> TokenKind {
|
||||||
|
debug_assert!(self.prev() == '/' && self.nth_char(0) == '/');
|
||||||
|
self.bump();
|
||||||
|
loop {
|
||||||
|
match self.nth_char(0) {
|
||||||
|
'\n' => break,
|
||||||
|
'\r' if self.nth_char(1) == '\n' => break,
|
||||||
|
EOF_CHAR if self.is_eof() => break,
|
||||||
|
_ => {
|
||||||
|
self.bump();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LineComment
|
||||||
|
}
|
||||||
|
|
||||||
|
fn block_comment(&mut self) -> TokenKind {
|
||||||
|
debug_assert!(self.prev() == '/' && self.nth_char(0) == '*');
|
||||||
|
self.bump();
|
||||||
|
let mut depth = 1usize;
|
||||||
|
while let Some(c) = self.bump() {
|
||||||
|
match c {
|
||||||
|
'/' if self.nth_char(0) == '*' => {
|
||||||
|
self.bump();
|
||||||
|
depth += 1;
|
||||||
|
}
|
||||||
|
'*' if self.nth_char(0) == '/' => {
|
||||||
|
self.bump();
|
||||||
|
depth -= 1;
|
||||||
|
if depth == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockComment { terminated: depth == 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn whitespace(&mut self) -> TokenKind {
|
||||||
|
debug_assert!(character_properties::is_whitespace(self.prev()));
|
||||||
|
while character_properties::is_whitespace(self.nth_char(0)) {
|
||||||
|
self.bump();
|
||||||
|
}
|
||||||
|
Whitespace
|
||||||
|
}
|
||||||
|
|
||||||
|
fn raw_ident(&mut self) -> TokenKind {
|
||||||
|
debug_assert!(
|
||||||
|
self.prev() == 'r'
|
||||||
|
&& self.nth_char(0) == '#'
|
||||||
|
&& character_properties::is_id_start(self.nth_char(1))
|
||||||
|
);
|
||||||
|
self.bump();
|
||||||
|
self.bump();
|
||||||
|
while character_properties::is_id_continue(self.nth_char(0)) {
|
||||||
|
self.bump();
|
||||||
|
}
|
||||||
|
RawIdent
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ident(&mut self) -> TokenKind {
|
||||||
|
debug_assert!(character_properties::is_id_start(self.prev()));
|
||||||
|
while character_properties::is_id_continue(self.nth_char(0)) {
|
||||||
|
self.bump();
|
||||||
|
}
|
||||||
|
Ident
|
||||||
|
}
|
||||||
|
|
||||||
|
fn number(&mut self, first_digit: char) -> LiteralKind {
|
||||||
|
debug_assert!('0' <= self.prev() && self.prev() <= '9');
|
||||||
|
let mut base = Base::Decimal;
|
||||||
|
if first_digit == '0' {
|
||||||
|
let has_digits = match self.nth_char(0) {
|
||||||
|
'b' => {
|
||||||
|
base = Base::Binary;
|
||||||
|
self.bump();
|
||||||
|
self.eat_decimal_digits()
|
||||||
|
}
|
||||||
|
'o' => {
|
||||||
|
base = Base::Octal;
|
||||||
|
self.bump();
|
||||||
|
self.eat_decimal_digits()
|
||||||
|
}
|
||||||
|
'x' => {
|
||||||
|
base = Base::Hexadecimal;
|
||||||
|
self.bump();
|
||||||
|
self.eat_hexadecimal_digits()
|
||||||
|
}
|
||||||
|
'0'..='9' | '_' | '.' | 'e' | 'E' => {
|
||||||
|
self.eat_decimal_digits();
|
||||||
|
true
|
||||||
|
}
|
||||||
|
// just a 0
|
||||||
|
_ => return Int { base, empty_int: false },
|
||||||
|
};
|
||||||
|
if !has_digits {
|
||||||
|
return Int { base, empty_int: true };
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.eat_decimal_digits();
|
||||||
|
};
|
||||||
|
|
||||||
|
match self.nth_char(0) {
|
||||||
|
// 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.nth_char(1) != '.'
|
||||||
|
&& !character_properties::is_id_start(self.nth_char(1)) =>
|
||||||
|
{
|
||||||
|
// might have stuff after the ., and if it does, it needs to start
|
||||||
|
// with a number
|
||||||
|
self.bump();
|
||||||
|
let mut empty_exponent = false;
|
||||||
|
if self.nth_char(0).is_digit(10) {
|
||||||
|
self.eat_decimal_digits();
|
||||||
|
match self.nth_char(0) {
|
||||||
|
'e' | 'E' => {
|
||||||
|
self.bump();
|
||||||
|
empty_exponent = self.float_exponent().is_err()
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Float { base, empty_exponent }
|
||||||
|
}
|
||||||
|
'e' | 'E' => {
|
||||||
|
self.bump();
|
||||||
|
let empty_exponent = self.float_exponent().is_err();
|
||||||
|
Float { base, empty_exponent }
|
||||||
|
}
|
||||||
|
_ => Int { base, empty_int: false },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lifetime_or_char(&mut self) -> TokenKind {
|
||||||
|
debug_assert!(self.prev() == '\'');
|
||||||
|
let mut starts_with_number = false;
|
||||||
|
if (character_properties::is_id_start(self.nth_char(0))
|
||||||
|
|| self.nth_char(0).is_digit(10) && {
|
||||||
|
starts_with_number = true;
|
||||||
|
true
|
||||||
|
})
|
||||||
|
&& self.nth_char(1) != '\''
|
||||||
|
{
|
||||||
|
self.bump();
|
||||||
|
while character_properties::is_id_continue(self.nth_char(0)) {
|
||||||
|
self.bump();
|
||||||
|
}
|
||||||
|
|
||||||
|
return if self.nth_char(0) == '\'' {
|
||||||
|
self.bump();
|
||||||
|
let kind = Char { terminated: true };
|
||||||
|
Literal { kind, suffix_start: self.len_consumed() }
|
||||||
|
} else {
|
||||||
|
Lifetime { starts_with_number }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
let terminated = self.single_quoted_string();
|
||||||
|
let suffix_start = self.len_consumed();
|
||||||
|
if terminated {
|
||||||
|
self.eat_literal_suffix();
|
||||||
|
}
|
||||||
|
let kind = Char { terminated };
|
||||||
|
return Literal { kind, suffix_start };
|
||||||
|
}
|
||||||
|
|
||||||
|
fn single_quoted_string(&mut self) -> bool {
|
||||||
|
debug_assert!(self.prev() == '\'');
|
||||||
|
// parse `'''` as a single char literal
|
||||||
|
if self.nth_char(0) == '\'' && self.nth_char(1) == '\'' {
|
||||||
|
self.bump();
|
||||||
|
}
|
||||||
|
let mut first = true;
|
||||||
|
loop {
|
||||||
|
match self.nth_char(0) {
|
||||||
|
'/' if !first => break,
|
||||||
|
'\n' if self.nth_char(1) != '\'' => break,
|
||||||
|
'\r' if self.nth_char(1) == '\n' => break,
|
||||||
|
EOF_CHAR if self.is_eof() => break,
|
||||||
|
'\'' => {
|
||||||
|
self.bump();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
'\\' => {
|
||||||
|
self.bump();
|
||||||
|
self.bump();
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
self.bump();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn double_quoted_string(&mut self) -> bool {
|
||||||
|
debug_assert!(self.prev() == '"');
|
||||||
|
loop {
|
||||||
|
match self.nth_char(0) {
|
||||||
|
'"' => {
|
||||||
|
self.bump();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
EOF_CHAR if self.is_eof() => return false,
|
||||||
|
'\\' if self.nth_char(1) == '\\' || self.nth_char(1) == '"' => {
|
||||||
|
self.bump();
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
self.bump();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn raw_double_quoted_string(&mut self) -> (usize, bool, bool) {
|
||||||
|
debug_assert!(self.prev() == 'r');
|
||||||
|
let n_hashes = {
|
||||||
|
let mut acc: usize = 0;
|
||||||
|
loop {
|
||||||
|
match self.bump() {
|
||||||
|
Some('#') => acc += 1,
|
||||||
|
Some('"') => break acc,
|
||||||
|
None | Some(_) => return (acc, false, false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match self.bump() {
|
||||||
|
Some('"') => {
|
||||||
|
let mut acc = n_hashes;
|
||||||
|
while self.nth_char(0) == '#' && acc > 0 {
|
||||||
|
self.bump();
|
||||||
|
acc -= 1;
|
||||||
|
}
|
||||||
|
if acc == 0 {
|
||||||
|
return (n_hashes, true, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(_) => (),
|
||||||
|
None => return (n_hashes, true, false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eat_decimal_digits(&mut self) -> bool {
|
||||||
|
let mut has_digits = false;
|
||||||
|
loop {
|
||||||
|
match self.nth_char(0) {
|
||||||
|
'_' => {
|
||||||
|
self.bump();
|
||||||
|
}
|
||||||
|
'0'..='9' => {
|
||||||
|
has_digits = true;
|
||||||
|
self.bump();
|
||||||
|
}
|
||||||
|
_ => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
has_digits
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eat_hexadecimal_digits(&mut self) -> bool {
|
||||||
|
let mut has_digits = false;
|
||||||
|
loop {
|
||||||
|
match self.nth_char(0) {
|
||||||
|
'_' => {
|
||||||
|
self.bump();
|
||||||
|
}
|
||||||
|
'0'..='9' | 'a'..='f' | 'A'..='F' => {
|
||||||
|
has_digits = true;
|
||||||
|
self.bump();
|
||||||
|
}
|
||||||
|
_ => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
has_digits
|
||||||
|
}
|
||||||
|
|
||||||
|
fn float_exponent(&mut self) -> Result<(), ()> {
|
||||||
|
debug_assert!(self.prev() == 'e' || self.prev() == 'E');
|
||||||
|
if self.nth_char(0) == '-' || self.nth_char(0) == '+' {
|
||||||
|
self.bump();
|
||||||
|
}
|
||||||
|
if self.eat_decimal_digits() { Ok(()) } else { Err(()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eat_literal_suffix(&mut self) {
|
||||||
|
if !character_properties::is_id_start(self.nth_char(0)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.bump();
|
||||||
|
|
||||||
|
while character_properties::is_id_continue(self.nth_char(0)) {
|
||||||
|
self.bump();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eat_assign(&mut self) -> bool {
|
||||||
|
if self.nth_char(0) == '=' {
|
||||||
|
self.bump();
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod character_properties {
|
||||||
|
// this is Pattern_White_Space
|
||||||
|
#[cfg(feature = "unicode-xid")]
|
||||||
|
pub fn is_whitespace(c: char) -> bool {
|
||||||
|
match c {
|
||||||
|
'\u{0009}' | '\u{000A}' | '\u{000B}' | '\u{000C}' | '\u{000D}' | '\u{0020}'
|
||||||
|
| '\u{0085}' | '\u{200E}' | '\u{200F}' | '\u{2028}' | '\u{2029}' => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "unicode-xid"))]
|
||||||
|
pub fn is_whitespace(c: char) -> bool {
|
||||||
|
core::unicode::property::Pattern_White_Space(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is XID_Start OR '_' (which formally is not a XID_Start)
|
||||||
|
#[cfg(feature = "unicode-xid")]
|
||||||
|
pub fn is_id_start(c: char) -> bool {
|
||||||
|
('a' <= c && c <= 'z')
|
||||||
|
|| ('A' <= c && c <= 'Z')
|
||||||
|
|| c == '_'
|
||||||
|
|| (c > '\x7f' && unicode_xid::UnicodeXID::is_xid_start(c))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "unicode-xid"))]
|
||||||
|
pub fn is_id_start(c: char) -> bool {
|
||||||
|
('a' <= c && c <= 'z')
|
||||||
|
|| ('A' <= c && c <= 'Z')
|
||||||
|
|| c == '_'
|
||||||
|
|| (c > '\x7f' && c.is_xid_start())
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is XID_Continue
|
||||||
|
#[cfg(feature = "unicode-xid")]
|
||||||
|
pub fn is_id_continue(c: char) -> bool {
|
||||||
|
('a' <= c && c <= 'z')
|
||||||
|
|| ('A' <= c && c <= 'Z')
|
||||||
|
|| ('0' <= c && c <= '9')
|
||||||
|
|| c == '_'
|
||||||
|
|| (c > '\x7f' && unicode_xid::UnicodeXID::is_xid_continue(c))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "unicode-xid"))]
|
||||||
|
pub fn is_id_continue(c: char) -> bool {
|
||||||
|
('a' <= c && c <= 'z')
|
||||||
|
|| ('A' <= c && c <= 'Z')
|
||||||
|
|| ('0' <= c && c <= '9')
|
||||||
|
|| c == '_'
|
||||||
|
|| (c > '\x7f' && c.is_xid_continue())
|
||||||
|
}
|
||||||
|
}
|
@ -18,6 +18,7 @@ lazy_static = "1.0.0"
|
|||||||
syntax_pos = { path = "../libsyntax_pos" }
|
syntax_pos = { path = "../libsyntax_pos" }
|
||||||
errors = { path = "../librustc_errors", package = "rustc_errors" }
|
errors = { path = "../librustc_errors", package = "rustc_errors" }
|
||||||
rustc_data_structures = { path = "../librustc_data_structures" }
|
rustc_data_structures = { path = "../librustc_data_structures" }
|
||||||
|
rustc_lexer = { path = "../librustc_lexer" }
|
||||||
rustc_macros = { path = "../librustc_macros" }
|
rustc_macros = { path = "../librustc_macros" }
|
||||||
rustc_target = { path = "../librustc_target" }
|
rustc_target = { path = "../librustc_target" }
|
||||||
smallvec = { version = "0.6.7", features = ["union", "may_dangle"] }
|
smallvec = { version = "0.6.7", features = ["union", "may_dangle"] }
|
||||||
|
@ -2,11 +2,10 @@ pub use CommentStyle::*;
|
|||||||
|
|
||||||
use crate::ast;
|
use crate::ast;
|
||||||
use crate::source_map::SourceMap;
|
use crate::source_map::SourceMap;
|
||||||
use crate::parse::lexer::{is_block_doc_comment, is_pattern_whitespace};
|
use crate::parse::lexer::is_block_doc_comment;
|
||||||
use crate::parse::lexer::{self, ParseSess, StringReader};
|
use crate::parse::lexer::ParseSess;
|
||||||
|
|
||||||
use syntax_pos::{BytePos, CharPos, Pos, FileName};
|
use syntax_pos::{BytePos, CharPos, Pos, FileName};
|
||||||
use log::debug;
|
|
||||||
|
|
||||||
use std::usize;
|
use std::usize;
|
||||||
|
|
||||||
@ -135,66 +134,6 @@ pub fn strip_doc_comment_decoration(comment: &str) -> String {
|
|||||||
panic!("not a doc-comment: {}", comment);
|
panic!("not a doc-comment: {}", comment);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_blank_line_comment(rdr: &StringReader<'_>, comments: &mut Vec<Comment>) {
|
|
||||||
debug!(">>> blank-line comment");
|
|
||||||
comments.push(Comment {
|
|
||||||
style: BlankLine,
|
|
||||||
lines: Vec::new(),
|
|
||||||
pos: rdr.pos,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn consume_whitespace_counting_blank_lines(
|
|
||||||
rdr: &mut StringReader<'_>,
|
|
||||||
comments: &mut Vec<Comment>
|
|
||||||
) {
|
|
||||||
while is_pattern_whitespace(rdr.ch) && !rdr.is_eof() {
|
|
||||||
if rdr.ch_is('\n') {
|
|
||||||
push_blank_line_comment(rdr, &mut *comments);
|
|
||||||
}
|
|
||||||
rdr.bump();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_shebang_comment(rdr: &mut StringReader<'_>,
|
|
||||||
code_to_the_left: bool,
|
|
||||||
comments: &mut Vec<Comment>) {
|
|
||||||
debug!(">>> shebang comment");
|
|
||||||
let p = rdr.pos;
|
|
||||||
debug!("<<< shebang comment");
|
|
||||||
comments.push(Comment {
|
|
||||||
style: if code_to_the_left { Trailing } else { Isolated },
|
|
||||||
lines: vec![rdr.read_one_line_comment()],
|
|
||||||
pos: p,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_line_comments(rdr: &mut StringReader<'_>,
|
|
||||||
code_to_the_left: bool,
|
|
||||||
comments: &mut Vec<Comment>) {
|
|
||||||
debug!(">>> line comments");
|
|
||||||
let p = rdr.pos;
|
|
||||||
let mut lines: Vec<String> = Vec::new();
|
|
||||||
while rdr.ch_is('/') && rdr.nextch_is('/') {
|
|
||||||
let line = rdr.read_one_line_comment();
|
|
||||||
debug!("{}", line);
|
|
||||||
// Doc comments are not put in comments.
|
|
||||||
if is_doc_comment(&line[..]) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
lines.push(line);
|
|
||||||
rdr.consume_non_eol_whitespace();
|
|
||||||
}
|
|
||||||
debug!("<<< line comments");
|
|
||||||
if !lines.is_empty() {
|
|
||||||
comments.push(Comment {
|
|
||||||
style: if code_to_the_left { Trailing } else { Isolated },
|
|
||||||
lines,
|
|
||||||
pos: p,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `None` if the first `col` chars of `s` contain a non-whitespace char.
|
/// Returns `None` if the first `col` chars of `s` contain a non-whitespace char.
|
||||||
/// Otherwise returns `Some(k)` where `k` is first char offset after that leading
|
/// Otherwise returns `Some(k)` where `k` is first char offset after that leading
|
||||||
/// whitespace. Note that `k` may be outside bounds of `s`.
|
/// whitespace. Note that `k` may be outside bounds of `s`.
|
||||||
@ -209,170 +148,103 @@ fn all_whitespace(s: &str, col: CharPos) -> Option<usize> {
|
|||||||
Some(idx)
|
Some(idx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn trim_whitespace_prefix_and_push_line(lines: &mut Vec<String>, s: String, col: CharPos) {
|
fn trim_whitespace_prefix(s: &str, col: CharPos) -> &str {
|
||||||
let len = s.len();
|
let len = s.len();
|
||||||
let s1 = match all_whitespace(&s[..], col) {
|
match all_whitespace(&s, col) {
|
||||||
Some(col) => {
|
Some(col) => if col < len { &s[col..] } else { "" },
|
||||||
if col < len {
|
|
||||||
s[col..len].to_string()
|
|
||||||
} else {
|
|
||||||
String::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => s,
|
None => s,
|
||||||
};
|
}
|
||||||
debug!("pushing line: {}", s1);
|
|
||||||
lines.push(s1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_block_comment(rdr: &mut StringReader<'_>,
|
fn split_block_comment_into_lines(
|
||||||
code_to_the_left: bool,
|
text: &str,
|
||||||
comments: &mut Vec<Comment>) {
|
col: CharPos,
|
||||||
debug!(">>> block comment");
|
) -> Vec<String> {
|
||||||
let p = rdr.pos;
|
let mut res: Vec<String> = vec![];
|
||||||
let mut lines: Vec<String> = Vec::new();
|
let mut lines = text.lines();
|
||||||
|
// just push the first line
|
||||||
// Count the number of chars since the start of the line by rescanning.
|
res.extend(lines.next().map(|it| it.to_string()));
|
||||||
let src_index = rdr.src_index(rdr.source_file.line_begin_pos(rdr.pos));
|
// for other lines, strip common whitespace prefix
|
||||||
let end_src_index = rdr.src_index(rdr.pos);
|
for line in lines {
|
||||||
assert!(src_index <= end_src_index,
|
res.push(trim_whitespace_prefix(line, col).to_string())
|
||||||
"src_index={}, end_src_index={}, line_begin_pos={}",
|
|
||||||
src_index, end_src_index, rdr.source_file.line_begin_pos(rdr.pos).to_u32());
|
|
||||||
|
|
||||||
let col = CharPos(rdr.src[src_index..end_src_index].chars().count());
|
|
||||||
|
|
||||||
rdr.bump();
|
|
||||||
rdr.bump();
|
|
||||||
|
|
||||||
let mut curr_line = String::from("/*");
|
|
||||||
|
|
||||||
// doc-comments are not really comments, they are attributes
|
|
||||||
if (rdr.ch_is('*') && !rdr.nextch_is('*')) || rdr.ch_is('!') {
|
|
||||||
while !(rdr.ch_is('*') && rdr.nextch_is('/')) && !rdr.is_eof() {
|
|
||||||
curr_line.push(rdr.ch.unwrap());
|
|
||||||
rdr.bump();
|
|
||||||
}
|
|
||||||
if !rdr.is_eof() {
|
|
||||||
curr_line.push_str("*/");
|
|
||||||
rdr.bump();
|
|
||||||
rdr.bump();
|
|
||||||
}
|
|
||||||
if is_block_doc_comment(&curr_line[..]) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
assert!(!curr_line.contains('\n'));
|
|
||||||
lines.push(curr_line);
|
|
||||||
} else {
|
|
||||||
let mut level: isize = 1;
|
|
||||||
while level > 0 {
|
|
||||||
debug!("=== block comment level {}", level);
|
|
||||||
if rdr.is_eof() {
|
|
||||||
rdr.fatal_span_(rdr.pos, rdr.pos, "unterminated block comment").raise();
|
|
||||||
}
|
|
||||||
if rdr.ch_is('\n') {
|
|
||||||
trim_whitespace_prefix_and_push_line(&mut lines, curr_line, col);
|
|
||||||
curr_line = String::new();
|
|
||||||
rdr.bump();
|
|
||||||
} else {
|
|
||||||
curr_line.push(rdr.ch.unwrap());
|
|
||||||
if rdr.ch_is('/') && rdr.nextch_is('*') {
|
|
||||||
rdr.bump();
|
|
||||||
rdr.bump();
|
|
||||||
curr_line.push('*');
|
|
||||||
level += 1;
|
|
||||||
} else {
|
|
||||||
if rdr.ch_is('*') && rdr.nextch_is('/') {
|
|
||||||
rdr.bump();
|
|
||||||
rdr.bump();
|
|
||||||
curr_line.push('/');
|
|
||||||
level -= 1;
|
|
||||||
} else {
|
|
||||||
rdr.bump();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !curr_line.is_empty() {
|
|
||||||
trim_whitespace_prefix_and_push_line(&mut lines, curr_line, col);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
res
|
||||||
let mut style = if code_to_the_left {
|
|
||||||
Trailing
|
|
||||||
} else {
|
|
||||||
Isolated
|
|
||||||
};
|
|
||||||
rdr.consume_non_eol_whitespace();
|
|
||||||
if !rdr.is_eof() && !rdr.ch_is('\n') && lines.len() == 1 {
|
|
||||||
style = Mixed;
|
|
||||||
}
|
|
||||||
debug!("<<< block comment");
|
|
||||||
comments.push(Comment {
|
|
||||||
style,
|
|
||||||
lines,
|
|
||||||
pos: p,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fn consume_comment(rdr: &mut StringReader<'_>,
|
|
||||||
comments: &mut Vec<Comment>,
|
|
||||||
code_to_the_left: &mut bool,
|
|
||||||
anything_to_the_left: &mut bool) {
|
|
||||||
debug!(">>> consume comment");
|
|
||||||
if rdr.ch_is('/') && rdr.nextch_is('/') {
|
|
||||||
read_line_comments(rdr, *code_to_the_left, comments);
|
|
||||||
*code_to_the_left = false;
|
|
||||||
*anything_to_the_left = false;
|
|
||||||
} else if rdr.ch_is('/') && rdr.nextch_is('*') {
|
|
||||||
read_block_comment(rdr, *code_to_the_left, comments);
|
|
||||||
*anything_to_the_left = true;
|
|
||||||
} else if rdr.ch_is('#') && rdr.nextch_is('!') {
|
|
||||||
read_shebang_comment(rdr, *code_to_the_left, comments);
|
|
||||||
*code_to_the_left = false;
|
|
||||||
*anything_to_the_left = false;
|
|
||||||
} else {
|
|
||||||
panic!();
|
|
||||||
}
|
|
||||||
debug!("<<< consume comment");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// it appears this function is called only from pprust... that's
|
// it appears this function is called only from pprust... that's
|
||||||
// probably not a good thing.
|
// probably not a good thing.
|
||||||
pub fn gather_comments(sess: &ParseSess, path: FileName, src: String) -> Vec<Comment>
|
pub fn gather_comments(sess: &ParseSess, path: FileName, src: String) -> Vec<Comment> {
|
||||||
{
|
|
||||||
let cm = SourceMap::new(sess.source_map().path_mapping().clone());
|
let cm = SourceMap::new(sess.source_map().path_mapping().clone());
|
||||||
let source_file = cm.new_source_file(path, src);
|
let source_file = cm.new_source_file(path, src);
|
||||||
let mut rdr = lexer::StringReader::new(sess, source_file, None);
|
let text = (*source_file.src.as_ref().unwrap()).clone();
|
||||||
|
|
||||||
|
let text: &str = text.as_str();
|
||||||
|
let start_bpos = source_file.start_pos;
|
||||||
|
let mut pos = 0;
|
||||||
let mut comments: Vec<Comment> = Vec::new();
|
let mut comments: Vec<Comment> = Vec::new();
|
||||||
let mut code_to_the_left = false; // Only code
|
let mut code_to_the_left = false;
|
||||||
let mut anything_to_the_left = false; // Code or comments
|
|
||||||
|
|
||||||
while !rdr.is_eof() {
|
if let Some(shebang_len) = rustc_lexer::strip_shebang(text) {
|
||||||
loop {
|
comments.push(Comment {
|
||||||
// Eat all the whitespace and count blank lines.
|
style: Isolated,
|
||||||
rdr.consume_non_eol_whitespace();
|
lines: vec![text[..shebang_len].to_string()],
|
||||||
if rdr.ch_is('\n') {
|
pos: start_bpos,
|
||||||
if anything_to_the_left {
|
});
|
||||||
rdr.bump(); // The line is not blank, do not count.
|
pos += shebang_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
for token in rustc_lexer::tokenize(&text[pos..]) {
|
||||||
|
let token_text = &text[pos..pos + token.len];
|
||||||
|
match token.kind {
|
||||||
|
rustc_lexer::TokenKind::Whitespace => {
|
||||||
|
if let Some(mut idx) = token_text.find('\n') {
|
||||||
|
code_to_the_left = false;
|
||||||
|
while let Some(next_newline) = &token_text[idx + 1..].find('\n') {
|
||||||
|
idx = idx + 1 + next_newline;
|
||||||
|
comments.push(Comment {
|
||||||
|
style: BlankLine,
|
||||||
|
lines: vec![],
|
||||||
|
pos: start_bpos + BytePos((pos + idx) as u32),
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
consume_whitespace_counting_blank_lines(&mut rdr, &mut comments);
|
|
||||||
code_to_the_left = false;
|
|
||||||
anything_to_the_left = false;
|
|
||||||
}
|
}
|
||||||
// Eat one comment group
|
rustc_lexer::TokenKind::BlockComment { terminated: _ } => {
|
||||||
if rdr.peeking_at_comment() {
|
if !is_block_doc_comment(token_text) {
|
||||||
consume_comment(&mut rdr, &mut comments,
|
let code_to_the_right = match text[pos + token.len..].chars().next() {
|
||||||
&mut code_to_the_left, &mut anything_to_the_left);
|
Some('\r') | Some('\n') => false,
|
||||||
} else {
|
_ => true,
|
||||||
break
|
};
|
||||||
|
let style = match (code_to_the_left, code_to_the_right) {
|
||||||
|
(true, true) | (false, true) => Mixed,
|
||||||
|
(false, false) => Isolated,
|
||||||
|
(true, false) => Trailing,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Count the number of chars since the start of the line by rescanning.
|
||||||
|
let pos_in_file = start_bpos + BytePos(pos as u32);
|
||||||
|
let line_begin_in_file = source_file.line_begin_pos(pos_in_file);
|
||||||
|
let line_begin_pos = (line_begin_in_file - start_bpos).to_usize();
|
||||||
|
let col = CharPos(text[line_begin_pos..pos].chars().count());
|
||||||
|
|
||||||
|
let lines = split_block_comment_into_lines(token_text, col);
|
||||||
|
comments.push(Comment { style, lines, pos: pos_in_file })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rustc_lexer::TokenKind::LineComment => {
|
||||||
|
if !is_doc_comment(token_text) {
|
||||||
|
comments.push(Comment {
|
||||||
|
style: if code_to_the_left { Trailing } else { Isolated },
|
||||||
|
lines: vec![token_text.to_string()],
|
||||||
|
pos: start_bpos + BytePos(pos as u32),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
code_to_the_left = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pos += token.len;
|
||||||
rdr.next_token();
|
|
||||||
code_to_the_left = true;
|
|
||||||
anything_to_the_left = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
comments
|
comments
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
use super::StringReader;
|
use super::StringReader;
|
||||||
use errors::{Applicability, DiagnosticBuilder};
|
use errors::{Applicability, DiagnosticBuilder};
|
||||||
use syntax_pos::{Pos, Span, NO_EXPANSION};
|
use syntax_pos::{BytePos, Pos, Span, NO_EXPANSION};
|
||||||
|
|
||||||
#[rustfmt::skip] // for line breaks
|
#[rustfmt::skip] // for line breaks
|
||||||
const UNICODE_ARRAY: &[(char, &str, char)] = &[
|
const UNICODE_ARRAY: &[(char, &str, char)] = &[
|
||||||
@ -327,6 +327,7 @@ const ASCII_ARRAY: &[(char, &str)] = &[
|
|||||||
|
|
||||||
crate fn check_for_substitution<'a>(
|
crate fn check_for_substitution<'a>(
|
||||||
reader: &StringReader<'a>,
|
reader: &StringReader<'a>,
|
||||||
|
pos: BytePos,
|
||||||
ch: char,
|
ch: char,
|
||||||
err: &mut DiagnosticBuilder<'a>,
|
err: &mut DiagnosticBuilder<'a>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
@ -335,19 +336,19 @@ crate fn check_for_substitution<'a>(
|
|||||||
None => return false,
|
None => return false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let span = Span::new(reader.pos, reader.next_pos, NO_EXPANSION);
|
let span = Span::new(pos, pos + Pos::from_usize(ch.len_utf8()), NO_EXPANSION);
|
||||||
|
|
||||||
let ascii_name = match ASCII_ARRAY.iter().find(|&&(c, _)| c == ascii_char) {
|
let ascii_name = match ASCII_ARRAY.iter().find(|&&(c, _)| c == ascii_char) {
|
||||||
Some((_ascii_char, ascii_name)) => ascii_name,
|
Some((_ascii_char, ascii_name)) => ascii_name,
|
||||||
None => {
|
None => {
|
||||||
let msg = format!("substitution character not found for '{}'", ch);
|
let msg = format!("substitution character not found for '{}'", ch);
|
||||||
reader.sess.span_diagnostic.span_bug_no_panic(span, &msg);
|
reader.sess.span_diagnostic.span_bug_no_panic(span, &msg);
|
||||||
return false
|
return false;
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// special help suggestion for "directed" double quotes
|
// special help suggestion for "directed" double quotes
|
||||||
if let Some(s) = reader.peek_delimited('“', '”') {
|
if let Some(s) = peek_delimited(&reader.src[reader.src_index(pos)..], '“', '”') {
|
||||||
let msg = format!(
|
let msg = format!(
|
||||||
"Unicode characters '“' (Left Double Quotation Mark) and \
|
"Unicode characters '“' (Left Double Quotation Mark) and \
|
||||||
'”' (Right Double Quotation Mark) look like '{}' ({}), but are not",
|
'”' (Right Double Quotation Mark) look like '{}' ({}), but are not",
|
||||||
@ -355,8 +356,8 @@ crate fn check_for_substitution<'a>(
|
|||||||
);
|
);
|
||||||
err.span_suggestion(
|
err.span_suggestion(
|
||||||
Span::new(
|
Span::new(
|
||||||
reader.pos,
|
pos,
|
||||||
reader.next_pos + Pos::from_usize(s.len()) + Pos::from_usize('”'.len_utf8()),
|
pos + Pos::from_usize('“'.len_utf8() + s.len() + '”'.len_utf8()),
|
||||||
NO_EXPANSION,
|
NO_EXPANSION,
|
||||||
),
|
),
|
||||||
&msg,
|
&msg,
|
||||||
@ -368,26 +369,18 @@ crate fn check_for_substitution<'a>(
|
|||||||
"Unicode character '{}' ({}) looks like '{}' ({}), but it is not",
|
"Unicode character '{}' ({}) looks like '{}' ({}), but it is not",
|
||||||
ch, u_name, ascii_char, ascii_name
|
ch, u_name, ascii_char, ascii_name
|
||||||
);
|
);
|
||||||
err.span_suggestion(
|
err.span_suggestion(span, &msg, ascii_char.to_string(), Applicability::MaybeIncorrect);
|
||||||
span,
|
|
||||||
&msg,
|
|
||||||
ascii_char.to_string(),
|
|
||||||
Applicability::MaybeIncorrect,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StringReader<'_> {
|
/// Extract string if found at current position with given delimiters
|
||||||
/// Immutably extract string if found at current position with given delimiters
|
fn peek_delimited(text: &str, from_ch: char, to_ch: char) -> Option<&str> {
|
||||||
fn peek_delimited(&self, from_ch: char, to_ch: char) -> Option<&str> {
|
let mut chars = text.chars();
|
||||||
let tail = &self.src[self.src_index(self.pos)..];
|
let first_char = chars.next()?;
|
||||||
let mut chars = tail.chars();
|
if first_char != from_ch {
|
||||||
let first_char = chars.next()?;
|
return None;
|
||||||
if first_char != from_ch {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let last_char_idx = chars.as_str().find(to_ch)?;
|
|
||||||
Some(&chars.as_str()[..last_char_idx])
|
|
||||||
}
|
}
|
||||||
|
let last_char_idx = chars.as_str().find(to_ch)?;
|
||||||
|
Some(&chars.as_str()[..last_char_idx])
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::ast::{self, Ident};
|
use crate::ast::{self, Ident};
|
||||||
use crate::source_map::FilePathMapping;
|
use crate::source_map::FilePathMapping;
|
||||||
use crate::parse::{ParseSess, PResult, source_file_to_stream};
|
use crate::parse::{ParseSess, PResult, source_file_to_stream};
|
||||||
use crate::parse::{lexer, new_parser_from_source_str};
|
use crate::parse::new_parser_from_source_str;
|
||||||
use crate::parse::parser::Parser;
|
use crate::parse::parser::Parser;
|
||||||
use crate::ptr::P;
|
use crate::ptr::P;
|
||||||
use crate::tokenstream::TokenStream;
|
use crate::tokenstream::TokenStream;
|
||||||
@ -113,14 +113,14 @@ pub fn matches_codepattern(a : &str, b : &str) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Advances the given peekable `Iterator` until it reaches a non-whitespace character
|
/// Advances the given peekable `Iterator` until it reaches a non-whitespace character
|
||||||
fn scan_for_non_ws_or_end<I: Iterator<Item= char>>(iter: &mut Peekable<I>) {
|
fn scan_for_non_ws_or_end<I: Iterator<Item = char>>(iter: &mut Peekable<I>) {
|
||||||
while lexer::is_pattern_whitespace(iter.peek().cloned()) {
|
while iter.peek().copied().map(|c| is_pattern_whitespace(c)) == Some(true) {
|
||||||
iter.next();
|
iter.next();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_pattern_whitespace(c: char) -> bool {
|
pub fn is_pattern_whitespace(c: char) -> bool {
|
||||||
lexer::is_pattern_whitespace(Some(c))
|
rustc_lexer::character_properties::is_whitespace(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
const UNIVERSAL_GRAVITATIONAL_CONSTANT: f64 = 6.674e−11; // m³⋅kg⁻¹⋅s⁻²
|
const UNIVERSAL_GRAVITATIONAL_CONSTANT: f64 = 6.674e−11; // m³⋅kg⁻¹⋅s⁻²
|
||||||
//~^ ERROR expected at least one digit in exponent
|
//~^ ERROR expected at least one digit in exponent
|
||||||
|
//~| ERROR unknown start of token: \u{2212}
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
@ -1,4 +1,10 @@
|
|||||||
error: expected at least one digit in exponent
|
error: expected at least one digit in exponent
|
||||||
|
--> $DIR/issue-49746-unicode-confusable-in-float-literal-expt.rs:1:47
|
||||||
|
|
|
||||||
|
LL | const UNIVERSAL_GRAVITATIONAL_CONSTANT: f64 = 6.674e−11; // m³⋅kg⁻¹⋅s⁻²
|
||||||
|
| ^^^^^^
|
||||||
|
|
||||||
|
error: unknown start of token: \u{2212}
|
||||||
--> $DIR/issue-49746-unicode-confusable-in-float-literal-expt.rs:1:53
|
--> $DIR/issue-49746-unicode-confusable-in-float-literal-expt.rs:1:53
|
||||||
|
|
|
|
||||||
LL | const UNIVERSAL_GRAVITATIONAL_CONSTANT: f64 = 6.674e−11; // m³⋅kg⁻¹⋅s⁻²
|
LL | const UNIVERSAL_GRAVITATIONAL_CONSTANT: f64 = 6.674e−11; // m³⋅kg⁻¹⋅s⁻²
|
||||||
@ -8,5 +14,5 @@ help: Unicode character '−' (Minus Sign) looks like '-' (Minus/Hyphen), but it
|
|||||||
LL | const UNIVERSAL_GRAVITATIONAL_CONSTANT: f64 = 6.674e-11; // m³⋅kg⁻¹⋅s⁻²
|
LL | const UNIVERSAL_GRAVITATIONAL_CONSTANT: f64 = 6.674e-11; // m³⋅kg⁻¹⋅s⁻²
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to 2 previous errors
|
||||||
|
|
||||||
|
@ -53,10 +53,10 @@ LL | 0o;
|
|||||||
| ^^
|
| ^^
|
||||||
|
|
||||||
error: expected at least one digit in exponent
|
error: expected at least one digit in exponent
|
||||||
--> $DIR/lex-bad-numeric-literals.rs:12:8
|
--> $DIR/lex-bad-numeric-literals.rs:12:5
|
||||||
|
|
|
|
||||||
LL | 1e+;
|
LL | 1e+;
|
||||||
| ^
|
| ^^^
|
||||||
|
|
||||||
error: hexadecimal float literal is not supported
|
error: hexadecimal float literal is not supported
|
||||||
--> $DIR/lex-bad-numeric-literals.rs:13:5
|
--> $DIR/lex-bad-numeric-literals.rs:13:5
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
error: unterminated raw string
|
error: unterminated raw string
|
||||||
--> $DIR/raw-byte-string-eof.rs:2:6
|
--> $DIR/raw-byte-string-eof.rs:2:5
|
||||||
|
|
|
|
||||||
LL | br##"a"#;
|
LL | br##"a"#;
|
||||||
| ^ unterminated raw string
|
| ^ unterminated raw string
|
||||||
|
|
|
|
||||||
= note: this raw string should be terminated with `"##`
|
= note: this raw string should be terminated with `"##`
|
||||||
|
|
||||||
|
@ -11,10 +11,10 @@ LL | br"é";
|
|||||||
| ^
|
| ^
|
||||||
|
|
||||||
error: found invalid character; only `#` is allowed in raw string delimitation: ~
|
error: found invalid character; only `#` is allowed in raw string delimitation: ~
|
||||||
--> $DIR/raw-byte-string-literals.rs:6:6
|
--> $DIR/raw-byte-string-literals.rs:6:5
|
||||||
|
|
|
|
||||||
LL | br##~"a"~##;
|
LL | br##~"a"~##;
|
||||||
| ^^^
|
| ^^^^^
|
||||||
|
|
||||||
error: aborting due to 3 previous errors
|
error: aborting due to 3 previous errors
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ error: found invalid character; only `#` is allowed in raw string delimitation:
|
|||||||
--> $DIR/raw-str-delim.rs:2:5
|
--> $DIR/raw-str-delim.rs:2:5
|
||||||
|
|
|
|
||||||
LL | r#~"#"~#
|
LL | r#~"#"~#
|
||||||
| ^^
|
| ^^^
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user