summaryrefslogtreecommitdiffstats
path: root/third_party/rust/proc-macro-error-attr/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/rust/proc-macro-error-attr/src
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/proc-macro-error-attr/src')
-rw-r--r--third_party/rust/proc-macro-error-attr/src/lib.rs121
-rw-r--r--third_party/rust/proc-macro-error-attr/src/parse.rs89
-rw-r--r--third_party/rust/proc-macro-error-attr/src/settings.rs72
3 files changed, 282 insertions, 0 deletions
diff --git a/third_party/rust/proc-macro-error-attr/src/lib.rs b/third_party/rust/proc-macro-error-attr/src/lib.rs
new file mode 100644
index 0000000000..ac0ac21a26
--- /dev/null
+++ b/third_party/rust/proc-macro-error-attr/src/lib.rs
@@ -0,0 +1,121 @@
+//! This is `#[proc_macro_error]` attribute to be used with
+//! [`proc-macro-error`](https://docs.rs/proc-macro-error/). There you go.
+
+extern crate proc_macro;
+
+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::{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 {
+ 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())
+}
+
+#[cfg(not(always_assert_unwind))]
+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_error::entry_point(#closure, #is_proc_macro_hack) )
+}
+
+// FIXME:
+// proc_macro::TokenStream does not implement UnwindSafe until 1.37.0.
+// 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: 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) )
+}
+
+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")
+ })
+}
diff --git a/third_party/rust/proc-macro-error-attr/src/parse.rs b/third_party/rust/proc-macro-error-attr/src/parse.rs
new file mode 100644
index 0000000000..6f4663f80e
--- /dev/null
+++ b/third_party/rust/proc-macro-error-attr/src/parse.rs
@@ -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(), |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(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);
+ }
+}
diff --git a/third_party/rust/proc-macro-error-attr/src/settings.rs b/third_party/rust/proc-macro-error-attr/src/settings.rs
new file mode 100644
index 0000000000..0b7ec766f6
--- /dev/null
+++ b/third_party/rust/proc-macro-error-attr/src/settings.rs
@@ -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(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.iter().any(|s| *s == setting)
+ }
+
+ pub(crate) fn set(&mut self, setting: Setting) {
+ self.0.push(setting)
+ }
+}