#![doc = concat!("\"", alias_file!(), "\".")]
#![doc = include_str!(concat!("../", alias_file!()))]
#![cfg_attr(feature = "nightly", feature(doc_cfg))]
#![cfg_attr(feature = "nightly", feature(proc_macro_tracked_path))]
#![forbid(unsafe_code)]
#![warn(unused_results)]
use std::error;
use std::result;
#[cfg(feature = "nightly")]
use proc_macro::tracked;
use proc_macro::Delimiter;
use proc_macro::Group;
use proc_macro::Literal;
use proc_macro::Punct;
use proc_macro::Spacing;
use proc_macro::Span;
use proc_macro::TokenStream;
use proc_macro::TokenTree;
macro_rules! alias_file {
() => {
"src/attr-aliases.txt"
};
}
use alias_file;
macro_rules! tokens {
( $($token:expr ,)+ ) => {{
use proc_macro::TokenTree;
[$(TokenTree::from($token)),+].into_iter()
}};
}
macro_rules! path {
( $($name:expr),+ ) => {{
use proc_macro::Ident;
use proc_macro::Punct;
use proc_macro::Spacing;
use proc_macro::Span;
tokens!(
$(
Punct::new(':', Spacing::Joint),
Punct::new(':', Spacing::Alone),
Ident::new($name, Span::call_site()),
)+
)
}};
}
mod aliases;
use aliases::Aliases;
fn core_macro(name: &str, arg: &str) -> impl Iterator<Item = TokenTree> {
path!("core", name).chain(tokens!(
Punct::new('!', Spacing::Alone),
Group::new(
Delimiter::Parenthesis,
TokenTree::Literal(Literal::string(arg)).into(),
),
Punct::new(';', Spacing::Alone),
))
}
struct Error {
span: Span,
message: String,
}
impl Error {
fn new(message: &'static str) -> Self {
Self {
span: Span::call_site(),
message: message.to_owned(),
}
}
fn token(token: &TokenTree) -> Self {
Self {
span: token.span(),
message: "unexpected token".to_owned(),
}
}
fn into_compile_error(self) -> TokenStream {
core_macro("compile_error", &self.message)
.map(|mut token| {
token.set_span(self.span);
token
})
.collect()
}
}
type Result<T> = result::Result<T, Error>;
trait ResultExt<T> {
fn wrap_err(self, message: &str) -> Result<T>;
}
impl<T, E> ResultExt<T> for result::Result<T, E>
where
E: error::Error,
{
fn wrap_err(self, message: &str) -> Result<T> {
self.map_err(|error| Error {
span: Span::call_site(),
message: format!("{}: {}", message, error),
})
}
}
fn parse_empty<I>(tokens: I) -> Result<()>
where
I: IntoIterator<Item = TokenTree>,
{
tokens
.into_iter()
.next()
.map(|x| Err(Error::token(&x)))
.unwrap_or(Ok(()))
}
fn eval_item(item: TokenStream, resolved: &mut bool) -> Result<TokenStream> {
let mut attr = false;
item.into_iter()
.map(|mut token| {
if let TokenTree::Group(group) = &mut token {
let delimiter = group.delimiter();
let mut stream = group.stream();
if attr && delimiter == Delimiter::Bracket {
*resolved |= Aliases::get()?.resolve(&mut stream)?;
} else {
stream = eval_item(stream, resolved)?;
}
*group = Group::new(delimiter, stream);
}
attr = matches!(
&token,
TokenTree::Punct(x)
if x.as_char() == '#' || (attr && x.as_char() == '!'),
);
Ok(token)
})
.collect()
}
#[cfg(feature = "nightly")]
#[cfg_attr(feature = "nightly", doc(cfg(feature = "nightly")))]
#[proc_macro_attribute]
pub fn attr_alias(args: TokenStream, item: TokenStream) -> TokenStream {
tracked::path(Aliases::FILE);
Aliases::get()
.and_then(|x| x.resolve_args(args))
.map(|alias| {
tokens!(
Punct::new('#', Spacing::Joint),
Group::new(Delimiter::Bracket, alias),
)
.chain(item)
.collect()
})
.unwrap_or_else(Error::into_compile_error)
}
#[cfg_attr(
feature = "nightly",
doc = "
Using [`#[eval]`][macro@eval] would require a nightly feature:
```
#![feature(proc_macro_hygiene)]
#[attr_alias::eval]
#[attr_alias(macos, cfg_attr(*, path = \"sys/macos.rs\"))]
#[attr_alias(macos, cfg_attr(not(*), path = \"sys/common.rs\"))]
mod sys;
```"
)]
#[proc_macro]
pub fn eval_block(item: TokenStream) -> TokenStream {
let mut resolved = false;
let mut result = eval_item(item, &mut resolved)
.unwrap_or_else(Error::into_compile_error);
let trigger = if resolved {
Aliases::create_trigger()
} else {
Err(Error::new("unnecessary attribute"))
};
match trigger {
Ok(trigger) => result.extend(trigger),
Err(error) => result.extend(error.into_compile_error()),
}
result
}
#[cfg_attr(
feature = "nightly",
doc = " #[attr_alias(macos_or_windows, doc(cfg(*)))]"
)]
#[cfg_attr(
feature = "nightly",
doc = "
**Setting Lint Configuration:**
```
#![feature(custom_inner_attributes)]
# #![feature(prelude_import)]
#![attr_alias::eval]
#![attr_alias(warnings, *)]
```"
)]
#[proc_macro_attribute]
pub fn eval(args: TokenStream, item: TokenStream) -> TokenStream {
if let Err(error) = parse_empty(args) {
return error.into_compile_error();
}
eval_block(item)
}