move syntax::parse -> librustc_parse

also move MACRO_ARGUMENTS -> librustc_parse
This commit is contained in:
Mazdak Farrokhzad 2019-10-15 22:48:13 +02:00
parent be023ebe85
commit 4ae2728fa8
67 changed files with 480 additions and 424 deletions

View file

@ -0,0 +1,21 @@
[package]
authors = ["The Rust Project Developers"]
name = "rustc_parse"
version = "0.0.0"
edition = "2018"
[lib]
name = "rustc_parse"
path = "lib.rs"
doctest = false
[dependencies]
bitflags = "1.0"
log = "0.4"
syntax_pos = { path = "../libsyntax_pos" }
syntax = { path = "../libsyntax" }
errors = { path = "../librustc_errors", package = "rustc_errors" }
rustc_data_structures = { path = "../librustc_data_structures" }
rustc_lexer = { path = "../librustc_lexer" }
rustc_target = { path = "../librustc_target" }
smallvec = { version = "1.0", features = ["union", "may_dangle"] }

View file

@ -0,0 +1,174 @@
// Error messages for EXXXX errors.
// Each message should start and end with a new line, and be wrapped to 80
// characters. In vim you can `:set tw=80` and use `gq` to wrap paragraphs. Use
// `:set tw=0` to disable.
syntax::register_diagnostics! {
E0178: r##"
In types, the `+` type operator has low precedence, so it is often necessary
to use parentheses.
For example:
```compile_fail,E0178
trait Foo {}
struct Bar<'a> {
w: &'a Foo + Copy, // error, use &'a (Foo + Copy)
x: &'a Foo + 'a, // error, use &'a (Foo + 'a)
y: &'a mut Foo + 'a, // error, use &'a mut (Foo + 'a)
z: fn() -> Foo + 'a, // error, use fn() -> (Foo + 'a)
}
```
More details can be found in [RFC 438].
[RFC 438]: https://github.com/rust-lang/rfcs/pull/438
"##,
E0583: r##"
A file wasn't found for an out-of-line module.
Erroneous code example:
```ignore (compile_fail not working here; see Issue #43707)
mod file_that_doesnt_exist; // error: file not found for module
fn main() {}
```
Please be sure that a file corresponding to the module exists. If you
want to use a module named `file_that_doesnt_exist`, you need to have a file
named `file_that_doesnt_exist.rs` or `file_that_doesnt_exist/mod.rs` in the
same directory.
"##,
E0584: r##"
A doc comment that is not attached to anything has been encountered.
Erroneous code example:
```compile_fail,E0584
trait Island {
fn lost();
/// I'm lost!
}
```
A little reminder: a doc comment has to be placed before the item it's supposed
to document. So if you want to document the `Island` trait, you need to put a
doc comment before it, not inside it. Same goes for the `lost` method: the doc
comment needs to be before it:
```
/// I'm THE island!
trait Island {
/// I'm lost!
fn lost();
}
```
"##,
E0585: r##"
A documentation comment that doesn't document anything was found.
Erroneous code example:
```compile_fail,E0585
fn main() {
// The following doc comment will fail:
/// This is a useless doc comment!
}
```
Documentation comments need to be followed by items, including functions,
types, modules, etc. Examples:
```
/// I'm documenting the following struct:
struct Foo;
/// I'm documenting the following function:
fn foo() {}
```
"##,
E0586: r##"
An inclusive range was used with no end.
Erroneous code example:
```compile_fail,E0586
fn main() {
let tmp = vec![0, 1, 2, 3, 4, 4, 3, 3, 2, 1];
let x = &tmp[1..=]; // error: inclusive range was used with no end
}
```
An inclusive range needs an end in order to *include* it. If you just need a
start and no end, use a non-inclusive range (with `..`):
```
fn main() {
let tmp = vec![0, 1, 2, 3, 4, 4, 3, 3, 2, 1];
let x = &tmp[1..]; // ok!
}
```
Or put an end to your inclusive range:
```
fn main() {
let tmp = vec![0, 1, 2, 3, 4, 4, 3, 3, 2, 1];
let x = &tmp[1..=3]; // ok!
}
```
"##,
E0704: r##"
This error indicates that a incorrect visibility restriction was specified.
Example of erroneous code:
```compile_fail,E0704
mod foo {
pub(foo) struct Bar {
x: i32
}
}
```
To make struct `Bar` only visible in module `foo` the `in` keyword should be
used:
```
mod foo {
pub(in crate::foo) struct Bar {
x: i32
}
}
# fn main() {}
```
For more information see the Rust Reference on [Visibility].
[Visibility]: https://doc.rust-lang.org/reference/visibility-and-privacy.html
"##,
E0743: r##"
C-variadic has been used on a non-foreign function.
Erroneous code example:
```compile_fail,E0743
fn foo2(x: u8, ...) {} // error!
```
Only foreign functions can use C-variadic (`...`). It is used to give an
undefined number of parameters to a given function (like `printf` in C). The
equivalent in Rust would be to use macros directly.
"##,
;
}

View file

@ -0,0 +1,643 @@
use syntax::token::{self, Token, TokenKind};
use syntax::sess::ParseSess;
use syntax::symbol::{sym, Symbol};
use syntax::util::comments;
use errors::{FatalError, DiagnosticBuilder};
use syntax_pos::{BytePos, Pos, Span};
use rustc_lexer::Base;
use rustc_lexer::unescape;
use std::char;
use std::convert::TryInto;
use rustc_data_structures::sync::Lrc;
use log::debug;
mod tokentrees;
mod unicode_chars;
mod unescape_error_reporting;
use unescape_error_reporting::{emit_unescape_error, push_escaped_char};
#[derive(Clone, Debug)]
pub struct UnmatchedBrace {
pub expected_delim: token::DelimToken,
pub found_delim: Option<token::DelimToken>,
pub found_span: Span,
pub unclosed_span: Option<Span>,
pub candidate_span: Option<Span>,
}
pub struct StringReader<'a> {
sess: &'a ParseSess,
/// Initial position, read-only.
start_pos: BytePos,
/// The absolute offset within the source_map of the current character.
// FIXME(#64197): `pub` is needed by tests for now.
pub pos: BytePos,
/// Stop reading src at this index.
end_src_index: usize,
/// Source text to tokenize.
src: Lrc<String>,
override_span: Option<Span>,
}
impl<'a> StringReader<'a> {
pub fn new(sess: &'a ParseSess,
source_file: Lrc<syntax_pos::SourceFile>,
override_span: Option<Span>) -> Self {
if source_file.src.is_none() {
sess.span_diagnostic.bug(&format!("cannot lex `source_file` without source: {}",
source_file.name));
}
let src = (*source_file.src.as_ref().unwrap()).clone();
StringReader {
sess,
start_pos: source_file.start_pos,
pos: source_file.start_pos,
end_src_index: src.len(),
src,
override_span,
}
}
pub fn retokenize(sess: &'a ParseSess, mut span: Span) -> Self {
let begin = sess.source_map().lookup_byte_offset(span.lo());
let end = sess.source_map().lookup_byte_offset(span.hi());
// Make the range zero-length if the span is invalid.
if begin.sf.start_pos != end.sf.start_pos {
span = span.shrink_to_lo();
}
let mut sr = StringReader::new(sess, begin.sf, None);
// Seek the lexer to the right byte range.
sr.end_src_index = sr.src_index(span.hi());
sr
}
fn mk_sp(&self, lo: BytePos, hi: BytePos) -> Span {
self.override_span.unwrap_or_else(|| Span::with_root_ctxt(lo, hi))
}
/// Returns the next token, including trivia like whitespace or comments.
///
/// `Err(())` means that some errors were encountered, which can be
/// retrieved using `buffer_fatal_errors`.
pub fn next_token(&mut self) -> Token {
let start_src_index = self.src_index(self.pos);
let text: &str = &self.src[start_src_index..self.end_src_index];
if text.is_empty() {
let span = self.mk_sp(self.pos, self.pos);
return Token::new(token::Eof, span);
}
{
let is_beginning_of_file = self.pos == self.start_pos;
if is_beginning_of_file {
if let Some(shebang_len) = rustc_lexer::strip_shebang(text) {
let start = self.pos;
self.pos = self.pos + BytePos::from_usize(shebang_len);
let sym = self.symbol_from(start + BytePos::from_usize("#!".len()));
let kind = token::Shebang(sym);
let span = self.mk_sp(start, self.pos);
return Token::new(kind, span);
}
}
}
let token = rustc_lexer::first_token(text);
let start = self.pos;
self.pos = self.pos + BytePos::from_usize(token.len);
debug!("try_next_token: {:?}({:?})", token.kind, self.str_from(start));
// This could use `?`, but that makes code significantly (10-20%) slower.
// https://github.com/rust-lang/rust/issues/37939
let kind = self.cook_lexer_token(token.kind, start);
let span = self.mk_sp(start, self.pos);
Token::new(kind, span)
}
/// Report a fatal lexical error with a given span.
fn fatal_span(&self, sp: Span, m: &str) -> FatalError {
self.sess.span_diagnostic.span_fatal(sp, m)
}
/// Report a lexical error with a given span.
fn err_span(&self, sp: Span, m: &str) {
self.sess.span_diagnostic.struct_span_err(sp, m).emit();
}
/// Report a fatal error spanning [`from_pos`, `to_pos`).
fn fatal_span_(&self, from_pos: BytePos, to_pos: BytePos, m: &str) -> FatalError {
self.fatal_span(self.mk_sp(from_pos, to_pos), m)
}
/// Report a lexical error spanning [`from_pos`, `to_pos`).
fn err_span_(&self, from_pos: BytePos, to_pos: BytePos, m: &str) {
self.err_span(self.mk_sp(from_pos, to_pos), m)
}
fn struct_span_fatal(&self, from_pos: BytePos, to_pos: BytePos, m: &str)
-> DiagnosticBuilder<'a>
{
self.sess.span_diagnostic.struct_span_fatal(self.mk_sp(from_pos, to_pos), m)
}
fn struct_fatal_span_char(&self, from_pos: BytePos, to_pos: BytePos, m: &str, c: char)
-> DiagnosticBuilder<'a>
{
let mut m = m.to_string();
m.push_str(": ");
push_escaped_char(&mut m, c);
self.sess.span_diagnostic.struct_span_fatal(self.mk_sp(from_pos, to_pos), &m[..])
}
/// Turns simple `rustc_lexer::TokenKind` enum into a rich
/// `libsyntax::TokenKind`. This turns strings into interned
/// symbols and runs additional validation.
fn cook_lexer_token(
&self,
token: rustc_lexer::TokenKind,
start: BytePos,
) -> TokenKind {
match token {
rustc_lexer::TokenKind::LineComment => {
let string = self.str_from(start);
// comments with only more "/"s are not doc comments
let tok = if comments::is_line_doc_comment(string) {
self.forbid_bare_cr(start, string, "bare CR not allowed in doc-comment");
token::DocComment(Symbol::intern(string))
} else {
token::Comment
};
tok
}
rustc_lexer::TokenKind::BlockComment { terminated } => {
let string = self.str_from(start);
// block comments starting with "/**" or "/*!" are doc-comments
// but comments with only "*"s between two "/"s are not
let is_doc_comment = comments::is_block_doc_comment(string);
if !terminated {
let msg = if is_doc_comment {
"unterminated block doc-comment"
} else {
"unterminated block comment"
};
let last_bpos = self.pos;
self.fatal_span_(start, last_bpos, msg).raise();
}
let tok = if is_doc_comment {
self.forbid_bare_cr(start,
string,
"bare CR not allowed in block doc-comment");
token::DocComment(Symbol::intern(string))
} else {
token::Comment
};
tok
}
rustc_lexer::TokenKind::Whitespace => token::Whitespace,
rustc_lexer::TokenKind::Ident | rustc_lexer::TokenKind::RawIdent => {
let is_raw_ident = token == rustc_lexer::TokenKind::RawIdent;
let mut ident_start = start;
if is_raw_ident {
ident_start = ident_start + BytePos(2);
}
// FIXME: perform NFKC normalization here. (Issue #2253)
let sym = self.symbol_from(ident_start);
if is_raw_ident {
let span = self.mk_sp(start, self.pos);
if !sym.can_be_raw() {
self.err_span(span, &format!("`{}` cannot be a raw identifier", sym));
}
self.sess.raw_identifier_spans.borrow_mut().push(span);
}
token::Ident(sym, is_raw_ident)
}
rustc_lexer::TokenKind::Literal { kind, suffix_start } => {
let suffix_start = start + BytePos(suffix_start as u32);
let (kind, symbol) = self.cook_lexer_literal(start, suffix_start, kind);
let suffix = if suffix_start < self.pos {
let string = self.str_from(suffix_start);
if string == "_" {
self.sess.span_diagnostic
.struct_span_warn(self.mk_sp(suffix_start, self.pos),
"underscore literal suffix is not allowed")
.warn("this was previously accepted by the compiler but is \
being phased out; it will become a hard error in \
a future release!")
.note("for more information, see issue #42326 \
<https://github.com/rust-lang/rust/issues/42326>")
.emit();
None
} else {
Some(Symbol::intern(string))
}
} else {
None
};
token::Literal(token::Lit { kind, symbol, suffix })
}
rustc_lexer::TokenKind::Lifetime { starts_with_number } => {
// Include the leading `'` in the real identifier, for macro
// expansion purposes. See #12512 for the gory details of why
// this is necessary.
let lifetime_name = self.str_from(start);
if starts_with_number {
self.err_span_(
start,
self.pos,
"lifetimes cannot start with a number",
);
}
let ident = Symbol::intern(lifetime_name);
token::Lifetime(ident)
}
rustc_lexer::TokenKind::Semi => token::Semi,
rustc_lexer::TokenKind::Comma => token::Comma,
rustc_lexer::TokenKind::Dot => token::Dot,
rustc_lexer::TokenKind::OpenParen => token::OpenDelim(token::Paren),
rustc_lexer::TokenKind::CloseParen => token::CloseDelim(token::Paren),
rustc_lexer::TokenKind::OpenBrace => token::OpenDelim(token::Brace),
rustc_lexer::TokenKind::CloseBrace => token::CloseDelim(token::Brace),
rustc_lexer::TokenKind::OpenBracket => token::OpenDelim(token::Bracket),
rustc_lexer::TokenKind::CloseBracket => token::CloseDelim(token::Bracket),
rustc_lexer::TokenKind::At => token::At,
rustc_lexer::TokenKind::Pound => token::Pound,
rustc_lexer::TokenKind::Tilde => token::Tilde,
rustc_lexer::TokenKind::Question => token::Question,
rustc_lexer::TokenKind::Colon => token::Colon,
rustc_lexer::TokenKind::Dollar => token::Dollar,
rustc_lexer::TokenKind::Eq => token::Eq,
rustc_lexer::TokenKind::Not => token::Not,
rustc_lexer::TokenKind::Lt => token::Lt,
rustc_lexer::TokenKind::Gt => token::Gt,
rustc_lexer::TokenKind::Minus => token::BinOp(token::Minus),
rustc_lexer::TokenKind::And => token::BinOp(token::And),
rustc_lexer::TokenKind::Or => token::BinOp(token::Or),
rustc_lexer::TokenKind::Plus => token::BinOp(token::Plus),
rustc_lexer::TokenKind::Star => token::BinOp(token::Star),
rustc_lexer::TokenKind::Slash => token::BinOp(token::Slash),
rustc_lexer::TokenKind::Caret => token::BinOp(token::Caret),
rustc_lexer::TokenKind::Percent => token::BinOp(token::Percent),
rustc_lexer::TokenKind::Unknown => {
let c = self.str_from(start).chars().next().unwrap();
let mut err = self.struct_fatal_span_char(start,
self.pos,
"unknown start of token",
c);
// FIXME: the lexer could be used to turn the ASCII version of unicode homoglyphs,
// instead of keeping a table in `check_for_substitution`into the token. Ideally,
// this should be inside `rustc_lexer`. However, we should first remove compound
// tokens like `<<` from `rustc_lexer`, and then add fancier error recovery to it,
// as there will be less overall work to do this way.
let token = unicode_chars::check_for_substitution(self, start, c, &mut err)
.unwrap_or_else(|| token::Unknown(self.symbol_from(start)));
err.emit();
token
}
}
}
fn cook_lexer_literal(
&self,
start: BytePos,
suffix_start: BytePos,
kind: rustc_lexer::LiteralKind
) -> (token::LitKind, Symbol) {
match kind {
rustc_lexer::LiteralKind::Char { terminated } => {
if !terminated {
self.fatal_span_(start, suffix_start,
"unterminated character literal".into())
.raise()
}
let content_start = start + BytePos(1);
let content_end = suffix_start - BytePos(1);
self.validate_char_escape(content_start, content_end);
let id = self.symbol_from_to(content_start, content_end);
(token::Char, id)
},
rustc_lexer::LiteralKind::Byte { terminated } => {
if !terminated {
self.fatal_span_(start + BytePos(1), suffix_start,
"unterminated byte constant".into())
.raise()
}
let content_start = start + BytePos(2);
let content_end = suffix_start - BytePos(1);
self.validate_byte_escape(content_start, content_end);
let id = self.symbol_from_to(content_start, content_end);
(token::Byte, id)
},
rustc_lexer::LiteralKind::Str { terminated } => {
if !terminated {
self.fatal_span_(start, suffix_start,
"unterminated double quote string".into())
.raise()
}
let content_start = start + BytePos(1);
let content_end = suffix_start - BytePos(1);
self.validate_str_escape(content_start, content_end);
let id = self.symbol_from_to(content_start, content_end);
(token::Str, id)
}
rustc_lexer::LiteralKind::ByteStr { terminated } => {
if !terminated {
self.fatal_span_(start + BytePos(1), suffix_start,
"unterminated double quote byte string".into())
.raise()
}
let content_start = start + BytePos(2);
let content_end = suffix_start - BytePos(1);
self.validate_byte_str_escape(content_start, content_end);
let id = self.symbol_from_to(content_start, content_end);
(token::ByteStr, id)
}
rustc_lexer::LiteralKind::RawStr { n_hashes, started, terminated } => {
if !started {
self.report_non_started_raw_string(start);
}
if !terminated {
self.report_unterminated_raw_string(start, n_hashes)
}
let n_hashes: u16 = self.restrict_n_hashes(start, n_hashes);
let n = u32::from(n_hashes);
let content_start = start + BytePos(2 + n);
let content_end = suffix_start - BytePos(1 + n);
self.validate_raw_str_escape(content_start, content_end);
let id = self.symbol_from_to(content_start, content_end);
(token::StrRaw(n_hashes), id)
}
rustc_lexer::LiteralKind::RawByteStr { n_hashes, started, terminated } => {
if !started {
self.report_non_started_raw_string(start);
}
if !terminated {
self.report_unterminated_raw_string(start, n_hashes)
}
let n_hashes: u16 = self.restrict_n_hashes(start, n_hashes);
let n = u32::from(n_hashes);
let content_start = start + BytePos(3 + n);
let content_end = suffix_start - BytePos(1 + n);
self.validate_raw_byte_str_escape(content_start, content_end);
let id = self.symbol_from_to(content_start, content_end);
(token::ByteStrRaw(n_hashes), id)
}
rustc_lexer::LiteralKind::Int { base, empty_int } => {
if empty_int {
self.err_span_(start, suffix_start, "no valid digits found for number");
(token::Integer, sym::integer(0))
} else {
self.validate_int_literal(base, start, suffix_start);
(token::Integer, self.symbol_from_to(start, suffix_start))
}
},
rustc_lexer::LiteralKind::Float { base, empty_exponent } => {
if empty_exponent {
let mut err = self.struct_span_fatal(
start, self.pos,
"expected at least one digit in exponent"
);
err.emit();
}
match base {
Base::Hexadecimal => {
self.err_span_(start, suffix_start,
"hexadecimal float literal is not supported")
}
Base::Octal => {
self.err_span_(start, suffix_start,
"octal float literal is not supported")
}
Base::Binary => {
self.err_span_(start, suffix_start,
"binary float literal is not supported")
}
_ => ()
}
let id = self.symbol_from_to(start, suffix_start);
(token::Float, id)
},
}
}
#[inline]
fn src_index(&self, pos: BytePos) -> usize {
(pos - self.start_pos).to_usize()
}
/// Slice of the source text from `start` up to but excluding `self.pos`,
/// meaning the slice does not include the character `self.ch`.
fn str_from(&self, start: BytePos) -> &str
{
self.str_from_to(start, self.pos)
}
/// Creates a Symbol from a given offset to the current offset.
fn symbol_from(&self, start: BytePos) -> Symbol {
debug!("taking an ident from {:?} to {:?}", start, self.pos);
Symbol::intern(self.str_from(start))
}
/// As symbol_from, with an explicit endpoint.
fn symbol_from_to(&self, start: BytePos, end: BytePos) -> Symbol {
debug!("taking an ident from {:?} to {:?}", start, end);
Symbol::intern(self.str_from_to(start, end))
}
/// Slice of the source text spanning from `start` up to but excluding `end`.
fn str_from_to(&self, start: BytePos, end: BytePos) -> &str
{
&self.src[self.src_index(start)..self.src_index(end)]
}
fn forbid_bare_cr(&self, start: BytePos, s: &str, errmsg: &str) {
let mut idx = 0;
loop {
idx = match s[idx..].find('\r') {
None => break,
Some(it) => idx + it + 1
};
self.err_span_(start + BytePos(idx as u32 - 1),
start + BytePos(idx as u32),
errmsg);
}
}
fn report_non_started_raw_string(&self, start: BytePos) -> ! {
let bad_char = self.str_from(start).chars().last().unwrap();
self
.struct_fatal_span_char(
start,
self.pos,
"found invalid character; only `#` is allowed \
in raw string delimitation",
bad_char,
)
.emit();
FatalError.raise()
}
fn report_unterminated_raw_string(&self, start: BytePos, n_hashes: usize) -> ! {
let mut err = self.struct_span_fatal(
start, start,
"unterminated raw string",
);
err.span_label(
self.mk_sp(start, start),
"unterminated raw string",
);
if n_hashes > 0 {
err.note(&format!("this raw string should be terminated with `\"{}`",
"#".repeat(n_hashes as usize)));
}
err.emit();
FatalError.raise()
}
fn restrict_n_hashes(&self, start: BytePos, n_hashes: usize) -> u16 {
match n_hashes.try_into() {
Ok(n_hashes) => n_hashes,
Err(_) => {
self.fatal_span_(start,
self.pos,
"too many `#` symbols: raw strings may be \
delimited by up to 65535 `#` symbols").raise();
}
}
}
fn validate_char_escape(&self, content_start: BytePos, content_end: BytePos) {
let lit = self.str_from_to(content_start, content_end);
if let Err((off, err)) = unescape::unescape_char(lit) {
emit_unescape_error(
&self.sess.span_diagnostic,
lit,
self.mk_sp(content_start - BytePos(1), content_end + BytePos(1)),
unescape::Mode::Char,
0..off,
err,
)
}
}
fn validate_byte_escape(&self, content_start: BytePos, content_end: BytePos) {
let lit = self.str_from_to(content_start, content_end);
if let Err((off, err)) = unescape::unescape_byte(lit) {
emit_unescape_error(
&self.sess.span_diagnostic,
lit,
self.mk_sp(content_start - BytePos(1), content_end + BytePos(1)),
unescape::Mode::Byte,
0..off,
err,
)
}
}
fn validate_str_escape(&self, content_start: BytePos, content_end: BytePos) {
let lit = self.str_from_to(content_start, content_end);
unescape::unescape_str(lit, &mut |range, c| {
if let Err(err) = c {
emit_unescape_error(
&self.sess.span_diagnostic,
lit,
self.mk_sp(content_start - BytePos(1), content_end + BytePos(1)),
unescape::Mode::Str,
range,
err,
)
}
})
}
fn validate_raw_str_escape(&self, content_start: BytePos, content_end: BytePos) {
let lit = self.str_from_to(content_start, content_end);
unescape::unescape_raw_str(lit, &mut |range, c| {
if let Err(err) = c {
emit_unescape_error(
&self.sess.span_diagnostic,
lit,
self.mk_sp(content_start - BytePos(1), content_end + BytePos(1)),
unescape::Mode::Str,
range,
err,
)
}
})
}
fn validate_raw_byte_str_escape(&self, content_start: BytePos, content_end: BytePos) {
let lit = self.str_from_to(content_start, content_end);
unescape::unescape_raw_byte_str(lit, &mut |range, c| {
if let Err(err) = c {
emit_unescape_error(
&self.sess.span_diagnostic,
lit,
self.mk_sp(content_start - BytePos(1), content_end + BytePos(1)),
unescape::Mode::ByteStr,
range,
err,
)
}
})
}
fn validate_byte_str_escape(&self, content_start: BytePos, content_end: BytePos) {
let lit = self.str_from_to(content_start, content_end);
unescape::unescape_byte_str(lit, &mut |range, c| {
if let Err(err) = c {
emit_unescape_error(
&self.sess.span_diagnostic,
lit,
self.mk_sp(content_start - BytePos(1), content_end + BytePos(1)),
unescape::Mode::ByteStr,
range,
err,
)
}
})
}
fn validate_int_literal(&self, base: Base, content_start: BytePos, content_end: BytePos) {
let base = match base {
Base::Binary => 2,
Base::Octal => 8,
_ => return,
};
let s = self.str_from_to(content_start + BytePos(2), content_end);
for (idx, c) in s.char_indices() {
let idx = idx as u32;
if c != '_' && c.to_digit(base).is_none() {
let lo = content_start + BytePos(2 + idx);
let hi = content_start + BytePos(2 + idx + c.len_utf8() as u32);
self.err_span_(lo, hi,
&format!("invalid digit for a base {} literal", base));
}
}
}
}

