From 10855a36b53d33aa2e4f8b98107ee54a0cca5e9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Wed, 3 Apr 2019 02:43:49 +0200 Subject: [PATCH 1/5] Use a proc macro to declare preallocated symbols --- Cargo.lock | 1 + src/librustc_macros/src/lib.rs | 6 + src/librustc_macros/src/symbols.rs | 173 +++++++++++++++++++++++ src/libsyntax/ast.rs | 6 + src/libsyntax/attr/mod.rs | 21 +++ src/libsyntax/feature_gate.rs | 28 ++-- src/libsyntax_pos/Cargo.toml | 1 + src/libsyntax_pos/lib.rs | 2 + src/libsyntax_pos/symbol.rs | 213 ++++++++++++----------------- 9 files changed, 312 insertions(+), 139 deletions(-) create mode 100644 src/librustc_macros/src/symbols.rs diff --git a/Cargo.lock b/Cargo.lock index 1ad9dbd4ea5d8..80a4029e1aa09 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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)", diff --git a/src/librustc_macros/src/lib.rs b/src/librustc_macros/src/lib.rs index e99ceb1b0c79b..98fba55218f9b 100644 --- a/src/librustc_macros/src/lib.rs +++ b/src/librustc_macros/src/lib.rs @@ -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); diff --git a/src/librustc_macros/src/symbols.rs b/src/librustc_macros/src/symbols.rs new file mode 100644 index 0000000000000..e72ab7f84e92a --- /dev/null +++ b/src/librustc_macros/src/symbols.rs @@ -0,0 +1,173 @@ +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 { + let name = input.parse()?; + input.parse::()?; + let value = input.parse()?; + input.parse::()?; + + Ok(Keyword { + name, + value, + }) + } +} + +struct Symbol(Ident); + +impl Parse for Symbol { + fn parse(input: ParseStream<'_>) -> Result { + let ident: Ident = input.parse()?; + input.parse::()?; + + Ok(Symbol(ident)) + } +} + +/// A type used to greedily parse another type until the input is empty. +struct List(Vec); + +impl Parse for List { + fn parse(input: ParseStream<'_>) -> Result { + let mut list = Vec::new(); + while !input.is_empty() { + list.push(input.parse()?); + } + Ok(List(list)) + } +} + +struct Input { + keywords: List, + symbols: List, +} + +impl Parse for Input { + fn parse(input: ParseStream<'_>) -> Result { + input.parse::()?; + let content; + braced!(content in input); + let keywords = content.parse()?; + + input.parse::()?; + 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::::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! { + #[allow(non_upper_case_globals)] + 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 } + } + + #keyword_stream + + impl std::str::FromStr for Keyword { + type Err = (); + + fn from_str(s: &str) -> Result { + match s { + #from_str_stream + _ => Err(()), + } + } + } + } + + #[allow(non_upper_case_globals)] + pub mod symbols { + use super::Symbol; + + #symbols_stream + } + + impl Interner { + pub fn fresh() -> Self { + Interner::prefill(&[ + #prefill_stream + ]) + } + } + }) +} diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index cf909e30e322a..c015e9b06acdb 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -68,6 +68,12 @@ pub struct Path { pub segments: Vec, } +impl PartialEq for Path { + fn eq(&self, symbol: &Symbol) -> bool { + self.segments.len() == 1 && self.segments[0].ident.name.interned() == *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 diff --git a/src/libsyntax/attr/mod.rs b/src/libsyntax/attr/mod.rs index c0bd5c79b1dd1..f34bbc9f35dad 100644 --- a/src/libsyntax/attr/mod.rs +++ b/src/libsyntax/attr/mod.rs @@ -85,6 +85,11 @@ impl NestedMetaItem { self.meta_item().map_or(false, |meta_item| meta_item.check_name(name)) } + /// Returns `true` if this list item is a MetaItem with a name of `name`. + pub fn check_name_symbol(&self, name: Symbol) -> bool { + self.meta_item().map_or(false, |meta_item| meta_item.check_name_symbol(name)) + } + /// For a single-segment meta-item returns its name, otherwise returns `None`. pub fn ident(&self) -> Option { self.meta_item().and_then(|meta_item| meta_item.ident()) @@ -159,6 +164,18 @@ impl Attribute { matches } + /// Returns `true` if the attribute's path matches the argument. If it matches, then the + /// attribute is marked as used. + /// + /// To check the attribute name without marking it used, use the `path` field directly. + pub fn check_name_symbol(&self, name: Symbol) -> bool { + let matches = self.path == name; + if matches { + mark_used(self); + } + matches + } + /// For a single-segment attribute returns its name, otherwise returns `None`. pub fn ident(&self) -> Option { if self.path.segments.len() == 1 { @@ -248,6 +265,10 @@ impl MetaItem { self.path == name } + pub fn check_name_symbol(&self, name: Symbol) -> bool { + self.path == name + } + pub fn is_value_str(&self) -> bool { self.value_str().is_some() } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index f77593ed02a14..b3e5d808a476d 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -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_symbol(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_symbol(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_symbol(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_symbol(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_symbol(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_symbol(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_symbol(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_symbol(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_symbol(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_symbol(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_symbol(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_symbol(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", diff --git a/src/libsyntax_pos/Cargo.toml b/src/libsyntax_pos/Cargo.toml index 691abffbbc1af..af7edc0a6bd3e 100644 --- a/src/libsyntax_pos/Cargo.toml +++ b/src/libsyntax_pos/Cargo.toml @@ -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" diff --git a/src/libsyntax_pos/lib.rs b/src/libsyntax_pos/lib.rs index 81cf804cf0b73..1ad556bbcfa56 100644 --- a/src/libsyntax_pos/lib.rs +++ b/src/libsyntax_pos/lib.rs @@ -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; diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs index d2fb67f8c066f..8c195fb45ef7b 100644 --- a/src/libsyntax_pos/symbol.rs +++ b/src/libsyntax_pos/symbol.rs @@ -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,93 @@ 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, + } +} + #[derive(Copy, Clone, Eq)] pub struct Ident { pub name: Symbol, @@ -317,131 +405,6 @@ 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)) - }; - )* - - impl std::str::FromStr for Keyword { - type Err = (); - - fn from_str(s: &str) -> Result { - match s { - $($string => Ok($konst),)* - _ => Err(()), - } - } - } - } - - impl Interner { - pub fn fresh() -> Self { - Interner::prefill(&[$($string,)*]) - } - } -}} - -// 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") -} - impl Symbol { fn is_used_keyword_2018(self) -> bool { self == keywords::Dyn.name() From 61a7a60d45fd20bee343a37693969a32e63cb905 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Fri, 5 Apr 2019 03:05:30 +0200 Subject: [PATCH 2/5] Make check_name generic --- src/librustc/middle/lib_features.rs | 6 ++-- .../persist/dirty_clean.rs | 2 +- src/librustc_lint/unused.rs | 2 +- src/libsyntax/attr/mod.rs | 36 +++++++------------ src/libsyntax/feature_gate.rs | 24 ++++++------- src/libsyntax_ext/proc_macro_decls.rs | 2 +- src/libsyntax_pos/symbol.rs | 3 +- 7 files changed, 32 insertions(+), 43 deletions(-) diff --git a/src/librustc/middle/lib_features.rs b/src/librustc/middle/lib_features.rs index ee8dd9e58b5ff..e79ef8bfc6fb4 100644 --- a/src/librustc/middle/lib_features.rs +++ b/src/librustc/middle/lib_features.rs @@ -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, 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 { diff --git a/src/librustc_incremental/persist/dirty_clean.rs b/src/librustc_incremental/persist/dirty_clean.rs index 5c4fd9dba7af4..6f5b411946c2d 100644 --- a/src/librustc_incremental/persist/dirty_clean.rs +++ b/src/librustc_incremental/persist/dirty_clean.rs @@ -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; } } diff --git a/src/librustc_lint/unused.rs b/src/librustc_lint/unused.rs index d41d97f58bcbe..2aee21abb58e5 100644 --- a/src/librustc_lint/unused.rs +++ b/src/librustc_lint/unused.rs @@ -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; } diff --git a/src/libsyntax/attr/mod.rs b/src/libsyntax/attr/mod.rs index f34bbc9f35dad..e00f91e395280 100644 --- a/src/libsyntax/attr/mod.rs +++ b/src/libsyntax/attr/mod.rs @@ -81,15 +81,13 @@ 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(&self, name: T) -> bool + where + Path: PartialEq, + { self.meta_item().map_or(false, |meta_item| meta_item.check_name(name)) } - /// Returns `true` if this list item is a MetaItem with a name of `name`. - pub fn check_name_symbol(&self, name: Symbol) -> bool { - self.meta_item().map_or(false, |meta_item| meta_item.check_name_symbol(name)) - } - /// For a single-segment meta-item returns its name, otherwise returns `None`. pub fn ident(&self) -> Option { self.meta_item().and_then(|meta_item| meta_item.ident()) @@ -156,19 +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 { - let matches = self.path == name; - if matches { - mark_used(self); - } - matches - } - - /// Returns `true` if the attribute's path matches the argument. If it matches, then the - /// attribute is marked as used. - /// - /// To check the attribute name without marking it used, use the `path` field directly. - pub fn check_name_symbol(&self, name: Symbol) -> bool { + pub fn check_name(&self, name: T) -> bool + where + Path: PartialEq, + { let matches = self.path == name; if matches { mark_used(self); @@ -261,11 +250,10 @@ impl MetaItem { } } - pub fn check_name(&self, name: &str) -> bool { - self.path == name - } - - pub fn check_name_symbol(&self, name: Symbol) -> bool { + pub fn check_name(&self, name: T) -> bool + where + Path: PartialEq, + { self.path == name } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index b3e5d808a476d..ba4380416fb2a 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -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_symbol(symbols::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_symbol(symbols::doc) { + if attr.check_name(symbols::doc) { if let Some(content) = attr.meta_item_list() { - if content.len() == 1 && content[0].check_name_symbol(symbols::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_symbol(symbols::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_symbol(symbols::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_symbol(symbols::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_symbol(symbols::keyword)) { + } else if content.iter().any(|c| c.check_name(symbols::keyword)) { gate_feature_post!(&self, doc_keyword, attr.span, "#[doc(keyword = \"...\")] is experimental" ); @@ -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_symbol(symbols::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_symbol(symbols::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_symbol(symbols::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_symbol(symbols::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_symbol(symbols::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", diff --git a/src/libsyntax_ext/proc_macro_decls.rs b/src/libsyntax_ext/proc_macro_decls.rs index 22fee902aea26..f0390ba3d40cb 100644 --- a/src/libsyntax_ext/proc_macro_decls.rs +++ b/src/libsyntax_ext/proc_macro_decls.rs @@ -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> { diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs index 8c195fb45ef7b..83cb6f4e86b0e 100644 --- a/src/libsyntax_pos/symbol.rs +++ b/src/libsyntax_pos/symbol.rs @@ -100,7 +100,8 @@ symbols! { // Other symbols that can be referred to with syntax_pos::symbols::* Other { - doc, cfg, masked, spotlight, alias, keyword, feature, include, simd, align, + doc, cfg, masked, spotlight, alias, keyword, feature, include, simd, align, stable, + unstable, rustc_const_unstable, } } From afeda72dad761325ea759fd2008730e24a165b9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Tue, 9 Apr 2019 09:18:49 +0200 Subject: [PATCH 3/5] Ensure the symbols are pure strings --- src/libsyntax/ast.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index c015e9b06acdb..0668730b3ef01 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -70,7 +70,13 @@ pub struct Path { impl PartialEq for Path { fn eq(&self, symbol: &Symbol) -> bool { - self.segments.len() == 1 && self.segments[0].ident.name.interned() == *symbol + 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 + } } } From baebf79057fba32c27c3353aacbd389692a54a0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Tue, 9 Apr 2019 09:36:17 +0200 Subject: [PATCH 4/5] Move modules outside the proc macro --- src/librustc_macros/src/symbols.rs | 42 ++++++++++++------------------ src/libsyntax_pos/symbol.rs | 28 ++++++++++++++++++++ 2 files changed, 44 insertions(+), 26 deletions(-) diff --git a/src/librustc_macros/src/symbols.rs b/src/librustc_macros/src/symbols.rs index e72ab7f84e92a..15f4d988a1f65 100644 --- a/src/librustc_macros/src/symbols.rs +++ b/src/librustc_macros/src/symbols.rs @@ -129,37 +129,27 @@ pub fn symbols(input: TokenStream) -> TokenStream { } TokenStream::from(quote! { - #[allow(non_upper_case_globals)] - 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 } - } - - #keyword_stream - - impl std::str::FromStr for Keyword { - type Err = (); - - fn from_str(s: &str) -> Result { - match s { - #from_str_stream - _ => Err(()), + macro_rules! keywords { + () => { + #keyword_stream + + impl std::str::FromStr for Keyword { + type Err = (); + + fn from_str(s: &str) -> Result { + match s { + #from_str_stream + _ => Err(()), + } } } } } - #[allow(non_upper_case_globals)] - pub mod symbols { - use super::Symbol; - - #symbols_stream + macro_rules! symbols { + () => { + #symbols_stream + } } impl Interner { diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs index 83cb6f4e86b0e..a5611d9ab147f 100644 --- a/src/libsyntax_pos/symbol.rs +++ b/src/libsyntax_pos/symbol.rs @@ -406,6 +406,34 @@ impl Interner { } } +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 + } + } + + keywords!(); +} + +pub mod symbols { + use super::Symbol; + symbols!(); +} + impl Symbol { fn is_used_keyword_2018(self) -> bool { self == keywords::Dyn.name() From f598091c055100daa1dc0d9c3f0f0a10209a0a84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Tue, 9 Apr 2019 09:39:48 +0200 Subject: [PATCH 5/5] Use colon for keyword defs --- src/librustc_macros/src/symbols.rs | 2 +- src/libsyntax_pos/symbol.rs | 122 ++++++++++++++--------------- 2 files changed, 62 insertions(+), 62 deletions(-) diff --git a/src/librustc_macros/src/symbols.rs b/src/librustc_macros/src/symbols.rs index 15f4d988a1f65..169c5e1371833 100644 --- a/src/librustc_macros/src/symbols.rs +++ b/src/librustc_macros/src/symbols.rs @@ -22,7 +22,7 @@ struct Keyword { impl Parse for Keyword { fn parse(input: ParseStream<'_>) -> Result { let name = input.parse()?; - input.parse::()?; + input.parse::()?; let value = input.parse()?; input.parse::()?; diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs index a5611d9ab147f..c89bccd5afbf8 100644 --- a/src/libsyntax_pos/symbol.rs +++ b/src/libsyntax_pos/symbol.rs @@ -23,79 +23,79 @@ symbols! { Keywords { // Special reserved identifiers used internally for elided lifetimes, // unnamed method parameters, crate root module, error recovery etc. - Invalid, "", - PathRoot, "{{root}}", - DollarCrate, "$crate", - Underscore, "_", + 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", + 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", + 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 + 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 + Async: "async", // >= 2018 Edition only + Try: "try", // >= 2018 Edition only // Special lifetime names - UnderscoreLifetime, "'_", - StaticLifetime, "'static", + UnderscoreLifetime: "'_", + StaticLifetime: "'static", // Weak keywords, have special meaning only in specific contexts. - Auto, "auto", - Catch, "catch", - Default, "default", - Existential, "existential", - Union, "union", + Auto: "auto", + Catch: "catch", + Default: "default", + Existential: "existential", + Union: "union", } // Other symbols that can be referred to with syntax_pos::symbols::*