summaryrefslogtreecommitdiff
path: root/vendor/thiserror-impl/src/expand.rs
diff options
context:
space:
mode:
authormo khan <mo@mokhan.ca>2025-07-02 18:36:06 -0600
committermo khan <mo@mokhan.ca>2025-07-02 18:36:06 -0600
commit8cdfa445d6629ffef4cb84967ff7017654045bc2 (patch)
tree22f0b0907c024c78d26a731e2e1f5219407d8102 /vendor/thiserror-impl/src/expand.rs
parent4351c74c7c5f97156bc94d3a8549b9940ac80e3f (diff)
chore: add vendor directory
Diffstat (limited to 'vendor/thiserror-impl/src/expand.rs')
-rw-r--r--vendor/thiserror-impl/src/expand.rs583
1 files changed, 583 insertions, 0 deletions
diff --git a/vendor/thiserror-impl/src/expand.rs b/vendor/thiserror-impl/src/expand.rs
new file mode 100644
index 00000000..c6939214
--- /dev/null
+++ b/vendor/thiserror-impl/src/expand.rs
@@ -0,0 +1,583 @@
+use crate::ast::{Enum, Field, Input, Struct};
+use crate::attr::Trait;
+use crate::fallback;
+use crate::generics::InferredBounds;
+use crate::unraw::MemberUnraw;
+use proc_macro2::{Ident, Span, TokenStream};
+use quote::{format_ident, quote, quote_spanned, ToTokens};
+use std::collections::BTreeSet as Set;
+use syn::{DeriveInput, GenericArgument, PathArguments, Result, Token, Type};
+
+pub fn derive(input: &DeriveInput) -> TokenStream {
+ match try_expand(input) {
+ Ok(expanded) => expanded,
+ // If there are invalid attributes in the input, expand to an Error impl
+ // anyway to minimize spurious secondary errors in other code that uses
+ // this type as an Error.
+ Err(error) => fallback::expand(input, error),
+ }
+}
+
+fn try_expand(input: &DeriveInput) -> Result<TokenStream> {
+ let input = Input::from_syn(input)?;
+ input.validate()?;
+ Ok(match input {
+ Input::Struct(input) => impl_struct(input),
+ Input::Enum(input) => impl_enum(input),
+ })
+}
+
+fn impl_struct(input: Struct) -> TokenStream {
+ let ty = call_site_ident(&input.ident);
+ let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
+ let mut error_inferred_bounds = InferredBounds::new();
+
+ let source_body = if let Some(transparent_attr) = &input.attrs.transparent {
+ let only_field = &input.fields[0];
+ if only_field.contains_generic {
+ error_inferred_bounds.insert(only_field.ty, quote!(::thiserror::__private::Error));
+ }
+ let member = &only_field.member;
+ Some(quote_spanned! {transparent_attr.span=>
+ ::thiserror::__private::Error::source(self.#member.as_dyn_error())
+ })
+ } else if let Some(source_field) = input.source_field() {
+ let source = &source_field.member;
+ if source_field.contains_generic {
+ let ty = unoptional_type(source_field.ty);
+ error_inferred_bounds.insert(ty, quote!(::thiserror::__private::Error + 'static));
+ }
+ let asref = if type_is_option(source_field.ty) {
+ Some(quote_spanned!(source.span()=> .as_ref()?))
+ } else {
+ None
+ };
+ let dyn_error = quote_spanned! {source_field.source_span()=>
+ self.#source #asref.as_dyn_error()
+ };
+ Some(quote! {
+ ::core::option::Option::Some(#dyn_error)
+ })
+ } else {
+ None
+ };
+ let source_method = source_body.map(|body| {
+ quote! {
+ fn source(&self) -> ::core::option::Option<&(dyn ::thiserror::__private::Error + 'static)> {
+ use ::thiserror::__private::AsDynError as _;
+ #body
+ }
+ }
+ });
+
+ let provide_method = input.backtrace_field().map(|backtrace_field| {
+ let request = quote!(request);
+ let backtrace = &backtrace_field.member;
+ let body = if let Some(source_field) = input.source_field() {
+ let source = &source_field.member;
+ let source_provide = if type_is_option(source_field.ty) {
+ quote_spanned! {source.span()=>
+ if let ::core::option::Option::Some(source) = &self.#source {
+ source.thiserror_provide(#request);
+ }
+ }
+ } else {
+ quote_spanned! {source.span()=>
+ self.#source.thiserror_provide(#request);
+ }
+ };
+ let self_provide = if source == backtrace {
+ None
+ } else if type_is_option(backtrace_field.ty) {
+ Some(quote! {
+ if let ::core::option::Option::Some(backtrace) = &self.#backtrace {
+ #request.provide_ref::<::thiserror::__private::Backtrace>(backtrace);
+ }
+ })
+ } else {
+ Some(quote! {
+ #request.provide_ref::<::thiserror::__private::Backtrace>(&self.#backtrace);
+ })
+ };
+ quote! {
+ use ::thiserror::__private::ThiserrorProvide as _;
+ #source_provide
+ #self_provide
+ }
+ } else if type_is_option(backtrace_field.ty) {
+ quote! {
+ if let ::core::option::Option::Some(backtrace) = &self.#backtrace {
+ #request.provide_ref::<::thiserror::__private::Backtrace>(backtrace);
+ }
+ }
+ } else {
+ quote! {
+ #request.provide_ref::<::thiserror::__private::Backtrace>(&self.#backtrace);
+ }
+ };
+ quote! {
+ fn provide<'_request>(&'_request self, #request: &mut ::core::error::Request<'_request>) {
+ #body
+ }
+ }
+ });
+
+ let mut display_implied_bounds = Set::new();
+ let display_body = if input.attrs.transparent.is_some() {
+ let only_field = &input.fields[0].member;
+ display_implied_bounds.insert((0, Trait::Display));
+ Some(quote! {
+ ::core::fmt::Display::fmt(&self.#only_field, __formatter)
+ })
+ } else if let Some(display) = &input.attrs.display {
+ display_implied_bounds.clone_from(&display.implied_bounds);
+ let use_as_display = use_as_display(display.has_bonus_display);
+ let pat = fields_pat(&input.fields);
+ Some(quote! {
+ #use_as_display
+ #[allow(unused_variables, deprecated)]
+ let Self #pat = self;
+ #display
+ })
+ } else {
+ None
+ };
+ let display_impl = display_body.map(|body| {
+ let mut display_inferred_bounds = InferredBounds::new();
+ for (field, bound) in display_implied_bounds {
+ let field = &input.fields[field];
+ if field.contains_generic {
+ display_inferred_bounds.insert(field.ty, bound);
+ }
+ }
+ let display_where_clause = display_inferred_bounds.augment_where_clause(input.generics);
+ quote! {
+ #[allow(unused_qualifications)]
+ #[automatically_derived]
+ impl #impl_generics ::core::fmt::Display for #ty #ty_generics #display_where_clause {
+ #[allow(clippy::used_underscore_binding)]
+ fn fmt(&self, __formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
+ #body
+ }
+ }
+ }
+ });
+
+ let from_impl = input.from_field().map(|from_field| {
+ let span = from_field.attrs.from.unwrap().span;
+ let backtrace_field = input.distinct_backtrace_field();
+ let from = unoptional_type(from_field.ty);
+ let source_var = Ident::new("source", span);
+ let body = from_initializer(from_field, backtrace_field, &source_var);
+ let from_function = quote! {
+ fn from(#source_var: #from) -> Self {
+ #ty #body
+ }
+ };
+ let from_impl = quote_spanned! {span=>
+ #[automatically_derived]
+ impl #impl_generics ::core::convert::From<#from> for #ty #ty_generics #where_clause {
+ #from_function
+ }
+ };
+ Some(quote! {
+ #[allow(
+ deprecated,
+ unused_qualifications,
+ clippy::elidable_lifetime_names,
+ clippy::needless_lifetimes,
+ )]
+ #from_impl
+ })
+ });
+
+ if input.generics.type_params().next().is_some() {
+ let self_token = <Token![Self]>::default();
+ error_inferred_bounds.insert(self_token, Trait::Debug);
+ error_inferred_bounds.insert(self_token, Trait::Display);
+ }
+ let error_where_clause = error_inferred_bounds.augment_where_clause(input.generics);
+
+ quote! {
+ #[allow(unused_qualifications)]
+ #[automatically_derived]
+ impl #impl_generics ::thiserror::__private::Error for #ty #ty_generics #error_where_clause {
+ #source_method
+ #provide_method
+ }
+ #display_impl
+ #from_impl
+ }
+}
+
+fn impl_enum(input: Enum) -> TokenStream {
+ let ty = call_site_ident(&input.ident);
+ let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
+ let mut error_inferred_bounds = InferredBounds::new();
+
+ let source_method = if input.has_source() {
+ let arms = input.variants.iter().map(|variant| {
+ let ident = &variant.ident;
+ if let Some(transparent_attr) = &variant.attrs.transparent {
+ let only_field = &variant.fields[0];
+ if only_field.contains_generic {
+ error_inferred_bounds.insert(only_field.ty, quote!(::thiserror::__private::Error));
+ }
+ let member = &only_field.member;
+ let source = quote_spanned! {transparent_attr.span=>
+ ::thiserror::__private::Error::source(transparent.as_dyn_error())
+ };
+ quote! {
+ #ty::#ident {#member: transparent} => #source,
+ }
+ } else if let Some(source_field) = variant.source_field() {
+ let source = &source_field.member;
+ if source_field.contains_generic {
+ let ty = unoptional_type(source_field.ty);
+ error_inferred_bounds.insert(ty, quote!(::thiserror::__private::Error + 'static));
+ }
+ let asref = if type_is_option(source_field.ty) {
+ Some(quote_spanned!(source.span()=> .as_ref()?))
+ } else {
+ None
+ };
+ let varsource = quote!(source);
+ let dyn_error = quote_spanned! {source_field.source_span()=>
+ #varsource #asref.as_dyn_error()
+ };
+ quote! {
+ #ty::#ident {#source: #varsource, ..} => ::core::option::Option::Some(#dyn_error),
+ }
+ } else {
+ quote! {
+ #ty::#ident {..} => ::core::option::Option::None,
+ }
+ }
+ });
+ Some(quote! {
+ fn source(&self) -> ::core::option::Option<&(dyn ::thiserror::__private::Error + 'static)> {
+ use ::thiserror::__private::AsDynError as _;
+ #[allow(deprecated)]
+ match self {
+ #(#arms)*
+ }
+ }
+ })
+ } else {
+ None
+ };
+
+ let provide_method = if input.has_backtrace() {
+ let request = quote!(request);
+ let arms = input.variants.iter().map(|variant| {
+ let ident = &variant.ident;
+ match (variant.backtrace_field(), variant.source_field()) {
+ (Some(backtrace_field), Some(source_field))
+ if backtrace_field.attrs.backtrace.is_none() =>
+ {
+ let backtrace = &backtrace_field.member;
+ let source = &source_field.member;
+ let varsource = quote!(source);
+ let source_provide = if type_is_option(source_field.ty) {
+ quote_spanned! {source.span()=>
+ if let ::core::option::Option::Some(source) = #varsource {
+ source.thiserror_provide(#request);
+ }
+ }
+ } else {
+ quote_spanned! {source.span()=>
+ #varsource.thiserror_provide(#request);
+ }
+ };
+ let self_provide = if type_is_option(backtrace_field.ty) {
+ quote! {
+ if let ::core::option::Option::Some(backtrace) = backtrace {
+ #request.provide_ref::<::thiserror::__private::Backtrace>(backtrace);
+ }
+ }
+ } else {
+ quote! {
+ #request.provide_ref::<::thiserror::__private::Backtrace>(backtrace);
+ }
+ };
+ quote! {
+ #ty::#ident {
+ #backtrace: backtrace,
+ #source: #varsource,
+ ..
+ } => {
+ use ::thiserror::__private::ThiserrorProvide as _;
+ #source_provide
+ #self_provide
+ }
+ }
+ }
+ (Some(backtrace_field), Some(source_field))
+ if backtrace_field.member == source_field.member =>
+ {
+ let backtrace = &backtrace_field.member;
+ let varsource = quote!(source);
+ let source_provide = if type_is_option(source_field.ty) {
+ quote_spanned! {backtrace.span()=>
+ if let ::core::option::Option::Some(source) = #varsource {
+ source.thiserror_provide(#request);
+ }
+ }
+ } else {
+ quote_spanned! {backtrace.span()=>
+ #varsource.thiserror_provide(#request);
+ }
+ };
+ quote! {
+ #ty::#ident {#backtrace: #varsource, ..} => {
+ use ::thiserror::__private::ThiserrorProvide as _;
+ #source_provide
+ }
+ }
+ }
+ (Some(backtrace_field), _) => {
+ let backtrace = &backtrace_field.member;
+ let body = if type_is_option(backtrace_field.ty) {
+ quote! {
+ if let ::core::option::Option::Some(backtrace) = backtrace {
+ #request.provide_ref::<::thiserror::__private::Backtrace>(backtrace);
+ }
+ }
+ } else {
+ quote! {
+ #request.provide_ref::<::thiserror::__private::Backtrace>(backtrace);
+ }
+ };
+ quote! {
+ #ty::#ident {#backtrace: backtrace, ..} => {
+ #body
+ }
+ }
+ }
+ (None, _) => quote! {
+ #ty::#ident {..} => {}
+ },
+ }
+ });
+ Some(quote! {
+ fn provide<'_request>(&'_request self, #request: &mut ::core::error::Request<'_request>) {
+ #[allow(deprecated)]
+ match self {
+ #(#arms)*
+ }
+ }
+ })
+ } else {
+ None
+ };
+
+ let display_impl = if input.has_display() {
+ let mut display_inferred_bounds = InferredBounds::new();
+ let has_bonus_display = input.variants.iter().any(|v| {
+ v.attrs
+ .display
+ .as_ref()
+ .map_or(false, |display| display.has_bonus_display)
+ });
+ let use_as_display = use_as_display(has_bonus_display);
+ let void_deref = if input.variants.is_empty() {
+ Some(quote!(*))
+ } else {
+ None
+ };
+ let arms = input.variants.iter().map(|variant| {
+ let mut display_implied_bounds = Set::new();
+ let display = if let Some(display) = &variant.attrs.display {
+ display_implied_bounds.clone_from(&display.implied_bounds);
+ display.to_token_stream()
+ } else if let Some(fmt) = &variant.attrs.fmt {
+ let fmt_path = &fmt.path;
+ let vars = variant.fields.iter().map(|field| match &field.member {
+ MemberUnraw::Named(ident) => ident.to_local(),
+ MemberUnraw::Unnamed(index) => format_ident!("_{}", index),
+ });
+ quote!(#fmt_path(#(#vars,)* __formatter))
+ } else {
+ let only_field = match &variant.fields[0].member {
+ MemberUnraw::Named(ident) => ident.to_local(),
+ MemberUnraw::Unnamed(index) => format_ident!("_{}", index),
+ };
+ display_implied_bounds.insert((0, Trait::Display));
+ quote!(::core::fmt::Display::fmt(#only_field, __formatter))
+ };
+ for (field, bound) in display_implied_bounds {
+ let field = &variant.fields[field];
+ if field.contains_generic {
+ display_inferred_bounds.insert(field.ty, bound);
+ }
+ }
+ let ident = &variant.ident;
+ let pat = fields_pat(&variant.fields);
+ quote! {
+ #ty::#ident #pat => #display
+ }
+ });
+ let arms = arms.collect::<Vec<_>>();
+ let display_where_clause = display_inferred_bounds.augment_where_clause(input.generics);
+ Some(quote! {
+ #[allow(unused_qualifications)]
+ #[automatically_derived]
+ impl #impl_generics ::core::fmt::Display for #ty #ty_generics #display_where_clause {
+ fn fmt(&self, __formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
+ #use_as_display
+ #[allow(unused_variables, deprecated, clippy::used_underscore_binding)]
+ match #void_deref self {
+ #(#arms,)*
+ }
+ }
+ }
+ })
+ } else {
+ None
+ };
+
+ let from_impls = input.variants.iter().filter_map(|variant| {
+ let from_field = variant.from_field()?;
+ let span = from_field.attrs.from.unwrap().span;
+ let backtrace_field = variant.distinct_backtrace_field();
+ let variant = &variant.ident;
+ let from = unoptional_type(from_field.ty);
+ let source_var = Ident::new("source", span);
+ let body = from_initializer(from_field, backtrace_field, &source_var);
+ let from_function = quote! {
+ fn from(#source_var: #from) -> Self {
+ #ty::#variant #body
+ }
+ };
+ let from_impl = quote_spanned! {span=>
+ #[automatically_derived]
+ impl #impl_generics ::core::convert::From<#from> for #ty #ty_generics #where_clause {
+ #from_function
+ }
+ };
+ Some(quote! {
+ #[allow(
+ deprecated,
+ unused_qualifications,
+ clippy::elidable_lifetime_names,
+ clippy::needless_lifetimes,
+ )]
+ #from_impl
+ })
+ });
+
+ if input.generics.type_params().next().is_some() {
+ let self_token = <Token![Self]>::default();
+ error_inferred_bounds.insert(self_token, Trait::Debug);
+ error_inferred_bounds.insert(self_token, Trait::Display);
+ }
+ let error_where_clause = error_inferred_bounds.augment_where_clause(input.generics);
+
+ quote! {
+ #[allow(unused_qualifications)]
+ #[automatically_derived]
+ impl #impl_generics ::thiserror::__private::Error for #ty #ty_generics #error_where_clause {
+ #source_method
+ #provide_method
+ }
+ #display_impl
+ #(#from_impls)*
+ }
+}
+
+// Create an ident with which we can expand `impl Trait for #ident {}` on a
+// deprecated type without triggering deprecation warning on the generated impl.
+pub(crate) fn call_site_ident(ident: &Ident) -> Ident {
+ let mut ident = ident.clone();
+ ident.set_span(ident.span().resolved_at(Span::call_site()));
+ ident
+}
+
+fn fields_pat(fields: &[Field]) -> TokenStream {
+ let mut members = fields.iter().map(|field| &field.member).peekable();
+ match members.peek() {
+ Some(MemberUnraw::Named(_)) => quote!({ #(#members),* }),
+ Some(MemberUnraw::Unnamed(_)) => {
+ let vars = members.map(|member| match member {
+ MemberUnraw::Unnamed(index) => format_ident!("_{}", index),
+ MemberUnraw::Named(_) => unreachable!(),
+ });
+ quote!((#(#vars),*))
+ }
+ None => quote!({}),
+ }
+}
+
+fn use_as_display(needs_as_display: bool) -> Option<TokenStream> {
+ if needs_as_display {
+ Some(quote! {
+ use ::thiserror::__private::AsDisplay as _;
+ })
+ } else {
+ None
+ }
+}
+
+fn from_initializer(
+ from_field: &Field,
+ backtrace_field: Option<&Field>,
+ source_var: &Ident,
+) -> TokenStream {
+ let from_member = &from_field.member;
+ let some_source = if type_is_option(from_field.ty) {
+ quote!(::core::option::Option::Some(#source_var))
+ } else {
+ quote!(#source_var)
+ };
+ let backtrace = backtrace_field.map(|backtrace_field| {
+ let backtrace_member = &backtrace_field.member;
+ if type_is_option(backtrace_field.ty) {
+ quote! {
+ #backtrace_member: ::core::option::Option::Some(::thiserror::__private::Backtrace::capture()),
+ }
+ } else {
+ quote! {
+ #backtrace_member: ::core::convert::From::from(::thiserror::__private::Backtrace::capture()),
+ }
+ }
+ });
+ quote!({
+ #from_member: #some_source,
+ #backtrace
+ })
+}
+
+fn type_is_option(ty: &Type) -> bool {
+ type_parameter_of_option(ty).is_some()
+}
+
+fn unoptional_type(ty: &Type) -> TokenStream {
+ let unoptional = type_parameter_of_option(ty).unwrap_or(ty);
+ quote!(#unoptional)
+}
+
+fn type_parameter_of_option(ty: &Type) -> Option<&Type> {
+ let path = match ty {
+ Type::Path(ty) => &ty.path,
+ _ => return None,
+ };
+
+ let last = path.segments.last().unwrap();
+ if last.ident != "Option" {
+ return None;
+ }
+
+ let bracketed = match &last.arguments {
+ PathArguments::AngleBracketed(bracketed) => bracketed,
+ _ => return None,
+ };
+
+ if bracketed.args.len() != 1 {
+ return None;
+ }
+
+ match &bracketed.args[0] {
+ GenericArgument::Type(arg) => Some(arg),
+ _ => None,
+ }
+}