View file

@ -0,0 +1,280 @@
use rustc_data_structures::fx::FxHashMap;
use syntax_pos::Span;
use super::{StringReader, UnmatchedBrace};
use syntax::print::pprust::token_to_string;
use syntax::token::{self, Token};
use syntax::tokenstream::{DelimSpan, IsJoint::{self, *}, TokenStream, TokenTree, TreeAndJoint};
use errors::PResult;
impl<'a> StringReader<'a> {
crate fn into_token_trees(self) -> (PResult<'a, TokenStream>, Vec<UnmatchedBrace>) {
let mut tt_reader = TokenTreesReader {
string_reader: self,
token: Token::dummy(),
joint_to_prev: Joint,
open_braces: Vec::new(),
unmatched_braces: Vec::new(),
matching_delim_spans: Vec::new(),
last_unclosed_found_span: None,
last_delim_empty_block_spans: FxHashMap::default()
};
let res = tt_reader.parse_all_token_trees();
(res, tt_reader.unmatched_braces)
}
}
struct TokenTreesReader<'a> {
string_reader: StringReader<'a>,
token: Token,
joint_to_prev: IsJoint,
/// Stack of open delimiters and their spans. Used for error message.
open_braces: Vec<(token::DelimToken, Span)>,
unmatched_braces: Vec<UnmatchedBrace>,
/// The type and spans for all braces
///
/// Used only for error recovery when arriving to EOF with mismatched braces.
matching_delim_spans: Vec<(token::DelimToken, Span, Span)>,
last_unclosed_found_span: Option<Span>,
last_delim_empty_block_spans: FxHashMap<token::DelimToken, Span>
}
impl<'a> TokenTreesReader<'a> {
// Parse a stream of tokens into a list of `TokenTree`s, up to an `Eof`.
fn parse_all_token_trees(&mut self) -> PResult<'a, TokenStream> {
let mut buf = TokenStreamBuilder::default();
self.real_token();
while self.token != token::Eof {
buf.push(self.parse_token_tree()?);
}
Ok(buf.into_token_stream())
}
// Parse a stream of tokens into a list of `TokenTree`s, up to a `CloseDelim`.
fn parse_token_trees_until_close_delim(&mut self) -> TokenStream {
let mut buf = TokenStreamBuilder::default();
loop {
if let token::CloseDelim(..) = self.token.kind {
return buf.into_token_stream();
}
match self.parse_token_tree() {
Ok(tree) => buf.push(tree),
Err(mut e) => {
e.emit();
return buf.into_token_stream();
}
}
}
}
fn parse_token_tree(&mut self) -> PResult<'a, TreeAndJoint> {
let sm = self.string_reader.sess.source_map();
match self.token.kind {
token::Eof => {
let msg = "this file contains an un-closed delimiter";
let mut err = self.string_reader.sess.span_diagnostic
.struct_span_err(self.token.span, msg);
for &(_, sp) in &self.open_braces {
err.span_label(sp, "un-closed delimiter");
self.unmatched_braces.push(UnmatchedBrace {
expected_delim: token::DelimToken::Brace,
found_delim: None,
found_span: self.token.span,
unclosed_span: Some(sp),
candidate_span: None,
});
}
if let Some((delim, _)) = self.open_braces.last() {
if let Some((_, open_sp, close_sp)) = self.matching_delim_spans.iter()
.filter(|(d, open_sp, close_sp)| {
if let Some(close_padding) = sm.span_to_margin(*close_sp) {
if let Some(open_padding) = sm.span_to_margin(*open_sp) {
return delim == d && close_padding != open_padding;
}
}
false
}).next() // these are in reverse order as they get inserted on close, but
{ // we want the last open/first close
err.span_label(
*open_sp,
"this delimiter might not be properly closed...",
);
err.span_label(
*close_sp,
"...as it matches this but it has different indentation",
);
}
}
Err(err)
},
token::OpenDelim(delim) => {
// The span for beginning of the delimited section
let pre_span = self.token.span;
// Parse the open delimiter.
self.open_braces.push((delim, self.token.span));
self.real_token();
// Parse the token trees within the delimiters.
// We stop at any delimiter so we can try to recover if the user
// uses an incorrect delimiter.
let tts = self.parse_token_trees_until_close_delim();
// Expand to cover the entire delimited token tree
let delim_span = DelimSpan::from_pair(pre_span, self.token.span);
match self.token.kind {
// Correct delimiter.
token::CloseDelim(d) if d == delim => {
let (open_brace, open_brace_span) = self.open_braces.pop().unwrap();
let close_brace_span = self.token.span;
if tts.is_empty() {
let empty_block_span = open_brace_span.to(close_brace_span);
self.last_delim_empty_block_spans.insert(delim, empty_block_span);
}
if self.open_braces.len() == 0 {
// Clear up these spans to avoid suggesting them as we've found
// properly matched delimiters so far for an entire block.
self.matching_delim_spans.clear();
} else {
self.matching_delim_spans.push(
(open_brace, open_brace_span, close_brace_span),
);
}
// Parse the close delimiter.
self.real_token();
}
// Incorrect delimiter.
token::CloseDelim(other) => {
let mut unclosed_delimiter = None;
let mut candidate = None;
if self.last_unclosed_found_span != Some(self.token.span) {
// do not complain about the same unclosed delimiter multiple times
self.last_unclosed_found_span = Some(self.token.span);
// This is a conservative error: only report the last unclosed
// delimiter. The previous unclosed delimiters could actually be
// closed! The parser just hasn't gotten to them yet.
if let Some(&(_, sp)) = self.open_braces.last() {
unclosed_delimiter = Some(sp);
};
if let Some(current_padding) = sm.span_to_margin(self.token.span) {
for (brace, brace_span) in &self.open_braces {
if let Some(padding) = sm.span_to_margin(*brace_span) {
// high likelihood of these two corresponding
if current_padding == padding && brace == &other {
candidate = Some(*brace_span);
}
}
}
}
let (tok, _) = self.open_braces.pop().unwrap();
self.unmatched_braces.push(UnmatchedBrace {
expected_delim: tok,
found_delim: Some(other),
found_span: self.token.span,
unclosed_span: unclosed_delimiter,
candidate_span: candidate,
});
} else {
self.open_braces.pop();
}
// If the incorrect delimiter matches an earlier opening
// delimiter, then don't consume it (it can be used to
// close the earlier one). Otherwise, consume it.
// E.g., we try to recover from:
// fn foo() {
// bar(baz(
// } // Incorrect delimiter but matches the earlier `{`
if !self.open_braces.iter().any(|&(b, _)| b == other) {
self.real_token();
}
}
token::Eof => {
// Silently recover, the EOF token will be seen again
// and an error emitted then. Thus we don't pop from
// self.open_braces here.
},
_ => {}
}
Ok(TokenTree::Delimited(
delim_span,
delim,
tts.into()
).into())
},
token::CloseDelim(delim) => {
// An unexpected closing delimiter (i.e., there is no
// matching opening delimiter).
let token_str = token_to_string(&self.token);
let msg = format!("unexpected close delimiter: `{}`", token_str);
let mut err = self.string_reader.sess.span_diagnostic
.struct_span_err(self.token.span, &msg);
if let Some(span) = self.last_delim_empty_block_spans.remove(&delim) {
err.span_label(
span,
"this block is empty, you might have not meant to close it"
);
}
err.span_label(self.token.span, "unexpected close delimiter");
Err(err)
},
_ => {
let tt = TokenTree::Token(self.token.take());
self.real_token();
let is_joint = self.joint_to_prev == Joint && self.token.is_op();
Ok((tt, if is_joint { Joint } else { NonJoint }))
}
}
}
fn real_token(&mut self) {
self.joint_to_prev = Joint;
loop {
let token = self.string_reader.next_token();
match token.kind {
token::Whitespace | token::Comment | token::Shebang(_) | token::Unknown(_) => {
self.joint_to_prev = NonJoint;
}
_ => {
self.token = token;
return;
}
}
}
}
}
#[derive(Default)]
struct TokenStreamBuilder {
buf: Vec<TreeAndJoint>,
}
impl TokenStreamBuilder {
fn push(&mut self, (tree, joint): TreeAndJoint) {
if let Some((TokenTree::Token(prev_token), Joint)) = self.buf.last() {
if let TokenTree::Token(token) = &tree {
if let Some(glued) = prev_token.glue(token) {
self.buf.pop();
self.buf.push((TokenTree::Token(glued), joint));
return;
}
}
}
self.buf.push((tree, joint))
}
fn into_token_stream(self) -> TokenStream {
TokenStream::new(self.buf)
}
}

View file

@ -0,0 +1,215 @@
//! Utilities for rendering escape sequence errors as diagnostics.
use std::ops::Range;
use std::iter::once;
use rustc_lexer::unescape::{EscapeError, Mode};
use syntax_pos::{Span, BytePos};
use syntax::errors::{Handler, Applicability};
pub(crate) fn emit_unescape_error(
handler: &Handler,
// interior part of the literal, without quotes
lit: &str,
// full span of the literal, including quotes
span_with_quotes: Span,
mode: Mode,
// range of the error inside `lit`
range: Range<usize>,
error: EscapeError,
) {
log::debug!("emit_unescape_error: {:?}, {:?}, {:?}, {:?}, {:?}",
lit, span_with_quotes, mode, range, error);
let span = {
let Range { start, end } = range;
let (start, end) = (start as u32, end as u32);
let lo = span_with_quotes.lo() + BytePos(start + 1);
let hi = lo + BytePos(end - start);
span_with_quotes
.with_lo(lo)
.with_hi(hi)
};
let last_char = || {
let c = lit[range.clone()].chars().rev().next().unwrap();
let span = span.with_lo(span.hi() - BytePos(c.len_utf8() as u32));
(c, span)
};
match error {
EscapeError::LoneSurrogateUnicodeEscape => {
handler.struct_span_err(span, "invalid unicode character escape")
.help("unicode escape must not be a surrogate")
.emit();
}
EscapeError::OutOfRangeUnicodeEscape => {
handler.struct_span_err(span, "invalid unicode character escape")
.help("unicode escape must be at most 10FFFF")
.emit();
}
EscapeError::MoreThanOneChar => {
let msg = if mode.is_bytes() {
"if you meant to write a byte string literal, use double quotes"
} else {
"if you meant to write a `str` literal, use double quotes"
};
handler
.struct_span_err(
span_with_quotes,
"character literal may only contain one codepoint",
)
.span_suggestion(
span_with_quotes,
msg,
format!("\"{}\"", lit),
Applicability::MachineApplicable,
).emit()
}
EscapeError::EscapeOnlyChar => {
let (c, _span) = last_char();
let mut msg = if mode.is_bytes() {
"byte constant must be escaped: "
} else {
"character constant must be escaped: "
}.to_string();
push_escaped_char(&mut msg, c);
handler.span_err(span, msg.as_str())
}
EscapeError::BareCarriageReturn => {
let msg = if mode.in_double_quotes() {
"bare CR not allowed in string, use \\r instead"
} else {
"character constant must be escaped: \\r"
};
handler.span_err(span, msg);
}
EscapeError::BareCarriageReturnInRawString => {
assert!(mode.in_double_quotes());
let msg = "bare CR not allowed in raw string";
handler.span_err(span, msg);
}
EscapeError::InvalidEscape => {
let (c, span) = last_char();
let label = if mode.is_bytes() {
"unknown byte escape"
} else {
"unknown character escape"
};
let mut msg = label.to_string();
msg.push_str(": ");
push_escaped_char(&mut msg, c);
let mut diag = handler.struct_span_err(span, msg.as_str());
diag.span_label(span, label);
if c == '{' || c == '}' && !mode.is_bytes() {
diag.help("if used in a formatting string, \
curly braces are escaped with `{{` and `}}`");
} else if c == '\r' {
diag.help("this is an isolated carriage return; \
consider checking your editor and version control settings");
}
diag.emit();
}
EscapeError::TooShortHexEscape => {
handler.span_err(span, "numeric character escape is too short")
}
EscapeError::InvalidCharInHexEscape | EscapeError::InvalidCharInUnicodeEscape => {
let (c, span) = last_char();
let mut msg = if error == EscapeError::InvalidCharInHexEscape {
"invalid character in numeric character escape: "
} else {
"invalid character in unicode escape: "
}.to_string();
push_escaped_char(&mut msg, c);
handler.span_err(span, msg.as_str())
}
EscapeError::NonAsciiCharInByte => {
assert!(mode.is_bytes());
let (_c, span) = last_char();
handler.span_err(span, "byte constant must be ASCII. \
Use a \\xHH escape for a non-ASCII byte")
}
EscapeError::NonAsciiCharInByteString => {
assert!(mode.is_bytes());
let (_c, span) = last_char();
handler.span_err(span, "raw byte string must be ASCII")
}
EscapeError::OutOfRangeHexEscape => {
handler.span_err(span, "this form of character escape may only be used \
with characters in the range [\\x00-\\x7f]")
}
EscapeError::LeadingUnderscoreUnicodeEscape => {
let (_c, span) = last_char();
handler.span_err(span, "invalid start of unicode escape")
}
EscapeError::OverlongUnicodeEscape => {
handler.span_err(span, "overlong unicode escape (must have at most 6 hex digits)")
}
EscapeError::UnclosedUnicodeEscape => {
handler.span_err(span, "unterminated unicode escape (needed a `}`)")
}
EscapeError::NoBraceInUnicodeEscape => {
let msg = "incorrect unicode escape sequence";
let mut diag = handler.struct_span_err(span, msg);
let mut suggestion = "\\u{".to_owned();
let mut suggestion_len = 0;
let (c, char_span) = last_char();
let chars = once(c).chain(lit[range.end..].chars());
for c in chars.take(6).take_while(|c| c.is_digit(16)) {
suggestion.push(c);
suggestion_len += c.len_utf8();
}
if suggestion_len > 0 {
suggestion.push('}');
let lo = char_span.lo();
let hi = lo + BytePos(suggestion_len as u32);
diag.span_suggestion(
span.with_lo(lo).with_hi(hi),
"format of unicode escape sequences uses braces",
suggestion,
Applicability::MaybeIncorrect,
);
} else {
diag.span_label(span, msg);
diag.help(
"format of unicode escape sequences is `\\u{...}`",
);
}
diag.emit();
}
EscapeError::UnicodeEscapeInByte => {
handler.span_err(span, "unicode escape sequences cannot be used \
as a byte or in a byte string")
}
EscapeError::EmptyUnicodeEscape => {
handler.span_err(span, "empty unicode escape (must have at least 1 hex digit)")
}
EscapeError::ZeroChars => {
handler.span_err(span, "empty character literal")
}
EscapeError::LoneSlash => {
handler.span_err(span, "invalid trailing slash in literal")
}
}
}
/// Pushes a character to a message string for error reporting
pub(crate) fn push_escaped_char(msg: &mut String, c: char) {
match c {
'\u{20}'..='\u{7e}' => {
// Don't escape \, ' or " for user-facing messages
msg.push(c);
}
_ => {
msg.extend(c.escape_default());
}
}
}

