aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ast.rs58
-rw-r--r--src/error.rs3
-rw-r--r--src/lib.rs30
-rw-r--r--src/to_tokens.rs3
-rw-r--r--src/utils.rs5
5 files changed, 54 insertions, 45 deletions
diff --git a/src/ast.rs b/src/ast.rs
index b156493..88a2b96 100644
--- a/src/ast.rs
+++ b/src/ast.rs
@@ -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,
diff --git a/src/lib.rs b/src/lib.rs
index 1f818f8..c023fb1 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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(()),
}
}