diff --git a/Cargo.toml b/Cargo.toml index e011ea9..b2bda08 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,5 +27,3 @@ proc-macro = true version_check = "0.9.2" [dependencies] -proc-macro2 = "1.0" -quote = "1.0" diff --git a/src/ast.rs b/src/ast.rs index e53aef3..b9acee5 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -1,8 +1,8 @@ -use proc_macro2::{Delimiter, Literal, Span, TokenStream, TokenTree}; -use quote::ToTokens; +use proc_macro::{Delimiter, Literal, Span, TokenStream, TokenTree}; use std::iter::Peekable; use crate::{ + to_tokens::ToTokens, utils::{parse_as_empty, tt_span}, Result, }; @@ -23,17 +23,22 @@ pub(crate) fn parse_input(input: TokenStream) -> Result { parse_as_empty(input)?; if body.is_none() - || !sig.iter().any(|tt| if let TokenTree::Ident(i) = tt { i == "fn" } else { false }) + || !sig + .iter() + .any(|tt| if let TokenTree::Ident(i) = tt { i.to_string() == "fn" } else { false }) { return Err(error!( Span::call_site(), "#[const_fn] attribute may only be used on functions" )); } - if !sig.iter().any(|tt| if let TokenTree::Ident(i) = tt { i == "const" } else { false }) { + if !sig + .iter() + .any(|tt| if let TokenTree::Ident(i) = tt { i.to_string() == "const" } else { false }) + { let span = sig .iter() - .position(|tt| if let TokenTree::Ident(i) = tt { i == "fn" } else { false }) + .position(|tt| if let TokenTree::Ident(i) = tt { i.to_string() == "fn" } else { false }) .map(|i| sig[i].span()) .unwrap(); return Err(error!(span, "#[const_fn] attribute may only be used on const functions")); @@ -50,7 +55,9 @@ impl ToTokens for Func { } else { self.sig .iter() - .filter(|tt| if let TokenTree::Ident(i) = tt { i != "const" } else { true }) + .filter( + |tt| if let TokenTree::Ident(i) = tt { i.to_string() != "const" } else { true }, + ) .for_each(|tt| tt.to_tokens(tokens)); } self.body.to_tokens(tokens); diff --git a/src/lib.rs b/src/lib.rs index 823bd35..5d43ff1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,15 +52,15 @@ mod utils; mod ast; mod error; +mod to_tokens; -use proc_macro::TokenStream; -use proc_macro2::{Delimiter, TokenStream as TokenStream2, TokenTree}; -use quote::{quote, ToTokens}; +use proc_macro::{Delimiter, TokenStream, TokenTree}; use std::str::FromStr; use crate::{ error::Error, - utils::{parse_as_empty, tt_span}, + to_tokens::ToTokens, + utils::{cfg_attrs, parse_as_empty, tt_span}, }; pub(crate) type Result = std::result::Result; @@ -69,43 +69,43 @@ pub(crate) type Result = std::result::Result; /// See crate level documentation for details. #[proc_macro_attribute] pub fn const_fn(args: TokenStream, input: TokenStream) -> TokenStream { - let arg = match parse_arg(args.into()) { + let arg = match parse_arg(args) { Ok(a) => a, Err(e) => return e.to_compile_error(), }; - let mut func = match ast::parse_input(input.into()) { + let mut func = match ast::parse_input(input) { Ok(i) => i, Err(e) => return e.to_compile_error(), }; match arg { Arg::Cfg(c) => { - let mut tokens = quote!(#[cfg(#c)]); + let (mut tokens, cfg_not) = cfg_attrs(c); tokens.extend(func.to_token_stream()); - tokens.extend(quote!(#[cfg(not(#c))])); + tokens.extend(cfg_not); func.print_const = false; - tokens.extend(func.into_token_stream()); - tokens.into() + tokens.extend(func.to_token_stream()); + tokens } Arg::Feature(f) => { - let mut tokens = quote!(#[cfg(#f)]); + let (mut tokens, cfg_not) = cfg_attrs(f); tokens.extend(func.to_token_stream()); - tokens.extend(quote!(#[cfg(not(#f))])); + tokens.extend(cfg_not); func.print_const = false; - tokens.extend(func.into_token_stream()); - tokens.into() + tokens.extend(func.to_token_stream()); + tokens } Arg::Version(req) => { if req.major > 1 || req.minor > VERSION.minor { func.print_const = false; } - func.into_token_stream().into() + func.to_token_stream() } Arg::Nightly => { if !VERSION.nightly { func.print_const = false; } - func.into_token_stream().into() + func.to_token_stream() } } } @@ -116,12 +116,12 @@ enum Arg { // `const_fn(nightly)` Nightly, // `const_fn(cfg(...))` - Cfg(TokenStream2), + Cfg(TokenStream), // `const_fn(feature = "...")` - Feature(TokenStream2), + Feature(TokenStream), } -fn parse_arg(tokens: TokenStream2) -> Result { +fn parse_arg(tokens: TokenStream) -> Result { let tokens2 = tokens.clone(); let mut iter = tokens.into_iter(); diff --git a/src/to_tokens.rs b/src/to_tokens.rs new file mode 100644 index 0000000..6cc51fd --- /dev/null +++ b/src/to_tokens.rs @@ -0,0 +1,36 @@ +use proc_macro::*; +use std::iter; + +pub(crate) trait ToTokens { + fn to_tokens(&self, tokens: &mut TokenStream); + + fn to_token_stream(&self) -> TokenStream { + let mut tokens = TokenStream::new(); + self.to_tokens(&mut tokens); + tokens + } +} + +impl ToTokens for Ident { + fn to_tokens(&self, tokens: &mut TokenStream) { + tokens.extend(iter::once(TokenTree::Ident(self.clone()))); + } +} + +impl ToTokens for Literal { + fn to_tokens(&self, tokens: &mut TokenStream) { + tokens.extend(iter::once(TokenTree::Literal(self.clone()))); + } +} + +impl ToTokens for TokenTree { + fn to_tokens(&self, tokens: &mut TokenStream) { + tokens.extend(iter::once(self.clone())); + } +} + +impl ToTokens for TokenStream { + fn to_tokens(&self, tokens: &mut TokenStream) { + tokens.extend(self.clone()); + } +} diff --git a/src/utils.rs b/src/utils.rs index 897dd71..78be0a9 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,10 +1,11 @@ -use proc_macro2::{Span, TokenTree}; +use proc_macro::*; +use std::iter::FromIterator; use crate::Result; macro_rules! error { ($span:expr, $msg:expr) => {{ - crate::error::Error::new($span.unwrap(), $msg) + crate::error::Error::new($span, $msg) }}; ($span:expr, $($tt:tt)*) => { error!($span, format!($($tt)*)) @@ -21,3 +22,25 @@ pub(crate) fn parse_as_empty(mut tokens: impl Iterator) -> Res None => Ok(()), } } + +// (`#[cfg()]`, `#[cfg(not())]`) +pub(crate) fn cfg_attrs(tokens: TokenStream) -> (TokenStream, TokenStream) { + let f = |tokens| { + let tokens = TokenStream::from_iter(vec![ + TokenTree::Ident(Ident::new("cfg", Span::call_site())), + TokenTree::Group(Group::new(Delimiter::Parenthesis, tokens)), + ]); + TokenStream::from_iter(vec![ + TokenTree::Punct(Punct::new('#', Spacing::Alone)), + TokenTree::Group(Group::new(Delimiter::Bracket, tokens)), + ]) + }; + + let cfg_not = TokenTree::Group(Group::new(Delimiter::Parenthesis, tokens.clone())); + let cfg_not = TokenStream::from_iter(vec![ + TokenTree::Ident(Ident::new("not", Span::call_site())), + cfg_not, + ]); + + (f(tokens), f(cfg_not)) +}