Skip to content

Commit f02b037

Browse files
committed
rustc_span: Generate keyword classification functions automatically
1 parent e37b75e commit f02b037

File tree

4 files changed

+77
-31
lines changed

4 files changed

+77
-31
lines changed

Cargo.lock

+1
Original file line numberDiff line numberDiff line change
@@ -3625,6 +3625,7 @@ dependencies = [
36253625
name = "rustc_macros"
36263626
version = "0.1.0"
36273627
dependencies = [
3628+
"indexmap",
36283629
"proc-macro2",
36293630
"quote",
36303631
"syn",

src/librustc_macros/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ edition = "2018"
88
proc-macro = true
99

1010
[dependencies]
11+
indexmap = "1"
1112
synstructure = "0.12.1"
1213
syn = { version = "1", features = ["full"] }
1314
proc-macro2 = "1"

src/librustc_macros/src/symbols.rs

+63-18
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1+
use indexmap::IndexMap;
12
use proc_macro::TokenStream;
3+
use proc_macro2::TokenTree;
24
use quote::quote;
5+
use std::collections::hash_map::RandomState;
36
use std::collections::HashSet;
47
use syn::parse::{Parse, ParseStream, Result};
58
use syn::{braced, parse_macro_input, Ident, LitStr, Token};
@@ -44,22 +47,43 @@ impl Parse for Symbol {
4447
}
4548
}
4649

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

50-
impl<T: Parse> Parse for List<T> {
55+
impl Parse for Keywords {
56+
fn parse(input: ParseStream<'_>) -> Result<Self> {
57+
let mut classes = IndexMap::<_, Vec<_>, _>::with_hasher(Default::default());
58+
let mut current_class = None;
59+
while !input.is_empty() {
60+
if input.peek(Token![fn]) {
61+
input.parse::<TokenTree>()?;
62+
current_class = Some(input.parse::<Ident>()?);
63+
input.parse::<Token![:]>()?;
64+
} else {
65+
classes.entry(current_class.clone()).or_default().push(input.parse()?);
66+
}
67+
}
68+
Ok(Keywords(classes))
69+
}
70+
}
71+
72+
struct Symbols(Vec<Symbol>);
73+
74+
impl Parse for Symbols {
5175
fn parse(input: ParseStream<'_>) -> Result<Self> {
5276
let mut list = Vec::new();
5377
while !input.is_empty() {
5478
list.push(input.parse()?);
5579
}
56-
Ok(List(list))
80+
Ok(Symbols(list))
5781
}
5882
}
5983

6084
struct Input {
61-
keywords: List<Keyword>,
62-
symbols: List<Symbol>,
85+
keywords: Keywords,
86+
symbols: Symbols,
6387
}
6488

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

108133
// Generate the listed keywords.
109-
for keyword in &input.keywords.0 {
110-
let name = &keyword.name;
111-
let value = &keyword.value;
112-
check_dup(&value.value(), &mut errors);
113-
prefill_stream.extend(quote! {
114-
#value,
115-
});
116-
keyword_stream.extend(quote! {
117-
#[allow(non_upper_case_globals)]
118-
pub const #name: Symbol = Symbol::new(#counter);
119-
});
120-
counter += 1;
134+
for (class, keywords) in &input.keywords.0 {
135+
let mut class_stream = quote! {};
136+
for keyword in keywords {
137+
let name = &keyword.name;
138+
let value = &keyword.value;
139+
check_dup(&value.value(), &mut errors);
140+
prefill_stream.extend(quote! {
141+
#value,
142+
});
143+
keyword_stream.extend(quote! {
144+
#[allow(non_upper_case_globals)]
145+
pub const #name: Symbol = Symbol::new(#counter);
146+
});
147+
class_stream.extend(quote! {
148+
| kw::#name
149+
});
150+
counter += 1;
151+
}
152+
if let Some(class) = class {
153+
keyword_class_stream.extend(quote! {
154+
fn #class(self) -> bool {
155+
match self {
156+
#class_stream => true,
157+
_ => false
158+
}
159+
}
160+
});
161+
}
121162
}
122163

123164
// Generate the listed symbols.
@@ -185,6 +226,10 @@ pub fn symbols(input: TokenStream) -> TokenStream {
185226
])
186227
}
187228
}
229+
230+
impl Symbol {
231+
#keyword_class_stream
232+
}
188233
});
189234

190235
// To see the generated code generated, uncomment this line, recompile, and

src/librustc_span/symbol.rs

+12-13
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,14 @@ symbols! {
2525
Keywords {
2626
// Special reserved identifiers used internally for elided lifetimes,
2727
// unnamed method parameters, crate root module, error recovery etc.
28+
fn is_special:
2829
Invalid: "",
2930
PathRoot: "{{root}}",
3031
DollarCrate: "$crate",
3132
Underscore: "_",
3233

33-
// Keywords that are used in stable Rust.
34+
// Keywords that are used in stable Rust on all editions.
35+
fn is_used_keyword_20xx:
3436
As: "as",
3537
Break: "break",
3638
Const: "const",
@@ -67,7 +69,8 @@ symbols! {
6769
Where: "where",
6870
While: "while",
6971

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

8487
// Edition-specific keywords that are used in stable Rust.
88+
fn is_used_keyword_2018:
8589
Async: "async", // >= 2018 Edition only
8690
Await: "await", // >= 2018 Edition only
8791
Dyn: "dyn", // >= 2018 Edition only
8892

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

9297
// Special lifetime names
98+
fn is_special_lifetime:
9399
UnderscoreLifetime: "'_",
94100
StaticLifetime: "'static",
95101

96102
// Weak keywords, have special meaning only in specific contexts.
103+
fn is_weak_keyword:
97104
Auto: "auto",
98105
Catch: "catch",
99106
Default: "default",
@@ -1546,14 +1553,6 @@ pub mod sym {
15461553
}
15471554

15481555
impl Symbol {
1549-
fn is_used_keyword_2018(self) -> bool {
1550-
self >= kw::Async && self <= kw::Dyn
1551-
}
1552-
1553-
fn is_unused_keyword_2018(self) -> bool {
1554-
self == kw::Try
1555-
}
1556-
15571556
/// A keyword or reserved identifier that can be used as a path segment.
15581557
pub fn is_path_segment_keyword(self) -> bool {
15591558
self == kw::Super
@@ -1579,20 +1578,20 @@ impl Ident {
15791578
// Returns `true` for reserved identifiers used internally for elided lifetimes,
15801579
// unnamed method parameters, crate root module, error recovery etc.
15811580
pub fn is_special(self) -> bool {
1582-
self.name <= kw::Underscore
1581+
self.name.is_special()
15831582
}
15841583

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

15921591
/// Returns `true` if the token is a keyword reserved for possible future use.
15931592
pub fn is_unused_keyword(self) -> bool {
15941593
// Note: `span.edition()` is relatively expensive, don't call it unless necessary.
1595-
self.name >= kw::Abstract && self.name <= kw::Yield
1594+
self.name.is_unused_keyword_20xx()
15961595
|| self.name.is_unused_keyword_2018() && self.span.rust_2018()
15971596
}
15981597

0 commit comments

Comments
 (0)