summaryrefslogtreecommitdiffstats
path: root/third_party/rust/displaydoc/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/displaydoc/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/displaydoc/src')
-rw-r--r--third_party/rust/displaydoc/src/attr.rs134
-rw-r--r--third_party/rust/displaydoc/src/expand.rs145
-rw-r--r--third_party/rust/displaydoc/src/fmt.rs159
-rw-r--r--third_party/rust/displaydoc/src/lib.rs122
4 files changed, 560 insertions, 0 deletions
diff --git a/third_party/rust/displaydoc/src/attr.rs b/third_party/rust/displaydoc/src/attr.rs
new file mode 100644
index 0000000000..e14593556c
--- /dev/null
+++ b/third_party/rust/displaydoc/src/attr.rs
@@ -0,0 +1,134 @@
+use proc_macro2::TokenStream;
+use quote::{quote, ToTokens};
+use syn::{Attribute, LitStr, Meta, Result};
+
+#[derive(Clone)]
+pub(crate) struct Display {
+ pub(crate) fmt: LitStr,
+ pub(crate) args: TokenStream,
+}
+
+pub(crate) struct VariantDisplay {
+ pub(crate) r#enum: Option<Display>,
+ pub(crate) variant: Display,
+}
+
+impl ToTokens for Display {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ let fmt = &self.fmt;
+ let args = &self.args;
+ tokens.extend(quote! {
+ write!(formatter, #fmt #args)
+ });
+ }
+}
+
+impl ToTokens for VariantDisplay {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ if let Some(ref r#enum) = self.r#enum {
+ r#enum.to_tokens(tokens);
+ tokens.extend(quote! { ?; write!(formatter, ": ")?; });
+ }
+ self.variant.to_tokens(tokens);
+ }
+}
+
+pub(crate) struct AttrsHelper {
+ ignore_extra_doc_attributes: bool,
+ prefix_enum_doc_attributes: bool,
+}
+
+impl AttrsHelper {
+ pub(crate) fn new(attrs: &[Attribute]) -> Self {
+ let ignore_extra_doc_attributes = attrs
+ .iter()
+ .any(|attr| attr.path.is_ident("ignore_extra_doc_attributes"));
+ let prefix_enum_doc_attributes = attrs
+ .iter()
+ .any(|attr| attr.path.is_ident("prefix_enum_doc_attributes"));
+
+ Self {
+ ignore_extra_doc_attributes,
+ prefix_enum_doc_attributes,
+ }
+ }
+
+ pub(crate) fn display(&self, attrs: &[Attribute]) -> Result<Option<Display>> {
+ let displaydoc_attr = attrs.iter().find(|attr| attr.path.is_ident("displaydoc"));
+
+ if let Some(displaydoc_attr) = displaydoc_attr {
+ let lit = displaydoc_attr
+ .parse_args()
+ .expect("#[displaydoc(\"foo\")] must contain string arguments");
+ let mut display = Display {
+ fmt: lit,
+ args: TokenStream::new(),
+ };
+
+ display.expand_shorthand();
+ return Ok(Some(display));
+ }
+
+ let num_doc_attrs = attrs
+ .iter()
+ .filter(|attr| attr.path.is_ident("doc"))
+ .count();
+
+ if !self.ignore_extra_doc_attributes && num_doc_attrs > 1 {
+ panic!("Multi-line comments are disabled by default by displaydoc. Please consider using block doc comments (/** */) or adding the #[ignore_extra_doc_attributes] attribute to your type next to the derive.");
+ }
+
+ for attr in attrs {
+ if attr.path.is_ident("doc") {
+ let meta = attr.parse_meta()?;
+ let lit = match meta {
+ Meta::NameValue(syn::MetaNameValue {
+ lit: syn::Lit::Str(lit),
+ ..
+ }) => lit,
+ _ => unimplemented!(),
+ };
+
+ // Make an attempt and cleaning up multiline doc comments
+ let doc_str = lit
+ .value()
+ .lines()
+ .map(|line| line.trim().trim_start_matches('*').trim())
+ .collect::<Vec<&str>>()
+ .join("\n");
+
+ let lit = LitStr::new(doc_str.trim(), lit.span());
+
+ let mut display = Display {
+ fmt: lit,
+ args: TokenStream::new(),
+ };
+
+ display.expand_shorthand();
+ return Ok(Some(display));
+ }
+ }
+
+ Ok(None)
+ }
+
+ pub(crate) fn display_with_input(
+ &self,
+ r#enum: &[Attribute],
+ variant: &[Attribute],
+ ) -> Result<Option<VariantDisplay>> {
+ let r#enum = if self.prefix_enum_doc_attributes {
+ let result = self
+ .display(r#enum)?
+ .expect("Missing doc comment on enum with #[prefix_enum_doc_attributes]. Please remove the attribute or add a doc comment to the enum itself.");
+
+ Some(result)
+ } else {
+ None
+ };
+
+ Ok(self
+ .display(variant)?
+ .map(|variant| VariantDisplay { r#enum, variant }))
+ }
+}
diff --git a/third_party/rust/displaydoc/src/expand.rs b/third_party/rust/displaydoc/src/expand.rs
new file mode 100644
index 0000000000..6904cbc8c7
--- /dev/null
+++ b/third_party/rust/displaydoc/src/expand.rs
@@ -0,0 +1,145 @@
+use super::attr::AttrsHelper;
+use proc_macro2::TokenStream;
+use quote::{format_ident, quote};
+use syn::{Data, DataEnum, DataStruct, DeriveInput, Error, Fields, Result};
+
+pub(crate) fn derive(input: &DeriveInput) -> Result<TokenStream> {
+ let impls = match &input.data {
+ Data::Struct(data) => impl_struct(input, data),
+ Data::Enum(data) => impl_enum(input, data),
+ Data::Union(_) => Err(Error::new_spanned(input, "Unions are not supported")),
+ }?;
+
+ let helpers = specialization();
+ let dummy_const = format_ident!("_DERIVE_Display_FOR_{}", input.ident);
+ Ok(quote! {
+ #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
+ const #dummy_const: () = {
+ #helpers
+ #impls
+ };
+ })
+}
+
+#[cfg(feature = "std")]
+fn specialization() -> TokenStream {
+ quote! {
+ trait DisplayToDisplayDoc {
+ fn __displaydoc_display(&self) -> Self;
+ }
+
+ impl<T: core::fmt::Display> DisplayToDisplayDoc for &T {
+ fn __displaydoc_display(&self) -> Self {
+ self
+ }
+ }
+
+ // If the `std` feature gets enabled we want to ensure that any crate
+ // using displaydoc can still reference the std crate, which is already
+ // being compiled in by whoever enabled the `std` feature in
+ // `displaydoc`, even if the crates using displaydoc are no_std.
+ extern crate std;
+
+ trait PathToDisplayDoc {
+ fn __displaydoc_display(&self) -> std::path::Display<'_>;
+ }
+
+ impl PathToDisplayDoc for std::path::Path {
+ fn __displaydoc_display(&self) -> std::path::Display<'_> {
+ self.display()
+ }
+ }
+
+ impl PathToDisplayDoc for std::path::PathBuf {
+ fn __displaydoc_display(&self) -> std::path::Display<'_> {
+ self.display()
+ }
+ }
+ }
+}
+
+#[cfg(not(feature = "std"))]
+fn specialization() -> TokenStream {
+ quote! {}
+}
+
+fn impl_struct(input: &DeriveInput, data: &DataStruct) -> Result<TokenStream> {
+ let ty = &input.ident;
+ let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
+
+ let helper = AttrsHelper::new(&input.attrs);
+
+ let display = helper.display(&input.attrs)?.map(|display| {
+ let pat = match &data.fields {
+ Fields::Named(fields) => {
+ let var = fields.named.iter().map(|field| &field.ident);
+ quote!(Self { #(#var),* })
+ }
+ Fields::Unnamed(fields) => {
+ let var = (0..fields.unnamed.len()).map(|i| format_ident!("_{}", i));
+ quote!(Self(#(#var),*))
+ }
+ Fields::Unit => quote!(_),
+ };
+ quote! {
+ impl #impl_generics core::fmt::Display for #ty #ty_generics #where_clause {
+ fn fmt(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
+ #[allow(unused_variables)]
+ let #pat = self;
+ #display
+ }
+ }
+ }
+ });
+
+ Ok(quote! { #display })
+}
+
+fn impl_enum(input: &DeriveInput, data: &DataEnum) -> Result<TokenStream> {
+ let ty = &input.ident;
+ let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
+
+ let helper = AttrsHelper::new(&input.attrs);
+
+ let displays = data
+ .variants
+ .iter()
+ .map(|variant| helper.display_with_input(&input.attrs, &variant.attrs))
+ .collect::<Result<Vec<_>>>()?;
+
+ if displays.iter().any(Option::is_some) {
+ let arms = data
+ .variants
+ .iter()
+ .zip(displays)
+ .map(|(variant, display)| {
+ let display =
+ display.ok_or_else(|| Error::new_spanned(variant, "missing doc comment"))?;
+ let ident = &variant.ident;
+ Ok(match &variant.fields {
+ Fields::Named(fields) => {
+ let var = fields.named.iter().map(|field| &field.ident);
+ quote!(#ty::#ident { #(#var),* } => { #display })
+ }
+ Fields::Unnamed(fields) => {
+ let var = (0..fields.unnamed.len()).map(|i| format_ident!("_{}", i));
+ quote!(#ty::#ident(#(#var),*) => { #display })
+ }
+ Fields::Unit => quote!(#ty::#ident => { #display }),
+ })
+ })
+ .collect::<Result<Vec<_>>>()?;
+ Ok(quote! {
+ impl #impl_generics core::fmt::Display for #ty #ty_generics #where_clause {
+ fn fmt(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
+ #[allow(unused_variables)]
+ match self {
+ #(#arms,)*
+ }
+ }
+ }
+ })
+ } else {
+ Err(Error::new_spanned(input, "Missing doc comments"))
+ }
+}
diff --git a/third_party/rust/displaydoc/src/fmt.rs b/third_party/rust/displaydoc/src/fmt.rs
new file mode 100644
index 0000000000..5848557ea4
--- /dev/null
+++ b/third_party/rust/displaydoc/src/fmt.rs
@@ -0,0 +1,159 @@
+use crate::attr::Display;
+use proc_macro2::TokenStream;
+use quote::quote_spanned;
+use syn::{Ident, LitStr};
+
+macro_rules! peek_next {
+ ($read:ident) => {
+ match $read.chars().next() {
+ Some(next) => next,
+ None => return,
+ }
+ };
+}
+
+impl Display {
+ // Transform `"error {var}"` to `"error {}", var`.
+ pub(crate) fn expand_shorthand(&mut self) {
+ let span = self.fmt.span();
+ let fmt = self.fmt.value();
+ let mut read = fmt.as_str();
+ let mut out = String::new();
+ let mut args = TokenStream::new();
+
+ while let Some(brace) = read.find('{') {
+ out += &read[..=brace];
+ read = &read[brace + 1..];
+
+ // skip cases where we find a {{
+ if read.starts_with('{') {
+ out.push('{');
+ read = &read[1..];
+ continue;
+ }
+
+ let next = peek_next!(read);
+
+ let var = match next {
+ '0'..='9' => take_int(&mut read),
+ 'a'..='z' | 'A'..='Z' | '_' => take_ident(&mut read),
+ _ => return,
+ };
+
+ let ident = Ident::new(&var, span);
+
+ let next = peek_next!(read);
+
+ let arg = if cfg!(feature = "std") && next == '}' {
+ quote_spanned!(span=> , #ident.__displaydoc_display())
+ } else {
+ quote_spanned!(span=> , #ident)
+ };
+
+ args.extend(arg);
+ }
+
+ out += read;
+ self.fmt = LitStr::new(&out, self.fmt.span());
+ self.args = args;
+ }
+}
+
+fn take_int(read: &mut &str) -> String {
+ let mut int = String::new();
+ int.push('_');
+ for (i, ch) in read.char_indices() {
+ match ch {
+ '0'..='9' => int.push(ch),
+ _ => {
+ *read = &read[i..];
+ break;
+ }
+ }
+ }
+ int
+}
+
+fn take_ident(read: &mut &str) -> String {
+ let mut ident = String::new();
+ for (i, ch) in read.char_indices() {
+ match ch {
+ 'a'..='z' | 'A'..='Z' | '0'..='9' | '_' => ident.push(ch),
+ _ => {
+ *read = &read[i..];
+ break;
+ }
+ }
+ }
+ ident
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use pretty_assertions::assert_eq;
+ use proc_macro2::Span;
+
+ fn assert(input: &str, fmt: &str, args: &str) {
+ let mut display = Display {
+ fmt: LitStr::new(input, Span::call_site()),
+ args: TokenStream::new(),
+ };
+ display.expand_shorthand();
+ assert_eq!(fmt, display.fmt.value());
+ assert_eq!(args, display.args.to_string());
+ }
+
+ #[test]
+ fn test_expand() {
+ assert("fn main() {{ }}", "fn main() {{ }}", "");
+ }
+
+ #[test]
+ #[cfg_attr(not(feature = "std"), ignore)]
+ fn test_std_expand() {
+ assert(
+ "{v} {v:?} {0} {0:?}",
+ "{} {:?} {} {:?}",
+ ", v . __displaydoc_display () , v , _0 . __displaydoc_display () , _0",
+ );
+ assert("error {var}", "error {}", ", var . __displaydoc_display ()");
+
+ assert(
+ "error {var1}",
+ "error {}",
+ ", var1 . __displaydoc_display ()",
+ );
+
+ assert(
+ "error {var1var}",
+ "error {}",
+ ", var1var . __displaydoc_display ()",
+ );
+
+ assert(
+ "The path {0}",
+ "The path {}",
+ ", _0 . __displaydoc_display ()",
+ );
+ assert("The path {0:?}", "The path {:?}", ", _0");
+ }
+
+ #[test]
+ #[cfg_attr(feature = "std", ignore)]
+ fn test_nostd_expand() {
+ assert(
+ "{v} {v:?} {0} {0:?}",
+ "{} {:?} {} {:?}",
+ ", v , v , _0 , _0",
+ );
+ assert("error {var}", "error {}", ", var");
+
+ assert("The path {0}", "The path {}", ", _0");
+ assert("The path {0:?}", "The path {:?}", ", _0");
+
+ assert("error {var1}", "error {}", ", var1");
+
+ assert("error {var1var}", "error {}", ", var1var");
+ }
+}
diff --git a/third_party/rust/displaydoc/src/lib.rs b/third_party/rust/displaydoc/src/lib.rs
new file mode 100644
index 0000000000..083c613845
--- /dev/null
+++ b/third_party/rust/displaydoc/src/lib.rs
@@ -0,0 +1,122 @@
+//! This library provides a convenient derive macro for the standard library's
+//! [`core::fmt::Display`] trait.
+//!
+//! [`core::fmt::Display`]: https://doc.rust-lang.org/std/fmt/trait.Display.html
+//!
+//! ```toml
+//! [dependencies]
+//! displaydoc = "0.2"
+//! ```
+//!
+//! *Compiler support: requires rustc 1.31+*
+//!
+//! <br>
+//!
+//! ## Example
+//!
+//! ```rust
+//! use std::io;
+//! use displaydoc::Display;
+//! use thiserror::Error;
+//!
+//! #[derive(Display, Error, Debug)]
+//! pub enum DataStoreError {
+//! /// data store disconnected
+//! Disconnect(#[source] io::Error),
+//! /// the data for key `{0}` is not available
+//! Redaction(String),
+//! /// invalid header (expected {expected:?}, found {found:?})
+//! InvalidHeader {
+//! expected: String,
+//! found: String,
+//! },
+//! /// unknown data store error
+//! Unknown,
+//! }
+//! ```
+//!
+//! <br>
+//!
+//! ## Details
+//!
+//! - A `Display` impl is generated for your type if you provide doc comment
+//! messages on the struct or each variant of your enum, as shown above in the
+//! example.
+//!
+//! The messages support a shorthand for interpolating fields from the error.
+//!
+//! - `/// {var}` ⟶ `write!("{}", self.var)`
+//! - `/// {0}` ⟶ `write!("{}", self.0)`
+//! - `/// {var:?}` ⟶ `write!("{:?}", self.var)`
+//! - `/// {0:?}` ⟶ `write!("{:?}", self.0)`
+//!
+//! - Two optional attributes can be added to your types next to the derive:
+//!
+//! - `#[ignore_extra_doc_attributes]` makes the macro ignore any doc
+//! comment attributes (or `///` lines) after the first. Multi-line
+//! comments using `///` are otherwise treated as an error, so use this
+//! attribute or consider switching to block doc comments (`/** */`).
+//!
+//! - `#[prefix_enum_doc_attributes]` combines the doc comment message on
+//! your enum itself with the messages for each variant, in the format
+//! “enum: variant”. When added to an enum, the doc comment on the enum
+//! becomes mandatory. When added to any other type, it has no effect.
+//!
+//! - In case you want to have an independent doc comment, the
+//! `#[displaydoc("...")` atrribute may be used on the variant or struct to
+//! override it.
+//!
+//! <br>
+//!
+//! ## FAQ
+//!
+//! 1. **Is this crate `no_std` compatible?**
+//! * Yes! This crate implements the `core::fmt::Display` trait not the `std::fmt::Display` trait so it should work in `std` and `no_std` environments. Just add `default-features = false`.
+//!
+//! 2. **Does this crate work with `Path` and `PathBuf` via the `Display` trait?**
+//! * Yuuup. This crate uses @dtolnay's [autoref specialization technique](https://github.com/dtolnay/case-studies/blob/master/autoref-specialization/README.md) to add a special trait for types to get the display impl, it then specializes for `Path` and `PathBuf` and when either of these types are found it calls `self.display()` to get a `std::path::Display<'_>` type which can be used with the Display format specifier!
+#![doc(html_root_url = "https://docs.rs/displaydoc/0.2.3")]
+#![cfg_attr(docsrs, feature(doc_cfg))]
+#![warn(
+ rust_2018_idioms,
+ unreachable_pub,
+ bad_style,
+ const_err,
+ dead_code,
+ improper_ctypes,
+ non_shorthand_field_patterns,
+ no_mangle_generic_items,
+ overflowing_literals,
+ path_statements,
+ patterns_in_fns_without_body,
+ private_in_public,
+ unconditional_recursion,
+ unused,
+ unused_allocation,
+ unused_comparisons,
+ unused_parens,
+ while_true
+)]
+#![allow(clippy::try_err)]
+
+#[allow(unused_extern_crates)]
+extern crate proc_macro;
+
+mod attr;
+mod expand;
+mod fmt;
+
+use proc_macro::TokenStream;
+use syn::{parse_macro_input, DeriveInput};
+
+/// Derive macro for implementing `Display` via doc comment attributes
+#[proc_macro_derive(
+ Display,
+ attributes(ignore_extra_doc_attributes, prefix_enum_doc_attributes, displaydoc)
+)]
+pub fn derive_error(input: TokenStream) -> TokenStream {
+ let input = parse_macro_input!(input as DeriveInput);
+ expand::derive(&input)
+ .unwrap_or_else(|err| err.to_compile_error())
+ .into()
+}