Skip to content

Commit

Permalink
Accept const_fn attribute with no arguments and functions without con…
Browse files Browse the repository at this point in the history
…st keyword
  • Loading branch information
taiki-e committed Dec 9, 2020
1 parent 950ba90 commit 0ba1df1
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 17 deletions.
4 changes: 1 addition & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,7 @@ jobs:
strategy:
matrix:
rust:
- 1.31
- 1.33
- 1.39
- 1.45
- 1.46
- stable
- beta
Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@ This project adheres to [Semantic Versioning](https://semver.org).

## [Unreleased]

* Accept `const_fn` attribute with no arguments and functions without `const` keyword.
This allows `const_fn` to be used as an optional dependency.

```rust
#[cfg_attr(feature = "...", const_fn::const_fn)]
pub fn func() {
/* ... */
}
```

## [0.4.3] - 2020-11-02

* [`const_fn` no longer fails to compile if unable to determine rustc version. Instead, it now displays a warning.](https://github.com/taiki-e/const_fn/pull/31)
Expand Down
25 changes: 13 additions & 12 deletions src/ast.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use proc_macro::{Delimiter, Literal, Span, TokenStream, TokenTree};
use proc_macro::{Delimiter, Ident, Literal, Span, TokenStream, TokenTree};

use crate::{
iter::TokenIter,
Expand Down Expand Up @@ -32,17 +32,6 @@ 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 })
}
Expand All @@ -66,10 +55,22 @@ 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() {
"const" => has_const = true,
"async" | "unsafe" | "extern" | "fn" => {
sig.push(TokenTree::Ident(Ident::new("const", i.span())));
has_const = true;
}
_ => {}
}
sig.push(input.next().unwrap());
}
Some(_) => sig.push(input.next().unwrap()),
}
}
Expand Down
6 changes: 5 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ fn expand(arg: Arg, mut func: Func) -> TokenStream {
func.print_const = VERSION.nightly;
func.to_token_stream()
}
Arg::Always => func.to_token_stream(),
}
}

Expand All @@ -131,6 +132,8 @@ enum Arg {
Cfg(TokenStream),
// `const_fn(feature = "...")`
Feature(TokenStream),
// `const_fn`
Always,
}

fn parse_arg(tokens: TokenStream) -> Result<Arg> {
Expand All @@ -139,6 +142,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)?;
Expand Down Expand Up @@ -182,7 +186,7 @@ fn parse_arg(tokens: TokenStream) -> Result<Arg> {
};
}
}
_ => {}
Some(_) => {}
}

Err(error!(next_span, "expected one of: `nightly`, `cfg`, `feature`, string literal"))
Expand Down
73 changes: 72 additions & 1 deletion test_suite/tests/test.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,76 @@
#![cfg_attr(const_unstable, feature(const_fn))]
#![cfg_attr(const_unstable, feature(const_fn, const_extern_fn))]
#![warn(rust_2018_idioms, single_use_lifetimes)]
#![allow(clippy::missing_safety_doc)] // this is test

pub mod syntax {
#![allow(dead_code)]

use const_fn::const_fn;

// const
#[const_fn]
fn const_non_const() {}
#[const_fn]
pub fn const_non_const_pub() {}
#[const_fn]
const fn const_const() {}
#[const_fn]
pub const fn const_const_pub() {}
const _: () = const_non_const();
const _: () = const_non_const_pub();
const _: () = const_const();
const _: () = const_const_pub();

// const unsafe
#[const_fn]
unsafe fn const_unsafe_non_const() {}
#[const_fn]
pub unsafe fn const_unsafe_non_const_pub() {}
#[const_fn]
const unsafe fn const_unsafe_const() {}
#[const_fn]
pub const unsafe fn const_unsafe_const_pub() {}
const _: () = unsafe { const_unsafe_non_const() };
const _: () = unsafe { const_unsafe_non_const_pub() };
const _: () = unsafe { const_unsafe_const() };
const _: () = unsafe { const_unsafe_const_pub() };

// const extern
#[const_fn(cfg(const_unstable))]
extern "C" fn const_extern_non_const() {}
#[const_fn(cfg(const_unstable))]
pub extern "C" fn const_extern_non_const_pub() {}
#[const_fn(cfg(const_unstable))]
const extern "C" fn const_extern_const() {}
#[const_fn(cfg(const_unstable))]
pub const extern "C" fn const_extern_const_pub() {}
#[cfg(const_unstable)]
const _: () = const_extern_non_const();
#[cfg(const_unstable)]
const _: () = const_extern_non_const_pub();
#[cfg(const_unstable)]
const _: () = const_extern_const();
#[cfg(const_unstable)]
const _: () = const_extern_const_pub();

// const unsafe extern
#[const_fn(cfg(const_unstable))]
unsafe extern "C" fn const_unsafe_extern_non_const() {}
#[const_fn(cfg(const_unstable))]
pub unsafe extern "C" fn const_unsafe_extern_non_const_pub() {}
#[const_fn(cfg(const_unstable))]
const unsafe extern "C" fn const_unsafe_extern_const() {}
#[const_fn(cfg(const_unstable))]
pub const unsafe extern "C" fn const_unsafe_extern_const_pub() {}
#[cfg(const_unstable)]
const _: () = unsafe { const_unsafe_extern_non_const() };
#[cfg(const_unstable)]
const _: () = unsafe { const_unsafe_extern_non_const_pub() };
#[cfg(const_unstable)]
const _: () = unsafe { const_unsafe_extern_const() };
#[cfg(const_unstable)]
const _: () = unsafe { const_unsafe_extern_const_pub() };
}

pub mod version {
use const_fn::const_fn;
Expand Down

0 comments on commit 0ba1df1

Please sign in to comment.