View file

@ -0,0 +1,392 @@
// Characters and their corresponding confusables were collected from
// http://www.unicode.org/Public/security/10.0.0/confusables.txt
use super::StringReader;
use errors::{Applicability, DiagnosticBuilder};
use syntax_pos::{BytePos, Pos, Span, symbol::kw};
use crate::token;
#[rustfmt::skip] // for line breaks
const UNICODE_ARRAY: &[(char, &str, char)] = &[
('', "Line Separator", ' '),
('', "Paragraph Separator", ' '),
('', "Ogham Space mark", ' '),
(' ', "En Quad", ' '),
('', "Em Quad", ' '),
('', "En Space", ' '),
('', "Em Space", ' '),
('', "Three-Per-Em Space", ' '),
('', "Four-Per-Em Space", ' '),
('', "Six-Per-Em Space", ' '),
('', "Punctuation Space", ' '),
('', "Thin Space", ' '),
('', "Hair Space", ' '),
('', "Medium Mathematical Space", ' '),
(' ', "No-Break Space", ' '),
('', "Figure Space", ' '),
('', "Narrow No-Break Space", ' '),
(' ', "Ideographic Space", ' '),
('ߺ', "Nko Lajanyalan", '_'),
('', "Dashed Low Line", '_'),
('', "Centreline Low Line", '_'),
('', "Wavy Low Line", '_'),
('_', "Fullwidth Low Line", '_'),
('', "Hyphen", '-'),
('', "Non-Breaking Hyphen", '-'),
('', "Figure Dash", '-'),
('', "En Dash", '-'),
('—', "Em Dash", '-'),
('', "Small Em Dash", '-'),
('۔', "Arabic Full Stop", '-'),
('', "Hyphen Bullet", '-'),
('˗', "Modifier Letter Minus Sign", '-'),
('', "Minus Sign", '-'),
('', "Heavy Minus Sign", '-'),
('', "Coptic Letter Dialect-P Ni", '-'),
('ー', "Katakana-Hiragana Prolonged Sound Mark", '-'),
('', "Fullwidth Hyphen-Minus", '-'),
('―', "Horizontal Bar", '-'),
('─', "Box Drawings Light Horizontal", '-'),
('━', "Box Drawings Heavy Horizontal", '-'),
('㇐', "CJK Stroke H", '-'),
('ꟷ', "Latin Epigraphic Letter Sideways I", '-'),
('ᅳ', "Hangul Jungseong Eu", '-'),
('ㅡ', "Hangul Letter Eu", '-'),
('一', "CJK Unified Ideograph-4E00", '-'),
('⼀', "Kangxi Radical One", '-'),
('؍', "Arabic Date Separator", ','),
('٫', "Arabic Decimal Separator", ','),
('', "Single Low-9 Quotation Mark", ','),
('¸', "Cedilla", ','),
('', "Lisu Letter Tone Na Po", ','),
('', "Fullwidth Comma", ','),
(';', "Greek Question Mark", ';'),
('', "Fullwidth Semicolon", ';'),
('︔', "Presentation Form For Vertical Semicolon", ';'),
('', "Devanagari Sign Visarga", ':'),
('', "Gujarati Sign Visarga", ':'),
('', "Fullwidth Colon", ':'),
('։', "Armenian Full Stop", ':'),
('܃', "Syriac Supralinear Colon", ':'),
('܄', "Syriac Sublinear Colon", ':'),
('', "Runic Multiple Punctuation", ':'),
('', "Presentation Form For Vertical Two Dot Leader", ':'),
('', "Mongolian Full Stop", ':'),
('', "Mongolian Manchu Full Stop", ':'),
('', "Two Dot Punctuation", ':'),
('׃', "Hebrew Punctuation Sof Pasuq", ':'),
('˸', "Modifier Letter Raised Colon", ':'),
('', "Modifier Letter Colon", ':'),
('', "Ratio", ':'),
('ː', "Modifier Letter Triangular Colon", ':'),
('', "Lisu Letter Tone Mya Jeu", ':'),
('︓', "Presentation Form For Vertical Colon", ':'),
('', "Fullwidth Exclamation Mark", '!'),
('ǃ', "Latin Letter Retroflex Click", '!'),
('', "Tifinagh Letter Tuareg Yang", '!'),
('︕', "Presentation Form For Vertical Exclamation Mark", '!'),
('ʔ', "Latin Letter Glottal Stop", '?'),
('Ɂ', "Latin Capital Letter Glottal Stop", '?'),
('', "Devanagari Letter Glottal Stop", '?'),
('', "Cherokee Letter He", '?'),
('', "Bamum Letter Ntuu", '?'),
('', "Fullwidth Question Mark", '?'),
('︖', "Presentation Form For Vertical Question Mark", '?'),
('𝅭', "Musical Symbol Combining Augmentation Dot", '.'),
('', "One Dot Leader", '.'),
('܁', "Syriac Supralinear Full Stop", '.'),
('܂', "Syriac Sublinear Full Stop", '.'),
('', "Vai Full Stop", '.'),
('𐩐', "Kharoshthi Punctuation Dot", '.'),
('٠', "Arabic-Indic Digit Zero", '.'),
('۰', "Extended Arabic-Indic Digit Zero", '.'),
('', "Lisu Letter Tone Mya Ti", '.'),
('·', "Middle Dot", '.'),
('・', "Katakana Middle Dot", '.'),
('・', "Halfwidth Katakana Middle Dot", '.'),
('᛫', "Runic Single Punctuation", '.'),
('·', "Greek Ano Teleia", '.'),
('⸱', "Word Separator Middle Dot", '.'),
('𐄁', "Aegean Word Separator Dot", '.'),
('•', "Bullet", '.'),
('‧', "Hyphenation Point", '.'),
('∙', "Bullet Operator", '.'),
('⋅', "Dot Operator", '.'),
('ꞏ', "Latin Letter Sinological Dot", '.'),
('ᐧ', "Canadian Syllabics Final Middle Dot", '.'),
('ᐧ', "Canadian Syllabics Final Middle Dot", '.'),
('', "Fullwidth Full Stop", '.'),
('。', "Ideographic Full Stop", '.'),
('︒', "Presentation Form For Vertical Ideographic Full Stop", '.'),
('՝', "Armenian Comma", '\''),
('', "Fullwidth Apostrophe", '\''),
('', "Left Single Quotation Mark", '\''),
('', "Right Single Quotation Mark", '\''),
('', "Single High-Reversed-9 Quotation Mark", '\''),
('', "Prime", '\''),
('', "Reversed Prime", '\''),
('՚', "Armenian Apostrophe", '\''),
('׳', "Hebrew Punctuation Geresh", '\''),
('`', "Grave Accent", '\''),
('', "Greek Varia", '\''),
('', "Fullwidth Grave Accent", '\''),
('´', "Acute Accent", '\''),
('΄', "Greek Tonos", '\''),
('', "Greek Oxia", '\''),
('', "Greek Koronis", '\''),
('᾿', "Greek Psili", '\''),
('', "Greek Dasia", '\''),
('ʹ', "Modifier Letter Prime", '\''),
('ʹ', "Greek Numeral Sign", '\''),
('ˈ', "Modifier Letter Vertical Line", '\''),
('ˊ', "Modifier Letter Acute Accent", '\''),
('ˋ', "Modifier Letter Grave Accent", '\''),
('˴', "Modifier Letter Middle Grave Accent", '\''),
('ʻ', "Modifier Letter Turned Comma", '\''),
('ʽ', "Modifier Letter Reversed Comma", '\''),
('ʼ', "Modifier Letter Apostrophe", '\''),
('ʾ', "Modifier Letter Right Half Ring", '\''),
('', "Latin Small Letter Saltillo", '\''),
('י', "Hebrew Letter Yod", '\''),
('ߴ', "Nko High Tone Apostrophe", '\''),
('ߵ', "Nko Low Tone Apostrophe", '\''),
('', "Canadian Syllabics West-Cree P", '\''),
('', "Runic Letter Short-Twig-Sol S", '\''),
('𖽑', "Miao Sign Aspiration", '\''),
('𖽒', "Miao Sign Reformed Voicing", '\''),
('᳓', "Vedic Sign Nihshvasa", '"'),
('', "Fullwidth Quotation Mark", '"'),
('“', "Left Double Quotation Mark", '"'),
('”', "Right Double Quotation Mark", '"'),
('‟', "Double High-Reversed-9 Quotation Mark", '"'),
('″', "Double Prime", '"'),
('‶', "Reversed Double Prime", '"'),
('〃', "Ditto Mark", '"'),
('״', "Hebrew Punctuation Gershayim", '"'),
('˝', "Double Acute Accent", '"'),
('ʺ', "Modifier Letter Double Prime", '"'),
('˶', "Modifier Letter Middle Double Acute Accent", '"'),
('˵', "Modifier Letter Middle Double Grave Accent", '"'),
('ˮ', "Modifier Letter Double Apostrophe", '"'),
('ײ', "Hebrew Ligature Yiddish Double Yod", '"'),
('❞', "Heavy Double Comma Quotation Mark Ornament", '"'),
('❝', "Heavy Double Turned Comma Quotation Mark Ornament", '"'),
('', "Fullwidth Left Parenthesis", '('),
('', "Medium Left Parenthesis Ornament", '('),
('', "Ornate Left Parenthesis", '('),
('', "Fullwidth Right Parenthesis", ')'),
('', "Medium Right Parenthesis Ornament", ')'),
('﴿', "Ornate Right Parenthesis", ')'),
('', "Fullwidth Left Square Bracket", '['),
('', "Light Left Tortoise Shell Bracket Ornament", '['),
('「', "Left Corner Bracket", '['),
('『', "Left White Corner Bracket", '['),
('【', "Left Black Lenticular Bracket", '['),
('', "Left Tortoise Shell Bracket", '['),
('〖', "Left White Lenticular Bracket", '['),
('〘', "Left White Tortoise Shell Bracket", '['),
('〚', "Left White Square Bracket", '['),
('', "Fullwidth Right Square Bracket", ']'),
('', "Light Right Tortoise Shell Bracket Ornament", ']'),
('」', "Right Corner Bracket", ']'),
('』', "Right White Corner Bracket", ']'),
('】', "Right Black Lenticular Bracket", ']'),
('', "Right Tortoise Shell Bracket", ']'),
('〗', "Right White Lenticular Bracket", ']'),
('〙', "Right White Tortoise Shell Bracket", ']'),
('〛', "Right White Square Bracket", ']'),
('', "Medium Left Curly Bracket Ornament", '{'),
('𝄔', "Musical Symbol Brace", '{'),
('', "Fullwidth Left Curly Bracket", '{'),
('', "Medium Right Curly Bracket Ornament", '}'),
('', "Fullwidth Right Curly Bracket", '}'),
('', "Low Asterisk", '*'),
('٭', "Arabic Five Pointed Star", '*'),
('', "Asterisk Operator", '*'),
('𐌟', "Old Italic Letter Ess", '*'),
('', "Fullwidth Asterisk", '*'),
('', "Philippine Single Punctuation", '/'),
('', "Caret Insertion Point", '/'),
('', "Division Slash", '/'),
('', "Fraction Slash", '/'),
('', "Box Drawings Light Diagonal Upper Right To Lower Left", '/'),
('', "Mathematical Rising Diagonal", '/'),
('', "Big Solidus", '/'),
('𝈺', "Greek Instrumental Notation Symbol-47", '/'),
('', "CJK Stroke Sp", '/'),
('', "Vertical Kana Repeat Mark Upper Half", '/'),
('', "Coptic Capital Letter Old Coptic Esh", '/'),
('', "Katakana Letter No", '/'),
('丿', "CJK Unified Ideograph-4E3F", '/'),
('', "Kangxi Radical Slash", '/'),
('', "Fullwidth Solidus", '/'),
('', "Fullwidth Reverse Solidus", '\\'),
('', "Small Reverse Solidus", '\\'),
('', "Set Minus", '\\'),
('', "Mathematical Falling Diagonal", '\\'),
('', "Reverse Solidus Operator", '\\'),
('', "Big Reverse Solidus", '\\'),
('', "Greek Vocal Notation Symbol-16", '\\'),
('', "Greek Instrumental Symbol-48", '\\'),
('', "CJK Stroke D", '\\'),
('', "CJK Unified Ideograph-4E36", '\\'),
('', "Kangxi Radical Dot", '\\'),
('、', "Ideographic Comma", '\\'),
('ヽ', "Katakana Iteration Mark", '\\'),
('', "Latin Small Letter Um", '&'),
('', "Fullwidth Ampersand", '&'),
('', "Runic Cross Punctuation", '+'),
('', "Heavy Plus Sign", '+'),
('𐊛', "Lycian Letter H", '+'),
('﬩', "Hebrew Letter Alternative Plus Sign", '+'),
('', "Fullwidth Plus Sign", '+'),
('', "Single Left-Pointing Angle Quotation Mark", '<'),
('', "Heavy Left-Pointing Angle Quotation Mark Ornament", '<'),
('˂', "Modifier Letter Left Arrowhead", '<'),
('𝈶', "Greek Instrumental Symbol-40", '<'),
('', "Canadian Syllabics Pa", '<'),
('', "Runic Letter Kauna", '<'),
('❬', "Medium Left-Pointing Angle Bracket Ornament", '<'),
('⟨', "Mathematical Left Angle Bracket", '<'),
('〈', "Left-Pointing Angle Bracket", '<'),
('〈', "Left Angle Bracket", '<'),
('㇛', "CJK Stroke Pd", '<'),
('く', "Hiragana Letter Ku", '<'),
('𡿨', "CJK Unified Ideograph-21FE8", '<'),
('《', "Left Double Angle Bracket", '<'),
('', "Fullwidth Less-Than Sign", '<'),
('', "Canadian Syllabics Hyphen", '='),
('', "Double Hyphen", '='),
('', "Katakana-Hiragana Double Hyphen", '='),
('', "Lisu Punctuation Full Stop", '='),
('', "Fullwidth Equals Sign", '='),
('', "Single Right-Pointing Angle Quotation Mark", '>'),
('', "Heavy Right-Pointing Angle Quotation Mark Ornament", '>'),
('˃', "Modifier Letter Right Arrowhead", '>'),
('𝈷', "Greek Instrumental Symbol-42", '>'),
('', "Canadian Syllabics Po", '>'),
('𖼿', "Miao Letter Archaic Zza", '>'),
('❭', "Medium Right-Pointing Angle Bracket Ornament", '>'),
('⟩', "Mathematical Right Angle Bracket", '>'),
('〉', "Right-Pointing Angle Bracket", '>'),
('〉', "Right Angle Bracket", '>'),
('》', "Right Double Angle Bracket", '>'),
('', "Fullwidth Greater-Than Sign", '>'),
];
// FIXME: the lexer could be used to turn the ASCII version of unicode homoglyphs, instead of
// keeping the substitution token in this table. Ideally, this should be inside `rustc_lexer`.
// However, we should first remove compound tokens like `<<` from `rustc_lexer`, and then add
// fancier error recovery to it, as there will be less overall work to do this way.
const ASCII_ARRAY: &[(char, &str, Option<token::TokenKind>)] = &[
(' ', "Space", Some(token::Whitespace)),
('_', "Underscore", Some(token::Ident(kw::Underscore, false))),
('-', "Minus/Hyphen", Some(token::BinOp(token::Minus))),
(',', "Comma", Some(token::Comma)),
(';', "Semicolon", Some(token::Semi)),
(':', "Colon", Some(token::Colon)),
('!', "Exclamation Mark", Some(token::Not)),
('?', "Question Mark", Some(token::Question)),
('.', "Period", Some(token::Dot)),
('(', "Left Parenthesis", Some(token::OpenDelim(token::Paren))),
(')', "Right Parenthesis", Some(token::CloseDelim(token::Paren))),
('[', "Left Square Bracket", Some(token::OpenDelim(token::Bracket))),
(']', "Right Square Bracket", Some(token::CloseDelim(token::Bracket))),
('{', "Left Curly Brace", Some(token::OpenDelim(token::Brace))),
('}', "Right Curly Brace", Some(token::CloseDelim(token::Brace))),
('*', "Asterisk", Some(token::BinOp(token::Star))),
('/', "Slash", Some(token::BinOp(token::Slash))),
('\\', "Backslash", None),
('&', "Ampersand", Some(token::BinOp(token::And))),
('+', "Plus Sign", Some(token::BinOp(token::Plus))),
('<', "Less-Than Sign", Some(token::Lt)),
('=', "Equals Sign", Some(token::Eq)),
('>', "Greater-Than Sign", Some(token::Gt)),
// FIXME: Literals are already lexed by this point, so we can't recover gracefully just by
// spitting the correct token out.
('\'', "Single Quote", None),
('"', "Quotation Mark", None),
];
crate fn check_for_substitution<'a>(
reader: &StringReader<'a>,
pos: BytePos,
ch: char,
err: &mut DiagnosticBuilder<'a>,
) -> Option<token::TokenKind> {
let (u_name, ascii_char) = match UNICODE_ARRAY.iter().find(|&&(c, _, _)| c == ch) {
Some(&(_u_char, u_name, ascii_char)) => (u_name, ascii_char),
None => return None,
};
let span = Span::with_root_ctxt(pos, pos + Pos::from_usize(ch.len_utf8()));
let (ascii_name, token) = match ASCII_ARRAY.iter().find(|&&(c, _, _)| c == ascii_char) {
Some((_ascii_char, ascii_name, token)) => (ascii_name, token),
None => {
let msg = format!("substitution character not found for '{}'", ch);
reader.sess.span_diagnostic.span_bug_no_panic(span, &msg);
return None;
}
};
// special help suggestion for "directed" double quotes
if let Some(s) = peek_delimited(&reader.src[reader.src_index(pos)..], '“', '”') {
let msg = format!(
"Unicode characters '“' (Left Double Quotation Mark) and \
'”' (Right Double Quotation Mark) look like '{}' ({}), but are not",
ascii_char, ascii_name
);
err.span_suggestion(
Span::with_root_ctxt(
pos,
pos + Pos::from_usize('“'.len_utf8() + s.len() + '”'.len_utf8()),
),
&msg,
format!("\"{}\"", s),
Applicability::MaybeIncorrect,
);
} else {
let msg = format!(
"Unicode character '{}' ({}) looks like '{}' ({}), but it is not",
ch, u_name, ascii_char, ascii_name
);
err.span_suggestion(span, &msg, ascii_char.to_string(), Applicability::MaybeIncorrect);
}
token.clone()
}
/// Extract string if found at current position with given delimiters
fn peek_delimited(text: &str, from_ch: char, to_ch: char) -> Option<&str> {
let mut chars = text.chars();
let first_char = chars.next()?;
if first_char != from_ch {
return None;
}
let last_char_idx = chars.as_str().find(to_ch)?;
Some(&chars.as_str()[..last_char_idx])
}

423
src/librustc_parse/lib.rs Normal file
View file

