diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 77cf50a8341ed..ec567547a036a 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -1096,7 +1096,8 @@ pub fn build_session_( }; let target_cfg = config::build_target_config(&sopts, &span_diagnostic); - let p_s = parse::ParseSess::with_span_handler(span_diagnostic, codemap); + let p_s = parse::ParseSess::with_span_handler(span_diagnostic, codemap, + sopts.debugging_opts.edition); let default_sysroot = match sopts.maybe_sysroot { Some(_) => None, None => Some(filesearch::get_or_default_sysroot()), diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs index 1866122454c70..a3d3c97fad8ee 100644 --- a/src/librustc_passes/ast_validation.rs +++ b/src/librustc_passes/ast_validation.rs @@ -21,7 +21,6 @@ use rustc::session::Session; use syntax::ast::*; use syntax::attr; use syntax::codemap::Spanned; -use syntax::parse::token; use syntax::symbol::keywords; use syntax::visit::{self, Visitor}; use syntax_pos::Span; @@ -40,14 +39,15 @@ impl<'a> AstValidator<'a> { let valid_names = [keywords::UnderscoreLifetime.name(), keywords::StaticLifetime.name(), keywords::Invalid.name()]; - if !valid_names.contains(&ident.name) && - token::is_reserved_ident(ident.without_first_quote()) { + if !valid_names.contains(&ident.name) + && ident.without_first_quote().is_reserved() + { self.err_handler().span_err(ident.span, "lifetimes cannot use keyword names"); } } fn check_label(&self, ident: Ident) { - if token::is_reserved_ident(ident.without_first_quote()) { + if ident.without_first_quote().is_reserved() { self.err_handler() .span_err(ident.span, &format!("invalid label name `{}`", ident.name)); } diff --git a/src/libsyntax/edition.rs b/src/libsyntax/edition.rs index e579fc74b4266..71745433a0ab9 100644 --- a/src/libsyntax/edition.rs +++ b/src/libsyntax/edition.rs @@ -10,6 +10,7 @@ use std::fmt; use std::str::FromStr; +use symbol::Symbol; /// The edition of the compiler (RFC 2052) #[derive(Clone, Copy, Hash, PartialOrd, Ord, Eq, PartialEq, Debug)] @@ -56,6 +57,13 @@ impl Edition { } } + pub fn is_future_edition_keyword(&self, sym: Symbol) -> bool { + match *self { + Edition::Edition2015 => sym.is_future_edition_keyword_2015(), + Edition::Edition2018 => sym.is_future_edition_keyword_2018(), + } + } + pub fn feature_name(&self) -> &'static str { match *self { Edition::Edition2015 => "rust_2015_preview", diff --git a/src/libsyntax/parse/lexer/mod.rs b/src/libsyntax/parse/lexer/mod.rs index 39b2f77f2305e..78eafc6d24a5d 100644 --- a/src/libsyntax/parse/lexer/mod.rs +++ b/src/libsyntax/parse/lexer/mod.rs @@ -1117,7 +1117,7 @@ impl<'a> StringReader<'a> { let c = self.ch; if ident_start(c) { - let (is_ident_start, is_raw_ident) = + let (is_ident_start, mut is_raw_ident) = match (c.unwrap(), self.nextch(), self.nextnextch()) { // r# followed by an identifier starter is a raw identifier. // This is an exception to the r# case below. @@ -1155,9 +1155,13 @@ impl<'a> StringReader<'a> { &format!("`r#{}` is not currently supported.", ident.name) ).raise(); } + if is_raw_ident { let span = self.mk_sp(raw_start, self.pos); self.sess.raw_identifier_spans.borrow_mut().push(span); + } else if self.sess.edition.is_future_edition_keyword(ident.name) { + // we pretend these are raw even if they aren't + is_raw_ident = true; } token::Ident(ident, is_raw_ident) })); @@ -1778,6 +1782,7 @@ mod tests { use symbol::Symbol; use syntax_pos::{BytePos, Span, NO_EXPANSION}; use codemap::CodeMap; + use edition::Edition; use errors; use feature_gate::UnstableFeatures; use parse::token; @@ -1802,6 +1807,7 @@ mod tests { raw_identifier_spans: Lock::new(Vec::new()), registered_diagnostics: Lock::new(ErrorMap::new()), non_modrs_mods: Lock::new(vec![]), + edition: Edition::Edition2015, } } diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs index ff63c9a5c6d53..12df3a7c2b67e 100644 --- a/src/libsyntax/parse/mod.rs +++ b/src/libsyntax/parse/mod.rs @@ -14,6 +14,7 @@ use rustc_data_structures::sync::{Lrc, Lock}; use ast::{self, CrateConfig}; use codemap::{CodeMap, FilePathMapping}; use syntax_pos::{self, Span, FileMap, NO_EXPANSION, FileName}; +use edition::Edition; use errors::{Handler, ColorConfig, DiagnosticBuilder}; use feature_gate::UnstableFeatures; use parse::parser::Parser; @@ -54,6 +55,7 @@ pub struct ParseSess { // Spans where a `mod foo;` statement was included in a non-mod.rs file. // These are used to issue errors if the non_modrs_mods feature is not enabled. pub non_modrs_mods: Lock>, + pub edition: Edition, /// Used to determine and report recursive mod inclusions included_mod_stack: Lock>, code_map: Lrc, @@ -66,10 +68,11 @@ impl ParseSess { true, false, Some(cm.clone())); - ParseSess::with_span_handler(handler, cm) + ParseSess::with_span_handler(handler, cm, Edition::Edition2015) } - pub fn with_span_handler(handler: Handler, code_map: Lrc) -> ParseSess { + pub fn with_span_handler(handler: Handler, code_map: Lrc, + edition: Edition) -> ParseSess { ParseSess { span_diagnostic: handler, unstable_features: UnstableFeatures::from_environment(), @@ -80,6 +83,7 @@ impl ParseSess { included_mod_stack: Lock::new(vec![]), code_map, non_modrs_mods: Lock::new(vec![]), + edition, } } diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 8da79f92768a1..7196f48584b04 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -145,28 +145,7 @@ pub fn is_path_segment_keyword(id: ast::Ident) -> bool { // How was it written originally? Did it use the raw form? Let's try to guess. pub fn is_raw_guess(ident: ast::Ident) -> bool { ident.name != keywords::Invalid.name() && - is_reserved_ident(ident) && !is_path_segment_keyword(ident) -} - -// Returns true for reserved identifiers used internally for elided lifetimes, -// unnamed method parameters, crate root module, error recovery etc. -pub fn is_special_ident(id: ast::Ident) -> bool { - id.name <= keywords::Underscore.name() -} - -/// Returns `true` if the token is a keyword used in the language. -pub fn is_used_keyword(id: ast::Ident) -> bool { - id.name >= keywords::As.name() && id.name <= keywords::While.name() -} - -/// Returns `true` if the token is a keyword reserved for possible future use. -pub fn is_unused_keyword(id: ast::Ident) -> bool { - id.name >= keywords::Abstract.name() && id.name <= keywords::Yield.name() -} - -/// Returns `true` if the token is either a special identifier or a keyword. -pub fn is_reserved_ident(id: ast::Ident) -> bool { - is_special_ident(id) || is_used_keyword(id) || is_unused_keyword(id) + ident.is_reserved() && !is_path_segment_keyword(ident) } #[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Hash, Debug)] @@ -413,7 +392,7 @@ impl Token { // unnamed method parameters, crate root module, error recovery etc. pub fn is_special_ident(&self) -> bool { match self.ident() { - Some((id, false)) => is_special_ident(id), + Some((id, false)) => id.is_special(), _ => false, } } @@ -421,7 +400,7 @@ impl Token { /// Returns `true` if the token is a keyword used in the language. pub fn is_used_keyword(&self) -> bool { match self.ident() { - Some((id, false)) => is_used_keyword(id), + Some((id, false)) => id.is_used_keyword(), _ => false, } } @@ -429,7 +408,7 @@ impl Token { /// Returns `true` if the token is a keyword reserved for possible future use. pub fn is_unused_keyword(&self) -> bool { match self.ident() { - Some((id, false)) => is_unused_keyword(id), + Some((id, false)) => id.is_unused_keyword(), _ => false, } } @@ -437,7 +416,7 @@ impl Token { /// Returns `true` if the token is either a special identifier or a keyword. pub fn is_reserved_ident(&self) -> bool { match self.ident() { - Some((id, false)) => is_reserved_ident(id), + Some((id, false)) => id.is_reserved(), _ => false, } } diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs index 331b0fe5481d5..ce219cbb53028 100644 --- a/src/libsyntax_pos/symbol.rs +++ b/src/libsyntax_pos/symbol.rs @@ -278,8 +278,8 @@ macro_rules! declare_keywords {( // NB: leaving holes in the ident table is bad! a different ident will get // interned with the id from the hole, but it will be between the min and max // of the reserved words, and thus tagged as "reserved". -// After modifying this list adjust `is_special_ident`, `is_used_keyword`/`is_unused_keyword`, -// this should be rarely necessary though if the keywords are kept in alphabetic order. +// After modifying this list adjust `is_special`, `is_used_keyword`/`is_unused_keyword` +// velow declare_keywords! { // Special reserved identifiers used internally for elided lifetimes, // unnamed method parameters, crate root module, error recovery etc. @@ -325,6 +325,9 @@ declare_keywords! { (37, Use, "use") (38, Where, "where") (39, While, "while") + // edition-gated used keywords go here + // be sure to update is_used_keyword and + // is_future_edition_keyword_* below // Keywords reserved for future use. (40, Abstract, "abstract") @@ -343,17 +346,66 @@ declare_keywords! { (53, Unsized, "unsized") (54, Virtual, "virtual") (55, Yield, "yield") + // edition-gated reserved keywords + // be sure to update is_unused_keyword and + // is_future_edition_keyword_* below + (56, Async, "async") // Rust 2018+ only // Special lifetime names - (56, UnderscoreLifetime, "'_") - (57, StaticLifetime, "'static") + (57, UnderscoreLifetime, "'_") + (58, StaticLifetime, "'static") // Weak keywords, have special meaning only in specific contexts. - (58, Auto, "auto") - (59, Catch, "catch") - (60, Default, "default") - (61, Dyn, "dyn") - (62, Union, "union") + (59, Auto, "auto") + (60, Catch, "catch") + (61, Default, "default") + (62, Dyn, "dyn") + (63, Union, "union") +} + +impl Ident { + // Returns true for reserved identifiers used internally for elided lifetimes, + // unnamed method parameters, crate root module, error recovery etc. + #[inline] + pub fn is_special(self) -> bool { + self.name <= self::keywords::Underscore.name() + } + + /// Returns `true` if the token is a keyword used in the language, for + /// at least one edition. + /// + /// Keywords from future editions will be lexed as if they were raw identifiers + /// so they will not reach this step. + #[inline] + pub fn is_used_keyword(self) -> bool { + self.name >= self::keywords::As.name() && self.name <= self::keywords::While.name() + } + + /// Returns `true` if the token is a keyword reserved for possible future use, for + /// at least one edition. + #[inline] + pub fn is_unused_keyword(self) -> bool { + self.name >= self::keywords::Abstract.name() && self.name <= self::keywords::Async.name() + } + + + /// Returns `true` if the token is either a special identifier or a keyword. + #[inline] + pub fn is_reserved(self) -> bool { + self.is_special() || self.is_used_keyword() || self.is_unused_keyword() + } +} + +impl Symbol { + #[inline] + pub fn is_future_edition_keyword_2015(self) -> bool { + self == self::keywords::Async.name() + } + + #[inline] + pub fn is_future_edition_keyword_2018(self) -> bool { + false + } } // If an interner exists, return it. Otherwise, prepare a fresh one. diff --git a/src/test/compile-fail/auxiliary/edition-kw-macro-2015.rs b/src/test/compile-fail/auxiliary/edition-kw-macro-2015.rs new file mode 100644 index 0000000000000..d6ca9a11ef5a0 --- /dev/null +++ b/src/test/compile-fail/auxiliary/edition-kw-macro-2015.rs @@ -0,0 +1,17 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Zedition=2015 -Zunstable-options + +#[macro_export] +macro_rules! consumes_async { + (async) => (1) +} + diff --git a/src/test/compile-fail/edition-keywords-2018.rs b/src/test/compile-fail/edition-keywords-2018.rs new file mode 100644 index 0000000000000..24359ce4a4c89 --- /dev/null +++ b/src/test/compile-fail/edition-keywords-2018.rs @@ -0,0 +1,22 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Zedition=2018 -Zunstable-options +// aux-build:edition-kw-macro-2015.rs + +#![feature(raw_identifiers)] + +#[macro_use] +extern crate edition_kw_macro_2015; + +pub fn main() { + let _ = consumes_async!(async); + //~^ ERROR no rules expected the token `async` +} diff --git a/src/test/compile-fail/edition-kw.rs b/src/test/compile-fail/edition-kw.rs new file mode 100644 index 0000000000000..275a26c601224 --- /dev/null +++ b/src/test/compile-fail/edition-kw.rs @@ -0,0 +1,18 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Zedition=2018 -Zunstable-options + +#![allow(unused)] + +pub fn main() { + let async = 1; + //~^ ERROR expected pattern, found reserved keyword `async` +} diff --git a/src/test/run-pass/auxiliary/edition-kw-macro-2015.rs b/src/test/run-pass/auxiliary/edition-kw-macro-2015.rs new file mode 100644 index 0000000000000..e238d3c300109 --- /dev/null +++ b/src/test/run-pass/auxiliary/edition-kw-macro-2015.rs @@ -0,0 +1,37 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Zedition=2015 -Zunstable-options +#![feature(raw_identifiers)] + +#[macro_export] +macro_rules! produces_async { + () => (pub fn async() {}) +} + +#[macro_export] +macro_rules! produces_async_raw { + () => (pub fn r#async() {}) +} + +#[macro_export] +macro_rules! consumes_async { + (async) => (1) +} + +#[macro_export] +macro_rules! consumes_async_raw { + (r#async) => (1) +} + +#[macro_export] +macro_rules! consumes_ident { + ($i:ident) => ($i) +} diff --git a/src/test/run-pass/auxiliary/edition-kw-macro-2018.rs b/src/test/run-pass/auxiliary/edition-kw-macro-2018.rs new file mode 100644 index 0000000000000..18a786459cf88 --- /dev/null +++ b/src/test/run-pass/auxiliary/edition-kw-macro-2018.rs @@ -0,0 +1,32 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Zedition=2018 -Zunstable-options +#![feature(raw_identifiers)] + +#[macro_export] +macro_rules! produces_async { + () => (pub fn async() {}) +} + +#[macro_export] +macro_rules! produces_async_raw { + () => (pub fn r#async() {}) +} + +#[macro_export] +macro_rules! consumes_async_raw { + (r#async) => (1) +} + +#[macro_export] +macro_rules! consumes_ident { + ($i:ident) => ($i) +} diff --git a/src/test/run-pass/edition-keywords-2015.rs b/src/test/run-pass/edition-keywords-2015.rs new file mode 100644 index 0000000000000..4993841ae2586 --- /dev/null +++ b/src/test/run-pass/edition-keywords-2015.rs @@ -0,0 +1,45 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Zedition=2015 -Zunstable-options +// aux-build:edition-kw-macro-2018.rs + +#![feature(raw_identifiers)] + +#[macro_use] +extern crate edition_kw_macro_2018; + + +pub fn main() { + let mut async = 1; + async = consumes_async_raw!(r#async); + async = consumes_async_raw!(async); + if r#async == 1 { + + } + if consumes_ident!(r#async) == 1 { + // ... + } + if consumes_ident!(async) == 1 { + // ... + } + one::r#async(); + two::r#async(); + one::async(); + two::async(); +} + + +mod one { + produces_async! {} +} +mod two { + produces_async_raw! {} +} diff --git a/src/test/run-pass/edition-keywords-2018.rs b/src/test/run-pass/edition-keywords-2018.rs new file mode 100644 index 0000000000000..7ac92ea03db29 --- /dev/null +++ b/src/test/run-pass/edition-keywords-2018.rs @@ -0,0 +1,38 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Zedition=2018 -Zunstable-options +// aux-build:edition-kw-macro-2015.rs + +#![feature(raw_identifiers)] + +#[macro_use] +extern crate edition_kw_macro_2015; + +pub fn main() { + let mut r#async = 1; + + r#async = consumes_async!(r#async); + r#async = consumes_async_raw!(r#async); + + if consumes_ident!(r#async) == 1 { + // ... + } + one::r#async(); + two::r#async(); +} + + +mod one { + produces_async! {} +} +mod two { + produces_async_raw! {} +}