Vendor dependencies

This commit is contained in:
2026-08-01 16:11:49 +03:00
parent 7f139a0241
commit 6b5e7f0f8b
29706 changed files with 9575646 additions and 0 deletions
+111
View File
@@ -0,0 +1,111 @@
//! This is `#[proc_macro_error]` attribute to be used with
//! [`proc-macro-error`](https://docs.rs/proc-macro-error3/). There you go.
use crate::parse::parse_input;
use crate::parse::Attribute;
use proc_macro::TokenStream;
use proc_macro2::{Literal, Span, TokenStream as TokenStream2, TokenTree};
use quote::{quote, quote_spanned};
use crate::settings::{
parse_settings,
Setting::{AllowNotMacro, AssertUnwindSafe, ProcMacroHack},
Settings,
};
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 {
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);
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(&attrs) {
settings.set(ProcMacroHack);
}
if settings.is_set(ProcMacroHack) {
settings.set(AllowNotMacro);
}
if !(settings.is_set(AllowNotMacro) || is_proc_macro) {
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 body = gen_body(&body, &settings);
let res = quote! {
#(#attrs)*
#(#signature)*
{ #body }
};
Ok(res.into())
}
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 ))
} else {
quote!(|| #block)
};
quote!( ::proc_macro_error3::entry_point(#closure, #is_proc_macro_hack) )
}
fn detect_proc_macro_hack(attrs: &[Attribute]) -> bool {
attrs
.iter()
.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")
})
}
@@ -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(ref punct)) if punct.as_char() == '#' => input.next().unwrap(),
_ => return Ok(None),
};
let group = match input.peek() {
Some(TokenTree::Group(ref group)) if group.delimiter() == Delimiter::Bracket => {
let res = group.clone();
input.next();
res
}
other => {
let span = other.map_or(Span::call_site(), TokenTree::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(ref 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);
}
}
@@ -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, Clone, Copy)]
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(ref 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.contains(&setting)
}
pub(crate) fn set(&mut self, setting: Setting) {
self.0.push(setting);
}
}