@ -0,0 +1,423 @@
//! The main parser interface.
#![feature(crate_visibility_modifier)]
use syntax::ast;
use syntax::print::pprust;
use syntax::sess::ParseSess;
use syntax::token::{self, Nonterminal};
use syntax::tokenstream::{self, TokenStream, TokenTree};
use errors::{PResult, FatalError, Level, Diagnostic};
use rustc_data_structures::sync::Lrc;
use syntax_pos::{Span, SourceFile, FileName};
use std::borrow::Cow;
use std::path::Path;
use std::str;
use log::info;
pub const MACRO_ARGUMENTS: Option<&'static str> = Some("macro arguments");
#[macro_use]
pub mod parser;
use parser::{Parser, emit_unclosed_delims, make_unclosed_delims_error};
pub mod lexer;
pub mod validate_attr;
pub mod error_codes;
#[derive(Clone)]
pub struct Directory<'a> {
pub path: Cow<'a, Path>,
pub ownership: DirectoryOwnership,
}
#[derive(Copy, Clone)]
pub enum DirectoryOwnership {
Owned {
// None if `mod.rs`, `Some("foo")` if we're in `foo.rs`.
relative: Option<ast::Ident>,
},
UnownedViaBlock,
UnownedViaMod,
}
// A bunch of utility functions of the form `parse_<thing>_from_<source>`
// where <thing> includes crate, expr, item, stmt, tts, and one that
// uses a HOF to parse anything, and <source> includes file and
// `source_str`.
/// A variant of 'panictry!' that works on a Vec<Diagnostic> instead of a single DiagnosticBuilder.
macro_rules! panictry_buffer {
($handler:expr, $e:expr) => ({
use std::result::Result::{Ok, Err};
use errors::FatalError;
match $e {
Ok(e) => e,
Err(errs) => {
for e in errs {
$handler.emit_diagnostic(&e);
}
FatalError.raise()
}
}
})
}
pub fn parse_crate_from_file<'a>(input: &Path, sess: &'a ParseSess) -> PResult<'a, ast::Crate> {
let mut parser = new_parser_from_file(sess, input);
parser.parse_crate_mod()
}
pub fn parse_crate_attrs_from_file<'a>(input: &Path, sess: &'a ParseSess)
-> PResult<'a, Vec<ast::Attribute>> {
let mut parser = new_parser_from_file(sess, input);
parser.parse_inner_attributes()
}
pub fn parse_crate_from_source_str(name: FileName, source: String, sess: &ParseSess)
-> PResult<'_, ast::Crate> {
new_parser_from_source_str(sess, name, source).parse_crate_mod()
}
pub fn parse_crate_attrs_from_source_str(name: FileName, source: String, sess: &ParseSess)
-> PResult<'_, Vec<ast::Attribute>> {
new_parser_from_source_str(sess, name, source).parse_inner_attributes()
}
pub fn parse_stream_from_source_str(
name: FileName,
source: String,
sess: &ParseSess,
override_span: Option<Span>,
) -> TokenStream {
let (stream, mut errors) = source_file_to_stream(
sess,
sess.source_map().new_source_file(name, source),
override_span,
);
emit_unclosed_delims(&mut errors, &sess);
stream
}
/// Creates a new parser from a source string.
pub fn new_parser_from_source_str(sess: &ParseSess, name: FileName, source: String) -> Parser<'_> {
panictry_buffer!(&sess.span_diagnostic, maybe_new_parser_from_source_str(sess, name, source))
}
/// Creates a new parser from a source string. Returns any buffered errors from lexing the initial
/// token stream.
pub fn maybe_new_parser_from_source_str(sess: &ParseSess, name: FileName, source: String)
-> Result<Parser<'_>, Vec<Diagnostic>>
{
let mut parser = maybe_source_file_to_parser(sess,
sess.source_map().new_source_file(name, source))?;
parser.recurse_into_file_modules = false;
Ok(parser)
}
/// Creates a new parser, handling errors as appropriate if the file doesn't exist.
pub fn new_parser_from_file<'a>(sess: &'a ParseSess, path: &Path) -> Parser<'a> {
source_file_to_parser(sess, file_to_source_file(sess, path, None))
}
/// Creates a new parser, returning buffered diagnostics if the file doesn't exist,
/// or from lexing the initial token stream.
pub fn maybe_new_parser_from_file<'a>(sess: &'a ParseSess, path: &Path)
-> Result<Parser<'a>, Vec<Diagnostic>> {
let file = try_file_to_source_file(sess, path, None).map_err(|db| vec![db])?;
maybe_source_file_to_parser(sess, file)
}
/// Given a session, a crate config, a path, and a span, add
/// the file at the given path to the `source_map`, and returns a parser.
/// On an error, uses the given span as the source of the problem.
pub fn new_sub_parser_from_file<'a>(sess: &'a ParseSess,
path: &Path,
directory_ownership: DirectoryOwnership,
module_name: Option<String>,
sp: Span) -> Parser<'a> {
let mut p = source_file_to_parser(sess, file_to_source_file(sess, path, Some(sp)));
p.directory.ownership = directory_ownership;
p.root_module_name = module_name;
p
}
/// Given a `source_file` and config, returns a parser.
fn source_file_to_parser(sess: &ParseSess, source_file: Lrc<SourceFile>) -> Parser<'_> {
panictry_buffer!(&sess.span_diagnostic,
maybe_source_file_to_parser(sess, source_file))
}
/// Given a `source_file` and config, return a parser. Returns any buffered errors from lexing the
/// initial token stream.
fn maybe_source_file_to_parser(
sess: &ParseSess,
source_file: Lrc<SourceFile>,
) -> Result<Parser<'_>, Vec<Diagnostic>> {
let end_pos = source_file.end_pos;
let (stream, unclosed_delims) = maybe_file_to_stream(sess, source_file, None)?;
let mut parser = stream_to_parser(sess, stream, None);
parser.unclosed_delims = unclosed_delims;
if parser.token == token::Eof && parser.token.span.is_dummy() {
parser.token.span = Span::new(end_pos, end_pos, parser.token.span.ctxt());
}
Ok(parser)
}
// Must preserve old name for now, because `quote!` from the *existing*
// compiler expands into it.
pub fn new_parser_from_tts(sess: &ParseSess, tts: Vec<TokenTree>) -> Parser<'_> {
stream_to_parser(sess, tts.into_iter().collect(), crate::MACRO_ARGUMENTS)
}
// Base abstractions
/// Given a session and a path and an optional span (for error reporting),
/// add the path to the session's source_map and return the new source_file or
/// error when a file can't be read.
fn try_file_to_source_file(sess: &ParseSess, path: &Path, spanopt: Option<Span>)
-> Result<Lrc<SourceFile>, Diagnostic> {
sess.source_map().load_file(path)
.map_err(|e| {
let msg = format!("couldn't read {}: {}", path.display(), e);
let mut diag = Diagnostic::new(Level::Fatal, &msg);
if let Some(sp) = spanopt {
diag.set_span(sp);
}
diag
})
}
/// Given a session and a path and an optional span (for error reporting),
/// adds the path to the session's `source_map` and returns the new `source_file`.
fn file_to_source_file(sess: &ParseSess, path: &Path, spanopt: Option<Span>)
-> Lrc<SourceFile> {
match try_file_to_source_file(sess, path, spanopt) {
Ok(source_file) => source_file,
Err(d) => {
sess.span_diagnostic.emit_diagnostic(&d);
FatalError.raise();
}
}
}
/// Given a `source_file`, produces a sequence of token trees.
pub fn source_file_to_stream(
sess: &ParseSess,
source_file: Lrc<SourceFile>,
override_span: Option<Span>,
) -> (TokenStream, Vec<lexer::UnmatchedBrace>) {
panictry_buffer!(&sess.span_diagnostic, maybe_file_to_stream(sess, source_file, override_span))
}
/// Given a source file, produces a sequence of token trees. Returns any buffered errors from
/// parsing the token stream.
pub fn maybe_file_to_stream(
sess: &ParseSess,
source_file: Lrc<SourceFile>,
override_span: Option<Span>,
) -> Result<(TokenStream, Vec<lexer::UnmatchedBrace>), Vec<Diagnostic>> {
let srdr = lexer::StringReader::new(sess, source_file, override_span);
let (token_trees, unmatched_braces) = srdr.into_token_trees();
match token_trees {
Ok(stream) => Ok((stream, unmatched_braces)),
Err(err) => {
let mut buffer = Vec::with_capacity(1);
err.buffer(&mut buffer);
// Not using `emit_unclosed_delims` to use `db.buffer`
for unmatched in unmatched_braces {
if let Some(err) = make_unclosed_delims_error(unmatched, &sess) {
err.buffer(&mut buffer);
}
}
Err(buffer)
}
}
}
/// Given a stream and the `ParseSess`, produces a parser.
pub fn stream_to_parser<'a>(
sess: &'a ParseSess,
stream: TokenStream,
subparser_name: Option<&'static str>,
) -> Parser<'a> {
Parser::new(sess, stream, None, true, false, subparser_name)
}
/// Given a stream, the `ParseSess` and the base directory, produces a parser.
///
/// Use this function when you are creating a parser from the token stream
/// and also care about the current working directory of the parser (e.g.,
/// you are trying to resolve modules defined inside a macro invocation).
///
/// # Note
///
/// The main usage of this function is outside of rustc, for those who uses
/// libsyntax as a library. Please do not remove this function while refactoring
/// just because it is not used in rustc codebase!
pub fn stream_to_parser_with_base_dir<'a>(
sess: &'a ParseSess,
stream: TokenStream,
base_dir: Directory<'a>,
) -> Parser<'a> {
Parser::new(sess, stream, Some(base_dir), true, false, None)
}
/// Runs the given subparser `f` on the tokens of the given `attr`'s item.
pub fn parse_in_attr<'a, T>(
sess: &'a ParseSess,
attr: &ast::Attribute,
mut f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>,
) -> PResult<'a, T> {
let mut parser = Parser::new(
sess,
attr.get_normal_item().tokens.clone(),
None,
false,
false,
Some("attribute"),
);
let result = f(&mut parser)?;
if parser.token != token::Eof {
parser.unexpected()?;
}
Ok(result)
}
// NOTE(Centril): The following probably shouldn't be here but it acknowledges the
// fact that architecturally, we are using parsing (read on below to understand why).
pub fn nt_to_tokenstream(nt: &Nonterminal, sess: &ParseSess, span: Span) -> TokenStream {
// A `Nonterminal` is often a parsed AST item. At this point we now
// need to convert the parsed AST to an actual token stream, e.g.
// un-parse it basically.
//
// Unfortunately there's not really a great way to do that in a
// guaranteed lossless fashion right now. The fallback here is to just
// stringify the AST node and reparse it, but this loses all span
// information.
//
// As a result, some AST nodes are annotated with the token stream they
// came from. Here we attempt to extract these lossless token streams
// before we fall back to the stringification.
let tokens = match *nt {
Nonterminal::NtItem(ref item) => {
prepend_attrs(sess, &item.attrs, item.tokens.as_ref(), span)
}
Nonterminal::NtTraitItem(ref item) => {
prepend_attrs(sess, &item.attrs, item.tokens.as_ref(), span)
}
Nonterminal::NtImplItem(ref item) => {
prepend_attrs(sess, &item.attrs, item.tokens.as_ref(), span)
}
Nonterminal::NtIdent(ident, is_raw) => {
Some(tokenstream::TokenTree::token(token::Ident(ident.name, is_raw), ident.span).into())
}
Nonterminal::NtLifetime(ident) => {
Some(tokenstream::TokenTree::token(token::Lifetime(ident.name), ident.span).into())
}
Nonterminal::NtTT(ref tt) => {
Some(tt.clone().into())
}
_ => None,
};
// FIXME(#43081): Avoid this pretty-print + reparse hack
let source = pprust::nonterminal_to_string(nt);
let filename = FileName::macro_expansion_source_code(&source);
let tokens_for_real = parse_stream_from_source_str(filename, source, sess, Some(span));
// During early phases of the compiler the AST could get modified
// directly (e.g., attributes added or removed) and the internal cache
// of tokens my not be invalidated or updated. Consequently if the
// "lossless" token stream disagrees with our actual stringification
// (which has historically been much more battle-tested) then we go
// with the lossy stream anyway (losing span information).
//
// Note that the comparison isn't `==` here to avoid comparing spans,
// but it *also* is a "probable" equality which is a pretty weird
// definition. We mostly want to catch actual changes to the AST
// like a `#[cfg]` being processed or some weird `macro_rules!`
// expansion.
//
// What we *don't* want to catch is the fact that a user-defined
// literal like `0xf` is stringified as `15`, causing the cached token
// stream to not be literal `==` token-wise (ignoring spans) to the
// token stream we got from stringification.
//
// Instead the "probably equal" check here is "does each token
// recursively have the same discriminant?" We basically don't look at
// the token values here and assume that such fine grained token stream
// modifications, including adding/removing typically non-semantic
// tokens such as extra braces and commas, don't happen.
if let Some(tokens) = tokens {
if tokens.probably_equal_for_proc_macro(&tokens_for_real) {
return tokens
}
info!("cached tokens found, but they're not \"probably equal\", \
going with stringified version");
}
return tokens_for_real
}
fn prepend_attrs(
sess: &ParseSess,
attrs: &[ast::Attribute],
tokens: Option<&tokenstream::TokenStream>,
span: syntax_pos::Span
) -> Option<tokenstream::TokenStream> {
let tokens = tokens?;
if attrs.len() == 0 {
return Some(tokens.clone())
}
let mut builder = tokenstream::TokenStreamBuilder::new();
for attr in attrs {
assert_eq!(attr.style, ast::AttrStyle::Outer,
"inner attributes should prevent cached tokens from existing");
let source = pprust::attribute_to_string(attr);
let macro_filename = FileName::macro_expansion_source_code(&source);
let item = match attr.kind {
ast::AttrKind::Normal(ref item) => item,
ast::AttrKind::DocComment(_) => {
let stream = parse_stream_from_source_str(macro_filename, source, sess, Some(span));
builder.push(stream);
continue
}
};
// synthesize # [ $path $tokens ] manually here
let mut brackets = tokenstream::TokenStreamBuilder::new();
// For simple paths, push the identifier directly
if item.path.segments.len() == 1 && item.path.segments[0].args.is_none() {
let ident = item.path.segments[0].ident;
let token = token::Ident(ident.name, ident.as_str().starts_with("r#"));
brackets.push(tokenstream::TokenTree::token(token, ident.span));
// ... and for more complicated paths, fall back to a reparse hack that
// should eventually be removed.
} else {
let stream = parse_stream_from_source_str(macro_filename, source, sess, Some(span));
brackets.push(stream);
}
brackets.push(item.tokens.clone());
// The span we list here for `#` and for `[ ... ]` are both wrong in
// that it encompasses more than each token, but it hopefully is "good
// enough" for now at least.
builder.push(tokenstream::TokenTree::token(token::Pound, attr.span));
let delim_span = tokenstream::DelimSpan::from_single(attr.span);
builder.push(tokenstream::TokenTree::Delimited(
delim_span, token::DelimToken::Bracket, brackets.build().into()));
}
builder.push(tokens.clone());
Some(builder.build())
}

View file

