Skip to content

Commit

Permalink
Merge #20
Browse files Browse the repository at this point in the history
20: Remove dependency on quote & proc-macro2 r=taiki-e a=taiki-e

Closes #16

Co-authored-by: Taiki Endo <[email protected]>
  • Loading branch information
bors[bot] and taiki-e authored Aug 25, 2020
2 parents 384d0b7 + 85520ea commit 12672ab
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 29 deletions.
2 changes: 0 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,3 @@ proc-macro = true
version_check = "0.9.2"

[dependencies]
proc-macro2 = "1.0"
quote = "1.0"
19 changes: 13 additions & 6 deletions src/ast.rs
Original file line number Diff line number Diff line change
@@ -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,
};
Expand All @@ -23,17 +23,22 @@ pub(crate) fn parse_input(input: TokenStream) -> Result<Func> {
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"));
Expand All @@ -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);
Expand Down
38 changes: 19 additions & 19 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T, E = Error> = std::result::Result<T, E>;
Expand All @@ -69,43 +69,43 @@ pub(crate) type Result<T, E = Error> = std::result::Result<T, E>;
/// 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()
}
}
}
Expand All @@ -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<Arg> {
fn parse_arg(tokens: TokenStream) -> Result<Arg> {
let tokens2 = tokens.clone();
let mut iter = tokens.into_iter();

Expand Down
36 changes: 36 additions & 0 deletions src/to_tokens.rs
Original file line number Diff line number Diff line change
@@ -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());
}
}
27 changes: 25 additions & 2 deletions src/utils.rs
Original file line number Diff line number Diff line change
@@ -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)*))
Expand All @@ -21,3 +22,25 @@ pub(crate) fn parse_as_empty(mut tokens: impl Iterator<Item = TokenTree>) -> Res
None => Ok(()),
}
}

// (`#[cfg(<tokens>)]`, `#[cfg(not(<tokens>))]`)
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))
}

0 comments on commit 12672ab

Please sign in to comment.