Pre-expand Hash derives

This commit is contained in:
David Tolnay 2020-08-16 22:51:52 -07:00
parent f89b47203d
commit ac1124b354
No known key found for this signature in database
GPG Key ID: F9BA143B95FF6D82
12 changed files with 2853 additions and 606 deletions

164
codegen/src/hash.rs Normal file
View File

@ -0,0 +1,164 @@
use crate::{cfg, file, lookup};
use anyhow::Result;
use proc_macro2::{Ident, Span, TokenStream};
use quote::{format_ident, quote};
use syn_codegen::{Data, Definitions, Node, Type};
const DEBUG_SRC: &str = "../src/gen/hash.rs";
fn skip(field_type: &Type) -> bool {
match field_type {
Type::Syn(node) => node == "Reserved",
Type::Token(_) | Type::Group(_) => true,
Type::Box(inner) => skip(inner),
Type::Tuple(inner) => inner.iter().all(skip),
_ => false,
}
}
fn expand_impl_body(defs: &Definitions, node: &Node) -> TokenStream {
let type_name = &node.ident;
let ident = Ident::new(type_name, Span::call_site());
match &node.data {
Data::Enum(variants) => {
let arms = variants
.iter()
.enumerate()
.map(|(i, (variant_name, fields))| {
let i = i as u8;
let variant = Ident::new(variant_name, Span::call_site());
if fields.is_empty() {
quote! {
#ident::#variant => {
state.write_u8(#i);
}
}
} else {
let mut pats = Vec::new();
let mut hashes = Vec::new();
for (i, field) in fields.iter().enumerate() {
if skip(field) {
pats.push(format_ident!("_"));
continue;
}
let var = format_ident!("v{}", i);
let mut hashed_val = quote!(#var);
match field {
Type::Ext(ty) if ty == "TokenStream" => {
hashed_val = quote!(TokenStreamHelper(#hashed_val));
}
Type::Ext(ty) if ty == "Literal" => {
hashed_val = quote!(#hashed_val.to_string());
}
_ => {}
}
hashes.push(quote! {
#hashed_val.hash(state);
});
pats.push(var);
}
let mut cfg = None;
if node.ident == "Expr" {
if let Type::Syn(ty) = &fields[0] {
if !lookup::node(defs, ty).features.any.contains("derive") {
cfg = Some(quote!(#[cfg(feature = "full")]));
}
}
}
quote! {
#cfg
#ident::#variant(#(#pats),*) => {
state.write_u8(#i);
#(#hashes)*
}
}
}
});
let nonexhaustive = if node.exhaustive {
None
} else {
Some(quote!(_ => unreachable!()))
};
quote! {
match self {
#(#arms)*
#nonexhaustive
}
}
}
Data::Struct(fields) => fields
.iter()
.filter_map(|(f, ty)| {
if skip(ty) {
return None;
}
let ident = Ident::new(f, Span::call_site());
let mut val = quote!(self.#ident);
if let Type::Ext(ty) = ty {
if ty == "TokenStream" {
val = quote!(TokenStreamHelper(&#val));
}
}
Some(quote! {
#val.hash(state);
})
})
.collect(),
Data::Private => unreachable!(),
}
}
fn expand_impl(defs: &Definitions, node: &Node) -> TokenStream {
let manual_hash = node.data == Data::Private
|| node.ident == "LitBool"
|| node.ident == "Member"
|| node.ident == "Index"
|| node.ident == "Lifetime";
if manual_hash {
return TokenStream::new();
}
let ident = Ident::new(&node.ident, Span::call_site());
let cfg_features = cfg::features(&node.features);
let body = expand_impl_body(defs, node);
let state = if body.is_empty() {
quote!(_state)
} else {
quote!(state)
};
quote! {
#cfg_features
impl Hash for #ident {
fn hash<H>(&self, #state: &mut H)
where
H: Hasher,
{
#body
}
}
}
}
pub fn generate(defs: &Definitions) -> Result<()> {
let mut impls = TokenStream::new();
for node in &defs.types {
impls.extend(expand_impl(defs, node));
}
file::write(
DEBUG_SRC,
quote! {
use crate::*;
#[cfg(any(feature = "derive", feature = "full"))]
use crate::tt::TokenStreamHelper;
use std::hash::{Hash, Hasher};
#impls
},
)?;
Ok(())
}

View File

@ -18,6 +18,7 @@ mod file;
mod fold;
mod full;
mod gen;
mod hash;
mod json;
mod lookup;
mod operand;
@ -32,6 +33,7 @@ fn main() -> anyhow::Result<()> {
let defs = parse::parse()?;
debug::generate(&defs)?;
eq::generate(&defs)?;
hash::generate(&defs)?;
json::generate(&defs)?;
fold::generate(&defs)?;
visit::generate(&defs)?;

View File

@ -9,10 +9,6 @@ use proc_macro2::TokenStream;
use crate::parse::{Parse, ParseBuffer, ParseStream, Parser, Result};
#[cfg(feature = "parsing")]
use crate::punctuated::Pair;
#[cfg(feature = "extra-traits")]
use crate::tt::TokenStreamHelper;
#[cfg(feature = "extra-traits")]
use std::hash::{Hash, Hasher};
ast_struct! {
/// An attribute like `#[repr(transparent)]`.
@ -159,20 +155,6 @@ ast_struct! {
}
}
#[cfg(feature = "extra-traits")]
impl Hash for Attribute {
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
self.style.hash(state);
self.pound_token.hash(state);
self.bracket_token.hash(state);
self.path.hash(state);
TokenStreamHelper(&self.tokens).hash(state);
}
}
impl Attribute {
/// Parses the content of the attribute, consisting of the path and tokens,
/// as a [`Meta`] if possible.

View File

@ -2,8 +2,6 @@ use super::*;
use crate::punctuated::Punctuated;
#[cfg(feature = "full")]
use crate::reserved::Reserved;
#[cfg(feature = "extra-traits")]
use crate::tt::TokenStreamHelper;
use proc_macro2::{Span, TokenStream};
#[cfg(feature = "printing")]
use quote::IdentFragment;
@ -722,178 +720,6 @@ ast_struct! {
}
}
#[cfg(feature = "extra-traits")]
impl Hash for Expr {
fn hash<H>(&self, hash: &mut H)
where
H: Hasher,
{
match self {
Expr::Array(expr) => {
hash.write_u8(0);
expr.hash(hash);
}
Expr::Assign(expr) => {
hash.write_u8(1);
expr.hash(hash);
}
Expr::AssignOp(expr) => {
hash.write_u8(2);
expr.hash(hash);
}
Expr::Async(expr) => {
hash.write_u8(3);
expr.hash(hash);
}
Expr::Await(expr) => {
hash.write_u8(4);
expr.hash(hash);
}
Expr::Binary(expr) => {
hash.write_u8(5);
expr.hash(hash);
}
Expr::Block(expr) => {
hash.write_u8(6);
expr.hash(hash);
}
Expr::Box(expr) => {
hash.write_u8(7);
expr.hash(hash);
}
Expr::Break(expr) => {
hash.write_u8(8);
expr.hash(hash);
}
Expr::Call(expr) => {
hash.write_u8(9);
expr.hash(hash);
}
Expr::Cast(expr) => {
hash.write_u8(10);
expr.hash(hash);
}
Expr::Closure(expr) => {
hash.write_u8(11);
expr.hash(hash);
}
Expr::Continue(expr) => {
hash.write_u8(12);
expr.hash(hash);
}
Expr::Field(expr) => {
hash.write_u8(13);
expr.hash(hash);
}
Expr::ForLoop(expr) => {
hash.write_u8(14);
expr.hash(hash);
}
Expr::Group(expr) => {
hash.write_u8(15);
expr.hash(hash);
}
Expr::If(expr) => {
hash.write_u8(16);
expr.hash(hash);
}
Expr::Index(expr) => {
hash.write_u8(17);
expr.hash(hash);
}
Expr::Let(expr) => {
hash.write_u8(18);
expr.hash(hash);
}
Expr::Lit(expr) => {
hash.write_u8(19);
expr.hash(hash);
}
Expr::Loop(expr) => {
hash.write_u8(20);
expr.hash(hash);
}
Expr::Macro(expr) => {
hash.write_u8(21);
expr.hash(hash);
}
Expr::Match(expr) => {
hash.write_u8(22);
expr.hash(hash);
}
Expr::MethodCall(expr) => {
hash.write_u8(23);
expr.hash(hash);
}
Expr::Paren(expr) => {
hash.write_u8(24);
expr.hash(hash);
}
Expr::Path(expr) => {
hash.write_u8(25);
expr.hash(hash);
}
Expr::Range(expr) => {
hash.write_u8(26);
expr.hash(hash);
}
Expr::Reference(expr) => {
hash.write_u8(27);
expr.hash(hash);
}
Expr::Repeat(expr) => {
hash.write_u8(28);
expr.hash(hash);
}
Expr::Return(expr) => {
hash.write_u8(29);
expr.hash(hash);
}
Expr::Struct(expr) => {
hash.write_u8(30);
expr.hash(hash);
}
Expr::Try(expr) => {
hash.write_u8(31);
expr.hash(hash);
}
Expr::TryBlock(expr) => {
hash.write_u8(32);
expr.hash(hash);
}
Expr::Tuple(expr) => {
hash.write_u8(33);
expr.hash(hash);
}
Expr::Type(expr) => {
hash.write_u8(34);
expr.hash(hash);
}
Expr::Unary(expr) => {
hash.write_u8(35);
expr.hash(hash);
}
Expr::Unsafe(expr) => {
hash.write_u8(36);
expr.hash(hash);
}
Expr::Verbatim(expr) => {
hash.write_u8(37);
TokenStreamHelper(expr).hash(hash);
}
Expr::While(expr) => {
hash.write_u8(38);
expr.hash(hash);
}
Expr::Yield(expr) => {
hash.write_u8(39);
expr.hash(hash);
}
Expr::__Nonexhaustive => unreachable!(),
}
}
}
impl Expr {
#[cfg(all(feature = "parsing", feature = "full"))]
pub(crate) fn replace_attrs(&mut self, new: Vec<Attribute>) -> Vec<Attribute> {

2683
src/gen/hash.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@ -3,10 +3,6 @@ use crate::derive::{Data, DataEnum, DataStruct, DataUnion, DeriveInput};
use crate::punctuated::Punctuated;
use proc_macro2::TokenStream;
#[cfg(feature = "extra-traits")]
use crate::tt::TokenStreamHelper;
#[cfg(feature = "extra-traits")]
use std::hash::{Hash, Hasher};
#[cfg(feature = "parsing")]
use std::mem;
@ -326,86 +322,6 @@ ast_struct! {
}
}
#[cfg(feature = "extra-traits")]
impl Hash for Item {
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
match self {
Item::Const(item) => {
state.write_u8(0);
item.hash(state);
}
Item::Enum(item) => {
state.write_u8(1);
item.hash(state);
}
Item::ExternCrate(item) => {
state.write_u8(2);
item.hash(state);
}
Item::Fn(item) => {
state.write_u8(3);
item.hash(state);
}
Item::ForeignMod(item) => {
state.write_u8(4);
item.hash(state);
}
Item::Impl(item) => {
state.write_u8(5);
item.hash(state);
}
Item::Macro(item) => {
state.write_u8(6);
item.hash(state);
}
Item::Macro2(item) => {
state.write_u8(7);
item.hash(state);
}
Item::Mod(item) => {
state.write_u8(8);
item.hash(state);
}
Item::Static(item) => {
state.write_u8(9);
item.hash(state);
}
Item::Struct(item) => {
state.write_u8(10);
item.hash(state);
}
Item::Trait(item) => {
state.write_u8(11);
item.hash(state);
}
Item::TraitAlias(item) => {
state.write_u8(12);
item.hash(state);
}
Item::Type(item) => {
state.write_u8(13);
item.hash(state);
}
Item::Union(item) => {
state.write_u8(14);
item.hash(state);
}
Item::Use(item) => {
state.write_u8(15);
item.hash(state);
}
Item::Verbatim(item) => {
state.write_u8(16);
TokenStreamHelper(item).hash(state);
}
Item::__Nonexhaustive => unreachable!(),
}
}
}
impl Item {
#[cfg(feature = "parsing")]
pub(crate) fn replace_attrs(&mut self, new: Vec<Attribute>) -> Vec<Attribute> {
@ -432,20 +348,6 @@ impl Item {
}
}
#[cfg(feature = "extra-traits")]
impl Hash for ItemMacro2 {
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
self.attrs.hash(state);
self.vis.hash(state);
self.macro_token.hash(state);
self.ident.hash(state);
TokenStreamHelper(&self.rules).hash(state);
}
}
impl From<DeriveInput> for Item {
fn from(input: DeriveInput) -> Item {
match input.data {
@ -693,38 +595,6 @@ ast_struct! {
}
}
#[cfg(feature = "extra-traits")]
impl Hash for ForeignItem {
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
match self {
ForeignItem::Fn(item) => {
state.write_u8(0);
item.hash(state);
}
ForeignItem::Static(item) => {
state.write_u8(1);
item.hash(state);
}
ForeignItem::Type(item) => {
state.write_u8(2);
item.hash(state);
}
ForeignItem::Macro(item) => {
state.write_u8(3);
item.hash(state);
}
ForeignItem::Verbatim(item) => {
state.write_u8(4);
TokenStreamHelper(item).hash(state);
}
ForeignItem::__Nonexhaustive => unreachable!(),
}
}
}
ast_enum_of_structs! {
/// An item declaration within the definition of a trait.
///
@ -813,38 +683,6 @@ ast_struct! {
}
}
#[cfg(feature = "extra-traits")]
impl Hash for TraitItem {
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
match self {
TraitItem::Const(item) => {
state.write_u8(0);
item.hash(state);
}
TraitItem::Method(item) => {
state.write_u8(1);
item.hash(state);
}
TraitItem::Type(item) => {
state.write_u8(2);
item.hash(state);
}
TraitItem::Macro(item) => {
state.write_u8(3);
item.hash(state);
}
TraitItem::Verbatim(item) => {
state.write_u8(4);
TokenStreamHelper(item).hash(state);
}
TraitItem::__Nonexhaustive => unreachable!(),
}
}
}
ast_enum_of_structs! {
/// An item within an impl block.
///
@ -938,38 +776,6 @@ ast_struct! {
}
}
#[cfg(feature = "extra-traits")]
impl Hash for ImplItem {
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
match self {
ImplItem::Const(item) => {
state.write_u8(0);
item.hash(state);
}
ImplItem::Method(item) => {
state.write_u8(1);
item.hash(state);
}
ImplItem::Type(item) => {
state.write_u8(2);
item.hash(state);
}
ImplItem::Macro(item) => {
state.write_u8(3);
item.hash(state);
}
ImplItem::Verbatim(item) => {
state.write_u8(4);
TokenStreamHelper(item).hash(state);
}
ImplItem::__Nonexhaustive => unreachable!(),
}
}
}
ast_struct! {
/// A function signature in a trait or implementation: `unsafe fn
/// initialize(&self)`.

View File

@ -770,6 +770,10 @@ mod gen {
#[rustfmt::skip]
mod eq;
#[cfg(feature = "extra-traits")]
#[rustfmt::skip]
mod hash;
#[cfg(feature = "extra-traits")]
#[rustfmt::skip]
mod debug;

View File

@ -131,49 +131,6 @@ ast_struct! {
}
}
#[cfg(feature = "extra-traits")]
impl Hash for Lit {
fn hash<H>(&self, hash: &mut H)
where
H: Hasher,
{
match self {
Lit::Str(lit) => {
hash.write_u8(0);
lit.hash(hash);
}
Lit::ByteStr(lit) => {
hash.write_u8(1);
lit.hash(hash);
}
Lit::Byte(lit) => {
hash.write_u8(2);
lit.hash(hash);
}
Lit::Char(lit) => {
hash.write_u8(3);
lit.hash(hash);
}
Lit::Int(lit) => {
hash.write_u8(4);
lit.hash(hash);
}
Lit::Float(lit) => {
hash.write_u8(5);
lit.hash(hash);
}
Lit::Bool(lit) => {
hash.write_u8(6);
lit.hash(hash);
}
Lit::Verbatim(lit) => {
hash.write_u8(7);
lit.to_string().hash(hash);
}
}
}
}
impl LitStr {
pub fn new(value: &str, span: Span) -> Self {
let mut token = Literal::string(value);

View File

@ -6,10 +6,6 @@ use proc_macro2::{Delimiter, Group, Span, TokenTree};
#[cfg(feature = "parsing")]
use crate::parse::{Parse, ParseStream, Parser, Result};
#[cfg(feature = "extra-traits")]
use crate::tt::TokenStreamHelper;
#[cfg(feature = "extra-traits")]
use std::hash::{Hash, Hasher};
ast_struct! {
/// A macro invocation: `println!("{}", mac)`.
@ -36,19 +32,6 @@ ast_enum! {
}
}
#[cfg(feature = "extra-traits")]
impl Hash for Macro {
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
self.path.hash(state);
self.bang_token.hash(state);
self.delimiter.hash(state);
TokenStreamHelper(&self.tokens).hash(state);
}
}
#[cfg(feature = "parsing")]
fn delimiter_span_close(macro_delimiter: &MacroDelimiter) -> Span {
let delimiter = match macro_delimiter {

View File

@ -4,12 +4,10 @@ macro_rules! ast_struct {
struct $name:ident #full $($rest:tt)*
) => {
#[cfg(feature = "full")]
#[cfg_attr(feature = "extra-traits", derive(Hash))]
#[cfg_attr(feature = "clone-impls", derive(Clone))]
$($attrs_pub)* struct $name $($rest)*
#[cfg(not(feature = "full"))]
#[cfg_attr(feature = "extra-traits", derive(Hash))]
#[cfg_attr(feature = "clone-impls", derive(Clone))]
$($attrs_pub)* struct $name {
_noconstruct: ::std::marker::PhantomData<::proc_macro2::Span>,
@ -35,7 +33,6 @@ macro_rules! ast_struct {
[$($attrs_pub:tt)*]
struct $name:ident $($rest:tt)*
) => {
#[cfg_attr(feature = "extra-traits", derive(Hash))]
#[cfg_attr(feature = "clone-impls", derive(Clone))]
$($attrs_pub)* struct $name $($rest)*
};
@ -66,7 +63,6 @@ macro_rules! ast_enum {
[$($attrs_pub:tt)*]
enum $name:ident $($rest:tt)*
) => (
#[cfg_attr(feature = "extra-traits", derive(Hash))]
#[cfg_attr(feature = "clone-impls", derive(Clone))]
$($attrs_pub)* enum $name $($rest)*
);

View File

@ -1,10 +1,6 @@
use super::*;
use crate::punctuated::Punctuated;
#[cfg(feature = "extra-traits")]
use crate::tt::TokenStreamHelper;
use proc_macro2::TokenStream;
#[cfg(feature = "extra-traits")]
use std::hash::{Hash, Hasher};
ast_enum_of_structs! {
/// A pattern in a local binding, function signature, match expression, or
@ -278,82 +274,6 @@ ast_struct! {
}
}
#[cfg(feature = "extra-traits")]
impl Hash for Pat {
fn hash<H>(&self, hash: &mut H)
where
H: Hasher,
{
match self {
Pat::Box(pat) => {
hash.write_u8(0);
pat.hash(hash);
}
Pat::Ident(pat) => {
hash.write_u8(1);
pat.hash(hash);
}
Pat::Lit(pat) => {
hash.write_u8(2);
pat.hash(hash);
}
Pat::Macro(pat) => {
hash.write_u8(3);
pat.hash(hash);
}
Pat::Or(pat) => {
hash.write_u8(4);
pat.hash(hash);
}
Pat::Path(pat) => {
hash.write_u8(5);
pat.hash(hash);
}
Pat::Range(pat) => {
hash.write_u8(6);
pat.hash(hash);
}
Pat::Reference(pat) => {
hash.write_u8(7);
pat.hash(hash);
}
Pat::Rest(pat) => {
hash.write_u8(8);
pat.hash(hash);
}
Pat::Slice(pat) => {
hash.write_u8(9);
pat.hash(hash);
}
Pat::Struct(pat) => {
hash.write_u8(10);
pat.hash(hash);
}
Pat::Tuple(pat) => {
hash.write_u8(11);
pat.hash(hash);
}
Pat::TupleStruct(pat) => {
hash.write_u8(12);
pat.hash(hash);
}
Pat::Type(pat) => {
hash.write_u8(13);
pat.hash(hash);
}
Pat::Verbatim(pat) => {
hash.write_u8(14);
TokenStreamHelper(pat).hash(hash);
}
Pat::Wild(pat) => {
hash.write_u8(15);
pat.hash(hash);
}
Pat::__Nonexhaustive => unreachable!(),
}
}
}
#[cfg(feature = "parsing")]
pub mod parsing {
use super::*;

View File

@ -1,10 +1,6 @@
use super::*;
use crate::punctuated::Punctuated;
#[cfg(feature = "extra-traits")]
use crate::tt::TokenStreamHelper;
use proc_macro2::TokenStream;
#[cfg(feature = "extra-traits")]
use std::hash::{Hash, Hasher};
ast_enum_of_structs! {
/// The possible types that a Rust value could have.
@ -240,78 +236,6 @@ ast_struct! {
}
}
#[cfg(feature = "extra-traits")]
impl Hash for Type {
fn hash<H>(&self, hash: &mut H)
where
H: Hasher,
{
match self {
Type::Array(ty) => {
hash.write_u8(0);
ty.hash(hash);
}
Type::BareFn(ty) => {
hash.write_u8(1);
ty.hash(hash);
}
Type::Group(ty) => {
hash.write_u8(2);
ty.hash(hash);
}
Type::ImplTrait(ty) => {
hash.write_u8(3);
ty.hash(hash);
}
Type::Infer(ty) => {
hash.write_u8(4);
ty.hash(hash);
}
Type::Macro(ty) => {
hash.write_u8(5);
ty.hash(hash);
}
Type::Never(ty) => {
hash.write_u8(6);
ty.hash(hash);
}
Type::Paren(ty) => {
hash.write_u8(7);
ty.hash(hash);
}
Type::Path(ty) => {
hash.write_u8(8);
ty.hash(hash);
}
Type::Ptr(ty) => {
hash.write_u8(9);
ty.hash(hash);
}
Type::Reference(ty) => {
hash.write_u8(10);
ty.hash(hash);
}
Type::Slice(ty) => {
hash.write_u8(11);
ty.hash(hash);
}
Type::TraitObject(ty) => {
hash.write_u8(12);
ty.hash(hash);
}
Type::Tuple(ty) => {
hash.write_u8(13);
ty.hash(hash);
}
Type::Verbatim(ty) => {
hash.write_u8(14);
TokenStreamHelper(ty).hash(hash);
}
Type::__Nonexhaustive => unreachable!(),
}
}
}
ast_struct! {
/// The binary interface of a function: `extern "C"`.
///