@ -0,0 +1,351 @@
use super::{SeqSep, Parser, TokenType, PathStyle};
use syntax::attr;
use syntax::ast;
use syntax::util::comments;
use syntax::token::{self, Nonterminal, DelimToken};
use syntax::tokenstream::{TokenStream, TokenTree};
use syntax_pos::{Span, Symbol};
use errors::PResult;
use log::debug;
#[derive(Debug)]
enum InnerAttributeParsePolicy<'a> {
Permitted,
NotPermitted { reason: &'a str, saw_doc_comment: bool, prev_attr_sp: Option<Span> },
}
const DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG: &str = "an inner attribute is not \
permitted in this context";
impl<'a> Parser<'a> {
/// Parses attributes that appear before an item.
pub(super) fn parse_outer_attributes(&mut self) -> PResult<'a, Vec<ast::Attribute>> {
let mut attrs: Vec<ast::Attribute> = Vec::new();
let mut just_parsed_doc_comment = false;
loop {
debug!("parse_outer_attributes: self.token={:?}", self.token);
match self.token.kind {
token::Pound => {
let inner_error_reason = if just_parsed_doc_comment {
"an inner attribute is not permitted following an outer doc comment"
} else if !attrs.is_empty() {
"an inner attribute is not permitted following an outer attribute"
} else {
DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG
};
let inner_parse_policy =
InnerAttributeParsePolicy::NotPermitted {
reason: inner_error_reason,
saw_doc_comment: just_parsed_doc_comment,
prev_attr_sp: attrs.last().and_then(|a| Some(a.span))
};
let attr = self.parse_attribute_with_inner_parse_policy(inner_parse_policy)?;
attrs.push(attr);
just_parsed_doc_comment = false;
}
token::DocComment(s) => {
let attr = self.mk_doc_comment(s);
if attr.style != ast::AttrStyle::Outer {
let mut err = self.fatal("expected outer doc comment");
err.note("inner doc comments like this (starting with \
`//!` or `/*!`) can only appear before items");
return Err(err);
}
attrs.push(attr);
self.bump();
just_parsed_doc_comment = true;
}
_ => break,
}
}
Ok(attrs)
}
fn mk_doc_comment(&self, s: Symbol) -> ast::Attribute {
let style = comments::doc_comment_style(&s.as_str());
attr::mk_doc_comment(style, s, self.token.span)
}
/// Matches `attribute = # ! [ meta_item ]`.
///
/// If `permit_inner` is `true`, then a leading `!` indicates an inner
/// attribute.
pub fn parse_attribute(&mut self, permit_inner: bool) -> PResult<'a, ast::Attribute> {
debug!("parse_attribute: permit_inner={:?} self.token={:?}",
permit_inner,
self.token);
let inner_parse_policy = if permit_inner {
InnerAttributeParsePolicy::Permitted
} else {
InnerAttributeParsePolicy::NotPermitted {
reason: DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG,
saw_doc_comment: false,
prev_attr_sp: None
}
};
self.parse_attribute_with_inner_parse_policy(inner_parse_policy)
}
/// The same as `parse_attribute`, except it takes in an `InnerAttributeParsePolicy`
/// that prescribes how to handle inner attributes.
fn parse_attribute_with_inner_parse_policy(
&mut self,
inner_parse_policy: InnerAttributeParsePolicy<'_>
) -> PResult<'a, ast::Attribute> {
debug!("parse_attribute_with_inner_parse_policy: inner_parse_policy={:?} self.token={:?}",
inner_parse_policy,
self.token);
let (span, item, style) = match self.token.kind {
token::Pound => {
let lo = self.token.span;
self.bump();
if let InnerAttributeParsePolicy::Permitted = inner_parse_policy {
self.expected_tokens.push(TokenType::Token(token::Not));
}
let style = if self.token == token::Not {
self.bump();
ast::AttrStyle::Inner
} else {
ast::AttrStyle::Outer
};
self.expect(&token::OpenDelim(token::Bracket))?;
let item = self.parse_attr_item()?;
self.expect(&token::CloseDelim(token::Bracket))?;
let hi = self.prev_span;
let attr_sp = lo.to(hi);
// Emit error if inner attribute is encountered and not permitted
if style == ast::AttrStyle::Inner {
if let InnerAttributeParsePolicy::NotPermitted { reason,
saw_doc_comment, prev_attr_sp } = inner_parse_policy {
let prev_attr_note = if saw_doc_comment {
"previous doc comment"
} else {
"previous outer attribute"
};
let mut diagnostic = self
.diagnostic()
.struct_span_err(attr_sp, reason);
if let Some(prev_attr_sp) = prev_attr_sp {
diagnostic
.span_label(attr_sp, "not permitted following an outer attibute")
.span_label(prev_attr_sp, prev_attr_note);
}
diagnostic
.note("inner attributes, like `#![no_std]`, annotate the item \
enclosing them, and are usually found at the beginning of \
source files. Outer attributes, like `#[test]`, annotate the \
item following them.")
.emit()
}
}
(attr_sp, item, style)
}
_ => {
let token_str = self.this_token_to_string();
return Err(self.fatal(&format!("expected `#`, found `{}`", token_str)));
}
};
Ok(attr::mk_attr_from_item(style, item, span))
}
/// Parses an inner part of an attribute (the path and following tokens).
/// The tokens must be either a delimited token stream, or empty token stream,
/// or the "legacy" key-value form.
/// PATH `(` TOKEN_STREAM `)`
/// PATH `[` TOKEN_STREAM `]`
/// PATH `{` TOKEN_STREAM `}`
/// PATH
/// PATH `=` UNSUFFIXED_LIT
/// The delimiters or `=` are still put into the resulting token stream.
pub fn parse_attr_item(&mut self) -> PResult<'a, ast::AttrItem> {
let item = match self.token.kind {
token::Interpolated(ref nt) => match **nt {
Nonterminal::NtMeta(ref item) => Some(item.clone()),
_ => None,
},
_ => None,
};
Ok(if let Some(item) = item {
self.bump();
item
} else {
let path = self.parse_path(PathStyle::Mod)?;
let tokens = if self.check(&token::OpenDelim(DelimToken::Paren)) ||
self.check(&token::OpenDelim(DelimToken::Bracket)) ||
self.check(&token::OpenDelim(DelimToken::Brace)) {
self.parse_token_tree().into()
} else if self.eat(&token::Eq) {
let eq = TokenTree::token(token::Eq, self.prev_span);
let mut is_interpolated_expr = false;
if let token::Interpolated(nt) = &self.token.kind {
if let token::NtExpr(..) = **nt {
is_interpolated_expr = true;
}
}
let token_tree = if is_interpolated_expr {
// We need to accept arbitrary interpolated expressions to continue
// supporting things like `doc = $expr` that work on stable.
// Non-literal interpolated expressions are rejected after expansion.
self.parse_token_tree()
} else {
self.parse_unsuffixed_lit()?.token_tree()
};
TokenStream::new(vec![eq.into(), token_tree.into()])
} else {
TokenStream::default()
};
ast::AttrItem { path, tokens }
})
}
/// Parses attributes that appear after the opening of an item. These should
/// be preceded by an exclamation mark, but we accept and warn about one
/// terminated by a semicolon.
///
/// Matches `inner_attrs*`.
crate fn parse_inner_attributes(&mut self) -> PResult<'a, Vec<ast::Attribute>> {
let mut attrs: Vec<ast::Attribute> = vec![];
loop {
match self.token.kind {
token::Pound => {
// Don't even try to parse if it's not an inner attribute.
if !self.look_ahead(1, |t| t == &token::Not) {
break;
}
let attr = self.parse_attribute(true)?;
assert_eq!(attr.style, ast::AttrStyle::Inner);
attrs.push(attr);
}
token::DocComment(s) => {
// We need to get the position of this token before we bump.
let attr = self.mk_doc_comment(s);
if attr.style == ast::AttrStyle::Inner {
attrs.push(attr);
self.bump();
} else {
break;
}
}
_ => break,
}
}
Ok(attrs)
}
fn parse_unsuffixed_lit(&mut self) -> PResult<'a, ast::Lit> {
let lit = self.parse_lit()?;
debug!("checking if {:?} is unusuffixed", lit);
if !lit.kind.is_unsuffixed() {
let msg = "suffixed literals are not allowed in attributes";
self.diagnostic().struct_span_err(lit.span, msg)
.help("instead of using a suffixed literal \
(1u8, 1.0f32, etc.), use an unsuffixed version \
(1, 1.0, etc.).")
.emit()
}
Ok(lit)
}
/// Parses `cfg_attr(pred, attr_item_list)` where `attr_item_list` is comma-delimited.
pub fn parse_cfg_attr(&mut self) -> PResult<'a, (ast::MetaItem, Vec<(ast::AttrItem, Span)>)> {
self.expect(&token::OpenDelim(token::Paren))?;
let cfg_predicate = self.parse_meta_item()?;
self.expect(&token::Comma)?;
// Presumably, the majority of the time there will only be one attr.
let mut expanded_attrs = Vec::with_capacity(1);
while !self.check(&token::CloseDelim(token::Paren)) {
let lo = self.token.span.lo();
let item = self.parse_attr_item()?;
expanded_attrs.push((item, self.prev_span.with_lo(lo)));
self.expect_one_of(&[token::Comma], &[token::CloseDelim(token::Paren)])?;
}
self.expect(&token::CloseDelim(token::Paren))?;
Ok((cfg_predicate, expanded_attrs))
}
/// Matches the following grammar (per RFC 1559).
///
/// meta_item : PATH ( '=' UNSUFFIXED_LIT | '(' meta_item_inner? ')' )? ;
/// meta_item_inner : (meta_item | UNSUFFIXED_LIT) (',' meta_item_inner)? ;
pub fn parse_meta_item(&mut self) -> PResult<'a, ast::MetaItem> {
let nt_meta = match self.token.kind {
token::Interpolated(ref nt) => match **nt {
token::NtMeta(ref e) => Some(e.clone()),
_ => None,
},
_ => None,
};
if let Some(item) = nt_meta {
return match item.meta(item.path.span) {
Some(meta) => {
self.bump();
Ok(meta)
}
None => self.unexpected(),
}
}
let lo = self.token.span;
let path = self.parse_path(PathStyle::Mod)?;
let kind = self.parse_meta_item_kind()?;
let span = lo.to(self.prev_span);
Ok(ast::MetaItem { path, kind, span })
}
crate fn parse_meta_item_kind(&mut self) -> PResult<'a, ast::MetaItemKind> {
Ok(if self.eat(&token::Eq) {
ast::MetaItemKind::NameValue(self.parse_unsuffixed_lit()?)
} else if self.eat(&token::OpenDelim(token::Paren)) {
ast::MetaItemKind::List(self.parse_meta_seq()?)
} else {
ast::MetaItemKind::Word
})
}
/// Matches `meta_item_inner : (meta_item | UNSUFFIXED_LIT) ;`.
fn parse_meta_item_inner(&mut self) -> PResult<'a, ast::NestedMetaItem> {
match self.parse_unsuffixed_lit() {
Ok(lit) => {
return Ok(ast::NestedMetaItem::Literal(lit))
}
Err(ref mut err) => err.cancel(),
}
match self.parse_meta_item() {
Ok(mi) => {
return Ok(ast::NestedMetaItem::MetaItem(mi))
}
Err(ref mut err) => err.cancel(),
}
let found = self.this_token_to_string();
let msg = format!("expected unsuffixed literal or identifier, found `{}`", found);
Err(self.diagnostic().struct_span_err(self.token.span, &msg))
}
/// Matches `meta_seq = ( COMMASEP(meta_item_inner) )`.
fn parse_meta_seq(&mut self) -> PResult<'a, Vec<ast::NestedMetaItem>> {
self.parse_seq_to_end(&token::CloseDelim(token::Paren),
SeqSep::trailing_allowed(token::Comma),
|p: &mut Parser<'a>| p.parse_meta_item_inner())
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,308 @@
use super::Parser;
use syntax::ast::{self, WhereClause, GenericParam, GenericParamKind, GenericBounds, Attribute};
use syntax::token;
use syntax::source_map::DUMMY_SP;
use syntax_pos::symbol::{kw, sym};
use errors::PResult;
impl<'a> Parser<'a> {
/// Parses bounds of a lifetime parameter `BOUND + BOUND + BOUND`, possibly with trailing `+`.
///
/// ```
/// BOUND = LT_BOUND (e.g., `'a`)
/// ```
fn parse_lt_param_bounds(&mut self) -> GenericBounds {
let mut lifetimes = Vec::new();
while self.check_lifetime() {
lifetimes.push(ast::GenericBound::Outlives(self.expect_lifetime()));
if !self.eat_plus() {
break
}
}
lifetimes
}
/// Matches `typaram = IDENT (`?` unbound)? optbounds ( EQ ty )?`.
fn parse_ty_param(&mut self,
preceding_attrs: Vec<Attribute>)
-> PResult<'a, GenericParam> {
let ident = self.parse_ident()?;
// Parse optional colon and param bounds.
let bounds = if self.eat(&token::Colon) {
self.parse_generic_bounds(Some(self.prev_span))?
} else {
Vec::new()
};
let default = if self.eat(&token::Eq) {
Some(self.parse_ty()?)
} else {
None
};
Ok(GenericParam {
ident,
id: ast::DUMMY_NODE_ID,
attrs: preceding_attrs.into(),
bounds,
kind: GenericParamKind::Type {
default,
},
is_placeholder: false
})
}
fn parse_const_param(&mut self, preceding_attrs: Vec<Attribute>) -> PResult<'a, GenericParam> {
let lo = self.token.span;
self.expect_keyword(kw::Const)?;
let ident = self.parse_ident()?;
self.expect(&token::Colon)?;
let ty = self.parse_ty()?;
self.sess.gated_spans.gate(sym::const_generics, lo.to(self.prev_span));
Ok(GenericParam {
ident,
id: ast::DUMMY_NODE_ID,
attrs: preceding_attrs.into(),
bounds: Vec::new(),
kind: GenericParamKind::Const {
ty,
},
is_placeholder: false
})
}
/// Parses a (possibly empty) list of lifetime and type parameters, possibly including
/// a trailing comma and erroneous trailing attributes.
pub(super) fn parse_generic_params(&mut self) -> PResult<'a, Vec<ast::GenericParam>> {
let mut params = Vec::new();
loop {
let attrs = self.parse_outer_attributes()?;
if self.check_lifetime() {
let lifetime = self.expect_lifetime();
// Parse lifetime parameter.
let bounds = if self.eat(&token::Colon) {
self.parse_lt_param_bounds()
} else {
Vec::new()
};
params.push(ast::GenericParam {
ident: lifetime.ident,
id: lifetime.id,
attrs: attrs.into(),
bounds,
kind: ast::GenericParamKind::Lifetime,
is_placeholder: false
});
} else if self.check_keyword(kw::Const) {
// Parse const parameter.
params.push(self.parse_const_param(attrs)?);
} else if self.check_ident() {
// Parse type parameter.
params.push(self.parse_ty_param(attrs)?);
} else if self.token.can_begin_type() {
// Trying to write an associated type bound? (#26271)
let snapshot = self.clone();
match self.parse_ty_where_predicate() {
Ok(where_predicate) => {
self.struct_span_err(
where_predicate.span(),
"bounds on associated types do not belong here",
)
.span_label(where_predicate.span(), "belongs in `where` clause")
.emit();
}
Err(mut err) => {
err.cancel();
std::mem::replace(self, snapshot);
break
}
}
} else {
// Check for trailing attributes and stop parsing.
if !attrs.is_empty() {
if !params.is_empty() {
self.struct_span_err(
attrs[0].span,
"trailing attribute after generic parameter",
)
.span_label(attrs[0].span, "attributes must go before parameters")
.emit();
} else {
self.struct_span_err(
attrs[0].span,
&format!("attribute without generic parameters"),
)
.span_label(
attrs[0].span,
"attributes are only permitted when preceding parameters",
)
.emit();
}
}
break
}
if !self.eat(&token::Comma) {
break
}
}
Ok(params)
}
/// Parses a set of optional generic type parameter declarations. Where
/// clauses are not parsed here, and must be added later via
/// `parse_where_clause()`.
///
/// matches generics = ( ) | ( < > ) | ( < typaramseq ( , )? > ) | ( < lifetimes ( , )? > )
/// | ( < lifetimes , typaramseq ( , )? > )
/// where typaramseq = ( typaram ) | ( typaram , typaramseq )
pub(super) fn parse_generics(&mut self) -> PResult<'a, ast::Generics> {
let span_lo = self.token.span;
let (params, span) = if self.eat_lt() {
let params = self.parse_generic_params()?;
self.expect_gt()?;
(params, span_lo.to(self.prev_span))
} else {
(vec![], self.prev_span.between(self.token.span))
};
Ok(ast::Generics {
params,
where_clause: WhereClause {
predicates: Vec::new(),
span: DUMMY_SP,
},
span,
})
}
/// Parses an optional where-clause and places it in `generics`.
///
/// ```ignore (only-for-syntax-highlight)
/// where T : Trait<U, V> + 'b, 'a : 'b
/// ```
pub(super) fn parse_where_clause(&mut self) -> PResult<'a, WhereClause> {
let mut where_clause = WhereClause {
predicates: Vec::new(),
span: self.prev_span.to(self.prev_span),
};
if !self.eat_keyword(kw::Where) {
return Ok(where_clause);
}
let lo = self.prev_span;
// We are considering adding generics to the `where` keyword as an alternative higher-rank
// parameter syntax (as in `where<'a>` or `where<T>`. To avoid that being a breaking
// change we parse those generics now, but report an error.
if self.choose_generics_over_qpath() {
let generics = self.parse_generics()?;
self.struct_span_err(
generics.span,
"generic parameters on `where` clauses are reserved for future use",
)
.span_label(generics.span, "currently unsupported")
.emit();
}
loop {
let lo = self.token.span;
if self.check_lifetime() && self.look_ahead(1, |t| !t.is_like_plus()) {
let lifetime = self.expect_lifetime();
// Bounds starting with a colon are mandatory, but possibly empty.
self.expect(&token::Colon)?;
let bounds = self.parse_lt_param_bounds();
where_clause.predicates.push(ast::WherePredicate::RegionPredicate(
ast::WhereRegionPredicate {
span: lo.to(self.prev_span),
lifetime,
bounds,
}
));
} else if self.check_type() {
where_clause.predicates.push(self.parse_ty_where_predicate()?);
} else {
break
}
if !self.eat(&token::Comma) {
break
}
}
where_clause.span = lo.to(self.prev_span);
Ok(where_clause)
}
fn parse_ty_where_predicate(&mut self) -> PResult<'a, ast::WherePredicate> {
let lo = self.token.span;
// Parse optional `for<'a, 'b>`.
// This `for` is parsed greedily and applies to the whole predicate,
// the bounded type can have its own `for` applying only to it.
// Examples:
// * `for<'a> Trait1<'a>: Trait2<'a /* ok */>`
// * `(for<'a> Trait1<'a>): Trait2<'a /* not ok */>`
// * `for<'a> for<'b> Trait1<'a, 'b>: Trait2<'a /* ok */, 'b /* not ok */>`
let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
// Parse type with mandatory colon and (possibly empty) bounds,
// or with mandatory equality sign and the second type.
let ty = self.parse_ty()?;
if self.eat(&token::Colon) {
let bounds = self.parse_generic_bounds(Some(self.prev_span))?;
Ok(ast::WherePredicate::BoundPredicate(
ast::WhereBoundPredicate {
span: lo.to(self.prev_span),
bound_generic_params: lifetime_defs,
bounded_ty: ty,
bounds,
}
))
// FIXME: Decide what should be used here, `=` or `==`.
// FIXME: We are just dropping the binders in lifetime_defs on the floor here.
} else if self.eat(&token::Eq) || self.eat(&token::EqEq) {
let rhs_ty = self.parse_ty()?;
Ok(ast::WherePredicate::EqPredicate(
ast::WhereEqPredicate {
span: lo.to(self.prev_span),
lhs_ty: ty,
rhs_ty,
id: ast::DUMMY_NODE_ID,
}
))
} else {
self.unexpected()
}
}
pub(super) fn choose_generics_over_qpath(&self) -> bool {
// There's an ambiguity between generic parameters and qualified paths in impls.
// If we see `<` it may start both, so we have to inspect some following tokens.
// The following combinations can only start generics,
// but not qualified paths (with one exception):
// `<` `>` - empty generic parameters
// `<` `#` - generic parameters with attributes
// `<` (LIFETIME|IDENT) `>` - single generic parameter
// `<` (LIFETIME|IDENT) `,` - first generic parameter in a list
// `<` (LIFETIME|IDENT) `:` - generic parameter with bounds
// `<` (LIFETIME|IDENT) `=` - generic parameter with a default
// `<` const - generic const parameter
// The only truly ambiguous case is
// `<` IDENT `>` `::` IDENT ...
// we disambiguate it in favor of generics (`impl<T> ::absolute::Path<T> { ... }`)
// because this is what almost always expected in practice, qualified paths in impls
// (`impl <Type>::AssocTy { ... }`) aren't even allowed by type checker at the moment.
self.token == token::Lt &&
(self.look_ahead(1, |t| t == &token::Pound || t == &token::Gt) ||
self.look_ahead(1, |t| t.is_lifetime() || t.is_ident()) &&
self.look_ahead(2, |t| t == &token::Gt || t == &token::Comma ||
t == &token::Colon || t == &token::Eq) ||
self.is_keyword_ahead(1, &[kw::Const]))
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,316 @@
use super::Parser;
use super::item::ItemInfo;
use super::diagnostics::Error;
use crate::{new_sub_parser_from_file, DirectoryOwnership};
use syntax::attr;
use syntax::ast::{self, Ident, Attribute, ItemKind, Mod, Crate};
use syntax::token::{self, TokenKind};
use syntax::source_map::{SourceMap, Span, DUMMY_SP, FileName};
use syntax_pos::symbol::sym;
use errors::PResult;
use std::path::{self, Path, PathBuf};
/// Information about the path to a module.
pub(super) struct ModulePath {
name: String,
path_exists: bool,
pub result: Result<ModulePathSuccess, Error>,
}
pub(super) struct ModulePathSuccess {
pub path: PathBuf,
pub directory_ownership: DirectoryOwnership,
}
impl<'a> Parser<'a> {
/// Parses a source module as a crate. This is the main entry point for the parser.
pub fn parse_crate_mod(&mut self) -> PResult<'a, Crate> {
let lo = self.token.span;
let krate = Ok(ast::Crate {
attrs: self.parse_inner_attributes()?,
module: self.parse_mod_items(&token::Eof, lo)?,
span: lo.to(self.token.span),
});
krate
}
/// Parses a `mod <foo> { ... }` or `mod <foo>;` item.
pub(super) fn parse_item_mod(&mut self, outer_attrs: &[Attribute]) -> PResult<'a, ItemInfo> {
// HACK(Centril): See documentation on `ParseSess::process_cfg_mod`.
let (in_cfg, outer_attrs) = (self.sess.process_cfg_mod)(
self.sess,
self.cfg_mods,
outer_attrs,
);
let id_span = self.token.span;
let id = self.parse_ident()?;
if self.eat(&token::Semi) {
if in_cfg && self.recurse_into_file_modules {
// This mod is in an external file. Let's go get it!
let ModulePathSuccess { path, directory_ownership } =
self.submod_path(id, &outer_attrs, id_span)?;
let (module, attrs) =
self.eval_src_mod(path, directory_ownership, id.to_string(), id_span)?;
Ok((id, ItemKind::Mod(module), Some(attrs)))
} else {
let placeholder = ast::Mod {
inner: DUMMY_SP,
items: Vec::new(),
inline: false
};
Ok((id, ItemKind::Mod(placeholder), None))
}
} else {
let old_directory = self.directory.clone();
self.push_directory(id, &outer_attrs);
self.expect(&token::OpenDelim(token::Brace))?;
let mod_inner_lo = self.token.span;
let attrs = self.parse_inner_attributes()?;
let module = self.parse_mod_items(&token::CloseDelim(token::Brace), mod_inner_lo)?;
self.directory = old_directory;
Ok((id, ItemKind::Mod(module), Some(attrs)))
}
}
/// Given a termination token, parses all of the items in a module.
fn parse_mod_items(&mut self, term: &TokenKind, inner_lo: Span) -> PResult<'a, Mod> {
let mut items = vec![];
while let Some(item) = self.parse_item()? {
items.push(item);
self.maybe_consume_incorrect_semicolon(&items);
}
if !self.eat(term) {
let token_str = self.this_token_descr();
if !self.maybe_consume_incorrect_semicolon(&items) {
let mut err = self.fatal(&format!("expected item, found {}", token_str));
err.span_label(self.token.span, "expected item");
return Err(err);
}
}
let hi = if self.token.span.is_dummy() {
inner_lo
} else {
self.prev_span
};
Ok(Mod {
inner: inner_lo.to(hi),
items,
inline: true
})
}
fn submod_path(
&mut self,
id: ast::Ident,
outer_attrs: &[Attribute],
id_sp: Span
) -> PResult<'a, ModulePathSuccess> {
if let Some(path) = Parser::submod_path_from_attr(outer_attrs, &self.directory.path) {
return Ok(ModulePathSuccess {
directory_ownership: match path.file_name().and_then(|s| s.to_str()) {
// All `#[path]` files are treated as though they are a `mod.rs` file.
// This means that `mod foo;` declarations inside `#[path]`-included
// files are siblings,
//
// Note that this will produce weirdness when a file named `foo.rs` is
// `#[path]` included and contains a `mod foo;` declaration.
// If you encounter this, it's your own darn fault :P
Some(_) => DirectoryOwnership::Owned { relative: None },
_ => DirectoryOwnership::UnownedViaMod,
},
path,
});
}
let relative = match self.directory.ownership {
DirectoryOwnership::Owned { relative } => relative,
DirectoryOwnership::UnownedViaBlock |
DirectoryOwnership::UnownedViaMod => None,
};
let paths = Parser::default_submod_path(
id, relative, &self.directory.path, self.sess.source_map());
match self.directory.ownership {
DirectoryOwnership::Owned { .. } => {
paths.result.map_err(|err| self.span_fatal_err(id_sp, err))
},
DirectoryOwnership::UnownedViaBlock => {
let msg =
"Cannot declare a non-inline module inside a block \
unless it has a path attribute";
let mut err = self.diagnostic().struct_span_err(id_sp, msg);
if paths.path_exists {
let msg = format!("Maybe `use` the module `{}` instead of redeclaring it",
paths.name);
err.span_note(id_sp, &msg);
}
Err(err)
}
DirectoryOwnership::UnownedViaMod => {
let mut err = self.diagnostic().struct_span_err(id_sp,
"cannot declare a new module at this location");
if !id_sp.is_dummy() {
let src_path = self.sess.source_map().span_to_filename(id_sp);
if let FileName::Real(src_path) = src_path {
if let Some(stem) = src_path.file_stem() {
let mut dest_path = src_path.clone();
dest_path.set_file_name(stem);
dest_path.push("mod.rs");
err.span_note(id_sp,
&format!("maybe move this module `{}` to its own \
directory via `{}`", src_path.display(),
dest_path.display()));
}
}
}
if paths.path_exists {
err.span_note(id_sp,
&format!("... or maybe `use` the module `{}` instead \
of possibly redeclaring it",
paths.name));
}
Err(err)
}
}
}
pub(super) fn submod_path_from_attr(attrs: &[Attribute], dir_path: &Path) -> Option<PathBuf> {
if let Some(s) = attr::first_attr_value_str_by_name(attrs, sym::path) {
let s = s.as_str();
// On windows, the base path might have the form
// `\\?\foo\bar` in which case it does not tolerate
// mixed `/` and `\` separators, so canonicalize
// `/` to `\`.
#[cfg(windows)]
let s = s.replace("/", "\\");
Some(dir_path.join(&*s))
} else {
None
}
}
/// Returns a path to a module.
pub(super) fn default_submod_path(
id: ast::Ident,
relative: Option<ast::Ident>,
dir_path: &Path,
source_map: &SourceMap) -> ModulePath
{
// If we're in a foo.rs file instead of a mod.rs file,
// we need to look for submodules in
// `./foo/<id>.rs` and `./foo/<id>/mod.rs` rather than
// `./<id>.rs` and `./<id>/mod.rs`.
let relative_prefix_string;
let relative_prefix = if let Some(ident) = relative {
relative_prefix_string = format!("{}{}", ident, path::MAIN_SEPARATOR);
&relative_prefix_string
} else {
""
};
let mod_name = id.to_string();
let default_path_str = format!("{}{}.rs", relative_prefix, mod_name);
let secondary_path_str = format!("{}{}{}mod.rs",
relative_prefix, mod_name, path::MAIN_SEPARATOR);
let default_path = dir_path.join(&default_path_str);
let secondary_path = dir_path.join(&secondary_path_str);
let default_exists = source_map.file_exists(&default_path);
let secondary_exists = source_map.file_exists(&secondary_path);
let result = match (default_exists, secondary_exists) {
(true, false) => Ok(ModulePathSuccess {
path: default_path,
directory_ownership: DirectoryOwnership::Owned {
relative: Some(id),
},
}),
(false, true) => Ok(ModulePathSuccess {
path: secondary_path,
directory_ownership: DirectoryOwnership::Owned {
relative: None,
},
}),
(false, false) => Err(Error::FileNotFoundForModule {
mod_name: mod_name.clone(),
default_path: default_path_str,
secondary_path: secondary_path_str,
dir_path: dir_path.display().to_string(),
}),
(true, true) => Err(Error::DuplicatePaths {
mod_name: mod_name.clone(),
default_path: default_path_str,
secondary_path: secondary_path_str,
}),
};
ModulePath {
name: mod_name,
path_exists: default_exists || secondary_exists,
result,
}
}
/// Reads a module from a source file.
fn eval_src_mod(
&mut self,
path: PathBuf,
directory_ownership: DirectoryOwnership,
name: String,
id_sp: Span,
) -> PResult<'a, (Mod, Vec<Attribute>)> {
let mut included_mod_stack = self.sess.included_mod_stack.borrow_mut();
if let Some(i) = included_mod_stack.iter().position(|p| *p == path) {
let mut err = String::from("circular modules: ");
let len = included_mod_stack.len();
for p in &included_mod_stack[i.. len] {
err.push_str(&p.to_string_lossy());
err.push_str(" -> ");
}
err.push_str(&path.to_string_lossy());
return Err(self.span_fatal(id_sp, &err[..]));
}
included_mod_stack.push(path.clone());
drop(included_mod_stack);
let mut p0 =
new_sub_parser_from_file(self.sess, &path, directory_ownership, Some(name), id_sp);
p0.cfg_mods = self.cfg_mods;
let mod_inner_lo = p0.token.span;
let mod_attrs = p0.parse_inner_attributes()?;
let mut m0 = p0.parse_mod_items(&token::Eof, mod_inner_lo)?;
m0.inline = false;
self.sess.included_mod_stack.borrow_mut().pop();
Ok((m0, mod_attrs))
}
fn push_directory(&mut self, id: Ident, attrs: &[Attribute]) {
if let Some(path) = attr::first_attr_value_str_by_name(attrs, sym::path) {
self.directory.path.to_mut().push(&*path.as_str());
self.directory.ownership = DirectoryOwnership::Owned { relative: None };
} else {
// We have to push on the current module name in the case of relative
// paths in order to ensure that any additional module paths from inline
// `mod x { ... }` come after the relative extension.
//
// For example, a `mod z { ... }` inside `x/y.rs` should set the current
// directory path to `/x/y/z`, not `/x/z` with a relative offset of `y`.
if let DirectoryOwnership::Owned { relative } = &mut self.directory.ownership {
if let Some(ident) = relative.take() { // remove the relative offset
self.directory.path.to_mut().push(&*ident.as_str());
}
}
self.directory.path.to_mut().push(&*id.as_str());
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,497 @@
use super::{Parser, TokenType};
use crate::maybe_whole;
use syntax::ast::{self, QSelf, Path, PathSegment, Ident, ParenthesizedArgs, AngleBracketedArgs};
use syntax::ast::{AnonConst, GenericArg, AssocTyConstraint, AssocTyConstraintKind, BlockCheckMode};
use syntax::ThinVec;
use syntax::token::{self, Token};
use syntax::source_map::{Span, BytePos};
use syntax_pos::symbol::{kw, sym};
use std::mem;
use log::debug;
use errors::{PResult, Applicability, pluralize};
/// Specifies how to parse a path.
#[derive(Copy, Clone, PartialEq)]
pub enum PathStyle {
/// In some contexts, notably in expressions, paths with generic arguments are ambiguous
/// with something else. For example, in expressions `segment < ....` can be interpreted
/// as a comparison and `segment ( ....` can be interpreted as a function call.
/// In all such contexts the non-path interpretation is preferred by default for practical
/// reasons, but the path interpretation can be forced by the disambiguator `::`, e.g.
/// `x<y>` - comparisons, `x::<y>` - unambiguously a path.
Expr,
/// In other contexts, notably in types, no ambiguity exists and paths can be written
/// without the disambiguator, e.g., `x<y>` - unambiguously a path.
/// Paths with disambiguators are still accepted, `x::<Y>` - unambiguously a path too.
Type,
/// A path with generic arguments disallowed, e.g., `foo::bar::Baz`, used in imports,
/// visibilities or attributes.
/// Technically, this variant is unnecessary and e.g., `Expr` can be used instead
/// (paths in "mod" contexts have to be checked later for absence of generic arguments
/// anyway, due to macros), but it is used to avoid weird suggestions about expected
/// tokens when something goes wrong.
Mod,
}
impl<'a> Parser<'a> {
/// Parses a qualified path.
/// Assumes that the leading `<` has been parsed already.
///
/// `qualified_path = <type [as trait_ref]>::path`
///
/// # Examples
/// `<T>::default`
/// `<T as U>::a`
/// `<T as U>::F::a<S>` (without disambiguator)
/// `<T as U>::F::a::<S>` (with disambiguator)
pub(super) fn parse_qpath(&mut self, style: PathStyle) -> PResult<'a, (QSelf, Path)> {
let lo = self.prev_span;
let ty = self.parse_ty()?;
// `path` will contain the prefix of the path up to the `>`,
// if any (e.g., `U` in the `<T as U>::*` examples
// above). `path_span` has the span of that path, or an empty
// span in the case of something like `<T>::Bar`.
let (mut path, path_span);
if self.eat_keyword(kw::As) {
let path_lo = self.token.span;
path = self.parse_path(PathStyle::Type)?;
path_span = path_lo.to(self.prev_span);
} else {
path_span = self.token.span.to(self.token.span);
path = ast::Path { segments: Vec::new(), span: path_span };
}
// See doc comment for `unmatched_angle_bracket_count`.
self.expect(&token::Gt)?;
if self.unmatched_angle_bracket_count > 0 {
self.unmatched_angle_bracket_count -= 1;
debug!("parse_qpath: (decrement) count={:?}", self.unmatched_angle_bracket_count);
}
self.expect(&token::ModSep)?;
let qself = QSelf { ty, path_span, position: path.segments.len() };
self.parse_path_segments(&mut path.segments, style)?;
Ok((qself, Path { segments: path.segments, span: lo.to(self.prev_span) }))
}
/// Parses simple paths.
///
/// `path = [::] segment+`
/// `segment = ident | ident[::]<args> | ident[::](args) [-> type]`
///
/// # Examples
/// `a::b::C<D>` (without disambiguator)
/// `a::b::C::<D>` (with disambiguator)
/// `Fn(Args)` (without disambiguator)
/// `Fn::(Args)` (with disambiguator)
pub fn parse_path(&mut self, style: PathStyle) -> PResult<'a, Path> {
maybe_whole!(self, NtPath, |path| {
if style == PathStyle::Mod &&
path.segments.iter().any(|segment| segment.args.is_some()) {
self.diagnostic().span_err(path.span, "unexpected generic arguments in path");
}
path
});
let lo = self.meta_var_span.unwrap_or(self.token.span);
let mut segments = Vec::new();
let mod_sep_ctxt = self.token.span.ctxt();
if self.eat(&token::ModSep) {
segments.push(PathSegment::path_root(lo.shrink_to_lo().with_ctxt(mod_sep_ctxt)));
}
self.parse_path_segments(&mut segments, style)?;
Ok(Path { segments, span: lo.to(self.prev_span) })
}
/// Like `parse_path`, but also supports parsing `Word` meta items into paths for
/// backwards-compatibility. This is used when parsing derive macro paths in `#[derive]`
/// attributes.
fn parse_path_allowing_meta(&mut self, style: PathStyle) -> PResult<'a, Path> {
let meta_ident = match self.token.kind {
token::Interpolated(ref nt) => match **nt {
token::NtMeta(ref item) => match item.tokens.is_empty() {
true => Some(item.path.clone()),
false => None,
},
_ => None,
},
_ => None,
};
if let Some(path) = meta_ident {
self.bump();
return Ok(path);
}
self.parse_path(style)
}
/// Parse a list of paths inside `#[derive(path_0, ..., path_n)]`.
pub fn parse_derive_paths(&mut self) -> PResult<'a, Vec<Path>> {
self.expect(&token::OpenDelim(token::Paren))?;
let mut list = Vec::new();
while !self.eat(&token::CloseDelim(token::Paren)) {
let path = self.parse_path_allowing_meta(PathStyle::Mod)?;
list.push(path);
if !self.eat(&token::Comma) {
self.expect(&token::CloseDelim(token::Paren))?;
break
}
}
Ok(list)
}
pub(super) fn parse_path_segments(
&mut self,
segments: &mut Vec<PathSegment>,
style: PathStyle,
) -> PResult<'a, ()> {
loop {
let segment = self.parse_path_segment(style)?;
if style == PathStyle::Expr {
// In order to check for trailing angle brackets, we must have finished
// recursing (`parse_path_segment` can indirectly call this function),
// that is, the next token must be the highlighted part of the below example:
//
// `Foo::<Bar as Baz<T>>::Qux`
// ^ here
//
// As opposed to the below highlight (if we had only finished the first
// recursion):
//
// `Foo::<Bar as Baz<T>>::Qux`
// ^ here
//
// `PathStyle::Expr` is only provided at the root invocation and never in
// `parse_path_segment` to recurse and therefore can be checked to maintain
// this invariant.
self.check_trailing_angle_brackets(&segment, token::ModSep);
}
segments.push(segment);
if self.is_import_coupler() || !self.eat(&token::ModSep) {
return Ok(());
}
}
}
pub(super) fn parse_path_segment(&mut self, style: PathStyle) -> PResult<'a, PathSegment> {
let ident = self.parse_path_segment_ident()?;
let is_args_start = |token: &Token| match token.kind {
token::Lt | token::BinOp(token::Shl) | token::OpenDelim(token::Paren)
| token::LArrow => true,
_ => false,
};
let check_args_start = |this: &mut Self| {
this.expected_tokens.extend_from_slice(
&[TokenType::Token(token::Lt), TokenType::Token(token::OpenDelim(token::Paren))]
);
is_args_start(&this.token)
};
Ok(if style == PathStyle::Type && check_args_start(self) ||
style != PathStyle::Mod && self.check(&token::ModSep)
&& self.look_ahead(1, |t| is_args_start(t)) {
// We use `style == PathStyle::Expr` to check if this is in a recursion or not. If
// it isn't, then we reset the unmatched angle bracket count as we're about to start
// parsing a new path.
if style == PathStyle::Expr {
self.unmatched_angle_bracket_count = 0;
self.max_angle_bracket_count = 0;
}
// Generic arguments are found - `<`, `(`, `::<` or `::(`.
self.eat(&token::ModSep);
let lo = self.token.span;
let args = if self.eat_lt() {
// `<'a, T, A = U>`
let (args, constraints) =
self.parse_generic_args_with_leaning_angle_bracket_recovery(style, lo)?;
self.expect_gt()?;
let span = lo.to(self.prev_span);
AngleBracketedArgs { args, constraints, span }.into()
} else {
// `(T, U) -> R`
let (inputs, _) = self.parse_paren_comma_seq(|p| p.parse_ty())?;
let span = ident.span.to(self.prev_span);
let output = if self.eat(&token::RArrow) {
Some(self.parse_ty_common(false, false, false)?)
} else {
None
};
ParenthesizedArgs { inputs, output, span }.into()
};
PathSegment { ident, args, id: ast::DUMMY_NODE_ID }
} else {
// Generic arguments are not found.
PathSegment::from_ident(ident)
})
}
pub(super) fn parse_path_segment_ident(&mut self) -> PResult<'a, Ident> {
match self.token.kind {
token::Ident(name, _) if name.is_path_segment_keyword() => {
let span = self.token.span;
self.bump();
Ok(Ident::new(name, span))
}
_ => self.parse_ident(),
}
}
/// Parses generic args (within a path segment) with recovery for extra leading angle brackets.
/// For the purposes of understanding the parsing logic of generic arguments, this function
/// can be thought of being the same as just calling `self.parse_generic_args()` if the source
/// had the correct amount of leading angle brackets.
///
/// ```ignore (diagnostics)
/// bar::<<<<T as Foo>::Output>();
/// ^^ help: remove extra angle brackets
/// ```
fn parse_generic_args_with_leaning_angle_bracket_recovery(
&mut self,
style: PathStyle,
lo: Span,
) -> PResult<'a, (Vec<GenericArg>, Vec<AssocTyConstraint>)> {
// We need to detect whether there are extra leading left angle brackets and produce an
// appropriate error and suggestion. This cannot be implemented by looking ahead at
// upcoming tokens for a matching `>` character - if there are unmatched `<` tokens
// then there won't be matching `>` tokens to find.
//
// To explain how this detection works, consider the following example:
//
// ```ignore (diagnostics)
// bar::<<<<T as Foo>::Output>();
// ^^ help: remove extra angle brackets
// ```
//
// Parsing of the left angle brackets starts in this function. We start by parsing the
// `<` token (incrementing the counter of unmatched angle brackets on `Parser` via
// `eat_lt`):
//
// *Upcoming tokens:* `<<<<T as Foo>::Output>;`
// *Unmatched count:* 1
// *`parse_path_segment` calls deep:* 0
//
// This has the effect of recursing as this function is called if a `<` character
// is found within the expected generic arguments:
//
// *Upcoming tokens:* `<<<T as Foo>::Output>;`
// *Unmatched count:* 2
// *`parse_path_segment` calls deep:* 1
//
// Eventually we will have recursed until having consumed all of the `<` tokens and
// this will be reflected in the count:
//
// *Upcoming tokens:* `T as Foo>::Output>;`
// *Unmatched count:* 4
// `parse_path_segment` calls deep:* 3
//
// The parser will continue until reaching the first `>` - this will decrement the
// unmatched angle bracket count and return to the parent invocation of this function
// having succeeded in parsing:
//
// *Upcoming tokens:* `::Output>;`
// *Unmatched count:* 3
// *`parse_path_segment` calls deep:* 2
//
// This will continue until the next `>` character which will also return successfully
// to the parent invocation of this function and decrement the count:
//
// *Upcoming tokens:* `;`
// *Unmatched count:* 2
// *`parse_path_segment` calls deep:* 1
//
// At this point, this function will expect to find another matching `>` character but
// won't be able to and will return an error. This will continue all the way up the
// call stack until the first invocation:
//
// *Upcoming tokens:* `;`
// *Unmatched count:* 2
// *`parse_path_segment` calls deep:* 0
//
// In doing this, we have managed to work out how many unmatched leading left angle
// brackets there are, but we cannot recover as the unmatched angle brackets have
// already been consumed. To remedy this, we keep a snapshot of the parser state
// before we do the above. We can then inspect whether we ended up with a parsing error
// and unmatched left angle brackets and if so, restore the parser state before we
// consumed any `<` characters to emit an error and consume the erroneous tokens to
// recover by attempting to parse again.
//
// In practice, the recursion of this function is indirect and there will be other
// locations that consume some `<` characters - as long as we update the count when
// this happens, it isn't an issue.
let is_first_invocation = style == PathStyle::Expr;
// Take a snapshot before attempting to parse - we can restore this later.
let snapshot = if is_first_invocation {
Some(self.clone())
} else {
None
};
debug!("parse_generic_args_with_leading_angle_bracket_recovery: (snapshotting)");
match self.parse_generic_args() {
Ok(value) => Ok(value),
Err(ref mut e) if is_first_invocation && self.unmatched_angle_bracket_count > 0 => {
// Cancel error from being unable to find `>`. We know the error
// must have been this due to a non-zero unmatched angle bracket
// count.
e.cancel();
// Swap `self` with our backup of the parser state before attempting to parse
// generic arguments.
let snapshot = mem::replace(self, snapshot.unwrap());
debug!(
"parse_generic_args_with_leading_angle_bracket_recovery: (snapshot failure) \
snapshot.count={:?}",
snapshot.unmatched_angle_bracket_count,
);
// Eat the unmatched angle brackets.
for _ in 0..snapshot.unmatched_angle_bracket_count {
self.eat_lt();
}
// Make a span over ${unmatched angle bracket count} characters.
let span = lo.with_hi(
lo.lo() + BytePos(snapshot.unmatched_angle_bracket_count)
);
self.diagnostic()
.struct_span_err(
span,
&format!(
"unmatched angle bracket{}",
pluralize!(snapshot.unmatched_angle_bracket_count)
),
)
.span_suggestion(
span,
&format!(
"remove extra angle bracket{}",
pluralize!(snapshot.unmatched_angle_bracket_count)
),
String::new(),
Applicability::MachineApplicable,
)
.emit();
// Try again without unmatched angle bracket characters.
self.parse_generic_args()
},
Err(e) => Err(e),
}
}
/// Parses (possibly empty) list of lifetime and type arguments and associated type bindings,
/// possibly including trailing comma.
fn parse_generic_args(&mut self) -> PResult<'a, (Vec<GenericArg>, Vec<AssocTyConstraint>)> {
let mut args = Vec::new();
let mut constraints = Vec::new();
let mut misplaced_assoc_ty_constraints: Vec<Span> = Vec::new();
let mut assoc_ty_constraints: Vec<Span> = Vec::new();
let args_lo = self.token.span;
loop {
if self.check_lifetime() && self.look_ahead(1, |t| !t.is_like_plus()) {
// Parse lifetime argument.
args.push(GenericArg::Lifetime(self.expect_lifetime()));
misplaced_assoc_ty_constraints.append(&mut assoc_ty_constraints);
} else if self.check_ident()
&& self.look_ahead(1, |t| t == &token::Eq || t == &token::Colon)
{
// Parse associated type constraint.
let lo = self.token.span;
let ident = self.parse_ident()?;
let kind = if self.eat(&token::Eq) {
AssocTyConstraintKind::Equality {
ty: self.parse_ty()?,
}
} else if self.eat(&token::Colon) {
AssocTyConstraintKind::Bound {
bounds: self.parse_generic_bounds(Some(self.prev_span))?,
}
} else {
unreachable!();
};
let span = lo.to(self.prev_span);
// Gate associated type bounds, e.g., `Iterator<Item: Ord>`.
if let AssocTyConstraintKind::Bound { .. } = kind {
self.sess.gated_spans.gate(sym::associated_type_bounds, span);
}
constraints.push(AssocTyConstraint {
id: ast::DUMMY_NODE_ID,
ident,
kind,
span,
});
assoc_ty_constraints.push(span);
} else if self.check_const_arg() {
// Parse const argument.
let expr = if let token::OpenDelim(token::Brace) = self.token.kind {
self.parse_block_expr(
None, self.token.span, BlockCheckMode::Default, ThinVec::new()
)?
} else if self.token.is_ident() {
// FIXME(const_generics): to distinguish between idents for types and consts,
// we should introduce a GenericArg::Ident in the AST and distinguish when
// lowering to the HIR. For now, idents for const args are not permitted.
if self.token.is_bool_lit() {
self.parse_literal_maybe_minus()?
} else {
return Err(
self.fatal("identifiers may currently not be used for const generics")
);
}
} else {
self.parse_literal_maybe_minus()?
};
let value = AnonConst {
id: ast::DUMMY_NODE_ID,
value: expr,
};
args.push(GenericArg::Const(value));
misplaced_assoc_ty_constraints.append(&mut assoc_ty_constraints);
} else if self.check_type() {
// Parse type argument.
args.push(GenericArg::Type(self.parse_ty()?));
misplaced_assoc_ty_constraints.append(&mut assoc_ty_constraints);
} else {
break
}
if !self.eat(&token::Comma) {
break
}
}
// FIXME: we would like to report this in ast_validation instead, but we currently do not
// preserve ordering of generic parameters with respect to associated type binding, so we
// lose that information after parsing.
if misplaced_assoc_ty_constraints.len() > 0 {
let mut err = self.struct_span_err(
args_lo.to(self.prev_span),
"associated type bindings must be declared after generic parameters",
);
for span in misplaced_assoc_ty_constraints {
err.span_label(
span,
"this associated type binding should be moved after the generic parameters",
);
}
err.emit();
}
Ok((args, constraints))
}
}

