diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/ast.rs | 58 | ||||
-rw-r--r-- | src/error.rs | 3 | ||||
-rw-r--r-- | src/lib.rs | 30 | ||||
-rw-r--r-- | src/to_tokens.rs | 3 | ||||
-rw-r--r-- | src/utils.rs | 5 |
5 files changed, 54 insertions, 45 deletions
@@ -1,16 +1,12 @@ -use proc_macro::{Delimiter, Literal, Span, TokenStream, TokenTree}; +use proc_macro::{Delimiter, Ident, Literal, Span, TokenStream, TokenTree}; -use crate::{ - iter::TokenIter, - to_tokens::ToTokens, - utils::{parse_as_empty, tt_span}, - Result, -}; +use crate::{iter::TokenIter, to_tokens::ToTokens, utils::tt_span, Result}; pub(crate) struct Func { - pub(crate) attrs: Vec<Attribute>, - pub(crate) sig: Vec<TokenTree>, - pub(crate) body: TokenTree, + attrs: Vec<Attribute>, + // [const] [async] [unsafe] [extern [<abi>]] fn + sig: Vec<TokenTree>, + body: TokenStream, pub(crate) print_const: bool, } @@ -19,10 +15,9 @@ pub(crate) fn parse_input(input: TokenStream) -> Result<Func> { let attrs = parse_attrs(&mut input)?; let sig = parse_signature(&mut input); - let body = input.next(); - parse_as_empty(input)?; + let body: TokenStream = input.collect(); - if body.is_none() + if body.is_empty() || !sig .iter() .any(|tt| if let TokenTree::Ident(i) = tt { i.to_string() == "fn" } else { false }) @@ -32,19 +27,8 @@ pub(crate) fn parse_input(input: TokenStream) -> Result<Func> { "#[const_fn] attribute may only be used on functions" )); } - 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.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")); - } - Ok(Func { attrs, sig, body: body.unwrap(), print_const: true }) + Ok(Func { attrs, sig, body, print_const: true }) } impl ToTokens for Func { @@ -66,10 +50,32 @@ impl ToTokens for Func { fn parse_signature(input: &mut TokenIter) -> Vec<TokenTree> { let mut sig = Vec::new(); + let mut has_const = false; loop { match input.peek() { - Some(TokenTree::Group(group)) if group.delimiter() == Delimiter::Brace => break, None => break, + Some(TokenTree::Ident(i)) if !has_const => { + match &*i.to_string() { + "fn" => { + sig.push(TokenTree::Ident(Ident::new("const", i.span()))); + sig.push(input.next().unwrap()); + break; + } + "const" => { + has_const = true; + } + "async" | "unsafe" | "extern" => { + has_const = true; + sig.push(TokenTree::Ident(Ident::new("const", i.span()))); + } + _ => {} + } + sig.push(input.next().unwrap()); + } + Some(TokenTree::Ident(i)) if i.to_string() == "fn" => { + sig.push(input.next().unwrap()); + break; + } Some(_) => sig.push(input.next().unwrap()), } } diff --git a/src/error.rs b/src/error.rs index b572d5a..e4edb0e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,6 +1,7 @@ -use proc_macro::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree}; use std::iter::FromIterator; +use proc_macro::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree}; + pub(crate) struct Error { span: Span, msg: String, @@ -1,4 +1,5 @@ -//! An attribute for easy generation of const functions with conditional compilations. +//! An attribute for easy generation of const functions with conditional +//! compilations. //! //! # Examples //! @@ -39,23 +40,16 @@ //! You can manually define declarative macros with similar functionality (see [`if_rust_version`](https://github.com/ogoffart/if_rust_version#examples)), or [you can define the same function twice with different cfg](https://github.com/crossbeam-rs/crossbeam/blob/0b6ea5f69fde8768c1cfac0d3601e0b4325d7997/crossbeam-epoch/src/atomic.rs#L340-L372). //! (Note: the former approach requires more macros to be defined depending on the number of version requirements, the latter approach requires more functions to be maintained manually) -#![doc(html_root_url = "https://docs.rs/const_fn/0.4.3")] #![doc(test( no_crate_inject, - attr(deny(warnings, rust_2018_idioms, single_use_lifetimes), allow(dead_code)) + attr( + deny(warnings, rust_2018_idioms, single_use_lifetimes), + allow(dead_code, unused_variables) + ) ))] #![forbid(unsafe_code)] #![warn(future_incompatible, rust_2018_idioms, single_use_lifetimes, unreachable_pub)] #![warn(clippy::all, clippy::default_trait_access)] -// mem::take, #[non_exhaustive], and Option::{as_deref, as_deref_mut} require Rust 1.40, -// matches! requires Rust 1.42, str::{strip_prefix, strip_suffix} requires Rust 1.45 -#![allow( - clippy::mem_replace_with_default, - clippy::manual_non_exhaustive, - clippy::option_as_ref_deref, - clippy::match_like_matches_macro, - clippy::manual_strip -)] // older compilers require explicit `extern crate`. #[allow(unused_extern_crates)] @@ -69,9 +63,10 @@ mod error; mod iter; mod to_tokens; -use proc_macro::{Delimiter, TokenStream, TokenTree}; use std::str::FromStr; +use proc_macro::{Delimiter, TokenStream, TokenTree}; + use crate::{ ast::{Func, LitStr}, error::Error, @@ -82,6 +77,7 @@ use crate::{ type Result<T, E = Error> = std::result::Result<T, E>; /// An attribute for easy generation of const functions with conditional compilations. +/// /// See crate level documentation for details. #[proc_macro_attribute] pub fn const_fn(args: TokenStream, input: TokenStream) -> TokenStream { @@ -125,6 +121,7 @@ fn expand(arg: Arg, mut func: Func) -> TokenStream { func.print_const = VERSION.nightly; func.to_token_stream() } + Arg::Always => func.to_token_stream(), } } @@ -137,6 +134,8 @@ enum Arg { Cfg(TokenStream), // `const_fn(feature = "...")` Feature(TokenStream), + // `const_fn` + Always, } fn parse_arg(tokens: TokenStream) -> Result<Arg> { @@ -145,6 +144,7 @@ fn parse_arg(tokens: TokenStream) -> Result<Arg> { let next = iter.next(); let next_span = tt_span(next.as_ref()); match next { + None => return Ok(Arg::Always), Some(TokenTree::Ident(i)) => match &*i.to_string() { "nightly" => { parse_as_empty(iter)?; @@ -188,7 +188,7 @@ fn parse_arg(tokens: TokenStream) -> Result<Arg> { }; } } - _ => {} + Some(_) => {} } Err(error!(next_span, "expected one of: `nightly`, `cfg`, `feature`, string literal")) @@ -228,7 +228,7 @@ struct Version { } #[cfg(const_fn_has_build_script)] -const VERSION: Version = include!(concat!(env!("OUT_DIR"), "/version.rs")); +const VERSION: Version = include!(concat!(env!("OUT_DIR"), "/version")); // If build script has not run or unable to determine version, it is considered as Rust 1.0. #[cfg(not(const_fn_has_build_script))] const VERSION: Version = Version { minor: 0, nightly: false }; diff --git a/src/to_tokens.rs b/src/to_tokens.rs index 771c7f6..2f44223 100644 --- a/src/to_tokens.rs +++ b/src/to_tokens.rs @@ -1,6 +1,7 @@ -use proc_macro::{Ident, Literal, TokenStream, TokenTree}; use std::iter; +use proc_macro::{Ident, Literal, TokenStream, TokenTree}; + pub(crate) trait ToTokens { fn to_tokens(&self, tokens: &mut TokenStream); diff --git a/src/utils.rs b/src/utils.rs index e557ea5..30b04aa 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,6 +1,7 @@ -use proc_macro::{Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree}; use std::iter::FromIterator; +use proc_macro::{Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree}; + use crate::Result; macro_rules! error { @@ -18,7 +19,7 @@ pub(crate) fn tt_span(tt: Option<&TokenTree>) -> Span { pub(crate) fn parse_as_empty(mut tokens: impl Iterator<Item = TokenTree>) -> Result<()> { match tokens.next() { - Some(tt) => Err(error!(tt.span(), "unexpected token: {}", tt)), + Some(tt) => Err(error!(tt.span(), "unexpected token `{}`", tt)), None => Ok(()), } } |