diff options
| author | bors <bors@rust-lang.org> | 2018-05-18 10:57:05 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2018-05-18 10:57:05 +0000 |
| commit | df40e61382a2cba0be621fdabb9971ce3475e9a7 (patch) | |
| tree | 7bdf66599659eccd8b03f5921c2e5087efe94008 /src/libsyntax_pos | |
| parent | fd18d2537ddcffb24b3739393b085ed28dfc340e (diff) | |
| parent | d8bbc1ee1ad44e9c7bd93c8d59103eacd0ed36e8 (diff) | |
| download | rust-df40e61382a2cba0be621fdabb9971ce3475e9a7.tar.gz rust-df40e61382a2cba0be621fdabb9971ce3475e9a7.zip | |
Auto merge of #50307 - petrochenkov:keyhyg2, r=nikomatsakis
Implement edition hygiene for keywords Determine "keywordness" of an identifier in its hygienic context. cc https://github.com/rust-lang/rust/pull/49611 I've resurrected `proc` as an Edition-2015-only keyword for testing purposes, but it should probably be buried again. EDIT: `proc` is removed again.
Diffstat (limited to 'src/libsyntax_pos')
| -rw-r--r-- | src/libsyntax_pos/edition.rs | 82 | ||||
| -rw-r--r-- | src/libsyntax_pos/hygiene.rs | 13 | ||||
| -rw-r--r-- | src/libsyntax_pos/lib.rs | 8 | ||||
| -rw-r--r-- | src/libsyntax_pos/symbol.rs | 69 |
4 files changed, 164 insertions, 8 deletions
diff --git a/src/libsyntax_pos/edition.rs b/src/libsyntax_pos/edition.rs new file mode 100644 index 00000000000..18446c10996 --- /dev/null +++ b/src/libsyntax_pos/edition.rs @@ -0,0 +1,82 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::fmt; +use std::str::FromStr; + +/// The edition of the compiler (RFC 2052) +#[derive(Clone, Copy, Hash, PartialOrd, Ord, Eq, PartialEq, Debug, RustcEncodable, RustcDecodable)] +#[non_exhaustive] +pub enum Edition { + // editions must be kept in order, newest to oldest + + /// The 2015 edition + Edition2015, + /// The 2018 edition + Edition2018, + + // when adding new editions, be sure to update: + // + // - Update the `ALL_EDITIONS` const + // - Update the EDITION_NAME_LIST const + // - add a `rust_####()` function to the session + // - update the enum in Cargo's sources as well +} + +// must be in order from oldest to newest +pub const ALL_EDITIONS: &[Edition] = &[Edition::Edition2015, Edition::Edition2018]; + +pub const EDITION_NAME_LIST: &'static str = "2015|2018"; + +pub const DEFAULT_EDITION: Edition = Edition::Edition2015; + +impl fmt::Display for Edition { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let s = match *self { + Edition::Edition2015 => "2015", + Edition::Edition2018 => "2018", + }; + write!(f, "{}", s) + } +} + +impl Edition { + pub fn lint_name(&self) -> &'static str { + match *self { + Edition::Edition2015 => "rust_2015_compatibility", + Edition::Edition2018 => "rust_2018_compatibility", + } + } + + pub fn feature_name(&self) -> &'static str { + match *self { + Edition::Edition2015 => "rust_2015_preview", + Edition::Edition2018 => "rust_2018_preview", + } + } + + pub fn is_stable(&self) -> bool { + match *self { + Edition::Edition2015 => true, + Edition::Edition2018 => false, + } + } +} + +impl FromStr for Edition { + type Err = (); + fn from_str(s: &str) -> Result<Self, ()> { + match s { + "2015" => Ok(Edition::Edition2015), + "2018" => Ok(Edition::Edition2018), + _ => Err(()) + } + } +} diff --git a/src/libsyntax_pos/hygiene.rs b/src/libsyntax_pos/hygiene.rs index be031ea98c9..1365ac396ff 100644 --- a/src/libsyntax_pos/hygiene.rs +++ b/src/libsyntax_pos/hygiene.rs @@ -17,6 +17,7 @@ use GLOBALS; use Span; +use edition::Edition; use symbol::{Ident, Symbol}; use serialize::{Encodable, Decodable, Encoder, Decoder}; @@ -151,6 +152,7 @@ pub struct HygieneData { syntax_contexts: Vec<SyntaxContextData>, markings: HashMap<(SyntaxContext, Mark), SyntaxContext>, gensym_to_ctxt: HashMap<Symbol, Span>, + default_edition: Edition, } impl HygieneData { @@ -168,6 +170,7 @@ impl HygieneData { }], markings: HashMap::new(), gensym_to_ctxt: HashMap::new(), + default_edition: Edition::Edition2015, } } @@ -176,6 +179,14 @@ impl HygieneData { } } +pub fn default_edition() -> Edition { + HygieneData::with(|data| data.default_edition) +} + +pub fn set_default_edition(edition: Edition) { + HygieneData::with(|data| data.default_edition = edition); +} + pub fn clear_markings() { HygieneData::with(|data| data.markings = HashMap::new()); } @@ -443,6 +454,8 @@ pub struct NameAndSpan { /// Whether the macro is allowed to use `unsafe` internally /// even if the user crate has `#![forbid(unsafe_code)]`. pub allow_internal_unsafe: bool, + /// Edition of the crate in which the macro is defined. + pub edition: Edition, /// The span of the macro definition itself. The macro may not /// have a sensible definition span (e.g. something defined /// completely inside libsyntax) in which case this is None. diff --git a/src/libsyntax_pos/lib.rs b/src/libsyntax_pos/lib.rs index 73f0e6a6018..17163576901 100644 --- a/src/libsyntax_pos/lib.rs +++ b/src/libsyntax_pos/lib.rs @@ -20,6 +20,7 @@ #![feature(const_fn)] #![feature(custom_attribute)] +#![feature(non_exhaustive)] #![feature(optin_builtin_traits)] #![allow(unused_attributes)] #![feature(specialization)] @@ -48,6 +49,7 @@ extern crate serialize as rustc_serialize; // used by deriving extern crate unicode_width; +pub mod edition; pub mod hygiene; pub use hygiene::{Mark, SyntaxContext, ExpnInfo, ExpnFormat, NameAndSpan, CompilerDesugaringKind}; @@ -298,6 +300,12 @@ impl Span { self.ctxt().outer().expn_info().map(|i| i.call_site) } + /// Edition of the crate from which this span came. + pub fn edition(self) -> edition::Edition { + self.ctxt().outer().expn_info().map_or_else(|| hygiene::default_edition(), + |einfo| einfo.callee.edition) + } + /// Return the source callee. /// /// Returns None if the supplied span has no expansion trace, diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs index 2258ed12779..a08f9b2e54a 100644 --- a/src/libsyntax_pos/symbol.rs +++ b/src/libsyntax_pos/symbol.rs @@ -12,6 +12,7 @@ //! allows bidirectional lookup; i.e. given a value, one can easily find the //! type, and vice versa. +use edition::Edition; use hygiene::SyntaxContext; use {Span, DUMMY_SP, GLOBALS}; @@ -318,7 +319,7 @@ 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`, +// After modifying this list adjust `is_special`, `is_used_keyword`/`is_unused_keyword`, // this should be rarely necessary though if the keywords are kept in alphabetic order. declare_keywords! { // Special reserved identifiers used internally for elided lifetimes, @@ -383,16 +384,68 @@ declare_keywords! { (53, Virtual, "virtual") (54, Yield, "yield") + // Edition-specific keywords reserved for future use. + (55, Async, "async") // >= 2018 Edition Only + // Special lifetime names - (55, UnderscoreLifetime, "'_") - (56, StaticLifetime, "'static") + (56, UnderscoreLifetime, "'_") + (57, StaticLifetime, "'static") // Weak keywords, have special meaning only in specific contexts. - (57, Auto, "auto") - (58, Catch, "catch") - (59, Default, "default") - (60, Dyn, "dyn") - (61, Union, "union") + (58, Auto, "auto") + (59, Catch, "catch") + (60, Default, "default") + (61, Dyn, "dyn") + (62, Union, "union") +} + +impl Symbol { + fn is_unused_keyword_2018(self) -> bool { + self == keywords::Async.name() + } +} + +impl Ident { + // Returns true for reserved identifiers used internally for elided lifetimes, + // unnamed method parameters, crate root module, error recovery etc. + pub fn is_special(self) -> bool { + self.name <= keywords::Underscore.name() + } + + /// Returns `true` if the token is a keyword used in the language. + pub fn is_used_keyword(self) -> bool { + self.name >= keywords::As.name() && self.name <= keywords::While.name() + } + + /// Returns `true` if the token is a keyword reserved for possible future use. + pub fn is_unused_keyword(self) -> bool { + // Note: `span.edition()` is relatively expensive, don't call it unless necessary. + self.name >= keywords::Abstract.name() && self.name <= keywords::Yield.name() || + self.name.is_unused_keyword_2018() && self.span.edition() == Edition::Edition2018 + } + + /// Returns `true` if the token is either a special identifier or a keyword. + pub fn is_reserved(self) -> bool { + self.is_special() || self.is_used_keyword() || self.is_unused_keyword() + } + + /// A keyword or reserved identifier that can be used as a path segment. + pub fn is_path_segment_keyword(self) -> bool { + self.name == keywords::Super.name() || + self.name == keywords::SelfValue.name() || + self.name == keywords::SelfType.name() || + self.name == keywords::Extern.name() || + self.name == keywords::Crate.name() || + self.name == keywords::CrateRoot.name() || + self.name == keywords::DollarCrate.name() + } + + // We see this identifier in a normal identifier position, like variable name or a type. + // How was it written originally? Did it use the raw form? Let's try to guess. + pub fn is_raw_guess(self) -> bool { + self.name != keywords::Invalid.name() && + self.is_reserved() && !self.is_path_segment_keyword() + } } // If an interner exists, return it. Otherwise, prepare a fresh one. |