View file

@ -0,0 +1,482 @@
use super::{Parser, Restrictions, PrevTokenKind, SemiColonMode, BlockMode};
use super::expr::LhsExpr;
use super::path::PathStyle;
use super::pat::GateOr;
use super::diagnostics::Error;
use crate::maybe_whole;
use crate::DirectoryOwnership;
use syntax::ThinVec;
use syntax::ptr::P;
use syntax::ast;
use syntax::ast::{DUMMY_NODE_ID, Stmt, StmtKind, Local, Block, BlockCheckMode, Expr, ExprKind};
use syntax::ast::{Attribute, AttrStyle, VisibilityKind, MacStmtStyle, Mac, MacDelimiter};
use syntax::util::classify;
use syntax::token;
use syntax::source_map::{respan, Span};
use syntax::symbol::{kw, sym};
use std::mem;
use errors::{PResult, Applicability};
impl<'a> Parser<'a> {
/// Parses a statement. This stops just before trailing semicolons on everything but items.
/// e.g., a `StmtKind::Semi` parses to a `StmtKind::Expr`, leaving the trailing `;` unconsumed.
pub fn parse_stmt(&mut self) -> PResult<'a, Option<Stmt>> {
Ok(self.parse_stmt_(true))
}
fn parse_stmt_(&mut self, macro_legacy_warnings: bool) -> Option<Stmt> {
self.parse_stmt_without_recovery(macro_legacy_warnings).unwrap_or_else(|mut e| {
e.emit();
self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore);
None
})
}
fn parse_stmt_without_recovery(
&mut self,
macro_legacy_warnings: bool,
) -> PResult<'a, Option<Stmt>> {
maybe_whole!(self, NtStmt, |x| Some(x));
let attrs = self.parse_outer_attributes()?;
let lo = self.token.span;
Ok(Some(if self.eat_keyword(kw::Let) {
Stmt {
id: DUMMY_NODE_ID,
kind: StmtKind::Local(self.parse_local(attrs.into())?),
span: lo.to(self.prev_span),
}
} else if let Some(macro_def) = self.eat_macro_def(
&attrs,
&respan(lo, VisibilityKind::Inherited),
lo,
)? {
Stmt {
id: DUMMY_NODE_ID,
kind: StmtKind::Item(macro_def),
span: lo.to(self.prev_span),
}
// Starts like a simple path, being careful to avoid contextual keywords
// such as a union items, item with `crate` visibility or auto trait items.
// Our goal here is to parse an arbitrary path `a::b::c` but not something that starts
// like a path (1 token), but it fact not a path.
// `union::b::c` - path, `union U { ... }` - not a path.
// `crate::b::c` - path, `crate struct S;` - not a path.
} else if self.token.is_path_start() &&
!self.token.is_qpath_start() &&
!self.is_union_item() &&
!self.is_crate_vis() &&
!self.is_auto_trait_item() &&
!self.is_async_fn() {
let path = self.parse_path(PathStyle::Expr)?;
if !self.eat(&token::Not) {
let expr = if self.check(&token::OpenDelim(token::Brace)) {
self.parse_struct_expr(lo, path, ThinVec::new())?
} else {
let hi = self.prev_span;
self.mk_expr(lo.to(hi), ExprKind::Path(None, path), ThinVec::new())
};
let expr = self.with_res(Restrictions::STMT_EXPR, |this| {
let expr = this.parse_dot_or_call_expr_with(expr, lo, attrs.into())?;
this.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(expr))
})?;
return Ok(Some(Stmt {
id: DUMMY_NODE_ID,
kind: StmtKind::Expr(expr),
span: lo.to(self.prev_span),
}));
}
let (delim, tts) = self.expect_delimited_token_tree()?;
let hi = self.prev_span;
let style = if delim == MacDelimiter::Brace {
MacStmtStyle::Braces
} else {
MacStmtStyle::NoBraces
};
let mac = Mac {
path,
tts,
delim,
span: lo.to(hi),
prior_type_ascription: self.last_type_ascription,
};
let kind = if delim == MacDelimiter::Brace ||
self.token == token::Semi || self.token == token::Eof {
StmtKind::Mac(P((mac, style, attrs.into())))
}
// We used to incorrectly stop parsing macro-expanded statements here.
// If the next token will be an error anyway but could have parsed with the
// earlier behavior, stop parsing here and emit a warning to avoid breakage.
else if macro_legacy_warnings && self.token.can_begin_expr() &&
match self.token.kind {
// These can continue an expression, so we can't stop parsing and warn.
token::OpenDelim(token::Paren) | token::OpenDelim(token::Bracket) |
token::BinOp(token::Minus) | token::BinOp(token::Star) |
token::BinOp(token::And) | token::BinOp(token::Or) |
token::AndAnd | token::OrOr |
token::DotDot | token::DotDotDot | token::DotDotEq => false,
_ => true,
}
{
self.warn_missing_semicolon();
StmtKind::Mac(P((mac, style, attrs.into())))
} else {
let e = self.mk_expr(mac.span, ExprKind::Mac(mac), ThinVec::new());
let e = self.maybe_recover_from_bad_qpath(e, true)?;
let e = self.parse_dot_or_call_expr_with(e, lo, attrs.into())?;
let e = self.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(e))?;
StmtKind::Expr(e)
};
Stmt {
id: DUMMY_NODE_ID,
span: lo.to(hi),
kind,
}
} else {
// FIXME: Bad copy of attrs
let old_directory_ownership =
mem::replace(&mut self.directory.ownership, DirectoryOwnership::UnownedViaBlock);
let item = self.parse_item_(attrs.clone(), false, true)?;
self.directory.ownership = old_directory_ownership;
match item {
Some(i) => Stmt {
id: DUMMY_NODE_ID,
span: lo.to(i.span),
kind: StmtKind::Item(i),
},
None => {
let unused_attrs = |attrs: &[Attribute], s: &mut Self| {
if !attrs.is_empty() {
if s.prev_token_kind == PrevTokenKind::DocComment {
s.span_fatal_err(s.prev_span, Error::UselessDocComment).emit();
} else if attrs.iter().any(|a| a.style == AttrStyle::Outer) {
s.span_err(
s.token.span, "expected statement after outer attribute"
);
}
}
};
// Do not attempt to parse an expression if we're done here.
if self.token == token::Semi {
unused_attrs(&attrs, self);
self.bump();
let mut last_semi = lo;
while self.token == token::Semi {
last_semi = self.token.span;
self.bump();
}
// We are encoding a string of semicolons as an
// an empty tuple that spans the excess semicolons
// to preserve this info until the lint stage
return Ok(Some(Stmt {
id: DUMMY_NODE_ID,
span: lo.to(last_semi),
kind: StmtKind::Semi(self.mk_expr(lo.to(last_semi),
ExprKind::Tup(Vec::new()),
ThinVec::new()
)),
}));
}
if self.token == token::CloseDelim(token::Brace) {
unused_attrs(&attrs, self);
return Ok(None);
}
// Remainder are line-expr stmts.
let e = self.parse_expr_res(
Restrictions::STMT_EXPR, Some(attrs.into()))?;
Stmt {
id: DUMMY_NODE_ID,
span: lo.to(e.span),
kind: StmtKind::Expr(e),
}
}
}
}))
}
/// Parses a local variable declaration.
fn parse_local(&mut self, attrs: ThinVec<Attribute>) -> PResult<'a, P<Local>> {
let lo = self.prev_span;
let pat = self.parse_top_pat(GateOr::Yes)?;
let (err, ty) = if self.eat(&token::Colon) {
// Save the state of the parser before parsing type normally, in case there is a `:`
// instead of an `=` typo.
let parser_snapshot_before_type = self.clone();
let colon_sp = self.prev_span;
match self.parse_ty() {
Ok(ty) => (None, Some(ty)),
Err(mut err) => {
// Rewind to before attempting to parse the type and continue parsing.
let parser_snapshot_after_type = self.clone();
mem::replace(self, parser_snapshot_before_type);
let snippet = self.span_to_snippet(pat.span).unwrap();
err.span_label(pat.span, format!("while parsing the type for `{}`", snippet));
(Some((parser_snapshot_after_type, colon_sp, err)), None)
}
}
} else {
(None, None)
};
let init = match (self.parse_initializer(err.is_some()), err) {
(Ok(init), None) => { // init parsed, ty parsed
init
}
(Ok(init), Some((_, colon_sp, mut err))) => { // init parsed, ty error
// Could parse the type as if it were the initializer, it is likely there was a
// typo in the code: `:` instead of `=`. Add suggestion and emit the error.
err.span_suggestion_short(
colon_sp,
"use `=` if you meant to assign",
" =".to_string(),
Applicability::MachineApplicable
);
err.emit();
// As this was parsed successfully, continue as if the code has been fixed for the
// rest of the file. It will still fail due to the emitted error, but we avoid
// extra noise.
init
}
(Err(mut init_err), Some((snapshot, _, ty_err))) => { // init error, ty error
init_err.cancel();
// Couldn't parse the type nor the initializer, only raise the type error and
// return to the parser state before parsing the type as the initializer.
// let x: <parse_error>;
mem::replace(self, snapshot);
return Err(ty_err);
}
(Err(err), None) => { // init error, ty parsed
// Couldn't parse the initializer and we're not attempting to recover a failed
// parse of the type, return the error.
return Err(err);
}
};
let hi = if self.token == token::Semi {
self.token.span
} else {
self.prev_span
};
Ok(P(ast::Local {
ty,
pat,
init,
id: DUMMY_NODE_ID,
span: lo.to(hi),
attrs,
}))
}
/// Parses the RHS of a local variable declaration (e.g., '= 14;').
fn parse_initializer(&mut self, skip_eq: bool) -> PResult<'a, Option<P<Expr>>> {
if self.eat(&token::Eq) {
Ok(Some(self.parse_expr()?))
} else if skip_eq {
Ok(Some(self.parse_expr()?))
} else {
Ok(None)
}
}
fn is_auto_trait_item(&self) -> bool {
// auto trait
(self.token.is_keyword(kw::Auto) &&
self.is_keyword_ahead(1, &[kw::Trait]))
|| // unsafe auto trait
(self.token.is_keyword(kw::Unsafe) &&
self.is_keyword_ahead(1, &[kw::Auto]) &&
self.is_keyword_ahead(2, &[kw::Trait]))
}
/// Parses a block. No inner attributes are allowed.
pub fn parse_block(&mut self) -> PResult<'a, P<Block>> {
maybe_whole!(self, NtBlock, |x| x);
let lo = self.token.span;
if !self.eat(&token::OpenDelim(token::Brace)) {
let sp = self.token.span;
let tok = self.this_token_descr();
let mut e = self.span_fatal(sp, &format!("expected `{{`, found {}", tok));
let do_not_suggest_help =
self.token.is_keyword(kw::In) || self.token == token::Colon;
if self.token.is_ident_named(sym::and) {
e.span_suggestion_short(
self.token.span,
"use `&&` instead of `and` for the boolean operator",
"&&".to_string(),
Applicability::MaybeIncorrect,
);
}
if self.token.is_ident_named(sym::or) {
e.span_suggestion_short(
self.token.span,
"use `||` instead of `or` for the boolean operator",
"||".to_string(),
Applicability::MaybeIncorrect,
);
}
// Check to see if the user has written something like
//
// if (cond)
// bar;
//
// which is valid in other languages, but not Rust.
match self.parse_stmt_without_recovery(false) {
Ok(Some(stmt)) => {
if self.look_ahead(1, |t| t == &token::OpenDelim(token::Brace))
|| do_not_suggest_help {
// If the next token is an open brace (e.g., `if a b {`), the place-
// inside-a-block suggestion would be more likely wrong than right.
e.span_label(sp, "expected `{`");
return Err(e);
}
let mut stmt_span = stmt.span;
// Expand the span to include the semicolon, if it exists.
if self.eat(&token::Semi) {
stmt_span = stmt_span.with_hi(self.prev_span.hi());
}
if let Ok(snippet) = self.span_to_snippet(stmt_span) {
e.span_suggestion(
stmt_span,
"try placing this code inside a block",
format!("{{ {} }}", snippet),
// Speculative; has been misleading in the past (#46836).
Applicability::MaybeIncorrect,
);
}
}
Err(mut e) => {
self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore);
e.cancel();
}
_ => ()
}
e.span_label(sp, "expected `{`");
return Err(e);
}
self.parse_block_tail(lo, BlockCheckMode::Default)
}
/// Parses a block. Inner attributes are allowed.
pub(super) fn parse_inner_attrs_and_block(
&mut self
) -> PResult<'a, (Vec<Attribute>, P<Block>)> {
maybe_whole!(self, NtBlock, |x| (Vec::new(), x));
let lo = self.token.span;
self.expect(&token::OpenDelim(token::Brace))?;
Ok((self.parse_inner_attributes()?,
self.parse_block_tail(lo, BlockCheckMode::Default)?))
}
/// Parses the rest of a block expression or function body.
/// Precondition: already parsed the '{'.
pub(super) fn parse_block_tail(
&mut self,
lo: Span,
s: BlockCheckMode
) -> PResult<'a, P<Block>> {
let mut stmts = vec![];
while !self.eat(&token::CloseDelim(token::Brace)) {
if self.token == token::Eof {
break;
}
let stmt = match self.parse_full_stmt(false) {
Err(mut err) => {
self.maybe_annotate_with_ascription(&mut err, false);
err.emit();
self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore);
Some(Stmt {
id: DUMMY_NODE_ID,
kind: StmtKind::Expr(self.mk_expr_err(self.token.span)),
span: self.token.span,
})
}
Ok(stmt) => stmt,
};
if let Some(stmt) = stmt {
stmts.push(stmt);
} else {
// Found only `;` or `}`.
continue;
};
}
Ok(P(ast::Block {
stmts,
id: DUMMY_NODE_ID,
rules: s,
span: lo.to(self.prev_span),
}))
}
/// Parses a statement, including the trailing semicolon.
pub fn parse_full_stmt(&mut self, macro_legacy_warnings: bool) -> PResult<'a, Option<Stmt>> {
// Skip looking for a trailing semicolon when we have an interpolated statement.
maybe_whole!(self, NtStmt, |x| Some(x));
let mut stmt = match self.parse_stmt_without_recovery(macro_legacy_warnings)? {
Some(stmt) => stmt,
None => return Ok(None),
};
let mut eat_semi = true;
match stmt.kind {
StmtKind::Expr(ref expr) if self.token != token::Eof => {
// expression without semicolon
if classify::expr_requires_semi_to_be_stmt(expr) {
// Just check for errors and recover; do not eat semicolon yet.
if let Err(mut e) =
self.expect_one_of(&[], &[token::Semi, token::CloseDelim(token::Brace)])
{
e.emit();
self.recover_stmt();
// Don't complain about type errors in body tail after parse error (#57383).
let sp = expr.span.to(self.prev_span);
stmt.kind = StmtKind::Expr(self.mk_expr_err(sp));
}
}
}
StmtKind::Local(..) => {
// We used to incorrectly allow a macro-expanded let statement to lack a semicolon.
if macro_legacy_warnings && self.token != token::Semi {
self.warn_missing_semicolon();
} else {
self.expect_semi()?;
eat_semi = false;
}
}
_ => {}
}
if eat_semi && self.eat(&token::Semi) {
stmt = stmt.add_trailing_semicolon();
}
stmt.span = stmt.span.to(self.prev_span);
Ok(Some(stmt))
}
fn warn_missing_semicolon(&self) {
self.diagnostic().struct_span_warn(self.token.span, {
&format!("expected `;`, found {}", self.this_token_descr())
}).note({
"this was erroneously allowed and will become a hard error in a future release"
}).emit();
}
}

