Skip to content

Commit

Permalink
rustc_span: Generate keyword classification functions automatically.
Browse files Browse the repository at this point in the history
  • Loading branch information
petrochenkov authored and nnethercote committed Aug 31, 2020
1 parent b9b11a0 commit 3b1aa36
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 31 deletions.
1 change: 1 addition & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3688,6 +3688,7 @@ dependencies = [
name = "rustc_macros"
version = "0.1.0"
dependencies = [
"indexmap",
"proc-macro2",
"quote",
"syn",
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ edition = "2018"
proc-macro = true

[dependencies]
indexmap = "1"
synstructure = "0.12.1"
syn = { version = "1", features = ["full"] }
proc-macro2 = "1"
Expand Down
81 changes: 63 additions & 18 deletions compiler/rustc_macros/src/symbols.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use indexmap::IndexMap;
use proc_macro::TokenStream;
use proc_macro2::TokenTree;
use quote::quote;
use std::collections::hash_map::RandomState;
use std::collections::HashSet;
use syn::parse::{Parse, ParseStream, Result};
use syn::{braced, parse_macro_input, Ident, LitStr, Token};
Expand Down Expand Up @@ -44,22 +47,43 @@ impl Parse for Symbol {
}
}

/// A type used to greedily parse another type until the input is empty.
struct List<T>(Vec<T>);
// Map from an optional keyword class to the list of keywords in it.
// FIXME: the indexmap crate thinks `has_std` is false when building `rustc_macros`,
// so we have to provide the hasher manually.
struct Keywords(IndexMap<Option<Ident>, Vec<Keyword>, RandomState>);

impl<T: Parse> Parse for List<T> {
impl Parse for Keywords {
fn parse(input: ParseStream<'_>) -> Result<Self> {
let mut classes = IndexMap::<_, Vec<_>, _>::with_hasher(Default::default());
let mut current_class = None;
while !input.is_empty() {
if input.peek(Token![fn]) {
input.parse::<TokenTree>()?;
current_class = Some(input.parse::<Ident>()?);
input.parse::<Token![:]>()?;
} else {
classes.entry(current_class.clone()).or_default().push(input.parse()?);
}
}
Ok(Keywords(classes))
}
}

struct Symbols(Vec<Symbol>);

impl Parse for Symbols {
fn parse(input: ParseStream<'_>) -> Result<Self> {
let mut list = Vec::new();
while !input.is_empty() {
list.push(input.parse()?);
}
Ok(List(list))
Ok(Symbols(list))
}
}

struct Input {
keywords: List<Keyword>,
symbols: List<Symbol>,
keywords: Keywords,
symbols: Symbols,
}

impl Parse for Input {
Expand All @@ -85,6 +109,7 @@ pub fn symbols(input: TokenStream) -> TokenStream {
let mut symbols_stream = quote! {};
let mut digits_stream = quote! {};
let mut prefill_stream = quote! {};
let mut keyword_class_stream = quote! {};
let mut counter = 0u32;
let mut keys = HashSet::<String>::new();
let mut prev_key: Option<String> = None;
Expand All @@ -106,18 +131,34 @@ pub fn symbols(input: TokenStream) -> TokenStream {
};

// Generate the listed keywords.
for keyword in &input.keywords.0 {
let name = &keyword.name;
let value = &keyword.value;
check_dup(&value.value(), &mut errors);
prefill_stream.extend(quote! {
#value,
});
keyword_stream.extend(quote! {
#[allow(non_upper_case_globals)]
pub const #name: Symbol = Symbol::new(#counter);
});
counter += 1;
for (class, keywords) in &input.keywords.0 {
let mut class_stream = quote! {};
for keyword in keywords {
let name = &keyword.name;
let value = &keyword.value;
check_dup(&value.value(), &mut errors);
prefill_stream.extend(quote! {
#value,
});
keyword_stream.extend(quote! {
#[allow(non_upper_case_globals)]
pub const #name: Symbol = Symbol::new(#counter);
});
class_stream.extend(quote! {
| kw::#name
});
counter += 1;
}
if let Some(class) = class {
keyword_class_stream.extend(quote! {
fn #class(self) -> bool {
match self {
#class_stream => true,
_ => false
}
}
});
}
}

// Generate the listed symbols.
Expand Down Expand Up @@ -185,6 +226,10 @@ pub fn symbols(input: TokenStream) -> TokenStream {
])
}
}

impl Symbol {
#keyword_class_stream
}
});

// To see the generated code generated, uncomment this line, recompile, and
Expand Down
25 changes: 12 additions & 13 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,14 @@ symbols! {
Keywords {
// Special reserved identifiers used internally for elided lifetimes,
// unnamed method parameters, crate root module, error recovery etc.
fn is_special:
Invalid: "",
PathRoot: "{{root}}",
DollarCrate: "$crate",
Underscore: "_",

// Keywords that are used in stable Rust.
// Keywords that are used in stable Rust on all editions.
fn is_used_keyword_20xx:
As: "as",
Break: "break",
Const: "const",
Expand Down Expand Up @@ -67,7 +69,8 @@ symbols! {
Where: "where",
While: "while",

// Keywords that are used in unstable Rust or reserved for future use.
// Keywords that are used in unstable Rust or reserved for future use on all editions.
fn is_unused_keyword_20xx:
Abstract: "abstract",
Become: "become",
Box: "box",
Expand All @@ -82,18 +85,22 @@ symbols! {
Yield: "yield",

// Edition-specific keywords that are used in stable Rust.
fn is_used_keyword_2018:
Async: "async", // >= 2018 Edition only
Await: "await", // >= 2018 Edition only
Dyn: "dyn", // >= 2018 Edition only

// Edition-specific keywords that are used in unstable Rust or reserved for future use.
fn is_unused_keyword_2018:
Try: "try", // >= 2018 Edition only

// Special lifetime names
fn is_special_lifetime:
UnderscoreLifetime: "'_",
StaticLifetime: "'static",

// Weak keywords, have special meaning only in specific contexts.
fn is_weak_keyword:
Auto: "auto",
Catch: "catch",
Default: "default",
Expand Down Expand Up @@ -1548,14 +1555,6 @@ pub mod sym {
}

impl Symbol {
fn is_used_keyword_2018(self) -> bool {
self >= kw::Async && self <= kw::Dyn
}

fn is_unused_keyword_2018(self) -> bool {
self == kw::Try
}

/// A keyword or reserved identifier that can be used as a path segment.
pub fn is_path_segment_keyword(self) -> bool {
self == kw::Super
Expand All @@ -1581,20 +1580,20 @@ 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 <= kw::Underscore
self.name.is_special()
}

/// Returns `true` if the token is a keyword used in the language.
pub fn is_used_keyword(self) -> bool {
// Note: `span.edition()` is relatively expensive, don't call it unless necessary.
self.name >= kw::As && self.name <= kw::While
self.name.is_used_keyword_20xx()
|| self.name.is_used_keyword_2018() && self.span.rust_2018()
}

/// 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 >= kw::Abstract && self.name <= kw::Yield
self.name.is_unused_keyword_20xx()
|| self.name.is_unused_keyword_2018() && self.span.rust_2018()
}

Expand Down

0 comments on commit 3b1aa36

Please sign in to comment.