about summary refs log tree commit diff
path: root/src/libsyntax_pos
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2018-05-18 10:57:05 +0000
committerbors <bors@rust-lang.org>2018-05-18 10:57:05 +0000
commitdf40e61382a2cba0be621fdabb9971ce3475e9a7 (patch)
tree7bdf66599659eccd8b03f5921c2e5087efe94008 /src/libsyntax_pos
parentfd18d2537ddcffb24b3739393b085ed28dfc340e (diff)
parentd8bbc1ee1ad44e9c7bd93c8d59103eacd0ed36e8 (diff)
downloadrust-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.rs82
-rw-r--r--src/libsyntax_pos/hygiene.rs13
-rw-r--r--src/libsyntax_pos/lib.rs8
-rw-r--r--src/libsyntax_pos/symbol.rs69
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.