Skip to content

Commit

Permalink
Remove dependency on syn/visit feature
Browse files Browse the repository at this point in the history
The builtin visitor is fairly expensive to compile (3700 lines of code),
particularly if something else in the dependency graph also enables
syn/full. For the usage in serde_derive, it turns out to be easy to
replace.
  • Loading branch information
dtolnay committed Jan 24, 2021
1 parent 2fd5212 commit 85c6904
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 5 deletions.
2 changes: 1 addition & 1 deletion serde_derive/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ proc-macro = true
[dependencies]
proc-macro2 = "1.0"
quote = "1.0"
syn = { version = "1.0.58", features = ["visit"] }
syn = "1.0.60"

[dev-dependencies]
serde = { version = "1.0", path = "../serde" }
Expand Down
97 changes: 94 additions & 3 deletions serde_derive/src/bound.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ use std::collections::HashSet;

use syn;
use syn::punctuated::{Pair, Punctuated};
use syn::visit::{self, Visit};

use internals::ast::{Container, Data};
use internals::{attr, ungroup};
Expand Down Expand Up @@ -112,7 +111,8 @@ pub fn with_bound(
// parameters.
associated_type_usage: Vec<&'ast syn::TypePath>,
}
impl<'ast> Visit<'ast> for FindTyParams<'ast> {

impl<'ast> FindTyParams<'ast> {
fn visit_field(&mut self, field: &'ast syn::Field) {
if let syn::Type::Path(ty) = ungroup(&field.ty) {
if let Some(Pair::Punctuated(t, _)) = ty.path.segments.pairs().next() {
Expand All @@ -138,7 +138,98 @@ pub fn with_bound(
self.relevant_type_params.insert(id.clone());
}
}
visit::visit_path(self, path);
for segment in &path.segments {
self.visit_path_segment(segment);
}
}

// Everything below is simply traversing the syntax tree.

fn visit_type(&mut self, ty: &'ast syn::Type) {
match ty {
syn::Type::Array(ty) => self.visit_type(&ty.elem),
syn::Type::BareFn(ty) => {
for arg in &ty.inputs {
self.visit_type(&arg.ty);
}
self.visit_return_type(&ty.output);
}
syn::Type::Group(ty) => self.visit_type(&ty.elem),
syn::Type::ImplTrait(ty) => {
for bound in &ty.bounds {
self.visit_type_param_bound(bound);
}
}
syn::Type::Macro(ty) => self.visit_macro(&ty.mac),
syn::Type::Paren(ty) => self.visit_type(&ty.elem),
syn::Type::Path(ty) => {
if let Some(qself) = &ty.qself {
self.visit_type(&qself.ty);
}
self.visit_path(&ty.path);
}
syn::Type::Ptr(ty) => self.visit_type(&ty.elem),
syn::Type::Reference(ty) => self.visit_type(&ty.elem),
syn::Type::Slice(ty) => self.visit_type(&ty.elem),
syn::Type::TraitObject(ty) => {
for bound in &ty.bounds {
self.visit_type_param_bound(bound);
}
}
syn::Type::Tuple(ty) => {
for elem in &ty.elems {
self.visit_type(elem);
}
}

syn::Type::Infer(_) | syn::Type::Never(_) | syn::Type::Verbatim(_) => {}

#[cfg(test)]
syn::Type::__TestExhaustive(_) => unimplemented!(),
#[cfg(not(test))]
_ => {}
}
}

fn visit_path_segment(&mut self, segment: &'ast syn::PathSegment) {
self.visit_path_arguments(&segment.arguments);
}

fn visit_path_arguments(&mut self, arguments: &'ast syn::PathArguments) {
match arguments {
syn::PathArguments::None => {}
syn::PathArguments::AngleBracketed(arguments) => {
for arg in &arguments.args {
match arg {
syn::GenericArgument::Type(arg) => self.visit_type(arg),
syn::GenericArgument::Binding(arg) => self.visit_type(&arg.ty),
syn::GenericArgument::Lifetime(_)
| syn::GenericArgument::Constraint(_)
| syn::GenericArgument::Const(_) => {}
}
}
}
syn::PathArguments::Parenthesized(arguments) => {
for argument in &arguments.inputs {
self.visit_type(argument);
}
self.visit_return_type(&arguments.output);
}
}
}

fn visit_return_type(&mut self, return_type: &'ast syn::ReturnType) {
match return_type {
syn::ReturnType::Default => {}
syn::ReturnType::Type(_, output) => self.visit_type(output),
}
}

fn visit_type_param_bound(&mut self, bound: &'ast syn::TypeParamBound) {
match bound {
syn::TypeParamBound::Trait(bound) => self.visit_path(&bound.path),
syn::TypeParamBound::Lifetime(_) => {}
}
}

// Type parameter should not be considered used by a macro path.
Expand Down
2 changes: 1 addition & 1 deletion serde_derive_internals/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ path = "lib.rs"
[dependencies]
proc-macro2 = "1.0"
quote = "1.0"
syn = { version = "1.0.33", default-features = false, features = ["derive", "parsing", "printing", "clone-impls"] }
syn = { version = "1.0.60", default-features = false, features = ["derive", "parsing", "printing", "clone-impls"] }

[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

0 comments on commit 85c6904

Please sign in to comment.