From 8cdfa445d6629ffef4cb84967ff7017654045bc2 Mon Sep 17 00:00:00 2001 From: mo khan Date: Wed, 2 Jul 2025 18:36:06 -0600 Subject: chore: add vendor directory --- vendor/thiserror-impl/.cargo-checksum.json | 1 + vendor/thiserror-impl/Cargo.lock | 47 +++ vendor/thiserror-impl/Cargo.toml | 51 +++ vendor/thiserror-impl/LICENSE-APACHE | 176 +++++++++ vendor/thiserror-impl/LICENSE-MIT | 23 ++ vendor/thiserror-impl/src/ast.rs | 185 +++++++++ vendor/thiserror-impl/src/attr.rs | 358 ++++++++++++++++++ vendor/thiserror-impl/src/expand.rs | 583 +++++++++++++++++++++++++++++ vendor/thiserror-impl/src/fallback.rs | 32 ++ vendor/thiserror-impl/src/fmt.rs | 322 ++++++++++++++++ vendor/thiserror-impl/src/generics.rs | 83 ++++ vendor/thiserror-impl/src/lib.rs | 39 ++ vendor/thiserror-impl/src/prop.rs | 148 ++++++++ vendor/thiserror-impl/src/scan_expr.rs | 264 +++++++++++++ vendor/thiserror-impl/src/unraw.rs | 142 +++++++ vendor/thiserror-impl/src/valid.rs | 248 ++++++++++++ 16 files changed, 2702 insertions(+) create mode 100644 vendor/thiserror-impl/.cargo-checksum.json create mode 100644 vendor/thiserror-impl/Cargo.lock create mode 100644 vendor/thiserror-impl/Cargo.toml create mode 100644 vendor/thiserror-impl/LICENSE-APACHE create mode 100644 vendor/thiserror-impl/LICENSE-MIT create mode 100644 vendor/thiserror-impl/src/ast.rs create mode 100644 vendor/thiserror-impl/src/attr.rs create mode 100644 vendor/thiserror-impl/src/expand.rs create mode 100644 vendor/thiserror-impl/src/fallback.rs create mode 100644 vendor/thiserror-impl/src/fmt.rs create mode 100644 vendor/thiserror-impl/src/generics.rs create mode 100644 vendor/thiserror-impl/src/lib.rs create mode 100644 vendor/thiserror-impl/src/prop.rs create mode 100644 vendor/thiserror-impl/src/scan_expr.rs create mode 100644 vendor/thiserror-impl/src/unraw.rs create mode 100644 vendor/thiserror-impl/src/valid.rs (limited to 'vendor/thiserror-impl') diff --git a/vendor/thiserror-impl/.cargo-checksum.json b/vendor/thiserror-impl/.cargo-checksum.json new file mode 100644 index 00000000..4352adc3 --- /dev/null +++ b/vendor/thiserror-impl/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.lock":"a4ccfef62250083ba68dbfc29872a4c03a5a81a0b37d8b3523e5d6b566564672","Cargo.toml":"4cdb56333675df024c3c9980713aff1aa76f50aafb72dc87357c43a531058496","LICENSE-APACHE":"62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","src/ast.rs":"c717fffeb2923b2840d58fd02923a1926a48fa8336039f57a9e94f9275ae7ec6","src/attr.rs":"d3c7fa9e8269973f140e5382f753664ac32bee44c09875f678100b890aea4d06","src/expand.rs":"9169c10d622028ee6aab960e73a510a09ddc0a93817ee5850ef1ea64ccbddced","src/fallback.rs":"505c5a56a07a8a25bf533b44111e44a85e13b9dac787f24f2191e3d7389db446","src/fmt.rs":"e6391502143c44b9f04cdef9085a84392804bdde75d8b87d741fded7f7882c7a","src/generics.rs":"03134b3882790f657a11700e5fb0b0ca744214a47d609febaabc14926d2d7e03","src/lib.rs":"8ddbfd56670c3a508fd13f2f44d80782aac760f6a32db0a57af3c2c9ef27dd26","src/prop.rs":"82fb29bd85b0e1163c32dca2ff874e1dfbece7f681b986c6fe618f29e34bdd26","src/scan_expr.rs":"d46ae7a3eaaa6476553db3164676ec71aa82bcd8a2e4cad59deb07893c79f1c8","src/unraw.rs":"d4ee6048f7722160c3d61f69378d2c8d17068a319a8e6a33c6f25ea673e88312","src/valid.rs":"5a4e781773cc9a2f3129f620fa4f201f71fd3265b8315ca138239bec3bf618fb"},"package":"7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"} \ No newline at end of file diff --git a/vendor/thiserror-impl/Cargo.lock b/vendor/thiserror-impl/Cargo.lock new file mode 100644 index 00000000..17f093e2 --- /dev/null +++ b/vendor/thiserror-impl/Cargo.lock @@ -0,0 +1,47 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "proc-macro2" +version = "1.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "2.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.12" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe" diff --git a/vendor/thiserror-impl/Cargo.toml b/vendor/thiserror-impl/Cargo.toml new file mode 100644 index 00000000..28e890cc --- /dev/null +++ b/vendor/thiserror-impl/Cargo.toml @@ -0,0 +1,51 @@ +# 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 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 = "2021" +rust-version = "1.61" +name = "thiserror-impl" +version = "2.0.12" +authors = ["David Tolnay "] +build = false +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "Implementation detail of the `thiserror` crate" +readme = false +license = "MIT OR Apache-2.0" +repository = "https://github.com/dtolnay/thiserror" + +[package.metadata.docs.rs] +rustdoc-args = [ + "--generate-link-to-definition", + "--extern-html-root-url=core=https://doc.rust-lang.org", + "--extern-html-root-url=alloc=https://doc.rust-lang.org", + "--extern-html-root-url=std=https://doc.rust-lang.org", + "--extern-html-root-url=proc_macro=https://doc.rust-lang.org", +] +targets = ["x86_64-unknown-linux-gnu"] + +[lib] +name = "thiserror_impl" +path = "src/lib.rs" +proc-macro = true + +[dependencies.proc-macro2] +version = "1.0.74" + +[dependencies.quote] +version = "1.0.35" + +[dependencies.syn] +version = "2.0.87" diff --git a/vendor/thiserror-impl/LICENSE-APACHE b/vendor/thiserror-impl/LICENSE-APACHE new file mode 100644 index 00000000..1b5ec8b7 --- /dev/null +++ b/vendor/thiserror-impl/LICENSE-APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/vendor/thiserror-impl/LICENSE-MIT b/vendor/thiserror-impl/LICENSE-MIT new file mode 100644 index 00000000..31aa7938 --- /dev/null +++ b/vendor/thiserror-impl/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/vendor/thiserror-impl/src/ast.rs b/vendor/thiserror-impl/src/ast.rs new file mode 100644 index 00000000..77f95837 --- /dev/null +++ b/vendor/thiserror-impl/src/ast.rs @@ -0,0 +1,185 @@ +use crate::attr::{self, Attrs}; +use crate::generics::ParamsInScope; +use crate::unraw::{IdentUnraw, MemberUnraw}; +use proc_macro2::Span; +use std::fmt::{self, Display}; +use syn::{ + Data, DataEnum, DataStruct, DeriveInput, Error, Fields, Generics, Ident, Index, 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: MemberUnraw, + pub ty: &'a Type, + pub contains_generic: bool, +} + +#[derive(Copy, Clone)] +pub enum ContainerKind { + Struct, + TupleStruct, + UnitStruct, + StructVariant, + TupleVariant, + UnitVariant, +} + +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 scope = ParamsInScope::new(&node.generics); + let fields = Field::multiple_from_syn(&data.fields, &scope)?; + if let Some(display) = &mut attrs.display { + let container = ContainerKind::from_struct(data); + display.expand_shorthand(&fields, container)?; + } + 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 scope = ParamsInScope::new(&node.generics); + let variants = data + .variants + .iter() + .map(|node| { + let mut variant = Variant::from_syn(node, &scope)?; + if variant.attrs.display.is_none() + && variant.attrs.transparent.is_none() + && variant.attrs.fmt.is_none() + { + variant.attrs.display.clone_from(&attrs.display); + variant.attrs.transparent = attrs.transparent; + variant.attrs.fmt.clone_from(&attrs.fmt); + } + if let Some(display) = &mut variant.attrs.display { + let container = ContainerKind::from_variant(node); + display.expand_shorthand(&variant.fields, container)?; + } + 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, scope: &ParamsInScope<'a>) -> Result { + let attrs = attr::get(&node.attrs)?; + Ok(Variant { + original: node, + attrs, + ident: node.ident.clone(), + fields: Field::multiple_from_syn(&node.fields, scope)?, + }) + } +} + +impl<'a> Field<'a> { + fn multiple_from_syn(fields: &'a Fields, scope: &ParamsInScope<'a>) -> Result> { + fields + .iter() + .enumerate() + .map(|(i, field)| Field::from_syn(i, field, scope)) + .collect() + } + + fn from_syn(i: usize, node: &'a syn::Field, scope: &ParamsInScope<'a>) -> Result { + Ok(Field { + original: node, + attrs: attr::get(&node.attrs)?, + member: match &node.ident { + Some(name) => MemberUnraw::Named(IdentUnraw::new(name.clone())), + None => MemberUnraw::Unnamed(Index { + index: i as u32, + span: Span::call_site(), + }), + }, + ty: &node.ty, + contains_generic: scope.intersects(&node.ty), + }) + } +} + +impl ContainerKind { + fn from_struct(node: &DataStruct) -> Self { + match node.fields { + Fields::Named(_) => ContainerKind::Struct, + Fields::Unnamed(_) => ContainerKind::TupleStruct, + Fields::Unit => ContainerKind::UnitStruct, + } + } + + fn from_variant(node: &syn::Variant) -> Self { + match node.fields { + Fields::Named(_) => ContainerKind::StructVariant, + Fields::Unnamed(_) => ContainerKind::TupleVariant, + Fields::Unit => ContainerKind::UnitVariant, + } + } +} + +impl Display for ContainerKind { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str(match self { + ContainerKind::Struct => "struct", + ContainerKind::TupleStruct => "tuple struct", + ContainerKind::UnitStruct => "unit struct", + ContainerKind::StructVariant => "struct variant", + ContainerKind::TupleVariant => "tuple variant", + ContainerKind::UnitVariant => "unit variant", + }) + } +} diff --git a/vendor/thiserror-impl/src/attr.rs b/vendor/thiserror-impl/src/attr.rs new file mode 100644 index 00000000..7ad83e02 --- /dev/null +++ b/vendor/thiserror-impl/src/attr.rs @@ -0,0 +1,358 @@ +use proc_macro2::{Delimiter, Group, Literal, Punct, Spacing, Span, TokenStream, TokenTree}; +use quote::{format_ident, quote, quote_spanned, ToTokens}; +use std::collections::BTreeSet as Set; +use syn::parse::discouraged::Speculative; +use syn::parse::{End, ParseStream}; +use syn::{ + braced, bracketed, parenthesized, token, Attribute, Error, ExprPath, Ident, Index, LitFloat, + LitInt, LitStr, Meta, Result, Token, +}; + +pub struct Attrs<'a> { + pub display: Option>, + pub source: Option>, + pub backtrace: Option<&'a Attribute>, + pub from: Option>, + pub transparent: Option>, + pub fmt: Option>, +} + +#[derive(Clone)] +pub struct Display<'a> { + pub original: &'a Attribute, + pub fmt: LitStr, + pub args: TokenStream, + pub requires_fmt_machinery: bool, + pub has_bonus_display: bool, + pub infinite_recursive: bool, + pub implied_bounds: Set<(usize, Trait)>, + pub bindings: Vec<(Ident, TokenStream)>, +} + +#[derive(Copy, Clone)] +pub struct Source<'a> { + pub original: &'a Attribute, + pub span: Span, +} + +#[derive(Copy, Clone)] +pub struct From<'a> { + pub original: &'a Attribute, + pub span: Span, +} + +#[derive(Copy, Clone)] +pub struct Transparent<'a> { + pub original: &'a Attribute, + pub span: Span, +} + +#[derive(Clone)] +pub struct Fmt<'a> { + pub original: &'a Attribute, + pub path: ExprPath, +} + +#[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 { + let mut attrs = Attrs { + display: None, + source: None, + backtrace: None, + from: None, + transparent: None, + fmt: None, + }; + + for attr in input { + if attr.path().is_ident("error") { + parse_error_attribute(&mut attrs, attr)?; + } else if attr.path().is_ident("source") { + attr.meta.require_path_only()?; + if attrs.source.is_some() { + return Err(Error::new_spanned(attr, "duplicate #[source] attribute")); + } + let span = (attr.pound_token.span) + .join(attr.bracket_token.span.join()) + .unwrap_or(attr.path().get_ident().unwrap().span()); + attrs.source = Some(Source { + original: attr, + span, + }); + } else if attr.path().is_ident("backtrace") { + attr.meta.require_path_only()?; + 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") { + match attr.meta { + Meta::Path(_) => {} + Meta::List(_) | Meta::NameValue(_) => { + // 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")); + } + let span = (attr.pound_token.span) + .join(attr.bracket_token.span.join()) + .unwrap_or(attr.path().get_ident().unwrap().span()); + attrs.from = Some(From { + original: attr, + span, + }); + } + } + + Ok(attrs) +} + +fn parse_error_attribute<'a>(attrs: &mut Attrs<'a>, attr: &'a Attribute) -> Result<()> { + mod kw { + syn::custom_keyword!(transparent); + syn::custom_keyword!(fmt); + } + + attr.parse_args_with(|input: ParseStream| { + let lookahead = input.lookahead1(); + let fmt = if lookahead.peek(LitStr) { + input.parse::()? + } else if lookahead.peek(kw::transparent) { + let kw: kw::transparent = input.parse()?; + if attrs.transparent.is_some() { + return Err(Error::new_spanned( + attr, + "duplicate #[error(transparent)] attribute", + )); + } + attrs.transparent = Some(Transparent { + original: attr, + span: kw.span, + }); + return Ok(()); + } else if lookahead.peek(kw::fmt) { + input.parse::()?; + input.parse::()?; + let path: ExprPath = input.parse()?; + if attrs.fmt.is_some() { + return Err(Error::new_spanned( + attr, + "duplicate #[error(fmt = ...)] attribute", + )); + } + attrs.fmt = Some(Fmt { + original: attr, + path, + }); + return Ok(()); + } else { + return Err(lookahead.error()); + }; + + let args = if input.is_empty() || input.peek(Token![,]) && input.peek2(End) { + input.parse::>()?; + TokenStream::new() + } else { + parse_token_expr(input, false)? + }; + + let requires_fmt_machinery = !args.is_empty(); + + let display = Display { + original: attr, + fmt, + args, + requires_fmt_machinery, + has_bonus_display: false, + infinite_recursive: false, + implied_bounds: Set::new(), + bindings: Vec::new(), + }; + 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 input.peek(token::Group) { + let group: TokenTree = input.parse()?; + tokens.push(group); + begin_expr = false; + continue; + } + + if begin_expr && input.peek(Token![.]) { + if input.peek2(Ident) { + input.parse::()?; + begin_expr = false; + continue; + } else if input.peek2(LitInt) { + input.parse::()?; + let int: Index = input.parse()?; + tokens.push({ + let ident = format_ident!("_{}", int.index, span = int.span); + TokenTree::Ident(ident) + }); + begin_expr = false; + continue; + } else if input.peek2(LitFloat) { + let ahead = input.fork(); + ahead.parse::()?; + let float: LitFloat = ahead.parse()?; + let repr = float.to_string(); + let mut indices = repr.split('.').map(syn::parse_str::); + if let (Some(Ok(first)), Some(Ok(second)), None) = + (indices.next(), indices.next(), indices.next()) + { + input.advance_to(&ahead); + tokens.push({ + let ident = format_ident!("_{}", first, span = float.span()); + TokenTree::Ident(ident) + }); + tokens.push({ + let mut punct = Punct::new('.', Spacing::Alone); + punct.set_span(float.span()); + TokenTree::Punct(punct) + }); + tokens.push({ + let mut literal = Literal::u32_unsuffixed(second.index); + literal.set_span(float.span()); + TokenTree::Literal(literal) + }); + 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.join()); + 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.join()); + 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.join()); + TokenTree::Group(group) + } else { + input.parse()? + }; + tokens.push(token); + } + Ok(TokenStream::from_iter(tokens)) +} + +impl ToTokens for Display<'_> { + fn to_tokens(&self, tokens: &mut TokenStream) { + if self.infinite_recursive { + let span = self.fmt.span(); + tokens.extend(quote_spanned! {span=> + #[warn(unconditional_recursion)] + fn _fmt() { _fmt() } + }); + } + + let fmt = &self.fmt; + let args = &self.args; + + // Currently `write!(f, "text")` produces less efficient code than + // `f.write_str("text")`. We recognize the case when the format string + // has no braces and no interpolated values, and generate simpler code. + let write = if self.requires_fmt_machinery { + quote! { + ::core::write!(__formatter, #fmt #args) + } + } else { + quote! { + __formatter.write_str(#fmt) + } + }; + + tokens.extend(if self.bindings.is_empty() { + write + } else { + let locals = self.bindings.iter().map(|(local, _value)| local); + let values = self.bindings.iter().map(|(_local, value)| value); + quote! { + match (#(#values,)*) { + (#(#locals,)*) => #write + } + } + }); + } +} + +impl ToTokens for Trait { + fn to_tokens(&self, tokens: &mut TokenStream) { + let trait_name = match self { + Trait::Debug => "Debug", + Trait::Display => "Display", + Trait::Octal => "Octal", + Trait::LowerHex => "LowerHex", + Trait::UpperHex => "UpperHex", + Trait::Pointer => "Pointer", + Trait::Binary => "Binary", + Trait::LowerExp => "LowerExp", + Trait::UpperExp => "UpperExp", + }; + let ident = Ident::new(trait_name, Span::call_site()); + tokens.extend(quote!(::core::fmt::#ident)); + } +} diff --git a/vendor/thiserror-impl/src/expand.rs b/vendor/thiserror-impl/src/expand.rs new file mode 100644 index 00000000..c6939214 --- /dev/null +++ b/vendor/thiserror-impl/src/expand.rs @@ -0,0 +1,583 @@ +use crate::ast::{Enum, Field, Input, Struct}; +use crate::attr::Trait; +use crate::fallback; +use crate::generics::InferredBounds; +use crate::unraw::MemberUnraw; +use proc_macro2::{Ident, Span, TokenStream}; +use quote::{format_ident, quote, quote_spanned, ToTokens}; +use std::collections::BTreeSet as Set; +use syn::{DeriveInput, GenericArgument, PathArguments, Result, Token, Type}; + +pub fn derive(input: &DeriveInput) -> TokenStream { + match try_expand(input) { + Ok(expanded) => expanded, + // If there are invalid attributes in the input, expand to an Error impl + // anyway to minimize spurious secondary errors in other code that uses + // this type as an Error. + Err(error) => fallback::expand(input, error), + } +} + +fn try_expand(input: &DeriveInput) -> Result { + let input = Input::from_syn(input)?; + input.validate()?; + Ok(match input { + Input::Struct(input) => impl_struct(input), + Input::Enum(input) => impl_enum(input), + }) +} + +fn impl_struct(input: Struct) -> TokenStream { + let ty = call_site_ident(&input.ident); + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + let mut error_inferred_bounds = InferredBounds::new(); + + let source_body = if let Some(transparent_attr) = &input.attrs.transparent { + let only_field = &input.fields[0]; + if only_field.contains_generic { + error_inferred_bounds.insert(only_field.ty, quote!(::thiserror::__private::Error)); + } + let member = &only_field.member; + Some(quote_spanned! {transparent_attr.span=> + ::thiserror::__private::Error::source(self.#member.as_dyn_error()) + }) + } else if let Some(source_field) = input.source_field() { + let source = &source_field.member; + if source_field.contains_generic { + let ty = unoptional_type(source_field.ty); + error_inferred_bounds.insert(ty, quote!(::thiserror::__private::Error + 'static)); + } + let asref = if type_is_option(source_field.ty) { + Some(quote_spanned!(source.span()=> .as_ref()?)) + } else { + None + }; + let dyn_error = quote_spanned! {source_field.source_span()=> + self.#source #asref.as_dyn_error() + }; + Some(quote! { + ::core::option::Option::Some(#dyn_error) + }) + } else { + None + }; + let source_method = source_body.map(|body| { + quote! { + fn source(&self) -> ::core::option::Option<&(dyn ::thiserror::__private::Error + 'static)> { + use ::thiserror::__private::AsDynError as _; + #body + } + } + }); + + let provide_method = input.backtrace_field().map(|backtrace_field| { + let request = quote!(request); + let backtrace = &backtrace_field.member; + let body = if let Some(source_field) = input.source_field() { + let source = &source_field.member; + let source_provide = if type_is_option(source_field.ty) { + quote_spanned! {source.span()=> + if let ::core::option::Option::Some(source) = &self.#source { + source.thiserror_provide(#request); + } + } + } else { + quote_spanned! {source.span()=> + self.#source.thiserror_provide(#request); + } + }; + let self_provide = if source == backtrace { + None + } else if type_is_option(backtrace_field.ty) { + Some(quote! { + if let ::core::option::Option::Some(backtrace) = &self.#backtrace { + #request.provide_ref::<::thiserror::__private::Backtrace>(backtrace); + } + }) + } else { + Some(quote! { + #request.provide_ref::<::thiserror::__private::Backtrace>(&self.#backtrace); + }) + }; + quote! { + use ::thiserror::__private::ThiserrorProvide as _; + #source_provide + #self_provide + } + } else if type_is_option(backtrace_field.ty) { + quote! { + if let ::core::option::Option::Some(backtrace) = &self.#backtrace { + #request.provide_ref::<::thiserror::__private::Backtrace>(backtrace); + } + } + } else { + quote! { + #request.provide_ref::<::thiserror::__private::Backtrace>(&self.#backtrace); + } + }; + quote! { + fn provide<'_request>(&'_request self, #request: &mut ::core::error::Request<'_request>) { + #body + } + } + }); + + let mut display_implied_bounds = Set::new(); + let display_body = if input.attrs.transparent.is_some() { + let only_field = &input.fields[0].member; + display_implied_bounds.insert((0, Trait::Display)); + Some(quote! { + ::core::fmt::Display::fmt(&self.#only_field, __formatter) + }) + } else if let Some(display) = &input.attrs.display { + display_implied_bounds.clone_from(&display.implied_bounds); + let use_as_display = use_as_display(display.has_bonus_display); + let pat = fields_pat(&input.fields); + Some(quote! { + #use_as_display + #[allow(unused_variables, deprecated)] + let Self #pat = self; + #display + }) + } else { + None + }; + let display_impl = display_body.map(|body| { + let mut display_inferred_bounds = InferredBounds::new(); + for (field, bound) in display_implied_bounds { + let field = &input.fields[field]; + if field.contains_generic { + display_inferred_bounds.insert(field.ty, bound); + } + } + let display_where_clause = display_inferred_bounds.augment_where_clause(input.generics); + quote! { + #[allow(unused_qualifications)] + #[automatically_derived] + impl #impl_generics ::core::fmt::Display for #ty #ty_generics #display_where_clause { + #[allow(clippy::used_underscore_binding)] + fn fmt(&self, __formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + #body + } + } + } + }); + + let from_impl = input.from_field().map(|from_field| { + let span = from_field.attrs.from.unwrap().span; + let backtrace_field = input.distinct_backtrace_field(); + let from = unoptional_type(from_field.ty); + let source_var = Ident::new("source", span); + let body = from_initializer(from_field, backtrace_field, &source_var); + let from_function = quote! { + fn from(#source_var: #from) -> Self { + #ty #body + } + }; + let from_impl = quote_spanned! {span=> + #[automatically_derived] + impl #impl_generics ::core::convert::From<#from> for #ty #ty_generics #where_clause { + #from_function + } + }; + Some(quote! { + #[allow( + deprecated, + unused_qualifications, + clippy::elidable_lifetime_names, + clippy::needless_lifetimes, + )] + #from_impl + }) + }); + + if input.generics.type_params().next().is_some() { + let self_token = ::default(); + error_inferred_bounds.insert(self_token, Trait::Debug); + error_inferred_bounds.insert(self_token, Trait::Display); + } + let error_where_clause = error_inferred_bounds.augment_where_clause(input.generics); + + quote! { + #[allow(unused_qualifications)] + #[automatically_derived] + impl #impl_generics ::thiserror::__private::Error for #ty #ty_generics #error_where_clause { + #source_method + #provide_method + } + #display_impl + #from_impl + } +} + +fn impl_enum(input: Enum) -> TokenStream { + let ty = call_site_ident(&input.ident); + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + let mut error_inferred_bounds = InferredBounds::new(); + + let source_method = if input.has_source() { + let arms = input.variants.iter().map(|variant| { + let ident = &variant.ident; + if let Some(transparent_attr) = &variant.attrs.transparent { + let only_field = &variant.fields[0]; + if only_field.contains_generic { + error_inferred_bounds.insert(only_field.ty, quote!(::thiserror::__private::Error)); + } + let member = &only_field.member; + let source = quote_spanned! {transparent_attr.span=> + ::thiserror::__private::Error::source(transparent.as_dyn_error()) + }; + quote! { + #ty::#ident {#member: transparent} => #source, + } + } else if let Some(source_field) = variant.source_field() { + let source = &source_field.member; + if source_field.contains_generic { + let ty = unoptional_type(source_field.ty); + error_inferred_bounds.insert(ty, quote!(::thiserror::__private::Error + 'static)); + } + let asref = if type_is_option(source_field.ty) { + Some(quote_spanned!(source.span()=> .as_ref()?)) + } else { + None + }; + let varsource = quote!(source); + let dyn_error = quote_spanned! {source_field.source_span()=> + #varsource #asref.as_dyn_error() + }; + quote! { + #ty::#ident {#source: #varsource, ..} => ::core::option::Option::Some(#dyn_error), + } + } else { + quote! { + #ty::#ident {..} => ::core::option::Option::None, + } + } + }); + Some(quote! { + fn source(&self) -> ::core::option::Option<&(dyn ::thiserror::__private::Error + 'static)> { + use ::thiserror::__private::AsDynError as _; + #[allow(deprecated)] + match self { + #(#arms)* + } + } + }) + } else { + None + }; + + let provide_method = if input.has_backtrace() { + let request = quote!(request); + let arms = input.variants.iter().map(|variant| { + let ident = &variant.ident; + match (variant.backtrace_field(), variant.source_field()) { + (Some(backtrace_field), Some(source_field)) + if backtrace_field.attrs.backtrace.is_none() => + { + let backtrace = &backtrace_field.member; + let source = &source_field.member; + let varsource = quote!(source); + let source_provide = if type_is_option(source_field.ty) { + quote_spanned! {source.span()=> + if let ::core::option::Option::Some(source) = #varsource { + source.thiserror_provide(#request); + } + } + } else { + quote_spanned! {source.span()=> + #varsource.thiserror_provide(#request); + } + }; + let self_provide = if type_is_option(backtrace_field.ty) { + quote! { + if let ::core::option::Option::Some(backtrace) = backtrace { + #request.provide_ref::<::thiserror::__private::Backtrace>(backtrace); + } + } + } else { + quote! { + #request.provide_ref::<::thiserror::__private::Backtrace>(backtrace); + } + }; + quote! { + #ty::#ident { + #backtrace: backtrace, + #source: #varsource, + .. + } => { + use ::thiserror::__private::ThiserrorProvide as _; + #source_provide + #self_provide + } + } + } + (Some(backtrace_field), Some(source_field)) + if backtrace_field.member == source_field.member => + { + let backtrace = &backtrace_field.member; + let varsource = quote!(source); + let source_provide = if type_is_option(source_field.ty) { + quote_spanned! {backtrace.span()=> + if let ::core::option::Option::Some(source) = #varsource { + source.thiserror_provide(#request); + } + } + } else { + quote_spanned! {backtrace.span()=> + #varsource.thiserror_provide(#request); + } + }; + quote! { + #ty::#ident {#backtrace: #varsource, ..} => { + use ::thiserror::__private::ThiserrorProvide as _; + #source_provide + } + } + } + (Some(backtrace_field), _) => { + let backtrace = &backtrace_field.member; + let body = if type_is_option(backtrace_field.ty) { + quote! { + if let ::core::option::Option::Some(backtrace) = backtrace { + #request.provide_ref::<::thiserror::__private::Backtrace>(backtrace); + } + } + } else { + quote! { + #request.provide_ref::<::thiserror::__private::Backtrace>(backtrace); + } + }; + quote! { + #ty::#ident {#backtrace: backtrace, ..} => { + #body + } + } + } + (None, _) => quote! { + #ty::#ident {..} => {} + }, + } + }); + Some(quote! { + fn provide<'_request>(&'_request self, #request: &mut ::core::error::Request<'_request>) { + #[allow(deprecated)] + match self { + #(#arms)* + } + } + }) + } else { + None + }; + + let display_impl = if input.has_display() { + let mut display_inferred_bounds = InferredBounds::new(); + let has_bonus_display = input.variants.iter().any(|v| { + v.attrs + .display + .as_ref() + .map_or(false, |display| display.has_bonus_display) + }); + let use_as_display = use_as_display(has_bonus_display); + let void_deref = if input.variants.is_empty() { + Some(quote!(*)) + } else { + None + }; + let arms = input.variants.iter().map(|variant| { + let mut display_implied_bounds = Set::new(); + let display = if let Some(display) = &variant.attrs.display { + display_implied_bounds.clone_from(&display.implied_bounds); + display.to_token_stream() + } else if let Some(fmt) = &variant.attrs.fmt { + let fmt_path = &fmt.path; + let vars = variant.fields.iter().map(|field| match &field.member { + MemberUnraw::Named(ident) => ident.to_local(), + MemberUnraw::Unnamed(index) => format_ident!("_{}", index), + }); + quote!(#fmt_path(#(#vars,)* __formatter)) + } else { + let only_field = match &variant.fields[0].member { + MemberUnraw::Named(ident) => ident.to_local(), + MemberUnraw::Unnamed(index) => format_ident!("_{}", index), + }; + display_implied_bounds.insert((0, Trait::Display)); + quote!(::core::fmt::Display::fmt(#only_field, __formatter)) + }; + for (field, bound) in display_implied_bounds { + let field = &variant.fields[field]; + if field.contains_generic { + display_inferred_bounds.insert(field.ty, bound); + } + } + let ident = &variant.ident; + let pat = fields_pat(&variant.fields); + quote! { + #ty::#ident #pat => #display + } + }); + let arms = arms.collect::>(); + let display_where_clause = display_inferred_bounds.augment_where_clause(input.generics); + Some(quote! { + #[allow(unused_qualifications)] + #[automatically_derived] + impl #impl_generics ::core::fmt::Display for #ty #ty_generics #display_where_clause { + fn fmt(&self, __formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + #use_as_display + #[allow(unused_variables, deprecated, clippy::used_underscore_binding)] + match #void_deref self { + #(#arms,)* + } + } + } + }) + } else { + None + }; + + let from_impls = input.variants.iter().filter_map(|variant| { + let from_field = variant.from_field()?; + let span = from_field.attrs.from.unwrap().span; + let backtrace_field = variant.distinct_backtrace_field(); + let variant = &variant.ident; + let from = unoptional_type(from_field.ty); + let source_var = Ident::new("source", span); + let body = from_initializer(from_field, backtrace_field, &source_var); + let from_function = quote! { + fn from(#source_var: #from) -> Self { + #ty::#variant #body + } + }; + let from_impl = quote_spanned! {span=> + #[automatically_derived] + impl #impl_generics ::core::convert::From<#from> for #ty #ty_generics #where_clause { + #from_function + } + }; + Some(quote! { + #[allow( + deprecated, + unused_qualifications, + clippy::elidable_lifetime_names, + clippy::needless_lifetimes, + )] + #from_impl + }) + }); + + if input.generics.type_params().next().is_some() { + let self_token = ::default(); + error_inferred_bounds.insert(self_token, Trait::Debug); + error_inferred_bounds.insert(self_token, Trait::Display); + } + let error_where_clause = error_inferred_bounds.augment_where_clause(input.generics); + + quote! { + #[allow(unused_qualifications)] + #[automatically_derived] + impl #impl_generics ::thiserror::__private::Error for #ty #ty_generics #error_where_clause { + #source_method + #provide_method + } + #display_impl + #(#from_impls)* + } +} + +// Create an ident with which we can expand `impl Trait for #ident {}` on a +// deprecated type without triggering deprecation warning on the generated impl. +pub(crate) fn call_site_ident(ident: &Ident) -> Ident { + let mut ident = ident.clone(); + ident.set_span(ident.span().resolved_at(Span::call_site())); + ident +} + +fn fields_pat(fields: &[Field]) -> TokenStream { + let mut members = fields.iter().map(|field| &field.member).peekable(); + match members.peek() { + Some(MemberUnraw::Named(_)) => quote!({ #(#members),* }), + Some(MemberUnraw::Unnamed(_)) => { + let vars = members.map(|member| match member { + MemberUnraw::Unnamed(index) => format_ident!("_{}", index), + MemberUnraw::Named(_) => unreachable!(), + }); + quote!((#(#vars),*)) + } + None => quote!({}), + } +} + +fn use_as_display(needs_as_display: bool) -> Option { + if needs_as_display { + Some(quote! { + use ::thiserror::__private::AsDisplay as _; + }) + } else { + None + } +} + +fn from_initializer( + from_field: &Field, + backtrace_field: Option<&Field>, + source_var: &Ident, +) -> TokenStream { + let from_member = &from_field.member; + let some_source = if type_is_option(from_field.ty) { + quote!(::core::option::Option::Some(#source_var)) + } else { + quote!(#source_var) + }; + let backtrace = backtrace_field.map(|backtrace_field| { + let backtrace_member = &backtrace_field.member; + if type_is_option(backtrace_field.ty) { + quote! { + #backtrace_member: ::core::option::Option::Some(::thiserror::__private::Backtrace::capture()), + } + } else { + quote! { + #backtrace_member: ::core::convert::From::from(::thiserror::__private::Backtrace::capture()), + } + } + }); + quote!({ + #from_member: #some_source, + #backtrace + }) +} + +fn type_is_option(ty: &Type) -> bool { + type_parameter_of_option(ty).is_some() +} + +fn unoptional_type(ty: &Type) -> TokenStream { + let unoptional = type_parameter_of_option(ty).unwrap_or(ty); + quote!(#unoptional) +} + +fn type_parameter_of_option(ty: &Type) -> Option<&Type> { + let path = match ty { + Type::Path(ty) => &ty.path, + _ => return None, + }; + + let last = path.segments.last().unwrap(); + if last.ident != "Option" { + return None; + } + + let bracketed = match &last.arguments { + PathArguments::AngleBracketed(bracketed) => bracketed, + _ => return None, + }; + + if bracketed.args.len() != 1 { + return None; + } + + match &bracketed.args[0] { + GenericArgument::Type(arg) => Some(arg), + _ => None, + } +} diff --git a/vendor/thiserror-impl/src/fallback.rs b/vendor/thiserror-impl/src/fallback.rs new file mode 100644 index 00000000..e9c429ba --- /dev/null +++ b/vendor/thiserror-impl/src/fallback.rs @@ -0,0 +1,32 @@ +use crate::expand::call_site_ident; +use proc_macro2::TokenStream; +use quote::quote; +use syn::DeriveInput; + +pub(crate) fn expand(input: &DeriveInput, error: syn::Error) -> TokenStream { + let ty = call_site_ident(&input.ident); + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + + let error = error.to_compile_error(); + + quote! { + #error + + #[allow(unused_qualifications)] + #[automatically_derived] + impl #impl_generics ::thiserror::__private::Error for #ty #ty_generics #where_clause + where + // Work around trivial bounds being unstable. + // https://github.com/rust-lang/rust/issues/48214 + for<'workaround> #ty #ty_generics: ::core::fmt::Debug, + {} + + #[allow(unused_qualifications)] + #[automatically_derived] + impl #impl_generics ::core::fmt::Display for #ty #ty_generics #where_clause { + fn fmt(&self, __formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + ::core::unreachable!() + } + } + } +} diff --git a/vendor/thiserror-impl/src/fmt.rs b/vendor/thiserror-impl/src/fmt.rs new file mode 100644 index 00000000..1da28c19 --- /dev/null +++ b/vendor/thiserror-impl/src/fmt.rs @@ -0,0 +1,322 @@ +use crate::ast::{ContainerKind, Field}; +use crate::attr::{Display, Trait}; +use crate::scan_expr::scan_expr; +use crate::unraw::{IdentUnraw, MemberUnraw}; +use proc_macro2::{Delimiter, TokenStream, TokenTree}; +use quote::{format_ident, quote, quote_spanned, ToTokens as _}; +use std::collections::{BTreeSet, HashMap}; +use std::iter; +use syn::ext::IdentExt; +use syn::parse::discouraged::Speculative; +use syn::parse::{Error, ParseStream, Parser, Result}; +use syn::{Expr, Ident, Index, LitStr, Token}; + +impl Display<'_> { + pub fn expand_shorthand(&mut self, fields: &[Field], container: ContainerKind) -> Result<()> { + let raw_args = self.args.clone(); + let FmtArguments { + named: user_named_args, + first_unnamed, + } = explicit_named_args.parse2(raw_args).unwrap(); + + let mut member_index = HashMap::new(); + let mut extra_positional_arguments_allowed = true; + for (i, field) in fields.iter().enumerate() { + member_index.insert(&field.member, i); + extra_positional_arguments_allowed &= matches!(&field.member, MemberUnraw::Named(_)); + } + + let span = self.fmt.span(); + let fmt = self.fmt.value(); + let mut read = fmt.as_str(); + let mut out = String::new(); + let mut has_bonus_display = false; + let mut infinite_recursive = false; + let mut implied_bounds = BTreeSet::new(); + let mut bindings = Vec::new(); + let mut macro_named_args = BTreeSet::new(); + + self.requires_fmt_machinery = self.requires_fmt_machinery || fmt.contains('}'); + + while let Some(brace) = read.find('{') { + self.requires_fmt_machinery = true; + 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 Ok(()), + }; + let member = match next { + '0'..='9' => { + let int = take_int(&mut read); + if !extra_positional_arguments_allowed { + if let Some(first_unnamed) = &first_unnamed { + let msg = format!("ambiguous reference to positional arguments by number in a {container}; change this to a named argument"); + return Err(Error::new_spanned(first_unnamed, msg)); + } + } + match int.parse::() { + Ok(index) => MemberUnraw::Unnamed(Index { index, span }), + Err(_) => return Ok(()), + } + } + 'a'..='z' | 'A'..='Z' | '_' => { + if read.starts_with("r#") { + continue; + } + let repr = take_ident(&mut read); + if repr == "_" { + // Invalid. Let rustc produce the diagnostic. + out += repr; + continue; + } + let ident = IdentUnraw::new(Ident::new(repr, span)); + if user_named_args.contains(&ident) { + // Refers to a named argument written by the user, not to field. + out += repr; + continue; + } + MemberUnraw::Named(ident) + } + _ => continue, + }; + let end_spec = match read.find('}') { + Some(end_spec) => end_spec, + None => return Ok(()), + }; + let mut bonus_display = false; + 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(_) => Trait::Display, + None => { + bonus_display = true; + has_bonus_display = true; + Trait::Display + } + }; + infinite_recursive |= member == *"self" && bound == Trait::Display; + let field = match member_index.get(&member) { + Some(&field) => field, + None => { + out += &member.to_string(); + continue; + } + }; + implied_bounds.insert((field, bound)); + let formatvar_prefix = if bonus_display { + "__display" + } else if bound == Trait::Pointer { + "__pointer" + } else { + "__field" + }; + let mut formatvar = IdentUnraw::new(match &member { + MemberUnraw::Unnamed(index) => format_ident!("{}{}", formatvar_prefix, index), + MemberUnraw::Named(ident) => { + format_ident!("{}_{}", formatvar_prefix, ident.to_string()) + } + }); + while user_named_args.contains(&formatvar) { + formatvar = IdentUnraw::new(format_ident!("_{}", formatvar.to_string())); + } + formatvar.set_span(span); + out += &formatvar.to_string(); + if !macro_named_args.insert(formatvar.clone()) { + // Already added to bindings by a previous use. + continue; + } + let mut binding_value = match &member { + MemberUnraw::Unnamed(index) => format_ident!("_{}", index), + MemberUnraw::Named(ident) => ident.to_local(), + }; + binding_value.set_span(span.resolved_at(fields[field].member.span())); + let wrapped_binding_value = if bonus_display { + quote_spanned!(span=> #binding_value.as_display()) + } else if bound == Trait::Pointer { + quote!(::thiserror::__private::Var(#binding_value)) + } else { + binding_value.into_token_stream() + }; + bindings.push((formatvar.to_local(), wrapped_binding_value)); + } + + out += read; + self.fmt = LitStr::new(&out, self.fmt.span()); + self.has_bonus_display = has_bonus_display; + self.infinite_recursive = infinite_recursive; + self.implied_bounds = implied_bounds; + self.bindings = bindings; + Ok(()) + } +} + +struct FmtArguments { + named: BTreeSet, + first_unnamed: Option, +} + +#[allow(clippy::unnecessary_wraps)] +fn explicit_named_args(input: ParseStream) -> Result { + let ahead = input.fork(); + if let Ok(set) = try_explicit_named_args(&ahead) { + input.advance_to(&ahead); + return Ok(set); + } + + let ahead = input.fork(); + if let Ok(set) = fallback_explicit_named_args(&ahead) { + input.advance_to(&ahead); + return Ok(set); + } + + input.parse::().unwrap(); + Ok(FmtArguments { + named: BTreeSet::new(), + first_unnamed: None, + }) +} + +fn try_explicit_named_args(input: ParseStream) -> Result { + let mut syn_full = None; + let mut args = FmtArguments { + named: BTreeSet::new(), + first_unnamed: None, + }; + + while !input.is_empty() { + input.parse::()?; + if input.is_empty() { + break; + } + + let mut begin_unnamed = None; + if input.peek(Ident::peek_any) && input.peek2(Token![=]) && !input.peek2(Token![==]) { + let ident: IdentUnraw = input.parse()?; + input.parse::()?; + args.named.insert(ident); + } else { + begin_unnamed = Some(input.fork()); + } + + let ahead = input.fork(); + if *syn_full.get_or_insert_with(is_syn_full) && ahead.parse::().is_ok() { + input.advance_to(&ahead); + } else { + scan_expr(input)?; + } + + if let Some(begin_unnamed) = begin_unnamed { + if args.first_unnamed.is_none() { + args.first_unnamed = Some(between(&begin_unnamed, input)); + } + } + } + + Ok(args) +} + +fn fallback_explicit_named_args(input: ParseStream) -> Result { + let mut args = FmtArguments { + named: BTreeSet::new(), + first_unnamed: None, + }; + + while !input.is_empty() { + if input.peek(Token![,]) + && input.peek2(Ident::peek_any) + && input.peek3(Token![=]) + && !input.peek3(Token![==]) + { + input.parse::()?; + let ident: IdentUnraw = input.parse()?; + input.parse::()?; + args.named.insert(ident); + } else { + input.parse::()?; + } + } + + Ok(args) +} + +fn is_syn_full() -> bool { + // Expr::Block contains syn::Block which contains Vec. In the + // current version of Syn, syn::Stmt is exhaustive and could only plausibly + // represent `trait Trait {}` in Stmt::Item which contains syn::Item. Most + // of the point of syn's non-"full" mode is to avoid compiling Item and the + // entire expansive syntax tree it comprises. So the following expression + // being parsed to Expr::Block is a reliable indication that "full" is + // enabled. + let test = quote!({ + trait Trait {} + }); + match syn::parse2(test) { + Ok(Expr::Verbatim(_)) | Err(_) => false, + Ok(Expr::Block(_)) => true, + Ok(_) => unreachable!(), + } +} + +fn take_int<'a>(read: &mut &'a str) -> &'a str { + let mut int_len = 0; + for ch in read.chars() { + match ch { + '0'..='9' => int_len += 1, + _ => break, + } + } + let (int, rest) = read.split_at(int_len); + *read = rest; + int +} + +fn take_ident<'a>(read: &mut &'a str) -> &'a str { + let mut ident_len = 0; + for ch in read.chars() { + match ch { + 'a'..='z' | 'A'..='Z' | '0'..='9' | '_' => ident_len += 1, + _ => break, + } + } + let (ident, rest) = read.split_at(ident_len); + *read = rest; + ident +} + +fn between<'a>(begin: ParseStream<'a>, end: ParseStream<'a>) -> TokenStream { + let end = end.cursor(); + let mut cursor = begin.cursor(); + let mut tokens = TokenStream::new(); + + while cursor < end { + let (tt, next) = cursor.token_tree().unwrap(); + + if end < next { + if let Some((inside, _span, _after)) = cursor.group(Delimiter::None) { + cursor = inside; + continue; + } + if tokens.is_empty() { + tokens.extend(iter::once(tt)); + } + break; + } + + tokens.extend(iter::once(tt)); + cursor = next; + } + + tokens +} diff --git a/vendor/thiserror-impl/src/generics.rs b/vendor/thiserror-impl/src/generics.rs new file mode 100644 index 00000000..26fe0a9a --- /dev/null +++ b/vendor/thiserror-impl/src/generics.rs @@ -0,0 +1,83 @@ +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 let Some(qself) = &ty.qself { + crawl(in_scope, &qself.ty, found); + } else { + let front = ty.path.segments.first().unwrap(); + if front.arguments.is_none() && in_scope.names.contains(&front.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, Punctuated)>, + order: Vec, +} + +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() + } +} diff --git a/vendor/thiserror-impl/src/lib.rs b/vendor/thiserror-impl/src/lib.rs new file mode 100644 index 00000000..7f3767ef --- /dev/null +++ b/vendor/thiserror-impl/src/lib.rs @@ -0,0 +1,39 @@ +#![allow( + clippy::blocks_in_conditions, + clippy::cast_lossless, + clippy::cast_possible_truncation, + clippy::enum_glob_use, + clippy::manual_find, + clippy::manual_let_else, + clippy::manual_map, + clippy::map_unwrap_or, + clippy::module_name_repetitions, + clippy::needless_pass_by_value, + clippy::range_plus_one, + clippy::single_match_else, + clippy::struct_field_names, + clippy::too_many_lines, + clippy::wrong_self_convention +)] + +extern crate proc_macro; + +mod ast; +mod attr; +mod expand; +mod fallback; +mod fmt; +mod generics; +mod prop; +mod scan_expr; +mod unraw; +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).into() +} diff --git a/vendor/thiserror-impl/src/prop.rs b/vendor/thiserror-impl/src/prop.rs new file mode 100644 index 00000000..0a101fc0 --- /dev/null +++ b/vendor/thiserror-impl/src/prop.rs @@ -0,0 +1,148 @@ +use crate::ast::{Enum, Field, Struct, Variant}; +use crate::unraw::MemberUnraw; +use proc_macro2::Span; +use syn::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) + } + + 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<'_> { + 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.attrs.fmt.is_some() + || self + .variants + .iter() + .any(|variant| variant.attrs.display.is_some() || variant.attrs.fmt.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) + } + + 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<'_> { + pub(crate) fn is_backtrace(&self) -> bool { + type_is_backtrace(self.ty) + } + + pub(crate) fn source_span(&self) -> Span { + if let Some(source_attr) = &self.attrs.source { + source_attr.span + } else if let Some(from_attr) = &self.attrs.from { + from_attr.span + } else { + self.member.span() + } + } +} + +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 { + MemberUnraw::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 +} + +// 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, + _ => return false, + }; + + let last = path.segments.last().unwrap(); + last.ident == "Backtrace" && last.arguments.is_empty() +} diff --git a/vendor/thiserror-impl/src/scan_expr.rs b/vendor/thiserror-impl/src/scan_expr.rs new file mode 100644 index 00000000..155b5b63 --- /dev/null +++ b/vendor/thiserror-impl/src/scan_expr.rs @@ -0,0 +1,264 @@ +use self::{Action::*, Input::*}; +use proc_macro2::{Delimiter, Ident, Spacing, TokenTree}; +use syn::parse::{ParseStream, Result}; +use syn::{AngleBracketedGenericArguments, BinOp, Expr, ExprPath, Lifetime, Lit, Token, Type}; + +enum Input { + Keyword(&'static str), + Punct(&'static str), + ConsumeAny, + ConsumeBinOp, + ConsumeBrace, + ConsumeDelimiter, + ConsumeIdent, + ConsumeLifetime, + ConsumeLiteral, + ConsumeNestedBrace, + ExpectPath, + ExpectTurbofish, + ExpectType, + CanBeginExpr, + Otherwise, + Empty, +} + +enum Action { + SetState(&'static [(Input, Action)]), + IncDepth, + DecDepth, + Finish, +} + +static INIT: [(Input, Action); 28] = [ + (ConsumeDelimiter, SetState(&POSTFIX)), + (Keyword("async"), SetState(&ASYNC)), + (Keyword("break"), SetState(&BREAK_LABEL)), + (Keyword("const"), SetState(&CONST)), + (Keyword("continue"), SetState(&CONTINUE)), + (Keyword("for"), SetState(&FOR)), + (Keyword("if"), IncDepth), + (Keyword("let"), SetState(&PATTERN)), + (Keyword("loop"), SetState(&BLOCK)), + (Keyword("match"), IncDepth), + (Keyword("move"), SetState(&CLOSURE)), + (Keyword("return"), SetState(&RETURN)), + (Keyword("static"), SetState(&CLOSURE)), + (Keyword("unsafe"), SetState(&BLOCK)), + (Keyword("while"), IncDepth), + (Keyword("yield"), SetState(&RETURN)), + (Keyword("_"), SetState(&POSTFIX)), + (Punct("!"), SetState(&INIT)), + (Punct("#"), SetState(&[(ConsumeDelimiter, SetState(&INIT))])), + (Punct("&"), SetState(&REFERENCE)), + (Punct("*"), SetState(&INIT)), + (Punct("-"), SetState(&INIT)), + (Punct("..="), SetState(&INIT)), + (Punct(".."), SetState(&RANGE)), + (Punct("|"), SetState(&CLOSURE_ARGS)), + (ConsumeLifetime, SetState(&[(Punct(":"), SetState(&INIT))])), + (ConsumeLiteral, SetState(&POSTFIX)), + (ExpectPath, SetState(&PATH)), +]; + +static POSTFIX: [(Input, Action); 10] = [ + (Keyword("as"), SetState(&[(ExpectType, SetState(&POSTFIX))])), + (Punct("..="), SetState(&INIT)), + (Punct(".."), SetState(&RANGE)), + (Punct("."), SetState(&DOT)), + (Punct("?"), SetState(&POSTFIX)), + (ConsumeBinOp, SetState(&INIT)), + (Punct("="), SetState(&INIT)), + (ConsumeNestedBrace, SetState(&IF_THEN)), + (ConsumeDelimiter, SetState(&POSTFIX)), + (Empty, Finish), +]; + +static ASYNC: [(Input, Action); 3] = [ + (Keyword("move"), SetState(&ASYNC)), + (Punct("|"), SetState(&CLOSURE_ARGS)), + (ConsumeBrace, SetState(&POSTFIX)), +]; + +static BLOCK: [(Input, Action); 1] = [(ConsumeBrace, SetState(&POSTFIX))]; + +static BREAK_LABEL: [(Input, Action); 2] = [ + (ConsumeLifetime, SetState(&BREAK_VALUE)), + (Otherwise, SetState(&BREAK_VALUE)), +]; + +static BREAK_VALUE: [(Input, Action); 3] = [ + (ConsumeNestedBrace, SetState(&IF_THEN)), + (CanBeginExpr, SetState(&INIT)), + (Otherwise, SetState(&POSTFIX)), +]; + +static CLOSURE: [(Input, Action); 6] = [ + (Keyword("async"), SetState(&CLOSURE)), + (Keyword("move"), SetState(&CLOSURE)), + (Punct(","), SetState(&CLOSURE)), + (Punct(">"), SetState(&CLOSURE)), + (Punct("|"), SetState(&CLOSURE_ARGS)), + (ConsumeLifetime, SetState(&CLOSURE)), +]; + +static CLOSURE_ARGS: [(Input, Action); 2] = [ + (Punct("|"), SetState(&CLOSURE_RET)), + (ConsumeAny, SetState(&CLOSURE_ARGS)), +]; + +static CLOSURE_RET: [(Input, Action); 2] = [ + (Punct("->"), SetState(&[(ExpectType, SetState(&BLOCK))])), + (Otherwise, SetState(&INIT)), +]; + +static CONST: [(Input, Action); 2] = [ + (Punct("|"), SetState(&CLOSURE_ARGS)), + (ConsumeBrace, SetState(&POSTFIX)), +]; + +static CONTINUE: [(Input, Action); 2] = [ + (ConsumeLifetime, SetState(&POSTFIX)), + (Otherwise, SetState(&POSTFIX)), +]; + +static DOT: [(Input, Action); 3] = [ + (Keyword("await"), SetState(&POSTFIX)), + (ConsumeIdent, SetState(&METHOD)), + (ConsumeLiteral, SetState(&POSTFIX)), +]; + +static FOR: [(Input, Action); 2] = [ + (Punct("<"), SetState(&CLOSURE)), + (Otherwise, SetState(&PATTERN)), +]; + +static IF_ELSE: [(Input, Action); 2] = [(Keyword("if"), SetState(&INIT)), (ConsumeBrace, DecDepth)]; +static IF_THEN: [(Input, Action); 2] = + [(Keyword("else"), SetState(&IF_ELSE)), (Otherwise, DecDepth)]; + +static METHOD: [(Input, Action); 1] = [(ExpectTurbofish, SetState(&POSTFIX))]; + +static PATH: [(Input, Action); 4] = [ + (Punct("!="), SetState(&INIT)), + (Punct("!"), SetState(&INIT)), + (ConsumeNestedBrace, SetState(&IF_THEN)), + (Otherwise, SetState(&POSTFIX)), +]; + +static PATTERN: [(Input, Action); 15] = [ + (ConsumeDelimiter, SetState(&PATTERN)), + (Keyword("box"), SetState(&PATTERN)), + (Keyword("in"), IncDepth), + (Keyword("mut"), SetState(&PATTERN)), + (Keyword("ref"), SetState(&PATTERN)), + (Keyword("_"), SetState(&PATTERN)), + (Punct("!"), SetState(&PATTERN)), + (Punct("&"), SetState(&PATTERN)), + (Punct("..="), SetState(&PATTERN)), + (Punct(".."), SetState(&PATTERN)), + (Punct("="), SetState(&INIT)), + (Punct("@"), SetState(&PATTERN)), + (Punct("|"), SetState(&PATTERN)), + (ConsumeLiteral, SetState(&PATTERN)), + (ExpectPath, SetState(&PATTERN)), +]; + +static RANGE: [(Input, Action); 6] = [ + (Punct("..="), SetState(&INIT)), + (Punct(".."), SetState(&RANGE)), + (Punct("."), SetState(&DOT)), + (ConsumeNestedBrace, SetState(&IF_THEN)), + (Empty, Finish), + (Otherwise, SetState(&INIT)), +]; + +static RAW: [(Input, Action); 3] = [ + (Keyword("const"), SetState(&INIT)), + (Keyword("mut"), SetState(&INIT)), + (Otherwise, SetState(&POSTFIX)), +]; + +static REFERENCE: [(Input, Action); 3] = [ + (Keyword("mut"), SetState(&INIT)), + (Keyword("raw"), SetState(&RAW)), + (Otherwise, SetState(&INIT)), +]; + +static RETURN: [(Input, Action); 2] = [ + (CanBeginExpr, SetState(&INIT)), + (Otherwise, SetState(&POSTFIX)), +]; + +pub(crate) fn scan_expr(input: ParseStream) -> Result<()> { + let mut state = INIT.as_slice(); + let mut depth = 0usize; + 'table: loop { + for rule in state { + if match rule.0 { + Input::Keyword(expected) => input.step(|cursor| match cursor.ident() { + Some((ident, rest)) if ident == expected => Ok((true, rest)), + _ => Ok((false, *cursor)), + })?, + Input::Punct(expected) => input.step(|cursor| { + let begin = *cursor; + let mut cursor = begin; + for (i, ch) in expected.chars().enumerate() { + match cursor.punct() { + Some((punct, _)) if punct.as_char() != ch => break, + Some((_, rest)) if i == expected.len() - 1 => { + return Ok((true, rest)); + } + Some((punct, rest)) if punct.spacing() == Spacing::Joint => { + cursor = rest; + } + _ => break, + } + } + Ok((false, begin)) + })?, + Input::ConsumeAny => input.parse::>()?.is_some(), + Input::ConsumeBinOp => input.parse::().is_ok(), + Input::ConsumeBrace | Input::ConsumeNestedBrace => { + (matches!(rule.0, Input::ConsumeBrace) || depth > 0) + && input.step(|cursor| match cursor.group(Delimiter::Brace) { + Some((_inside, _span, rest)) => Ok((true, rest)), + None => Ok((false, *cursor)), + })? + } + Input::ConsumeDelimiter => input.step(|cursor| match cursor.any_group() { + Some((_inside, _delimiter, _span, rest)) => Ok((true, rest)), + None => Ok((false, *cursor)), + })?, + Input::ConsumeIdent => input.parse::>()?.is_some(), + Input::ConsumeLifetime => input.parse::>()?.is_some(), + Input::ConsumeLiteral => input.parse::>()?.is_some(), + Input::ExpectPath => { + input.parse::()?; + true + } + Input::ExpectTurbofish => { + if input.peek(Token![::]) { + input.parse::()?; + } + true + } + Input::ExpectType => { + Type::without_plus(input)?; + true + } + Input::CanBeginExpr => Expr::peek(input), + Input::Otherwise => true, + Input::Empty => input.is_empty() || input.peek(Token![,]), + } { + state = match rule.1 { + Action::SetState(next) => next, + Action::IncDepth => (depth += 1, &INIT).1, + Action::DecDepth => (depth -= 1, &POSTFIX).1, + Action::Finish => return if depth == 0 { Ok(()) } else { break }, + }; + continue 'table; + } + } + return Err(input.error("unsupported expression")); + } +} diff --git a/vendor/thiserror-impl/src/unraw.rs b/vendor/thiserror-impl/src/unraw.rs new file mode 100644 index 00000000..73b9970a --- /dev/null +++ b/vendor/thiserror-impl/src/unraw.rs @@ -0,0 +1,142 @@ +use proc_macro2::{Ident, Span, TokenStream}; +use quote::ToTokens; +use std::cmp::Ordering; +use std::fmt::{self, Display}; +use std::hash::{Hash, Hasher}; +use syn::ext::IdentExt as _; +use syn::parse::{Parse, ParseStream, Result}; +use syn::Index; + +#[derive(Clone)] +#[repr(transparent)] +pub struct IdentUnraw(Ident); + +impl IdentUnraw { + pub fn new(ident: Ident) -> Self { + IdentUnraw(ident) + } + + pub fn to_local(&self) -> Ident { + let unraw = self.0.unraw(); + let repr = unraw.to_string(); + if syn::parse_str::(&repr).is_err() { + if let "_" | "super" | "self" | "Self" | "crate" = repr.as_str() { + // Some identifiers are never allowed to appear as raw, like r#self and r#_. + } else { + return Ident::new_raw(&repr, Span::call_site()); + } + } + unraw + } + + pub fn set_span(&mut self, span: Span) { + self.0.set_span(span); + } +} + +impl Display for IdentUnraw { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + Display::fmt(&self.0.unraw(), formatter) + } +} + +impl Eq for IdentUnraw {} + +impl PartialEq for IdentUnraw { + fn eq(&self, other: &Self) -> bool { + PartialEq::eq(&self.0.unraw(), &other.0.unraw()) + } +} + +impl PartialEq for IdentUnraw { + fn eq(&self, other: &str) -> bool { + self.0 == other + } +} + +impl Ord for IdentUnraw { + fn cmp(&self, other: &Self) -> Ordering { + Ord::cmp(&self.0.unraw(), &other.0.unraw()) + } +} + +impl PartialOrd for IdentUnraw { + fn partial_cmp(&self, other: &Self) -> Option { + Some(Self::cmp(self, other)) + } +} + +impl Parse for IdentUnraw { + fn parse(input: ParseStream) -> Result { + input.call(Ident::parse_any).map(IdentUnraw::new) + } +} + +impl ToTokens for IdentUnraw { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.0.unraw().to_tokens(tokens); + } +} + +#[derive(Clone)] +pub enum MemberUnraw { + Named(IdentUnraw), + Unnamed(Index), +} + +impl MemberUnraw { + pub fn span(&self) -> Span { + match self { + MemberUnraw::Named(ident) => ident.0.span(), + MemberUnraw::Unnamed(index) => index.span, + } + } +} + +impl Display for MemberUnraw { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + match self { + MemberUnraw::Named(this) => Display::fmt(this, formatter), + MemberUnraw::Unnamed(this) => Display::fmt(&this.index, formatter), + } + } +} + +impl Eq for MemberUnraw {} + +impl PartialEq for MemberUnraw { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (MemberUnraw::Named(this), MemberUnraw::Named(other)) => this == other, + (MemberUnraw::Unnamed(this), MemberUnraw::Unnamed(other)) => this == other, + _ => false, + } + } +} + +impl PartialEq for MemberUnraw { + fn eq(&self, other: &str) -> bool { + match self { + MemberUnraw::Named(this) => this == other, + MemberUnraw::Unnamed(_) => false, + } + } +} + +impl Hash for MemberUnraw { + fn hash(&self, hasher: &mut H) { + match self { + MemberUnraw::Named(ident) => ident.0.unraw().hash(hasher), + MemberUnraw::Unnamed(index) => index.hash(hasher), + } + } +} + +impl ToTokens for MemberUnraw { + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + MemberUnraw::Named(ident) => ident.to_local().to_tokens(tokens), + MemberUnraw::Unnamed(index) => index.to_tokens(tokens), + } + } +} diff --git a/vendor/thiserror-impl/src/valid.rs b/vendor/thiserror-impl/src/valid.rs new file mode 100644 index 00000000..21bd885e --- /dev/null +++ b/vendor/thiserror-impl/src/valid.rs @@ -0,0 +1,248 @@ +use crate::ast::{Enum, Field, Input, Struct, Variant}; +use crate::attr::Attrs; +use syn::{Error, GenericArgument, PathArguments, Result, Type}; + +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.original, + "#[error(transparent)] requires exactly one field", + )); + } + if let Some(source) = self.fields.iter().find_map(|f| f.attrs.source) { + return Err(Error::new_spanned( + source.original, + "transparent error struct can't contain #[source]", + )); + } + } + if let Some(fmt) = &self.attrs.fmt { + return Err(Error::new_spanned( + fmt.original, + "#[error(fmt = ...)] is only supported in enums; for a struct, handwrite your own Display impl", + )); + } + 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() + && variant.attrs.fmt.is_none() + { + return Err(Error::new_spanned( + variant.original, + "missing #[error(\"...\")] display attribute", + )); + } + } + 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().find_map(|f| f.attrs.source) { + return Err(Error::new_spanned( + source.original, + "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(unexpected_display_attr) = if let Some(display) = &self.attrs.display { + Some(display.original) + } else if let Some(fmt) = &self.attrs.fmt { + Some(fmt.original) + } else { + None + } { + return Err(Error::new_spanned( + unexpected_display_attr, + "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.original, + "not expected here; the #[from] attribute belongs on a specific field", + )); + } + if let Some(source) = &attrs.source { + return Err(Error::new_spanned( + source.original, + "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 attrs.transparent.is_some() { + if let Some(display) = &attrs.display { + return Err(Error::new_spanned( + display.original, + "cannot have both #[error(transparent)] and a display attribute", + )); + } + if let Some(fmt) = &attrs.fmt { + return Err(Error::new_spanned( + fmt.original, + "cannot have both #[error(transparent)] and #[error(fmt = ...)]", + )); + } + } else if let (Some(display), Some(_)) = (&attrs.display, &attrs.fmt) { + return Err(Error::new_spanned( + display.original, + "cannot have both #[error(fmt = ...)] and a format arguments 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.original, + "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.original, + "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; + } + if let Some(transparent) = field.attrs.transparent { + return Err(Error::new_spanned( + transparent.original, + "#[error(transparent)] needs to go outside the enum or struct, not on an individual field", + )); + } + has_backtrace |= field.is_backtrace(); + } + if let (Some(from_field), Some(source_field)) = (from_field, source_field) { + if from_field.member != source_field.member { + return Err(Error::new_spanned( + from_field.attrs.from.unwrap().original, + "#[from] is only supported on the source field, not any other field", + )); + } + } + if let Some(from_field) = from_field { + let max_expected_fields = match backtrace_field { + Some(backtrace_field) => 1 + (from_field.member != backtrace_field.member) as usize, + None => 1 + has_backtrace as usize, + }; + if fields.len() > max_expected_fields { + return Err(Error::new_spanned( + from_field.attrs.from.unwrap().original, + "deriving From requires no fields other than source and backtrace", + )); + } + } + if let Some(source_field) = source_field.or(from_field) { + 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", + )); + } + } + Ok(()) +} + +fn contains_non_static_lifetime(ty: &Type) -> bool { + match ty { + Type::Path(ty) => { + let bracketed = match &ty.path.segments.last().unwrap().arguments { + PathArguments::AngleBracketed(bracketed) => bracketed, + _ => return false, + }; + for arg in &bracketed.args { + match arg { + GenericArgument::Type(ty) if contains_non_static_lifetime(ty) => return true, + GenericArgument::Lifetime(lifetime) if lifetime.ident != "static" => { + return true + } + _ => {} + } + } + false + } + Type::Reference(ty) => ty + .lifetime + .as_ref() + .map_or(false, |lifetime| lifetime.ident != "static"), + _ => false, // maybe implement later if there are common other cases + } +} -- cgit v1.2.3