thiserror-impl-1.0.10/Cargo.toml.orig010064400017500001750000000006331361411027200156600ustar0000000000000000[package] name = "thiserror-impl" version = "1.0.10" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" description = "Implementation detail of the `thiserror` crate" repository = "https://github.com/dtolnay/thiserror" [lib] proc-macro = true [badges] travis-ci = { repository = "dtolnay/thiserror" } [dependencies] proc-macro2 = "1.0" quote = "1.0" syn = "1.0.11" thiserror-impl-1.0.10/Cargo.toml0000644000000017301361411030300121600ustar00# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] edition = "2018" name = "thiserror-impl" version = "1.0.10" authors = ["David Tolnay "] description = "Implementation detail of the `thiserror` crate" license = "MIT OR Apache-2.0" repository = "https://github.com/dtolnay/thiserror" [lib] proc-macro = true [dependencies.proc-macro2] version = "1.0" [dependencies.quote] version = "1.0" [dependencies.syn] version = "1.0.11" [badges.travis-ci] repository = "dtolnay/thiserror" thiserror-impl-1.0.10/src/ast.rs010064400017500001750000000067451361255111400147310ustar0000000000000000use crate::attr::{self, Attrs}; use syn::{ Data, DataEnum, DataStruct, DeriveInput, Error, Fields, Generics, Ident, Index, Member, Result, Type, }; pub enum Input<'a> { Struct(Struct<'a>), Enum(Enum<'a>), } pub struct Struct<'a> { pub attrs: Attrs<'a>, pub ident: Ident, pub generics: &'a Generics, pub fields: Vec>, } pub struct Enum<'a> { pub attrs: Attrs<'a>, pub ident: Ident, pub generics: &'a Generics, pub variants: Vec>, } pub struct Variant<'a> { pub original: &'a syn::Variant, pub attrs: Attrs<'a>, pub ident: Ident, pub fields: Vec>, } pub struct Field<'a> { pub original: &'a syn::Field, pub attrs: Attrs<'a>, pub member: Member, pub ty: &'a Type, } impl<'a> Input<'a> { pub fn from_syn(node: &'a DeriveInput) -> Result { match &node.data { Data::Struct(data) => Struct::from_syn(node, data).map(Input::Struct), Data::Enum(data) => Enum::from_syn(node, data).map(Input::Enum), Data::Union(_) => Err(Error::new_spanned( node, "union as errors are not supported", )), } } } impl<'a> Struct<'a> { fn from_syn(node: &'a DeriveInput, data: &'a DataStruct) -> Result { let mut attrs = attr::get(&node.attrs)?; let fields = Field::multiple_from_syn(&data.fields)?; if let Some(display) = &mut attrs.display { display.expand_shorthand(&fields); } Ok(Struct { attrs, ident: node.ident.clone(), generics: &node.generics, fields, }) } } impl<'a> Enum<'a> { fn from_syn(node: &'a DeriveInput, data: &'a DataEnum) -> Result { let attrs = attr::get(&node.attrs)?; let variants = data .variants .iter() .map(|node| { let mut variant = Variant::from_syn(node)?; if let display @ None = &mut variant.attrs.display { *display = attrs.display.clone(); } if let Some(display) = &mut variant.attrs.display { display.expand_shorthand(&variant.fields); } else if variant.attrs.transparent.is_none() { variant.attrs.transparent = attrs.transparent; } Ok(variant) }) .collect::>()?; Ok(Enum { attrs, ident: node.ident.clone(), generics: &node.generics, variants, }) } } impl<'a> Variant<'a> { fn from_syn(node: &'a syn::Variant) -> Result { Ok(Variant { original: node, attrs: attr::get(&node.attrs)?, ident: node.ident.clone(), fields: Field::multiple_from_syn(&node.fields)?, }) } } impl<'a> Field<'a> { fn multiple_from_syn(fields: &'a Fields) -> Result> { fields .iter() .enumerate() .map(|(i, field)| Field::from_syn(i, field)) .collect() } fn from_syn(i: usize, node: &'a syn::Field) -> Result { Ok(Field { original: node, attrs: attr::get(&node.attrs)?, member: node .ident .clone() .map(Member::Named) .unwrap_or_else(|| Member::Unnamed(Index::from(i))), ty: &node.ty, }) } } thiserror-impl-1.0.10/src/attr.rs010064400017500001750000000136341361410661200151100ustar0000000000000000use proc_macro2::{Delimiter, Group, TokenStream, TokenTree}; use quote::{format_ident, quote, ToTokens}; use std::iter::FromIterator; use syn::parse::{Nothing, ParseStream}; use syn::{ braced, bracketed, parenthesized, token, Attribute, Error, Ident, Index, LitInt, LitStr, Result, Token, }; pub struct Attrs<'a> { pub display: Option>, pub source: Option<&'a Attribute>, pub backtrace: Option<&'a Attribute>, pub from: Option<&'a Attribute>, pub transparent: Option<&'a Attribute>, } #[derive(Clone)] pub struct Display<'a> { pub original: &'a Attribute, pub fmt: LitStr, pub args: TokenStream, pub has_bonus_display: bool, } pub fn get(input: &[Attribute]) -> Result { let mut attrs = Attrs { display: None, source: None, backtrace: None, from: None, transparent: None, }; for attr in input { if attr.path.is_ident("error") { parse_error_attribute(&mut attrs, attr)?; } else if attr.path.is_ident("source") { require_empty_attribute(attr)?; if attrs.source.is_some() { return Err(Error::new_spanned(attr, "duplicate #[source] attribute")); } attrs.source = Some(attr); } else if attr.path.is_ident("backtrace") { require_empty_attribute(attr)?; if attrs.backtrace.is_some() { return Err(Error::new_spanned(attr, "duplicate #[backtrace] attribute")); } attrs.backtrace = Some(attr); } else if attr.path.is_ident("from") { if !attr.tokens.is_empty() { // Assume this is meant for derive_more crate or something. continue; } if attrs.from.is_some() { return Err(Error::new_spanned(attr, "duplicate #[from] attribute")); } attrs.from = Some(attr); } } Ok(attrs) } fn parse_error_attribute<'a>(attrs: &mut Attrs<'a>, attr: &'a Attribute) -> Result<()> { syn::custom_keyword!(transparent); attr.parse_args_with(|input: ParseStream| { if input.parse::>()?.is_some() { if attrs.transparent.is_some() { return Err(Error::new_spanned( attr, "duplicate #[error(transparent)] attribute", )); } attrs.transparent = Some(attr); return Ok(()); } let display = Display { original: attr, fmt: input.parse()?, args: parse_token_expr(input, false)?, has_bonus_display: false, }; if attrs.display.is_some() { return Err(Error::new_spanned( attr, "only one #[error(...)] attribute is allowed", )); } attrs.display = Some(display); Ok(()) }) } fn parse_token_expr(input: ParseStream, mut begin_expr: bool) -> Result { let mut tokens = Vec::new(); while !input.is_empty() { if begin_expr && input.peek(Token![.]) { if input.peek2(Ident) { input.parse::()?; begin_expr = false; continue; } if input.peek2(LitInt) { input.parse::()?; let int: Index = input.parse()?; let ident = format_ident!("_{}", int.index, span = int.span); tokens.push(TokenTree::Ident(ident)); begin_expr = false; continue; } } begin_expr = input.peek(Token![break]) || input.peek(Token![continue]) || input.peek(Token![if]) || input.peek(Token![in]) || input.peek(Token![match]) || input.peek(Token![mut]) || input.peek(Token![return]) || input.peek(Token![while]) || input.peek(Token![+]) || input.peek(Token![&]) || input.peek(Token![!]) || input.peek(Token![^]) || input.peek(Token![,]) || input.peek(Token![/]) || input.peek(Token![=]) || input.peek(Token![>]) || input.peek(Token![<]) || input.peek(Token![|]) || input.peek(Token![%]) || input.peek(Token![;]) || input.peek(Token![*]) || input.peek(Token![-]); let token: TokenTree = if input.peek(token::Paren) { let content; let delimiter = parenthesized!(content in input); let nested = parse_token_expr(&content, true)?; let mut group = Group::new(Delimiter::Parenthesis, nested); group.set_span(delimiter.span); TokenTree::Group(group) } else if input.peek(token::Brace) { let content; let delimiter = braced!(content in input); let nested = parse_token_expr(&content, true)?; let mut group = Group::new(Delimiter::Brace, nested); group.set_span(delimiter.span); TokenTree::Group(group) } else if input.peek(token::Bracket) { let content; let delimiter = bracketed!(content in input); let nested = parse_token_expr(&content, true)?; let mut group = Group::new(Delimiter::Bracket, nested); group.set_span(delimiter.span); TokenTree::Group(group) } else { input.parse()? }; tokens.push(token); } Ok(TokenStream::from_iter(tokens)) } fn require_empty_attribute(attr: &Attribute) -> Result<()> { syn::parse2::(attr.tokens.clone())?; Ok(()) } 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) }); } } thiserror-impl-1.0.10/src/expand.rs010064400017500001750000000317051361255111400154130ustar0000000000000000use crate::ast::{Enum, Field, Input, Struct}; use proc_macro2::TokenStream; use quote::{format_ident, quote, quote_spanned, ToTokens}; use syn::spanned::Spanned; use syn::{DeriveInput, Member, PathArguments, Result, Type}; pub fn derive(node: &DeriveInput) -> Result { let input = Input::from_syn(node)?; 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 = &input.ident; let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); let source_body = if input.attrs.transparent.is_some() { let only_field = &input.fields[0].member; Some(quote! { std::error::Error::source(self.#only_field.as_dyn_error()) }) } else if let Some(source_field) = input.source_field() { let source = &source_field.member; let asref = if type_is_option(source_field.ty) { Some(quote_spanned!(source.span()=> .as_ref()?)) } else { None }; let dyn_error = quote_spanned!(source.span()=> self.#source #asref.as_dyn_error()); Some(quote! { std::option::Option::Some(#dyn_error) }) } else { None }; let source_method = source_body.map(|body| { quote! { fn source(&self) -> std::option::Option<&(dyn std::error::Error + 'static)> { use thiserror::private::AsDynError; #body } } }); let backtrace_method = input.backtrace_field().map(|backtrace_field| { let backtrace = &backtrace_field.member; let body = if let Some(source_field) = input.source_field() { let source = &source_field.member; let source_backtrace = if type_is_option(source_field.ty) { quote_spanned! {source.span()=> self.#source.as_ref().and_then(|source| source.as_dyn_error().backtrace()) } } else { quote_spanned! {source.span()=> self.#source.as_dyn_error().backtrace() } }; let combinator = if type_is_option(backtrace_field.ty) { quote! { #source_backtrace.or(self.#backtrace.as_ref()) } } else { quote! { std::option::Option::Some(#source_backtrace.unwrap_or(&self.#backtrace)) } }; quote! { use thiserror::private::AsDynError; #combinator } } else if type_is_option(backtrace_field.ty) { quote! { self.#backtrace.as_ref() } } else { quote! { std::option::Option::Some(&self.#backtrace) } }; quote! { fn backtrace(&self) -> std::option::Option<&std::backtrace::Backtrace> { #body } } }); let display_body = if input.attrs.transparent.is_some() { let only_field = &input.fields[0].member; Some(quote! { std::fmt::Display::fmt(&self.#only_field, __formatter) }) } else if let Some(display) = &input.attrs.display { let use_as_display = if display.has_bonus_display { Some(quote! { #[allow(unused_imports)] use thiserror::private::{DisplayAsDisplay, PathAsDisplay}; }) } else { None }; let pat = fields_pat(&input.fields); Some(quote! { #use_as_display #[allow(unused_variables)] let Self #pat = self; #display }) } else { None }; let display_impl = display_body.map(|body| { quote! { impl #impl_generics std::fmt::Display for #ty #ty_generics #where_clause { fn fmt(&self, __formatter: &mut std::fmt::Formatter) -> std::fmt::Result { #body } } } }); let from_impl = input.from_field().map(|from_field| { let backtrace_field = input.backtrace_field(); let from = from_field.ty; let body = from_initializer(from_field, backtrace_field); quote! { impl #impl_generics std::convert::From<#from> for #ty #ty_generics #where_clause { fn from(source: #from) -> Self { #ty #body } } } }); quote! { impl #impl_generics std::error::Error for #ty #ty_generics #where_clause { #source_method #backtrace_method } #display_impl #from_impl } } fn impl_enum(input: Enum) -> TokenStream { let ty = &input.ident; let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); let source_method = if input.has_source() { let arms = input.variants.iter().map(|variant| { let ident = &variant.ident; if variant.attrs.transparent.is_some() { let only_field = &variant.fields[0].member; let source = quote!(std::error::Error::source(transparent.as_dyn_error())); quote! { #ty::#ident {#only_field: transparent} => #source, } } else if let Some(source_field) = variant.source_field() { let source = &source_field.member; let asref = if type_is_option(source_field.ty) { Some(quote_spanned!(source.span()=> .as_ref()?)) } else { None }; let dyn_error = quote_spanned!(source.span()=> source #asref.as_dyn_error()); quote! { #ty::#ident {#source: source, ..} => std::option::Option::Some(#dyn_error), } } else { quote! { #ty::#ident {..} => std::option::Option::None, } } }); Some(quote! { fn source(&self) -> std::option::Option<&(dyn std::error::Error + 'static)> { use thiserror::private::AsDynError; match self { #(#arms)* } } }) } else { None }; let backtrace_method = if input.has_backtrace() { 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 source_backtrace = if type_is_option(source_field.ty) { quote_spanned! {source.span()=> source.as_ref().and_then(|source| source.as_dyn_error().backtrace()) } } else { quote_spanned! {source.span()=> source.as_dyn_error().backtrace() } }; let combinator = if type_is_option(backtrace_field.ty) { quote! { #source_backtrace.or(backtrace.as_ref()) } } else { quote! { std::option::Option::Some(#source_backtrace.unwrap_or(backtrace)) } }; quote! { #ty::#ident { #backtrace: backtrace, #source: source, .. } => { use thiserror::private::AsDynError; #combinator } } } (Some(backtrace_field), _) => { let backtrace = &backtrace_field.member; let body = if type_is_option(backtrace_field.ty) { quote!(backtrace.as_ref()) } else { quote!(std::option::Option::Some(backtrace)) }; quote! { #ty::#ident {#backtrace: backtrace, ..} => #body, } } (None, _) => quote! { #ty::#ident {..} => std::option::Option::None, }, } }); Some(quote! { fn backtrace(&self) -> std::option::Option<&std::backtrace::Backtrace> { match self { #(#arms)* } } }) } else { None }; let display_impl = if input.has_display() { let use_as_display = if input.variants.iter().any(|v| { v.attrs .display .as_ref() .map_or(false, |display| display.has_bonus_display) }) { Some(quote! { #[allow(unused_imports)] use thiserror::private::{DisplayAsDisplay, PathAsDisplay}; }) } else { None }; let void_deref = if input.variants.is_empty() { Some(quote!(*)) } else { None }; let arms = input.variants.iter().map(|variant| { let display = match &variant.attrs.display { Some(display) => display.to_token_stream(), None => { let only_field = match &variant.fields[0].member { Member::Named(ident) => ident.clone(), Member::Unnamed(index) => format_ident!("_{}", index), }; quote!(std::fmt::Display::fmt(#only_field, __formatter)) } }; let ident = &variant.ident; let pat = fields_pat(&variant.fields); quote! { #ty::#ident #pat => #display } }); Some(quote! { impl #impl_generics std::fmt::Display for #ty #ty_generics #where_clause { fn fmt(&self, __formatter: &mut std::fmt::Formatter) -> std::fmt::Result { #use_as_display #[allow(unused_variables)] match #void_deref self { #(#arms,)* } } } }) } else { None }; let from_impls = input.variants.iter().filter_map(|variant| { let from_field = variant.from_field()?; let backtrace_field = variant.backtrace_field(); let variant = &variant.ident; let from = from_field.ty; let body = from_initializer(from_field, backtrace_field); Some(quote! { impl #impl_generics std::convert::From<#from> for #ty #ty_generics #where_clause { fn from(source: #from) -> Self { #ty::#variant #body } } }) }); quote! { impl #impl_generics std::error::Error for #ty #ty_generics #where_clause { #source_method #backtrace_method } #display_impl #(#from_impls)* } } fn fields_pat(fields: &[Field]) -> TokenStream { let mut members = fields.iter().map(|field| &field.member).peekable(); match members.peek() { Some(Member::Named(_)) => quote!({ #(#members),* }), Some(Member::Unnamed(_)) => { let vars = members.map(|member| match member { Member::Unnamed(member) => format_ident!("_{}", member), Member::Named(_) => unreachable!(), }); quote!((#(#vars),*)) } None => quote!({}), } } fn from_initializer(from_field: &Field, backtrace_field: Option<&Field>) -> TokenStream { let from_member = &from_field.member; let backtrace = backtrace_field.map(|backtrace_field| { let backtrace_member = &backtrace_field.member; if type_is_option(backtrace_field.ty) { quote! { #backtrace_member: std::option::Option::Some(std::backtrace::Backtrace::capture()), } } else { quote! { #backtrace_member: std::backtrace::Backtrace::capture(), } } }); quote!({ #from_member: source, #backtrace }) } fn type_is_option(ty: &Type) -> bool { let path = match ty { Type::Path(ty) => &ty.path, _ => return false, }; let last = path.segments.last().unwrap(); if last.ident != "Option" { return false; } match &last.arguments { PathArguments::AngleBracketed(bracketed) => bracketed.args.len() == 1, _ => false, } } thiserror-impl-1.0.10/src/fmt.rs010064400017500001750000000110461357166334500147340ustar0000000000000000use crate::ast::Field; use crate::attr::Display; use proc_macro2::TokenTree; use quote::{format_ident, quote_spanned}; use std::collections::HashSet as Set; use syn::ext::IdentExt; use syn::parse::{ParseStream, Parser}; use syn::{Ident, Index, LitStr, Member, Result, Token}; impl Display<'_> { // Transform `"error {var}"` to `"error {}", var`. pub fn expand_shorthand(&mut self, fields: &[Field]) { let raw_args = self.args.clone(); let mut named_args = explicit_named_args.parse2(raw_args).unwrap(); let fields: Set = fields.iter().map(|f| f.member.clone()).collect(); let span = self.fmt.span(); let fmt = self.fmt.value(); let mut read = fmt.as_str(); let mut out = String::new(); let mut args = self.args.clone(); let mut has_bonus_display = false; let mut has_trailing_comma = false; if let Some(TokenTree::Punct(punct)) = args.clone().into_iter().last() { if punct.as_char() == ',' { has_trailing_comma = true; } } while let Some(brace) = read.find('{') { out += &read[..brace + 1]; read = &read[brace + 1..]; if read.starts_with('{') { out.push('{'); read = &read[1..]; continue; } let next = match read.chars().next() { Some(next) => next, None => return, }; let member = match next { '0'..='9' => { let int = take_int(&mut read); let member = match int.parse::() { Ok(index) => Member::Unnamed(Index { index, span }), Err(_) => return, }; if !fields.contains(&member) { out += ∫ continue; } member } 'a'..='z' | 'A'..='Z' | '_' => { let ident = take_ident(&mut read); Member::Named(Ident::new(&ident, span)) } _ => continue, }; let local = match &member { Member::Unnamed(index) => format_ident!("_{}", index), Member::Named(ident) => ident.clone(), }; let mut formatvar = local.clone(); if formatvar.to_string().starts_with('_') { // Work around leading underscore being rejected by 1.40 and // older compilers. https://github.com/rust-lang/rust/pull/66847 formatvar = format_ident!("field_{}", formatvar); } out += &formatvar.to_string(); if !named_args.insert(formatvar.clone()) { // Already specified in the format argument list. continue; } if !has_trailing_comma { args.extend(quote_spanned!(span=> ,)); has_trailing_comma = false; } args.extend(quote_spanned!(span=> #formatvar = #local)); if read.starts_with('}') && fields.contains(&member) { has_bonus_display = true; args.extend(quote_spanned!(span=> .as_display())); } } out += read; self.fmt = LitStr::new(&out, self.fmt.span()); self.args = args; self.has_bonus_display = has_bonus_display; } } fn explicit_named_args(input: ParseStream) -> Result> { let mut named_args = Set::new(); while !input.is_empty() { if input.peek(Token![,]) && input.peek2(Ident::peek_any) && input.peek3(Token![=]) { input.parse::()?; let ident: Ident = input.parse()?; input.parse::()?; named_args.insert(ident); } else { input.parse::()?; } } Ok(named_args) } fn take_int(read: &mut &str) -> String { let mut int = String::new(); 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 } thiserror-impl-1.0.10/src/lib.rs010064400017500001750000000010061361255111400146710ustar0000000000000000#![allow(clippy::block_in_if_condition_stmt, clippy::range_plus_one)] extern crate proc_macro; mod ast; mod attr; mod expand; mod fmt; mod prop; mod valid; use proc_macro::TokenStream; use syn::{parse_macro_input, DeriveInput}; #[proc_macro_derive(Error, attributes(backtrace, error, from, source))] 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() } thiserror-impl-1.0.10/src/prop.rs010064400017500001750000000053421361255111400151120ustar0000000000000000use crate::ast::{Enum, Field, Struct, Variant}; use syn::{Member, Type}; impl Struct<'_> { pub(crate) fn from_field(&self) -> Option<&Field> { from_field(&self.fields) } pub(crate) fn source_field(&self) -> Option<&Field> { source_field(&self.fields) } pub(crate) fn backtrace_field(&self) -> Option<&Field> { backtrace_field(&self.fields) } } impl Enum<'_> { pub(crate) fn has_source(&self) -> bool { self.variants .iter() .any(|variant| variant.source_field().is_some() || variant.attrs.transparent.is_some()) } pub(crate) fn has_backtrace(&self) -> bool { self.variants .iter() .any(|variant| variant.backtrace_field().is_some()) } pub(crate) fn has_display(&self) -> bool { self.attrs.display.is_some() || self.attrs.transparent.is_some() || self .variants .iter() .any(|variant| variant.attrs.display.is_some()) || self .variants .iter() .all(|variant| variant.attrs.transparent.is_some()) } } impl Variant<'_> { pub(crate) fn from_field(&self) -> Option<&Field> { from_field(&self.fields) } pub(crate) fn source_field(&self) -> Option<&Field> { source_field(&self.fields) } pub(crate) fn backtrace_field(&self) -> Option<&Field> { backtrace_field(&self.fields) } } impl Field<'_> { pub(crate) fn is_backtrace(&self) -> bool { type_is_backtrace(self.ty) } } fn from_field<'a, 'b>(fields: &'a [Field<'b>]) -> Option<&'a Field<'b>> { for field in fields { if field.attrs.from.is_some() { return Some(&field); } } None } fn source_field<'a, 'b>(fields: &'a [Field<'b>]) -> Option<&'a Field<'b>> { for field in fields { if field.attrs.from.is_some() || field.attrs.source.is_some() { return Some(&field); } } for field in fields { match &field.member { Member::Named(ident) if ident == "source" => return Some(&field), _ => {} } } None } fn backtrace_field<'a, 'b>(fields: &'a [Field<'b>]) -> Option<&'a Field<'b>> { for field in fields { if field.attrs.backtrace.is_some() { return Some(&field); } } for field in fields { if field.is_backtrace() { return Some(&field); } } None } fn type_is_backtrace(ty: &Type) -> bool { let path = match ty { Type::Path(ty) => &ty.path, _ => return false, }; let last = path.segments.last().unwrap(); last.ident == "Backtrace" && last.arguments.is_empty() } thiserror-impl-1.0.10/src/valid.rs010064400017500001750000000143771357107703300152500ustar0000000000000000use crate::ast::{Enum, Field, Input, Struct, Variant}; use crate::attr::Attrs; use quote::ToTokens; use std::collections::BTreeSet as Set; use syn::{Error, Member, Result}; impl Input<'_> { pub(crate) fn validate(&self) -> Result<()> { match self { Input::Struct(input) => input.validate(), Input::Enum(input) => input.validate(), } } } impl Struct<'_> { fn validate(&self) -> Result<()> { check_non_field_attrs(&self.attrs)?; if let Some(transparent) = self.attrs.transparent { if self.fields.len() != 1 { return Err(Error::new_spanned( transparent, "#[error(transparent)] requires exactly one field", )); } if let Some(source) = self.fields.iter().filter_map(|f| f.attrs.source).next() { return Err(Error::new_spanned( source, "transparent error struct can't contain #[source]", )); } } check_field_attrs(&self.fields)?; for field in &self.fields { field.validate()?; } Ok(()) } } impl Enum<'_> { fn validate(&self) -> Result<()> { check_non_field_attrs(&self.attrs)?; let has_display = self.has_display(); for variant in &self.variants { variant.validate()?; if has_display && variant.attrs.display.is_none() && variant.attrs.transparent.is_none() { return Err(Error::new_spanned( variant.original, "missing #[error(\"...\")] display attribute", )); } } let mut from_types = Set::new(); for variant in &self.variants { if let Some(from_field) = variant.from_field() { let repr = from_field.ty.to_token_stream().to_string(); if !from_types.insert(repr) { return Err(Error::new_spanned( from_field.original, "cannot derive From because another variant has the same source type", )); } } } Ok(()) } } impl Variant<'_> { fn validate(&self) -> Result<()> { check_non_field_attrs(&self.attrs)?; if self.attrs.transparent.is_some() { if self.fields.len() != 1 { return Err(Error::new_spanned( self.original, "#[error(transparent)] requires exactly one field", )); } if let Some(source) = self.fields.iter().filter_map(|f| f.attrs.source).next() { return Err(Error::new_spanned( source, "transparent variant can't contain #[source]", )); } } check_field_attrs(&self.fields)?; for field in &self.fields { field.validate()?; } Ok(()) } } impl Field<'_> { fn validate(&self) -> Result<()> { if let Some(display) = &self.attrs.display { return Err(Error::new_spanned( display.original, "not expected here; the #[error(...)] attribute belongs on top of a struct or an enum variant", )); } Ok(()) } } fn check_non_field_attrs(attrs: &Attrs) -> Result<()> { if let Some(from) = &attrs.from { return Err(Error::new_spanned( from, "not expected here; the #[from] attribute belongs on a specific field", )); } if let Some(source) = &attrs.source { return Err(Error::new_spanned( source, "not expected here; the #[source] attribute belongs on a specific field", )); } if let Some(backtrace) = &attrs.backtrace { return Err(Error::new_spanned( backtrace, "not expected here; the #[backtrace] attribute belongs on a specific field", )); } if let Some(display) = &attrs.display { if attrs.transparent.is_some() { return Err(Error::new_spanned( display.original, "cannot have both #[error(transparent)] and a display attribute", )); } } Ok(()) } fn check_field_attrs(fields: &[Field]) -> Result<()> { let mut from_field = None; let mut source_field = None; let mut backtrace_field = None; let mut has_backtrace = false; for field in fields { if let Some(from) = field.attrs.from { if from_field.is_some() { return Err(Error::new_spanned(from, "duplicate #[from] attribute")); } from_field = Some(field); } if let Some(source) = field.attrs.source { if source_field.is_some() { return Err(Error::new_spanned(source, "duplicate #[source] attribute")); } source_field = Some(field); } if let Some(backtrace) = field.attrs.backtrace { if backtrace_field.is_some() { return Err(Error::new_spanned( backtrace, "duplicate #[backtrace] attribute", )); } backtrace_field = Some(field); has_backtrace = true; } has_backtrace |= field.is_backtrace(); } if let (Some(from_field), Some(source_field)) = (from_field, source_field) { if !same_member(from_field, source_field) { return Err(Error::new_spanned( from_field.attrs.from, "#[from] is only supported on the source field, not any other field", )); } } if let Some(from_field) = from_field { if fields.len() > 1 + has_backtrace as usize { return Err(Error::new_spanned( from_field.attrs.from, "deriving From requires no fields other than source and backtrace", )); } } Ok(()) } fn same_member(one: &Field, two: &Field) -> bool { match (&one.member, &two.member) { (Member::Named(one), Member::Named(two)) => one == two, (Member::Unnamed(one), Member::Unnamed(two)) => one.index == two.index, _ => unreachable!(), } } thiserror-impl-1.0.10/.cargo_vcs_info.json0000644000000001121361411030300141530ustar00{ "git": { "sha1": "ccbb2ab862cdff9912a438aca9c54d5945e20351" } }