1#![doc(test(
76 no_crate_inject,
77 attr(
78 deny(warnings, rust_2018_idioms, single_use_lifetimes),
79 allow(dead_code, unused_variables)
80 )
81))]
82#![forbid(unsafe_code)]
83
84#[allow(unused_extern_crates)]
86extern crate proc_macro;
87
88#[macro_use]
89mod error;
90
91mod ast;
92mod iter;
93mod to_tokens;
94mod utils;
95
96use std::str::FromStr;
97
98use proc_macro::{Delimiter, TokenStream, TokenTree};
99
100use crate::{
101 ast::LitStr,
102 error::{Error, Result},
103 iter::TokenIter,
104 to_tokens::ToTokens,
105 utils::{cfg_attrs, parse_as_empty, tt_span},
106};
107
108#[proc_macro_attribute]
112pub fn const_fn(args: TokenStream, input: TokenStream) -> TokenStream {
113 expand(args, input).unwrap_or_else(Error::into_compile_error)
114}
115
116fn expand(args: TokenStream, input: TokenStream) -> Result<TokenStream> {
117 let arg = parse_arg(args)?;
118 let mut func = ast::parse_input(input)?;
119 Ok(match arg {
120 Arg::Cfg(cfg) => {
121 let (mut tokens, cfg_not) = cfg_attrs(cfg);
122 tokens.extend(func.to_token_stream());
123 tokens.extend(cfg_not);
124 func.print_const = false;
125 tokens.extend(func.to_token_stream());
126 tokens
127 }
128 Arg::Feature(feat) => {
129 let (mut tokens, cfg_not) = cfg_attrs(feat);
130 tokens.extend(func.to_token_stream());
131 tokens.extend(cfg_not);
132 func.print_const = false;
133 tokens.extend(func.to_token_stream());
134 tokens
135 }
136 Arg::Version(req) => {
137 if req.major > 1
138 || req.minor + cfg!(const_fn_assume_incomplete_release) as u32 > VERSION.minor
139 {
140 func.print_const = false;
141 }
142 func.to_token_stream()
143 }
144 Arg::Nightly => {
145 func.print_const = VERSION.nightly;
146 func.to_token_stream()
147 }
148 Arg::Always => func.to_token_stream(),
149 })
150}
151
152enum Arg {
153 Version(VersionReq),
155 Nightly,
157 Cfg(TokenStream),
159 Feature(TokenStream),
161 Always,
163}
164
165fn parse_arg(tokens: TokenStream) -> Result<Arg> {
166 let iter = &mut TokenIter::new(tokens);
167
168 let next = iter.next();
169 let next_span = tt_span(next.as_ref());
170 match next {
171 None => return Ok(Arg::Always),
172 Some(TokenTree::Ident(i)) => match &*i.to_string() {
173 "nightly" => {
174 parse_as_empty(iter)?;
175 return Ok(Arg::Nightly);
176 }
177 "cfg" => {
178 return match iter.next().as_ref() {
179 Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Parenthesis => {
180 parse_as_empty(iter)?;
181 Ok(Arg::Cfg(g.stream()))
182 }
183 tt => bail!(tt_span(tt), "expected `(`"),
184 };
185 }
186 "feature" => {
187 let next = iter.next();
188 return match next.as_ref() {
189 Some(TokenTree::Punct(p)) if p.as_char() == '=' => match iter.next() {
190 Some(TokenTree::Literal(l)) => {
191 let l = LitStr::new(l)?;
192 parse_as_empty(iter)?;
193 Ok(Arg::Feature(
194 vec![TokenTree::Ident(i), next.unwrap(), l.token.into()]
195 .into_iter()
196 .collect(),
197 ))
198 }
199 tt => bail!(tt_span(tt.as_ref()), "expected string literal"),
200 },
201 tt => bail!(tt_span(tt), "expected `=`"),
202 };
203 }
204 _ => {}
205 },
206 Some(TokenTree::Literal(l)) => {
207 if let Ok(l) = LitStr::new(l) {
208 parse_as_empty(iter)?;
209 return match l.value().parse::<VersionReq>() {
210 Ok(req) => Ok(Arg::Version(req)),
211 Err(e) => bail!(l.span(), "{}", e),
212 };
213 }
214 }
215 Some(_) => {}
216 }
217
218 bail!(next_span, "expected one of: `nightly`, `cfg`, `feature`, string literal")
219}
220
221struct VersionReq {
222 major: u32,
223 minor: u32,
224}
225
226impl FromStr for VersionReq {
227 type Err = String;
228
229 fn from_str(s: &str) -> Result<Self, Self::Err> {
230 let mut pieces = s.split('.');
231 let major = pieces
232 .next()
233 .ok_or("need to specify the major version")?
234 .parse::<u32>()
235 .map_err(|e| e.to_string())?;
236 let minor = pieces
237 .next()
238 .ok_or("need to specify the minor version")?
239 .parse::<u32>()
240 .map_err(|e| e.to_string())?;
241 if let Some(s) = pieces.next() {
242 Err(format!("unexpected input: .{}", s))
243 } else {
244 Ok(Self { major, minor })
245 }
246 }
247}
248
249struct Version {
250 minor: u32,
251 nightly: bool,
252}
253
254#[cfg(const_fn_has_build_script)]
257#[cfg(not(host_os = "windows"))]
258const VERSION: Version = include!(concat!(env!("OUT_DIR"), "/version"));
259#[cfg(const_fn_has_build_script)]
260#[cfg(host_os = "windows")]
261const VERSION: Version = include!(concat!(env!("OUT_DIR"), "\\version"));
262#[cfg(not(const_fn_has_build_script))]
264const VERSION: Version = Version { minor: 0, nightly: false };