1#![recursion_limit = "300"]
2
3#![cfg_attr(feature = "cargo-clippy", deny(clippy, clippy_pedantic))]
4#![cfg_attr(feature = "cargo-clippy", allow(
5 large_enum_variant,
6 too_many_arguments,
7 use_self,
8))]
9
10extern crate proc_macro;
599extern crate proc_macro2;
600#[macro_use]
601extern crate quote;
602#[macro_use]
603extern crate syn;
604extern crate syntex_fmt_macros;
605
606#[proc_macro_derive(ErrorChain, attributes(error_chain))]
607pub fn derive_error_chain(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
608 let ast: syn::DeriveInput = syn::parse(input).unwrap();
609
610 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
611
612 let mut generics_lifetime = ast.generics.clone();
613 generics_lifetime.params = std::iter::once(parse_quote!('__a)).chain(generics_lifetime.params).collect();
614 let (impl_generics_lifetime, _, _) = generics_lifetime.split_for_impl();
615
616 let mut result_generics = ast.generics.clone();
617 result_generics.params.push(parse_quote!(__T));
618 let (_, result_ty_generics, _) = result_generics.split_for_impl();
619
620 let mut result_ext_generics_t = ast.generics.clone();
621 result_ext_generics_t.params.push(parse_quote!(__T));
622 let (result_ext_impl_generics_t, result_ext_ty_generics_t, _) = result_ext_generics_t.split_for_impl();
623
624 let mut result_ext_generics_t_e = result_ext_generics_t.clone();
625 result_ext_generics_t_e.params.push(parse_quote!(__E: ::std::error::Error + ::std::marker::Send + 'static));
626 let (result_ext_impl_generics_t_e, _, _) = result_ext_generics_t_e.split_for_impl();
627
628 let generics: std::collections::HashSet<_> =
629 ast.generics.params.iter()
630 .filter_map(|param|
631 if let syn::GenericParam::Type(syn::TypeParam { ref ident, .. }) = *param {
632 Some(ident)
633 }
634 else {
635 None
636 })
637 .collect();
638
639 let TopLevelProperties {
640 error_kind_name,
641 error_kind_vis,
642 error_name,
643 result_ext_name,
644 result_name,
645 support_backtrace,
646 error_chain_name,
647 } = (&ast).into();
648
649 let result = match ast.data {
650 syn::Data::Enum(syn::DataEnum { variants, .. }) => {
651 let links: Vec<Link> = variants.into_iter().map(Into::into).collect();
652
653 let error_kind_description_cases = links.iter().map(|link| link.error_kind_description(&error_kind_name));
654
655 let error_kind_display_cases = links.iter().map(|link| link.error_kind_display_case(&error_kind_name));
656
657 let error_kind_from_impls =
658 links.iter().filter_map(|link|
659 link.error_kind_from_impl(
660 &error_kind_name,
661 &impl_generics, &impl_generics_lifetime, &ty_generics, where_clause,
662 ));
663
664 let error_cause_cases = links.iter().filter_map(|link| link.error_cause_case(&error_kind_name));
665
666 let error_doc_comment = format!(r"The Error type.
667
668This struct is made of three things:
669
670- `{0}` which is used to determine the type of the error.
671- a backtrace, generated when the error is created.
672- an error chain, used for the implementation of `Error::cause()`.", error_kind_name);
673
674 let error_from_impls =
675 links.iter().filter_map(|link|
676 link.error_from_impl(
677 &error_kind_name, &error_name,
678 &generics,
679 &impl_generics, &impl_generics_lifetime, &ty_generics, where_clause,
680 ));
681
682 let extract_backtrace_fn = if support_backtrace {
683 let chained_error_extract_backtrace_cases = links.iter().filter_map(Link::chained_error_extract_backtrace_case);
684
685 Some(quote! {
686 fn extract_backtrace(err: &(::std::error::Error + Send + 'static)) -> Option<::std::sync::Arc<#error_chain_name::Backtrace>> {
687 if let Some(err) = err.downcast_ref::<Self>() {
688 return err.1.backtrace.clone();
689 }
690
691 #(#chained_error_extract_backtrace_cases)*
692
693 None
694 }
695 })
696 }
697 else {
698 None
699 };
700
701 let result_ext_chain_err_doc_comment = format!("\
702 If the `Result` is an `Err` then `chain_err` evaluates the closure, \
703 which returns *some type that can be converted to `{}`*, \
704 boxes the original error to store as the cause, then returns a new error \
705 containing the original error.\
706 ", error_kind_name);
707
708 let result_wrapper = result_name.map(|result_name| quote! {
709 #error_kind_vis type #result_name #result_ty_generics = ::std::result::Result<__T, #error_name #ty_generics>;
711 });
712
713 quote! {
714 extern crate error_chain as #error_chain_name;
715
716 impl #impl_generics #error_kind_name #ty_generics #where_clause {
717 pub fn description(&self) -> &str {
719 #[cfg_attr(feature = "cargo-clippy", allow(match_same_arms))]
720 match *self {
721 #(#error_kind_description_cases)*
722 }
723 }
724 }
725
726 impl #impl_generics ::std::fmt::Display for #error_kind_name #ty_generics #where_clause {
727 fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
728 #[cfg_attr(feature = "cargo-clippy", allow(match_same_arms))]
729 match *self {
730 #(#error_kind_display_cases)*
731 }
732 }
733 }
734
735 #(#error_kind_from_impls)*
736
737 impl #impl_generics From<#error_name #ty_generics> for #error_kind_name #ty_generics #where_clause {
738 fn from(err: #error_name #ty_generics) -> Self { err.0 }
739 }
740
741 #[doc = #error_doc_comment]
742 #[derive(Debug)]
743 #error_kind_vis struct #error_name #impl_generics (
744 pub #error_kind_name #ty_generics,
746
747 pub #error_chain_name::State,
749 ) #where_clause ;
750
751 #[allow(unused)]
752 impl #impl_generics #error_name #ty_generics #where_clause {
753 pub fn from_kind(kind: #error_kind_name #ty_generics) -> Self {
755 #error_name(kind, #error_chain_name::State::default())
756 }
757
758 pub fn with_chain<__E, __K>(error: __E, kind: __K) -> Self
760 where __E: ::std::error::Error + Send + 'static, __K: Into<#error_kind_name #ty_generics>
761 {
762 #error_name::with_boxed_chain(Box::new(error), kind)
763 }
764
765 pub fn with_boxed_chain<__K>(error: Box<::std::error::Error + Send>, kind: __K) -> #error_name #ty_generics
767 where __K: Into<#error_kind_name #ty_generics>
768 {
769 #error_name(kind.into(), #error_chain_name::State::new::<Self>(error))
770 }
771
772 pub fn kind(&self) -> &#error_kind_name #ty_generics { &self.0 }
774
775 pub fn iter(&self) -> #error_chain_name::Iter {
777 #error_chain_name::ChainedError::iter(self)
778 }
779
780 pub fn backtrace(&self) -> Option<&#error_chain_name::Backtrace> {
782 self.1.backtrace()
783 }
784
785 pub fn chain_err<__F, __EK>(self, error: __F) -> Self where __F: FnOnce() -> __EK, __EK: Into<#error_kind_name #ty_generics> {
787 #error_name::with_chain(self, Self::from_kind(error().into()))
788 }
789 }
790
791 impl #impl_generics ::std::error::Error for #error_name #ty_generics #where_clause {
792 fn description(&self) -> &str { self.0.description() }
793
794 fn cause(&self) -> Option<&::std::error::Error> {
795 #[cfg_attr(feature = "cargo-clippy", allow(match_same_arms))]
796 match self.1.next_error {
797 Some(ref c) => Some(&**c),
798 None => match self.0 {
799 #(#error_cause_cases)*
800
801 _ => None,
802 },
803 }
804 }
805 }
806
807 impl #impl_generics ::std::fmt::Display for #error_name #ty_generics #where_clause {
808 fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
809 ::std::fmt::Display::fmt(&self.0, f)
810 }
811 }
812
813 #(#error_from_impls)*
814
815 impl #impl_generics From<#error_kind_name #ty_generics> for #error_name #ty_generics #where_clause {
816 fn from(kind: #error_kind_name #ty_generics) -> Self { Self::from_kind(kind) }
817 }
818
819 impl #impl_generics ::std::ops::Deref for #error_name #ty_generics #where_clause {
820 type Target = #error_kind_name #ty_generics;
821
822 fn deref(&self) -> &Self::Target { &self.0 }
823 }
824
825 impl #impl_generics #error_chain_name::ChainedError for #error_name #ty_generics #where_clause {
826 type ErrorKind = #error_kind_name #ty_generics;
827
828 fn new(kind: Self::ErrorKind, state: #error_chain_name::State) -> Self {
829 #error_name(kind, state)
830 }
831
832 fn from_kind(kind: Self::ErrorKind) -> Self {
833 Self::from_kind(kind)
834 }
835
836 fn with_chain<__E, __K>(error: __E, kind: __K) -> Self
837 where __E: ::std::error::Error + Send + 'static, __K: Into<Self::ErrorKind> {
838
839 Self::with_chain(error, kind)
840 }
841
842 fn kind(&self) -> &Self::ErrorKind {
843 self.kind()
844 }
845
846 fn iter(&self) -> #error_chain_name::Iter {
847 #error_chain_name::Iter::new(Some(self))
848 }
849
850 fn backtrace(&self) -> Option<&#error_chain_name::Backtrace> {
851 self.backtrace()
852 }
853
854 fn chain_err<__F, __EK>(self, error: __F) -> Self where __F: FnOnce() -> __EK, __EK: Into<Self::ErrorKind> {
855 self.chain_err(error)
856 }
857
858 #extract_backtrace_fn
859 }
860
861 #error_kind_vis trait #result_ext_name #result_ext_impl_generics_t #where_clause {
863 #[doc = #result_ext_chain_err_doc_comment]
864 fn chain_err<__F, __EK>(self, callback: __F) -> ::std::result::Result<__T, #error_name #ty_generics>
865 where __F: FnOnce() -> __EK, __EK: Into<#error_kind_name #ty_generics>;
866 }
867
868 impl #result_ext_impl_generics_t_e #result_ext_name #result_ext_ty_generics_t for ::std::result::Result<__T, __E> #where_clause {
869 fn chain_err<__F, __EK>(self, callback: __F) -> ::std::result::Result<__T, #error_name #ty_generics>
870 where __F: FnOnce() -> __EK, __EK: Into<#error_kind_name #ty_generics> {
871 self.map_err(move |e| {
872 let state = #error_chain_name::State::new::<#error_name #ty_generics>(Box::new(e));
873 #error_chain_name::ChainedError::new(callback().into(), state)
874 })
875 }
876 }
877
878 impl #result_ext_impl_generics_t #result_ext_name #result_ext_ty_generics_t for ::std::option::Option<__T> #where_clause {
879 fn chain_err<__F, __EK>(self, callback: __F) -> ::std::result::Result<__T, #error_name #ty_generics>
880 where __F: FnOnce() -> __EK, __EK: Into<#error_kind_name #ty_generics> {
881 self.ok_or_else(move || {
882 #error_chain_name::ChainedError::from_kind(callback().into())
883 })
884 }
885 }
886
887 #result_wrapper
888 }
889 },
890
891 _ => panic!("#[derive(ErrorChain] can only be used with enums."),
892 };
893
894 result.into()
895}
896
897struct TopLevelProperties {
898 error_kind_name: proc_macro2::Ident,
899 error_kind_vis: syn::Visibility,
900 error_name: proc_macro2::Ident,
901 result_ext_name: proc_macro2::Ident,
902 result_name: Option<proc_macro2::Ident>,
903 error_chain_name: proc_macro2::Ident,
904 support_backtrace: bool,
905}
906
907impl<'a> From<&'a syn::DeriveInput> for TopLevelProperties {
908 fn from(ast: &'a syn::DeriveInput) -> Self {
909 let mut error_name = proc_macro2::Ident::new("Error", proc_macro2::Span::call_site());
910 let mut result_ext_name = proc_macro2::Ident::new("ResultExt", proc_macro2::Span::call_site());
911 let mut result_name = Some(proc_macro2::Ident::new("Result", proc_macro2::Span::call_site()));
912 let mut support_backtrace = true;
913
914 for attr in &ast.attrs {
915 if !is_error_chain_attribute(attr) {
916 continue;
917 }
918
919 match attr.interpret_meta() {
920 Some(syn::Meta::List(syn::MetaList { nested, .. })) => {
921 for nested_meta in nested {
922 match nested_meta {
923 syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue { ident, lit: syn::Lit::Str(value), .. })) => {
924 let value = &value.value();
925
926 match &*ident.to_string() {
927 "error" => error_name = syn::parse_str(value).unwrap_or_else(|err|
928 panic!("Could not parse `error` value as an identifier - {}", err)),
929
930 "result_ext" => result_ext_name = syn::parse_str(value).unwrap_or_else(|err|
931 panic!("Could not parse `result_ext` value as an identifier - {}", err)),
932
933 "result" => result_name =
934 if value == "" {
935 None
936 }
937 else {
938 Some(syn::parse_str(value).unwrap_or_else(|err|
939 panic!("Could not parse `result` value as an identifier - {}", err)))
940 },
941
942 "backtrace" => support_backtrace = value.parse().unwrap_or_else(|err|
943 panic!("Could not parse `backtrace` value - {}", err)),
944
945 _ =>
946 panic!("Could not parse `error_chain` attribute - expected one of `error`, `result_ext`, `result`, `backtrace` but got {}", ident),
947 }
948 },
949
950 syn::NestedMeta::Meta(syn::Meta::NameValue(
951 syn::MetaNameValue { ref ident, lit: syn::Lit::Bool(syn::LitBool { value, .. }), .. }))
952 if ident == "backtrace" => support_backtrace = value,
953
954 _ => panic!("Could not parse `error_chain` attribute - expected one of `error`, `result_ext`, `result`, `backtrace`"),
955 }
956 }
957 },
958
959 _ => panic!("Could not parse `error_chain` attribute - expected one of `error`, `result_ext`, `result`, `backtrace`"),
960 }
961 }
962
963 let error_chain_name = syn::parse_str(&format!("{}_error_chain", error_name)).unwrap_or_else(|err|
964 panic!("Could not generate error_chain crate name as a valid ident - {}", err));
965
966 TopLevelProperties {
967 error_kind_name: ast.ident.clone(),
968 error_kind_vis: ast.vis.clone(),
969 error_name,
970 result_ext_name,
971 result_name,
972 error_chain_name,
973 support_backtrace,
974 }
975 }
976}
977
978struct Link {
979 variant_ident: proc_macro2::Ident,
980 variant_fields: syn::Fields,
981 link_type: LinkType,
982 custom_description: Option<CustomFormatter>,
983 custom_display: Option<CustomFormatter>,
984 custom_cause: Option<syn::Expr>,
985}
986
987enum LinkType {
988 Msg,
989 Chainable(syn::Type, syn::Type),
990 Foreign(syn::Type),
991 Custom,
992}
993
994impl From<syn::Variant> for Link {
995 fn from(syn::Variant { ident: variant_ident, attrs, fields: variant_fields, .. }: syn::Variant) -> Self {
996 let is_msg = loop {
997 if variant_ident != "Msg" {
998 break false;
999 }
1000
1001 if let syn::Fields::Unnamed(syn::FieldsUnnamed { ref unnamed, .. }) = variant_fields {
1002 if unnamed.len() == 1 {
1003 if let syn::Type::Path(syn::TypePath { ref path, .. }) = unnamed[0].ty {
1004 if !path.global() && path.segments.len() == 1 && path.segments[0].ident == "String" {
1005 break true;
1006 }
1007 }
1008 }
1009 }
1010
1011 panic!("Expected Msg member to be a tuple of String");
1012 };
1013
1014 if is_msg {
1015 return Link {
1016 variant_ident,
1017 variant_fields,
1018 link_type: LinkType::Msg,
1019 custom_description: None,
1020 custom_display: None,
1021 custom_cause: None,
1022 };
1023 }
1024
1025 let mut link_type = None;
1026 let mut custom_description = None;
1027 let mut custom_display = None;
1028 let mut custom_cause: Option<syn::Expr> = None;
1029
1030 for attr in attrs {
1031 if !is_error_chain_attribute(&attr) {
1032 continue;
1033 }
1034
1035 if let Some(syn::Meta::List(syn::MetaList { nested, .. })) = attr.interpret_meta() {
1036 for nested_meta in nested {
1037 match nested_meta {
1038 syn::NestedMeta::Meta(syn::Meta::Word(ident)) => match &*ident.to_string() {
1039 "foreign" => match variant_fields {
1040 syn::Fields::Unnamed(syn::FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 =>
1041 link_type = Some(LinkType::Foreign(unnamed[0].ty.clone())),
1042
1043 _ => panic!("Foreign link {} must be a tuple of one element (the foreign error type).", variant_ident),
1044 },
1045
1046 "custom" => link_type = Some(LinkType::Custom),
1047
1048 _ => panic!(
1049 "Could not parse `error_chain` attribute of member {} - expected one of `foreign`, `custom` but got {}",
1050 variant_ident, ident),
1051 },
1052
1053 syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue { ident, lit: syn::Lit::Str(value), .. })) => {
1054 let value = &value.value();
1055
1056 match &*ident.to_string() {
1057 "link" => match variant_fields {
1058 syn::Fields::Unnamed(syn::FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 =>
1059 link_type = Some(LinkType::Chainable(
1060 syn::parse_str(value).unwrap_or_else(|err|
1061 panic!("Could not parse `link` attribute of member {} as a type - {}", variant_ident, err)),
1062 unnamed[0].ty.clone())),
1063
1064 _ => panic!("Chainable link {} must be a tuple of one element (the chainable error kind).", variant_ident),
1065 },
1066
1067 "description" => custom_description = Some(CustomFormatter::Expr(syn::parse_str(value).unwrap_or_else(|err|
1068 panic!("Could not parse `description` attribute of member {} as an expression - {}", variant_ident, err)))),
1069
1070 "display" => custom_display = Some(CustomFormatter::Expr(syn::parse_str(value).unwrap_or_else(|err|
1071 panic!("Could not parse `display` attribute of member {} as an expression - {}", variant_ident, err)))),
1072
1073 "cause" => custom_cause = Some(syn::parse_str(value).unwrap_or_else(|err|
1074 panic!("Could not parse `cause` attribute of member {} as an expression - {}", variant_ident, err))),
1075
1076 _ => panic!(
1077 "Could not parse `error_chain` attribute of member {} - expected one of `link`, `description`, `display`, `cause` but got {}",
1078 variant_ident, ident),
1079 }
1080 },
1081
1082 _ => panic!("Could not parse `error_chain` attribute of member {} - expected term or name-value meta item", variant_ident),
1083 }
1084 }
1085 }
1086 else {
1087 let mut tts = {
1088 let mut tts = attr.tts.into_iter();
1089
1090 let tt = match tts.next() {
1091 Some(proc_macro2::TokenTree::Group(ref group)) if group.delimiter() == proc_macro2::Delimiter::Parenthesis => group.stream(),
1092 Some(tt) => panic!("Could not parse `error_chain` attribute of member {} - expected `(tokens)` but found {}", variant_ident, tt),
1093 None => panic!("Could not parse `error_chain` attribute of member {} - expected `(tokens)`", variant_ident),
1094 };
1095
1096 if let Some(tt) = tts.next() {
1097 panic!("Could not parse `error_chain` attribute of member {} - unexpected token {} after `(tokens)`", variant_ident, tt);
1098 }
1099
1100 tt.into_iter()
1101 };
1102
1103 let ident = match tts.next() {
1104 Some(proc_macro2::TokenTree::Ident(ident)) => ident,
1105 Some(tt) => panic!("Could not parse `error_chain` attribute of member {} - expected a term but got {}", variant_ident, tt),
1106 None => panic!("Could not parse `error_chain` attribute of member {} - expected a term", variant_ident),
1107 };
1108 let ident = ident.to_string();
1109
1110 match tts.next() {
1111 Some(proc_macro2::TokenTree::Punct(ref punct)) if punct.as_char() == '=' => (),
1112 Some(tt) => panic!("Could not parse `error_chain` attribute of member {} - expected `=` but got {}", variant_ident, tt),
1113 None => panic!("Could not parse `error_chain` attribute of member {} - expected `=`", variant_ident),
1114 }
1115
1116 let value: proc_macro2::TokenStream = tts.collect();
1117 if value.is_empty() {
1118 panic!("Could not parse `error_chain` attribute of member {} - expected tokens after `=`", variant_ident);
1119 }
1120
1121 match &*ident {
1122 "link" => match variant_fields {
1123 syn::Fields::Unnamed(syn::FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 =>
1124 link_type = Some(LinkType::Chainable(
1125 syn::parse2(value).unwrap_or_else(|err|
1126 panic!("Could not parse `link` attribute of member {} as a type - {}", variant_ident, err)),
1127 unnamed[0].ty.clone())),
1128
1129 _ => panic!("Chainable link {} must be a tuple of one element (the chainable error kind).", variant_ident),
1130 },
1131
1132 "description" => custom_description = Some(CustomFormatter::parse(value, "description", &variant_ident, &variant_fields)),
1133
1134 "display" => custom_display = Some(CustomFormatter::parse(value, "display", &variant_ident, &variant_fields)),
1135
1136 "cause" => custom_cause = Some(syn::parse2(value).unwrap_or_else(|err|
1137 panic!("Could not parse `cause` attribute of member {} as an expression - {}", variant_ident, err))),
1138
1139 _ => panic!(
1140 "Could not parse `error_chain` attribute of member {} - expected one of `link`, `description`, `display`, `cause` but got {}",
1141 variant_ident, ident),
1142 }
1143 }
1144 }
1145
1146 let link_type = link_type.unwrap_or_else(||
1147 panic!(r#"Member {} does not have any of #[error_chain(link = "...")] or #[error_chain(foreign)] or #[error_chain(custom)]."#, variant_ident));
1148
1149 Link {
1150 variant_ident,
1151 variant_fields,
1152 link_type,
1153 custom_description,
1154 custom_display,
1155 custom_cause,
1156 }
1157 }
1158}
1159
1160impl Link {
1161 fn error_kind_description(&self, error_kind_name: &proc_macro2::Ident) -> proc_macro2::TokenStream {
1162 let variant_ident = &self.variant_ident;
1163
1164 match (self.custom_description.as_ref(), &self.link_type) {
1165 (_, &LinkType::Msg) => quote! {
1166 #error_kind_name::#variant_ident(ref s) => s,
1167 },
1168
1169 (Some(&CustomFormatter::FormatString { ref format_string, .. }), &LinkType::Chainable(_, _)) |
1170 (Some(&CustomFormatter::FormatString { ref format_string, .. }), &LinkType::Foreign(_)) => quote! {
1171 #error_kind_name::#variant_ident(_) => #format_string,
1172 },
1173
1174 (Some(&CustomFormatter::Expr(ref custom_description)), &LinkType::Chainable(_, _)) |
1175 (Some(&CustomFormatter::Expr(ref custom_description)), &LinkType::Foreign(_)) if is_closure(custom_description) => quote! {
1176 #error_kind_name::#variant_ident(ref err) => {
1177 #[cfg_attr(feature = "cargo-clippy", allow(redundant_closure_call))]
1178 { (#custom_description)(err) }
1179 },
1180 },
1181
1182 (Some(&CustomFormatter::Expr(ref custom_description)), &LinkType::Chainable(_, _)) |
1183 (Some(&CustomFormatter::Expr(ref custom_description)), &LinkType::Foreign(_)) => quote! {
1184 #error_kind_name::#variant_ident(ref err) => #custom_description(err),
1185 },
1186
1187 (Some(&CustomFormatter::FormatString { ref format_string, .. }), &LinkType::Custom) => {
1188 let pattern = fields_pattern_ignore(&self.variant_fields);
1189
1190 quote! {
1191 #error_kind_name::#variant_ident #pattern => #format_string,
1192 }
1193 },
1194
1195 (Some(&CustomFormatter::Expr(ref custom_description)), &LinkType::Custom) => {
1196 let pattern = fields_pattern(&self.variant_fields);
1197 let args = args(&self.variant_fields);
1198
1199 if is_closure(custom_description) {
1200 quote! {
1201 #error_kind_name::#variant_ident #pattern => {
1202 #[cfg_attr(feature = "cargo-clippy", allow(redundant_closure_call))]
1203 { (#custom_description)(#args) }
1204 },
1205 }
1206 }
1207 else {
1208 quote! {
1209 #error_kind_name::#variant_ident #pattern => #custom_description(#args),
1210 }
1211 }
1212 },
1213
1214 (None, &LinkType::Chainable(_, _)) => quote! {
1215 #error_kind_name::#variant_ident(ref kind) => kind.description(),
1216 },
1217
1218 (None, &LinkType::Foreign(_)) => quote! {
1219 #error_kind_name::#variant_ident(ref err) => ::std::error::Error::description(err),
1220 },
1221
1222 (None, &LinkType::Custom) => {
1223 let pattern = fields_pattern_ignore(&self.variant_fields);
1224
1225 quote! {
1226 #error_kind_name::#variant_ident #pattern => stringify!(#variant_ident),
1227 }
1228 },
1229 }
1230 }
1231
1232 fn error_kind_display_case(
1233 &self,
1234 error_kind_name: &proc_macro2::Ident,
1235 ) -> proc_macro2::TokenStream {
1236 let variant_ident = &self.variant_ident;
1237
1238 match (self.custom_display.as_ref(), &self.link_type) {
1239 (_, &LinkType::Msg) => quote! {
1240 #error_kind_name::#variant_ident(ref s) => ::std::fmt::Display::fmt(s, f),
1241 },
1242
1243 (Some(&CustomFormatter::FormatString { ref format_string, ref pattern, ref args }), &LinkType::Chainable(_, _)) => quote! {
1244 #error_kind_name::#variant_ident #pattern => write!(f, #format_string, #args),
1245 },
1246
1247 (Some(&CustomFormatter::Expr(ref custom_display)), &LinkType::Chainable(_, _)) if is_closure(custom_display) => quote! {
1248 #error_kind_name::#variant_ident(ref kind) => {
1249 #[cfg_attr(feature = "cargo-clippy", allow(redundant_closure_call))]
1250 { (#custom_display)(kind) }
1251 },
1252 },
1253
1254 (Some(&CustomFormatter::Expr(ref custom_display)), &LinkType::Chainable(_, _)) => quote! {
1255 #error_kind_name::#variant_ident(ref kind) => #custom_display(f, kind),
1256 },
1257
1258 (Some(&CustomFormatter::FormatString { ref format_string, ref pattern, ref args }), &LinkType::Foreign(_)) => quote! {
1259 #error_kind_name::#variant_ident #pattern => write!(f, #format_string, #args),
1260 },
1261
1262 (Some(&CustomFormatter::Expr(ref custom_display)), &LinkType::Foreign(_)) if is_closure(custom_display) => quote! {
1263 #error_kind_name::#variant_ident(ref err) => {
1264 #[cfg_attr(feature = "cargo-clippy", allow(redundant_closure_call))]
1265 { (#custom_display)(err) }
1266 },
1267 },
1268
1269 (Some(&CustomFormatter::Expr(ref custom_display)), &LinkType::Foreign(_)) => quote! {
1270 #error_kind_name::#variant_ident(ref err) => #custom_display(f, err),
1271 },
1272
1273 (Some(&CustomFormatter::FormatString { ref format_string, ref pattern, ref args }), &LinkType::Custom) => quote! {
1274 #error_kind_name::#variant_ident #pattern => write!(f, #format_string, #args),
1275 },
1276
1277 (Some(&CustomFormatter::Expr(ref custom_display)), &LinkType::Custom) => {
1278 let pattern = fields_pattern(&self.variant_fields);
1279 let args = args(&self.variant_fields);
1280
1281 if is_closure(custom_display) {
1282 quote! {
1283 #error_kind_name::#variant_ident #pattern => {
1284 #[cfg_attr(feature = "cargo-clippy", allow(redundant_closure_call))]
1285 { (#custom_display)(#args) }
1286 },
1287 }
1288 }
1289 else {
1290 quote! {
1291 #error_kind_name::#variant_ident #pattern => #custom_display(f, #args),
1292 }
1293 }
1294 },
1295
1296 (None, &LinkType::Chainable(_, _)) => quote! {
1297 #error_kind_name::#variant_ident(ref kind) => ::std::fmt::Display::fmt(kind, f),
1298 },
1299
1300 (None, &LinkType::Foreign(_)) => quote! {
1301 #error_kind_name::#variant_ident(ref err) => ::std::fmt::Display::fmt(err, f),
1302 },
1303
1304 (None, &LinkType::Custom) => {
1305 let pattern = fields_pattern_ignore(&self.variant_fields);
1306
1307 quote! {
1308 #error_kind_name::#variant_ident #pattern => ::std::fmt::Display::fmt(self.description(), f),
1309 }
1310 },
1311 }
1312 }
1313
1314 fn error_kind_from_impl(
1315 &self,
1316 error_kind_name: &proc_macro2::Ident,
1317 impl_generics: &syn::ImplGenerics, impl_generics_lifetime: &syn::ImplGenerics, ty_generics: &syn::TypeGenerics, where_clause: Option<&syn::WhereClause>,
1318 ) -> Option<proc_macro2::TokenStream> {
1319 let variant_ident = &self.variant_ident;
1320
1321 match self.link_type {
1322 LinkType::Msg => Some(quote! {
1323 impl #impl_generics_lifetime From<&'__a str> for #error_kind_name #ty_generics #where_clause {
1324 fn from(s: &'__a str) -> Self { #error_kind_name::#variant_ident(s.to_string()) }
1325 }
1326
1327 impl #impl_generics From<String> for #error_kind_name #ty_generics #where_clause {
1328 fn from(s: String) -> Self { #error_kind_name::#variant_ident(s) }
1329 }
1330 }),
1331
1332 LinkType::Chainable(_, ref error_kind_ty) => Some(quote! {
1333 impl #impl_generics From<#error_kind_ty> for #error_kind_name #ty_generics #where_clause {
1334 fn from(kind: #error_kind_ty) -> Self {
1335 #error_kind_name::#variant_ident(kind)
1336 }
1337 }
1338 }),
1339
1340 LinkType::Foreign(_) |
1341 LinkType::Custom => None,
1342 }
1343 }
1344
1345 fn error_cause_case(
1346 &self,
1347 error_kind_name: &proc_macro2::Ident,
1348 ) -> Option<proc_macro2::TokenStream> {
1349 let variant_ident = &self.variant_ident;
1350
1351 #[cfg_attr(feature = "cargo-clippy", allow(match_same_arms))]
1352 match (self.custom_cause.as_ref(), &self.link_type) {
1353 (_, &LinkType::Msg) => None,
1354
1355 (Some(custom_cause), _) => Some({
1356 let pattern = fields_pattern(&self.variant_fields);
1357 let args = args(&self.variant_fields);
1358
1359 if is_closure(custom_cause) {
1360 quote! {
1361 #error_kind_name::#variant_ident #pattern => {
1362 #[cfg_attr(feature = "cargo-clippy", allow(redundant_closure_call))]
1363 let result = (#custom_cause)(#args);
1364 Some(result)
1365 },
1366 }
1367 }
1368 else {
1369 quote! {
1370 #error_kind_name::#variant_ident #pattern => Some(#custom_cause(#args)),
1371 }
1372 }
1373 }),
1374
1375 (None, &LinkType::Foreign(_)) => Some(quote! {
1376 #error_kind_name::#variant_ident(ref err) => ::std::error::Error::cause(err),
1377 }),
1378
1379 (None, &LinkType::Chainable(_, _)) |
1380 (None, &LinkType::Custom) => None,
1381 }
1382 }
1383
1384 fn error_from_impl(
1385 &self,
1386 error_kind_name: &proc_macro2::Ident, error_name: &proc_macro2::Ident,
1387 generics: &std::collections::HashSet<&proc_macro2::Ident>,
1388 impl_generics: &syn::ImplGenerics, impl_generics_lifetime: &syn::ImplGenerics, ty_generics: &syn::TypeGenerics, where_clause: Option<&syn::WhereClause>,
1389 ) -> Option<proc_macro2::TokenStream> {
1390 let variant_ident = &self.variant_ident;
1391
1392 match self.link_type {
1393 LinkType::Msg => Some(quote! {
1394 impl #impl_generics_lifetime From<&'__a str> for #error_name #ty_generics #where_clause {
1395 fn from(s: &'__a str) -> Self { Self::from_kind(s.into()) }
1396 }
1397
1398 impl #impl_generics From<String> for #error_name #ty_generics #where_clause {
1399 fn from(s: String) -> Self { Self::from_kind(s.into()) }
1400 }
1401 }),
1402
1403 LinkType::Chainable(ref error_ty, _) => Some(quote! {
1404 impl #impl_generics From<#error_ty> for #error_name #ty_generics #where_clause {
1405 fn from(err: #error_ty) -> Self {
1406 #error_name(#error_kind_name::#variant_ident(err.0), err.1)
1407 }
1408 }
1409 }),
1410
1411 LinkType::Foreign(syn::Type::Path(syn::TypePath { ref path, .. }))
1414 if !path.global() && path.segments.len() == 1 && generics.contains(&path.segments[0].ident) => None,
1415
1416 LinkType::Foreign(ref ty) => Some(quote! {
1417 impl #impl_generics From<#ty> for #error_name #ty_generics #where_clause {
1418 fn from(err: #ty) -> Self {
1419 Self::from_kind(#error_kind_name::#variant_ident(err))
1420 }
1421 }
1422 }),
1423
1424 LinkType::Custom => None,
1425 }
1426 }
1427
1428 fn chained_error_extract_backtrace_case(&self) -> Option<proc_macro2::TokenStream> {
1429 match self.link_type {
1430 LinkType::Chainable(ref error_ty, _) => Some(quote! {
1431 if let Some(err) = err.downcast_ref::<#error_ty>() {
1432 return err.1.backtrace.clone();
1433 }
1434 }),
1435
1436 LinkType::Msg |
1437 LinkType::Foreign(_) |
1438 LinkType::Custom => None,
1439 }
1440 }
1441}
1442
1443enum CustomFormatter {
1444 FormatString { format_string: String, pattern: proc_macro2::TokenStream, args: proc_macro2::TokenStream },
1445 Expr(syn::Expr),
1446}
1447
1448impl CustomFormatter {
1449 fn parse(tokens: proc_macro2::TokenStream, attr_name: &str, variant_ident: &proc_macro2::Ident, variant_fields: &syn::Fields) -> Self {
1450 let err = match syn::parse(tokens.clone().into()) {
1451 Ok(expr) => return CustomFormatter::Expr(expr),
1452 Err(err) => err,
1453 };
1454
1455 let mut tts = tokens.into_iter();
1456
1457 match tts.next() {
1458 Some(proc_macro2::TokenTree::Ident(ref ident)) if ident == "const" => (),
1459
1460 Some(tt) => panic!(
1461 "Could not parse `{}` attribute of member {}. Expression - {}. Format string - expected `const` but got {}",
1462 attr_name, variant_ident, err, tt),
1463
1464 _ => panic!(
1465 "Could not parse `{}` attribute of member {}. Expression - {}. Format string - expected `const`",
1466 attr_name, variant_ident, err),
1467 }
1468
1469 let value = match tts.next() {
1470 Some(proc_macro2::TokenTree::Group(ref group)) if group.delimiter() == proc_macro2::Delimiter::Parenthesis => group.stream(),
1471
1472 Some(tt) => panic!(
1473 "Could not parse `{}` attribute of member {} - expected `(string literal)` but got {}",
1474 attr_name, variant_ident, tt),
1475
1476 _ => panic!(
1477 "Could not parse `{}` attribute of member {} - expected `(string literal)`",
1478 attr_name, variant_ident),
1479 };
1480
1481 let format_string = match syn::parse2(value) {
1482 Ok(syn::Lit::Str(value)) => value.value(),
1483
1484 Ok(lit) => panic!(
1485 "Could not parse `{}` attribute of member {} - expected string literal but got {}",
1486 attr_name, variant_ident, quote!(#lit).to_string()),
1487
1488 Err(err) => panic!(
1489 "Could not parse `{}` attribute of member {} - {}",
1490 attr_name, variant_ident, err),
1491 };
1492
1493 if let Some(tt) = tts.next() {
1494 panic!(
1495 "Could not parse `{}` attribute of member {} - unexpected token {} after string literal",
1496 attr_name, variant_ident, tt);
1497 }
1498
1499 match *variant_fields {
1500 syn::Fields::Named(syn::FieldsNamed { ref named, .. }) => {
1501 let referenced_names = get_parameter_names(&format_string).unwrap_or_else(|err| panic!(
1502 "Could not parse `{}` attribute of member {} - {}",
1503 attr_name, variant_ident, err));
1504
1505 let (patterns, args): (Vec<_>, Vec<_>) = named.into_iter().map(|f| {
1506 let field_name = f.ident.as_ref().unwrap();
1507 if referenced_names.contains(field_name) {
1508 (quote!(ref #field_name), quote!(#field_name = #field_name,))
1509 }
1510 else {
1511 let ignored_field_name = proc_macro2::Ident::new(&format!("_{}", field_name), proc_macro2::Span::call_site());
1512 (quote!(#field_name: ref #ignored_field_name), quote!())
1513 }
1514 }).unzip();
1515
1516 CustomFormatter::FormatString {
1517 format_string,
1518 pattern: quote!({ #(#patterns,)* }),
1519 args: quote!(#(#args)*),
1520 }
1521 },
1522
1523 syn::Fields::Unnamed(syn::FieldsUnnamed { ref unnamed, .. }) => {
1524 let referenced_positions = get_parameter_positions(&format_string).unwrap_or_else(|err| panic!(
1525 "Could not parse `{}` attribute of member {} - {}",
1526 attr_name, variant_ident, err));
1527
1528 let (patterns, args): (Vec<_>, Vec<_>) = unnamed.into_iter().enumerate().map(|(i, _)| {
1529 if referenced_positions.contains(&i) {
1530 let field_name = proc_macro2::Ident::new(&format!("value{}", i), proc_macro2::Span::call_site());
1531 (quote!(ref #field_name), quote!(#field_name,))
1532 }
1533 else {
1534 (quote!(_), quote!())
1535 }
1536 }).unzip();
1537
1538 CustomFormatter::FormatString {
1539 format_string,
1540 pattern: quote!((#(#patterns,)*)),
1541 args: quote!(#(#args)*),
1542 }
1543 },
1544
1545 syn::Fields::Unit => {
1546 ensure_no_parameters(&format_string).unwrap_or_else(|err| panic!(
1547 "Could not parse `{}` attribute of member {} - {}",
1548 attr_name, variant_ident, err));
1549
1550 CustomFormatter::FormatString {
1551 format_string,
1552 pattern: quote!(),
1553 args: quote!(),
1554 }
1555 },
1556 }
1557 }
1558}
1559
1560fn is_error_chain_attribute(attr: &syn::Attribute) -> bool {
1561 if !attr.path.global() && attr.path.segments.len() == 1 {
1562 let segment = &attr.path.segments[0];
1563 return segment.ident == "error_chain" && segment.arguments.is_empty();
1564 }
1565
1566 false
1567}
1568
1569fn is_closure(expr: &syn::Expr) -> bool {
1570 if let syn::Expr::Closure(..) = *expr {
1571 true
1572 }
1573 else {
1574 false
1575 }
1576}
1577
1578fn fields_pattern(variant_fields: &syn::Fields) -> proc_macro2::TokenStream {
1579 match *variant_fields {
1580 syn::Fields::Named(syn::FieldsNamed { ref named, .. }) => {
1581 let fields = named.into_iter().map(|f| {
1582 let field_name = f.ident.as_ref().unwrap();
1583 quote!(ref #field_name)
1584 });
1585 quote!({ #(#fields,)* })
1586 },
1587
1588 syn::Fields::Unnamed(syn::FieldsUnnamed { ref unnamed, .. }) => {
1589 let fields = unnamed.into_iter().enumerate().map(|(i, _)| {
1590 let field_name = proc_macro2::Ident::new(&format!("value{}", i), proc_macro2::Span::call_site());
1591 quote!(ref #field_name)
1592 });
1593 quote!((#(#fields,)*))
1594 },
1595
1596 syn::Fields::Unit => quote!(),
1597 }
1598}
1599
1600fn fields_pattern_ignore(variant_fields: &syn::Fields) -> proc_macro2::TokenStream {
1601 match *variant_fields {
1602 syn::Fields::Named(syn::FieldsNamed { .. }) => quote!({ .. }),
1603 syn::Fields::Unnamed(_) => quote!((..)),
1604 syn::Fields::Unit => quote!(),
1605 }
1606}
1607
1608fn args(variant_fields: &syn::Fields) -> proc_macro2::TokenStream {
1609 match *variant_fields {
1610 syn::Fields::Named(syn::FieldsNamed { ref named, .. }) => {
1611 let fields = named.into_iter().map(|f| {
1612 let field_name = f.ident.as_ref().unwrap();
1613 quote!(#field_name)
1614 });
1615 quote!(#(#fields,)*)
1616 },
1617
1618 syn::Fields::Unnamed(syn::FieldsUnnamed { ref unnamed, .. }) => {
1619 let fields = unnamed.into_iter().enumerate().map(|(i, _)| {
1620 let field_name = proc_macro2::Ident::new(&format!("value{}", i), proc_macro2::Span::call_site());
1621 quote!(#field_name)
1622 });
1623 quote!(#(#fields,)*)
1624 },
1625
1626 syn::Fields::Unit => quote!(),
1627 }
1628}
1629
1630fn get_parameter_names(format_string: &str) -> Result<std::collections::HashSet<proc_macro2::Ident>, String> {
1631 let parser = syntex_fmt_macros::Parser::new(format_string);
1632
1633 parser
1634 .filter_map(|piece| match piece {
1635 syntex_fmt_macros::Piece::String(_) => None,
1636
1637 syntex_fmt_macros::Piece::NextArgument(syntex_fmt_macros::Argument { position, .. }) => match position {
1638 syntex_fmt_macros::Position::ArgumentNext => Some(Err("expected named parameter but found `{}`".to_string())),
1639 syntex_fmt_macros::Position::ArgumentIs(index) => Some(Err(format!("expected named parameter but found `{{{}}}`", index))),
1640 syntex_fmt_macros::Position::ArgumentNamed(name) => Some(syn::parse_str(name).map_err(|err| format!("could not parse named parameter `{{{}}}` - {}", name, err))),
1641 },
1642 })
1643 .collect()
1644}
1645
1646fn get_parameter_positions(format_string: &str) -> Result<std::collections::HashSet<usize>, String> {
1647 let parser = syntex_fmt_macros::Parser::new(format_string);
1648
1649 parser
1650 .filter_map(|piece| match piece {
1651 syntex_fmt_macros::Piece::String(_) => None,
1652
1653 syntex_fmt_macros::Piece::NextArgument(syntex_fmt_macros::Argument { position, .. }) => match position {
1654 syntex_fmt_macros::Position::ArgumentNext => Some(Err("expected positional parameter but found `{}`".to_string())),
1655 syntex_fmt_macros::Position::ArgumentIs(index) => Some(Ok(index)),
1656 syntex_fmt_macros::Position::ArgumentNamed(name) => Some(Err(format!("expected positional parameter but found `{{{}}}`", name))),
1657 },
1658 })
1659 .collect()
1660}
1661
1662fn ensure_no_parameters(format_string: &str) -> Result<(), String> {
1663 let parser = syntex_fmt_macros::Parser::new(format_string);
1664
1665 for piece in parser {
1666 match piece {
1667 syntex_fmt_macros::Piece::String(_) => (),
1668
1669 syntex_fmt_macros::Piece::NextArgument(syntex_fmt_macros::Argument { position, .. }) => match position {
1670 syntex_fmt_macros::Position::ArgumentNext => return Err("expected no parameters but found `{}`".to_string()),
1671 syntex_fmt_macros::Position::ArgumentIs(index) => return Err(format!("expected no parameters but found `{{{}}}`", index)),
1672 syntex_fmt_macros::Position::ArgumentNamed(name) => return Err(format!("expected no parameters but found `{{{}}}`", name)),
1673 },
1674 }
1675 }
1676
1677 Ok(())
1678}