View file

@ -0,0 +1,460 @@
use super::{Parser, PathStyle, PrevTokenKind, TokenType};
use super::item::ParamCfg;
use crate::{maybe_whole, maybe_recover_from_interpolated_ty_qpath};
use syntax::ptr::P;
use syntax::ast::{self, Ty, TyKind, MutTy, BareFnTy, FunctionRetTy, GenericParam, Lifetime, Ident};
use syntax::ast::{TraitBoundModifier, TraitObjectSyntax, GenericBound, GenericBounds, PolyTraitRef};
use syntax::ast::{Mutability, AnonConst, Mac};
use syntax::token::{self, Token};
use syntax::source_map::Span;
use syntax::struct_span_fatal;
use syntax_pos::symbol::kw;
use errors::{PResult, Applicability, pluralize};
/// Returns `true` if `IDENT t` can start a type -- `IDENT::a::b`, `IDENT<u8, u8>`,
/// `IDENT<<u8 as Trait>::AssocTy>`.
///
/// Types can also be of the form `IDENT(u8, u8) -> u8`, however this assumes
/// that `IDENT` is not the ident of a fn trait.
fn can_continue_type_after_non_fn_ident(t: &Token) -> bool {
t == &token::ModSep || t == &token::Lt ||
t == &token::BinOp(token::Shl)
}
impl<'a> Parser<'a> {
/// Parses a type.
pub fn parse_ty(&mut self) -> PResult<'a, P<Ty>> {
self.parse_ty_common(true, true, false)
}
/// Parses a type in restricted contexts where `+` is not permitted.
///
/// Example 1: `&'a TYPE`
/// `+` is prohibited to maintain operator priority (P(+) < P(&)).
/// Example 2: `value1 as TYPE + value2`
/// `+` is prohibited to avoid interactions with expression grammar.
pub(super) fn parse_ty_no_plus(&mut self) -> PResult<'a, P<Ty>> {
self.parse_ty_common(false, true, false)
}
/// Parses an optional return type `[ -> TY ]` in a function declaration.
pub(super) fn parse_ret_ty(&mut self, allow_plus: bool) -> PResult<'a, FunctionRetTy> {
if self.eat(&token::RArrow) {
Ok(FunctionRetTy::Ty(self.parse_ty_common(allow_plus, true, false)?))
} else {
Ok(FunctionRetTy::Default(self.token.span.shrink_to_lo()))
}
}
pub(super) fn parse_ty_common(&mut self, allow_plus: bool, allow_qpath_recovery: bool,
allow_c_variadic: bool) -> PResult<'a, P<Ty>> {
maybe_recover_from_interpolated_ty_qpath!(self, allow_qpath_recovery);
maybe_whole!(self, NtTy, |x| x);
let lo = self.token.span;
let mut impl_dyn_multi = false;
let kind = if self.eat(&token::OpenDelim(token::Paren)) {
// `(TYPE)` is a parenthesized type.
// `(TYPE,)` is a tuple with a single field of type TYPE.
let mut ts = vec![];
let mut last_comma = false;
while self.token != token::CloseDelim(token::Paren) {
ts.push(self.parse_ty()?);
if self.eat(&token::Comma) {
last_comma = true;
} else {
last_comma = false;
break;
}
}
let trailing_plus = self.prev_token_kind == PrevTokenKind::Plus;
self.expect(&token::CloseDelim(token::Paren))?;
if ts.len() == 1 && !last_comma {
let ty = ts.into_iter().nth(0).unwrap().into_inner();
let maybe_bounds = allow_plus && self.token.is_like_plus();
match ty.kind {
// `(TY_BOUND_NOPAREN) + BOUND + ...`.
TyKind::Path(None, ref path) if maybe_bounds => {
self.parse_remaining_bounds(Vec::new(), path.clone(), lo, true)?
}
TyKind::TraitObject(ref bounds, TraitObjectSyntax::None)
if maybe_bounds && bounds.len() == 1 && !trailing_plus => {
let path = match bounds[0] {
GenericBound::Trait(ref pt, ..) => pt.trait_ref.path.clone(),
GenericBound::Outlives(..) => self.bug("unexpected lifetime bound"),
};
self.parse_remaining_bounds(Vec::new(), path, lo, true)?
}
// `(TYPE)`
_ => TyKind::Paren(P(ty))
}
} else {
TyKind::Tup(ts)
}
} else if self.eat(&token::Not) {
// Never type `!`
TyKind::Never
} else if self.eat(&token::BinOp(token::Star)) {
// Raw pointer
TyKind::Ptr(self.parse_ptr()?)
} else if self.eat(&token::OpenDelim(token::Bracket)) {
// Array or slice
let t = self.parse_ty()?;
// Parse optional `; EXPR` in `[TYPE; EXPR]`
let t = match self.maybe_parse_fixed_length_of_vec()? {
None => TyKind::Slice(t),
Some(length) => TyKind::Array(t, AnonConst {
id: ast::DUMMY_NODE_ID,
value: length,
}),
};
self.expect(&token::CloseDelim(token::Bracket))?;
t
} else if self.check(&token::BinOp(token::And)) || self.check(&token::AndAnd) {
// Reference
self.expect_and()?;
self.parse_borrowed_pointee()?
} else if self.eat_keyword_noexpect(kw::Typeof) {
// `typeof(EXPR)`
// In order to not be ambiguous, the type must be surrounded by parens.
self.expect(&token::OpenDelim(token::Paren))?;
let e = AnonConst {
id: ast::DUMMY_NODE_ID,
value: self.parse_expr()?,
};
self.expect(&token::CloseDelim(token::Paren))?;
TyKind::Typeof(e)
} else if self.eat_keyword(kw::Underscore) {
// A type to be inferred `_`
TyKind::Infer
} else if self.token_is_bare_fn_keyword() {
// Function pointer type
self.parse_ty_bare_fn(Vec::new())?
} else if self.check_keyword(kw::For) {
// Function pointer type or bound list (trait object type) starting with a poly-trait.
// `for<'lt> [unsafe] [extern "ABI"] fn (&'lt S) -> T`
// `for<'lt> Trait1<'lt> + Trait2 + 'a`
let lo = self.token.span;
let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
if self.token_is_bare_fn_keyword() {
self.parse_ty_bare_fn(lifetime_defs)?
} else {
let path = self.parse_path(PathStyle::Type)?;
let parse_plus = allow_plus && self.check_plus();
self.parse_remaining_bounds(lifetime_defs, path, lo, parse_plus)?
}
} else if self.eat_keyword(kw::Impl) {
// Always parse bounds greedily for better error recovery.
let bounds = self.parse_generic_bounds(None)?;
impl_dyn_multi = bounds.len() > 1 || self.prev_token_kind == PrevTokenKind::Plus;
TyKind::ImplTrait(ast::DUMMY_NODE_ID, bounds)
} else if self.check_keyword(kw::Dyn) &&
(self.token.span.rust_2018() ||
self.look_ahead(1, |t| t.can_begin_bound() &&
!can_continue_type_after_non_fn_ident(t))) {
self.bump(); // `dyn`
// Always parse bounds greedily for better error recovery.
let bounds = self.parse_generic_bounds(None)?;
impl_dyn_multi = bounds.len() > 1 || self.prev_token_kind == PrevTokenKind::Plus;
TyKind::TraitObject(bounds, TraitObjectSyntax::Dyn)
} else if self.check(&token::Question) ||
self.check_lifetime() && self.look_ahead(1, |t| t.is_like_plus()) {
// Bound list (trait object type)
TyKind::TraitObject(self.parse_generic_bounds_common(allow_plus, None)?,
TraitObjectSyntax::None)
} else if self.eat_lt() {
// Qualified path
let (qself, path) = self.parse_qpath(PathStyle::Type)?;
TyKind::Path(Some(qself), path)
} else if self.token.is_path_start() {
// Simple path
let path = self.parse_path(PathStyle::Type)?;
if self.eat(&token::Not) {
// Macro invocation in type position
let (delim, tts) = self.expect_delimited_token_tree()?;
let mac = Mac {
path,
tts,
delim,
span: lo.to(self.prev_span),
prior_type_ascription: self.last_type_ascription,
};
TyKind::Mac(mac)
} else {
// Just a type path or bound list (trait object type) starting with a trait.
// `Type`
// `Trait1 + Trait2 + 'a`
if allow_plus && self.check_plus() {
self.parse_remaining_bounds(Vec::new(), path, lo, true)?
} else {
TyKind::Path(None, path)
}
}
} else if self.check(&token::DotDotDot) {
if allow_c_variadic {
self.eat(&token::DotDotDot);
TyKind::CVarArgs
} else {
return Err(struct_span_fatal!(
self.sess.span_diagnostic,
self.token.span,
E0743,
"only foreign functions are allowed to be C-variadic",
));
}
} else {
let msg = format!("expected type, found {}", self.this_token_descr());
let mut err = self.fatal(&msg);
err.span_label(self.token.span, "expected type");
self.maybe_annotate_with_ascription(&mut err, true);
return Err(err);
};
let span = lo.to(self.prev_span);
let ty = self.mk_ty(span, kind);
// Try to recover from use of `+` with incorrect priority.
self.maybe_report_ambiguous_plus(allow_plus, impl_dyn_multi, &ty);
self.maybe_recover_from_bad_type_plus(allow_plus, &ty)?;
self.maybe_recover_from_bad_qpath(ty, allow_qpath_recovery)
}
fn parse_remaining_bounds(&mut self, generic_params: Vec<GenericParam>, path: ast::Path,
lo: Span, parse_plus: bool) -> PResult<'a, TyKind> {
let poly_trait_ref = PolyTraitRef::new(generic_params, path, lo.to(self.prev_span));
let mut bounds = vec![GenericBound::Trait(poly_trait_ref, TraitBoundModifier::None)];
if parse_plus {
self.eat_plus(); // `+`, or `+=` gets split and `+` is discarded
bounds.append(&mut self.parse_generic_bounds(Some(self.prev_span))?);
}
Ok(TyKind::TraitObject(bounds, TraitObjectSyntax::None))
}
fn parse_ptr(&mut self) -> PResult<'a, MutTy> {
let mutbl = self.parse_const_or_mut().unwrap_or_else(|| {
let span = self.prev_span;
let msg = "expected mut or const in raw pointer type";
self.struct_span_err(span, msg)
.span_label(span, msg)
.help("use `*mut T` or `*const T` as appropriate")
.emit();
Mutability::Immutable
});
let t = self.parse_ty_no_plus()?;
Ok(MutTy { ty: t, mutbl })
}
fn maybe_parse_fixed_length_of_vec(&mut self) -> PResult<'a, Option<P<ast::Expr>>> {
if self.eat(&token::Semi) {
Ok(Some(self.parse_expr()?))
} else {
Ok(None)
}
}
fn parse_borrowed_pointee(&mut self) -> PResult<'a, TyKind> {
let opt_lifetime = if self.check_lifetime() { Some(self.expect_lifetime()) } else { None };
let mutbl = self.parse_mutability();
let ty = self.parse_ty_no_plus()?;
return Ok(TyKind::Rptr(opt_lifetime, MutTy { ty, mutbl }));
}
/// Is the current token one of the keywords that signals a bare function type?
fn token_is_bare_fn_keyword(&mut self) -> bool {
self.check_keyword(kw::Fn) ||
self.check_keyword(kw::Unsafe) ||
self.check_keyword(kw::Extern)
}
/// Parses a `TyKind::BareFn` type.
fn parse_ty_bare_fn(&mut self, generic_params: Vec<GenericParam>) -> PResult<'a, TyKind> {
/*
[unsafe] [extern "ABI"] fn (S) -> T
^~~~^ ^~~~^ ^~^ ^
| | | |
| | | Return type
| | Argument types
| |
| ABI
Function Style
*/
let unsafety = self.parse_unsafety();
let abi = self.parse_extern_abi()?;
self.expect_keyword(kw::Fn)?;
let cfg = ParamCfg {
is_self_allowed: false,
allow_c_variadic: true,
is_name_required: |_| false,
};
let decl = self.parse_fn_decl(cfg, false)?;
Ok(TyKind::BareFn(P(BareFnTy {
abi,
unsafety,
generic_params,
decl,
})))
}
pub(super) fn parse_generic_bounds(&mut self,
colon_span: Option<Span>) -> PResult<'a, GenericBounds> {
self.parse_generic_bounds_common(true, colon_span)
}
/// Parses bounds of a type parameter `BOUND + BOUND + ...`, possibly with trailing `+`.
///
/// ```
/// BOUND = TY_BOUND | LT_BOUND
/// LT_BOUND = LIFETIME (e.g., `'a`)
/// TY_BOUND = TY_BOUND_NOPAREN | (TY_BOUND_NOPAREN)
/// TY_BOUND_NOPAREN = [?] [for<LT_PARAM_DEFS>] SIMPLE_PATH (e.g., `?for<'a: 'b> m::Trait<'a>`)
/// ```
fn parse_generic_bounds_common(&mut self,
allow_plus: bool,
colon_span: Option<Span>) -> PResult<'a, GenericBounds> {
let mut bounds = Vec::new();
let mut negative_bounds = Vec::new();
let mut last_plus_span = None;
let mut was_negative = false;
loop {
// This needs to be synchronized with `TokenKind::can_begin_bound`.
let is_bound_start = self.check_path() || self.check_lifetime() ||
self.check(&token::Not) || // used for error reporting only
self.check(&token::Question) ||
self.check_keyword(kw::For) ||
self.check(&token::OpenDelim(token::Paren));
if is_bound_start {
let lo = self.token.span;
let has_parens = self.eat(&token::OpenDelim(token::Paren));
let inner_lo = self.token.span;
let is_negative = self.eat(&token::Not);
let question = if self.eat(&token::Question) { Some(self.prev_span) } else { None };
if self.token.is_lifetime() {
if let Some(question_span) = question {
self.span_err(question_span,
"`?` may only modify trait bounds, not lifetime bounds");
}
bounds.push(GenericBound::Outlives(self.expect_lifetime()));
if has_parens {
let inner_span = inner_lo.to(self.prev_span);
self.expect(&token::CloseDelim(token::Paren))?;
let mut err = self.struct_span_err(
lo.to(self.prev_span),
"parenthesized lifetime bounds are not supported"
);
if let Ok(snippet) = self.span_to_snippet(inner_span) {
err.span_suggestion_short(
lo.to(self.prev_span),
"remove the parentheses",
snippet.to_owned(),
Applicability::MachineApplicable
);
}
err.emit();
}
} else {
let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
let path = self.parse_path(PathStyle::Type)?;
if has_parens {
self.expect(&token::CloseDelim(token::Paren))?;
}
let poly_span = lo.to(self.prev_span);
if is_negative {
was_negative = true;
if let Some(sp) = last_plus_span.or(colon_span) {
negative_bounds.push(sp.to(poly_span));
}
} else {
let poly_trait = PolyTraitRef::new(lifetime_defs, path, poly_span);
let modifier = if question.is_some() {
TraitBoundModifier::Maybe
} else {
TraitBoundModifier::None
};
bounds.push(GenericBound::Trait(poly_trait, modifier));
}
}
} else {
break
}
if !allow_plus || !self.eat_plus() {
break
} else {
last_plus_span = Some(self.prev_span);
}
}
if !negative_bounds.is_empty() || was_negative {
let negative_bounds_len = negative_bounds.len();
let last_span = negative_bounds.last().map(|sp| *sp);
let mut err = self.struct_span_err(
negative_bounds,
"negative trait bounds are not supported",
);
if let Some(sp) = last_span {
err.span_label(sp, "negative trait bounds are not supported");
}
if let Some(bound_list) = colon_span {
let bound_list = bound_list.to(self.prev_span);
let mut new_bound_list = String::new();
if !bounds.is_empty() {
let mut snippets = bounds.iter().map(|bound| bound.span())
.map(|span| self.span_to_snippet(span));
while let Some(Ok(snippet)) = snippets.next() {
new_bound_list.push_str(" + ");
new_bound_list.push_str(&snippet);
}
new_bound_list = new_bound_list.replacen(" +", ":", 1);
}
err.span_suggestion_hidden(
bound_list,
&format!("remove the trait bound{}", pluralize!(negative_bounds_len)),
new_bound_list,
Applicability::MachineApplicable,
);
}
err.emit();
}
return Ok(bounds);
}
pub(super) fn parse_late_bound_lifetime_defs(&mut self) -> PResult<'a, Vec<GenericParam>> {
if self.eat_keyword(kw::For) {
self.expect_lt()?;
let params = self.parse_generic_params()?;
self.expect_gt()?;
// We rely on AST validation to rule out invalid cases: There must not be type
// parameters, and the lifetime parameters must not have bounds.
Ok(params)
} else {
Ok(Vec::new())
}
}
pub fn check_lifetime(&mut self) -> bool {
self.expected_tokens.push(TokenType::Lifetime);
self.token.is_lifetime()
}
/// Parses a single lifetime `'a` or panics.
pub fn expect_lifetime(&mut self) -> Lifetime {
if let Some(ident) = self.token.lifetime() {
let span = self.token.span;
self.bump();
Lifetime { ident: Ident::new(ident.name, span), id: ast::DUMMY_NODE_ID }
} else {
self.span_bug(self.token.span, "not a lifetime")
}
}
pub(super) fn mk_ty(&self, span: Span, kind: TyKind) -> P<Ty> {
P(Ty { kind, span, id: ast::DUMMY_NODE_ID })
}
}

