diff options
author | Valentin Popov <valentin@popov.link> | 2024-01-08 00:21:28 +0300 |
---|---|---|
committer | Valentin Popov <valentin@popov.link> | 2024-01-08 00:21:28 +0300 |
commit | 1b6a04ca5504955c571d1c97504fb45ea0befee4 (patch) | |
tree | 7579f518b23313e8a9748a88ab6173d5e030b227 /vendor/miette-derive | |
parent | 5ecd8cf2cba827454317368b68571df0d13d7842 (diff) | |
download | fparkan-1b6a04ca5504955c571d1c97504fb45ea0befee4.tar.xz fparkan-1b6a04ca5504955c571d1c97504fb45ea0befee4.zip |
Initial vendor packages
Signed-off-by: Valentin Popov <valentin@popov.link>
Diffstat (limited to 'vendor/miette-derive')
-rw-r--r-- | vendor/miette-derive/.cargo-checksum.json | 1 | ||||
-rw-r--r-- | vendor/miette-derive/Cargo.toml | 31 | ||||
-rw-r--r-- | vendor/miette-derive/LICENSE | 229 | ||||
-rw-r--r-- | vendor/miette-derive/src/code.rs | 80 | ||||
-rw-r--r-- | vendor/miette-derive/src/diagnostic.rs | 397 | ||||
-rw-r--r-- | vendor/miette-derive/src/diagnostic_arg.rs | 42 | ||||
-rw-r--r-- | vendor/miette-derive/src/diagnostic_source.rs | 78 | ||||
-rw-r--r-- | vendor/miette-derive/src/fmt.rs | 235 | ||||
-rw-r--r-- | vendor/miette-derive/src/forward.rs | 161 | ||||
-rw-r--r-- | vendor/miette-derive/src/help.rs | 146 | ||||
-rw-r--r-- | vendor/miette-derive/src/label.rs | 207 | ||||
-rw-r--r-- | vendor/miette-derive/src/lib.rs | 32 | ||||
-rw-r--r-- | vendor/miette-derive/src/related.rs | 79 | ||||
-rw-r--r-- | vendor/miette-derive/src/severity.rs | 89 | ||||
-rw-r--r-- | vendor/miette-derive/src/source_code.rs | 81 | ||||
-rw-r--r-- | vendor/miette-derive/src/url.rs | 139 | ||||
-rw-r--r-- | vendor/miette-derive/src/utils.rs | 140 |
17 files changed, 2167 insertions, 0 deletions
diff --git a/vendor/miette-derive/.cargo-checksum.json b/vendor/miette-derive/.cargo-checksum.json new file mode 100644 index 0000000..940faa0 --- /dev/null +++ b/vendor/miette-derive/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.toml":"599ac29de32593292836994f2f10f64950344d3c052f142fa9d0512d652af53c","LICENSE":"610432849befa0e875d601d044e7e5441eb86c83d052681b23aef357b2a0b928","src/code.rs":"a66b16975c657e931bf650617c1652efed89710fdf7d8610f300f6538cdd9ad6","src/diagnostic.rs":"137e53721f10bf12f7bcd47dd699f3c7a44dad1fe7284b6924624c9853e96803","src/diagnostic_arg.rs":"6cc263379779b42e57152399bd8cc3ec872f7c047696705379cee9e147fc4156","src/diagnostic_source.rs":"e99b8decaa0d4dced2a0fd0667ebe348a6ff0169fc1298859c1a656d754ea86c","src/fmt.rs":"de956c7bdcf44458bb77d029c402e8aae3b97fc8de450710824e4b88f059229e","src/forward.rs":"778ab4ce5455efa9b9673ec83ddfae4b7064a4bf57a674a09fd40ef3b2ff2ff7","src/help.rs":"fa141050182100265174e40957d355dfa175d12822e6ef8ed2e5ba3e41abf991","src/label.rs":"762a5728672cd1ed1587bd048b43ea4ffd0974d49d976b3cf0f4282c8633f02a","src/lib.rs":"92aa427146667829d4faa2cf11da57defdedaa0f237384f3bfd7d8a0a593116f","src/related.rs":"fa6af1336fc6184f6727390757fa732d32cde249907fec56638f9a5f41bfcf45","src/severity.rs":"70fc6c592a0ad39fe9414b7115c7e0fb3e64eaa3ba5bef6b34ac35ad08ec6aa2","src/source_code.rs":"c3c8bf539dae2de0b0bc63ccf70dd4973b0516d9b95e9ac4190fa7468cce63de","src/url.rs":"b0ab232341844c0a3d604716624da8a9edcdb9cffbb188e008bfcc3f3046fe25","src/utils.rs":"db268d54ecc04bbba4cfb9b5a7b8b09922b2e4da396fdd47dd955b97a38740a2"},"package":"49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c"}
\ No newline at end of file diff --git a/vendor/miette-derive/Cargo.toml b/vendor/miette-derive/Cargo.toml new file mode 100644 index 0000000..84a9fe3 --- /dev/null +++ b/vendor/miette-derive/Cargo.toml @@ -0,0 +1,31 @@ +# 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 = "2018" +name = "miette-derive" +version = "5.10.0" +authors = ["Kat Marchán <kzm@zkat.tech>"] +description = "Derive macros for miette. Like `thiserror` for Diagnostics." +license = "Apache-2.0" +repository = "https://github.com/zkat/miette" + +[lib] +proc-macro = true + +[dependencies.proc-macro2] +version = "1.0" + +[dependencies.quote] +version = "1.0" + +[dependencies.syn] +version = "2.0.11" diff --git a/vendor/miette-derive/LICENSE b/vendor/miette-derive/LICENSE new file mode 100644 index 0000000..545f464 --- /dev/null +++ b/vendor/miette-derive/LICENSE @@ -0,0 +1,229 @@ +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 + +APPENDIX: How to +apply the Apache License to your work. + +To apply the Apache License to your work, +attach the following boilerplate notice, with the fields enclosed by brackets +"[]" replaced with your own identifying information. (Don't include the +brackets!) The text should be enclosed in the appropriate comment syntax for the +file format. We also recommend that a file or class name and description of +purpose be included on the same "printed page" as the copyright notice for easier +identification within third-party archives. + +Copyright [yyyy] Kat +Marchán + +Licensed under the Apache License, Version 2.0 (the "License"); + +you may +not use this file except in compliance with the License. + +You may obtain a copy +of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by +applicable law or agreed to in writing, software + +distributed under the License +is distributed on an "AS IS" BASIS, + +WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. + +See the License for the specific language +governing permissions and + +limitations under the License.
\ No newline at end of file diff --git a/vendor/miette-derive/src/code.rs b/vendor/miette-derive/src/code.rs new file mode 100644 index 0000000..22dc795 --- /dev/null +++ b/vendor/miette-derive/src/code.rs @@ -0,0 +1,80 @@ +use proc_macro2::TokenStream; +use quote::quote; +use syn::{ + parenthesized, + parse::{Parse, ParseStream}, + Token, +}; + +use crate::{ + diagnostic::{DiagnosticConcreteArgs, DiagnosticDef}, + forward::WhichFn, + utils::gen_all_variants_with, +}; + +#[derive(Debug)] +pub struct Code(pub String); + +impl Parse for Code { + fn parse(input: ParseStream) -> syn::Result<Self> { + let ident = input.parse::<syn::Ident>()?; + if ident == "code" { + let la = input.lookahead1(); + if la.peek(syn::token::Paren) { + let content; + parenthesized!(content in input); + let la = content.lookahead1(); + if la.peek(syn::LitStr) { + let str = content.parse::<syn::LitStr>()?; + Ok(Code(str.value())) + } else { + let path = content.parse::<syn::Path>()?; + Ok(Code( + path.segments + .iter() + .map(|s| s.ident.to_string()) + .collect::<Vec<_>>() + .join("::"), + )) + } + } else { + input.parse::<Token![=]>()?; + Ok(Code(input.parse::<syn::LitStr>()?.value())) + } + } else { + Err(syn::Error::new(ident.span(), "diagnostic code is required. Use #[diagnostic(code = ...)] or #[diagnostic(code(...))] to define one.")) + } + } +} + +impl Code { + pub(crate) fn gen_enum(variants: &[DiagnosticDef]) -> Option<TokenStream> { + gen_all_variants_with( + variants, + WhichFn::Code, + |ident, fields, DiagnosticConcreteArgs { code, .. }| { + let code = &code.as_ref()?.0; + Some(match fields { + syn::Fields::Named(_) => { + quote! { Self::#ident { .. } => std::option::Option::Some(std::boxed::Box::new(#code)), } + } + syn::Fields::Unnamed(_) => { + quote! { Self::#ident(..) => std::option::Option::Some(std::boxed::Box::new(#code)), } + } + syn::Fields::Unit => { + quote! { Self::#ident => std::option::Option::Some(std::boxed::Box::new(#code)), } + } + }) + }, + ) + } + + pub(crate) fn gen_struct(&self) -> Option<TokenStream> { + let code = &self.0; + Some(quote! { + fn code(&self) -> std::option::Option<std::boxed::Box<dyn std::fmt::Display + '_>> { + std::option::Option::Some(std::boxed::Box::new(#code)) + } + }) + } +} diff --git a/vendor/miette-derive/src/diagnostic.rs b/vendor/miette-derive/src/diagnostic.rs new file mode 100644 index 0000000..0173d2a --- /dev/null +++ b/vendor/miette-derive/src/diagnostic.rs @@ -0,0 +1,397 @@ +use proc_macro2::TokenStream; +use quote::quote; +use syn::{punctuated::Punctuated, DeriveInput, Token}; + +use crate::code::Code; +use crate::diagnostic_arg::DiagnosticArg; +use crate::diagnostic_source::DiagnosticSource; +use crate::forward::{Forward, WhichFn}; +use crate::help::Help; +use crate::label::Labels; +use crate::related::Related; +use crate::severity::Severity; +use crate::source_code::SourceCode; +use crate::url::Url; + +pub enum Diagnostic { + Struct { + generics: syn::Generics, + ident: syn::Ident, + fields: syn::Fields, + args: DiagnosticDefArgs, + }, + Enum { + ident: syn::Ident, + generics: syn::Generics, + variants: Vec<DiagnosticDef>, + }, +} + +pub struct DiagnosticDef { + pub ident: syn::Ident, + pub fields: syn::Fields, + pub args: DiagnosticDefArgs, +} + +pub enum DiagnosticDefArgs { + Transparent(Forward), + Concrete(Box<DiagnosticConcreteArgs>), +} + +impl DiagnosticDefArgs { + pub(crate) fn forward_or_override_enum( + &self, + variant: &syn::Ident, + which_fn: WhichFn, + mut f: impl FnMut(&DiagnosticConcreteArgs) -> Option<TokenStream>, + ) -> Option<TokenStream> { + match self { + Self::Transparent(forward) => Some(forward.gen_enum_match_arm(variant, which_fn)), + Self::Concrete(concrete) => f(concrete).or_else(|| { + concrete + .forward + .as_ref() + .map(|forward| forward.gen_enum_match_arm(variant, which_fn)) + }), + } + } +} + +#[derive(Default)] +pub struct DiagnosticConcreteArgs { + pub code: Option<Code>, + pub severity: Option<Severity>, + pub help: Option<Help>, + pub labels: Option<Labels>, + pub source_code: Option<SourceCode>, + pub url: Option<Url>, + pub forward: Option<Forward>, + pub related: Option<Related>, + pub diagnostic_source: Option<DiagnosticSource>, +} + +impl DiagnosticConcreteArgs { + fn for_fields(fields: &syn::Fields) -> Result<Self, syn::Error> { + let labels = Labels::from_fields(fields)?; + let source_code = SourceCode::from_fields(fields)?; + let related = Related::from_fields(fields)?; + let help = Help::from_fields(fields)?; + let diagnostic_source = DiagnosticSource::from_fields(fields)?; + Ok(DiagnosticConcreteArgs { + code: None, + help, + related, + severity: None, + labels, + url: None, + forward: None, + source_code, + diagnostic_source, + }) + } + + fn add_args( + &mut self, + attr: &syn::Attribute, + args: impl Iterator<Item = DiagnosticArg>, + errors: &mut Vec<syn::Error>, + ) { + for arg in args { + match arg { + DiagnosticArg::Transparent => { + errors.push(syn::Error::new_spanned(attr, "transparent not allowed")); + } + DiagnosticArg::Forward(to_field) => { + if self.forward.is_some() { + errors.push(syn::Error::new_spanned( + attr, + "forward has already been specified", + )); + } + self.forward = Some(to_field); + } + DiagnosticArg::Code(new_code) => { + if self.code.is_some() { + errors.push(syn::Error::new_spanned( + attr, + "code has already been specified", + )); + } + self.code = Some(new_code); + } + DiagnosticArg::Severity(sev) => { + if self.severity.is_some() { + errors.push(syn::Error::new_spanned( + attr, + "severity has already been specified", + )); + } + self.severity = Some(sev); + } + DiagnosticArg::Help(hl) => { + if self.help.is_some() { + errors.push(syn::Error::new_spanned( + attr, + "help has already been specified", + )); + } + self.help = Some(hl); + } + DiagnosticArg::Url(u) => { + if self.url.is_some() { + errors.push(syn::Error::new_spanned( + attr, + "url has already been specified", + )); + } + self.url = Some(u); + } + } + } + } +} + +impl DiagnosticDefArgs { + fn parse( + _ident: &syn::Ident, + fields: &syn::Fields, + attrs: &[&syn::Attribute], + allow_transparent: bool, + ) -> syn::Result<Self> { + let mut errors = Vec::new(); + + // Handle the only condition where Transparent is allowed + if allow_transparent && attrs.len() == 1 { + if let Ok(args) = + attrs[0].parse_args_with(Punctuated::<DiagnosticArg, Token![,]>::parse_terminated) + { + if matches!(args.first(), Some(DiagnosticArg::Transparent)) { + let forward = Forward::for_transparent_field(fields)?; + return Ok(Self::Transparent(forward)); + } + } + } + + // Create errors for any appearances of Transparent + let error_message = if allow_transparent { + "diagnostic(transparent) not allowed in combination with other args" + } else { + "diagnostic(transparent) not allowed here" + }; + fn is_transparent(d: &DiagnosticArg) -> bool { + matches!(d, DiagnosticArg::Transparent) + } + + let mut concrete = DiagnosticConcreteArgs::for_fields(fields)?; + for attr in attrs { + let args = + attr.parse_args_with(Punctuated::<DiagnosticArg, Token![,]>::parse_terminated); + let args = match args { + Ok(args) => args, + Err(error) => { + errors.push(error); + continue; + } + }; + + if args.iter().any(is_transparent) { + errors.push(syn::Error::new_spanned(attr, error_message)); + } + + let args = args + .into_iter() + .filter(|x| !matches!(x, DiagnosticArg::Transparent)); + + concrete.add_args(attr, args, &mut errors); + } + + let combined_error = errors.into_iter().reduce(|mut lhs, rhs| { + lhs.combine(rhs); + lhs + }); + if let Some(error) = combined_error { + Err(error) + } else { + Ok(DiagnosticDefArgs::Concrete(Box::new(concrete))) + } + } +} + +impl Diagnostic { + pub fn from_derive_input(input: DeriveInput) -> Result<Self, syn::Error> { + let input_attrs = input + .attrs + .iter() + .filter(|x| x.path().is_ident("diagnostic")) + .collect::<Vec<&syn::Attribute>>(); + Ok(match input.data { + syn::Data::Struct(data_struct) => { + let args = DiagnosticDefArgs::parse( + &input.ident, + &data_struct.fields, + &input_attrs, + true, + )?; + + Diagnostic::Struct { + fields: data_struct.fields, + ident: input.ident, + generics: input.generics, + args, + } + } + syn::Data::Enum(syn::DataEnum { variants, .. }) => { + let mut vars = Vec::new(); + for var in variants { + let mut variant_attrs = input_attrs.clone(); + variant_attrs + .extend(var.attrs.iter().filter(|x| x.path().is_ident("diagnostic"))); + let args = + DiagnosticDefArgs::parse(&var.ident, &var.fields, &variant_attrs, true)?; + vars.push(DiagnosticDef { + ident: var.ident, + fields: var.fields, + args, + }); + } + Diagnostic::Enum { + ident: input.ident, + generics: input.generics, + variants: vars, + } + } + syn::Data::Union(_) => { + return Err(syn::Error::new( + input.ident.span(), + "Can't derive Diagnostic for Unions", + )) + } + }) + } + + pub fn gen(&self) -> TokenStream { + match self { + Self::Struct { + ident, + fields, + generics, + args, + } => { + let (impl_generics, ty_generics, where_clause) = &generics.split_for_impl(); + match args { + DiagnosticDefArgs::Transparent(forward) => { + let code_method = forward.gen_struct_method(WhichFn::Code); + let help_method = forward.gen_struct_method(WhichFn::Help); + let url_method = forward.gen_struct_method(WhichFn::Url); + let labels_method = forward.gen_struct_method(WhichFn::Labels); + let source_code_method = forward.gen_struct_method(WhichFn::SourceCode); + let severity_method = forward.gen_struct_method(WhichFn::Severity); + let related_method = forward.gen_struct_method(WhichFn::Related); + let diagnostic_source_method = + forward.gen_struct_method(WhichFn::DiagnosticSource); + + quote! { + impl #impl_generics miette::Diagnostic for #ident #ty_generics #where_clause { + #code_method + #help_method + #url_method + #labels_method + #severity_method + #source_code_method + #related_method + #diagnostic_source_method + } + } + } + DiagnosticDefArgs::Concrete(concrete) => { + let forward = |which| { + concrete + .forward + .as_ref() + .map(|fwd| fwd.gen_struct_method(which)) + }; + let code_body = concrete + .code + .as_ref() + .and_then(|x| x.gen_struct()) + .or_else(|| forward(WhichFn::Code)); + let help_body = concrete + .help + .as_ref() + .and_then(|x| x.gen_struct(fields)) + .or_else(|| forward(WhichFn::Help)); + let sev_body = concrete + .severity + .as_ref() + .and_then(|x| x.gen_struct()) + .or_else(|| forward(WhichFn::Severity)); + let rel_body = concrete + .related + .as_ref() + .and_then(|x| x.gen_struct()) + .or_else(|| forward(WhichFn::Related)); + let url_body = concrete + .url + .as_ref() + .and_then(|x| x.gen_struct(ident, fields)) + .or_else(|| forward(WhichFn::Url)); + let labels_body = concrete + .labels + .as_ref() + .and_then(|x| x.gen_struct(fields)) + .or_else(|| forward(WhichFn::Labels)); + let src_body = concrete + .source_code + .as_ref() + .and_then(|x| x.gen_struct(fields)) + .or_else(|| forward(WhichFn::SourceCode)); + let diagnostic_source = concrete + .diagnostic_source + .as_ref() + .and_then(|x| x.gen_struct()) + .or_else(|| forward(WhichFn::DiagnosticSource)); + quote! { + impl #impl_generics miette::Diagnostic for #ident #ty_generics #where_clause { + #code_body + #help_body + #sev_body + #rel_body + #url_body + #labels_body + #src_body + #diagnostic_source + } + } + } + } + } + Self::Enum { + ident, + generics, + variants, + } => { + let (impl_generics, ty_generics, where_clause) = &generics.split_for_impl(); + let code_body = Code::gen_enum(variants); + let help_body = Help::gen_enum(variants); + let sev_body = Severity::gen_enum(variants); + let labels_body = Labels::gen_enum(variants); + let src_body = SourceCode::gen_enum(variants); + let rel_body = Related::gen_enum(variants); + let url_body = Url::gen_enum(ident, variants); + let diagnostic_source_body = DiagnosticSource::gen_enum(variants); + quote! { + impl #impl_generics miette::Diagnostic for #ident #ty_generics #where_clause { + #code_body + #help_body + #sev_body + #labels_body + #src_body + #rel_body + #url_body + #diagnostic_source_body + } + } + } + } + } +} diff --git a/vendor/miette-derive/src/diagnostic_arg.rs b/vendor/miette-derive/src/diagnostic_arg.rs new file mode 100644 index 0000000..bade6f0 --- /dev/null +++ b/vendor/miette-derive/src/diagnostic_arg.rs @@ -0,0 +1,42 @@ +use syn::parse::{Parse, ParseStream}; + +use crate::code::Code; +use crate::forward::Forward; +use crate::help::Help; +use crate::severity::Severity; +use crate::url::Url; + +pub enum DiagnosticArg { + Transparent, + Code(Code), + Severity(Severity), + Help(Help), + Url(Url), + Forward(Forward), +} + +impl Parse for DiagnosticArg { + fn parse(input: ParseStream) -> syn::Result<Self> { + let ident = input.fork().parse::<syn::Ident>()?; + if ident == "transparent" { + // consume the token + let _: syn::Ident = input.parse()?; + Ok(DiagnosticArg::Transparent) + } else if ident == "forward" { + Ok(DiagnosticArg::Forward(input.parse()?)) + } else if ident == "code" { + Ok(DiagnosticArg::Code(input.parse()?)) + } else if ident == "severity" { + Ok(DiagnosticArg::Severity(input.parse()?)) + } else if ident == "help" { + Ok(DiagnosticArg::Help(input.parse()?)) + } else if ident == "url" { + Ok(DiagnosticArg::Url(input.parse()?)) + } else { + Err(syn::Error::new( + ident.span(), + "Unrecognized diagnostic option", + )) + } + } +} diff --git a/vendor/miette-derive/src/diagnostic_source.rs b/vendor/miette-derive/src/diagnostic_source.rs new file mode 100644 index 0000000..1104eb7 --- /dev/null +++ b/vendor/miette-derive/src/diagnostic_source.rs @@ -0,0 +1,78 @@ +use proc_macro2::TokenStream; +use quote::quote; +use syn::spanned::Spanned; + +use crate::forward::WhichFn; +use crate::{ + diagnostic::{DiagnosticConcreteArgs, DiagnosticDef}, + utils::{display_pat_members, gen_all_variants_with}, +}; + +pub struct DiagnosticSource(syn::Member); + +impl DiagnosticSource { + pub(crate) fn from_fields(fields: &syn::Fields) -> syn::Result<Option<Self>> { + match fields { + syn::Fields::Named(named) => Self::from_fields_vec(named.named.iter().collect()), + syn::Fields::Unnamed(unnamed) => { + Self::from_fields_vec(unnamed.unnamed.iter().collect()) + } + syn::Fields::Unit => Ok(None), + } + } + + fn from_fields_vec(fields: Vec<&syn::Field>) -> syn::Result<Option<Self>> { + for (i, field) in fields.iter().enumerate() { + for attr in &field.attrs { + if attr.path().is_ident("diagnostic_source") { + let diagnostic_source = if let Some(ident) = field.ident.clone() { + syn::Member::Named(ident) + } else { + syn::Member::Unnamed(syn::Index { + index: i as u32, + span: field.span(), + }) + }; + return Ok(Some(DiagnosticSource(diagnostic_source))); + } + } + } + Ok(None) + } + + pub(crate) fn gen_enum(variants: &[DiagnosticDef]) -> Option<TokenStream> { + gen_all_variants_with( + variants, + WhichFn::DiagnosticSource, + |ident, + fields, + DiagnosticConcreteArgs { + diagnostic_source, .. + }| { + let (display_pat, _display_members) = display_pat_members(fields); + diagnostic_source.as_ref().map(|diagnostic_source| { + let rel = match &diagnostic_source.0 { + syn::Member::Named(ident) => ident.clone(), + syn::Member::Unnamed(syn::Index { index, .. }) => { + quote::format_ident!("_{}", index) + } + }; + quote! { + Self::#ident #display_pat => { + std::option::Option::Some(std::borrow::Borrow::borrow(#rel)) + } + } + }) + }, + ) + } + + pub(crate) fn gen_struct(&self) -> Option<TokenStream> { + let rel = &self.0; + Some(quote! { + fn diagnostic_source<'a>(&'a self) -> std::option::Option<&'a dyn miette::Diagnostic> { + std::option::Option::Some(std::borrow::Borrow::borrow(&self.#rel)) + } + }) + } +} diff --git a/vendor/miette-derive/src/fmt.rs b/vendor/miette-derive/src/fmt.rs new file mode 100644 index 0000000..692c5ad --- /dev/null +++ b/vendor/miette-derive/src/fmt.rs @@ -0,0 +1,235 @@ +// NOTE: Most code in this file is taken straight from `thiserror`. +use std::collections::HashSet as Set; +use std::iter::FromIterator; + +use proc_macro2::{Delimiter, Group, TokenStream, TokenTree}; +use quote::{format_ident, quote, quote_spanned, ToTokens}; +use syn::ext::IdentExt; +use syn::parse::{ParseStream, Parser}; +use syn::{braced, bracketed, parenthesized, Ident, Index, LitStr, Member, Result, Token}; + +#[derive(Clone)] +pub struct Display { + pub fmt: LitStr, + pub args: TokenStream, + pub has_bonus_display: bool, +} + +impl ToTokens for Display { + fn to_tokens(&self, tokens: &mut TokenStream) { + let fmt = &self.fmt; + let args = &self.args; + tokens.extend(quote! { + write!(__formatter, #fmt #args) + }); + } +} + +impl Display { + // Transform `"error {var}"` to `"error {}", var`. + pub fn expand_shorthand(&mut self, members: &Set<Member>) { + let raw_args = self.args.clone(); + let mut named_args = explicit_named_args.parse2(raw_args).unwrap(); + + let span = self.fmt.span(); + let fmt = self.fmt.value(); + let mut read = fmt.as_str(); + let mut out = String::new(); + let mut args = self.args.clone(); + let mut has_bonus_display = false; + + let mut has_trailing_comma = false; + if let Some(TokenTree::Punct(punct)) = args.clone().into_iter().last() { + if punct.as_char() == ',' { + has_trailing_comma = true; + } + } + + while let Some(brace) = read.find('{') { + out += &read[..brace + 1]; + read = &read[brace + 1..]; + if read.starts_with('{') { + out.push('{'); + read = &read[1..]; + continue; + } + let next = match read.chars().next() { + Some(next) => next, + None => return, + }; + let member = match next { + '0'..='9' => { + let int = take_int(&mut read); + let member = match int.parse::<u32>() { + Ok(index) => Member::Unnamed(Index { index, span }), + Err(_) => return, + }; + if !members.contains(&member) { + out += ∫ + continue; + } + member + } + 'a'..='z' | 'A'..='Z' | '_' => { + let mut ident = take_ident(&mut read); + ident.set_span(span); + Member::Named(ident) + } + _ => continue, + }; + let local = match &member { + Member::Unnamed(index) => format_ident!("_{}", index), + Member::Named(ident) => ident.clone(), + }; + let mut formatvar = local.clone(); + if formatvar.to_string().starts_with("r#") { + formatvar = format_ident!("r_{}", formatvar); + } + if formatvar.to_string().starts_with('_') { + // Work around leading underscore being rejected by 1.40 and + // older compilers. https://github.com/rust-lang/rust/pull/66847 + formatvar = format_ident!("field_{}", formatvar); + } + out += &formatvar.to_string(); + if !named_args.insert(formatvar.clone()) { + // Already specified in the format argument list. + continue; + } + if !has_trailing_comma { + args.extend(quote_spanned!(span=> ,)); + } + args.extend(quote_spanned!(span=> #formatvar = #local)); + if read.starts_with('}') && members.contains(&member) { + has_bonus_display = true; + // args.extend(quote_spanned!(span=> .as_display())); + } + has_trailing_comma = false; + } + + out += read; + self.fmt = LitStr::new(&out, self.fmt.span()); + self.args = args; + self.has_bonus_display = has_bonus_display; + } +} + +fn explicit_named_args(input: ParseStream) -> Result<Set<Ident>> { + let mut named_args = Set::new(); + + while !input.is_empty() { + if input.peek(Token![,]) && input.peek2(Ident::peek_any) && input.peek3(Token![=]) { + input.parse::<Token![,]>()?; + let ident = input.call(Ident::parse_any)?; + input.parse::<Token![=]>()?; + named_args.insert(ident); + } else { + input.parse::<TokenTree>()?; + } + } + + Ok(named_args) +} + +fn take_int(read: &mut &str) -> String { + let mut int = String::new(); + for (i, ch) in read.char_indices() { + match ch { + '0'..='9' => int.push(ch), + _ => { + *read = &read[i..]; + break; + } + } + } + int +} + +fn take_ident(read: &mut &str) -> Ident { + let mut ident = String::new(); + let raw = read.starts_with("r#"); + if raw { + ident.push_str("r#"); + *read = &read[2..]; + } + for (i, ch) in read.char_indices() { + match ch { + 'a'..='z' | 'A'..='Z' | '0'..='9' | '_' => ident.push(ch), + _ => { + *read = &read[i..]; + break; + } + } + } + Ident::parse_any.parse_str(&ident).unwrap() +} + +pub fn parse_token_expr(input: ParseStream, mut begin_expr: bool) -> Result<TokenStream> { + let mut tokens = Vec::new(); + while !input.is_empty() { + if begin_expr && input.peek(Token![.]) { + if input.peek2(Ident) { + input.parse::<Token![.]>()?; + begin_expr = false; + continue; + } + if input.peek2(syn::LitInt) { + input.parse::<Token![.]>()?; + let int: Index = input.parse()?; + let ident = format_ident!("_{}", int.index, span = int.span); + tokens.push(TokenTree::Ident(ident)); + begin_expr = false; + continue; + } + } + + begin_expr = input.peek(Token![break]) + || input.peek(Token![continue]) + || input.peek(Token![if]) + || input.peek(Token![in]) + || input.peek(Token![match]) + || input.peek(Token![mut]) + || input.peek(Token![return]) + || input.peek(Token![while]) + || input.peek(Token![+]) + || input.peek(Token![&]) + || input.peek(Token![!]) + || input.peek(Token![^]) + || input.peek(Token![,]) + || input.peek(Token![/]) + || input.peek(Token![=]) + || input.peek(Token![>]) + || input.peek(Token![<]) + || input.peek(Token![|]) + || input.peek(Token![%]) + || input.peek(Token![;]) + || input.peek(Token![*]) + || input.peek(Token![-]); + + let token: TokenTree = if input.peek(syn::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(syn::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(syn::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)) +} diff --git a/vendor/miette-derive/src/forward.rs b/vendor/miette-derive/src/forward.rs new file mode 100644 index 0000000..171019a --- /dev/null +++ b/vendor/miette-derive/src/forward.rs @@ -0,0 +1,161 @@ +use proc_macro2::TokenStream; +use quote::{format_ident, quote}; +use syn::{ + parenthesized, + parse::{Parse, ParseStream}, + spanned::Spanned, +}; + +pub enum Forward { + Unnamed(usize), + Named(syn::Ident), +} + +impl Parse for Forward { + fn parse(input: ParseStream) -> syn::Result<Self> { + let forward = input.parse::<syn::Ident>()?; + if forward != "forward" { + return Err(syn::Error::new(forward.span(), "msg")); + } + let content; + parenthesized!(content in input); + let looky = content.lookahead1(); + if looky.peek(syn::LitInt) { + let int: syn::LitInt = content.parse()?; + let index = int.base10_parse()?; + return Ok(Forward::Unnamed(index)); + } + Ok(Forward::Named(content.parse()?)) + } +} + +#[derive(Copy, Clone)] +pub enum WhichFn { + Code, + Help, + Url, + Severity, + Labels, + SourceCode, + Related, + DiagnosticSource, +} + +impl WhichFn { + pub fn method_call(&self) -> TokenStream { + match self { + Self::Code => quote! { code() }, + Self::Help => quote! { help() }, + Self::Url => quote! { url() }, + Self::Severity => quote! { severity() }, + Self::Labels => quote! { labels() }, + Self::SourceCode => quote! { source_code() }, + Self::Related => quote! { related() }, + Self::DiagnosticSource => quote! { diagnostic_source() }, + } + } + + pub fn signature(&self) -> TokenStream { + match self { + Self::Code => quote! { + fn code(& self) -> std::option::Option<std::boxed::Box<dyn std::fmt::Display + '_>> + }, + Self::Help => quote! { + fn help(& self) -> std::option::Option<std::boxed::Box<dyn std::fmt::Display + '_>> + }, + Self::Url => quote! { + fn url(& self) -> std::option::Option<std::boxed::Box<dyn std::fmt::Display + '_>> + }, + Self::Severity => quote! { + fn severity(&self) -> std::option::Option<miette::Severity> + }, + Self::Related => quote! { + fn related(&self) -> std::option::Option<std::boxed::Box<dyn std::iter::Iterator<Item = &dyn miette::Diagnostic> + '_>> + }, + Self::Labels => quote! { + fn labels(&self) -> std::option::Option<std::boxed::Box<dyn std::iter::Iterator<Item = miette::LabeledSpan> + '_>> + }, + Self::SourceCode => quote! { + fn source_code(&self) -> std::option::Option<&dyn miette::SourceCode> + }, + Self::DiagnosticSource => quote! { + fn diagnostic_source(&self) -> std::option::Option<&dyn miette::Diagnostic> + }, + } + } + + pub fn catchall_arm(&self) -> TokenStream { + quote! { _ => std::option::Option::None } + } +} + +impl Forward { + pub fn for_transparent_field(fields: &syn::Fields) -> syn::Result<Self> { + let make_err = || { + syn::Error::new( + fields.span(), + "you can only use #[diagnostic(transparent)] with exactly one field", + ) + }; + match fields { + syn::Fields::Named(named) => { + let mut iter = named.named.iter(); + let field = iter.next().ok_or_else(make_err)?; + if iter.next().is_some() { + return Err(make_err()); + } + let field_name = field + .ident + .clone() + .unwrap_or_else(|| format_ident!("unnamed")); + Ok(Self::Named(field_name)) + } + syn::Fields::Unnamed(unnamed) => { + if unnamed.unnamed.iter().len() != 1 { + return Err(make_err()); + } + Ok(Self::Unnamed(0)) + } + _ => Err(syn::Error::new( + fields.span(), + "you cannot use #[diagnostic(transparent)] with a unit struct or a unit variant", + )), + } + } + + pub fn gen_struct_method(&self, which_fn: WhichFn) -> TokenStream { + let signature = which_fn.signature(); + let method_call = which_fn.method_call(); + + let field_name = match self { + Forward::Named(field_name) => quote!(#field_name), + Forward::Unnamed(index) => { + let index = syn::Index::from(*index); + quote!(#index) + } + }; + + quote! { + #[inline] + #signature { + self.#field_name.#method_call + } + } + } + + pub fn gen_enum_match_arm(&self, variant: &syn::Ident, which_fn: WhichFn) -> TokenStream { + let method_call = which_fn.method_call(); + match self { + Forward::Named(field_name) => quote! { + Self::#variant { #field_name, .. } => #field_name.#method_call, + }, + Forward::Unnamed(index) => { + let underscores: Vec<_> = core::iter::repeat(quote! { _, }).take(*index).collect(); + let unnamed = format_ident!("unnamed"); + quote! { + Self::#variant ( #(#underscores)* #unnamed, .. ) => #unnamed.#method_call, + } + } + } + } +} diff --git a/vendor/miette-derive/src/help.rs b/vendor/miette-derive/src/help.rs new file mode 100644 index 0000000..1c21054 --- /dev/null +++ b/vendor/miette-derive/src/help.rs @@ -0,0 +1,146 @@ +use proc_macro2::TokenStream; +use quote::{format_ident, quote}; +use syn::{ + parenthesized, + parse::{Parse, ParseStream}, + spanned::Spanned, + Fields, Token, +}; + +use crate::{ + diagnostic::{DiagnosticConcreteArgs, DiagnosticDef}, + utils::{display_pat_members, gen_all_variants_with}, +}; +use crate::{ + fmt::{self, Display}, + forward::WhichFn, +}; + +pub enum Help { + Display(Display), + Field(syn::Member, Box<syn::Type>), +} + +impl Parse for Help { + fn parse(input: ParseStream) -> syn::Result<Self> { + let ident = input.parse::<syn::Ident>()?; + if ident == "help" { + let la = input.lookahead1(); + if la.peek(syn::token::Paren) { + let content; + parenthesized!(content in input); + let fmt = content.parse()?; + let args = if content.is_empty() { + TokenStream::new() + } else { + fmt::parse_token_expr(&content, false)? + }; + let display = Display { + fmt, + args, + has_bonus_display: false, + }; + Ok(Help::Display(display)) + } else { + input.parse::<Token![=]>()?; + Ok(Help::Display(Display { + fmt: input.parse()?, + args: TokenStream::new(), + has_bonus_display: false, + })) + } + } else { + Err(syn::Error::new(ident.span(), "not a help")) + } + } +} + +impl Help { + pub(crate) fn from_fields(fields: &syn::Fields) -> syn::Result<Option<Self>> { + match fields { + syn::Fields::Named(named) => Self::from_fields_vec(named.named.iter().collect()), + syn::Fields::Unnamed(unnamed) => { + Self::from_fields_vec(unnamed.unnamed.iter().collect()) + } + syn::Fields::Unit => Ok(None), + } + } + + fn from_fields_vec(fields: Vec<&syn::Field>) -> syn::Result<Option<Self>> { + for (i, field) in fields.iter().enumerate() { + for attr in &field.attrs { + if attr.path().is_ident("help") { + let help = if let Some(ident) = field.ident.clone() { + syn::Member::Named(ident) + } else { + syn::Member::Unnamed(syn::Index { + index: i as u32, + span: field.span(), + }) + }; + return Ok(Some(Help::Field(help, Box::new(field.ty.clone())))); + } + } + } + Ok(None) + } + pub(crate) fn gen_enum(variants: &[DiagnosticDef]) -> Option<TokenStream> { + gen_all_variants_with( + variants, + WhichFn::Help, + |ident, fields, DiagnosticConcreteArgs { help, .. }| { + let (display_pat, display_members) = display_pat_members(fields); + match &help.as_ref()? { + Help::Display(display) => { + let (fmt, args) = display.expand_shorthand_cloned(&display_members); + Some(quote! { + Self::#ident #display_pat => std::option::Option::Some(std::boxed::Box::new(format!(#fmt #args))), + }) + } + Help::Field(member, ty) => { + let help = match &member { + syn::Member::Named(ident) => ident.clone(), + syn::Member::Unnamed(syn::Index { index, .. }) => { + format_ident!("_{}", index) + } + }; + let var = quote! { __miette_internal_var }; + Some(quote! { + Self::#ident #display_pat => { + use miette::macro_helpers::ToOption; + miette::macro_helpers::OptionalWrapper::<#ty>::new().to_option(&#help).as_ref().map(|#var| -> std::boxed::Box<dyn std::fmt::Display + '_> { std::boxed::Box::new(format!("{}", #var)) }) + }, + }) + } + } + }, + ) + } + + pub(crate) fn gen_struct(&self, fields: &Fields) -> Option<TokenStream> { + let (display_pat, display_members) = display_pat_members(fields); + match self { + Help::Display(display) => { + let (fmt, args) = display.expand_shorthand_cloned(&display_members); + Some(quote! { + fn help(&self) -> std::option::Option<std::boxed::Box<dyn std::fmt::Display + '_>> { + #[allow(unused_variables, deprecated)] + let Self #display_pat = self; + std::option::Option::Some(std::boxed::Box::new(format!(#fmt #args))) + } + }) + } + Help::Field(member, ty) => { + let var = quote! { __miette_internal_var }; + Some(quote! { + fn help(&self) -> std::option::Option<std::boxed::Box<dyn std::fmt::Display + '_>> { + #[allow(unused_variables, deprecated)] + let Self #display_pat = self; + use miette::macro_helpers::ToOption; + miette::macro_helpers::OptionalWrapper::<#ty>::new().to_option(&self.#member).as_ref().map(|#var| -> std::boxed::Box<dyn std::fmt::Display + '_> { std::boxed::Box::new(format!("{}", #var)) }) + } + }) + } + } + } +} diff --git a/vendor/miette-derive/src/label.rs b/vendor/miette-derive/src/label.rs new file mode 100644 index 0000000..e0bc70a --- /dev/null +++ b/vendor/miette-derive/src/label.rs @@ -0,0 +1,207 @@ +use proc_macro2::TokenStream; +use quote::{format_ident, quote}; +use syn::{ + parenthesized, + parse::{Parse, ParseStream}, + spanned::Spanned, + Token, +}; + +use crate::{ + diagnostic::{DiagnosticConcreteArgs, DiagnosticDef}, + fmt::{self, Display}, + forward::WhichFn, + utils::{display_pat_members, gen_all_variants_with}, +}; + +pub struct Labels(Vec<Label>); + +struct Label { + label: Option<Display>, + ty: syn::Type, + span: syn::Member, +} + +struct LabelAttr { + label: Option<Display>, +} + +impl Parse for LabelAttr { + fn parse(input: ParseStream) -> syn::Result<Self> { + // Skip a token. + // This should receive one of: + // - label = "..." + // - label("...") + let _ = input.step(|cursor| { + if let Some((_, next)) = cursor.token_tree() { + Ok(((), next)) + } else { + Err(cursor.error("unexpected empty attribute")) + } + }); + let la = input.lookahead1(); + let label = if la.peek(syn::token::Paren) { + // #[label("{}", x)] + let content; + parenthesized!(content in input); + if content.peek(syn::LitStr) { + let fmt = content.parse()?; + let args = if content.is_empty() { + TokenStream::new() + } else { + fmt::parse_token_expr(&content, false)? + }; + let display = Display { + fmt, + args, + has_bonus_display: false, + }; + Some(display) + } else { + return Err(syn::Error::new(input.span(), "Invalid argument to label() attribute. The first argument must be a literal string.")); + } + } else if la.peek(Token![=]) { + // #[label = "blabla"] + input.parse::<Token![=]>()?; + Some(Display { + fmt: input.parse()?, + args: TokenStream::new(), + has_bonus_display: false, + }) + } else { + None + }; + Ok(LabelAttr { label }) + } +} + +impl Labels { + pub fn from_fields(fields: &syn::Fields) -> syn::Result<Option<Self>> { + match fields { + syn::Fields::Named(named) => Self::from_fields_vec(named.named.iter().collect()), + syn::Fields::Unnamed(unnamed) => { + Self::from_fields_vec(unnamed.unnamed.iter().collect()) + } + syn::Fields::Unit => Ok(None), + } + } + + fn from_fields_vec(fields: Vec<&syn::Field>) -> syn::Result<Option<Self>> { + let mut labels = Vec::new(); + for (i, field) in fields.iter().enumerate() { + for attr in &field.attrs { + if attr.path().is_ident("label") { + let span = if let Some(ident) = field.ident.clone() { + syn::Member::Named(ident) + } else { + syn::Member::Unnamed(syn::Index { + index: i as u32, + span: field.span(), + }) + }; + use quote::ToTokens; + let LabelAttr { label } = + syn::parse2::<LabelAttr>(attr.meta.to_token_stream())?; + labels.push(Label { + label, + span, + ty: field.ty.clone(), + }); + } + } + } + if labels.is_empty() { + Ok(None) + } else { + Ok(Some(Labels(labels))) + } + } + + pub(crate) fn gen_struct(&self, fields: &syn::Fields) -> Option<TokenStream> { + let (display_pat, display_members) = display_pat_members(fields); + let labels = self.0.iter().map(|highlight| { + let Label { span, label, ty } = highlight; + let var = quote! { __miette_internal_var }; + if let Some(display) = label { + let (fmt, args) = display.expand_shorthand_cloned(&display_members); + quote! { + miette::macro_helpers::OptionalWrapper::<#ty>::new().to_option(&self.#span) + .map(|#var| miette::LabeledSpan::new_with_span( + std::option::Option::Some(format!(#fmt #args)), + #var.clone(), + )) + } + } else { + quote! { + miette::macro_helpers::OptionalWrapper::<#ty>::new().to_option(&self.#span) + .map(|#var| miette::LabeledSpan::new_with_span( + std::option::Option::None, + #var.clone(), + )) + } + } + }); + Some(quote! { + #[allow(unused_variables)] + fn labels(&self) -> std::option::Option<std::boxed::Box<dyn std::iter::Iterator<Item = miette::LabeledSpan> + '_>> { + use miette::macro_helpers::ToOption; + let Self #display_pat = self; + std::option::Option::Some(Box::new(vec![ + #(#labels),* + ].into_iter().filter(Option::is_some).map(Option::unwrap))) + } + }) + } + + pub(crate) fn gen_enum(variants: &[DiagnosticDef]) -> Option<TokenStream> { + gen_all_variants_with( + variants, + WhichFn::Labels, + |ident, fields, DiagnosticConcreteArgs { labels, .. }| { + let (display_pat, display_members) = display_pat_members(fields); + labels.as_ref().and_then(|labels| { + let variant_labels = labels.0.iter().map(|label| { + let Label { span, label, ty } = label; + let field = match &span { + syn::Member::Named(ident) => ident.clone(), + syn::Member::Unnamed(syn::Index { index, .. }) => { + format_ident!("_{}", index) + } + }; + let var = quote! { __miette_internal_var }; + if let Some(display) = label { + let (fmt, args) = display.expand_shorthand_cloned(&display_members); + quote! { + miette::macro_helpers::OptionalWrapper::<#ty>::new().to_option(#field) + .map(|#var| miette::LabeledSpan::new_with_span( + std::option::Option::Some(format!(#fmt #args)), + #var.clone(), + )) + } + } else { + quote! { + miette::macro_helpers::OptionalWrapper::<#ty>::new().to_option(#field) + .map(|#var| miette::LabeledSpan::new_with_span( + std::option::Option::None, + #var.clone(), + )) + } + } + }); + let variant_name = ident.clone(); + match &fields { + syn::Fields::Unit => None, + _ => Some(quote! { + Self::#variant_name #display_pat => { + use miette::macro_helpers::ToOption; + std::option::Option::Some(std::boxed::Box::new(vec![ + #(#variant_labels),* + ].into_iter().filter(Option::is_some).map(Option::unwrap))) + } + }), + } + }) + }, + ) + } +} diff --git a/vendor/miette-derive/src/lib.rs b/vendor/miette-derive/src/lib.rs new file mode 100644 index 0000000..0f7e64e --- /dev/null +++ b/vendor/miette-derive/src/lib.rs @@ -0,0 +1,32 @@ +use quote::quote; +use syn::{parse_macro_input, DeriveInput}; + +use diagnostic::Diagnostic; + +mod code; +mod diagnostic; +mod diagnostic_arg; +mod diagnostic_source; +mod fmt; +mod forward; +mod help; +mod label; +mod related; +mod severity; +mod source_code; +mod url; +mod utils; + +#[proc_macro_derive( + Diagnostic, + attributes(diagnostic, source_code, label, related, help, diagnostic_source) +)] +pub fn derive_diagnostic(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input = parse_macro_input!(input as DeriveInput); + let cmd = match Diagnostic::from_derive_input(input) { + Ok(cmd) => cmd.gen(), + Err(err) => return err.to_compile_error().into(), + }; + // panic!("{:#}", cmd.to_token_stream()); + quote!(#cmd).into() +} diff --git a/vendor/miette-derive/src/related.rs b/vendor/miette-derive/src/related.rs new file mode 100644 index 0000000..9b7f9e1 --- /dev/null +++ b/vendor/miette-derive/src/related.rs @@ -0,0 +1,79 @@ +use proc_macro2::TokenStream; +use quote::{format_ident, quote}; +use syn::spanned::Spanned; + +use crate::{ + diagnostic::{DiagnosticConcreteArgs, DiagnosticDef}, + forward::WhichFn, + utils::{display_pat_members, gen_all_variants_with}, +}; + +pub struct Related(syn::Member); + +impl Related { + pub(crate) fn from_fields(fields: &syn::Fields) -> syn::Result<Option<Self>> { + match fields { + syn::Fields::Named(named) => Self::from_fields_vec(named.named.iter().collect()), + syn::Fields::Unnamed(unnamed) => { + Self::from_fields_vec(unnamed.unnamed.iter().collect()) + } + syn::Fields::Unit => Ok(None), + } + } + + fn from_fields_vec(fields: Vec<&syn::Field>) -> syn::Result<Option<Self>> { + for (i, field) in fields.iter().enumerate() { + for attr in &field.attrs { + if attr.path().is_ident("related") { + let related = if let Some(ident) = field.ident.clone() { + syn::Member::Named(ident) + } else { + syn::Member::Unnamed(syn::Index { + index: i as u32, + span: field.span(), + }) + }; + return Ok(Some(Related(related))); + } + } + } + Ok(None) + } + + pub(crate) fn gen_enum(variants: &[DiagnosticDef]) -> Option<TokenStream> { + gen_all_variants_with( + variants, + WhichFn::Related, + |ident, fields, DiagnosticConcreteArgs { related, .. }| { + let (display_pat, _display_members) = display_pat_members(fields); + related.as_ref().map(|related| { + let rel = match &related.0 { + syn::Member::Named(ident) => ident.clone(), + syn::Member::Unnamed(syn::Index { index, .. }) => { + format_ident!("_{}", index) + } + }; + quote! { + Self::#ident #display_pat => { + std::option::Option::Some(std::boxed::Box::new( + #rel.iter().map(|x| -> &(dyn miette::Diagnostic) { &*x }) + )) + } + } + }) + }, + ) + } + + pub(crate) fn gen_struct(&self) -> Option<TokenStream> { + let rel = &self.0; + Some(quote! { + fn related<'a>(&'a self) -> std::option::Option<std::boxed::Box<dyn std::iter::Iterator<Item = &'a dyn miette::Diagnostic> + 'a>> { + use ::core::borrow::Borrow; + std::option::Option::Some(std::boxed::Box::new( + self.#rel.iter().map(|x| -> &(dyn miette::Diagnostic) { &*x.borrow() }) + )) + } + }) + } +} diff --git a/vendor/miette-derive/src/severity.rs b/vendor/miette-derive/src/severity.rs new file mode 100644 index 0000000..4f26e4e --- /dev/null +++ b/vendor/miette-derive/src/severity.rs @@ -0,0 +1,89 @@ +use proc_macro2::{Span, TokenStream}; +use quote::quote; +use syn::{ + parenthesized, + parse::{Parse, ParseStream}, + Token, +}; + +use crate::{ + diagnostic::{DiagnosticConcreteArgs, DiagnosticDef}, + forward::WhichFn, + utils::gen_all_variants_with, +}; + +pub struct Severity(pub syn::Ident); + +impl Parse for Severity { + fn parse(input: ParseStream) -> syn::Result<Self> { + let ident = input.parse::<syn::Ident>()?; + if ident == "severity" { + let la = input.lookahead1(); + if la.peek(syn::token::Paren) { + let content; + parenthesized!(content in input); + let la = content.lookahead1(); + if la.peek(syn::LitStr) { + let str = content.parse::<syn::LitStr>()?; + let sev = get_severity(&str.value(), str.span())?; + Ok(Severity(syn::Ident::new(&sev, str.span()))) + } else { + let ident = content.parse::<syn::Ident>()?; + let sev = get_severity(&ident.to_string(), ident.span())?; + Ok(Severity(syn::Ident::new(&sev, ident.span()))) + } + } else { + input.parse::<Token![=]>()?; + let str = input.parse::<syn::LitStr>()?; + let sev = get_severity(&str.value(), str.span())?; + Ok(Severity(syn::Ident::new(&sev, str.span()))) + } + } else { + Err(syn::Error::new( + ident.span(), + "MIETTE BUG: not a severity option", + )) + } + } +} + +fn get_severity(input: &str, span: Span) -> syn::Result<String> { + match input.to_lowercase().as_ref() { + "error" | "err" => Ok("Error".into()), + "warning" | "warn" => Ok("Warning".into()), + "advice" | "adv" | "info" => Ok("Advice".into()), + _ => Err(syn::Error::new( + span, + "Invalid severity level. Only Error, Warning, and Advice are supported.", + )), + } +} + +impl Severity { + pub(crate) fn gen_enum(variants: &[DiagnosticDef]) -> Option<TokenStream> { + gen_all_variants_with( + variants, + WhichFn::Severity, + |ident, fields, DiagnosticConcreteArgs { severity, .. }| { + let severity = &severity.as_ref()?.0; + let fields = match fields { + syn::Fields::Named(_) => quote! { { .. } }, + syn::Fields::Unnamed(_) => quote! { (..) }, + syn::Fields::Unit => quote! {}, + }; + Some( + quote! { Self::#ident #fields => std::option::Option::Some(miette::Severity::#severity), }, + ) + }, + ) + } + + pub(crate) fn gen_struct(&self) -> Option<TokenStream> { + let sev = &self.0; + Some(quote! { + fn severity(&self) -> std::option::Option<miette::Severity> { + Some(miette::Severity::#sev) + } + }) + } +} diff --git a/vendor/miette-derive/src/source_code.rs b/vendor/miette-derive/src/source_code.rs new file mode 100644 index 0000000..62f28e7 --- /dev/null +++ b/vendor/miette-derive/src/source_code.rs @@ -0,0 +1,81 @@ +use proc_macro2::TokenStream; +use quote::{format_ident, quote}; +use syn::spanned::Spanned; + +use crate::{ + diagnostic::{DiagnosticConcreteArgs, DiagnosticDef}, + forward::WhichFn, + utils::{display_pat_members, gen_all_variants_with}, +}; + +pub struct SourceCode { + source_code: syn::Member, +} + +impl SourceCode { + pub fn from_fields(fields: &syn::Fields) -> syn::Result<Option<Self>> { + match fields { + syn::Fields::Named(named) => Self::from_fields_vec(named.named.iter().collect()), + syn::Fields::Unnamed(unnamed) => { + Self::from_fields_vec(unnamed.unnamed.iter().collect()) + } + syn::Fields::Unit => Ok(None), + } + } + + fn from_fields_vec(fields: Vec<&syn::Field>) -> syn::Result<Option<Self>> { + for (i, field) in fields.iter().enumerate() { + for attr in &field.attrs { + if attr.path().is_ident("source_code") { + let source_code = if let Some(ident) = field.ident.clone() { + syn::Member::Named(ident) + } else { + syn::Member::Unnamed(syn::Index { + index: i as u32, + span: field.span(), + }) + }; + return Ok(Some(SourceCode { source_code })); + } + } + } + Ok(None) + } + + pub(crate) fn gen_struct(&self, fields: &syn::Fields) -> Option<TokenStream> { + let (display_pat, _display_members) = display_pat_members(fields); + let src = &self.source_code; + Some(quote! { + #[allow(unused_variables)] + fn source_code(&self) -> std::option::Option<&dyn miette::SourceCode> { + let Self #display_pat = self; + Some(&self.#src) + } + }) + } + + pub(crate) fn gen_enum(variants: &[DiagnosticDef]) -> Option<TokenStream> { + gen_all_variants_with( + variants, + WhichFn::SourceCode, + |ident, fields, DiagnosticConcreteArgs { source_code, .. }| { + let (display_pat, _display_members) = display_pat_members(fields); + source_code.as_ref().and_then(|source_code| { + let field = match &source_code.source_code { + syn::Member::Named(ident) => ident.clone(), + syn::Member::Unnamed(syn::Index { index, .. }) => { + format_ident!("_{}", index) + } + }; + let variant_name = ident.clone(); + match &fields { + syn::Fields::Unit => None, + _ => Some(quote! { + Self::#variant_name #display_pat => std::option::Option::Some(#field), + }), + } + }) + }, + ) + } +} diff --git a/vendor/miette-derive/src/url.rs b/vendor/miette-derive/src/url.rs new file mode 100644 index 0000000..734d1a4 --- /dev/null +++ b/vendor/miette-derive/src/url.rs @@ -0,0 +1,139 @@ +use proc_macro2::TokenStream; +use quote::quote; +use syn::{ + parenthesized, + parse::{Parse, ParseStream}, + Fields, Token, +}; + +use crate::{ + diagnostic::{DiagnosticConcreteArgs, DiagnosticDef}, + utils::{display_pat_members, gen_all_variants_with, gen_unused_pat}, +}; +use crate::{ + fmt::{self, Display}, + forward::WhichFn, +}; + +pub enum Url { + Display(Display), + DocsRs, +} + +impl Parse for Url { + fn parse(input: ParseStream) -> syn::Result<Self> { + let ident = input.parse::<syn::Ident>()?; + if ident == "url" { + let la = input.lookahead1(); + if la.peek(syn::token::Paren) { + let content; + parenthesized!(content in input); + if content.peek(syn::LitStr) { + let fmt = content.parse()?; + let args = if content.is_empty() { + TokenStream::new() + } else { + fmt::parse_token_expr(&content, false)? + }; + let display = Display { + fmt, + args, + has_bonus_display: false, + }; + Ok(Url::Display(display)) + } else { + let option = content.parse::<syn::Ident>()?; + if option == "docsrs" { + Ok(Url::DocsRs) + } else { + Err(syn::Error::new(option.span(), "Invalid argument to url() sub-attribute. It must be either a string or a plain `docsrs` identifier")) + } + } + } else { + input.parse::<Token![=]>()?; + Ok(Url::Display(Display { + fmt: input.parse()?, + args: TokenStream::new(), + has_bonus_display: false, + })) + } + } else { + Err(syn::Error::new(ident.span(), "not a url")) + } + } +} + +impl Url { + pub(crate) fn gen_enum( + enum_name: &syn::Ident, + variants: &[DiagnosticDef], + ) -> Option<TokenStream> { + gen_all_variants_with( + variants, + WhichFn::Url, + |ident, fields, DiagnosticConcreteArgs { url, .. }| { + let (pat, fmt, args) = match url.as_ref()? { + // fall through to `_ => None` below + Url::Display(display) => { + let (display_pat, display_members) = display_pat_members(fields); + let (fmt, args) = display.expand_shorthand_cloned(&display_members); + (display_pat, fmt.value(), args) + } + Url::DocsRs => { + let pat = gen_unused_pat(fields); + let fmt = + "https://docs.rs/{crate_name}/{crate_version}/{mod_name}/{item_path}" + .into(); + let item_path = format!("enum.{}.html#variant.{}", enum_name, ident); + let args = quote! { + , + crate_name=env!("CARGO_PKG_NAME"), + crate_version=env!("CARGO_PKG_VERSION"), + mod_name=env!("CARGO_PKG_NAME").replace('-', "_"), + item_path=#item_path + }; + (pat, fmt, args) + } + }; + Some(quote! { + Self::#ident #pat => std::option::Option::Some(std::boxed::Box::new(format!(#fmt #args))), + }) + }, + ) + } + + pub(crate) fn gen_struct( + &self, + struct_name: &syn::Ident, + fields: &Fields, + ) -> Option<TokenStream> { + let (pat, fmt, args) = match self { + Url::Display(display) => { + let (display_pat, display_members) = display_pat_members(fields); + let (fmt, args) = display.expand_shorthand_cloned(&display_members); + (display_pat, fmt.value(), args) + } + Url::DocsRs => { + let pat = gen_unused_pat(fields); + let fmt = + "https://docs.rs/{crate_name}/{crate_version}/{mod_name}/{item_path}".into(); + let item_path = format!("struct.{}.html", struct_name); + let args = quote! { + , + crate_name=env!("CARGO_PKG_NAME"), + crate_version=env!("CARGO_PKG_VERSION"), + mod_name=env!("CARGO_PKG_NAME").replace('-', "_"), + item_path=#item_path + }; + (pat, fmt, args) + } + }; + Some(quote! { + fn url(&self) -> std::option::Option<std::boxed::Box<dyn std::fmt::Display + '_>> { + #[allow(unused_variables, deprecated)] + let Self #pat = self; + std::option::Option::Some(std::boxed::Box::new(format!(#fmt #args))) + } + }) + } +} diff --git a/vendor/miette-derive/src/utils.rs b/vendor/miette-derive/src/utils.rs new file mode 100644 index 0000000..b867849 --- /dev/null +++ b/vendor/miette-derive/src/utils.rs @@ -0,0 +1,140 @@ +use proc_macro2::TokenStream; +use quote::{format_ident, quote, ToTokens}; +use syn::{ + parse::{Parse, ParseStream}, + spanned::Spanned, +}; + +pub(crate) enum MemberOrString { + Member(syn::Member), + String(syn::LitStr), +} + +impl ToTokens for MemberOrString { + fn to_tokens(&self, tokens: &mut TokenStream) { + use MemberOrString::*; + match self { + Member(member) => member.to_tokens(tokens), + String(string) => string.to_tokens(tokens), + } + } +} + +impl Parse for MemberOrString { + fn parse(input: ParseStream) -> syn::Result<Self> { + let lookahead = input.lookahead1(); + if lookahead.peek(syn::Ident) || lookahead.peek(syn::LitInt) { + Ok(MemberOrString::Member(input.parse()?)) + } else if lookahead.peek(syn::LitStr) { + Ok(MemberOrString::String(input.parse()?)) + } else { + Err(syn::Error::new( + input.span(), + "Expected a string or a field reference.", + )) + } + } +} + +use crate::{ + diagnostic::{DiagnosticConcreteArgs, DiagnosticDef}, + forward::WhichFn, +}; + +pub(crate) fn gen_all_variants_with( + variants: &[DiagnosticDef], + which_fn: WhichFn, + mut f: impl FnMut(&syn::Ident, &syn::Fields, &DiagnosticConcreteArgs) -> Option<TokenStream>, +) -> Option<TokenStream> { + let pairs = variants + .iter() + .filter_map(|def| { + def.args + .forward_or_override_enum(&def.ident, which_fn, |concrete| { + f(&def.ident, &def.fields, concrete) + }) + }) + .collect::<Vec<_>>(); + if pairs.is_empty() { + return None; + } + let signature = which_fn.signature(); + let catchall = which_fn.catchall_arm(); + Some(quote! { + #signature { + #[allow(unused_variables, deprecated)] + match self { + #(#pairs)* + #catchall + } + } + }) +} + +use crate::fmt::Display; +use std::collections::HashSet; + +pub(crate) fn gen_unused_pat(fields: &syn::Fields) -> TokenStream { + match fields { + syn::Fields::Named(_) => quote! { { .. } }, + syn::Fields::Unnamed(_) => quote! { ( .. ) }, + syn::Fields::Unit => quote! {}, + } +} + +/// Goes in the slot `let Self #pat = self;` or `match self { Self #pat => ... +/// }`. +fn gen_fields_pat(fields: &syn::Fields) -> TokenStream { + let member_idents = fields.iter().enumerate().map(|(i, field)| { + field + .ident + .as_ref() + .cloned() + .unwrap_or_else(|| format_ident!("_{}", i)) + }); + match fields { + syn::Fields::Named(_) => quote! { + { #(#member_idents),* } + }, + syn::Fields::Unnamed(_) => quote! { + ( #(#member_idents),* ) + }, + syn::Fields::Unit => quote! {}, + } +} + +/// The returned tokens go in the slot `let Self #pat = self;` or `match self { +/// Self #pat => ... }`. The members can be passed to +/// `Display::expand_shorthand[_cloned]`. +pub(crate) fn display_pat_members(fields: &syn::Fields) -> (TokenStream, HashSet<syn::Member>) { + let pat = gen_fields_pat(fields); + let members: HashSet<syn::Member> = fields + .iter() + .enumerate() + .map(|(i, field)| { + if let Some(ident) = field.ident.as_ref().cloned() { + syn::Member::Named(ident) + } else { + syn::Member::Unnamed(syn::Index { + index: i as u32, + span: field.span(), + }) + } + }) + .collect(); + (pat, members) +} + +impl Display { + /// Returns `(fmt, args)` which must be passed to some kind of format macro + /// without tokens in between, i.e. `format!(#fmt #args)`. + pub(crate) fn expand_shorthand_cloned( + &self, + members: &HashSet<syn::Member>, + ) -> (syn::LitStr, TokenStream) { + let mut display = self.clone(); + display.expand_shorthand(members); + let Display { fmt, args, .. } = display; + (fmt, args) + } +} |