aboutsummaryrefslogtreecommitdiff
path: root/syn/codegen/src
diff options
context:
space:
mode:
Diffstat (limited to 'syn/codegen/src')
-rw-r--r--syn/codegen/src/debug.rs308
-rw-r--r--syn/codegen/src/file.rs32
-rw-r--r--syn/codegen/src/fold.rs284
-rw-r--r--syn/codegen/src/full.rs20
-rw-r--r--syn/codegen/src/gen.rs45
-rw-r--r--syn/codegen/src/json.rs18
-rw-r--r--syn/codegen/src/main.rs36
-rw-r--r--syn/codegen/src/operand.rs38
-rw-r--r--syn/codegen/src/parse.rs657
-rw-r--r--syn/codegen/src/version.rs24
-rw-r--r--syn/codegen/src/visit.rs265
-rw-r--r--syn/codegen/src/visit_mut.rs262
12 files changed, 0 insertions, 1989 deletions
diff --git a/syn/codegen/src/debug.rs b/syn/codegen/src/debug.rs
deleted file mode 100644
index 9193881..0000000
--- a/syn/codegen/src/debug.rs
+++ /dev/null
@@ -1,308 +0,0 @@
-use crate::file;
-use anyhow::Result;
-use proc_macro2::{Ident, Span, TokenStream};
-use quote::quote;
-use syn::Index;
-use syn_codegen::{Data, Definitions, Node, Type};
-
-const DEBUG_SRC: &str = "../tests/debug/gen.rs";
-
-fn rust_type(ty: &Type) -> TokenStream {
- match ty {
- Type::Syn(ty) => {
- let ident = Ident::new(ty, Span::call_site());
- quote!(syn::#ident)
- }
- Type::Std(ty) => {
- let ident = Ident::new(ty, Span::call_site());
- quote!(#ident)
- }
- Type::Ext(ty) => {
- let ident = Ident::new(ty, Span::call_site());
- quote!(proc_macro2::#ident)
- }
- Type::Token(ty) | Type::Group(ty) => {
- let ident = Ident::new(ty, Span::call_site());
- quote!(syn::token::#ident)
- }
- Type::Punctuated(ty) => {
- let element = rust_type(&ty.element);
- let punct = Ident::new(&ty.punct, Span::call_site());
- quote!(syn::punctuated::Punctuated<#element, #punct>)
- }
- Type::Option(ty) => {
- let inner = rust_type(ty);
- quote!(Option<#inner>)
- }
- Type::Box(ty) => {
- let inner = rust_type(ty);
- quote!(Box<#inner>)
- }
- Type::Vec(ty) => {
- let inner = rust_type(ty);
- quote!(Vec<#inner>)
- }
- Type::Tuple(ty) => {
- let inner = ty.iter().map(rust_type);
- quote!((#(#inner,)*))
- }
- }
-}
-
-fn is_printable(ty: &Type) -> bool {
- match ty {
- Type::Ext(name) => name != "Span",
- Type::Box(ty) => is_printable(ty),
- Type::Tuple(ty) => ty.iter().any(is_printable),
- Type::Token(_) | Type::Group(_) => false,
- Type::Syn(name) => name != "Reserved",
- Type::Std(_) | Type::Punctuated(_) | Type::Option(_) | Type::Vec(_) => true,
- }
-}
-
-fn format_field(val: &TokenStream, ty: &Type) -> Option<TokenStream> {
- if !is_printable(ty) {
- return None;
- }
- let format = match ty {
- Type::Option(ty) => {
- let inner = quote!(_val);
- let format = format_field(&inner, ty).map(|format| {
- quote! {
- formatter.write_str("(")?;
- Debug::fmt(#format, formatter)?;
- formatter.write_str(")")?;
- }
- });
- let ty = rust_type(ty);
- quote!({
- #[derive(RefCast)]
- #[repr(transparent)]
- struct Print(Option<#ty>);
- impl Debug for Print {
- fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
- match &self.0 {
- Some(#inner) => {
- formatter.write_str("Some")?;
- #format
- Ok(())
- }
- None => formatter.write_str("None"),
- }
- }
- }
- Print::ref_cast(#val)
- })
- }
- Type::Tuple(ty) => {
- let printable: Vec<TokenStream> = ty
- .iter()
- .enumerate()
- .filter_map(|(i, ty)| {
- let index = Index::from(i);
- let val = quote!(&#val.#index);
- format_field(&val, ty)
- })
- .collect();
- if printable.len() == 1 {
- printable.into_iter().next().unwrap()
- } else {
- quote! {
- &(#(#printable),*)
- }
- }
- }
- _ => quote! { Lite(#val) },
- };
- Some(format)
-}
-
-fn syntax_tree_enum<'a>(outer: &str, inner: &str, fields: &'a [Type]) -> Option<&'a str> {
- if fields.len() != 1 {
- return None;
- }
- const WHITELIST: &[&str] = &["PathArguments", "Visibility"];
- match &fields[0] {
- Type::Syn(ty) if WHITELIST.contains(&outer) || outer.to_owned() + inner == *ty => Some(ty),
- _ => None,
- }
-}
-
-fn lookup<'a>(defs: &'a Definitions, name: &str) -> &'a Node {
- for node in &defs.types {
- if node.ident == name {
- return node;
- }
- }
- panic!("not found: {}", name)
-}
-
-fn expand_impl_body(defs: &Definitions, node: &Node, name: &str) -> TokenStream {
- let ident = Ident::new(&node.ident, Span::call_site());
-
- match &node.data {
- Data::Enum(variants) => {
- let arms = variants.iter().map(|(v, fields)| {
- let variant = Ident::new(v, Span::call_site());
- if fields.is_empty() {
- quote! {
- syn::#ident::#variant => formatter.write_str(#v),
- }
- } else if let Some(inner) = syntax_tree_enum(name, v, fields) {
- let path = format!("{}::{}", name, v);
- let format = expand_impl_body(defs, lookup(defs, inner), &path);
- quote! {
- syn::#ident::#variant(_val) => {
- #format
- }
- }
- } else if fields.len() == 1 {
- let ty = &fields[0];
- let val = quote!(_val);
- let format = format_field(&val, ty).map(|format| {
- quote! {
- formatter.write_str("(")?;
- Debug::fmt(#format, formatter)?;
- formatter.write_str(")")?;
- }
- });
- quote! {
- syn::#ident::#variant(_val) => {
- formatter.write_str(#v)?;
- #format
- Ok(())
- }
- }
- } else {
- let pats = (0..fields.len())
- .map(|i| Ident::new(&format!("_v{}", i), Span::call_site()));
- let fields = fields.iter().enumerate().filter_map(|(i, ty)| {
- let index = Ident::new(&format!("_v{}", i), Span::call_site());
- let val = quote!(#index);
- let format = format_field(&val, ty)?;
- Some(quote! {
- formatter.field(#format);
- })
- });
- quote! {
- syn::#ident::#variant(#(#pats),*) => {
- let mut formatter = formatter.debug_tuple(#v);
- #(#fields)*
- formatter.finish()
- }
- }
- }
- });
- let nonexhaustive = if node.exhaustive {
- None
- } else {
- Some(quote!(_ => unreachable!()))
- };
- quote! {
- match _val {
- #(#arms)*
- #nonexhaustive
- }
- }
- }
- Data::Struct(fields) => {
- let fields = fields.iter().filter_map(|(f, ty)| {
- let ident = Ident::new(f, Span::call_site());
- if let Type::Option(ty) = ty {
- let inner = quote!(_val);
- let format = format_field(&inner, ty).map(|format| {
- quote! {
- let #inner = &self.0;
- formatter.write_str("(")?;
- Debug::fmt(#format, formatter)?;
- formatter.write_str(")")?;
- }
- });
- let ty = rust_type(ty);
- Some(quote! {
- if let Some(val) = &_val.#ident {
- #[derive(RefCast)]
- #[repr(transparent)]
- struct Print(#ty);
- impl Debug for Print {
- fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
- formatter.write_str("Some")?;
- #format
- Ok(())
- }
- }
- formatter.field(#f, Print::ref_cast(val));
- }
- })
- } else {
- let val = quote!(&_val.#ident);
- let format = format_field(&val, ty)?;
- let mut call = quote! {
- formatter.field(#f, #format);
- };
- if let Type::Vec(_) | Type::Punctuated(_) = ty {
- call = quote! {
- if !_val.#ident.is_empty() {
- #call
- }
- };
- }
- Some(call)
- }
- });
- quote! {
- let mut formatter = formatter.debug_struct(#name);
- #(#fields)*
- formatter.finish()
- }
- }
- Data::Private => {
- if node.ident == "LitInt" || node.ident == "LitFloat" {
- quote! {
- write!(formatter, "{}", _val)
- }
- } else {
- quote! {
- write!(formatter, "{:?}", _val.value())
- }
- }
- }
- }
-}
-
-fn expand_impl(defs: &Definitions, node: &Node) -> TokenStream {
- if node.ident == "Reserved" {
- return TokenStream::new();
- }
-
- let ident = Ident::new(&node.ident, Span::call_site());
- let body = expand_impl_body(defs, node, &node.ident);
-
- quote! {
- impl Debug for Lite<syn::#ident> {
- fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
- let _val = &self.value;
- #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 super::{Lite, RefCast};
- use std::fmt::{self, Debug};
-
- #impls
- },
- )?;
-
- Ok(())
-}
diff --git a/syn/codegen/src/file.rs b/syn/codegen/src/file.rs
deleted file mode 100644
index 5521d75..0000000
--- a/syn/codegen/src/file.rs
+++ /dev/null
@@ -1,32 +0,0 @@
-use anyhow::Result;
-use proc_macro2::TokenStream;
-use std::fs;
-use std::io::Write;
-use std::path::Path;
-
-pub fn write<P: AsRef<Path>>(path: P, content: TokenStream) -> Result<()> {
- let mut formatted = Vec::new();
- writeln!(
- formatted,
- "// This file is @generated by syn-internal-codegen."
- )?;
- writeln!(formatted, "// It is not intended for manual editing.")?;
- writeln!(formatted)?;
-
- let mut config = rustfmt::Config::default();
- config.set().emit_mode(rustfmt::EmitMode::Stdout);
- config.set().verbose(rustfmt::Verbosity::Quiet);
- config.set().format_macro_matchers(true);
- config.set().normalize_doc_attributes(true);
-
- let mut session = rustfmt::Session::new(config, Some(&mut formatted));
- session.format(rustfmt::Input::Text(content.to_string()))?;
- drop(session);
-
- if path.as_ref().is_file() && fs::read(&path)? == formatted {
- return Ok(());
- }
-
- fs::write(path, formatted)?;
- Ok(())
-}
diff --git a/syn/codegen/src/fold.rs b/syn/codegen/src/fold.rs
deleted file mode 100644
index 6914d76..0000000
--- a/syn/codegen/src/fold.rs
+++ /dev/null
@@ -1,284 +0,0 @@
-use crate::{file, full, gen};
-use anyhow::Result;
-use proc_macro2::{Ident, Span, TokenStream};
-use quote::quote;
-use syn::Index;
-use syn_codegen::{Data, Definitions, Features, Node, Type};
-
-const FOLD_SRC: &str = "../src/gen/fold.rs";
-
-fn simple_visit(item: &str, name: &TokenStream) -> TokenStream {
- let ident = gen::under_name(item);
- let method = Ident::new(&format!("fold_{}", ident), Span::call_site());
- quote! {
- f.#method(#name)
- }
-}
-
-fn visit(
- ty: &Type,
- features: &Features,
- defs: &Definitions,
- name: &TokenStream,
-) -> Option<TokenStream> {
- match ty {
- Type::Box(t) => {
- let res = visit(t, features, defs, &quote!(*#name))?;
- Some(quote! {
- Box::new(#res)
- })
- }
- Type::Vec(t) => {
- let operand = quote!(it);
- let val = visit(t, features, defs, &operand)?;
- Some(quote! {
- FoldHelper::lift(#name, |it| { #val })
- })
- }
- Type::Punctuated(p) => {
- let operand = quote!(it);
- let val = visit(&p.element, features, defs, &operand)?;
- Some(quote! {
- FoldHelper::lift(#name, |it| { #val })
- })
- }
- Type::Option(t) => {
- let it = quote!(it);
- let val = visit(t, features, defs, &it)?;
- Some(quote! {
- (#name).map(|it| { #val })
- })
- }
- Type::Tuple(t) => {
- let mut code = TokenStream::new();
- for (i, elem) in t.iter().enumerate() {
- let i = Index::from(i);
- let it = quote!((#name).#i);
- let val = visit(elem, features, defs, &it).unwrap_or(it);
- code.extend(val);
- code.extend(quote!(,));
- }
- Some(quote! {
- (#code)
- })
- }
- Type::Token(t) => {
- let repr = &defs.tokens[t];
- let is_keyword = repr.chars().next().unwrap().is_alphabetic();
- let spans = if is_keyword {
- quote!(span)
- } else {
- quote!(spans)
- };
- let ty = if repr == "await" {
- quote!(crate::token::Await)
- } else {
- syn::parse_str(&format!("Token![{}]", repr)).unwrap()
- };
- Some(quote! {
- #ty(tokens_helper(f, &#name.#spans))
- })
- }
- Type::Group(t) => {
- let ty = Ident::new(t, Span::call_site());
- Some(quote! {
- #ty(tokens_helper(f, &#name.span))
- })
- }
- Type::Syn(t) => {
- fn requires_full(features: &Features) -> bool {
- features.any.contains("full") && features.any.len() == 1
- }
- let mut res = simple_visit(t, name);
- let target = defs.types.iter().find(|ty| ty.ident == *t).unwrap();
- if requires_full(&target.features) && !requires_full(features) {
- res = quote!(full!(#res));
- }
- Some(res)
- }
- Type::Ext(t) if gen::TERMINAL_TYPES.contains(&&t[..]) => Some(simple_visit(t, name)),
- Type::Ext(_) | Type::Std(_) => None,
- }
-}
-
-fn node(traits: &mut TokenStream, impls: &mut TokenStream, s: &Node, defs: &Definitions) {
- let under_name = gen::under_name(&s.ident);
- let ty = Ident::new(&s.ident, Span::call_site());
- let fold_fn = Ident::new(&format!("fold_{}", under_name), Span::call_site());
-
- let mut fold_impl = TokenStream::new();
-
- match &s.data {
- Data::Enum(variants) => {
- let mut fold_variants = TokenStream::new();
-
- for (variant, fields) in variants {
- let variant_ident = Ident::new(variant, Span::call_site());
-
- if fields.is_empty() {
- fold_variants.extend(quote! {
- #ty::#variant_ident => {
- #ty::#variant_ident
- }
- });
- } else {
- let mut bind_fold_fields = TokenStream::new();
- let mut fold_fields = TokenStream::new();
-
- for (idx, ty) in fields.iter().enumerate() {
- let name = format!("_binding_{}", idx);
- let binding = Ident::new(&name, Span::call_site());
-
- bind_fold_fields.extend(quote! {
- #binding,
- });
-
- let owned_binding = quote!(#binding);
-
- fold_fields.extend(
- visit(ty, &s.features, defs, &owned_binding).unwrap_or(owned_binding),
- );
-
- fold_fields.extend(quote!(,));
- }
-
- fold_variants.extend(quote! {
- #ty::#variant_ident(#bind_fold_fields) => {
- #ty::#variant_ident(
- #fold_fields
- )
- }
- });
- }
- }
-
- let nonexhaustive = if s.exhaustive {
- None
- } else {
- Some(quote!(_ => unreachable!()))
- };
-
- fold_impl.extend(quote! {
- match node {
- #fold_variants
- #nonexhaustive
- }
- });
- }
- Data::Struct(fields) => {
- let mut fold_fields = TokenStream::new();
-
- for (field, ty) in fields {
- let id = Ident::new(&field, Span::call_site());
- let ref_toks = quote!(node.#id);
-
- if let Type::Syn(ty) = ty {
- if ty == "Reserved" {
- fold_fields.extend(quote! {
- #id: #ref_toks,
- });
- continue;
- }
- }
-
- let fold = visit(&ty, &s.features, defs, &ref_toks).unwrap_or(ref_toks);
-
- fold_fields.extend(quote! {
- #id: #fold,
- });
- }
-
- if !fields.is_empty() {
- fold_impl.extend(quote! {
- #ty {
- #fold_fields
- }
- })
- } else {
- if ty == "Ident" {
- fold_impl.extend(quote! {
- let mut node = node;
- let span = f.fold_span(node.span());
- node.set_span(span);
- });
- }
- fold_impl.extend(quote! {
- node
- });
- }
- }
- Data::Private => {
- if ty == "Ident" {
- fold_impl.extend(quote! {
- let mut node = node;
- let span = f.fold_span(node.span());
- node.set_span(span);
- });
- }
- fold_impl.extend(quote! {
- node
- });
- }
- }
-
- let fold_span_only =
- s.data == Data::Private && !gen::TERMINAL_TYPES.contains(&s.ident.as_str());
- if fold_span_only {
- fold_impl = quote! {
- let span = f.fold_span(node.span());
- let mut node = node;
- node.set_span(span);
- node
- };
- }
-
- traits.extend(quote! {
- fn #fold_fn(&mut self, i: #ty) -> #ty {
- #fold_fn(self, i)
- }
- });
-
- impls.extend(quote! {
- pub fn #fold_fn<F>(f: &mut F, node: #ty) -> #ty
- where
- F: Fold + ?Sized,
- {
- #fold_impl
- }
- });
-}
-
-pub fn generate(defs: &Definitions) -> Result<()> {
- let (traits, impls) = gen::traverse(defs, node);
- let full_macro = full::get_macro();
- file::write(
- FOLD_SRC,
- quote! {
- // Unreachable code is generated sometimes without the full feature.
- #![allow(unreachable_code, unused_variables)]
-
- use crate::*;
- #[cfg(any(feature = "full", feature = "derive"))]
- use crate::token::{Brace, Bracket, Paren, Group};
- use proc_macro2::Span;
- #[cfg(any(feature = "full", feature = "derive"))]
- use crate::gen::helper::fold::*;
-
- #full_macro
-
- /// Syntax tree traversal to transform the nodes of an owned syntax tree.
- ///
- /// See the [module documentation] for details.
- ///
- /// [module documentation]: self
- ///
- /// *This trait is available if Syn is built with the `"fold"` feature.*
- pub trait Fold {
- #traits
- }
-
- #impls
- },
- )?;
- Ok(())
-}
diff --git a/syn/codegen/src/full.rs b/syn/codegen/src/full.rs
deleted file mode 100644
index a410031..0000000
--- a/syn/codegen/src/full.rs
+++ /dev/null
@@ -1,20 +0,0 @@
-use proc_macro2::TokenStream;
-use quote::quote;
-
-pub fn get_macro() -> TokenStream {
- quote! {
- #[cfg(feature = "full")]
- macro_rules! full {
- ($e:expr) => {
- $e
- };
- }
-
- #[cfg(all(feature = "derive", not(feature = "full")))]
- macro_rules! full {
- ($e:expr) => {
- unreachable!()
- };
- }
- }
-}
diff --git a/syn/codegen/src/gen.rs b/syn/codegen/src/gen.rs
deleted file mode 100644
index ef43182..0000000
--- a/syn/codegen/src/gen.rs
+++ /dev/null
@@ -1,45 +0,0 @@
-use inflections::Inflect;
-use proc_macro2::{Ident, Span, TokenStream};
-use quote::quote;
-use syn_codegen::{Data, Definitions, Features, Node};
-
-pub const TERMINAL_TYPES: &[&str] = &["Span", "Ident"];
-
-pub fn under_name(name: &str) -> Ident {
- Ident::new(&name.to_snake_case(), Span::call_site())
-}
-
-pub fn traverse(
- defs: &Definitions,
- node: fn(&mut TokenStream, &mut TokenStream, &Node, &Definitions),
-) -> (TokenStream, TokenStream) {
- let mut types = defs.types.clone();
- for terminal in TERMINAL_TYPES {
- types.push(Node {
- ident: terminal.to_string(),
- features: Features::default(),
- data: Data::Private,
- exhaustive: true,
- });
- }
- types.sort_by(|a, b| a.ident.cmp(&b.ident));
-
- let mut traits = TokenStream::new();
- let mut impls = TokenStream::new();
- for s in types {
- if s.ident == "Reserved" {
- continue;
- }
- let features = &s.features.any;
- let features = match features.len() {
- 0 => quote!(),
- 1 => quote!(#[cfg(feature = #(#features)*)]),
- _ => quote!(#[cfg(any(#(feature = #features),*))]),
- };
- traits.extend(features.clone());
- impls.extend(features);
- node(&mut traits, &mut impls, &s, defs);
- }
-
- (traits, impls)
-}
diff --git a/syn/codegen/src/json.rs b/syn/codegen/src/json.rs
deleted file mode 100644
index 53904bf..0000000
--- a/syn/codegen/src/json.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-use anyhow::Result;
-use std::fs;
-use std::path::Path;
-use syn_codegen::Definitions;
-
-pub fn generate(defs: &Definitions) -> Result<()> {
- let mut j = serde_json::to_string_pretty(&defs)?;
- j.push('\n');
-
- let check: Definitions = serde_json::from_str(&j)?;
- assert_eq!(*defs, check);
-
- let codegen_root = Path::new(env!("CARGO_MANIFEST_DIR"));
- let json_path = codegen_root.join("../syn.json");
- fs::write(json_path, j)?;
-
- Ok(())
-}
diff --git a/syn/codegen/src/main.rs b/syn/codegen/src/main.rs
deleted file mode 100644
index 9b1b5dd..0000000
--- a/syn/codegen/src/main.rs
+++ /dev/null
@@ -1,36 +0,0 @@
-// This crate crawls the Syn source directory to find all structs and enums that
-// form the Syn syntax tree.
-//
-// A machine-readable representation of the syntax tree is saved to syn.json in
-// the repo root for other code generation tools to consume. The syn-codegen
-// crate (https://docs.rs/syn-codegen/) provides the data structures for parsing
-// and making use of syn.json from Rust code.
-//
-// Finally this crate generates the Visit, VisitMut, and Fold traits in Syn
-// programmatically from the syntax tree description.
-
-#![recursion_limit = "128"]
-#![allow(clippy::needless_pass_by_value)]
-
-mod debug;
-mod file;
-mod fold;
-mod full;
-mod gen;
-mod json;
-mod operand;
-mod parse;
-mod version;
-mod visit;
-mod visit_mut;
-
-fn main() -> anyhow::Result<()> {
- color_backtrace::install();
- let defs = parse::parse()?;
- json::generate(&defs)?;
- fold::generate(&defs)?;
- visit::generate(&defs)?;
- visit_mut::generate(&defs)?;
- debug::generate(&defs)?;
- Ok(())
-}
diff --git a/syn/codegen/src/operand.rs b/syn/codegen/src/operand.rs
deleted file mode 100644
index db3bd18..0000000
--- a/syn/codegen/src/operand.rs
+++ /dev/null
@@ -1,38 +0,0 @@
-use proc_macro2::TokenStream;
-use quote::quote;
-
-pub enum Operand {
- Borrowed(TokenStream),
- Owned(TokenStream),
-}
-
-pub use self::Operand::*;
-
-impl Operand {
- pub fn tokens(&self) -> &TokenStream {
- match self {
- Borrowed(n) | Owned(n) => n,
- }
- }
-
- pub fn ref_tokens(&self) -> TokenStream {
- match self {
- Borrowed(n) => n.clone(),
- Owned(n) => quote!(&#n),
- }
- }
-
- pub fn ref_mut_tokens(&self) -> TokenStream {
- match self {
- Borrowed(n) => n.clone(),
- Owned(n) => quote!(&mut #n),
- }
- }
-
- pub fn owned_tokens(&self) -> TokenStream {
- match self {
- Borrowed(n) => quote!(*#n),
- Owned(n) => n.clone(),
- }
- }
-}
diff --git a/syn/codegen/src/parse.rs b/syn/codegen/src/parse.rs
deleted file mode 100644
index cdd6085..0000000
--- a/syn/codegen/src/parse.rs
+++ /dev/null
@@ -1,657 +0,0 @@
-use crate::version;
-use anyhow::{bail, Result};
-use indexmap::IndexMap;
-use quote::quote;
-use syn::parse::Parser;
-use syn::{parse_quote, Data, DataStruct, DeriveInput, Ident, Item};
-use syn_codegen as types;
-use thiserror::Error;
-
-use std::collections::BTreeMap;
-use std::fs;
-use std::path::{Path, PathBuf};
-
-const SYN_CRATE_ROOT: &str = "../src/lib.rs";
-const TOKEN_SRC: &str = "../src/token.rs";
-const IGNORED_MODS: &[&str] = &["fold", "visit", "visit_mut"];
-const EXTRA_TYPES: &[&str] = &["Lifetime"];
-const NONEXHAUSTIVE: &str = "__Nonexhaustive";
-
-// NOTE: BTreeMap is used here instead of HashMap to have deterministic output.
-type ItemLookup = BTreeMap<Ident, AstItem>;
-type TokenLookup = BTreeMap<String, String>;
-
-/// Parse the contents of `src` and return a list of AST types.
-pub fn parse() -> Result<types::Definitions> {
- let mut item_lookup = BTreeMap::new();
- load_file(SYN_CRATE_ROOT, &[], &mut item_lookup)?;
-
- let token_lookup = load_token_file(TOKEN_SRC)?;
-
- let version = version::get()?;
-
- let types = item_lookup
- .values()
- .map(|item| introspect_item(item, &item_lookup, &token_lookup))
- .collect();
-
- let tokens = token_lookup
- .into_iter()
- .map(|(name, ty)| (ty, name))
- .collect();
-
- Ok(types::Definitions {
- version,
- types,
- tokens,
- })
-}
-
-/// Data extracted from syn source
-#[derive(Clone)]
-pub struct AstItem {
- ast: DeriveInput,
- features: Vec<syn::Attribute>,
-}
-
-fn introspect_item(item: &AstItem, items: &ItemLookup, tokens: &TokenLookup) -> types::Node {
- let features = introspect_features(&item.features);
-
- match &item.ast.data {
- Data::Enum(ref data) => types::Node {
- ident: item.ast.ident.to_string(),
- features,
- data: types::Data::Enum(introspect_enum(data, items, tokens)),
- exhaustive: data.variants.iter().all(|v| v.ident != NONEXHAUSTIVE),
- },
- Data::Struct(ref data) => types::Node {
- ident: item.ast.ident.to_string(),
- features,
- data: {
- if data.fields.iter().all(|f| is_pub(&f.vis)) {
- types::Data::Struct(introspect_struct(data, items, tokens))
- } else {
- types::Data::Private
- }
- },
- exhaustive: true,
- },
- Data::Union(..) => panic!("Union not supported"),
- }
-}
-
-fn introspect_enum(
- item: &syn::DataEnum,
- items: &ItemLookup,
- tokens: &TokenLookup,
-) -> types::Variants {
- item.variants
- .iter()
- .filter_map(|variant| {
- if variant.ident == NONEXHAUSTIVE {
- return None;
- }
- let fields = match &variant.fields {
- syn::Fields::Unnamed(fields) => fields
- .unnamed
- .iter()
- .map(|field| introspect_type(&field.ty, items, tokens))
- .collect(),
- syn::Fields::Unit => vec![],
- _ => panic!("Enum representation not supported"),
- };
- Some((variant.ident.to_string(), fields))
- })
- .collect()
-}
-
-fn introspect_struct(
- item: &syn::DataStruct,
- items: &ItemLookup,
- tokens: &TokenLookup,
-) -> types::Fields {
- match &item.fields {
- syn::Fields::Named(fields) => fields
- .named
- .iter()
- .map(|field| {
- (
- field.ident.as_ref().unwrap().to_string(),
- introspect_type(&field.ty, items, tokens),
- )
- })
- .collect(),
- syn::Fields::Unit => IndexMap::new(),
- _ => panic!("Struct representation not supported"),
- }
-}
-
-fn introspect_type(item: &syn::Type, items: &ItemLookup, tokens: &TokenLookup) -> types::Type {
- match item {
- syn::Type::Path(syn::TypePath {
- qself: None,
- ref path,
- }) => {
- let last = path.segments.last().unwrap();
- let string = last.ident.to_string();
-
- match string.as_str() {
- "Option" => {
- let nested = introspect_type(first_arg(&last.arguments), items, tokens);
- types::Type::Option(Box::new(nested))
- }
- "Punctuated" => {
- let nested = introspect_type(first_arg(&last.arguments), items, tokens);
- let punct = match introspect_type(last_arg(&last.arguments), items, tokens) {
- types::Type::Token(s) => s,
- _ => panic!(),
- };
-
- types::Type::Punctuated(types::Punctuated {
- element: Box::new(nested),
- punct,
- })
- }
- "Vec" => {
- let nested = introspect_type(first_arg(&last.arguments), items, tokens);
- types::Type::Vec(Box::new(nested))
- }
- "Box" => {
- let nested = introspect_type(first_arg(&last.arguments), items, tokens);
- types::Type::Box(Box::new(nested))
- }
- "Brace" | "Bracket" | "Paren" | "Group" => types::Type::Group(string),
- "TokenStream" | "Literal" | "Ident" | "Span" => types::Type::Ext(string),
- "String" | "u32" | "usize" | "bool" => types::Type::Std(string),
- "Await" => types::Type::Token("Await".to_string()),
- _ => {
- if items.get(&last.ident).is_some() || last.ident == "Reserved" {
- types::Type::Syn(string)
- } else {
- unimplemented!("{}", string);
- }
- }
- }
- }
- syn::Type::Tuple(syn::TypeTuple { ref elems, .. }) => {
- let tys = elems
- .iter()
- .map(|ty| introspect_type(&ty, items, tokens))
- .collect();
- types::Type::Tuple(tys)
- }
- syn::Type::Macro(syn::TypeMacro { ref mac })
- if mac.path.segments.last().unwrap().ident == "Token" =>
- {
- let content = mac.tokens.to_string();
- let ty = tokens.get(&content).unwrap().to_string();
-
- types::Type::Token(ty)
- }
- _ => panic!("{}", quote!(#item).to_string()),
- }
-}
-
-fn introspect_features(attrs: &[syn::Attribute]) -> types::Features {
- let mut ret = types::Features::default();
-
- for attr in attrs {
- if !attr.path.is_ident("cfg") {
- continue;
- }
-
- let features = parsing::parse_features.parse2(attr.tokens.clone()).unwrap();
-
- if ret.any.is_empty() {
- ret = features;
- } else if ret.any.len() < features.any.len() {
- assert!(ret.any.iter().all(|f| features.any.contains(f)));
- } else {
- assert!(features.any.iter().all(|f| ret.any.contains(f)));
- ret = features;
- }
- }
-
- ret
-}
-
-fn is_pub(vis: &syn::Visibility) -> bool {
- match vis {
- syn::Visibility::Public(_) => true,
- _ => false,
- }
-}
-
-fn first_arg(params: &syn::PathArguments) -> &syn::Type {
- let data = match *params {
- syn::PathArguments::AngleBracketed(ref data) => data,
- _ => panic!("Expected at least 1 type argument here"),
- };
-
- match *data
- .args
- .first()
- .expect("Expected at least 1 type argument here")
- {
- syn::GenericArgument::Type(ref ty) => ty,
- _ => panic!("Expected at least 1 type argument here"),
- }
-}
-
-fn last_arg(params: &syn::PathArguments) -> &syn::Type {
- let data = match *params {
- syn::PathArguments::AngleBracketed(ref data) => data,
- _ => panic!("Expected at least 1 type argument here"),
- };
-
- match *data
- .args
- .last()
- .expect("Expected at least 1 type argument here")
- {
- syn::GenericArgument::Type(ref ty) => ty,
- _ => panic!("Expected at least 1 type argument here"),
- }
-}
-
-mod parsing {
- use super::{AstItem, TokenLookup};
-
- use proc_macro2::{TokenStream, TokenTree};
- use quote::quote;
- use syn;
- use syn::parse::{ParseStream, Result};
- use syn::*;
- use syn_codegen as types;
-
- use std::collections::{BTreeMap, BTreeSet};
-
- fn peek_tag(input: ParseStream, tag: &str) -> bool {
- let ahead = input.fork();
- ahead.parse::<Token![#]>().is_ok()
- && ahead
- .parse::<Ident>()
- .map(|ident| ident == tag)
- .unwrap_or(false)
- }
-
- // Parses #full - returns #[cfg(feature = "full")] if it is present, and
- // nothing otherwise.
- fn full(input: ParseStream) -> Vec<syn::Attribute> {
- if peek_tag(input, "full") {
- input.parse::<Token![#]>().unwrap();
- input.parse::<Ident>().unwrap();
- vec![parse_quote!(#[cfg(feature = "full")])]
- } else {
- vec![]
- }
- }
-
- fn skip_manual_extra_traits(input: ParseStream) {
- if peek_tag(input, "manual_extra_traits") || peek_tag(input, "manual_extra_traits_debug") {
- input.parse::<Token![#]>().unwrap();
- input.parse::<Ident>().unwrap();
- }
- }
-
- // Parses a simple AstStruct without the `pub struct` prefix.
- fn ast_struct_inner(input: ParseStream) -> Result<AstItem> {
- let ident: Ident = input.parse()?;
- let features = full(input);
- skip_manual_extra_traits(input);
- let rest: TokenStream = input.parse()?;
- Ok(AstItem {
- ast: syn::parse2(quote! {
- pub struct #ident #rest
- })?,
- features,
- })
- }
-
- pub fn ast_struct(input: ParseStream) -> Result<AstItem> {
- input.call(Attribute::parse_outer)?;
- input.parse::<Token![pub]>()?;
- input.parse::<Token![struct]>()?;
- let res = input.call(ast_struct_inner)?;
- Ok(res)
- }
-
- fn no_visit(input: ParseStream) -> bool {
- if peek_tag(input, "no_visit") {
- input.parse::<Token![#]>().unwrap();
- input.parse::<Ident>().unwrap();
- true
- } else {
- false
- }
- }
-
- pub fn ast_enum(input: ParseStream) -> Result<Option<AstItem>> {
- input.call(Attribute::parse_outer)?;
- input.parse::<Token![pub]>()?;
- input.parse::<Token![enum]>()?;
- let ident: Ident = input.parse()?;
- skip_manual_extra_traits(input);
- let no_visit = no_visit(input);
- let rest: TokenStream = input.parse()?;
- Ok(if no_visit {
- None
- } else {
- Some(AstItem {
- ast: syn::parse2(quote! {
- pub enum #ident #rest
- })?,
- features: vec![],
- })
- })
- }
-
- // A single variant of an ast_enum_of_structs!
- struct EosVariant {
- name: Ident,
- member: Option<Path>,
- }
- fn eos_variant(input: ParseStream) -> Result<EosVariant> {
- input.call(Attribute::parse_outer)?;
- let variant: Ident = input.parse()?;
- let member = if input.peek(token::Paren) {
- let content;
- parenthesized!(content in input);
- let path: Path = content.parse()?;
- Some(path)
- } else {
- None
- };
- input.parse::<Token![,]>()?;
- Ok(EosVariant {
- name: variant,
- member,
- })
- }
-
- pub fn ast_enum_of_structs(input: ParseStream) -> Result<AstItem> {
- input.call(Attribute::parse_outer)?;
- input.parse::<Token![pub]>()?;
- input.parse::<Token![enum]>()?;
- let ident: Ident = input.parse()?;
- skip_manual_extra_traits(input);
-
- let content;
- braced!(content in input);
- let mut variants = Vec::new();
- while !content.is_empty() {
- variants.push(content.call(eos_variant)?);
- }
-
- if let Some(ident) = input.parse::<Option<Ident>>()? {
- assert_eq!(ident, "do_not_generate_to_tokens");
- }
-
- let enum_item = {
- let variants = variants.iter().map(|v| {
- let name = v.name.clone();
- match v.member {
- Some(ref member) => quote!(#name(#member)),
- None => quote!(#name),
- }
- });
- parse_quote! {
- pub enum #ident {
- #(#variants),*
- }
- }
- };
- Ok(AstItem {
- ast: enum_item,
- features: vec![],
- })
- }
-
- mod kw {
- syn::custom_keyword!(macro_rules);
- syn::custom_keyword!(Token);
- }
-
- pub fn parse_token_macro(input: ParseStream) -> Result<TokenLookup> {
- input.parse::<TokenTree>()?;
- input.parse::<Token![=>]>()?;
-
- let definition;
- braced!(definition in input);
- definition.call(Attribute::parse_outer)?;
- definition.parse::<kw::macro_rules>()?;
- definition.parse::<Token![!]>()?;
- definition.parse::<kw::Token>()?;
-
- let rules;
- braced!(rules in definition);
- input.parse::<Token![;]>()?;
-
- let mut tokens = BTreeMap::new();
- while !rules.is_empty() {
- if rules.peek(Token![$]) {
- rules.parse::<Token![$]>()?;
- rules.parse::<TokenTree>()?;
- rules.parse::<Token![*]>()?;
- tokens.insert("await".to_owned(), "Await".to_owned());
- } else {
- let pattern;
- parenthesized!(pattern in rules);
- let token = pattern.parse::<TokenStream>()?.to_string();
- rules.parse::<Token![=>]>()?;
- let expansion;
- braced!(expansion in rules);
- rules.parse::<Token![;]>()?;
- expansion.parse::<Token![$]>()?;
- let path: Path = expansion.parse()?;
- let ty = path.segments.last().unwrap().ident.to_string();
- tokens.insert(token, ty.to_string());
- }
- }
- Ok(tokens)
- }
-
- fn parse_feature(input: ParseStream) -> Result<String> {
- let i: syn::Ident = input.parse()?;
- assert_eq!(i, "feature");
-
- input.parse::<Token![=]>()?;
- let s = input.parse::<syn::LitStr>()?;
-
- Ok(s.value())
- }
-
- pub fn parse_features(input: ParseStream) -> Result<types::Features> {
- let mut features = BTreeSet::new();
-
- let level_1;
- parenthesized!(level_1 in input);
-
- let i: syn::Ident = level_1.fork().parse()?;
-
- if i == "any" {
- level_1.parse::<syn::Ident>()?;
-
- let level_2;
- parenthesized!(level_2 in level_1);
-
- while !level_2.is_empty() {
- features.insert(parse_feature(&level_2)?);
-
- if !level_2.is_empty() {
- level_2.parse::<Token![,]>()?;
- }
- }
- } else if i == "feature" {
- features.insert(parse_feature(&level_1)?);
- assert!(level_1.is_empty());
- } else {
- panic!("{:?}", i);
- }
-
- assert!(input.is_empty());
-
- Ok(types::Features { any: features })
- }
-}
-
-fn get_features(attrs: &[syn::Attribute], base: &[syn::Attribute]) -> Vec<syn::Attribute> {
- let mut ret = base.to_owned();
-
- for attr in attrs {
- if attr.path.is_ident("cfg") {
- ret.push(attr.clone());
- }
- }
-
- ret
-}
-
-#[derive(Error, Debug)]
-#[error("{path}:{line}:{column}: {error}")]
-struct LoadFileError {
- path: PathBuf,
- line: usize,
- column: usize,
- error: syn::Error,
-}
-
-fn load_file<P: AsRef<Path>>(
- name: P,
- features: &[syn::Attribute],
- lookup: &mut ItemLookup,
-) -> Result<()> {
- let error = match do_load_file(&name, features, lookup).err() {
- None => return Ok(()),
- Some(error) => error,
- };
-
- let error = error.downcast::<syn::Error>()?;
- let span = error.span().start();
-
- bail!(LoadFileError {
- path: name.as_ref().to_owned(),
- line: span.line,
- column: span.column + 1,
- error,
- })
-}
-
-fn do_load_file<P: AsRef<Path>>(
- name: P,
- features: &[syn::Attribute],
- lookup: &mut ItemLookup,
-) -> Result<()> {
- let name = name.as_ref();
- let parent = name.parent().expect("no parent path");
-
- // Parse the file
- let src = fs::read_to_string(name)?;
- let file = syn::parse_file(&src)?;
-
- // Collect all of the interesting AstItems declared in this file or submodules.
- 'items: for item in file.items {
- match item {
- Item::Mod(item) => {
- // Don't inspect inline modules.
- if item.content.is_some() {
- continue;
- }
-
- // We don't want to try to load the generated rust files and
- // parse them, so we ignore them here.
- for name in IGNORED_MODS {
- if item.ident == name {
- continue 'items;
- }
- }
-
- // Lookup any #[cfg()] attributes on the module and add them to
- // the feature set.
- //
- // The derive module is weird because it is built with either
- // `full` or `derive` but exported only under `derive`.
- let features = if item.ident == "derive" {
- vec![parse_quote!(#[cfg(feature = "derive")])]
- } else {
- get_features(&item.attrs, features)
- };
-
- // Look up the submodule file, and recursively parse it.
- // Only handles same-directory .rs file submodules for now.
- let path = parent.join(&format!("{}.rs", item.ident));
- load_file(path, &features, lookup)?;
- }
- Item::Macro(item) => {
- // Lookip any #[cfg()] attributes directly on the macro
- // invocation, and add them to the feature set.
- let features = get_features(&item.attrs, features);
-
- // Try to parse the AstItem declaration out of the item.
- let tts = item.mac.tokens.clone();
- let found = if item.mac.path.is_ident("ast_struct") {
- Some(parsing::ast_struct.parse2(tts)?)
- } else if item.mac.path.is_ident("ast_enum") {
- parsing::ast_enum.parse2(tts)?
- } else if item.mac.path.is_ident("ast_enum_of_structs") {
- Some(parsing::ast_enum_of_structs.parse2(tts)?)
- } else {
- continue;
- };
-
- // Record our features on the parsed AstItems.
- for mut item in found {
- if item.ast.ident != "Reserved" {
- item.features.extend(features.clone());
- lookup.insert(item.ast.ident.clone(), item);
- }
- }
- }
- Item::Struct(item) => {
- let ident = item.ident;
- if EXTRA_TYPES.contains(&&ident.to_string()[..]) {
- lookup.insert(
- ident.clone(),
- AstItem {
- ast: DeriveInput {
- ident,
- vis: item.vis,
- attrs: item.attrs,
- generics: item.generics,
- data: Data::Struct(DataStruct {
- fields: item.fields,
- struct_token: item.struct_token,
- semi_token: item.semi_token,
- }),
- },
- features: features.to_owned(),
- },
- );
- }
- }
- _ => {}
- }
- }
- Ok(())
-}
-
-fn load_token_file<P: AsRef<Path>>(name: P) -> Result<TokenLookup> {
- let name = name.as_ref();
- let src = fs::read_to_string(name)?;
- let file = syn::parse_file(&src)?;
- for item in file.items {
- match item {
- Item::Macro(item) => {
- match item.ident {
- Some(ref i) if i == "export_token_macro" => {}
- _ => continue,
- }
- let tokens = item.mac.parse_body_with(parsing::parse_token_macro)?;
- return Ok(tokens);
- }
- _ => {}
- }
- }
-
- panic!("failed to parse Token macro")
-}
diff --git a/syn/codegen/src/version.rs b/syn/codegen/src/version.rs
deleted file mode 100644
index 9374624..0000000
--- a/syn/codegen/src/version.rs
+++ /dev/null
@@ -1,24 +0,0 @@
-use anyhow::Result;
-use semver::Version;
-use serde::Deserialize;
-
-use std::fs;
-use std::path::Path;
-
-pub fn get() -> Result<Version> {
- let codegen_root = Path::new(env!("CARGO_MANIFEST_DIR"));
- let syn_cargo_toml = codegen_root.join("../Cargo.toml");
- let manifest = fs::read_to_string(syn_cargo_toml)?;
- let parsed: Manifest = toml::from_str(&manifest)?;
- Ok(parsed.package.version)
-}
-
-#[derive(Debug, Deserialize)]
-struct Manifest {
- package: Package,
-}
-
-#[derive(Debug, Deserialize)]
-struct Package {
- version: Version,
-}
diff --git a/syn/codegen/src/visit.rs b/syn/codegen/src/visit.rs
deleted file mode 100644
index 41bc9e9..0000000
--- a/syn/codegen/src/visit.rs
+++ /dev/null
@@ -1,265 +0,0 @@
-use crate::operand::{Borrowed, Operand, Owned};
-use crate::{file, full, gen};
-use anyhow::Result;
-use proc_macro2::{Ident, Span, TokenStream};
-use quote::quote;
-use syn::Index;
-use syn_codegen::{Data, Definitions, Features, Node, Type};
-
-const VISIT_SRC: &str = "../src/gen/visit.rs";
-
-fn simple_visit(item: &str, name: &Operand) -> TokenStream {
- let ident = gen::under_name(item);
- let method = Ident::new(&format!("visit_{}", ident), Span::call_site());
- let name = name.ref_tokens();
- quote! {
- v.#method(#name)
- }
-}
-
-fn noop_visit(name: &Operand) -> TokenStream {
- let name = name.tokens();
- quote! {
- skip!(#name)
- }
-}
-
-fn visit(
- ty: &Type,
- features: &Features,
- defs: &Definitions,
- name: &Operand,
-) -> Option<TokenStream> {
- match ty {
- Type::Box(t) => {
- let name = name.owned_tokens();
- visit(t, features, defs, &Owned(quote!(*#name)))
- }
- Type::Vec(t) => {
- let operand = Borrowed(quote!(it));
- let val = visit(t, features, defs, &operand)?;
- let name = name.ref_tokens();
- Some(quote! {
- for it in #name {
- #val
- }
- })
- }
- Type::Punctuated(p) => {
- let operand = Borrowed(quote!(it));
- let val = visit(&p.element, features, defs, &operand)?;
- let name = name.ref_tokens();
- Some(quote! {
- for el in Punctuated::pairs(#name) {
- let (it, p) = el.into_tuple();
- #val;
- if let Some(p) = p {
- tokens_helper(v, &p.spans);
- }
- }
- })
- }
- Type::Option(t) => {
- let it = Borrowed(quote!(it));
- let val = visit(t, features, defs, &it)?;
- let name = name.owned_tokens();
- Some(quote! {
- if let Some(it) = &#name {
- #val
- }
- })
- }
- Type::Tuple(t) => {
- let mut code = TokenStream::new();
- for (i, elem) in t.iter().enumerate() {
- let name = name.tokens();
- let i = Index::from(i);
- let it = Owned(quote!((#name).#i));
- let val = visit(elem, features, defs, &it).unwrap_or_else(|| noop_visit(&it));
- code.extend(val);
- code.extend(quote!(;));
- }
- Some(code)
- }
- Type::Token(t) => {
- let name = name.tokens();
- let repr = &defs.tokens[t];
- let is_keyword = repr.chars().next().unwrap().is_alphabetic();
- let spans = if is_keyword {
- quote!(span)
- } else {
- quote!(spans)
- };
- Some(quote! {
- tokens_helper(v, &#name.#spans)
- })
- }
- Type::Group(_) => {
- let name = name.tokens();
- Some(quote! {
- tokens_helper(v, &#name.span)
- })
- }
- Type::Syn(t) => {
- fn requires_full(features: &Features) -> bool {
- features.any.contains("full") && features.any.len() == 1
- }
- let mut res = simple_visit(t, name);
- let target = defs.types.iter().find(|ty| ty.ident == *t).unwrap();
- if requires_full(&target.features) && !requires_full(features) {
- res = quote!(full!(#res));
- }
- Some(res)
- }
- Type::Ext(t) if gen::TERMINAL_TYPES.contains(&&t[..]) => Some(simple_visit(t, name)),
- Type::Ext(_) | Type::Std(_) => None,
- }
-}
-
-fn node(traits: &mut TokenStream, impls: &mut TokenStream, s: &Node, defs: &Definitions) {
- let under_name = gen::under_name(&s.ident);
- let ty = Ident::new(&s.ident, Span::call_site());
- let visit_fn = Ident::new(&format!("visit_{}", under_name), Span::call_site());
-
- let mut visit_impl = TokenStream::new();
-
- match &s.data {
- Data::Enum(variants) => {
- let mut visit_variants = TokenStream::new();
-
- for (variant, fields) in variants {
- let variant_ident = Ident::new(variant, Span::call_site());
-
- if fields.is_empty() {
- visit_variants.extend(quote! {
- #ty::#variant_ident => {}
- });
- } else {
- let mut bind_visit_fields = TokenStream::new();
- let mut visit_fields = TokenStream::new();
-
- for (idx, ty) in fields.iter().enumerate() {
- let name = format!("_binding_{}", idx);
- let binding = Ident::new(&name, Span::call_site());
-
- bind_visit_fields.extend(quote! {
- #binding,
- });
-
- let borrowed_binding = Borrowed(quote!(#binding));
-
- visit_fields.extend(
- visit(ty, &s.features, defs, &borrowed_binding)
- .unwrap_or_else(|| noop_visit(&borrowed_binding)),
- );
-
- visit_fields.extend(quote!(;));
- }
-
- visit_variants.extend(quote! {
- #ty::#variant_ident(#bind_visit_fields) => {
- #visit_fields
- }
- });
- }
- }
-
- let nonexhaustive = if s.exhaustive {
- None
- } else {
- Some(quote!(_ => unreachable!()))
- };
-
- visit_impl.extend(quote! {
- match node {
- #visit_variants
- #nonexhaustive
- }
- });
- }
- Data::Struct(fields) => {
- for (field, ty) in fields {
- if let Type::Syn(ty) = ty {
- if ty == "Reserved" {
- continue;
- }
- }
-
- let id = Ident::new(&field, Span::call_site());
- let ref_toks = Owned(quote!(node.#id));
- let visit_field = visit(&ty, &s.features, defs, &ref_toks)
- .unwrap_or_else(|| noop_visit(&ref_toks));
- visit_impl.extend(quote! {
- #visit_field;
- });
- }
- }
- Data::Private => {
- if ty == "Ident" {
- visit_impl.extend(quote! {
- v.visit_span(&node.span());
- });
- }
- }
- }
-
- let ast_lifetime = if s.ident == "Span" {
- None
- } else {
- Some(quote!('ast))
- };
-
- traits.extend(quote! {
- fn #visit_fn(&mut self, i: &#ast_lifetime #ty) {
- #visit_fn(self, i)
- }
- });
-
- impls.extend(quote! {
- pub fn #visit_fn<'ast, V>(v: &mut V, node: &#ast_lifetime #ty)
- where
- V: Visit<'ast> + ?Sized,
- {
- #visit_impl
- }
- });
-}
-
-pub fn generate(defs: &Definitions) -> Result<()> {
- let (traits, impls) = gen::traverse(defs, node);
- let full_macro = full::get_macro();
- file::write(
- VISIT_SRC,
- quote! {
- #![allow(unused_variables)]
-
- use crate::*;
- #[cfg(any(feature = "full", feature = "derive"))]
- use crate::punctuated::Punctuated;
- use proc_macro2::Span;
- #[cfg(any(feature = "full", feature = "derive"))]
- use crate::gen::helper::visit::*;
-
- #full_macro
-
- #[cfg(any(feature = "full", feature = "derive"))]
- macro_rules! skip {
- ($($tt:tt)*) => {};
- }
-
- /// Syntax tree traversal to walk a shared borrow of a syntax tree.
- ///
- /// See the [module documentation] for details.
- ///
- /// [module documentation]: self
- ///
- /// *This trait is available if Syn is built with the `"visit"` feature.*
- pub trait Visit<'ast> {
- #traits
- }
-
- #impls
- },
- )?;
- Ok(())
-}
diff --git a/syn/codegen/src/visit_mut.rs b/syn/codegen/src/visit_mut.rs
deleted file mode 100644
index 71e56b3..0000000
--- a/syn/codegen/src/visit_mut.rs
+++ /dev/null
@@ -1,262 +0,0 @@
-use crate::operand::{Borrowed, Operand, Owned};
-use crate::{file, full, gen};
-use anyhow::Result;
-use proc_macro2::{Ident, Span, TokenStream};
-use quote::quote;
-use syn::Index;
-use syn_codegen::{Data, Definitions, Features, Node, Type};
-
-const VISIT_MUT_SRC: &str = "../src/gen/visit_mut.rs";
-
-fn simple_visit(item: &str, name: &Operand) -> TokenStream {
- let ident = gen::under_name(item);
- let method = Ident::new(&format!("visit_{}_mut", ident), Span::call_site());
- let name = name.ref_mut_tokens();
- quote! {
- v.#method(#name)
- }
-}
-
-fn noop_visit(name: &Operand) -> TokenStream {
- let name = name.tokens();
- quote! {
- skip!(#name)
- }
-}
-
-fn visit(
- ty: &Type,
- features: &Features,
- defs: &Definitions,
- name: &Operand,
-) -> Option<TokenStream> {
- match ty {
- Type::Box(t) => {
- let name = name.owned_tokens();
- visit(t, features, defs, &Owned(quote!(*#name)))
- }
- Type::Vec(t) => {
- let operand = Borrowed(quote!(it));
- let val = visit(t, features, defs, &operand)?;
- let name = name.ref_mut_tokens();
- Some(quote! {
- for it in #name {
- #val
- }
- })
- }
- Type::Punctuated(p) => {
- let operand = Borrowed(quote!(it));
- let val = visit(&p.element, features, defs, &operand)?;
- let name = name.ref_mut_tokens();
- Some(quote! {
- for el in Punctuated::pairs_mut(#name) {
- let (it, p) = el.into_tuple();
- #val;
- if let Some(p) = p {
- tokens_helper(v, &mut p.spans);
- }
- }
- })
- }
- Type::Option(t) => {
- let it = Borrowed(quote!(it));
- let val = visit(t, features, defs, &it)?;
- let name = name.owned_tokens();
- Some(quote! {
- if let Some(it) = &mut #name {
- #val
- }
- })
- }
- Type::Tuple(t) => {
- let mut code = TokenStream::new();
- for (i, elem) in t.iter().enumerate() {
- let name = name.tokens();
- let i = Index::from(i);
- let it = Owned(quote!((#name).#i));
- let val = visit(elem, features, defs, &it).unwrap_or_else(|| noop_visit(&it));
- code.extend(val);
- code.extend(quote!(;));
- }
- Some(code)
- }
- Type::Token(t) => {
- let name = name.tokens();
- let repr = &defs.tokens[t];
- let is_keyword = repr.chars().next().unwrap().is_alphabetic();
- let spans = if is_keyword {
- quote!(span)
- } else {
- quote!(spans)
- };
- Some(quote! {
- tokens_helper(v, &mut #name.#spans)
- })
- }
- Type::Group(_) => {
- let name = name.tokens();
- Some(quote! {
- tokens_helper(v, &mut #name.span)
- })
- }
- Type::Syn(t) => {
- fn requires_full(features: &Features) -> bool {
- features.any.contains("full") && features.any.len() == 1
- }
- let mut res = simple_visit(t, name);
- let target = defs.types.iter().find(|ty| ty.ident == *t).unwrap();
- if requires_full(&target.features) && !requires_full(features) {
- res = quote!(full!(#res));
- }
- Some(res)
- }
- Type::Ext(t) if gen::TERMINAL_TYPES.contains(&&t[..]) => Some(simple_visit(t, name)),
- Type::Ext(_) | Type::Std(_) => None,
- }
-}
-
-fn node(traits: &mut TokenStream, impls: &mut TokenStream, s: &Node, defs: &Definitions) {
- let under_name = gen::under_name(&s.ident);
- let ty = Ident::new(&s.ident, Span::call_site());
- let visit_mut_fn = Ident::new(&format!("visit_{}_mut", under_name), Span::call_site());
-
- let mut visit_mut_impl = TokenStream::new();
-
- match &s.data {
- Data::Enum(variants) => {
- let mut visit_mut_variants = TokenStream::new();
-
- for (variant, fields) in variants {
- let variant_ident = Ident::new(variant, Span::call_site());
-
- if fields.is_empty() {
- visit_mut_variants.extend(quote! {
- #ty::#variant_ident => {}
- });
- } else {
- let mut bind_visit_mut_fields = TokenStream::new();
- let mut visit_mut_fields = TokenStream::new();
-
- for (idx, ty) in fields.iter().enumerate() {
- let name = format!("_binding_{}", idx);
- let binding = Ident::new(&name, Span::call_site());
-
- bind_visit_mut_fields.extend(quote! {
- #binding,
- });
-
- let borrowed_binding = Borrowed(quote!(#binding));
-
- visit_mut_fields.extend(
- visit(ty, &s.features, defs, &borrowed_binding)
- .unwrap_or_else(|| noop_visit(&borrowed_binding)),
- );
-
- visit_mut_fields.extend(quote!(;));
- }
-
- visit_mut_variants.extend(quote! {
- #ty::#variant_ident(#bind_visit_mut_fields) => {
- #visit_mut_fields
- }
- });
- }
- }
-
- let nonexhaustive = if s.exhaustive {
- None
- } else {
- Some(quote!(_ => unreachable!()))
- };
-
- visit_mut_impl.extend(quote! {
- match node {
- #visit_mut_variants
- #nonexhaustive
- }
- });
- }
- Data::Struct(fields) => {
- for (field, ty) in fields {
- if let Type::Syn(ty) = ty {
- if ty == "Reserved" {
- continue;
- }
- }
-
- let id = Ident::new(&field, Span::call_site());
- let ref_toks = Owned(quote!(node.#id));
- let visit_mut_field = visit(&ty, &s.features, defs, &ref_toks)
- .unwrap_or_else(|| noop_visit(&ref_toks));
- visit_mut_impl.extend(quote! {
- #visit_mut_field;
- });
- }
- }
- Data::Private => {
- if ty == "Ident" {
- visit_mut_impl.extend(quote! {
- let mut span = node.span();
- v.visit_span_mut(&mut span);
- node.set_span(span);
- });
- }
- }
- }
-
- traits.extend(quote! {
- fn #visit_mut_fn(&mut self, i: &mut #ty) {
- #visit_mut_fn(self, i)
- }
- });
-
- impls.extend(quote! {
- pub fn #visit_mut_fn<V>(v: &mut V, node: &mut #ty)
- where
- V: VisitMut + ?Sized,
- {
- #visit_mut_impl
- }
- });
-}
-
-pub fn generate(defs: &Definitions) -> Result<()> {
- let (traits, impls) = gen::traverse(defs, node);
- let full_macro = full::get_macro();
- file::write(
- VISIT_MUT_SRC,
- quote! {
- #![allow(unused_variables)]
-
- use crate::*;
- #[cfg(any(feature = "full", feature = "derive"))]
- use crate::punctuated::Punctuated;
- use proc_macro2::Span;
- #[cfg(any(feature = "full", feature = "derive"))]
- use crate::gen::helper::visit_mut::*;
-
- #full_macro
-
- #[cfg(any(feature = "full", feature = "derive"))]
- macro_rules! skip {
- ($($tt:tt)*) => {};
- }
-
- /// Syntax tree traversal to mutate an exclusive borrow of a syntax tree in
- /// place.
- ///
- /// See the [module documentation] for details.
- ///
- /// [module documentation]: self
- ///
- /// *This trait is available if Syn is built with the `"visit-mut"` feature.*
- pub trait VisitMut {
- #traits
- }
-
- #impls
- },
- )?;
- Ok(())
-}