mirror of
https://gitee.com/openharmony/third_party_rust_proc-macro-error
synced 2024-11-23 07:30:04 +00:00
Make syn optional
This commit is contained in:
parent
d99e966694
commit
f7bbbe3e5e
10
Cargo.toml
10
Cargo.toml
@ -22,9 +22,13 @@ targets = ["x86_64-unknown-linux-gnu"]
|
||||
[dependencies]
|
||||
quote = "1"
|
||||
proc-macro2 = "1"
|
||||
syn = { version = "1", default-features = false, features = ["derive", "parsing", "proc-macro", "printing"] }
|
||||
proc-macro-error-attr = { path = "./proc-macro-error-attr", version = "=1.0.3"}
|
||||
|
||||
[dependencies.syn]
|
||||
version = "1"
|
||||
optional = true
|
||||
default-features = false
|
||||
|
||||
[dev-dependencies]
|
||||
test-crate = { path = "./test-crate" }
|
||||
proc-macro-hack-test = { path = "./test-crate/proc-macro-hack-test" }
|
||||
@ -34,3 +38,7 @@ serde_derive = "=1.0.107" # DO NOT BUMP
|
||||
|
||||
[build-dependencies]
|
||||
version_check = "0.9"
|
||||
|
||||
[features]
|
||||
default = ["syn-error"]
|
||||
syn-error = ["syn"]
|
||||
|
@ -18,9 +18,6 @@ targets = ["x86_64-unknown-linux-gnu"]
|
||||
[dependencies]
|
||||
quote = "1"
|
||||
proc-macro2 = "1"
|
||||
syn-mid = "0.5"
|
||||
# "derive" is for `Attribute`, "parsing" is for `Parse`
|
||||
syn = { version = "1", default-features = false, features = ["derive", "parsing", "proc-macro", "printing"] }
|
||||
|
||||
[build-dependencies]
|
||||
version_check = "0.9"
|
||||
|
@ -3,37 +3,59 @@
|
||||
|
||||
extern crate proc_macro;
|
||||
|
||||
use crate::parse::parse_input;
|
||||
use crate::parse::Attribute;
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::Ident;
|
||||
use quote::quote;
|
||||
use std::iter::FromIterator;
|
||||
use syn::{
|
||||
parse::{Parse, ParseStream},
|
||||
parse_macro_input,
|
||||
punctuated::Punctuated,
|
||||
Attribute, Token,
|
||||
};
|
||||
use syn_mid::{Block, ItemFn};
|
||||
use proc_macro2::{Literal, Span, TokenStream as TokenStream2, TokenTree};
|
||||
use quote::{quote, quote_spanned};
|
||||
|
||||
use self::Setting::*;
|
||||
use crate::settings::{Setting::*, *};
|
||||
|
||||
mod parse;
|
||||
mod settings;
|
||||
|
||||
type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
struct Error {
|
||||
span: Span,
|
||||
message: String,
|
||||
}
|
||||
|
||||
impl Error {
|
||||
fn new(span: Span, message: String) -> Self {
|
||||
Error { span, message }
|
||||
}
|
||||
|
||||
fn into_compile_error(self) -> TokenStream2 {
|
||||
let mut message = Literal::string(&self.message);
|
||||
message.set_span(self.span);
|
||||
quote_spanned!(self.span=> compile_error!{#message})
|
||||
}
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn proc_macro_error(attr: TokenStream, input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as ItemFn);
|
||||
let mut settings = match syn::parse::<Settings>(attr) {
|
||||
Ok(settings) => settings,
|
||||
Err(err) => {
|
||||
let err = err.to_compile_error();
|
||||
return quote!(#input #err).into();
|
||||
}
|
||||
};
|
||||
match impl_proc_macro_error(attr.into(), input.clone().into()) {
|
||||
Ok(ts) => ts,
|
||||
Err(e) => {
|
||||
let error = e.into_compile_error();
|
||||
let input = TokenStream2::from(input);
|
||||
|
||||
let is_proc_macro = is_proc_macro(&input.attrs);
|
||||
quote!(#input #error).into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn impl_proc_macro_error(attr: TokenStream2, input: TokenStream2) -> Result<TokenStream> {
|
||||
let (attrs, signature, body) = parse_input(input)?;
|
||||
let mut settings = parse_settings(attr)?;
|
||||
|
||||
let is_proc_macro = is_proc_macro(&attrs);
|
||||
if is_proc_macro {
|
||||
settings.set(AssertUnwindSafe);
|
||||
}
|
||||
|
||||
if detect_proc_macro_hack(&input.attrs) {
|
||||
if detect_proc_macro_hack(&attrs) {
|
||||
settings.set(ProcMacroHack);
|
||||
}
|
||||
|
||||
@ -42,80 +64,27 @@ pub fn proc_macro_error(attr: TokenStream, input: TokenStream) -> TokenStream {
|
||||
}
|
||||
|
||||
if !(settings.is_set(AllowNotMacro) || is_proc_macro) {
|
||||
return quote!(
|
||||
#input
|
||||
compile_error!(
|
||||
"#[proc_macro_error] attribute can be used only with a proc-macro\n\n \
|
||||
= hint: if you are really sure that #[proc_macro_error] should be applied \
|
||||
to this exact function use #[proc_macro_error(allow_not_macro)]\n");
|
||||
)
|
||||
.into();
|
||||
return Err(Error::new(
|
||||
Span::call_site(),
|
||||
"#[proc_macro_error] attribute can be used only with procedural macros\n\n \
|
||||
= hint: if you are really sure that #[proc_macro_error] should be applied \
|
||||
to this exact function use, #[proc_macro_error(allow_not_macro)]\n"
|
||||
.into(),
|
||||
));
|
||||
}
|
||||
|
||||
let ItemFn {
|
||||
attrs,
|
||||
vis,
|
||||
sig,
|
||||
block,
|
||||
} = input;
|
||||
let body = gen_body(body, settings);
|
||||
|
||||
let body = gen_body(*block, settings);
|
||||
|
||||
quote!(
|
||||
let res = quote! {
|
||||
#(#attrs)*
|
||||
#vis
|
||||
#sig
|
||||
#(#signature)*
|
||||
{ #body }
|
||||
)
|
||||
.into()
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
enum Setting {
|
||||
AssertUnwindSafe,
|
||||
AllowNotMacro,
|
||||
ProcMacroHack,
|
||||
}
|
||||
|
||||
impl Parse for Setting {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
let ident: Ident = input.parse()?;
|
||||
match &*ident.to_string() {
|
||||
"assert_unwind_safe" => Ok(AssertUnwindSafe),
|
||||
"allow_not_macro" => Ok(AllowNotMacro),
|
||||
"proc_macro_hack" => Ok(ProcMacroHack),
|
||||
_ => Err(syn::Error::new(
|
||||
ident.span(),
|
||||
format!(
|
||||
"unknown setting `{}`, expected one of \
|
||||
`assert_unwind_safe`, `allow_not_macro`, `proc_macro_hack`",
|
||||
ident
|
||||
),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Settings(Vec<Setting>);
|
||||
impl Parse for Settings {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
let punct = Punctuated::<Setting, Token![,]>::parse_terminated(input)?;
|
||||
Ok(Settings(Vec::from_iter(punct)))
|
||||
}
|
||||
}
|
||||
|
||||
impl Settings {
|
||||
fn is_set(&self, setting: Setting) -> bool {
|
||||
self.0.iter().any(|s| *s == setting)
|
||||
}
|
||||
|
||||
fn set(&mut self, setting: Setting) {
|
||||
self.0.push(setting)
|
||||
}
|
||||
};
|
||||
Ok(res.into())
|
||||
}
|
||||
|
||||
#[cfg(not(always_assert_unwind))]
|
||||
fn gen_body(block: Block, settings: Settings) -> proc_macro2::TokenStream {
|
||||
fn gen_body(block: TokenTree, settings: Settings) -> proc_macro2::TokenStream {
|
||||
let is_proc_macro_hack = settings.is_set(ProcMacroHack);
|
||||
let closure = if settings.is_set(AssertUnwindSafe) {
|
||||
quote!(::std::panic::AssertUnwindSafe(|| #block ))
|
||||
@ -131,7 +100,7 @@ fn gen_body(block: Block, settings: Settings) -> proc_macro2::TokenStream {
|
||||
// Considering this is the closure's return type the unwind safety check would fail
|
||||
// for virtually every closure possible, the check is meaningless.
|
||||
#[cfg(always_assert_unwind)]
|
||||
fn gen_body(block: Block, settings: Settings) -> proc_macro2::TokenStream {
|
||||
fn gen_body(block: TokenTree, settings: Settings) -> proc_macro2::TokenStream {
|
||||
let is_proc_macro_hack = settings.is_set(ProcMacroHack);
|
||||
let closure = quote!(::std::panic::AssertUnwindSafe(|| #block ));
|
||||
quote!( ::proc_macro_error::entry_point(#closure, #is_proc_macro_hack) )
|
||||
@ -140,13 +109,13 @@ fn gen_body(block: Block, settings: Settings) -> proc_macro2::TokenStream {
|
||||
fn detect_proc_macro_hack(attrs: &[Attribute]) -> bool {
|
||||
attrs
|
||||
.iter()
|
||||
.any(|attr| attr.path.is_ident("proc_macro_hack"))
|
||||
.any(|attr| attr.path_is_ident("proc_macro_hack"))
|
||||
}
|
||||
|
||||
fn is_proc_macro(attrs: &[Attribute]) -> bool {
|
||||
attrs.iter().any(|attr| {
|
||||
attr.path.is_ident("proc_macro")
|
||||
|| attr.path.is_ident("proc_macro_derive")
|
||||
|| attr.path.is_ident("proc_macro_attribute")
|
||||
attr.path_is_ident("proc_macro")
|
||||
|| attr.path_is_ident("proc_macro_derive")
|
||||
|| attr.path_is_ident("proc_macro_attribute")
|
||||
})
|
||||
}
|
||||
|
89
proc-macro-error-attr/src/parse.rs
Normal file
89
proc-macro-error-attr/src/parse.rs
Normal file
@ -0,0 +1,89 @@
|
||||
use crate::{Error, Result};
|
||||
use proc_macro2::{Delimiter, Ident, Span, TokenStream, TokenTree};
|
||||
use quote::ToTokens;
|
||||
use std::iter::Peekable;
|
||||
|
||||
pub(crate) fn parse_input(
|
||||
input: TokenStream,
|
||||
) -> Result<(Vec<Attribute>, Vec<TokenTree>, TokenTree)> {
|
||||
let mut input = input.into_iter().peekable();
|
||||
let mut attrs = Vec::new();
|
||||
|
||||
while let Some(attr) = parse_next_attr(&mut input)? {
|
||||
attrs.push(attr);
|
||||
}
|
||||
|
||||
let sig = parse_signature(&mut input);
|
||||
let body = input.next().ok_or_else(|| {
|
||||
Error::new(
|
||||
Span::call_site(),
|
||||
"`#[proc_macro_error]` can be applied only to functions".to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok((attrs, sig, body))
|
||||
}
|
||||
|
||||
fn parse_next_attr(
|
||||
input: &mut Peekable<impl Iterator<Item = TokenTree>>,
|
||||
) -> Result<Option<Attribute>> {
|
||||
let shebang = match input.peek() {
|
||||
Some(TokenTree::Punct(punct)) if punct.as_char() == '#' => input.next().unwrap(),
|
||||
_ => return Ok(None),
|
||||
};
|
||||
|
||||
let group = match input.peek() {
|
||||
Some(TokenTree::Group(group)) if group.delimiter() == Delimiter::Bracket => {
|
||||
let res = group.clone();
|
||||
input.next();
|
||||
res
|
||||
}
|
||||
other => {
|
||||
let span = other.map_or(Span::call_site(), |tt| tt.span());
|
||||
return Err(Error::new(span, "expected `[`".to_string()));
|
||||
}
|
||||
};
|
||||
|
||||
let path = match group.stream().into_iter().next() {
|
||||
Some(TokenTree::Ident(ident)) => Some(ident),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
Ok(Some(Attribute {
|
||||
shebang,
|
||||
group: TokenTree::Group(group),
|
||||
path,
|
||||
}))
|
||||
}
|
||||
|
||||
fn parse_signature(input: &mut Peekable<impl Iterator<Item = TokenTree>>) -> Vec<TokenTree> {
|
||||
let mut sig = Vec::new();
|
||||
loop {
|
||||
match input.peek() {
|
||||
Some(TokenTree::Group(group)) if group.delimiter() == Delimiter::Brace => {
|
||||
return sig;
|
||||
}
|
||||
None => return sig,
|
||||
_ => sig.push(input.next().unwrap()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct Attribute {
|
||||
pub(crate) shebang: TokenTree,
|
||||
pub(crate) group: TokenTree,
|
||||
pub(crate) path: Option<Ident>,
|
||||
}
|
||||
|
||||
impl Attribute {
|
||||
pub(crate) fn path_is_ident(&self, ident: &str) -> bool {
|
||||
self.path.as_ref().map_or(false, |p| *p == ident)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for Attribute {
|
||||
fn to_tokens(&self, ts: &mut TokenStream) {
|
||||
self.shebang.to_tokens(ts);
|
||||
self.group.to_tokens(ts);
|
||||
}
|
||||
}
|
72
proc-macro-error-attr/src/settings.rs
Normal file
72
proc-macro-error-attr/src/settings.rs
Normal file
@ -0,0 +1,72 @@
|
||||
use crate::{Error, Result};
|
||||
use proc_macro2::{Ident, Span, TokenStream, TokenTree};
|
||||
|
||||
macro_rules! decl_settings {
|
||||
($($val:expr => $variant:ident),+ $(,)*) => {
|
||||
#[derive(PartialEq)]
|
||||
pub(crate) enum Setting {
|
||||
$($variant),*
|
||||
}
|
||||
|
||||
fn ident_to_setting(ident: Ident) -> Result<Setting> {
|
||||
match &*ident.to_string() {
|
||||
$($val => Ok(Setting::$variant),)*
|
||||
_ => {
|
||||
let possible_vals = [$($val),*]
|
||||
.iter()
|
||||
.map(|v| format!("`{}`", v))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
|
||||
Err(Error::new(
|
||||
ident.span(),
|
||||
format!("unknown setting `{}`, expected one of {}", ident, possible_vals)))
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
decl_settings! {
|
||||
"assert_unwind_safe" => AssertUnwindSafe,
|
||||
"allow_not_macro" => AllowNotMacro,
|
||||
"proc_macro_hack" => ProcMacroHack,
|
||||
}
|
||||
|
||||
pub(crate) fn parse_settings(input: TokenStream) -> Result<Settings> {
|
||||
let mut input = input.into_iter();
|
||||
let mut res = Settings(Vec::new());
|
||||
loop {
|
||||
match input.next() {
|
||||
Some(TokenTree::Ident(ident)) => {
|
||||
res.0.push(ident_to_setting(ident)?);
|
||||
}
|
||||
None => return Ok(res),
|
||||
other => {
|
||||
let span = other.map_or(Span::call_site(), |tt| tt.span());
|
||||
return Err(Error::new(span, "expected identifier".to_string()));
|
||||
}
|
||||
}
|
||||
|
||||
match input.next() {
|
||||
Some(TokenTree::Punct(punct)) if punct.as_char() == ',' => {}
|
||||
None => return Ok(res),
|
||||
other => {
|
||||
let span = other.map_or(Span::call_site(), |tt| tt.span());
|
||||
return Err(Error::new(span, "expected `,`".to_string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct Settings(Vec<Setting>);
|
||||
|
||||
impl Settings {
|
||||
pub(crate) fn is_set(&self, setting: Setting) -> bool {
|
||||
self.0.iter().any(|s| *s == setting)
|
||||
}
|
||||
|
||||
pub(crate) fn set(&mut self, setting: Setting) {
|
||||
self.0.push(setting)
|
||||
}
|
||||
}
|
@ -181,7 +181,8 @@ impl ToTokens for Diagnostic {
|
||||
Cow::Owned(message)
|
||||
};
|
||||
|
||||
let msg = syn::LitStr::new(&*message, end);
|
||||
let mut msg = proc_macro2::Literal::string(&message);
|
||||
msg.set_span(end);
|
||||
let group = quote_spanned!(end=> { #msg } );
|
||||
quote_spanned!(start=> compile_error!#group)
|
||||
}
|
||||
@ -216,6 +217,7 @@ impl SuggestionKind {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "syn-error")]
|
||||
impl From<syn::Error> for Diagnostic {
|
||||
fn from(err: syn::Error) -> Self {
|
||||
use proc_macro2::{Delimiter, TokenTree};
|
||||
|
@ -13,4 +13,11 @@ proc-macro = true
|
||||
proc-macro-error = { path = "../" }
|
||||
quote = "1"
|
||||
proc-macro2 = "1"
|
||||
syn = { version = "1", default-features = false, features = ["derive", "parsing", "proc-macro", "printing"] }
|
||||
|
||||
[dependencies.syn]
|
||||
version = "1"
|
||||
optional = true
|
||||
default-features = false
|
||||
|
||||
[features]
|
||||
syn-error = ["syn"]
|
||||
|
@ -13,6 +13,7 @@ use syn::{parse_macro_input, spanned::Spanned};
|
||||
|
||||
#[proc_macro]
|
||||
#[proc_macro_error]
|
||||
#[cfg(feature = "syn-error")]
|
||||
pub fn abort_from(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let span = input.into_iter().next().unwrap().span();
|
||||
abort!(
|
||||
@ -145,7 +146,7 @@ pub fn emit_notes(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
#[proc_macro]
|
||||
#[proc_macro_error]
|
||||
pub fn option_ext(_input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let none: Option<syn::Error> = None;
|
||||
let none: Option<Diagnostic> = None;
|
||||
none.expect_or_abort("Option::expect_or_abort() test");
|
||||
quote!().into()
|
||||
}
|
||||
@ -154,7 +155,7 @@ pub fn option_ext(_input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
#[proc_macro_error]
|
||||
pub fn result_unwrap_or_abort(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let span = input.into_iter().next().unwrap().span();
|
||||
let err = syn::Error::new(span.into(), "Result::unwrap_or_abort() test");
|
||||
let err = Diagnostic::spanned(span.into(), Level::Error, "Result::unwrap_or_abort() test");
|
||||
let res: Result<(), _> = Err(err);
|
||||
res.unwrap_or_abort();
|
||||
quote!().into()
|
||||
@ -164,7 +165,7 @@ pub fn result_unwrap_or_abort(input: proc_macro::TokenStream) -> proc_macro::Tok
|
||||
#[proc_macro_error]
|
||||
pub fn result_expect_or_abort(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let span = input.into_iter().next().unwrap().span();
|
||||
let err = syn::Error::new(span.into(), "Result::expect_or_abort() test");
|
||||
let err = Diagnostic::spanned(span.into(), Level::Error, "Result::expect_or_abort() test");
|
||||
let res: Result<(), _> = Err(err);
|
||||
res.expect_or_abort("BOOM");
|
||||
quote!().into()
|
||||
@ -228,11 +229,12 @@ pub fn multiple_tokens(
|
||||
input: proc_macro::TokenStream,
|
||||
) -> proc_macro::TokenStream {
|
||||
let input = proc_macro2::TokenStream::from(input);
|
||||
Err(syn::Error::new_spanned(input, "...")).unwrap_or_abort()
|
||||
abort!(input, "...");
|
||||
}
|
||||
|
||||
#[proc_macro]
|
||||
#[proc_macro_error]
|
||||
#[cfg(feature = "syn-error")]
|
||||
pub fn to_tokens_span(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let ty = parse_macro_input!(input as syn::Type);
|
||||
emit_error!(ty, "whole type");
|
||||
|
Loading…
Reference in New Issue
Block a user