Auto merge of #59655 - Zoxc:symbols, r=petrochenkov

Use a proc macro to declare preallocated symbols

r? @petrochenkov
This commit is contained in:
bors 2019-04-15 10:03:39 +00:00
commit fcf850f34a
13 changed files with 326 additions and 140 deletions

View File

@ -3387,6 +3387,7 @@ dependencies = [
"arena 0.0.0",
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_data_structures 0.0.0",
"rustc_macros 0.1.0",
"scoped-tls 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serialize 0.0.0",
"unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",

View File

@ -8,7 +8,7 @@ use crate::ty::TyCtxt;
use crate::hir::intravisit::{self, NestedVisitorMap, Visitor};
use syntax::symbol::Symbol;
use syntax::ast::{Attribute, MetaItem, MetaItemKind};
use syntax_pos::Span;
use syntax_pos::{Span, symbols};
use rustc_data_structures::fx::{FxHashSet, FxHashMap};
use rustc_macros::HashStable;
use errors::DiagnosticId;
@ -51,12 +51,12 @@ impl<'a, 'tcx> LibFeatureCollector<'a, 'tcx> {
}
fn extract(&self, attr: &Attribute) -> Option<(Symbol, Option<Symbol>, Span)> {
let stab_attrs = vec!["stable", "unstable", "rustc_const_unstable"];
let stab_attrs = [symbols::stable, symbols::unstable, symbols::rustc_const_unstable];
// Find a stability attribute (i.e., `#[stable (..)]`, `#[unstable (..)]`,
// `#[rustc_const_unstable (..)]`).
if let Some(stab_attr) = stab_attrs.iter().find(|stab_attr| {
attr.check_name(stab_attr)
attr.check_name(**stab_attr)
}) {
let meta_item = attr.meta();
if let Some(MetaItem { node: MetaItemKind::List(ref metas), .. }) = meta_item {

View File

@ -599,7 +599,7 @@ impl<'a, 'tcx> FindAllAttrs<'a, 'tcx> {
fn is_active_attr(&mut self, attr: &Attribute) -> bool {
for attr_name in &self.attr_names {
if attr.check_name(attr_name) && check_config(self.tcx, attr) {
if attr.check_name(*attr_name) && check_config(self.tcx, attr) {
return true;
}
}

View File

@ -228,7 +228,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedAttributes {
let plugin_attributes = cx.sess().plugin_attributes.borrow_mut();
for &(ref name, ty) in plugin_attributes.iter() {
if ty == AttributeType::Whitelisted && attr.check_name(&name) {
if ty == AttributeType::Whitelisted && attr.check_name(&**name) {
debug!("{:?} (plugin attr) is whitelisted with ty {:?}", name, ty);
break;
}

View File

@ -9,10 +9,16 @@ use proc_macro::TokenStream;
mod hash_stable;
mod query;
mod symbols;
#[proc_macro]
pub fn rustc_queries(input: TokenStream) -> TokenStream {
query::rustc_queries(input)
}
#[proc_macro]
pub fn symbols(input: TokenStream) -> TokenStream {
symbols::symbols(input)
}
decl_derive!([HashStable, attributes(stable_hasher)] => hash_stable::hash_stable_derive);

View File

@ -0,0 +1,163 @@
use proc_macro::TokenStream;
use syn::{
Token, Ident, LitStr,
braced, parse_macro_input,
};
use syn::parse::{Result, Parse, ParseStream};
use syn;
use std::collections::HashSet;
use quote::quote;
#[allow(non_camel_case_types)]
mod kw {
syn::custom_keyword!(Keywords);
syn::custom_keyword!(Other);
}
struct Keyword {
name: Ident,
value: LitStr,
}
impl Parse for Keyword {
fn parse(input: ParseStream<'_>) -> Result<Self> {
let name = input.parse()?;
input.parse::<Token![:]>()?;
let value = input.parse()?;
input.parse::<Token![,]>()?;
Ok(Keyword {
name,
value,
})
}
}
struct Symbol(Ident);
impl Parse for Symbol {
fn parse(input: ParseStream<'_>) -> Result<Self> {
let ident: Ident = input.parse()?;
input.parse::<Token![,]>()?;
Ok(Symbol(ident))
}
}
/// A type used to greedily parse another type until the input is empty.
struct List<T>(Vec<T>);
impl<T: Parse> Parse for List<T> {
fn parse(input: ParseStream<'_>) -> Result<Self> {
let mut list = Vec::new();
while !input.is_empty() {
list.push(input.parse()?);
}
Ok(List(list))
}
}
struct Input {
keywords: List<Keyword>,
symbols: List<Symbol>,
}
impl Parse for Input {
fn parse(input: ParseStream<'_>) -> Result<Self> {
input.parse::<kw::Keywords>()?;
let content;
braced!(content in input);
let keywords = content.parse()?;
input.parse::<kw::Other>()?;
let content;
braced!(content in input);
let symbols = content.parse()?;
Ok(Input {
keywords,
symbols,
})
}
}
pub fn symbols(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as Input);
let mut keyword_stream = quote! {};
let mut symbols_stream = quote! {};
let mut prefill_stream = quote! {};
let mut from_str_stream = quote! {};
let mut counter = 0u32;
let mut keys = HashSet::<String>::new();
let mut check_dup = |str: &str| {
if !keys.insert(str.to_string()) {
panic!("Symbol `{}` is duplicated", str);
}
};
for keyword in &input.keywords.0 {
let name = &keyword.name;
let value = &keyword.value;
check_dup(&value.value());
prefill_stream.extend(quote! {
#value,
});
keyword_stream.extend(quote! {
pub const #name: Keyword = Keyword {
ident: Ident::with_empty_ctxt(super::Symbol::new(#counter))
};
});
from_str_stream.extend(quote! {
#value => Ok(#name),
});
counter += 1;
}
for symbol in &input.symbols.0 {
let value = &symbol.0;
let value_str = value.to_string();
check_dup(&value_str);
prefill_stream.extend(quote! {
#value_str,
});
symbols_stream.extend(quote! {
pub const #value: Symbol = Symbol::new(#counter);
});
counter += 1;
}
TokenStream::from(quote! {
macro_rules! keywords {
() => {
#keyword_stream
impl std::str::FromStr for Keyword {
type Err = ();
fn from_str(s: &str) -> Result<Self, ()> {
match s {
#from_str_stream
_ => Err(()),
}
}
}
}
}
macro_rules! symbols {
() => {
#symbols_stream
}
}
impl Interner {
pub fn fresh() -> Self {
Interner::prefill(&[
#prefill_stream
])
}
}
})
}

View File

@ -68,6 +68,18 @@ pub struct Path {
pub segments: Vec<PathSegment>,
}
impl PartialEq<Symbol> for Path {
fn eq(&self, symbol: &Symbol) -> bool {
self.segments.len() == 1 && {
let name = self.segments[0].ident.name;
// Make sure these symbols are pure strings
debug_assert!(!symbol.is_gensymed());
debug_assert!(!name.is_gensymed());
name == *symbol
}
}
}
impl<'a> PartialEq<&'a str> for Path {
fn eq(&self, string: &&'a str) -> bool {
self.segments.len() == 1 && self.segments[0].ident.name == *string

View File

@ -81,7 +81,10 @@ impl NestedMetaItem {
}
/// Returns `true` if this list item is a MetaItem with a name of `name`.
pub fn check_name(&self, name: &str) -> bool {
pub fn check_name<T>(&self, name: T) -> bool
where
Path: PartialEq<T>,
{
self.meta_item().map_or(false, |meta_item| meta_item.check_name(name))
}
@ -151,7 +154,10 @@ impl Attribute {
/// attribute is marked as used.
///
/// To check the attribute name without marking it used, use the `path` field directly.
pub fn check_name(&self, name: &str) -> bool {
pub fn check_name<T>(&self, name: T) -> bool
where
Path: PartialEq<T>,
{
let matches = self.path == name;
if matches {
mark_used(self);
@ -244,7 +250,10 @@ impl MetaItem {
}
}
pub fn check_name(&self, name: &str) -> bool {
pub fn check_name<T>(&self, name: T) -> bool
where
Path: PartialEq<T>,
{
self.path == name
}

View File

@ -28,7 +28,7 @@ use crate::tokenstream::TokenTree;
use errors::{DiagnosticBuilder, Handler};
use rustc_data_structures::fx::FxHashMap;
use rustc_target::spec::abi::Abi;
use syntax_pos::{Span, DUMMY_SP};
use syntax_pos::{Span, DUMMY_SP, symbols};
use log::debug;
use std::env;
@ -1366,7 +1366,7 @@ impl<'a> Context<'a> {
}
} else if n == "doc" {
if let Some(content) = attr.meta_item_list() {
if content.iter().any(|c| c.check_name("include")) {
if content.iter().any(|c| c.check_name(symbols::include)) {
gate_feature!(self, external_doc, attr.span,
"#[doc(include = \"...\")] is experimental"
);
@ -1667,25 +1667,25 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
// check for gated attributes
self.context.check_attribute(attr, false);
if attr.check_name("doc") {
if attr.check_name(symbols::doc) {
if let Some(content) = attr.meta_item_list() {
if content.len() == 1 && content[0].check_name("cfg") {
if content.len() == 1 && content[0].check_name(symbols::cfg) {
gate_feature_post!(&self, doc_cfg, attr.span,
"#[doc(cfg(...))] is experimental"
);
} else if content.iter().any(|c| c.check_name("masked")) {
} else if content.iter().any(|c| c.check_name(symbols::masked)) {
gate_feature_post!(&self, doc_masked, attr.span,
"#[doc(masked)] is experimental"
);
} else if content.iter().any(|c| c.check_name("spotlight")) {
} else if content.iter().any(|c| c.check_name(symbols::spotlight)) {
gate_feature_post!(&self, doc_spotlight, attr.span,
"#[doc(spotlight)] is experimental"
);
} else if content.iter().any(|c| c.check_name("alias")) {
} else if content.iter().any(|c| c.check_name(symbols::alias)) {
gate_feature_post!(&self, doc_alias, attr.span,
"#[doc(alias = \"...\")] is experimental"
);
} else if content.iter().any(|c| c.check_name("keyword")) {
} else if content.iter().any(|c| c.check_name(symbols::keyword)) {
gate_feature_post!(&self, doc_keyword, attr.span,
"#[doc(keyword = \"...\")] is experimental"
);
@ -1693,7 +1693,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
}
}
match BUILTIN_ATTRIBUTES.iter().find(|(name, ..)| attr.path == name) {
match BUILTIN_ATTRIBUTES.iter().find(|(name, ..)| attr.path == *name) {
Some(&(name, _, template, _)) => self.check_builtin_attribute(attr, name, template),
None => if let Some(TokenTree::Token(_, token::Eq)) = attr.tokens.trees().next() {
// All key-value attributes are restricted to meta-item syntax.
@ -1748,7 +1748,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
ast::ItemKind::Struct(..) => {
for attr in attr::filter_by_name(&i.attrs[..], "repr") {
for item in attr.meta_item_list().unwrap_or_else(Vec::new) {
if item.check_name("simd") {
if item.check_name(symbols::simd) {
gate_feature_post!(&self, repr_simd, attr.span,
"SIMD types are experimental and possibly buggy");
}
@ -1759,7 +1759,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
ast::ItemKind::Enum(..) => {
for attr in attr::filter_by_name(&i.attrs[..], "repr") {
for item in attr.meta_item_list().unwrap_or_else(Vec::new) {
if item.check_name("align") {
if item.check_name(symbols::align) {
gate_feature_post!(&self, repr_align_enum, attr.span,
"`#[repr(align(x))]` on enums is experimental");
}
@ -2083,7 +2083,7 @@ pub fn get_features(span_handler: &Handler, krate_attrs: &[ast::Attribute],
// Process the edition umbrella feature-gates first, to ensure
// `edition_enabled_features` is completed before it's queried.
for attr in krate_attrs {
if !attr.check_name("feature") {
if !attr.check_name(symbols::feature) {
continue
}
@ -2128,7 +2128,7 @@ pub fn get_features(span_handler: &Handler, krate_attrs: &[ast::Attribute],
}
for attr in krate_attrs {
if !attr.check_name("feature") {
if !attr.check_name(symbols::feature) {
continue
}
@ -2258,7 +2258,7 @@ fn maybe_stage_features(span_handler: &Handler, krate: &ast::Crate,
};
if !allow_features {
for attr in &krate.attrs {
if attr.check_name("feature") {
if attr.check_name(symbols::feature) {
let release_channel = option_env!("CFG_RELEASE_CHANNEL").unwrap_or("(unknown)");
span_err!(span_handler, attr.span, E0554,
"#![feature] may not be used on the {} release channel",

View File

@ -87,7 +87,7 @@ pub fn modify(sess: &ParseSess,
}
pub fn is_proc_macro_attr(attr: &ast::Attribute) -> bool {
PROC_MACRO_KINDS.iter().any(|kind| attr.check_name(kind))
PROC_MACRO_KINDS.iter().any(|kind| attr.check_name(*kind))
}
impl<'a> CollectProcMacros<'a> {

View File

@ -11,6 +11,7 @@ crate-type = ["dylib"]
[dependencies]
serialize = { path = "../libserialize" }
rustc_macros = { path = "../librustc_macros" }
rustc_data_structures = { path = "../librustc_data_structures" }
arena = { path = "../libarena" }
scoped-tls = "1.0"

View File

@ -16,6 +16,7 @@
#![feature(non_exhaustive)]
#![feature(optin_builtin_traits)]
#![feature(rustc_attrs)]
#![feature(proc_macro_hygiene)]
#![feature(specialization)]
#![feature(step_trait)]
@ -32,6 +33,7 @@ mod span_encoding;
pub use span_encoding::{Span, DUMMY_SP};
pub mod symbol;
pub use symbol::symbols;
mod analyze_source_file;

View File

@ -6,6 +6,7 @@ use arena::DroplessArena;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::indexed_vec::Idx;
use rustc_data_structures::newtype_index;
use rustc_macros::symbols;
use serialize::{Decodable, Decoder, Encodable, Encoder};
use std::fmt;
@ -16,6 +17,94 @@ use std::hash::{Hash, Hasher};
use crate::hygiene::SyntaxContext;
use crate::{Span, DUMMY_SP, GLOBALS};
symbols! {
// 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.
Keywords {
// Special reserved identifiers used internally for elided lifetimes,
// unnamed method parameters, crate root module, error recovery etc.
Invalid: "",
PathRoot: "{{root}}",
DollarCrate: "$crate",
Underscore: "_",
// Keywords that are used in stable Rust.
As: "as",
Box: "box",
Break: "break",
Const: "const",
Continue: "continue",
Crate: "crate",
Else: "else",
Enum: "enum",
Extern: "extern",
False: "false",
Fn: "fn",
For: "for",
If: "if",
Impl: "impl",
In: "in",
Let: "let",
Loop: "loop",
Match: "match",
Mod: "mod",
Move: "move",
Mut: "mut",
Pub: "pub",
Ref: "ref",
Return: "return",
SelfLower: "self",
SelfUpper: "Self",
Static: "static",
Struct: "struct",
Super: "super",
Trait: "trait",
True: "true",
Type: "type",
Unsafe: "unsafe",
Use: "use",
Where: "where",
While: "while",
// Keywords that are used in unstable Rust or reserved for future use.
Abstract: "abstract",
Become: "become",
Do: "do",
Final: "final",
Macro: "macro",
Override: "override",
Priv: "priv",
Typeof: "typeof",
Unsized: "unsized",
Virtual: "virtual",
Yield: "yield",
// Edition-specific keywords that are used in stable Rust.
Dyn: "dyn", // >= 2018 Edition only
// Edition-specific keywords that are used in unstable Rust or reserved for future use.
Async: "async", // >= 2018 Edition only
Try: "try", // >= 2018 Edition only
// Special lifetime names
UnderscoreLifetime: "'_",
StaticLifetime: "'static",
// Weak keywords, have special meaning only in specific contexts.
Auto: "auto",
Catch: "catch",
Default: "default",
Existential: "existential",
Union: "union",
}
// Other symbols that can be referred to with syntax_pos::symbols::*
Other {
doc, cfg, masked, spotlight, alias, keyword, feature, include, simd, align, stable,
unstable, rustc_const_unstable,
}
}
#[derive(Copy, Clone, Eq)]
pub struct Ident {
pub name: Symbol,
@ -317,129 +406,32 @@ impl Interner {
}
}
// In this macro, there is the requirement that the name (the number) must be monotonically
// increasing by one in the special identifiers, starting at 0; the same holds for the keywords,
// except starting from the next number instead of zero.
macro_rules! declare_keywords {(
$( ($index: expr, $konst: ident, $string: expr) )*
) => {
pub mod keywords {
use super::{Symbol, Ident};
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct Keyword {
ident: Ident,
}
impl Keyword {
#[inline] pub fn ident(self) -> Ident { self.ident }
#[inline] pub fn name(self) -> Symbol { self.ident.name }
}
$(
#[allow(non_upper_case_globals)]
pub const $konst: Keyword = Keyword {
ident: Ident::with_empty_ctxt(super::Symbol::new($index))
};
)*
pub mod keywords {
use super::{Symbol, Ident};
impl std::str::FromStr for Keyword {
type Err = ();
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct Keyword {
ident: Ident,
}
fn from_str(s: &str) -> Result<Self, ()> {
match s {
$($string => Ok($konst),)*
_ => Err(()),
}
}
impl Keyword {
#[inline]
pub fn ident(self) -> Ident {
self.ident
}
#[inline]
pub fn name(self) -> Symbol {
self.ident.name
}
}
impl Interner {
pub fn fresh() -> Self {
Interner::prefill(&[$($string,)*])
}
}
}}
keywords!();
}
// N.B., 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`, `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,
// unnamed method parameters, crate root module, error recovery etc.
(0, Invalid, "")
(1, PathRoot, "{{root}}")
(2, DollarCrate, "$crate")
(3, Underscore, "_")
// Keywords that are used in stable Rust.
(4, As, "as")
(5, Box, "box")
(6, Break, "break")
(7, Const, "const")
(8, Continue, "continue")
(9, Crate, "crate")
(10, Else, "else")
(11, Enum, "enum")
(12, Extern, "extern")
(13, False, "false")
(14, Fn, "fn")
(15, For, "for")
(16, If, "if")
(17, Impl, "impl")
(18, In, "in")
(19, Let, "let")
(20, Loop, "loop")
(21, Match, "match")
(22, Mod, "mod")
(23, Move, "move")
(24, Mut, "mut")
(25, Pub, "pub")
(26, Ref, "ref")
(27, Return, "return")
(28, SelfLower, "self")
(29, SelfUpper, "Self")
(30, Static, "static")
(31, Struct, "struct")
(32, Super, "super")
(33, Trait, "trait")
(34, True, "true")
(35, Type, "type")
(36, Unsafe, "unsafe")
(37, Use, "use")
(38, Where, "where")
(39, While, "while")
// Keywords that are used in unstable Rust or reserved for future use.
(40, Abstract, "abstract")
(41, Become, "become")
(42, Do, "do")
(43, Final, "final")
(44, Macro, "macro")
(45, Override, "override")
(46, Priv, "priv")
(47, Typeof, "typeof")
(48, Unsized, "unsized")
(49, Virtual, "virtual")
(50, Yield, "yield")
// Edition-specific keywords that are used in stable Rust.
(51, Dyn, "dyn") // >= 2018 Edition only
// Edition-specific keywords that are used in unstable Rust or reserved for future use.
(52, Async, "async") // >= 2018 Edition only
(53, Try, "try") // >= 2018 Edition only
// Special lifetime names
(54, UnderscoreLifetime, "'_")
(55, StaticLifetime, "'static")
// Weak keywords, have special meaning only in specific contexts.
(56, Auto, "auto")
(57, Catch, "catch")
(58, Default, "default")
(59, Existential, "existential")
(60, Union, "union")
pub mod symbols {
use super::Symbol;
symbols!();
}
impl Symbol {