View file

@ -0,0 +1,111 @@
//! Meta-syntax validation logic of attributes for post-expansion.
use errors::{PResult, Applicability};
use syntax::ast::{self, Attribute, AttrKind, Ident, MetaItem};
use syntax::attr::{AttributeTemplate, mk_name_value_item_str};
use syntax::early_buffered_lints::BufferedEarlyLintId;
use syntax::feature_gate::BUILTIN_ATTRIBUTE_MAP;
use syntax::token;
use syntax::tokenstream::TokenTree;
use syntax::sess::ParseSess;
use syntax_pos::{Symbol, sym};
pub fn check_meta(sess: &ParseSess, attr: &Attribute) {
let attr_info =
attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)).map(|a| **a);
// Check input tokens for built-in and key-value attributes.
match attr_info {
// `rustc_dummy` doesn't have any restrictions specific to built-in attributes.
Some((name, _, template, _)) if name != sym::rustc_dummy =>
check_builtin_attribute(sess, attr, name, template),
_ => if let Some(TokenTree::Token(token)) = attr.get_normal_item().tokens.trees().next() {
if token == token::Eq {
// All key-value attributes are restricted to meta-item syntax.
parse_meta(sess, attr).map_err(|mut err| err.emit()).ok();
}
}
}
}
pub fn parse_meta<'a>(sess: &'a ParseSess, attr: &Attribute) -> PResult<'a, MetaItem> {
Ok(match attr.kind {
AttrKind::Normal(ref item) => MetaItem {
path: item.path.clone(),
kind: super::parse_in_attr(sess, attr, |p| p.parse_meta_item_kind())?,
span: attr.span,
},
AttrKind::DocComment(comment) => {
mk_name_value_item_str(Ident::new(sym::doc, attr.span), comment, attr.span)
}
})
}
pub fn check_builtin_attribute(
sess: &ParseSess,
attr: &Attribute,
name: Symbol,
template: AttributeTemplate,
) {
// Some special attributes like `cfg` must be checked
// before the generic check, so we skip them here.
let should_skip = |name| name == sym::cfg;
// Some of previously accepted forms were used in practice,
// report them as warnings for now.
let should_warn = |name| name == sym::doc || name == sym::ignore ||
name == sym::inline || name == sym::link ||
name == sym::test || name == sym::bench;
match parse_meta(sess, attr) {
Ok(meta) => if !should_skip(name) && !template.compatible(&meta.kind) {
let error_msg = format!("malformed `{}` attribute input", name);
let mut msg = "attribute must be of the form ".to_owned();
let mut suggestions = vec![];
let mut first = true;
if template.word {
first = false;
let code = format!("#[{}]", name);
msg.push_str(&format!("`{}`", &code));
suggestions.push(code);
}
if let Some(descr) = template.list {
if !first {
msg.push_str(" or ");
}
first = false;
let code = format!("#[{}({})]", name, descr);
msg.push_str(&format!("`{}`", &code));
suggestions.push(code);
}
if let Some(descr) = template.name_value_str {
if !first {
msg.push_str(" or ");
}
let code = format!("#[{} = \"{}\"]", name, descr);
msg.push_str(&format!("`{}`", &code));
suggestions.push(code);
}
if should_warn(name) {
sess.buffer_lint(
BufferedEarlyLintId::IllFormedAttributeInput,
meta.span,
ast::CRATE_NODE_ID,
&msg,
);
} else {
sess.span_diagnostic.struct_span_err(meta.span, &error_msg)
.span_suggestions(
meta.span,
if suggestions.len() == 1 {
"must be of the form"
} else {
"the following are the possible correct uses"
},
suggestions.into_iter(),
Applicability::HasPlaceholders,
).emit();
}
}
Err(mut err) => err.emit(),
}
}