diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-05-10 07:02:48 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-05-10 07:02:48 +0000 |
commit | 16f5cd8cf0973f825ecc8580b810866134ec51c2 (patch) | |
tree | 398605ee28fd81fa44e6e7612f97ec4b5a3cedfa | |
parent | 5c9f86bcc640cc6face7d5dc55896cdff801a9ee (diff) | |
parent | 9396130f4e0bd8ec203e3d37b38120572c569f86 (diff) | |
download | thiserror-impl-android13-mainline-resolv-release.tar.gz |
Snap for 8564071 from 9396130f4e0bd8ec203e3d37b38120572c569f86 to mainline-resolv-releaseaml_res_331820000aml_res_331611010aml_res_331512000aml_res_331314010aml_res_331114000aml_res_331011050aml_res_330910000aml_res_330810000android13-mainline-resolv-release
Change-Id: I05d9e0050b8ff0389b6650ac4211db7bb0447553
-rw-r--r-- | .cargo_vcs_info.json | 7 | ||||
-rw-r--r-- | Android.bp | 10 | ||||
-rw-r--r-- | Cargo.toml | 12 | ||||
-rw-r--r-- | Cargo.toml.orig | 3 | ||||
-rw-r--r-- | METADATA | 10 | ||||
-rw-r--r-- | TEST_MAPPING | 125 | ||||
-rw-r--r-- | cargo2android.json | 4 | ||||
-rw-r--r-- | src/ast.rs | 28 | ||||
-rw-r--r-- | src/attr.rs | 23 | ||||
-rw-r--r-- | src/expand.rs | 148 | ||||
-rw-r--r-- | src/fmt.rs | 33 | ||||
-rw-r--r-- | src/generics.rs | 82 | ||||
-rw-r--r-- | src/lib.rs | 2 | ||||
-rw-r--r-- | src/prop.rs | 34 | ||||
-rw-r--r-- | src/valid.rs | 2 |
15 files changed, 448 insertions, 75 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json index 349cbda..2d04484 100644 --- a/.cargo_vcs_info.json +++ b/.cargo_vcs_info.json @@ -1,5 +1,6 @@ { "git": { - "sha1": "1b0a84996b9492c0dc5779127a91c930f23a259e" - } -} + "sha1": "672e9525bbc2e5682c380d36974f34716b963591" + }, + "path_in_vcs": "impl" +}
\ No newline at end of file @@ -1,4 +1,4 @@ -// This file is generated by cargo2android.py --run --dependencies --device. +// This file is generated by cargo2android.py --config cargo2android.json. // Do not modify this file as changes will be overridden on upgrade. package { @@ -42,6 +42,8 @@ license { rust_proc_macro { name: "libthiserror_impl", crate_name: "thiserror_impl", + cargo_env_compat: true, + cargo_pkg_version: "1.0.30", srcs: ["src/lib.rs"], edition: "2018", rustlibs: [ @@ -50,9 +52,3 @@ rust_proc_macro { "libsyn", ], } - -// dependent_library ["feature_list"] -// proc-macro2-1.0.24 "default,proc-macro" -// quote-1.0.9 "default,proc-macro" -// syn-1.0.60 "clone-impls,default,derive,parsing,printing,proc-macro,quote" -// unicode-xid-0.2.1 "default" @@ -3,17 +3,17 @@ # 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 +# 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) +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. [package] edition = "2018" +rust-version = "1.31" name = "thiserror-impl" -version = "1.0.24" +version = "1.0.30" authors = ["David Tolnay <dtolnay@gmail.com>"] description = "Implementation detail of the `thiserror` crate" license = "MIT OR Apache-2.0" diff --git a/Cargo.toml.orig b/Cargo.toml.orig index 3963fe2..e2ad11b 100644 --- a/Cargo.toml.orig +++ b/Cargo.toml.orig @@ -1,8 +1,9 @@ [package] name = "thiserror-impl" -version = "1.0.24" +version = "1.0.30" authors = ["David Tolnay <dtolnay@gmail.com>"] edition = "2018" +rust-version = "1.31" license = "MIT OR Apache-2.0" description = "Implementation detail of the `thiserror` crate" repository = "https://github.com/dtolnay/thiserror" @@ -7,13 +7,13 @@ third_party { } url { type: ARCHIVE - value: "https://static.crates.io/crates/thiserror-impl/thiserror-impl-1.0.24.crate" + value: "https://static.crates.io/crates/thiserror-impl/thiserror-impl-1.0.30.crate" } - version: "1.0.24" + version: "1.0.30" license_type: NOTICE last_upgrade_date { - year: 2021 - month: 2 - day: 18 + year: 2022 + month: 3 + day: 1 } } diff --git a/TEST_MAPPING b/TEST_MAPPING index 743367a..eea7991 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -1,53 +1,150 @@ // Generated by update_crate_tests.py for tests that depend on this crate. { + "imports": [ + { + "path": "external/rust/crates/anyhow" + }, + { + "path": "external/rust/crates/jni" + }, + { + "path": "external/rust/crates/serde-xml-rs" + } + ], "presubmit": [ { - "name": "anyhow_device_test_tests_test_autotrait" + "name": "ZipFuseTest" }, { - "name": "keystore2_selinux_test" + "name": "apkdmverity.test" + }, + { + "name": "authfs_device_test_src_lib" + }, + { + "name": "diced_open_dice_cbor_test" + }, + { + "name": "diced_sample_inputs_test" + }, + { + "name": "diced_test" + }, + { + "name": "diced_utils_test" }, { - "name": "anyhow_device_test_tests_test_repr" + "name": "diced_vendor_test" }, { - "name": "anyhow_device_test_tests_test_convert" + "name": "doh_unit_test" }, { - "name": "anyhow_device_test_tests_test_context" + "name": "keystore2_crypto_test_rust" }, { - "name": "anyhow_device_test_tests_test_boxed" + "name": "keystore2_selinux_concurrency_test" }, { - "name": "anyhow_device_test_tests_test_downcast" + "name": "keystore2_selinux_test" }, { "name": "keystore2_test" }, { - "name": "anyhow_device_test_tests_test_source" + "name": "keystore2_test_utils_test" }, { - "name": "anyhow_device_test_src_lib" + "name": "keystore2_vintf_test" }, { - "name": "anyhow_device_test_tests_test_fmt" + "name": "legacykeystore_test" }, { - "name": "anyhow_device_test_tests_test_ffi" + "name": "libapkverify.integration_test" }, { - "name": "anyhow_device_test_tests_test_chain" + "name": "libapkverify.test" }, { - "name": "libsqlite3-sys_device_test_src_lib" + "name": "libcert_request_validator_tests" + }, + { + "name": "librustutils_test" + }, + { + "name": "microdroid_manager_test" + }, + { + "name": "virtualizationservice_device_test" + } + ], + "presubmit-rust": [ + { + "name": "ZipFuseTest" + }, + { + "name": "apkdmverity.test" + }, + { + "name": "authfs_device_test_src_lib" + }, + { + "name": "diced_open_dice_cbor_test" + }, + { + "name": "diced_sample_inputs_test" + }, + { + "name": "diced_test" + }, + { + "name": "diced_utils_test" + }, + { + "name": "diced_vendor_test" + }, + { + "name": "doh_unit_test" }, { "name": "keystore2_crypto_test_rust" }, { - "name": "anyhow_device_test_tests_test_macros" + "name": "keystore2_selinux_concurrency_test" + }, + { + "name": "keystore2_selinux_test" + }, + { + "name": "keystore2_test" + }, + { + "name": "keystore2_test_utils_test" + }, + { + "name": "keystore2_vintf_test" + }, + { + "name": "legacykeystore_test" + }, + { + "name": "libapkverify.integration_test" + }, + { + "name": "libapkverify.test" + }, + { + "name": "libcert_request_validator_tests" + }, + { + "name": "librustutils_test" + }, + { + "name": "microdroid_manager_test" + }, + { + "name": "virtualizationservice_device_test" } ] } diff --git a/cargo2android.json b/cargo2android.json new file mode 100644 index 0000000..bf78496 --- /dev/null +++ b/cargo2android.json @@ -0,0 +1,4 @@ +{ + "device": true, + "run": true +}
\ No newline at end of file @@ -1,4 +1,5 @@ use crate::attr::{self, Attrs}; +use crate::generics::ParamsInScope; use proc_macro2::Span; use syn::{ Data, DataEnum, DataStruct, DeriveInput, Error, Fields, Generics, Ident, Index, Member, Result, @@ -38,6 +39,7 @@ pub struct Field<'a> { pub attrs: Attrs<'a>, pub member: Member, pub ty: &'a Type, + pub contains_generic: bool, } impl<'a> Input<'a> { @@ -56,8 +58,9 @@ impl<'a> Input<'a> { impl<'a> Struct<'a> { fn from_syn(node: &'a DeriveInput, data: &'a DataStruct) -> Result<Self> { let mut attrs = attr::get(&node.attrs)?; + let scope = ParamsInScope::new(&node.generics); let span = attrs.span().unwrap_or_else(Span::call_site); - let fields = Field::multiple_from_syn(&data.fields, span)?; + let fields = Field::multiple_from_syn(&data.fields, &scope, span)?; if let Some(display) = &mut attrs.display { display.expand_shorthand(&fields); } @@ -74,12 +77,13 @@ impl<'a> Struct<'a> { impl<'a> Enum<'a> { fn from_syn(node: &'a DeriveInput, data: &'a DataEnum) -> Result<Self> { let attrs = attr::get(&node.attrs)?; + let scope = ParamsInScope::new(&node.generics); let span = attrs.span().unwrap_or_else(Span::call_site); let variants = data .variants .iter() .map(|node| { - let mut variant = Variant::from_syn(node, span)?; + let mut variant = Variant::from_syn(node, &scope, span)?; if let display @ None = &mut variant.attrs.display { *display = attrs.display.clone(); } @@ -102,28 +106,37 @@ impl<'a> Enum<'a> { } impl<'a> Variant<'a> { - fn from_syn(node: &'a syn::Variant, span: Span) -> Result<Self> { + fn from_syn(node: &'a syn::Variant, scope: &ParamsInScope<'a>, span: Span) -> Result<Self> { let attrs = attr::get(&node.attrs)?; let span = attrs.span().unwrap_or(span); Ok(Variant { original: node, attrs, ident: node.ident.clone(), - fields: Field::multiple_from_syn(&node.fields, span)?, + fields: Field::multiple_from_syn(&node.fields, scope, span)?, }) } } impl<'a> Field<'a> { - fn multiple_from_syn(fields: &'a Fields, span: Span) -> Result<Vec<Self>> { + fn multiple_from_syn( + fields: &'a Fields, + scope: &ParamsInScope<'a>, + span: Span, + ) -> Result<Vec<Self>> { fields .iter() .enumerate() - .map(|(i, field)| Field::from_syn(i, field, span)) + .map(|(i, field)| Field::from_syn(i, field, scope, span)) .collect() } - fn from_syn(i: usize, node: &'a syn::Field, span: Span) -> Result<Self> { + fn from_syn( + i: usize, + node: &'a syn::Field, + scope: &ParamsInScope<'a>, + span: Span, + ) -> Result<Self> { Ok(Field { original: node, attrs: attr::get(&node.attrs)?, @@ -134,6 +147,7 @@ impl<'a> Field<'a> { }) }), ty: &node.ty, + contains_generic: scope.intersects(&node.ty), }) } } diff --git a/src/attr.rs b/src/attr.rs index 1ab1e28..9963fd6 100644 --- a/src/attr.rs +++ b/src/attr.rs @@ -1,5 +1,6 @@ use proc_macro2::{Delimiter, Group, Span, TokenStream, TokenTree}; use quote::{format_ident, quote, ToTokens}; +use std::collections::BTreeSet as Set; use std::iter::FromIterator; use syn::parse::{Nothing, ParseStream}; use syn::{ @@ -21,6 +22,7 @@ pub struct Display<'a> { pub fmt: LitStr, pub args: TokenStream, pub has_bonus_display: bool, + pub implied_bounds: Set<(usize, Trait)>, } #[derive(Copy, Clone)] @@ -29,6 +31,19 @@ pub struct Transparent<'a> { pub span: Span, } +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] +pub enum Trait { + Debug, + Display, + Octal, + LowerHex, + UpperHex, + Pointer, + Binary, + LowerExp, + UpperExp, +} + pub fn get(input: &[Attribute]) -> Result<Attrs> { let mut attrs = Attrs { display: None, @@ -91,6 +106,7 @@ fn parse_error_attribute<'a>(attrs: &mut Attrs<'a>, attr: &'a Attribute) -> Resu fmt: input.parse()?, args: parse_token_expr(input, false)?, has_bonus_display: false, + implied_bounds: Set::new(), }; if attrs.display.is_some() { return Err(Error::new_spanned( @@ -188,3 +204,10 @@ impl ToTokens for Display<'_> { }); } } + +impl ToTokens for Trait { + fn to_tokens(&self, tokens: &mut TokenStream) { + let trait_name = format_ident!("{}", format!("{:?}", self)); + tokens.extend(quote!(std::fmt::#trait_name)); + } +} diff --git a/src/expand.rs b/src/expand.rs index fa85cbb..435ad48 100644 --- a/src/expand.rs +++ b/src/expand.rs @@ -1,8 +1,13 @@ use crate::ast::{Enum, Field, Input, Struct}; +use crate::attr::Trait; +use crate::generics::InferredBounds; use proc_macro2::TokenStream; use quote::{format_ident, quote, quote_spanned, ToTokens}; +use std::collections::BTreeSet as Set; use syn::spanned::Spanned; -use syn::{Data, DeriveInput, Member, PathArguments, Result, Type, Visibility}; +use syn::{ + Data, DeriveInput, GenericArgument, Member, PathArguments, Result, Token, Type, Visibility, +}; pub fn derive(node: &DeriveInput) -> Result<TokenStream> { let input = Input::from_syn(node)?; @@ -16,14 +21,23 @@ pub fn derive(node: &DeriveInput) -> Result<TokenStream> { fn impl_struct(input: Struct) -> TokenStream { let ty = &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 input.attrs.transparent.is_some() { - let only_field = &input.fields[0].member; + let only_field = &input.fields[0]; + if only_field.contains_generic { + error_inferred_bounds.insert(only_field.ty, quote!(std::error::Error)); + } + let member = &only_field.member; Some(quote! { - std::error::Error::source(self.#only_field.as_dyn_error()) + std::error::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!(std::error::Error + 'static)); + } let asref = if type_is_option(source_field.ty) { Some(quote_spanned!(source.span()=> .as_ref()?)) } else { @@ -58,7 +72,9 @@ fn impl_struct(input: Struct) -> TokenStream { self.#source.as_dyn_error().backtrace() } }; - let combinator = if type_is_option(backtrace_field.ty) { + let combinator = if source == backtrace { + source_backtrace + } else if type_is_option(backtrace_field.ty) { quote! { #source_backtrace.or(self.#backtrace.as_ref()) } @@ -87,12 +103,15 @@ fn impl_struct(input: Struct) -> TokenStream { } }); + 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! { std::fmt::Display::fmt(&self.#only_field, __formatter) }) } else if let Some(display) = &input.attrs.display { + display_implied_bounds = display.implied_bounds.clone(); let use_as_display = if display.has_bonus_display { Some(quote! { #[allow(unused_imports)] @@ -112,9 +131,17 @@ fn impl_struct(input: Struct) -> TokenStream { 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)] - impl #impl_generics std::fmt::Display for #ty #ty_generics #where_clause { + impl #impl_generics std::fmt::Display for #ty #ty_generics #display_where_clause { #[allow(clippy::used_underscore_binding)] fn fmt(&self, __formatter: &mut std::fmt::Formatter) -> std::fmt::Result { #body @@ -124,8 +151,8 @@ fn impl_struct(input: Struct) -> TokenStream { }); let from_impl = input.from_field().map(|from_field| { - let backtrace_field = input.backtrace_field(); - let from = from_field.ty; + let backtrace_field = input.distinct_backtrace_field(); + let from = unoptional_type(from_field.ty); let body = from_initializer(from_field, backtrace_field); quote! { #[allow(unused_qualifications)] @@ -139,10 +166,16 @@ fn impl_struct(input: Struct) -> TokenStream { }); let error_trait = spanned_error_trait(input.original); + 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)] - impl #impl_generics #error_trait for #ty #ty_generics #where_clause { + impl #impl_generics #error_trait for #ty #ty_generics #error_where_clause { #source_method #backtrace_method } @@ -154,18 +187,27 @@ fn impl_struct(input: Struct) -> TokenStream { fn impl_enum(input: Enum) -> TokenStream { let ty = &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 variant.attrs.transparent.is_some() { - let only_field = &variant.fields[0].member; + let only_field = &variant.fields[0]; + if only_field.contains_generic { + error_inferred_bounds.insert(only_field.ty, quote!(std::error::Error)); + } + let member = &only_field.member; let source = quote!(std::error::Error::source(transparent.as_dyn_error())); quote! { - #ty::#ident {#only_field: transparent} => #source, + #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!(std::error::Error + 'static)); + } let asref = if type_is_option(source_field.ty) { Some(quote_spanned!(source.span()=> .as_ref()?)) } else { @@ -234,6 +276,27 @@ fn impl_enum(input: Enum) -> TokenStream { } } } + (Some(backtrace_field), Some(source_field)) + if backtrace_field.member == source_field.member => + { + let backtrace = &backtrace_field.member; + let varsource = quote!(source); + let source_backtrace = if type_is_option(source_field.ty) { + quote_spanned! {backtrace.span()=> + #varsource.as_ref().and_then(|source| source.as_dyn_error().backtrace()) + } + } else { + quote_spanned! {backtrace.span()=> + #varsource.as_dyn_error().backtrace() + } + }; + quote! { + #ty::#ident {#backtrace: #varsource, ..} => { + use thiserror::private::AsDynError; + #source_backtrace + } + } + } (Some(backtrace_field), _) => { let backtrace = &backtrace_field.member; let body = if type_is_option(backtrace_field.ty) { @@ -263,6 +326,7 @@ fn impl_enum(input: Enum) -> TokenStream { }; let display_impl = if input.has_display() { + let mut display_inferred_bounds = InferredBounds::new(); let use_as_display = if input.variants.iter().any(|v| { v.attrs .display @@ -282,25 +346,38 @@ fn impl_enum(input: Enum) -> TokenStream { None }; let arms = input.variants.iter().map(|variant| { + let mut display_implied_bounds = Set::new(); let display = match &variant.attrs.display { - Some(display) => display.to_token_stream(), + Some(display) => { + display_implied_bounds = display.implied_bounds.clone(); + display.to_token_stream() + } None => { let only_field = match &variant.fields[0].member { Member::Named(ident) => ident.clone(), Member::Unnamed(index) => format_ident!("_{}", index), }; + display_implied_bounds.insert((0, Trait::Display)); quote!(std::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)] - impl #impl_generics std::fmt::Display for #ty #ty_generics #where_clause { + impl #impl_generics std::fmt::Display for #ty #ty_generics #display_where_clause { fn fmt(&self, __formatter: &mut std::fmt::Formatter) -> std::fmt::Result { #use_as_display #[allow(unused_variables, deprecated, clippy::used_underscore_binding)] @@ -316,9 +393,9 @@ fn impl_enum(input: Enum) -> TokenStream { let from_impls = input.variants.iter().filter_map(|variant| { let from_field = variant.from_field()?; - let backtrace_field = variant.backtrace_field(); + let backtrace_field = variant.distinct_backtrace_field(); let variant = &variant.ident; - let from = from_field.ty; + let from = unoptional_type(from_field.ty); let body = from_initializer(from_field, backtrace_field); Some(quote! { #[allow(unused_qualifications)] @@ -332,10 +409,16 @@ fn impl_enum(input: Enum) -> TokenStream { }); let error_trait = spanned_error_trait(input.original); + 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)] - impl #impl_generics #error_trait for #ty #ty_generics #where_clause { + impl #impl_generics #error_trait for #ty #ty_generics #error_where_clause { #source_method #backtrace_method } @@ -361,6 +444,11 @@ fn fields_pat(fields: &[Field]) -> TokenStream { fn from_initializer(from_field: &Field, backtrace_field: Option<&Field>) -> TokenStream { let from_member = &from_field.member; + let some_source = if type_is_option(from_field.ty) { + quote!(std::option::Option::Some(source)) + } else { + quote!(source) + }; let backtrace = backtrace_field.map(|backtrace_field| { let backtrace_member = &backtrace_field.member; if type_is_option(backtrace_field.ty) { @@ -374,25 +462,43 @@ fn from_initializer(from_field: &Field, backtrace_field: Option<&Field>) -> Toke } }); quote!({ - #from_member: source, + #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 false, + _ => return None, }; let last = path.segments.last().unwrap(); if last.ident != "Option" { - return false; + return None; + } + + let bracketed = match &last.arguments { + PathArguments::AngleBracketed(bracketed) => bracketed, + _ => return None, + }; + + if bracketed.args.len() != 1 { + return None; } - match &last.arguments { - PathArguments::AngleBracketed(bracketed) => bracketed.args.len() == 1, - _ => false, + match &bracketed.args[0] { + GenericArgument::Type(arg) => Some(arg), + _ => None, } } @@ -1,8 +1,8 @@ use crate::ast::Field; -use crate::attr::Display; +use crate::attr::{Display, Trait}; use proc_macro2::TokenTree; use quote::{format_ident, quote_spanned}; -use std::collections::HashSet as Set; +use std::collections::{BTreeSet as Set, HashMap as Map}; use syn::ext::IdentExt; use syn::parse::{ParseStream, Parser}; use syn::{Ident, Index, LitStr, Member, Result, Token}; @@ -12,7 +12,10 @@ impl Display<'_> { 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<Member> = fields.iter().map(|f| f.member.clone()).collect(); + let mut member_index = Map::new(); + for (i, field) in fields.iter().enumerate() { + member_index.insert(&field.member, i); + } let span = self.fmt.span(); let fmt = self.fmt.value(); @@ -20,6 +23,7 @@ impl Display<'_> { let mut out = String::new(); let mut args = self.args.clone(); let mut has_bonus_display = false; + let mut implied_bounds = Set::new(); let mut has_trailing_comma = false; if let Some(TokenTree::Punct(punct)) = args.clone().into_iter().last() { @@ -47,7 +51,7 @@ impl Display<'_> { Ok(index) => Member::Unnamed(Index { index, span }), Err(_) => return, }; - if !fields.contains(&member) { + if !member_index.contains_key(&member) { out += ∫ continue; } @@ -60,6 +64,24 @@ impl Display<'_> { } _ => continue, }; + if let Some(&field) = member_index.get(&member) { + let end_spec = match read.find('}') { + Some(end_spec) => end_spec, + None => return, + }; + let bound = match read[..end_spec].chars().next_back() { + Some('?') => Trait::Debug, + Some('o') => Trait::Octal, + Some('x') => Trait::LowerHex, + Some('X') => Trait::UpperHex, + Some('p') => Trait::Pointer, + Some('b') => Trait::Binary, + Some('e') => Trait::LowerExp, + Some('E') => Trait::UpperExp, + Some(_) | None => Trait::Display, + }; + implied_bounds.insert((field, bound)); + } let local = match &member { Member::Unnamed(index) => format_ident!("_{}", index), Member::Named(ident) => ident.clone(), @@ -82,7 +104,7 @@ impl Display<'_> { args.extend(quote_spanned!(span=> ,)); } args.extend(quote_spanned!(span=> #formatvar = #local)); - if read.starts_with('}') && fields.contains(&member) { + if read.starts_with('}') && member_index.contains_key(&member) { has_bonus_display = true; args.extend(quote_spanned!(span=> .as_display())); } @@ -93,6 +115,7 @@ impl Display<'_> { self.fmt = LitStr::new(&out, self.fmt.span()); self.args = args; self.has_bonus_display = has_bonus_display; + self.implied_bounds = implied_bounds; } } diff --git a/src/generics.rs b/src/generics.rs new file mode 100644 index 0000000..254c2ed --- /dev/null +++ b/src/generics.rs @@ -0,0 +1,82 @@ +use proc_macro2::TokenStream; +use quote::ToTokens; +use std::collections::btree_map::Entry; +use std::collections::{BTreeMap as Map, BTreeSet as Set}; +use syn::punctuated::Punctuated; +use syn::{parse_quote, GenericArgument, Generics, Ident, PathArguments, Token, Type, WhereClause}; + +pub struct ParamsInScope<'a> { + names: Set<&'a Ident>, +} + +impl<'a> ParamsInScope<'a> { + pub fn new(generics: &'a Generics) -> Self { + ParamsInScope { + names: generics.type_params().map(|param| ¶m.ident).collect(), + } + } + + pub fn intersects(&self, ty: &Type) -> bool { + let mut found = false; + crawl(self, ty, &mut found); + found + } +} + +fn crawl(in_scope: &ParamsInScope, ty: &Type, found: &mut bool) { + if let Type::Path(ty) = ty { + if ty.qself.is_none() { + if let Some(ident) = ty.path.get_ident() { + if in_scope.names.contains(ident) { + *found = true; + } + } + } + for segment in &ty.path.segments { + if let PathArguments::AngleBracketed(arguments) = &segment.arguments { + for arg in &arguments.args { + if let GenericArgument::Type(ty) = arg { + crawl(in_scope, ty, found); + } + } + } + } + } +} + +pub struct InferredBounds { + bounds: Map<String, (Set<String>, Punctuated<TokenStream, Token![+]>)>, + order: Vec<TokenStream>, +} + +impl InferredBounds { + pub fn new() -> Self { + InferredBounds { + bounds: Map::new(), + order: Vec::new(), + } + } + + pub fn insert(&mut self, ty: impl ToTokens, bound: impl ToTokens) { + let ty = ty.to_token_stream(); + let bound = bound.to_token_stream(); + let entry = self.bounds.entry(ty.to_string()); + if let Entry::Vacant(_) = entry { + self.order.push(ty); + } + let (set, tokens) = entry.or_default(); + if set.insert(bound.to_string()) { + tokens.push(bound); + } + } + + pub fn augment_where_clause(&self, generics: &Generics) -> WhereClause { + let mut generics = generics.clone(); + let where_clause = generics.make_where_clause(); + for ty in &self.order { + let (_set, bounds) = &self.bounds[&ty.to_string()]; + where_clause.predicates.push(parse_quote!(#ty: #bounds)); + } + generics.where_clause.unwrap() + } +} @@ -1,6 +1,7 @@ #![allow( clippy::blocks_in_if_conditions, clippy::cast_possible_truncation, + clippy::manual_map, clippy::map_unwrap_or, clippy::needless_pass_by_value, clippy::option_if_let_else, @@ -15,6 +16,7 @@ mod ast; mod attr; mod expand; mod fmt; +mod generics; mod prop; mod valid; diff --git a/src/prop.rs b/src/prop.rs index e011848..6d8a924 100644 --- a/src/prop.rs +++ b/src/prop.rs @@ -13,6 +13,11 @@ impl Struct<'_> { pub(crate) fn backtrace_field(&self) -> Option<&Field> { backtrace_field(&self.fields) } + + pub(crate) fn distinct_backtrace_field(&self) -> Option<&Field> { + let backtrace_field = self.backtrace_field()?; + distinct_backtrace_field(backtrace_field, self.from_field()) + } } impl Enum<'_> { @@ -54,6 +59,11 @@ impl Variant<'_> { pub(crate) fn backtrace_field(&self) -> Option<&Field> { backtrace_field(&self.fields) } + + pub(crate) fn distinct_backtrace_field(&self) -> Option<&Field> { + let backtrace_field = self.backtrace_field()?; + distinct_backtrace_field(backtrace_field, self.from_field()) + } } impl Field<'_> { @@ -65,7 +75,7 @@ impl Field<'_> { 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); + return Some(field); } } None @@ -74,12 +84,12 @@ fn from_field<'a, 'b>(fields: &'a [Field<'b>]) -> Option<&'a Field<'b>> { 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); + return Some(field); } } for field in fields { match &field.member { - Member::Named(ident) if ident == "source" => return Some(&field), + Member::Named(ident) if ident == "source" => return Some(field), _ => {} } } @@ -89,17 +99,31 @@ fn source_field<'a, 'b>(fields: &'a [Field<'b>]) -> Option<&'a Field<'b>> { 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); + return Some(field); } } for field in fields { if field.is_backtrace() { - return Some(&field); + return Some(field); } } None } +// The #[backtrace] field, if it is not the same as the #[from] field. +fn distinct_backtrace_field<'a, 'b>( + backtrace_field: &'a Field<'b>, + from_field: Option<&Field>, +) -> Option<&'a Field<'b>> { + if from_field.map_or(false, |from_field| { + from_field.member == backtrace_field.member + }) { + None + } else { + Some(backtrace_field) + } +} + fn type_is_backtrace(ty: &Type) -> bool { let path = match ty { Type::Path(ty) => &ty.path, diff --git a/src/valid.rs b/src/valid.rs index 7246d0c..7657265 100644 --- a/src/valid.rs +++ b/src/valid.rs @@ -188,7 +188,7 @@ fn check_field_attrs(fields: &[Field]) -> Result<()> { } } if let Some(source_field) = source_field.or(from_field) { - if contains_non_static_lifetime(&source_field.ty) { + if contains_non_static_lifetime(source_field.ty) { return Err(Error::new_spanned( &source_field.original.ty, "non-static lifetimes are not allowed in the source of an error, because std::error::Error requires the source is dyn Error + 'static", |