blob: 861bedc49e00a07dfbc942a7eeb6022277dd6420 [file] [log] [blame]
//! Providing the features between "full" and "derive" of syn.
//!
//! This crate provides the following two unique data structures.
//!
//! * [`syn_mid::ItemFn`] -- A function whose body is not parsed.
//!
//! ```text
//! fn process(n: usize) -> Result<()> { ... }
//! ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^ ^
//! ```
//!
//! * [`syn_mid::Block`] -- A block whose body is not parsed.
//!
//! ```text
//! { ... }
//! ^ ^
//! ```
//!
//! Other data structures are the same as data structures of [syn]. These are defined in this crate
//! because they cannot be used in [syn] without "full" feature.
//!
//! ## Optional features
//!
//! syn-mid in the default features aims to provide the features between "full"
//! and "derive" of [syn].
//!
//! * **`clone-impls`** — Clone impls for all syntax tree types.
//!
//! [`syn_mid::ItemFn`]: struct.ItemFn.html
//! [`syn_mid::Block`]: struct.Block.html
//! [syn]: https://github.com/dtolnay/syn
#![doc(html_root_url = "https://docs.rs/syn-mid/0.4.0")]
#![doc(test(
no_crate_inject,
attr(deny(warnings, rust_2018_idioms, single_use_lifetimes), allow(dead_code))
))]
#![forbid(unsafe_code)]
#![warn(rust_2018_idioms, single_use_lifetimes, unreachable_pub)]
#![warn(clippy::all)]
#![allow(clippy::eval_order_dependence, clippy::large_enum_variant)]
// Many of the code contained in this crate are copies from https://github.com/dtolnay/syn.
#[macro_use]
mod macros;
mod arg;
mod pat;
mod path;
pub use self::{arg::*, pat::*};
use proc_macro2::TokenStream;
use syn::{
punctuated::Punctuated, token, Abi, Attribute, Generics, Ident, ReturnType, Token, Variadic,
Visibility,
};
ast_struct! {
/// A braced block containing Rust statements.
pub struct Block {
pub brace_token: token::Brace,
/// Statements in a block
pub stmts: TokenStream,
}
}
ast_struct! {
/// A free-standing function: `fn process(n: usize) -> Result<()> { ...
/// }`.
pub struct ItemFn {
pub attrs: Vec<Attribute>,
pub vis: Visibility,
pub sig: Signature,
pub block: Box<Block>,
}
}
ast_struct! {
/// A function signature in a trait or implementation: `unsafe fn
/// initialize(&self)`.
pub struct Signature {
pub constness: Option<Token![const]>,
pub asyncness: Option<Token![async]>,
pub unsafety: Option<Token![unsafe]>,
pub abi: Option<Abi>,
pub fn_token: Token![fn],
pub ident: Ident,
pub generics: Generics,
pub paren_token: token::Paren,
pub inputs: Punctuated<FnArg, Token![,]>,
pub variadic: Option<Variadic>,
pub output: ReturnType,
}
}
mod parsing {
use syn::{
braced, parenthesized,
parse::{Parse, ParseStream, Result},
parse2, Abi, Attribute, Generics, Ident, ReturnType, Token, Type, Variadic, Visibility,
WhereClause,
};
use super::{Block, FnArg, ItemFn, PatType, Signature};
impl Parse for Block {
fn parse(input: ParseStream<'_>) -> Result<Self> {
let content;
Ok(Self { brace_token: braced!(content in input), stmts: content.parse()? })
}
}
impl Parse for ItemFn {
fn parse(input: ParseStream<'_>) -> Result<Self> {
let attrs = input.call(Attribute::parse_outer)?;
let vis: Visibility = input.parse()?;
let constness: Option<Token![const]> = input.parse()?;
let asyncness: Option<Token![async]> = input.parse()?;
let unsafety: Option<Token![unsafe]> = input.parse()?;
let abi: Option<Abi> = input.parse()?;
let fn_token: Token![fn] = input.parse()?;
let ident: Ident = input.parse()?;
let generics: Generics = input.parse()?;
let content;
let paren_token = parenthesized!(content in input);
let inputs = content.parse_terminated(FnArg::parse)?;
let variadic = inputs.last().as_ref().and_then(get_variadic);
#[allow(clippy::trivially_copy_pass_by_ref)]
fn get_variadic(input: &&FnArg) -> Option<Variadic> {
if let FnArg::Typed(PatType { ty, .. }) = input {
if let Type::Verbatim(tokens) = &**ty {
if let Ok(dots) = parse2(tokens.clone()) {
return Some(Variadic { attrs: Vec::new(), dots });
}
}
}
None
}
let output: ReturnType = input.parse()?;
let where_clause: Option<WhereClause> = input.parse()?;
let block = input.parse()?;
Ok(Self {
attrs,
vis,
sig: Signature {
constness,
asyncness,
unsafety,
abi,
fn_token,
ident,
paren_token,
inputs,
output,
variadic,
generics: Generics { where_clause, ..generics },
},
block: Box::new(block),
})
}
}
}
mod printing {
use proc_macro2::TokenStream;
use quote::{ToTokens, TokenStreamExt};
use syn::{punctuated::Punctuated, Token, Type};
use super::{Block, FnArg, ItemFn, Signature};
impl ToTokens for Block {
fn to_tokens(&self, tokens: &mut TokenStream) {
self.brace_token.surround(tokens, |tokens| {
tokens.append_all(self.stmts.clone());
});
}
}
fn has_variadic(inputs: &Punctuated<FnArg, Token![,]>) -> bool {
let last = match inputs.last() {
Some(last) => last,
None => return false,
};
let pat = match last {
FnArg::Typed(pat) => pat,
FnArg::Receiver(_) => return false,
};
let tokens = match pat.ty.as_ref() {
Type::Verbatim(tokens) => tokens,
_ => return false,
};
tokens.to_string() == "..."
}
impl ToTokens for Signature {
fn to_tokens(&self, tokens: &mut TokenStream) {
self.constness.to_tokens(tokens);
self.asyncness.to_tokens(tokens);
self.unsafety.to_tokens(tokens);
self.abi.to_tokens(tokens);
self.fn_token.to_tokens(tokens);
self.ident.to_tokens(tokens);
self.generics.to_tokens(tokens);
self.paren_token.surround(tokens, |tokens| {
self.inputs.to_tokens(tokens);
if self.variadic.is_some() && !has_variadic(&self.inputs) {
if !self.inputs.empty_or_trailing() {
<Token![,]>::default().to_tokens(tokens);
}
self.variadic.to_tokens(tokens);
}
});
self.output.to_tokens(tokens);
self.generics.where_clause.to_tokens(tokens);
}
}
impl ToTokens for ItemFn {
fn to_tokens(&self, tokens: &mut TokenStream) {
tokens.append_all(&self.attrs);
self.vis.to_tokens(tokens);
self.sig.to_tokens(tokens);
self.block.brace_token.surround(tokens, |tokens| {
tokens.append_all(self.block.stmts.clone());
});
}
}
}