diff --git a/collector/compile-benchmarks/README.md b/collector/compile-benchmarks/README.md index cd94bb5e7..2c2da8fe2 100644 --- a/collector/compile-benchmarks/README.md +++ b/collector/compile-benchmarks/README.md @@ -25,8 +25,8 @@ They mostly consist of real-world crates. part of the Rust ecosystem. - **cargo-0.87.1**: The Rust package manager. A large program, and an important part of the Rust ecosystem. -- **clap-3.1.6**: A command line argument parser library. A crate used by many - Rust programs. +- **clap-3.1.6**: A command line argument parser library. A crate used by many Rust programs. +- **clap_derive-4.5.32**: The proc macro sub-crate of a command line argument parser library that is used by many Rust programs. - **cranelift-codegen-0.82.1**: The largest crate from a code generator. Used by wasmtime. Stresses obligation processing. - **diesel-1.4.8**: A type safe SQL query builder. Utilizes the type system to diff --git a/collector/compile-benchmarks/REUSE.toml b/collector/compile-benchmarks/REUSE.toml index 12db291dd..429195b91 100644 --- a/collector/compile-benchmarks/REUSE.toml +++ b/collector/compile-benchmarks/REUSE.toml @@ -42,6 +42,11 @@ path = "clap-3.1.6/**" SPDX-FileCopyrightText = "clap contributors" SPDX-License-Identifier = "MIT OR Apache-2.0" +[[annotations]] +path = "clap_derive-4.5.32/**" +SPDX-FileCopyrightText = "clap contributors" +SPDX-License-Identifier = "MIT OR Apache-2.0" + [[annotations]] path = "coercions/**" SPDX-FileCopyrightText = "The Rust Project Developers (see https://thanks.rust-lang.org)" diff --git a/collector/compile-benchmarks/clap_derive-4.5.32/0-println.patch b/collector/compile-benchmarks/clap_derive-4.5.32/0-println.patch new file mode 100644 index 000000000..207cc26a5 --- /dev/null +++ b/collector/compile-benchmarks/clap_derive-4.5.32/0-println.patch @@ -0,0 +1,12 @@ +diff --git a/src/lib.rs b/src/lib.rs +index 760c5b52..1281bb19 100644 +--- a/src/lib.rs ++++ b/src/lib.rs +@@ -36,6 +36,7 @@ mod utils; + /// Generates the `ValueEnum` impl. + #[proc_macro_derive(ValueEnum, attributes(clap, value))] + pub fn value_enum(input: TokenStream) -> TokenStream { ++ println!("testing!"); + let input: DeriveInput = parse_macro_input!(input); + derives::derive_value_enum(&input) + .unwrap_or_else(|err| { diff --git a/collector/compile-benchmarks/clap_derive-4.5.32/CONTRIBUTING.md b/collector/compile-benchmarks/clap_derive-4.5.32/CONTRIBUTING.md new file mode 100644 index 000000000..331971995 --- /dev/null +++ b/collector/compile-benchmarks/clap_derive-4.5.32/CONTRIBUTING.md @@ -0,0 +1,13 @@ +# How to Contribute + +See the [clap-wide CONTRIBUTING.md](../CONTRIBUTING.md). This will contain `clap_derive` specific notes. + +## Derive Gotchas + +- Always prefix generated variables with `__clap_` to minimize clashes with the user's variables, see [#2934](https://github.com/clap-rs/clap/issues/2934). +- Prefer the path `clap` over `::clap` to allow users to re-export clap, see [#2258](https://github.com/clap-rs/clap/pull/2258). +- Prefer substituting variable names to avoid problems with `macro_rules`, see [#2823](https://github.com/clap-rs/clap/pull/2823). +- Prefer `::std::result::Result` and `::std::option::Option`, see [#3092](https://github.com/clap-rs/clap/pull/3092). +- Put whitespace between `#quoted #variables`. +- New "magic" attributes must be documented in the [derive reference](../src/_derive.rs) + - If there is no related builder method, a `#![doc(alias = "")]` should also be added, see [#4984](https://github.com/clap-rs/clap/pull/4984) diff --git a/collector/compile-benchmarks/clap_derive-4.5.32/Cargo.lock b/collector/compile-benchmarks/clap_derive-4.5.32/Cargo.lock new file mode 100644 index 000000000..6d26b393c --- /dev/null +++ b/collector/compile-benchmarks/clap_derive-4.5.32/Cargo.lock @@ -0,0 +1,91 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "bitflags" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" + +[[package]] +name = "clap_derive" +version = "4.5.32" +dependencies = [ + "anstyle", + "heck", + "proc-macro2", + "pulldown-cmark", + "quote", + "syn", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "pulldown-cmark" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e8bbe1a966bd2f362681a44f6edce3c2310ac21e4d5067a6e7ec396297a6ea0" +dependencies = [ + "bitflags", + "memchr", + "unicase", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "2.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicase" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" diff --git a/collector/compile-benchmarks/clap_derive-4.5.32/Cargo.toml b/collector/compile-benchmarks/clap_derive-4.5.32/Cargo.toml new file mode 100644 index 000000000..dd55e0525 --- /dev/null +++ b/collector/compile-benchmarks/clap_derive-4.5.32/Cargo.toml @@ -0,0 +1,43 @@ +[package] +name = "clap_derive" +version = "4.5.32" +description = "Parse command line argument by defining a struct, derive crate." +categories = ["command-line-interface", "development-tools::procedural-macro-helpers"] +keywords = [ + "clap", + "cli", + "parse", + "derive", + "proc_macro" +] +edition = "2021" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[package.metadata.release] +shared-version = true +dependent-version = "upgrade" +tag-name = "v{{version}}" + +[lib] +proc-macro = true +bench = false + +[dependencies] +syn = { version = "2.0.8", features = ["full"] } +quote = "1.0.9" +proc-macro2 = "1.0.69" +heck = "0.5.0" +pulldown-cmark = { version = "0.13.0", default-features = false, optional = true } +anstyle = { version = "1.0.10", optional = true } + +[features] +default = [] +debug = [] +unstable-v5 = ["deprecated"] +deprecated = [] +raw-deprecated = ["deprecated"] +unstable-markdown = ["dep:pulldown-cmark", "dep:anstyle"] + +[workspace] diff --git a/collector/compile-benchmarks/clap_derive-4.5.32/LICENSE-APACHE b/collector/compile-benchmarks/clap_derive-4.5.32/LICENSE-APACHE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/collector/compile-benchmarks/clap_derive-4.5.32/LICENSE-APACHE @@ -0,0 +1,201 @@ + 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] [name of copyright owner] + + 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. diff --git a/collector/compile-benchmarks/clap_derive-4.5.32/LICENSE-MIT b/collector/compile-benchmarks/clap_derive-4.5.32/LICENSE-MIT new file mode 100644 index 000000000..7b05b8453 --- /dev/null +++ b/collector/compile-benchmarks/clap_derive-4.5.32/LICENSE-MIT @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015-2022 Kevin B. Knapp and Clap Contributors + +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/collector/compile-benchmarks/clap_derive-4.5.32/README.md b/collector/compile-benchmarks/clap_derive-4.5.32/README.md new file mode 100644 index 000000000..e1265653e --- /dev/null +++ b/collector/compile-benchmarks/clap_derive-4.5.32/README.md @@ -0,0 +1,26 @@ +# `clap_derive` + +> Note: this crate was extracted out of the `clap` workspace during the 2025 rustc-perf benchmark update. + +Macro implementation for clap's derives. + +[docs.rs](https://docs.rs/clap) +- [Derive Tutorial](https://docs.rs/clap/latest/clap/_derive/_tutorial/index.html) +- [Derive Reference](https://docs.rs/clap/latest/clap/_derive/index.html) + +## License + +Licensed under either of + +- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or ) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or ) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. + +See [CONTRIBUTING](CONTRIBUTING.md) for more details. diff --git a/collector/compile-benchmarks/clap_derive-4.5.32/perf-config.json b/collector/compile-benchmarks/clap_derive-4.5.32/perf-config.json new file mode 100644 index 000000000..a9cf0d2f3 --- /dev/null +++ b/collector/compile-benchmarks/clap_derive-4.5.32/perf-config.json @@ -0,0 +1,4 @@ +{ + "category": "primary", + "artifact": "library" +} diff --git a/collector/compile-benchmarks/clap_derive-4.5.32/src/attr.rs b/collector/compile-benchmarks/clap_derive-4.5.32/src/attr.rs new file mode 100644 index 000000000..51736c4d1 --- /dev/null +++ b/collector/compile-benchmarks/clap_derive-4.5.32/src/attr.rs @@ -0,0 +1,215 @@ +use std::iter::FromIterator; + +use proc_macro2::TokenStream; +use quote::quote; +use quote::ToTokens; +use syn::spanned::Spanned; +use syn::{ + parenthesized, + parse::{Parse, ParseStream}, + punctuated::Punctuated, + Attribute, Expr, Ident, LitStr, Token, +}; + +use crate::utils::Sp; + +#[derive(Clone)] +pub(crate) struct ClapAttr { + pub(crate) kind: Sp, + pub(crate) name: Ident, + pub(crate) magic: Option, + pub(crate) value: Option, +} + +impl ClapAttr { + pub(crate) fn parse_all(all_attrs: &[Attribute]) -> Result, syn::Error> { + let mut parsed = Vec::new(); + for attr in all_attrs { + let kind = if attr.path().is_ident("clap") { + Sp::new(AttrKind::Clap, attr.path().span()) + } else if attr.path().is_ident("structopt") { + Sp::new(AttrKind::StructOpt, attr.path().span()) + } else if attr.path().is_ident("command") { + Sp::new(AttrKind::Command, attr.path().span()) + } else if attr.path().is_ident("group") { + Sp::new(AttrKind::Group, attr.path().span()) + } else if attr.path().is_ident("arg") { + Sp::new(AttrKind::Arg, attr.path().span()) + } else if attr.path().is_ident("value") { + Sp::new(AttrKind::Value, attr.path().span()) + } else { + continue; + }; + for mut attr in + attr.parse_args_with(Punctuated::::parse_terminated)? + { + attr.kind = kind; + parsed.push(attr); + } + } + Ok(parsed) + } + + pub(crate) fn value_or_abort(&self) -> Result<&AttrValue, syn::Error> { + self.value + .as_ref() + .ok_or_else(|| format_err!(self.name, "attribute `{}` requires a value", self.name)) + } + + pub(crate) fn lit_str_or_abort(&self) -> Result<&LitStr, syn::Error> { + let value = self.value_or_abort()?; + match value { + AttrValue::LitStr(tokens) => Ok(tokens), + AttrValue::Expr(_) | AttrValue::Call(_) => { + abort!( + self.name, + "attribute `{}` can only accept string literals", + self.name + ) + } + } + } +} + +impl Parse for ClapAttr { + fn parse(input: ParseStream<'_>) -> syn::Result { + let name: Ident = input.parse()?; + let name_str = name.to_string(); + + let magic = match name_str.as_str() { + "rename_all" => Some(MagicAttrName::RenameAll), + "rename_all_env" => Some(MagicAttrName::RenameAllEnv), + "skip" => Some(MagicAttrName::Skip), + "next_display_order" => Some(MagicAttrName::NextDisplayOrder), + "next_help_heading" => Some(MagicAttrName::NextHelpHeading), + "default_value_t" => Some(MagicAttrName::DefaultValueT), + "default_values_t" => Some(MagicAttrName::DefaultValuesT), + "default_value_os_t" => Some(MagicAttrName::DefaultValueOsT), + "default_values_os_t" => Some(MagicAttrName::DefaultValuesOsT), + "long" => Some(MagicAttrName::Long), + "short" => Some(MagicAttrName::Short), + "value_parser" => Some(MagicAttrName::ValueParser), + "action" => Some(MagicAttrName::Action), + "env" => Some(MagicAttrName::Env), + "flatten" => Some(MagicAttrName::Flatten), + "value_enum" => Some(MagicAttrName::ValueEnum), + "from_global" => Some(MagicAttrName::FromGlobal), + "subcommand" => Some(MagicAttrName::Subcommand), + "external_subcommand" => Some(MagicAttrName::ExternalSubcommand), + "verbatim_doc_comment" => Some(MagicAttrName::VerbatimDocComment), + "about" => Some(MagicAttrName::About), + "long_about" => Some(MagicAttrName::LongAbout), + "long_help" => Some(MagicAttrName::LongHelp), + "author" => Some(MagicAttrName::Author), + "version" => Some(MagicAttrName::Version), + _ => None, + }; + + let value = if input.peek(Token![=]) { + // `name = value` attributes. + let assign_token = input.parse::()?; // skip '=' + if input.peek(LitStr) { + let lit: LitStr = input.parse()?; + Some(AttrValue::LitStr(lit)) + } else { + match input.parse::() { + Ok(expr) => Some(AttrValue::Expr(expr)), + + Err(_) => abort! { + assign_token, + "expected `string literal` or `expression` after `=`" + }, + } + } + } else if input.peek(syn::token::Paren) { + // `name(...)` attributes. + let nested; + parenthesized!(nested in input); + + let method_args: Punctuated<_, _> = nested.parse_terminated(Expr::parse, Token![,])?; + Some(AttrValue::Call(Vec::from_iter(method_args))) + } else { + None + }; + + Ok(Self { + kind: Sp::new(AttrKind::Clap, name.span()), + name, + magic, + value, + }) + } +} + +#[derive(Copy, Clone, PartialEq, Eq)] +pub(crate) enum MagicAttrName { + Short, + Long, + ValueParser, + Action, + Env, + Flatten, + ValueEnum, + FromGlobal, + Subcommand, + VerbatimDocComment, + ExternalSubcommand, + About, + LongAbout, + LongHelp, + Author, + Version, + RenameAllEnv, + RenameAll, + Skip, + DefaultValueT, + DefaultValuesT, + DefaultValueOsT, + DefaultValuesOsT, + NextDisplayOrder, + NextHelpHeading, +} + +#[derive(Clone)] +#[allow(clippy::large_enum_variant)] +pub(crate) enum AttrValue { + LitStr(LitStr), + Expr(Expr), + Call(Vec), +} + +impl ToTokens for AttrValue { + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + Self::LitStr(t) => t.to_tokens(tokens), + Self::Expr(t) => t.to_tokens(tokens), + Self::Call(t) => { + let t = quote!(#(#t),*); + t.to_tokens(tokens); + } + } + } +} + +#[derive(Copy, Clone, PartialEq, Eq)] +pub(crate) enum AttrKind { + Clap, + StructOpt, + Command, + Group, + Arg, + Value, +} + +impl AttrKind { + pub(crate) fn as_str(&self) -> &'static str { + match self { + Self::Clap => "clap", + Self::StructOpt => "structopt", + Self::Command => "command", + Self::Group => "group", + Self::Arg => "arg", + Self::Value => "value", + } + } +} diff --git a/collector/compile-benchmarks/clap_derive-4.5.32/src/derives/args.rs b/collector/compile-benchmarks/clap_derive-4.5.32/src/derives/args.rs new file mode 100644 index 000000000..04928db28 --- /dev/null +++ b/collector/compile-benchmarks/clap_derive-4.5.32/src/derives/args.rs @@ -0,0 +1,789 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) , +// Kevin Knapp (@kbknapp) , and +// Ana Hobden (@hoverbear) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +// +// This work was derived from Structopt (https://github.com/TeXitoi/structopt) +// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the +// MIT/Apache 2.0 license. + +use proc_macro2::{Ident, Span, TokenStream}; +use quote::{format_ident, quote, quote_spanned}; +use syn::{ + punctuated::Punctuated, spanned::Spanned, token::Comma, Data, DataStruct, DeriveInput, Field, + Fields, FieldsNamed, Generics, +}; + +use crate::item::{Item, Kind, Name}; +use crate::utils::{inner_type, sub_type, Sp, Ty}; + +pub(crate) fn derive_args(input: &DeriveInput) -> Result { + let ident = &input.ident; + + match input.data { + Data::Struct(DataStruct { + fields: Fields::Named(ref fields), + .. + }) => { + let name = Name::Derived(ident.clone()); + let item = Item::from_args_struct(input, name)?; + let fields = collect_args_fields(&item, fields)?; + gen_for_struct(&item, ident, &input.generics, &fields) + } + Data::Struct(DataStruct { + fields: Fields::Unit, + .. + }) => { + let name = Name::Derived(ident.clone()); + let item = Item::from_args_struct(input, name)?; + let fields = Punctuated::::new(); + let fields = fields + .iter() + .map(|field| { + let item = Item::from_args_field(field, item.casing(), item.env_casing())?; + Ok((field, item)) + }) + .collect::, syn::Error>>()?; + gen_for_struct(&item, ident, &input.generics, &fields) + } + _ => abort_call_site!("`#[derive(Args)]` only supports non-tuple structs"), + } +} + +pub(crate) fn gen_for_struct( + item: &Item, + item_name: &Ident, + generics: &Generics, + fields: &[(&Field, Item)], +) -> Result { + if !matches!(&*item.kind(), Kind::Command(_)) { + abort! { item.kind().span(), + "`{}` cannot be used with `command`", + item.kind().name(), + } + } + + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + + let constructor = gen_constructor(fields)?; + let updater = gen_updater(fields, true)?; + let raw_deprecated = raw_deprecated(); + + let app_var = Ident::new("__clap_app", Span::call_site()); + let augmentation = gen_augment(fields, &app_var, item, false)?; + let augmentation_update = gen_augment(fields, &app_var, item, true)?; + + let group_id = if item.skip_group() { + quote!(None) + } else { + let group_id = item.group_id(); + quote!(Some(clap::Id::from(#group_id))) + }; + + Ok(quote! { + #[allow( + dead_code, + unreachable_code, + unused_variables, + unused_braces, + unused_qualifications, + )] + #[allow( + clippy::style, + clippy::complexity, + clippy::pedantic, + clippy::restriction, + clippy::perf, + clippy::deprecated, + clippy::nursery, + clippy::cargo, + clippy::suspicious_else_formatting, + clippy::almost_swapped, + clippy::redundant_locals, + )] + #[automatically_derived] + impl #impl_generics clap::FromArgMatches for #item_name #ty_generics #where_clause { + fn from_arg_matches(__clap_arg_matches: &clap::ArgMatches) -> ::std::result::Result { + Self::from_arg_matches_mut(&mut __clap_arg_matches.clone()) + } + + fn from_arg_matches_mut(__clap_arg_matches: &mut clap::ArgMatches) -> ::std::result::Result { + #raw_deprecated + let v = #item_name #constructor; + ::std::result::Result::Ok(v) + } + + fn update_from_arg_matches(&mut self, __clap_arg_matches: &clap::ArgMatches) -> ::std::result::Result<(), clap::Error> { + self.update_from_arg_matches_mut(&mut __clap_arg_matches.clone()) + } + + fn update_from_arg_matches_mut(&mut self, __clap_arg_matches: &mut clap::ArgMatches) -> ::std::result::Result<(), clap::Error> { + #raw_deprecated + #updater + ::std::result::Result::Ok(()) + } + } + + #[allow( + dead_code, + unreachable_code, + unused_variables, + unused_braces, + unused_qualifications, + )] + #[allow( + clippy::style, + clippy::complexity, + clippy::pedantic, + clippy::restriction, + clippy::perf, + clippy::deprecated, + clippy::nursery, + clippy::cargo, + clippy::suspicious_else_formatting, + clippy::almost_swapped, + clippy::redundant_locals, + )] + #[automatically_derived] + impl #impl_generics clap::Args for #item_name #ty_generics #where_clause { + fn group_id() -> Option { + #group_id + } + fn augment_args<'b>(#app_var: clap::Command) -> clap::Command { + #augmentation + } + fn augment_args_for_update<'b>(#app_var: clap::Command) -> clap::Command { + #augmentation_update + } + } + }) +} + +/// Generate a block of code to add arguments/subcommands corresponding to +/// the `fields` to an cmd. +pub(crate) fn gen_augment( + fields: &[(&Field, Item)], + app_var: &Ident, + parent_item: &Item, + override_required: bool, +) -> Result { + let mut subcommand_specified = false; + let mut args = Vec::new(); + for (field, item) in fields { + let kind = item.kind(); + let genned = match &*kind { + Kind::Command(_) + | Kind::Value + | Kind::Skip(_, _) + | Kind::FromGlobal(_) + | Kind::ExternalSubcommand => None, + Kind::Subcommand(ty) => { + if subcommand_specified { + abort!( + field.span(), + "`#[command(subcommand)]` can only be used once per container" + ); + } + subcommand_specified = true; + + let subcmd_type = match (**ty, sub_type(&field.ty)) { + (Ty::Option, Some(sub_type)) => sub_type, + _ => &field.ty, + }; + let implicit_methods = if **ty == Ty::Option { + quote!() + } else { + quote_spanned! { kind.span()=> + .subcommand_required(true) + .arg_required_else_help(true) + } + }; + + let override_methods = if override_required { + quote_spanned! { kind.span()=> + .subcommand_required(false) + .arg_required_else_help(false) + } + } else { + quote!() + }; + + Some(quote! { + let #app_var = <#subcmd_type as clap::Subcommand>::augment_subcommands( #app_var ); + let #app_var = #app_var + #implicit_methods + #override_methods; + }) + } + Kind::Flatten(ty) => { + let inner_type = match (**ty, sub_type(&field.ty)) { + (Ty::Option, Some(sub_type)) => sub_type, + _ => &field.ty, + }; + + let next_help_heading = item.next_help_heading(); + let next_display_order = item.next_display_order(); + let flatten_group_assert = if matches!(**ty, Ty::Option) { + quote_spanned! { kind.span()=> + <#inner_type as clap::Args>::group_id().expect("cannot `#[flatten]` an `Option` with `#[group(skip)]`"); + } + } else { + quote! {} + }; + if override_required { + Some(quote_spanned! { kind.span()=> + #flatten_group_assert + let #app_var = #app_var + #next_help_heading + #next_display_order; + let #app_var = <#inner_type as clap::Args>::augment_args_for_update(#app_var); + }) + } else { + Some(quote_spanned! { kind.span()=> + #flatten_group_assert + let #app_var = #app_var + #next_help_heading + #next_display_order; + let #app_var = <#inner_type as clap::Args>::augment_args(#app_var); + }) + } + } + Kind::Arg(ty) => { + let value_parser = item.value_parser(&field.ty); + let action = item.action(&field.ty); + let value_name = item.value_name(); + + let implicit_methods = match **ty { + Ty::Unit => { + // Leaving out `value_parser` as it will always fail + quote_spanned! { ty.span()=> + .value_name(#value_name) + #action + } + } + Ty::Option => { + quote_spanned! { ty.span()=> + .value_name(#value_name) + #value_parser + #action + } + } + + Ty::OptionOption => quote_spanned! { ty.span()=> + .value_name(#value_name) + .num_args(0..=1) + #value_parser + #action + }, + + Ty::OptionVec => { + if item.is_positional() { + quote_spanned! { ty.span()=> + .value_name(#value_name) + .num_args(1..) // action won't be sufficient for getting multiple + #value_parser + #action + } + } else { + quote_spanned! { ty.span()=> + .value_name(#value_name) + #value_parser + #action + } + } + } + + Ty::Vec => { + if item.is_positional() { + quote_spanned! { ty.span()=> + .value_name(#value_name) + .num_args(1..) // action won't be sufficient for getting multiple + #value_parser + #action + } + } else { + quote_spanned! { ty.span()=> + .value_name(#value_name) + #value_parser + #action + } + } + } + + Ty::VecVec | Ty::OptionVecVec => { + quote_spanned! { ty.span() => + .value_name(#value_name) + #value_parser + #action + } + } + + Ty::Other => { + let required = item.find_default_method().is_none(); + // `ArgAction::takes_values` is assuming `ArgAction::default_value` will be + // set though that won't always be true but this should be good enough, + // otherwise we'll report an "arg required" error when unwrapping. + let action_value = action.args(); + quote_spanned! { ty.span()=> + .value_name(#value_name) + .required(#required && #action_value.takes_values()) + #value_parser + #action + } + } + }; + + let id = item.id(); + let explicit_methods = item.field_methods(); + let deprecations = if !override_required { + item.deprecations() + } else { + quote!() + }; + let override_methods = if override_required { + quote_spanned! { kind.span()=> + .required(false) + } + } else { + quote!() + }; + + Some(quote_spanned! { field.span()=> + let #app_var = #app_var.arg({ + #deprecations + + #[allow(deprecated)] + let arg = clap::Arg::new(#id) + #implicit_methods; + + let arg = arg + #explicit_methods; + + let arg = arg + #override_methods; + + arg + }); + }) + } + }; + args.push(genned); + } + + let deprecations = if !override_required { + parent_item.deprecations() + } else { + quote!() + }; + let initial_app_methods = parent_item.initial_top_level_methods(); + let final_app_methods = parent_item.final_top_level_methods(); + let group_app_methods = if parent_item.skip_group() { + quote!() + } else { + let group_id = parent_item.group_id(); + let literal_group_members = fields + .iter() + .filter_map(|(_field, item)| { + let kind = item.kind(); + if matches!(*kind, Kind::Arg(_)) { + Some(item.id()) + } else { + None + } + }) + .collect::>(); + let literal_group_members_len = literal_group_members.len(); + let mut literal_group_members = quote! {{ + let members: [clap::Id; #literal_group_members_len] = [#( clap::Id::from(#literal_group_members) ),* ]; + members + }}; + // HACK: Validation isn't ready yet for nested arg groups, so just don't populate the group in + // that situation + let possible_group_members_len = fields + .iter() + .filter(|(_field, item)| { + let kind = item.kind(); + matches!(*kind, Kind::Flatten(_)) + }) + .count(); + if 0 < possible_group_members_len { + literal_group_members = quote! {{ + let members: [clap::Id; 0] = []; + members + }}; + } + + let group_methods = parent_item.group_methods(); + + quote!( + .group( + clap::ArgGroup::new(#group_id) + .multiple(true) + #group_methods + .args(#literal_group_members) + ) + ) + }; + Ok(quote! {{ + #deprecations + let #app_var = #app_var + #initial_app_methods + #group_app_methods + ; + #( #args )* + #app_var #final_app_methods + }}) +} + +pub(crate) fn gen_constructor(fields: &[(&Field, Item)]) -> Result { + let fields = fields.iter().map(|(field, item)| { + let field_name = field.ident.as_ref().unwrap(); + let kind = item.kind(); + let arg_matches = format_ident!("__clap_arg_matches"); + let genned = match &*kind { + Kind::Command(_) + | Kind::Value + | Kind::ExternalSubcommand => { + abort! { kind.span(), + "`{}` cannot be used with `arg`", + kind.name(), + } + } + Kind::Subcommand(ty) => { + let subcmd_type = match (**ty, sub_type(&field.ty)) { + (Ty::Option, Some(sub_type)) => sub_type, + _ => &field.ty, + }; + match **ty { + Ty::Option => { + quote_spanned! { kind.span()=> + #field_name: { + if #arg_matches.subcommand_name().map(<#subcmd_type as clap::Subcommand>::has_subcommand).unwrap_or(false) { + Some(<#subcmd_type as clap::FromArgMatches>::from_arg_matches_mut(#arg_matches)?) + } else { + None + } + } + } + }, + Ty::Other => { + quote_spanned! { kind.span()=> + #field_name: { + <#subcmd_type as clap::FromArgMatches>::from_arg_matches_mut(#arg_matches)? + } + } + }, + Ty::Unit | + Ty::Vec | + Ty::OptionOption | + Ty::OptionVec | + Ty::VecVec | + Ty::OptionVecVec => { + abort!( + ty.span(), + "{} types are not supported for subcommand", + ty.as_str() + ); + } + } + } + + Kind::Flatten(ty) => { + let inner_type = match (**ty, sub_type(&field.ty)) { + (Ty::Option, Some(sub_type)) => sub_type, + _ => &field.ty, + }; + match **ty { + Ty::Other => { + quote_spanned! { kind.span()=> + #field_name: <#inner_type as clap::FromArgMatches>::from_arg_matches_mut(#arg_matches)? + } + }, + Ty::Option => { + quote_spanned! { kind.span()=> + #field_name: { + let group_id = <#inner_type as clap::Args>::group_id() + .expect("asserted during `Arg` creation"); + if #arg_matches.contains_id(group_id.as_str()) { + Some( + <#inner_type as clap::FromArgMatches>::from_arg_matches_mut(#arg_matches)? + ) + } else { + None + } + } + } + }, + Ty::Unit | + Ty::Vec | + Ty::OptionOption | + Ty::OptionVec | + Ty::VecVec | + Ty::OptionVecVec => { + abort!( + ty.span(), + "{} types are not supported for flatten", + ty.as_str() + ); + } + } + }, + + Kind::Skip(val, _) => match val { + None => quote_spanned!(kind.span()=> #field_name: Default::default()), + Some(val) => quote_spanned!(kind.span()=> #field_name: (#val).into()), + }, + + Kind::Arg(ty) | Kind::FromGlobal(ty) => { + gen_parsers(item, ty, field_name, field, None)? + } + }; + Ok(genned) + }).collect::, syn::Error>>()?; + + Ok(quote! {{ + #( #fields ),* + }}) +} + +pub(crate) fn gen_updater( + fields: &[(&Field, Item)], + use_self: bool, +) -> Result { + let mut genned_fields = Vec::new(); + for (field, item) in fields { + let field_name = field.ident.as_ref().unwrap(); + let kind = item.kind(); + + let access = if use_self { + quote! { + #[allow(non_snake_case)] + let #field_name = &mut self.#field_name; + } + } else { + quote!() + }; + let arg_matches = format_ident!("__clap_arg_matches"); + + let genned = match &*kind { + Kind::Command(_) | Kind::Value | Kind::ExternalSubcommand => { + abort! { kind.span(), + "`{}` cannot be used with `arg`", + kind.name(), + } + } + Kind::Subcommand(ty) => { + let subcmd_type = match (**ty, sub_type(&field.ty)) { + (Ty::Option, Some(sub_type)) => sub_type, + _ => &field.ty, + }; + + let updater = quote_spanned! { ty.span()=> + <#subcmd_type as clap::FromArgMatches>::update_from_arg_matches_mut(#field_name, #arg_matches)?; + }; + + let updater = match **ty { + Ty::Option => quote_spanned! { kind.span()=> + if let Some(#field_name) = #field_name.as_mut() { + #updater + } else { + *#field_name = Some(<#subcmd_type as clap::FromArgMatches>::from_arg_matches_mut( + #arg_matches + )?); + } + }, + _ => quote_spanned! { kind.span()=> + #updater + }, + }; + + quote_spanned! { kind.span()=> + { + #access + #updater + } + } + } + + Kind::Flatten(ty) => { + let inner_type = match (**ty, sub_type(&field.ty)) { + (Ty::Option, Some(sub_type)) => sub_type, + _ => &field.ty, + }; + + let updater = quote_spanned! { ty.span()=> + <#inner_type as clap::FromArgMatches>::update_from_arg_matches_mut(#field_name, #arg_matches)?; + }; + + let updater = match **ty { + Ty::Option => quote_spanned! { kind.span()=> + if let Some(#field_name) = #field_name.as_mut() { + #updater + } else { + *#field_name = Some(<#inner_type as clap::FromArgMatches>::from_arg_matches_mut( + #arg_matches + )?); + } + }, + _ => quote_spanned! { kind.span()=> + #updater + }, + }; + + quote_spanned! { kind.span()=> + { + #access + #updater + } + } + } + + Kind::Skip(_, _) => quote!(), + + Kind::Arg(ty) | Kind::FromGlobal(ty) => { + gen_parsers(item, ty, field_name, field, Some(&access))? + } + }; + genned_fields.push(genned); + } + + Ok(quote! { + #( #genned_fields )* + }) +} + +fn gen_parsers( + item: &Item, + ty: &Sp, + field_name: &Ident, + field: &Field, + update: Option<&TokenStream>, +) -> Result { + let span = ty.span(); + let convert_type = inner_type(&field.ty); + let id = item.id(); + let get_one = quote_spanned!(span=> remove_one::<#convert_type>); + let get_many = quote_spanned!(span=> remove_many::<#convert_type>); + let get_occurrences = quote_spanned!(span=> remove_occurrences::<#convert_type>); + + // Give this identifier the same hygiene + // as the `arg_matches` parameter definition. This + // allows us to refer to `arg_matches` within a `quote_spanned` block + let arg_matches = format_ident!("__clap_arg_matches"); + + let field_value = match **ty { + Ty::Unit => { + quote_spanned! { ty.span()=> + () + } + } + + Ty::Option => { + quote_spanned! { ty.span()=> + #arg_matches.#get_one(#id) + } + } + + Ty::OptionOption => quote_spanned! { ty.span()=> + if #arg_matches.contains_id(#id) { + Some( + #arg_matches.#get_one(#id) + ) + } else { + None + } + }, + + Ty::OptionVec => quote_spanned! { ty.span()=> + if #arg_matches.contains_id(#id) { + Some(#arg_matches.#get_many(#id) + .map(|v| v.collect::>()) + .unwrap_or_else(Vec::new)) + } else { + None + } + }, + + Ty::Vec => { + quote_spanned! { ty.span()=> + #arg_matches.#get_many(#id) + .map(|v| v.collect::>()) + .unwrap_or_else(Vec::new) + } + } + + Ty::VecVec => quote_spanned! { ty.span()=> + #arg_matches.#get_occurrences(#id) + .map(|g| g.map(::std::iter::Iterator::collect).collect::>>()) + .unwrap_or_else(Vec::new) + }, + + Ty::OptionVecVec => quote_spanned! { ty.span()=> + #arg_matches.#get_occurrences(#id) + .map(|g| g.map(::std::iter::Iterator::collect).collect::>>()) + }, + + Ty::Other => { + // Prefer `concat` where possible for reduced code size but fallback to `format!` to + // allow non-literal `id`s + match id { + Name::Assigned(_) => { + quote_spanned! { ty.span()=> + #arg_matches.#get_one(#id) + .ok_or_else(|| clap::Error::raw(clap::error::ErrorKind::MissingRequiredArgument, format!("The following required argument was not provided: {}", #id)))? + } + } + Name::Derived(_) => { + quote_spanned! { ty.span()=> + #arg_matches.#get_one(#id) + .ok_or_else(|| clap::Error::raw(clap::error::ErrorKind::MissingRequiredArgument, concat!("The following required argument was not provided: ", #id)))? + } + } + } + } + }; + + let genned = if let Some(access) = update { + quote_spanned! { field.span()=> + if #arg_matches.contains_id(#id) { + #access + *#field_name = #field_value + } + } + } else { + quote_spanned!(field.span()=> #field_name: #field_value ) + }; + Ok(genned) +} + +#[cfg(feature = "raw-deprecated")] +pub(crate) fn raw_deprecated() -> TokenStream { + quote! {} +} + +#[cfg(not(feature = "raw-deprecated"))] +pub(crate) fn raw_deprecated() -> TokenStream { + quote! { + #![allow(deprecated)] // Assuming any deprecation in here will be related to a deprecation in `Args` + + } +} + +pub(crate) fn collect_args_fields<'a>( + item: &'a Item, + fields: &'a FieldsNamed, +) -> Result, syn::Error> { + fields + .named + .iter() + .map(|field| { + let item = Item::from_args_field(field, item.casing(), item.env_casing())?; + Ok((field, item)) + }) + .collect() +} diff --git a/collector/compile-benchmarks/clap_derive-4.5.32/src/derives/into_app.rs b/collector/compile-benchmarks/clap_derive-4.5.32/src/derives/into_app.rs new file mode 100644 index 000000000..2807d9d91 --- /dev/null +++ b/collector/compile-benchmarks/clap_derive-4.5.32/src/derives/into_app.rs @@ -0,0 +1,117 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) , +// Kevin Knapp (@kbknapp) , and +// Ana Hobden (@hoverbear) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +// +// This work was derived from Structopt (https://github.com/TeXitoi/structopt) +// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the +// MIT/Apache 2.0 license. + +use proc_macro2::{Span, TokenStream}; +use quote::quote; +use syn::{Generics, Ident}; + +use crate::item::Item; + +pub(crate) fn gen_for_struct( + item: &Item, + item_name: &Ident, + generics: &Generics, +) -> Result { + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + + let name = item.cased_name(); + let app_var = Ident::new("__clap_app", Span::call_site()); + + let tokens = quote! { + #[allow( + dead_code, + unreachable_code, + unused_variables, + unused_braces, + unused_qualifications, + )] + #[allow( + clippy::style, + clippy::complexity, + clippy::pedantic, + clippy::restriction, + clippy::perf, + clippy::deprecated, + clippy::nursery, + clippy::cargo, + clippy::suspicious_else_formatting, + clippy::almost_swapped, + clippy::redundant_locals, + )] + #[automatically_derived] + impl #impl_generics clap::CommandFactory for #item_name #ty_generics #where_clause { + fn command<'b>() -> clap::Command { + let #app_var = clap::Command::new(#name); + ::augment_args(#app_var) + } + + fn command_for_update<'b>() -> clap::Command { + let #app_var = clap::Command::new(#name); + ::augment_args_for_update(#app_var) + } + } + }; + + Ok(tokens) +} + +pub(crate) fn gen_for_enum( + item: &Item, + item_name: &Ident, + generics: &Generics, +) -> Result { + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + + let name = item.cased_name(); + let app_var = Ident::new("__clap_app", Span::call_site()); + + Ok(quote! { + #[allow( + dead_code, + unreachable_code, + unused_variables, + unused_braces, + unused_qualifications, + )] + #[allow( + clippy::style, + clippy::complexity, + clippy::pedantic, + clippy::restriction, + clippy::perf, + clippy::deprecated, + clippy::nursery, + clippy::cargo, + clippy::suspicious_else_formatting, + clippy::almost_swapped, + clippy::redundant_locals, + )] + #[automatically_derived] + impl #impl_generics clap::CommandFactory for #item_name #ty_generics #where_clause { + fn command<'b>() -> clap::Command { + let #app_var = clap::Command::new(#name) + .subcommand_required(true) + .arg_required_else_help(true); + ::augment_subcommands(#app_var) + } + + fn command_for_update<'b>() -> clap::Command { + let #app_var = clap::Command::new(#name); + ::augment_subcommands_for_update(#app_var) + .subcommand_required(false) + .arg_required_else_help(false) + } + } + }) +} diff --git a/collector/compile-benchmarks/clap_derive-4.5.32/src/derives/mod.rs b/collector/compile-benchmarks/clap_derive-4.5.32/src/derives/mod.rs new file mode 100644 index 000000000..429e6358f --- /dev/null +++ b/collector/compile-benchmarks/clap_derive-4.5.32/src/derives/mod.rs @@ -0,0 +1,23 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) , +// Kevin Knapp (@kbknapp) , and +// Ana Hobden (@hoverbear) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +// +// This work was derived from Structopt (https://github.com/TeXitoi/structopt) +// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the +// MIT/Apache 2.0 license. +mod args; +mod into_app; +mod parser; +mod subcommand; +mod value_enum; + +pub(crate) use self::parser::derive_parser; +pub(crate) use args::derive_args; +pub(crate) use subcommand::derive_subcommand; +pub(crate) use value_enum::derive_value_enum; diff --git a/collector/compile-benchmarks/clap_derive-4.5.32/src/derives/parser.rs b/collector/compile-benchmarks/clap_derive-4.5.32/src/derives/parser.rs new file mode 100644 index 000000000..bdbcfd39b --- /dev/null +++ b/collector/compile-benchmarks/clap_derive-4.5.32/src/derives/parser.rs @@ -0,0 +1,119 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) , +// Kevin Knapp (@kbknapp) , and +// Ana Hobden (@hoverbear) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +// +// This work was derived from Structopt (https://github.com/TeXitoi/structopt) +// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the +// MIT/Apache 2.0 license. + +use proc_macro2::TokenStream; +use quote::quote; +use syn::Ident; +use syn::Variant; +use syn::{ + self, punctuated::Punctuated, token::Comma, Data, DataStruct, DeriveInput, Field, Fields, + Generics, +}; + +use crate::derives::args::collect_args_fields; +use crate::derives::{args, into_app, subcommand}; +use crate::item::Item; +use crate::item::Name; + +pub(crate) fn derive_parser(input: &DeriveInput) -> Result { + let ident = &input.ident; + let pkg_name = std::env::var("CARGO_PKG_NAME").ok().unwrap_or_default(); + + match input.data { + Data::Struct(DataStruct { + fields: Fields::Named(ref fields), + .. + }) => { + let name = Name::Assigned(quote!(#pkg_name)); + let item = Item::from_args_struct(input, name)?; + let fields = collect_args_fields(&item, fields)?; + gen_for_struct(&item, ident, &input.generics, &fields) + } + Data::Struct(DataStruct { + fields: Fields::Unit, + .. + }) => { + let name = Name::Assigned(quote!(#pkg_name)); + let item = Item::from_args_struct(input, name)?; + let fields = Punctuated::::new(); + let fields = fields + .iter() + .map(|field| { + let item = Item::from_args_field(field, item.casing(), item.env_casing())?; + Ok((field, item)) + }) + .collect::, syn::Error>>()?; + gen_for_struct(&item, ident, &input.generics, &fields) + } + Data::Enum(ref e) => { + let name = Name::Assigned(quote!(#pkg_name)); + let item = Item::from_subcommand_enum(input, name)?; + let variants = e + .variants + .iter() + .map(|variant| { + let item = + Item::from_subcommand_variant(variant, item.casing(), item.env_casing())?; + Ok((variant, item)) + }) + .collect::, syn::Error>>()?; + gen_for_enum(&item, ident, &input.generics, &variants) + } + _ => abort_call_site!("`#[derive(Parser)]` only supports non-tuple structs and enums"), + } +} + +fn gen_for_struct( + item: &Item, + item_name: &Ident, + generics: &Generics, + fields: &[(&Field, Item)], +) -> Result { + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + + let into_app = into_app::gen_for_struct(item, item_name, generics)?; + let args = args::gen_for_struct(item, item_name, generics, fields)?; + + Ok(quote! { + #[automatically_derived] + #[allow( + unused_qualifications, + clippy::redundant_locals, + )] + impl #impl_generics clap::Parser for #item_name #ty_generics #where_clause {} + + #into_app + #args + }) +} + +fn gen_for_enum( + item: &Item, + item_name: &Ident, + generics: &Generics, + variants: &[(&Variant, Item)], +) -> Result { + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + + let into_app = into_app::gen_for_enum(item, item_name, generics)?; + let subcommand = subcommand::gen_for_enum(item, item_name, generics, variants)?; + + Ok(quote! { + #[automatically_derived] + impl #impl_generics clap::Parser for #item_name #ty_generics #where_clause {} + + #into_app + #subcommand + }) +} diff --git a/collector/compile-benchmarks/clap_derive-4.5.32/src/derives/subcommand.rs b/collector/compile-benchmarks/clap_derive-4.5.32/src/derives/subcommand.rs new file mode 100644 index 000000000..6853b65d3 --- /dev/null +++ b/collector/compile-benchmarks/clap_derive-4.5.32/src/derives/subcommand.rs @@ -0,0 +1,674 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) , +// Kevin Knapp (@kbknapp) , and +// Ana Hobden (@hoverbear) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +// +// This work was derived from Structopt (https://github.com/TeXitoi/structopt) +// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the +// MIT/Apache 2.0 license. + +use proc_macro2::{Ident, Span, TokenStream}; +use quote::{format_ident, quote, quote_spanned}; +use syn::{spanned::Spanned, Data, DeriveInput, FieldsUnnamed, Generics, Variant}; + +use crate::derives::args; +use crate::derives::args::collect_args_fields; +use crate::item::{Item, Kind, Name}; +use crate::utils::{is_simple_ty, subty_if_name}; + +pub(crate) fn derive_subcommand(input: &DeriveInput) -> Result { + let ident = &input.ident; + + match input.data { + Data::Enum(ref e) => { + let name = Name::Derived(ident.clone()); + let item = Item::from_subcommand_enum(input, name)?; + let variants = e + .variants + .iter() + .map(|variant| { + let item = + Item::from_subcommand_variant(variant, item.casing(), item.env_casing())?; + Ok((variant, item)) + }) + .collect::, syn::Error>>()?; + gen_for_enum(&item, ident, &input.generics, &variants) + } + _ => abort_call_site!("`#[derive(Subcommand)]` only supports enums"), + } +} + +pub(crate) fn gen_for_enum( + item: &Item, + item_name: &Ident, + generics: &Generics, + variants: &[(&Variant, Item)], +) -> Result { + if !matches!(&*item.kind(), Kind::Command(_)) { + abort! { item.kind().span(), + "`{}` cannot be used with `command`", + item.kind().name(), + } + } + + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + + let from_arg_matches = gen_from_arg_matches(variants)?; + let update_from_arg_matches = gen_update_from_arg_matches(variants)?; + + let augmentation = gen_augment(variants, item, false)?; + let augmentation_update = gen_augment(variants, item, true)?; + let has_subcommand = gen_has_subcommand(variants)?; + + Ok(quote! { + #[allow( + dead_code, + unreachable_code, + unused_variables, + unused_braces, + unused_qualifications, + )] + #[allow( + clippy::style, + clippy::complexity, + clippy::pedantic, + clippy::restriction, + clippy::perf, + clippy::deprecated, + clippy::nursery, + clippy::cargo, + clippy::suspicious_else_formatting, + clippy::almost_swapped, + clippy::redundant_locals, + )] + #[automatically_derived] + impl #impl_generics clap::FromArgMatches for #item_name #ty_generics #where_clause { + fn from_arg_matches(__clap_arg_matches: &clap::ArgMatches) -> ::std::result::Result { + Self::from_arg_matches_mut(&mut __clap_arg_matches.clone()) + } + + #from_arg_matches + + fn update_from_arg_matches(&mut self, __clap_arg_matches: &clap::ArgMatches) -> ::std::result::Result<(), clap::Error> { + self.update_from_arg_matches_mut(&mut __clap_arg_matches.clone()) + } + #update_from_arg_matches + } + + #[allow( + dead_code, + unreachable_code, + unused_variables, + unused_braces, + unused_qualifications, + )] + #[allow( + clippy::style, + clippy::complexity, + clippy::pedantic, + clippy::restriction, + clippy::perf, + clippy::deprecated, + clippy::nursery, + clippy::cargo, + clippy::suspicious_else_formatting, + clippy::almost_swapped, + clippy::redundant_locals, + )] + #[automatically_derived] + impl #impl_generics clap::Subcommand for #item_name #ty_generics #where_clause { + fn augment_subcommands <'b>(__clap_app: clap::Command) -> clap::Command { + #augmentation + } + fn augment_subcommands_for_update <'b>(__clap_app: clap::Command) -> clap::Command { + #augmentation_update + } + fn has_subcommand(__clap_name: &str) -> bool { + #has_subcommand + } + } + }) +} + +fn gen_augment( + variants: &[(&Variant, Item)], + parent_item: &Item, + override_required: bool, +) -> Result { + use syn::Fields::{Named, Unit, Unnamed}; + + let app_var = Ident::new("__clap_app", Span::call_site()); + + let mut subcommands = Vec::new(); + for (variant, item) in variants { + let kind = item.kind(); + + let genned = match &*kind { + Kind::Skip(_, _) | Kind::Arg(_) | Kind::FromGlobal(_) | Kind::Value => None, + + Kind::ExternalSubcommand => { + let ty = match variant.fields { + Unnamed(ref fields) if fields.unnamed.len() == 1 => &fields.unnamed[0].ty, + + _ => abort!( + variant, + "The enum variant marked with `external_subcommand` must be \ + a single-typed tuple, and the type must be either `Vec` \ + or `Vec`." + ), + }; + let deprecations = if !override_required { + item.deprecations() + } else { + quote!() + }; + let subty = subty_if_name(ty, "Vec").ok_or_else(|| { + format_err!( + ty.span(), + "The type must be `Vec<_>` \ + to be used with `external_subcommand`." + ) + })?; + let subcommand = quote_spanned! { kind.span()=> + #deprecations + let #app_var = #app_var + .external_subcommand_value_parser(clap::value_parser!(#subty)); + }; + Some(subcommand) + } + + Kind::Flatten(_) => match variant.fields { + Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => { + let ty = &unnamed[0].ty; + let deprecations = if !override_required { + item.deprecations() + } else { + quote!() + }; + let next_help_heading = item.next_help_heading(); + let next_display_order = item.next_display_order(); + let subcommand = if override_required { + quote! { + #deprecations + let #app_var = #app_var + #next_help_heading + #next_display_order; + let #app_var = <#ty as clap::Subcommand>::augment_subcommands_for_update(#app_var); + } + } else { + quote! { + #deprecations + let #app_var = #app_var + #next_help_heading + #next_display_order; + let #app_var = <#ty as clap::Subcommand>::augment_subcommands(#app_var); + } + }; + Some(subcommand) + } + _ => abort!( + variant, + "`flatten` is usable only with single-typed tuple variants" + ), + }, + + Kind::Subcommand(_) => { + let subcommand_var = Ident::new("__clap_subcommand", Span::call_site()); + let arg_block = match variant.fields { + Named(_) => { + abort!(variant, "non single-typed tuple enums are not supported") + } + Unit => quote!( #subcommand_var ), + Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => { + let ty = &unnamed[0].ty; + if override_required { + quote_spanned! { ty.span()=> + { + <#ty as clap::Subcommand>::augment_subcommands_for_update(#subcommand_var) + } + } + } else { + quote_spanned! { ty.span()=> + { + <#ty as clap::Subcommand>::augment_subcommands(#subcommand_var) + } + } + } + } + Unnamed(..) => { + abort!(variant, "non single-typed tuple enums are not supported") + } + }; + + let name = item.cased_name(); + let deprecations = if !override_required { + item.deprecations() + } else { + quote!() + }; + let initial_app_methods = item.initial_top_level_methods(); + let final_from_attrs = item.final_top_level_methods(); + let override_methods = if override_required { + quote_spanned! { kind.span()=> + .subcommand_required(false) + .arg_required_else_help(false) + } + } else { + quote!() + }; + let subcommand = quote! { + let #app_var = #app_var.subcommand({ + #deprecations; + let #subcommand_var = clap::Command::new(#name); + let #subcommand_var = #subcommand_var + .subcommand_required(true) + .arg_required_else_help(true); + let #subcommand_var = #subcommand_var #initial_app_methods; + let #subcommand_var = #arg_block; + #subcommand_var #final_from_attrs #override_methods + }); + }; + Some(subcommand) + } + + Kind::Command(_) => { + let subcommand_var = Ident::new("__clap_subcommand", Span::call_site()); + let sub_augment = match variant.fields { + Named(ref fields) => { + // Defer to `gen_augment` for adding cmd methods + let fields = collect_args_fields(item, fields)?; + args::gen_augment(&fields, &subcommand_var, item, override_required)? + } + Unit => { + let arg_block = quote!( #subcommand_var ); + let initial_app_methods = item.initial_top_level_methods(); + let final_from_attrs = item.final_top_level_methods(); + quote! { + let #subcommand_var = #subcommand_var #initial_app_methods; + let #subcommand_var = #arg_block; + #subcommand_var #final_from_attrs + } + } + Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => { + let ty = &unnamed[0].ty; + let arg_block = if override_required { + quote_spanned! { ty.span()=> + { + <#ty as clap::Args>::augment_args_for_update(#subcommand_var) + } + } + } else { + quote_spanned! { ty.span()=> + { + <#ty as clap::Args>::augment_args(#subcommand_var) + } + } + }; + let initial_app_methods = item.initial_top_level_methods(); + let final_from_attrs = item.final_top_level_methods(); + quote! { + let #subcommand_var = #subcommand_var #initial_app_methods; + let #subcommand_var = #arg_block; + #subcommand_var #final_from_attrs + } + } + Unnamed(..) => { + abort!(variant, "non single-typed tuple enums are not supported") + } + }; + + let deprecations = if !override_required { + item.deprecations() + } else { + quote!() + }; + let name = item.cased_name(); + let subcommand = quote! { + let #app_var = #app_var.subcommand({ + #deprecations + let #subcommand_var = clap::Command::new(#name); + #sub_augment + }); + }; + Some(subcommand) + } + }; + subcommands.push(genned); + } + + let deprecations = if !override_required { + parent_item.deprecations() + } else { + quote!() + }; + let initial_app_methods = parent_item.initial_top_level_methods(); + let final_app_methods = parent_item.final_top_level_methods(); + Ok(quote! { + #deprecations; + let #app_var = #app_var #initial_app_methods; + #( #subcommands )*; + #app_var #final_app_methods + }) +} + +fn gen_has_subcommand(variants: &[(&Variant, Item)]) -> Result { + use syn::Fields::Unnamed; + + let mut ext_subcmd = false; + + let (flatten_variants, variants): (Vec<_>, Vec<_>) = variants + .iter() + .filter_map(|(variant, item)| { + let kind = item.kind(); + match &*kind { + Kind::Skip(_, _) | Kind::Arg(_) | Kind::FromGlobal(_) | Kind::Value => None, + + Kind::ExternalSubcommand => { + ext_subcmd = true; + None + } + Kind::Flatten(_) | Kind::Subcommand(_) | Kind::Command(_) => Some((variant, item)), + } + }) + .partition(|(_, item)| { + let kind = item.kind(); + matches!(&*kind, Kind::Flatten(_)) + }); + + let subcommands = variants.iter().map(|(_variant, item)| { + let sub_name = item.cased_name(); + quote! { + if #sub_name == __clap_name { + return true + } + } + }); + let child_subcommands = flatten_variants + .iter() + .map(|(variant, _attrs)| match variant.fields { + Unnamed(ref fields) if fields.unnamed.len() == 1 => { + let ty = &fields.unnamed[0].ty; + Ok(quote! { + if <#ty as clap::Subcommand>::has_subcommand(__clap_name) { + return true; + } + }) + } + _ => abort!( + variant, + "`flatten` is usable only with single-typed tuple variants" + ), + }) + .collect::, syn::Error>>()?; + + let genned = if ext_subcmd { + quote! { true } + } else { + quote! { + #( #subcommands )* + + #( #child_subcommands )else* + + false + } + }; + Ok(genned) +} + +fn gen_from_arg_matches(variants: &[(&Variant, Item)]) -> Result { + use syn::Fields::{Named, Unit, Unnamed}; + + let subcommand_name_var = format_ident!("__clap_name"); + let sub_arg_matches_var = format_ident!("__clap_arg_matches"); + + let mut ext_subcmd = None; + let mut flatten_variants = Vec::new(); + let mut unflatten_variants = Vec::new(); + for (variant, item) in variants { + let kind = item.kind(); + match &*kind { + Kind::Skip(_, _) | Kind::Arg(_) | Kind::FromGlobal(_) | Kind::Value => {} + + Kind::ExternalSubcommand => { + if ext_subcmd.is_some() { + abort!( + item.kind().span(), + "Only one variant can be marked with `external_subcommand`, \ + this is the second" + ); + } + + let ty = match variant.fields { + Unnamed(ref fields) if fields.unnamed.len() == 1 => &fields.unnamed[0].ty, + + _ => abort!( + variant, + "The enum variant marked with `external_subcommand` must be \ + a single-typed tuple, and the type must be either `Vec` \ + or `Vec`." + ), + }; + + let (span, str_ty) = match subty_if_name(ty, "Vec") { + Some(subty) => { + if is_simple_ty(subty, "String") { + (subty.span(), quote!(::std::string::String)) + } else if is_simple_ty(subty, "OsString") { + (subty.span(), quote!(::std::ffi::OsString)) + } else { + abort!( + ty.span(), + "The type must be either `Vec` or `Vec` \ + to be used with `external_subcommand`." + ); + } + } + + None => abort!( + ty.span(), + "The type must be either `Vec` or `Vec` \ + to be used with `external_subcommand`." + ), + }; + + ext_subcmd = Some((span, &variant.ident, str_ty)); + } + Kind::Flatten(_) | Kind::Subcommand(_) | Kind::Command(_) => { + if matches!(&*item.kind(), Kind::Flatten(_)) { + flatten_variants.push((variant, item)); + } else { + unflatten_variants.push((variant, item)); + } + } + } + } + + let subcommands = unflatten_variants.iter().map(|(variant, item)| { + let sub_name = item.cased_name(); + let variant_name = &variant.ident; + let constructor_block = match variant.fields { + Named(ref fields) => { + let fields = collect_args_fields(item, fields)?; + args::gen_constructor(&fields)? + }, + Unit => quote!(), + Unnamed(ref fields) if fields.unnamed.len() == 1 => { + let ty = &fields.unnamed[0].ty; + quote!( ( <#ty as clap::FromArgMatches>::from_arg_matches_mut(__clap_arg_matches)? ) ) + } + Unnamed(..) => abort_call_site!("{}: tuple enums are not supported", variant.ident), + }; + + Ok(quote! { + if #subcommand_name_var == #sub_name && !#sub_arg_matches_var.contains_id("") { + return ::std::result::Result::Ok(Self :: #variant_name #constructor_block) + } + }) + }).collect::, syn::Error>>()?; + let child_subcommands = flatten_variants.iter().map(|(variant, _attrs)| { + let variant_name = &variant.ident; + match variant.fields { + Unnamed(ref fields) if fields.unnamed.len() == 1 => { + let ty = &fields.unnamed[0].ty; + Ok(quote! { + if __clap_arg_matches + .subcommand_name() + .map(|__clap_name| <#ty as clap::Subcommand>::has_subcommand(__clap_name)) + .unwrap_or_default() + { + let __clap_res = <#ty as clap::FromArgMatches>::from_arg_matches_mut(__clap_arg_matches)?; + return ::std::result::Result::Ok(Self :: #variant_name (__clap_res)); + } + }) + } + _ => abort!( + variant, + "`flatten` is usable only with single-typed tuple variants" + ), + } + }).collect::, syn::Error>>()?; + + let wildcard = match ext_subcmd { + Some((span, var_name, str_ty)) => quote_spanned! { span=> + ::std::result::Result::Ok(Self::#var_name( + ::std::iter::once(#str_ty::from(#subcommand_name_var)) + .chain( + #sub_arg_matches_var + .remove_many::<#str_ty>("") + .unwrap() + .map(#str_ty::from) + ) + .collect::<::std::vec::Vec<_>>() + )) + }, + + None => quote! { + ::std::result::Result::Err(clap::Error::raw(clap::error::ErrorKind::InvalidSubcommand, format!("The subcommand '{}' wasn't recognized", #subcommand_name_var))) + }, + }; + + let raw_deprecated = args::raw_deprecated(); + Ok(quote! { + fn from_arg_matches_mut(__clap_arg_matches: &mut clap::ArgMatches) -> ::std::result::Result { + #raw_deprecated + + #( #child_subcommands )else* + + if let Some((#subcommand_name_var, mut __clap_arg_sub_matches)) = __clap_arg_matches.remove_subcommand() { + let #sub_arg_matches_var = &mut __clap_arg_sub_matches; + #( #subcommands )* + + #wildcard + } else { + ::std::result::Result::Err(clap::Error::raw(clap::error::ErrorKind::MissingSubcommand, "A subcommand is required but one was not provided.")) + } + } + }) +} + +fn gen_update_from_arg_matches(variants: &[(&Variant, Item)]) -> Result { + use syn::Fields::{Named, Unit, Unnamed}; + + let (flatten, variants): (Vec<_>, Vec<_>) = variants + .iter() + .filter_map(|(variant, item)| { + let kind = item.kind(); + match &*kind { + // Fallback to `from_arg_matches_mut` + Kind::Skip(_, _) + | Kind::Arg(_) + | Kind::FromGlobal(_) + | Kind::Value + | Kind::ExternalSubcommand => None, + Kind::Flatten(_) | Kind::Subcommand(_) | Kind::Command(_) => Some((variant, item)), + } + }) + .partition(|(_, item)| { + let kind = item.kind(); + matches!(&*kind, Kind::Flatten(_)) + }); + + let subcommands = variants.iter().map(|(variant, item)| { + let sub_name = item.cased_name(); + let variant_name = &variant.ident; + let (pattern, updater) = match variant.fields { + Named(ref fields) => { + let field_names = fields.named.iter().map(|field| { + field.ident.as_ref().unwrap() + }).collect::>(); + let fields = collect_args_fields(item, fields)?; + let update = args::gen_updater(&fields, false)?; + (quote!( { #( #field_names, )* }), quote!( { #update } )) + } + Unit => (quote!(), quote!({})), + Unnamed(ref fields) => { + if fields.unnamed.len() == 1 { + ( + quote!((ref mut __clap_arg)), + quote!(clap::FromArgMatches::update_from_arg_matches_mut( + __clap_arg, + __clap_arg_matches + )?), + ) + } else { + abort_call_site!("{}: tuple enums are not supported", variant.ident) + } + } + }; + + Ok(quote! { + Self :: #variant_name #pattern if #sub_name == __clap_name => { + let (_, mut __clap_arg_sub_matches) = __clap_arg_matches.remove_subcommand().unwrap(); + let __clap_arg_matches = &mut __clap_arg_sub_matches; + #updater + } + }) + }).collect::, _>>()?; + + let child_subcommands = flatten.iter().map(|(variant, _attrs)| { + let variant_name = &variant.ident; + match variant.fields { + Unnamed(ref fields) if fields.unnamed.len() == 1 => { + let ty = &fields.unnamed[0].ty; + Ok(quote! { + if <#ty as clap::Subcommand>::has_subcommand(__clap_name) { + if let Self :: #variant_name (child) = s { + <#ty as clap::FromArgMatches>::update_from_arg_matches_mut(child, __clap_arg_matches)?; + return ::std::result::Result::Ok(()); + } + } + }) + } + _ => abort!( + variant, + "`flatten` is usable only with single-typed tuple variants" + ), + } + }).collect::, _>>()?; + + let raw_deprecated = args::raw_deprecated(); + Ok(quote! { + fn update_from_arg_matches_mut<'b>( + &mut self, + __clap_arg_matches: &mut clap::ArgMatches, + ) -> ::std::result::Result<(), clap::Error> { + #raw_deprecated + + if let Some(__clap_name) = __clap_arg_matches.subcommand_name() { + match self { + #( #subcommands ),* + s => { + #( #child_subcommands )* + *s = ::from_arg_matches_mut(__clap_arg_matches)?; + } + } + } + ::std::result::Result::Ok(()) + } + }) +} diff --git a/collector/compile-benchmarks/clap_derive-4.5.32/src/derives/value_enum.rs b/collector/compile-benchmarks/clap_derive-4.5.32/src/derives/value_enum.rs new file mode 100644 index 000000000..a1ffa90ca --- /dev/null +++ b/collector/compile-benchmarks/clap_derive-4.5.32/src/derives/value_enum.rs @@ -0,0 +1,130 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) , +// Kevin Knapp (@kbknapp) , and +// Ana Hobden (@hoverbear) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use proc_macro2::TokenStream; +use quote::quote; +use quote::quote_spanned; +use syn::{spanned::Spanned, Data, DeriveInput, Fields, Ident, Variant}; + +use crate::item::{Item, Kind, Name}; + +pub(crate) fn derive_value_enum(input: &DeriveInput) -> Result { + let ident = &input.ident; + + match input.data { + Data::Enum(ref e) => { + let name = Name::Derived(ident.clone()); + let item = Item::from_value_enum(input, name)?; + let mut variants = Vec::new(); + for variant in &e.variants { + let item = + Item::from_value_enum_variant(variant, item.casing(), item.env_casing())?; + variants.push((variant, item)); + } + gen_for_enum(&item, ident, &variants) + } + _ => abort_call_site!("`#[derive(ValueEnum)]` only supports enums"), + } +} + +pub(crate) fn gen_for_enum( + item: &Item, + item_name: &Ident, + variants: &[(&Variant, Item)], +) -> Result { + if !matches!(&*item.kind(), Kind::Value) { + abort! { item.kind().span(), + "`{}` cannot be used with `value`", + item.kind().name(), + } + } + + let lits = lits(variants)?; + let value_variants = gen_value_variants(&lits); + let to_possible_value = gen_to_possible_value(item, &lits); + + Ok(quote! { + #[allow( + dead_code, + unreachable_code, + unused_variables, + unused_braces, + unused_qualifications, + )] + #[allow( + clippy::style, + clippy::complexity, + clippy::pedantic, + clippy::restriction, + clippy::perf, + clippy::deprecated, + clippy::nursery, + clippy::cargo, + clippy::suspicious_else_formatting, + clippy::almost_swapped, + clippy::redundant_locals, + )] + #[automatically_derived] + impl clap::ValueEnum for #item_name { + #value_variants + #to_possible_value + } + }) +} + +fn lits(variants: &[(&Variant, Item)]) -> Result, syn::Error> { + let mut genned = Vec::new(); + for (variant, item) in variants { + if let Kind::Skip(_, _) = &*item.kind() { + continue; + } + if !matches!(variant.fields, Fields::Unit) { + abort!(variant.span(), "`#[derive(ValueEnum)]` only supports unit variants. Non-unit variants must be skipped"); + } + let fields = item.field_methods(); + let deprecations = item.deprecations(); + let name = item.cased_name(); + genned.push(( + quote_spanned! { variant.span()=> { + #deprecations + clap::builder::PossibleValue::new(#name) + #fields + }}, + variant.ident.clone(), + )); + } + Ok(genned) +} + +fn gen_value_variants(lits: &[(TokenStream, Ident)]) -> TokenStream { + let lit = lits.iter().map(|l| &l.1).collect::>(); + + quote! { + fn value_variants<'a>() -> &'a [Self]{ + &[#(Self::#lit),*] + } + } +} + +fn gen_to_possible_value(item: &Item, lits: &[(TokenStream, Ident)]) -> TokenStream { + let (lit, variant): (Vec, Vec) = lits.iter().cloned().unzip(); + + let deprecations = item.deprecations(); + + quote! { + fn to_possible_value<'a>(&self) -> ::std::option::Option { + #deprecations + match self { + #(Self::#variant => Some(#lit),)* + _ => None + } + } + } +} diff --git a/collector/compile-benchmarks/clap_derive-4.5.32/src/dummies.rs b/collector/compile-benchmarks/clap_derive-4.5.32/src/dummies.rs new file mode 100644 index 000000000..627fdc024 --- /dev/null +++ b/collector/compile-benchmarks/clap_derive-4.5.32/src/dummies.rs @@ -0,0 +1,99 @@ +//! Dummy implementations that we emit along with an error. + +use proc_macro2::Ident; +use quote::quote; + +#[must_use] +pub(crate) fn parser(name: &Ident) -> proc_macro2::TokenStream { + let into_app = into_app(name); + quote!( + #[automatically_derived] + impl clap::Parser for #name {} + #into_app + ) +} + +#[must_use] +pub(crate) fn into_app(name: &Ident) -> proc_macro2::TokenStream { + quote! { + #[automatically_derived] + impl clap::CommandFactory for #name { + fn command<'b>() -> clap::Command { + unimplemented!() + } + fn command_for_update<'b>() -> clap::Command { + unimplemented!() + } + } + } +} + +#[must_use] +pub(crate) fn from_arg_matches(name: &Ident) -> proc_macro2::TokenStream { + quote! { + #[automatically_derived] + impl clap::FromArgMatches for #name { + fn from_arg_matches(_m: &clap::ArgMatches) -> ::std::result::Result { + unimplemented!() + } + fn update_from_arg_matches(&mut self, matches: &clap::ArgMatches) -> ::std::result::Result<(), clap::Error>{ + unimplemented!() + } + } + } +} + +#[must_use] +pub(crate) fn subcommand(name: &Ident) -> proc_macro2::TokenStream { + let from_arg_matches = from_arg_matches(name); + quote! { + #[automatically_derived] + impl clap::Subcommand for #name { + fn augment_subcommands(_cmd: clap::Command) -> clap::Command { + unimplemented!() + } + fn augment_subcommands_for_update(_cmd: clap::Command) -> clap::Command { + unimplemented!() + } + fn has_subcommand(name: &str) -> bool { + unimplemented!() + } + } + #from_arg_matches + } +} + +#[must_use] +pub(crate) fn args(name: &Ident) -> proc_macro2::TokenStream { + let from_arg_matches = from_arg_matches(name); + quote! { + #[automatically_derived] + impl clap::Args for #name { + fn augment_args(_cmd: clap::Command) -> clap::Command { + unimplemented!() + } + fn augment_args_for_update(_cmd: clap::Command) -> clap::Command { + unimplemented!() + } + } + #from_arg_matches + } +} + +#[must_use] +pub(crate) fn value_enum(name: &Ident) -> proc_macro2::TokenStream { + quote! { + #[automatically_derived] + impl clap::ValueEnum for #name { + fn value_variants<'a>() -> &'a [Self]{ + unimplemented!() + } + fn from_str(_input: &str, _ignore_case: bool) -> ::std::result::Result { + unimplemented!() + } + fn to_possible_value<'a>(&self) -> ::std::option::Option{ + unimplemented!() + } + } + } +} diff --git a/collector/compile-benchmarks/clap_derive-4.5.32/src/item.rs b/collector/compile-benchmarks/clap_derive-4.5.32/src/item.rs new file mode 100644 index 000000000..ab32918e5 --- /dev/null +++ b/collector/compile-benchmarks/clap_derive-4.5.32/src/item.rs @@ -0,0 +1,1472 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) , +// Kevin Knapp (@kbknapp) , and +// Ana Hobden (@hoverbear) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +// +// This work was derived from Structopt (https://github.com/TeXitoi/structopt) +// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the +// MIT/Apache 2.0 license. + +use std::env; + +use heck::{ToKebabCase, ToLowerCamelCase, ToShoutySnakeCase, ToSnakeCase, ToUpperCamelCase}; +use proc_macro2::{self, Span, TokenStream}; +use quote::{format_ident, quote, quote_spanned, ToTokens}; +use syn::DeriveInput; +use syn::{self, ext::IdentExt, spanned::Spanned, Attribute, Field, Ident, LitStr, Type, Variant}; + +use crate::attr::{AttrKind, AttrValue, ClapAttr, MagicAttrName}; +use crate::utils::{extract_doc_comment, format_doc_comment, inner_type, is_simple_ty, Sp, Ty}; + +/// Default casing style for generated arguments. +pub(crate) const DEFAULT_CASING: CasingStyle = CasingStyle::Kebab; + +/// Default casing style for environment variables +pub(crate) const DEFAULT_ENV_CASING: CasingStyle = CasingStyle::ScreamingSnake; + +#[derive(Clone)] +pub(crate) struct Item { + name: Name, + casing: Sp, + env_casing: Sp, + ty: Option, + doc_comment: Vec, + methods: Vec, + deprecations: Vec, + value_parser: Option, + action: Option, + verbatim_doc_comment: bool, + force_long_help: bool, + next_display_order: Option, + next_help_heading: Option, + is_enum: bool, + is_positional: bool, + skip_group: bool, + group_id: Name, + group_methods: Vec, + kind: Sp, +} + +impl Item { + pub(crate) fn from_args_struct(input: &DeriveInput, name: Name) -> Result { + let ident = input.ident.clone(); + let span = input.ident.span(); + let attrs = &input.attrs; + let argument_casing = Sp::new(DEFAULT_CASING, span); + let env_casing = Sp::new(DEFAULT_ENV_CASING, span); + let kind = Sp::new(Kind::Command(Sp::new(Ty::Other, span)), span); + + let mut res = Self::new(name, ident, None, argument_casing, env_casing, kind); + let parsed_attrs = ClapAttr::parse_all(attrs)?; + res.infer_kind(&parsed_attrs)?; + res.push_attrs(&parsed_attrs)?; + res.push_doc_comment(attrs, "about", Some("long_about")); + + Ok(res) + } + + pub(crate) fn from_subcommand_enum( + input: &DeriveInput, + name: Name, + ) -> Result { + let ident = input.ident.clone(); + let span = input.ident.span(); + let attrs = &input.attrs; + let argument_casing = Sp::new(DEFAULT_CASING, span); + let env_casing = Sp::new(DEFAULT_ENV_CASING, span); + let kind = Sp::new(Kind::Command(Sp::new(Ty::Other, span)), span); + + let mut res = Self::new(name, ident, None, argument_casing, env_casing, kind); + let parsed_attrs = ClapAttr::parse_all(attrs)?; + res.infer_kind(&parsed_attrs)?; + res.push_attrs(&parsed_attrs)?; + res.push_doc_comment(attrs, "about", Some("long_about")); + + Ok(res) + } + + pub(crate) fn from_value_enum(input: &DeriveInput, name: Name) -> Result { + let ident = input.ident.clone(); + let span = input.ident.span(); + let attrs = &input.attrs; + let argument_casing = Sp::new(DEFAULT_CASING, span); + let env_casing = Sp::new(DEFAULT_ENV_CASING, span); + let kind = Sp::new(Kind::Value, span); + + let mut res = Self::new(name, ident, None, argument_casing, env_casing, kind); + let parsed_attrs = ClapAttr::parse_all(attrs)?; + res.infer_kind(&parsed_attrs)?; + res.push_attrs(&parsed_attrs)?; + // Ignoring `push_doc_comment` as there is no top-level clap builder to add documentation + // to + + if res.has_explicit_methods() { + abort!( + res.methods[0].name.span(), + "{} doesn't exist for `ValueEnum` enums", + res.methods[0].name + ); + } + + Ok(res) + } + + pub(crate) fn from_subcommand_variant( + variant: &Variant, + struct_casing: Sp, + env_casing: Sp, + ) -> Result { + let name = variant.ident.clone(); + let ident = variant.ident.clone(); + let span = variant.span(); + let ty = match variant.fields { + syn::Fields::Unnamed(syn::FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => { + Ty::from_syn_ty(&unnamed[0].ty) + } + syn::Fields::Named(_) | syn::Fields::Unnamed(..) | syn::Fields::Unit => { + Sp::new(Ty::Other, span) + } + }; + let kind = Sp::new(Kind::Command(ty), span); + let mut res = Self::new( + Name::Derived(name), + ident, + None, + struct_casing, + env_casing, + kind, + ); + let parsed_attrs = ClapAttr::parse_all(&variant.attrs)?; + res.infer_kind(&parsed_attrs)?; + res.push_attrs(&parsed_attrs)?; + if matches!(&*res.kind, Kind::Command(_) | Kind::Subcommand(_)) { + res.push_doc_comment(&variant.attrs, "about", Some("long_about")); + } + + match &*res.kind { + Kind::Flatten(_) => { + if res.has_explicit_methods() { + abort!( + res.kind.span(), + "methods are not allowed for flattened entry" + ); + } + } + + Kind::Subcommand(_) + | Kind::ExternalSubcommand + | Kind::FromGlobal(_) + | Kind::Skip(_, _) + | Kind::Command(_) + | Kind::Value + | Kind::Arg(_) => (), + } + + Ok(res) + } + + pub(crate) fn from_value_enum_variant( + variant: &Variant, + argument_casing: Sp, + env_casing: Sp, + ) -> Result { + let ident = variant.ident.clone(); + let span = variant.span(); + let kind = Sp::new(Kind::Value, span); + let mut res = Self::new( + Name::Derived(variant.ident.clone()), + ident, + None, + argument_casing, + env_casing, + kind, + ); + let parsed_attrs = ClapAttr::parse_all(&variant.attrs)?; + res.infer_kind(&parsed_attrs)?; + res.push_attrs(&parsed_attrs)?; + if matches!(&*res.kind, Kind::Value) { + res.push_doc_comment(&variant.attrs, "help", None); + } + + Ok(res) + } + + pub(crate) fn from_args_field( + field: &Field, + struct_casing: Sp, + env_casing: Sp, + ) -> Result { + let name = field.ident.clone().unwrap(); + let ident = field.ident.clone().unwrap(); + let span = field.span(); + let ty = Ty::from_syn_ty(&field.ty); + let kind = Sp::new(Kind::Arg(ty), span); + let mut res = Self::new( + Name::Derived(name), + ident, + Some(field.ty.clone()), + struct_casing, + env_casing, + kind, + ); + let parsed_attrs = ClapAttr::parse_all(&field.attrs)?; + res.infer_kind(&parsed_attrs)?; + res.push_attrs(&parsed_attrs)?; + if matches!(&*res.kind, Kind::Arg(_)) { + res.push_doc_comment(&field.attrs, "help", Some("long_help")); + } + + match &*res.kind { + Kind::Flatten(_) => { + if res.has_explicit_methods() { + abort!( + res.kind.span(), + "methods are not allowed for flattened entry" + ); + } + } + + Kind::Subcommand(_) => { + if res.has_explicit_methods() { + abort!( + res.kind.span(), + "methods in attributes are not allowed for subcommand" + ); + } + } + Kind::Skip(_, _) + | Kind::FromGlobal(_) + | Kind::Arg(_) + | Kind::Command(_) + | Kind::Value + | Kind::ExternalSubcommand => {} + } + + Ok(res) + } + + fn new( + name: Name, + ident: Ident, + ty: Option, + casing: Sp, + env_casing: Sp, + kind: Sp, + ) -> Self { + let group_id = Name::Derived(ident); + Self { + name, + ty, + casing, + env_casing, + doc_comment: vec![], + methods: vec![], + deprecations: vec![], + value_parser: None, + action: None, + verbatim_doc_comment: false, + force_long_help: false, + next_display_order: None, + next_help_heading: None, + is_enum: false, + is_positional: true, + skip_group: false, + group_id, + group_methods: vec![], + kind, + } + } + + fn push_method(&mut self, kind: AttrKind, name: Ident, arg: impl ToTokens) { + self.push_method_(kind, name, arg.to_token_stream()); + } + + fn push_method_(&mut self, kind: AttrKind, name: Ident, arg: TokenStream) { + if name == "id" { + match kind { + AttrKind::Command | AttrKind::Value => { + self.deprecations.push(Deprecation { + span: name.span(), + id: "id_is_only_for_arg", + version: "4.0.0", + description: format!( + "`#[{}(id)] was allowed by mistake, instead use `#[{}(name)]`", + kind.as_str(), + kind.as_str() + ), + }); + self.name = Name::Assigned(arg); + } + AttrKind::Group => { + self.group_id = Name::Assigned(arg); + } + AttrKind::Arg | AttrKind::Clap | AttrKind::StructOpt => { + self.name = Name::Assigned(arg); + } + } + } else if name == "name" { + match kind { + AttrKind::Arg => { + self.deprecations.push(Deprecation { + span: name.span(), + id: "id_is_only_for_arg", + version: "4.0.0", + description: format!( + "`#[{}(name)] was allowed by mistake, instead use `#[{}(id)]` or `#[{}(value_name)]`", + kind.as_str(), + kind.as_str(), + kind.as_str() + ), + }); + self.name = Name::Assigned(arg); + } + AttrKind::Group => self.group_methods.push(Method::new(name, arg)), + AttrKind::Command | AttrKind::Value | AttrKind::Clap | AttrKind::StructOpt => { + self.name = Name::Assigned(arg); + } + } + } else if name == "value_parser" { + self.value_parser = Some(ValueParser::Explicit(Method::new(name, arg))); + } else if name == "action" { + self.action = Some(Action::Explicit(Method::new(name, arg))); + } else { + if name == "short" || name == "long" { + self.is_positional = false; + } + match kind { + AttrKind::Group => self.group_methods.push(Method::new(name, arg)), + _ => self.methods.push(Method::new(name, arg)), + }; + } + } + + fn infer_kind(&mut self, attrs: &[ClapAttr]) -> Result<(), syn::Error> { + for attr in attrs { + if let Some(AttrValue::Call(_)) = &attr.value { + continue; + } + + let actual_attr_kind = *attr.kind.get(); + let kind = match &attr.magic { + Some(MagicAttrName::FromGlobal) => { + if attr.value.is_some() { + let expr = attr.value_or_abort()?; + abort!(expr, "attribute `{}` does not accept a value", attr.name); + } + let ty = self + .kind() + .ty() + .cloned() + .unwrap_or_else(|| Sp::new(Ty::Other, self.kind.span())); + let kind = Sp::new(Kind::FromGlobal(ty), attr.name.span()); + Some(kind) + } + Some(MagicAttrName::Subcommand) if attr.value.is_none() => { + if attr.value.is_some() { + let expr = attr.value_or_abort()?; + abort!(expr, "attribute `{}` does not accept a value", attr.name); + } + let ty = self + .kind() + .ty() + .cloned() + .unwrap_or_else(|| Sp::new(Ty::Other, self.kind.span())); + let kind = Sp::new(Kind::Subcommand(ty), attr.name.span()); + Some(kind) + } + Some(MagicAttrName::ExternalSubcommand) if attr.value.is_none() => { + if attr.value.is_some() { + let expr = attr.value_or_abort()?; + abort!(expr, "attribute `{}` does not accept a value", attr.name); + } + let kind = Sp::new(Kind::ExternalSubcommand, attr.name.span()); + Some(kind) + } + Some(MagicAttrName::Flatten) if attr.value.is_none() => { + if attr.value.is_some() { + let expr = attr.value_or_abort()?; + abort!(expr, "attribute `{}` does not accept a value", attr.name); + } + let ty = self + .kind() + .ty() + .cloned() + .unwrap_or_else(|| Sp::new(Ty::Other, self.kind.span())); + let kind = Sp::new(Kind::Flatten(ty), attr.name.span()); + Some(kind) + } + Some(MagicAttrName::Skip) if actual_attr_kind != AttrKind::Group => { + let expr = attr.value.clone(); + let kind = Sp::new(Kind::Skip(expr, self.kind.attr_kind()), attr.name.span()); + Some(kind) + } + _ => None, + }; + + if let Some(kind) = kind { + self.set_kind(kind)?; + } + } + + Ok(()) + } + + fn push_attrs(&mut self, attrs: &[ClapAttr]) -> Result<(), syn::Error> { + for attr in attrs { + let actual_attr_kind = *attr.kind.get(); + let expected_attr_kind = self.kind.attr_kind(); + match (actual_attr_kind, expected_attr_kind) { + (AttrKind::Clap, _) | (AttrKind::StructOpt, _) => { + self.deprecations.push(Deprecation::attribute( + "4.0.0", + actual_attr_kind, + expected_attr_kind, + attr.kind.span(), + )); + } + + (AttrKind::Group, AttrKind::Command) => {} + + _ if attr.kind != expected_attr_kind => { + abort!( + attr.kind.span(), + "Expected `{}` attribute instead of `{}`", + expected_attr_kind.as_str(), + actual_attr_kind.as_str() + ); + } + + _ => {} + } + + if let Some(AttrValue::Call(tokens)) = &attr.value { + // Force raw mode with method call syntax + self.push_method(*attr.kind.get(), attr.name.clone(), quote!(#(#tokens),*)); + continue; + } + + match &attr.magic { + Some(MagicAttrName::Short) if attr.value.is_none() => { + assert_attr_kind(attr, &[AttrKind::Arg])?; + + self.push_method( + *attr.kind.get(), + attr.name.clone(), + self.name.clone().translate_char(*self.casing), + ); + } + + Some(MagicAttrName::Long) if attr.value.is_none() => { + assert_attr_kind(attr, &[AttrKind::Arg])?; + + self.push_method(*attr.kind.get(), attr.name.clone(), self.name.clone().translate(*self.casing)); + } + + Some(MagicAttrName::ValueParser) if attr.value.is_none() => { + assert_attr_kind(attr, &[AttrKind::Arg])?; + + self.deprecations.push(Deprecation { + span: attr.name.span(), + id: "bare_value_parser", + version: "4.0.0", + description: "`#[arg(value_parser)]` is now the default and is no longer needed`".to_owned(), + }); + self.value_parser = Some(ValueParser::Implicit(attr.name.clone())); + } + + Some(MagicAttrName::Action) if attr.value.is_none() => { + assert_attr_kind(attr, &[AttrKind::Arg])?; + + self.deprecations.push(Deprecation { + span: attr.name.span(), + id: "bare_action", + version: "4.0.0", + description: "`#[arg(action)]` is now the default and is no longer needed`".to_owned(), + }); + self.action = Some(Action::Implicit(attr.name.clone())); + } + + Some(MagicAttrName::Env) if attr.value.is_none() => { + assert_attr_kind(attr, &[AttrKind::Arg])?; + + self.push_method( + *attr.kind.get(), + attr.name.clone(), + self.name.clone().translate(*self.env_casing), + ); + } + + Some(MagicAttrName::ValueEnum) if attr.value.is_none() => { + assert_attr_kind(attr, &[AttrKind::Arg])?; + + self.is_enum = true; + } + + Some(MagicAttrName::VerbatimDocComment) if attr.value.is_none() => { + self.verbatim_doc_comment = true; + } + + Some(MagicAttrName::About) if attr.value.is_none() => { + assert_attr_kind(attr, &[AttrKind::Command])?; + + if let Some(method) = + Method::from_env(attr.name.clone(), "CARGO_PKG_DESCRIPTION")? + { + self.methods.push(method); + } + } + + Some(MagicAttrName::LongAbout) if attr.value.is_none() => { + assert_attr_kind(attr, &[AttrKind::Command])?; + + self.force_long_help = true; + } + + Some(MagicAttrName::LongHelp) if attr.value.is_none() => { + assert_attr_kind(attr, &[AttrKind::Arg])?; + + self.force_long_help = true; + } + + Some(MagicAttrName::Author) if attr.value.is_none() => { + assert_attr_kind(attr, &[AttrKind::Command])?; + + if let Some(method) = Method::from_env(attr.name.clone(), "CARGO_PKG_AUTHORS")? { + self.methods.push(method); + } + } + + Some(MagicAttrName::Version) if attr.value.is_none() => { + assert_attr_kind(attr, &[AttrKind::Command])?; + + if let Some(method) = Method::from_env(attr.name.clone(), "CARGO_PKG_VERSION")? { + self.methods.push(method); + } + } + + Some(MagicAttrName::DefaultValueT) => { + assert_attr_kind(attr, &[AttrKind::Arg])?; + + let ty = if let Some(ty) = self.ty.as_ref() { + ty + } else { + abort!( + attr.name.clone(), + "#[arg(default_value_t)] (without an argument) can be used \ + only on field level\n\n= note: {note}\n\n", + + note = "see \ + https://docs.rs/clap/latest/clap/_derive/index.html#arg-attributes") + }; + + let val = if let Some(expr) = &attr.value { + quote!(#expr) + } else { + quote!(<#ty as ::std::default::Default>::default()) + }; + + let val = if attrs + .iter() + .any(|a| a.magic == Some(MagicAttrName::ValueEnum)) + { + quote_spanned!(attr.name.span()=> { + static DEFAULT_VALUE: ::std::sync::OnceLock = ::std::sync::OnceLock::new(); + let s = DEFAULT_VALUE.get_or_init(|| { + let val: #ty = #val; + clap::ValueEnum::to_possible_value(&val).unwrap().get_name().to_owned() + }); + let s: &'static str = &*s; + s + }) + } else { + quote_spanned!(attr.name.span()=> { + static DEFAULT_VALUE: ::std::sync::OnceLock = ::std::sync::OnceLock::new(); + let s = DEFAULT_VALUE.get_or_init(|| { + let val: #ty = #val; + ::std::string::ToString::to_string(&val) + }); + let s: &'static str = &*s; + s + }) + }; + + let raw_ident = Ident::new("default_value", attr.name.span()); + self.methods.push(Method::new(raw_ident, val)); + } + + Some(MagicAttrName::DefaultValuesT) => { + assert_attr_kind(attr, &[AttrKind::Arg])?; + + let ty = if let Some(ty) = self.ty.as_ref() { + ty + } else { + abort!( + attr.name.clone(), + "#[arg(default_values_t)] (without an argument) can be used \ + only on field level\n\n= note: {note}\n\n", + + note = "see \ + https://docs.rs/clap/latest/clap/_derive/index.html#arg-attributes") + }; + let expr = attr.value_or_abort()?; + + let container_type = Ty::from_syn_ty(ty); + if *container_type != Ty::Vec { + abort!( + attr.name.clone(), + "#[arg(default_values_t)] can be used only on Vec types\n\n= note: {note}\n\n", + + note = "see \ + https://docs.rs/clap/latest/clap/_derive/index.html#arg-attributes") + } + let inner_type = inner_type(ty); + + // Use `Borrow<#inner_type>` so we accept `&Vec<#inner_type>` and + // `Vec<#inner_type>`. + let val = if attrs + .iter() + .any(|a| a.magic == Some(MagicAttrName::ValueEnum)) + { + quote_spanned!(attr.name.span()=> { + { + fn iter_to_vals(iterable: impl IntoIterator) -> impl Iterator + where + T: ::std::borrow::Borrow<#inner_type> + { + iterable + .into_iter() + .map(|val| { + clap::ValueEnum::to_possible_value(val.borrow()).unwrap().get_name().to_owned() + }) + } + + static DEFAULT_STRINGS: ::std::sync::OnceLock> = ::std::sync::OnceLock::new(); + static DEFAULT_VALUES: ::std::sync::OnceLock> = ::std::sync::OnceLock::new(); + DEFAULT_VALUES.get_or_init(|| { + DEFAULT_STRINGS.get_or_init(|| iter_to_vals(#expr).collect()).iter().map(::std::string::String::as_str).collect() + }).iter().copied() + } + }) + } else { + quote_spanned!(attr.name.span()=> { + { + fn iter_to_vals(iterable: impl IntoIterator) -> impl Iterator + where + T: ::std::borrow::Borrow<#inner_type> + { + iterable.into_iter().map(|val| val.borrow().to_string()) + } + + static DEFAULT_STRINGS: ::std::sync::OnceLock> = ::std::sync::OnceLock::new(); + static DEFAULT_VALUES: ::std::sync::OnceLock> = ::std::sync::OnceLock::new(); + DEFAULT_VALUES.get_or_init(|| { + DEFAULT_STRINGS.get_or_init(|| iter_to_vals(#expr).collect()).iter().map(::std::string::String::as_str).collect() + }).iter().copied() + } + }) + }; + + self.methods.push(Method::new( + Ident::new("default_values", attr.name.span()), + val, + )); + } + + Some(MagicAttrName::DefaultValueOsT) => { + assert_attr_kind(attr, &[AttrKind::Arg])?; + + let ty = if let Some(ty) = self.ty.as_ref() { + ty + } else { + abort!( + attr.name.clone(), + "#[arg(default_value_os_t)] (without an argument) can be used \ + only on field level\n\n= note: {note}\n\n", + + note = "see \ + https://docs.rs/clap/latest/clap/_derive/index.html#arg-attributes") + }; + + let val = if let Some(expr) = &attr.value { + quote!(#expr) + } else { + quote!(<#ty as ::std::default::Default>::default()) + }; + + let val = if attrs + .iter() + .any(|a| a.magic == Some(MagicAttrName::ValueEnum)) + { + quote_spanned!(attr.name.span()=> { + static DEFAULT_VALUE: ::std::sync::OnceLock = ::std::sync::OnceLock::new(); + let s = DEFAULT_VALUE.get_or_init(|| { + let val: #ty = #val; + clap::ValueEnum::to_possible_value(&val).unwrap().get_name().to_owned() + }); + let s: &'static str = &*s; + s + }) + } else { + quote_spanned!(attr.name.span()=> { + static DEFAULT_VALUE: ::std::sync::OnceLock<::std::ffi::OsString> = ::std::sync::OnceLock::new(); + let s = DEFAULT_VALUE.get_or_init(|| { + let val: #ty = #val; + ::std::ffi::OsString::from(val) + }); + let s: &'static ::std::ffi::OsStr = &*s; + s + }) + }; + + let raw_ident = Ident::new("default_value", attr.name.span()); + self.methods.push(Method::new(raw_ident, val)); + } + + Some(MagicAttrName::DefaultValuesOsT) => { + assert_attr_kind(attr, &[AttrKind::Arg])?; + + let ty = if let Some(ty) = self.ty.as_ref() { + ty + } else { + abort!( + attr.name.clone(), + "#[arg(default_values_os_t)] (without an argument) can be used \ + only on field level\n\n= note: {note}\n\n", + + note = "see \ + https://docs.rs/clap/latest/clap/_derive/index.html#arg-attributes") + }; + let expr = attr.value_or_abort()?; + + let container_type = Ty::from_syn_ty(ty); + if *container_type != Ty::Vec { + abort!( + attr.name.clone(), + "#[arg(default_values_os_t)] can be used only on Vec types\n\n= note: {note}\n\n", + + note = "see \ + https://docs.rs/clap/latest/clap/_derive/index.html#arg-attributes") + } + let inner_type = inner_type(ty); + + // Use `Borrow<#inner_type>` so we accept `&Vec<#inner_type>` and + // `Vec<#inner_type>`. + let val = if attrs + .iter() + .any(|a| a.magic == Some(MagicAttrName::ValueEnum)) + { + quote_spanned!(attr.name.span()=> { + { + fn iter_to_vals(iterable: impl IntoIterator) -> impl Iterator + where + T: ::std::borrow::Borrow<#inner_type> + { + iterable + .into_iter() + .map(|val| { + clap::ValueEnum::to_possible_value(val.borrow()).unwrap().get_name().to_owned().into() + }) + } + + static DEFAULT_STRINGS: ::std::sync::OnceLock> = ::std::sync::OnceLock::new(); + static DEFAULT_VALUES: ::std::sync::OnceLock> = ::std::sync::OnceLock::new(); + DEFAULT_VALUES.get_or_init(|| { + DEFAULT_STRINGS.get_or_init(|| iter_to_vals(#expr).collect()).iter().map(::std::ffi::OsString::as_os_str).collect() + }).iter().copied() + } + }) + } else { + quote_spanned!(attr.name.span()=> { + { + fn iter_to_vals(iterable: impl IntoIterator) -> impl Iterator + where + T: ::std::borrow::Borrow<#inner_type> + { + iterable.into_iter().map(|val| val.borrow().into()) + } + + static DEFAULT_STRINGS: ::std::sync::OnceLock> = ::std::sync::OnceLock::new(); + static DEFAULT_VALUES: ::std::sync::OnceLock> = ::std::sync::OnceLock::new(); + DEFAULT_VALUES.get_or_init(|| { + DEFAULT_STRINGS.get_or_init(|| iter_to_vals(#expr).collect()).iter().map(::std::ffi::OsString::as_os_str).collect() + }).iter().copied() + } + }) + }; + + self.methods.push(Method::new( + Ident::new("default_values", attr.name.span()), + val, + )); + } + + Some(MagicAttrName::NextDisplayOrder) => { + assert_attr_kind(attr, &[AttrKind::Command])?; + + let expr = attr.value_or_abort()?; + self.next_display_order = Some(Method::new(attr.name.clone(), quote!(#expr))); + } + + Some(MagicAttrName::NextHelpHeading) => { + assert_attr_kind(attr, &[AttrKind::Command])?; + + let expr = attr.value_or_abort()?; + self.next_help_heading = Some(Method::new(attr.name.clone(), quote!(#expr))); + } + + Some(MagicAttrName::RenameAll) => { + let lit = attr.lit_str_or_abort()?; + self.casing = CasingStyle::from_lit(lit)?; + } + + Some(MagicAttrName::RenameAllEnv) => { + assert_attr_kind(attr, &[AttrKind::Command, AttrKind::Arg])?; + + let lit = attr.lit_str_or_abort()?; + self.env_casing = CasingStyle::from_lit(lit)?; + } + + Some(MagicAttrName::Skip) if actual_attr_kind == AttrKind::Group => { + self.skip_group = true; + } + + None + // Magic only for the default, otherwise just forward to the builder + | Some(MagicAttrName::Short) + | Some(MagicAttrName::Long) + | Some(MagicAttrName::Env) + | Some(MagicAttrName::About) + | Some(MagicAttrName::LongAbout) + | Some(MagicAttrName::LongHelp) + | Some(MagicAttrName::Author) + | Some(MagicAttrName::Version) + => { + let expr = attr.value_or_abort()?; + self.push_method(*attr.kind.get(), attr.name.clone(), expr); + } + + // Magic only for the default, otherwise just forward to the builder + Some(MagicAttrName::ValueParser) | Some(MagicAttrName::Action) => { + let expr = attr.value_or_abort()?; + self.push_method(*attr.kind.get(), attr.name.clone(), expr); + } + + // Directives that never receive a value + Some(MagicAttrName::ValueEnum) + | Some(MagicAttrName::VerbatimDocComment) => { + let expr = attr.value_or_abort()?; + abort!(expr, "attribute `{}` does not accept a value", attr.name); + } + + // Kinds + Some(MagicAttrName::FromGlobal) + | Some(MagicAttrName::Subcommand) + | Some(MagicAttrName::ExternalSubcommand) + | Some(MagicAttrName::Flatten) + | Some(MagicAttrName::Skip) => { + } + } + } + + if self.has_explicit_methods() { + if let Kind::Skip(_, attr) = &*self.kind { + abort!( + self.methods[0].name.span(), + "`{}` cannot be used with `#[{}(skip)]", + self.methods[0].name, + attr.as_str(), + ); + } + if let Kind::FromGlobal(_) = &*self.kind { + abort!( + self.methods[0].name.span(), + "`{}` cannot be used with `#[arg(from_global)]", + self.methods[0].name, + ); + } + } + + Ok(()) + } + + fn push_doc_comment(&mut self, attrs: &[Attribute], short_name: &str, long_name: Option<&str>) { + let lines = extract_doc_comment(attrs); + + if !lines.is_empty() { + let (short_help, long_help) = + format_doc_comment(&lines, !self.verbatim_doc_comment, self.force_long_help); + let short_name = format_ident!("{short_name}"); + let short = Method::new( + short_name, + short_help + .map(|h| quote!(#h)) + .unwrap_or_else(|| quote!(None)), + ); + self.doc_comment.push(short); + if let Some(long_name) = long_name { + let long_name = format_ident!("{long_name}"); + let long = Method::new( + long_name, + long_help + .map(|h| quote!(#h)) + .unwrap_or_else(|| quote!(None)), + ); + self.doc_comment.push(long); + } + } + } + + fn set_kind(&mut self, kind: Sp) -> Result<(), syn::Error> { + match (self.kind.get(), kind.get()) { + (Kind::Arg(_), Kind::FromGlobal(_)) + | (Kind::Arg(_), Kind::Subcommand(_)) + | (Kind::Arg(_), Kind::Flatten(_)) + | (Kind::Arg(_), Kind::Skip(_, _)) + | (Kind::Command(_), Kind::Subcommand(_)) + | (Kind::Command(_), Kind::Flatten(_)) + | (Kind::Command(_), Kind::Skip(_, _)) + | (Kind::Command(_), Kind::ExternalSubcommand) + | (Kind::Value, Kind::Skip(_, _)) => { + self.kind = kind; + } + + (_, _) => { + let old = self.kind.name(); + let new = kind.name(); + abort!(kind.span(), "`{new}` cannot be used with `{old}`"); + } + } + Ok(()) + } + + pub(crate) fn find_default_method(&self) -> Option<&Method> { + self.methods + .iter() + .find(|m| m.name == "default_value" || m.name == "default_value_os") + } + + /// generate methods from attributes on top of struct or enum + pub(crate) fn initial_top_level_methods(&self) -> TokenStream { + let next_display_order = self.next_display_order.as_ref().into_iter(); + let next_help_heading = self.next_help_heading.as_ref().into_iter(); + quote!( + #(#next_display_order)* + #(#next_help_heading)* + ) + } + + pub(crate) fn final_top_level_methods(&self) -> TokenStream { + let methods = &self.methods; + let doc_comment = &self.doc_comment; + + quote!( #(#doc_comment)* #(#methods)*) + } + + /// generate methods on top of a field + pub(crate) fn field_methods(&self) -> TokenStream { + let methods = &self.methods; + let doc_comment = &self.doc_comment; + quote!( #(#doc_comment)* #(#methods)* ) + } + + pub(crate) fn group_id(&self) -> &Name { + &self.group_id + } + + pub(crate) fn group_methods(&self) -> TokenStream { + let group_methods = &self.group_methods; + quote!( #(#group_methods)* ) + } + + pub(crate) fn deprecations(&self) -> TokenStream { + let deprecations = &self.deprecations; + quote!( #(#deprecations)* ) + } + + pub(crate) fn next_display_order(&self) -> TokenStream { + let next_display_order = self.next_display_order.as_ref().into_iter(); + quote!( #(#next_display_order)* ) + } + + pub(crate) fn next_help_heading(&self) -> TokenStream { + let next_help_heading = self.next_help_heading.as_ref().into_iter(); + quote!( #(#next_help_heading)* ) + } + + pub(crate) fn id(&self) -> &Name { + &self.name + } + + pub(crate) fn cased_name(&self) -> TokenStream { + self.name.clone().translate(*self.casing) + } + + pub(crate) fn value_name(&self) -> TokenStream { + self.name.clone().translate(CasingStyle::ScreamingSnake) + } + + pub(crate) fn value_parser(&self, field_type: &Type) -> Method { + self.value_parser + .clone() + .map(|p| { + let inner_type = inner_type(field_type); + p.resolve(inner_type) + }) + .unwrap_or_else(|| { + let inner_type = inner_type(field_type); + if let Some(action) = self.action.as_ref() { + let span = action.span(); + default_value_parser(inner_type, span) + } else { + let span = self + .action + .as_ref() + .map(|a| a.span()) + .unwrap_or_else(|| self.kind.span()); + default_value_parser(inner_type, span) + } + }) + } + + pub(crate) fn action(&self, field_type: &Type) -> Method { + self.action + .clone() + .map(|p| p.resolve(field_type)) + .unwrap_or_else(|| { + if let Some(value_parser) = self.value_parser.as_ref() { + let span = value_parser.span(); + default_action(field_type, span) + } else { + let span = self + .value_parser + .as_ref() + .map(|a| a.span()) + .unwrap_or_else(|| self.kind.span()); + default_action(field_type, span) + } + }) + } + + pub(crate) fn kind(&self) -> Sp { + self.kind.clone() + } + + pub(crate) fn is_positional(&self) -> bool { + self.is_positional + } + + pub(crate) fn casing(&self) -> Sp { + self.casing + } + + pub(crate) fn env_casing(&self) -> Sp { + self.env_casing + } + + pub(crate) fn has_explicit_methods(&self) -> bool { + self.methods + .iter() + .any(|m| m.name != "help" && m.name != "long_help") + } + + pub(crate) fn skip_group(&self) -> bool { + self.skip_group + } +} + +#[derive(Clone)] +enum ValueParser { + Explicit(Method), + Implicit(Ident), +} + +impl ValueParser { + fn resolve(self, _inner_type: &Type) -> Method { + match self { + Self::Explicit(method) => method, + Self::Implicit(ident) => default_value_parser(_inner_type, ident.span()), + } + } + + fn span(&self) -> Span { + match self { + Self::Explicit(method) => method.name.span(), + Self::Implicit(ident) => ident.span(), + } + } +} + +fn default_value_parser(inner_type: &Type, span: Span) -> Method { + let func = Ident::new("value_parser", span); + Method::new( + func, + quote_spanned! { span=> + clap::value_parser!(#inner_type) + }, + ) +} + +#[derive(Clone)] +pub(crate) enum Action { + Explicit(Method), + Implicit(Ident), +} + +impl Action { + pub(crate) fn resolve(self, _field_type: &Type) -> Method { + match self { + Self::Explicit(method) => method, + Self::Implicit(ident) => default_action(_field_type, ident.span()), + } + } + + pub(crate) fn span(&self) -> Span { + match self { + Self::Explicit(method) => method.name.span(), + Self::Implicit(ident) => ident.span(), + } + } +} + +fn default_action(field_type: &Type, span: Span) -> Method { + let ty = Ty::from_syn_ty(field_type); + let args = match *ty { + Ty::Vec | Ty::OptionVec | Ty::VecVec | Ty::OptionVecVec => { + quote_spanned! { span=> + clap::ArgAction::Append + } + } + Ty::Option | Ty::OptionOption => { + quote_spanned! { span=> + clap::ArgAction::Set + } + } + _ => { + if is_simple_ty(field_type, "bool") { + quote_spanned! { span=> + clap::ArgAction::SetTrue + } + } else { + quote_spanned! { span=> + clap::ArgAction::Set + } + } + } + }; + + let func = Ident::new("action", span); + Method::new(func, args) +} + +#[allow(clippy::large_enum_variant)] +#[derive(Clone)] +pub(crate) enum Kind { + Arg(Sp), + Command(Sp), + Value, + FromGlobal(Sp), + Subcommand(Sp), + Flatten(Sp), + Skip(Option, AttrKind), + ExternalSubcommand, +} + +impl Kind { + pub(crate) fn name(&self) -> &'static str { + match self { + Self::Arg(_) => "arg", + Self::Command(_) => "command", + Self::Value => "value", + Self::FromGlobal(_) => "from_global", + Self::Subcommand(_) => "subcommand", + Self::Flatten(_) => "flatten", + Self::Skip(_, _) => "skip", + Self::ExternalSubcommand => "external_subcommand", + } + } + + pub(crate) fn attr_kind(&self) -> AttrKind { + match self { + Self::Arg(_) => AttrKind::Arg, + Self::Command(_) => AttrKind::Command, + Self::Value => AttrKind::Value, + Self::FromGlobal(_) => AttrKind::Arg, + Self::Subcommand(_) => AttrKind::Command, + Self::Flatten(_) => AttrKind::Command, + Self::Skip(_, kind) => *kind, + Self::ExternalSubcommand => AttrKind::Command, + } + } + + pub(crate) fn ty(&self) -> Option<&Sp> { + match self { + Self::Arg(ty) + | Self::Command(ty) + | Self::Flatten(ty) + | Self::FromGlobal(ty) + | Self::Subcommand(ty) => Some(ty), + Self::Value | Self::Skip(_, _) | Self::ExternalSubcommand => None, + } + } +} + +#[derive(Clone)] +pub(crate) struct Method { + name: Ident, + args: TokenStream, +} + +impl Method { + pub(crate) fn new(name: Ident, args: TokenStream) -> Self { + Method { name, args } + } + + fn from_env(ident: Ident, env_var: &str) -> Result, syn::Error> { + let mut lit = match env::var(env_var) { + Ok(val) => { + if val.is_empty() { + return Ok(None); + } + LitStr::new(&val, ident.span()) + } + Err(_) => { + abort!( + ident, + "cannot derive `{}` from Cargo.toml\n\n= note: {note}\n\n= help: {help}\n\n", + ident, + note = format_args!("`{env_var}` environment variable is not set"), + help = format_args!("use `{ident} = \"...\"` to set {ident} manually") + ); + } + }; + + if ident == "author" { + let edited = process_author_str(&lit.value()); + lit = LitStr::new(&edited, lit.span()); + } + + Ok(Some(Method::new(ident, quote!(#lit)))) + } + + pub(crate) fn args(&self) -> &TokenStream { + &self.args + } +} + +impl ToTokens for Method { + fn to_tokens(&self, ts: &mut TokenStream) { + let Method { ref name, ref args } = self; + + let tokens = quote!( .#name(#args) ); + + tokens.to_tokens(ts); + } +} + +#[derive(Clone)] +pub(crate) struct Deprecation { + pub(crate) span: Span, + pub(crate) id: &'static str, + pub(crate) version: &'static str, + pub(crate) description: String, +} + +impl Deprecation { + fn attribute(version: &'static str, old: AttrKind, new: AttrKind, span: Span) -> Self { + Self { + span, + id: "old_attribute", + version, + description: format!( + "Attribute `#[{}(...)]` has been deprecated in favor of `#[{}(...)]`", + old.as_str(), + new.as_str() + ), + } + } +} + +impl ToTokens for Deprecation { + fn to_tokens(&self, ts: &mut TokenStream) { + let tokens = if cfg!(feature = "deprecated") { + let Deprecation { + span, + id, + version, + description, + } = self; + let span = *span; + let id = Ident::new(id, span); + + quote_spanned!(span=> { + #[deprecated(since = #version, note = #description)] + fn #id() {} + #id(); + }) + } else { + quote!() + }; + + tokens.to_tokens(ts); + } +} + +fn assert_attr_kind(attr: &ClapAttr, possible_kind: &[AttrKind]) -> Result<(), syn::Error> { + if *attr.kind.get() == AttrKind::Clap || *attr.kind.get() == AttrKind::StructOpt { + // deprecated + } else if !possible_kind.contains(attr.kind.get()) { + let options = possible_kind + .iter() + .map(|k| format!("`#[{}({})]`", k.as_str(), attr.name)) + .collect::>(); + abort!( + attr.name, + "Unknown `#[{}({})]` attribute ({} exists)", + attr.kind.as_str(), + attr.name, + options.join(", ") + ); + } + Ok(()) +} + +/// replace all `:` with `, ` when not inside the `<>` +/// +/// `"author1:author2:author3" => "author1, author2, author3"` +/// `"author1 :author2" => "author1 , author2"` +fn process_author_str(author: &str) -> String { + let mut res = String::with_capacity(author.len()); + let mut inside_angle_braces = 0usize; + + for ch in author.chars() { + if inside_angle_braces > 0 && ch == '>' { + inside_angle_braces -= 1; + res.push(ch); + } else if ch == '<' { + inside_angle_braces += 1; + res.push(ch); + } else if inside_angle_braces == 0 && ch == ':' { + res.push_str(", "); + } else { + res.push(ch); + } + } + + res +} + +/// Defines the casing for the attributes long representation. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub(crate) enum CasingStyle { + /// Indicate word boundaries with uppercase letter, excluding the first word. + Camel, + /// Keep all letters lowercase and indicate word boundaries with hyphens. + Kebab, + /// Indicate word boundaries with uppercase letter, including the first word. + Pascal, + /// Keep all letters uppercase and indicate word boundaries with underscores. + ScreamingSnake, + /// Keep all letters lowercase and indicate word boundaries with underscores. + Snake, + /// Keep all letters lowercase and remove word boundaries. + Lower, + /// Keep all letters uppercase and remove word boundaries. + Upper, + /// Use the original attribute name defined in the code. + Verbatim, +} + +impl CasingStyle { + fn from_lit(name: &LitStr) -> Result, syn::Error> { + use self::CasingStyle::{ + Camel, Kebab, Lower, Pascal, ScreamingSnake, Snake, Upper, Verbatim, + }; + + let normalized = name.value().to_upper_camel_case().to_lowercase(); + let cs = |kind| Sp::new(kind, name.span()); + + let s = match normalized.as_ref() { + "camel" | "camelcase" => cs(Camel), + "kebab" | "kebabcase" => cs(Kebab), + "pascal" | "pascalcase" => cs(Pascal), + "screamingsnake" | "screamingsnakecase" => cs(ScreamingSnake), + "snake" | "snakecase" => cs(Snake), + "lower" | "lowercase" => cs(Lower), + "upper" | "uppercase" => cs(Upper), + "verbatim" | "verbatimcase" => cs(Verbatim), + s => abort!(name, "unsupported casing: `{s}`"), + }; + Ok(s) + } +} + +#[derive(Clone)] +pub(crate) enum Name { + Derived(Ident), + Assigned(TokenStream), +} + +impl Name { + pub(crate) fn translate(self, style: CasingStyle) -> TokenStream { + use CasingStyle::{Camel, Kebab, Lower, Pascal, ScreamingSnake, Snake, Upper, Verbatim}; + + match self { + Name::Assigned(tokens) => tokens, + Name::Derived(ident) => { + let s = ident.unraw().to_string(); + let s = match style { + Pascal => s.to_upper_camel_case(), + Kebab => s.to_kebab_case(), + Camel => s.to_lower_camel_case(), + ScreamingSnake => s.to_shouty_snake_case(), + Snake => s.to_snake_case(), + Lower => s.to_snake_case().replace('_', ""), + Upper => s.to_shouty_snake_case().replace('_', ""), + Verbatim => s, + }; + quote_spanned!(ident.span()=> #s) + } + } + } + + pub(crate) fn translate_char(self, style: CasingStyle) -> TokenStream { + use CasingStyle::{Camel, Kebab, Lower, Pascal, ScreamingSnake, Snake, Upper, Verbatim}; + + match self { + Name::Assigned(tokens) => quote!( (#tokens).chars().next().unwrap() ), + Name::Derived(ident) => { + let s = ident.unraw().to_string(); + let s = match style { + Pascal => s.to_upper_camel_case(), + Kebab => s.to_kebab_case(), + Camel => s.to_lower_camel_case(), + ScreamingSnake => s.to_shouty_snake_case(), + Snake => s.to_snake_case(), + Lower => s.to_snake_case(), + Upper => s.to_shouty_snake_case(), + Verbatim => s, + }; + + let s = s.chars().next().unwrap(); + quote_spanned!(ident.span()=> #s) + } + } + } +} + +impl ToTokens for Name { + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + Name::Assigned(t) => t.to_tokens(tokens), + Name::Derived(ident) => { + let s = ident.unraw().to_string(); + quote_spanned!(ident.span()=> #s).to_tokens(tokens); + } + } + } +} diff --git a/collector/compile-benchmarks/clap_derive-4.5.32/src/lib.rs b/collector/compile-benchmarks/clap_derive-4.5.32/src/lib.rs new file mode 100644 index 000000000..760c5b522 --- /dev/null +++ b/collector/compile-benchmarks/clap_derive-4.5.32/src/lib.rs @@ -0,0 +1,118 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) , +// Kevin Knapp (@kbknapp) , and +// Ana Hobden (@hoverbear) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +// +// This work was derived from Structopt (https://github.com/TeXitoi/structopt) +// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the +// MIT/Apache 2.0 license. + +#![doc = include_str!("../README.md")] +#![doc(html_logo_url = "https://raw.githubusercontent.com/clap-rs/clap/master/assets/clap.png")] +#![cfg_attr(docsrs, feature(doc_auto_cfg))] +#![forbid(unsafe_code)] +#![warn(missing_docs)] +#![warn(clippy::print_stderr)] +#![warn(clippy::print_stdout)] + +use proc_macro::TokenStream; +use syn::{parse_macro_input, DeriveInput}; +use syn::{Data, DataStruct, Fields}; + +#[macro_use] +mod macros; + +mod attr; +mod derives; +mod dummies; +mod item; +mod utils; + +/// Generates the `ValueEnum` impl. +#[proc_macro_derive(ValueEnum, attributes(clap, value))] +pub fn value_enum(input: TokenStream) -> TokenStream { + let input: DeriveInput = parse_macro_input!(input); + derives::derive_value_enum(&input) + .unwrap_or_else(|err| { + let dummy = dummies::value_enum(&input.ident); + to_compile_error(err, dummy) + }) + .into() +} + +/// Generates the `Parser` implementation. +/// +/// This is far less verbose than defining the `clap::Command` struct manually, +/// receiving an instance of `clap::ArgMatches` from conducting parsing, and then +/// implementing a conversion code to instantiate an instance of the user +/// context struct. +#[proc_macro_derive(Parser, attributes(clap, structopt, command, arg, group))] +pub fn parser(input: TokenStream) -> TokenStream { + let input: DeriveInput = parse_macro_input!(input); + derives::derive_parser(&input) + .unwrap_or_else(|err| { + let specific_dummy = match input.data { + Data::Struct(DataStruct { + fields: Fields::Named(ref _fields), + .. + }) => Some(dummies::args(&input.ident)), + Data::Struct(DataStruct { + fields: Fields::Unit, + .. + }) => Some(dummies::args(&input.ident)), + Data::Enum(_) => Some(dummies::subcommand(&input.ident)), + _ => None, + }; + let dummy = specific_dummy + .map(|specific_dummy| { + let parser_dummy = dummies::parser(&input.ident); + quote::quote! { + #parser_dummy + #specific_dummy + } + }) + .unwrap_or_else(|| quote::quote!()); + to_compile_error(err, dummy) + }) + .into() +} + +/// Generates the `Subcommand` impl. +#[proc_macro_derive(Subcommand, attributes(clap, command, arg, group))] +pub fn subcommand(input: TokenStream) -> TokenStream { + let input: DeriveInput = parse_macro_input!(input); + derives::derive_subcommand(&input) + .unwrap_or_else(|err| { + let dummy = dummies::subcommand(&input.ident); + to_compile_error(err, dummy) + }) + .into() +} + +/// Generates the `Args` impl. +#[proc_macro_derive(Args, attributes(clap, command, arg, group))] +pub fn args(input: TokenStream) -> TokenStream { + let input: DeriveInput = parse_macro_input!(input); + derives::derive_args(&input) + .unwrap_or_else(|err| { + let dummy = dummies::args(&input.ident); + to_compile_error(err, dummy) + }) + .into() +} + +fn to_compile_error( + error: syn::Error, + dummy: proc_macro2::TokenStream, +) -> proc_macro2::TokenStream { + let compile_errors = error.to_compile_error(); + quote::quote!( + #dummy + #compile_errors + ) +} diff --git a/collector/compile-benchmarks/clap_derive-4.5.32/src/macros.rs b/collector/compile-benchmarks/clap_derive-4.5.32/src/macros.rs new file mode 100644 index 000000000..282048bc1 --- /dev/null +++ b/collector/compile-benchmarks/clap_derive-4.5.32/src/macros.rs @@ -0,0 +1,21 @@ +macro_rules! format_err { + ($obj:expr, $($format:tt)+) => {{ + #[allow(unused_imports)] + use $crate::utils::error::*; + let msg = format!($($format)+); + $obj.EXPECTED_Span_OR_ToTokens(msg) + }}; +} + +macro_rules! abort { + ($obj:expr, $($format:tt)+) => {{ + return Err(format_err!($obj, $($format)+)); + }}; +} + +macro_rules! abort_call_site { + ($($format:tt)+) => {{ + let span = proc_macro2::Span::call_site(); + abort!(span, $($format)+) + }}; +} diff --git a/collector/compile-benchmarks/clap_derive-4.5.32/src/utils/doc_comments.rs b/collector/compile-benchmarks/clap_derive-4.5.32/src/utils/doc_comments.rs new file mode 100644 index 000000000..65faa55f8 --- /dev/null +++ b/collector/compile-benchmarks/clap_derive-4.5.32/src/utils/doc_comments.rs @@ -0,0 +1,410 @@ +//! The preprocessing we apply to doc comments. +//! +//! #[derive(Parser)] works in terms of "paragraphs". Paragraph is a sequence of +//! non-empty adjacent lines, delimited by sequences of blank (whitespace only) lines. + +#[cfg(feature = "unstable-markdown")] +use markdown::parse_markdown; + +pub(crate) fn extract_doc_comment(attrs: &[syn::Attribute]) -> Vec { + // multiline comments (`/** ... */`) may have LFs (`\n`) in them, + // we need to split so we could handle the lines correctly + // + // we also need to remove leading and trailing blank lines + let mut lines: Vec<_> = attrs + .iter() + .filter(|attr| attr.path().is_ident("doc")) + .filter_map(|attr| { + // non #[doc = "..."] attributes are not our concern + // we leave them for rustc to handle + match &attr.meta { + syn::Meta::NameValue(syn::MetaNameValue { + value: + syn::Expr::Lit(syn::ExprLit { + lit: syn::Lit::Str(s), + .. + }), + .. + }) => Some(s.value()), + _ => None, + } + }) + .skip_while(|s| is_blank(s)) + .flat_map(|s| { + let lines = s + .split('\n') + .map(|s| { + // remove one leading space no matter what + let s = s.strip_prefix(' ').unwrap_or(s); + s.to_owned() + }) + .collect::>(); + lines + }) + .collect(); + + while let Some(true) = lines.last().map(|s| is_blank(s)) { + lines.pop(); + } + + lines +} + +pub(crate) fn format_doc_comment( + lines: &[String], + preprocess: bool, + force_long: bool, +) -> (Option, Option) { + if preprocess { + let (short, long) = parse_markdown(lines); + let long = long.or_else(|| force_long.then(|| short.clone())); + + (Some(remove_period(short)), long) + } else if let Some(first_blank) = lines.iter().position(|s| is_blank(s)) { + let short = lines[..first_blank].join("\n"); + let long = lines.join("\n"); + + (Some(short), Some(long)) + } else { + let short = lines.join("\n"); + let long = force_long.then(|| short.clone()); + + (Some(short), long) + } +} + +#[cfg(not(feature = "unstable-markdown"))] +fn split_paragraphs(lines: &[String]) -> Vec { + use std::iter; + + let mut last_line = 0; + iter::from_fn(|| { + let slice = &lines[last_line..]; + let start = slice.iter().position(|s| !is_blank(s)).unwrap_or(0); + + let slice = &slice[start..]; + let len = slice + .iter() + .position(|s| is_blank(s)) + .unwrap_or(slice.len()); + + last_line += start + len; + + if len != 0 { + Some(merge_lines(&slice[..len])) + } else { + None + } + }) + .collect() +} + +fn remove_period(mut s: String) -> String { + if s.ends_with('.') && !s.ends_with("..") { + s.pop(); + } + s +} + +fn is_blank(s: &str) -> bool { + s.trim().is_empty() +} + +#[cfg(not(feature = "unstable-markdown"))] +fn merge_lines(lines: impl IntoIterator>) -> String { + lines + .into_iter() + .map(|s| s.as_ref().trim().to_owned()) + .collect::>() + .join(" ") +} + +#[cfg(not(feature = "unstable-markdown"))] +fn parse_markdown(lines: &[String]) -> (String, Option) { + if lines.iter().any(|s| is_blank(s)) { + let paragraphs = split_paragraphs(lines); + let short = paragraphs[0].clone(); + let long = paragraphs.join("\n\n"); + (short, Some(long)) + } else { + let short = merge_lines(lines); + (short, None) + } +} + +#[cfg(feature = "unstable-markdown")] +mod markdown { + use anstyle::{Reset, Style}; + use pulldown_cmark::{Event, Options, Parser, Tag, TagEnd}; + use std::fmt; + use std::fmt::Write; + use std::ops::AddAssign; + + #[derive(Default)] + struct MarkdownWriter { + output: String, + /// Prefix inserted for each line. + prefix: String, + /// Should an empty line be inserted before the next anything. + hanging_paragraph: bool, + /// Are we in an empty line + dirty_line: bool, + styles: Vec