summaryrefslogtreecommitdiffstats
path: root/third_party/rust/paste
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/paste')
-rw-r--r--third_party/rust/paste/.cargo-checksum.json1
-rw-r--r--third_party/rust/paste/Cargo.toml42
-rw-r--r--third_party/rust/paste/LICENSE-APACHE201
-rw-r--r--third_party/rust/paste/LICENSE-MIT23
-rw-r--r--third_party/rust/paste/README.md157
-rw-r--r--third_party/rust/paste/build.rs33
-rw-r--r--third_party/rust/paste/src/attr.rs164
-rw-r--r--third_party/rust/paste/src/error.rs47
-rw-r--r--third_party/rust/paste/src/lib.rs453
-rw-r--r--third_party/rust/paste/src/segment.rs233
-rw-r--r--third_party/rust/paste/tests/compiletest.rs7
-rw-r--r--third_party/rust/paste/tests/test_attr.rs60
-rw-r--r--third_party/rust/paste/tests/test_doc.rs77
-rw-r--r--third_party/rust/paste/tests/test_expr.rs283
-rw-r--r--third_party/rust/paste/tests/test_item.rs269
-rw-r--r--third_party/rust/paste/tests/ui/case-warning.rs15
-rw-r--r--third_party/rust/paste/tests/ui/case-warning.stderr16
-rw-r--r--third_party/rust/paste/tests/ui/env-empty.rs7
-rw-r--r--third_party/rust/paste/tests/ui/env-empty.stderr5
-rw-r--r--third_party/rust/paste/tests/ui/env-non-string.rs7
-rw-r--r--third_party/rust/paste/tests/ui/env-non-string.stderr5
-rw-r--r--third_party/rust/paste/tests/ui/env-suffix.rs7
-rw-r--r--third_party/rust/paste/tests/ui/env-suffix.stderr5
-rw-r--r--third_party/rust/paste/tests/ui/env-unexpected.rs7
-rw-r--r--third_party/rust/paste/tests/ui/env-unexpected.stderr5
-rw-r--r--third_party/rust/paste/tests/ui/invalid-ident.rs15
-rw-r--r--third_party/rust/paste/tests/ui/invalid-ident.stderr21
-rw-r--r--third_party/rust/paste/tests/ui/missing-paren-on-env.rs7
-rw-r--r--third_party/rust/paste/tests/ui/missing-paren-on-env.stderr5
-rw-r--r--third_party/rust/paste/tests/ui/no-env-var.rs7
-rw-r--r--third_party/rust/paste/tests/ui/no-env-var.stderr5
-rw-r--r--third_party/rust/paste/tests/ui/no-ident-after-colon.rs7
-rw-r--r--third_party/rust/paste/tests/ui/no-ident-after-colon.stderr5
-rw-r--r--third_party/rust/paste/tests/ui/unexpected-group.rs7
-rw-r--r--third_party/rust/paste/tests/ui/unexpected-group.stderr5
-rw-r--r--third_party/rust/paste/tests/ui/unexpected-modifier.rs7
-rw-r--r--third_party/rust/paste/tests/ui/unexpected-modifier.stderr5
-rw-r--r--third_party/rust/paste/tests/ui/unexpected-punct.rs7
-rw-r--r--third_party/rust/paste/tests/ui/unexpected-punct.stderr5
-rw-r--r--third_party/rust/paste/tests/ui/unsupported-literal.rs21
-rw-r--r--third_party/rust/paste/tests/ui/unsupported-literal.stderr23
-rw-r--r--third_party/rust/paste/tests/ui/unsupported-modifier.rs7
-rw-r--r--third_party/rust/paste/tests/ui/unsupported-modifier.stderr5
43 files changed, 2293 insertions, 0 deletions
diff --git a/third_party/rust/paste/.cargo-checksum.json b/third_party/rust/paste/.cargo-checksum.json
new file mode 100644
index 0000000000..f993c76977
--- /dev/null
+++ b/third_party/rust/paste/.cargo-checksum.json
@@ -0,0 +1 @@
+{"files":{"Cargo.toml":"5bda0a3466aa987522d5a19b4899620910ec7386ac0f97aa5118c787ba624fc2","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"cd8f304fa9cca9566dff0bb3492beae55f47f3972ddfb8f1fa38c3da1068b4a8","build.rs":"e1f79dc7a4f6507a5834fd0124a08eb7f90b700b2f4c0225eb1086eb9b8591d1","src/attr.rs":"e8301f019e107ef9f5d8a3bbed355ffa9c0fd73396646d7fedbba333d341b7bc","src/error.rs":"be08d9d48b4cb9984b5141b12f21c31bd7293942c18b3ce25754723930cf54f5","src/lib.rs":"713754426b2a20993f978bc3a5278e7f70e95a8a4755f179d755b09bc7974b1c","src/segment.rs":"462be04eea3ad48028d0841e8000c13205eb049522b8003bcc2926f21e93cb78","tests/compiletest.rs":"022a8e400ef813d7ea1875b944549cee5125f6a995dc33e93b48cba3e1b57bd1","tests/test_attr.rs":"810d5230f4e5e23662f743808141fac50d6c6c8d0c0c4a51260df3f5a1f47a00","tests/test_doc.rs":"b8a2023e707c85ca562947e75d20293506aa303e07da3e485f3b57d87e63bb02","tests/test_expr.rs":"9c31116f6153a91cf3ce65e48dc2c5ac28a624b703121fb7067520401c5c6768","tests/test_item.rs":"b436f666bde04fdf37acd26cfd449542da2c6e59b0bffe5df84021970f2690bc","tests/ui/case-warning.rs":"6b7cb803527911fe698e7fbe080160bc5d07c0da36fac0ec29d599753ce8bd10","tests/ui/case-warning.stderr":"7b7d470edc2a11b0864534099b360e0c42d423e068f49cea7858e0b340c2ef3d","tests/ui/env-empty.rs":"093fb0844475f4710580b5e4cad162918896d4fb48d1516bae04eb39dddd265a","tests/ui/env-empty.stderr":"3462f06f6f21130a81efff3be3bc838b395f6107bfbf753e479ec5a60e09b15d","tests/ui/env-non-string.rs":"516460860e7790b412b0fcd203289720a155dfba2498616967e4d7e6e6ab4990","tests/ui/env-non-string.stderr":"fea08d588a238e0f4c06de5c5ae97ac195bb2bcfc070040fb8087bdf4d1f4eee","tests/ui/env-suffix.rs":"0f1756608efc7a6c5810b8b21528092546ac95371e048c6060064e747c1b0a27","tests/ui/env-suffix.stderr":"287fecb2a91abc61501e0bd71d7f111670a526960ab18a554f4d16ed83d940b5","tests/ui/env-unexpected.rs":"18f06317e72ea16dae6ebe7c20bf5ef26b46e01a5c54fd31dc2a351ba68e8ca5","tests/ui/env-unexpected.stderr":"14d9a40160daf0191efc2b53be5fdde5b066674053e76af170e58af08f4022aa","tests/ui/invalid-ident.rs":"c3f375cac04d56684e427834076128b6e77eeeb9e99067a06307e27693b0ff51","tests/ui/invalid-ident.stderr":"6f3e768685665707a5038046bf01752687f95f6ee81d9cf1c0ee8d500898890f","tests/ui/missing-paren-on-env.rs":"05cafef972e8c99f82834e164e7713f6056ac1a24b1f31d0320ac4fe361a53bd","tests/ui/missing-paren-on-env.stderr":"baf7b420d201f033e2113eaebd85376047397d991c35c441f07e5c6f1ec2b685","tests/ui/no-env-var.rs":"330bb71f2f3d98d23176acc5aeb1c0b66443af14c0857bb5bc92bfd130eb4a62","tests/ui/no-env-var.stderr":"68af072d3ccb2537bca9a5cd6d5796bb5ab5408f59564c8e8c66b1590ac7c5a5","tests/ui/no-ident-after-colon.rs":"a5af9548ab8d7f5b6130e1410c90f2cc589c595eb99f6939e6deb3f54e7262a2","tests/ui/no-ident-after-colon.stderr":"efac036a78f077c9c10a1ed048d69531293dcb97784ed784af0b24788ecbab54","tests/ui/unexpected-group.rs":"6b2e398a9ad3e077ea49c68b7c134ec3268e398723f4d9d5ef7b4d7b8513ac44","tests/ui/unexpected-group.stderr":"1260b5a4c295639958e3dd0da475c385184f0a916f5fdc4f1f2bfe232688f43c","tests/ui/unexpected-modifier.rs":"8b7e4c93f06fe7e7be469375e8bbaf19fec5fdffe688a0c29d2de2944a420337","tests/ui/unexpected-modifier.stderr":"581212b89358ac6bd38632bfd4322c25c171046875711fd639cfb498e01629c8","tests/ui/unexpected-punct.rs":"430e98bc03abdcc46f72cea01264c278a9c25f3d1d63064a6d78f4ae71b6a2d9","tests/ui/unexpected-punct.stderr":"24ab2e32d2da8dad93ef04dc05a2ecf6fdeb11bdebeae9454ad4759cc2b16551","tests/ui/unsupported-literal.rs":"1b2d3634fba5dc35d5ac9012d488aa93e329d1fa4489df1eadee65613a010e59","tests/ui/unsupported-literal.stderr":"6b137ccd6bc2a341aeb1e0d5321a896aa6c8db9a4e2571f4e41bd744511760bf","tests/ui/unsupported-modifier.rs":"03bb0239880070e8b470a6072c06094e1483ea91256e7cb4b9f2e8761696fa5a","tests/ui/unsupported-modifier.stderr":"890d7f484fdbd46da72923db4d7aba0ac3eaece253bb7195b7a36ae9a918027e"},"package":"d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba"} \ No newline at end of file
diff --git a/third_party/rust/paste/Cargo.toml b/third_party/rust/paste/Cargo.toml
new file mode 100644
index 0000000000..091af693e3
--- /dev/null
+++ b/third_party/rust/paste/Cargo.toml
@@ -0,0 +1,42 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies.
+#
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2018"
+rust-version = "1.31"
+name = "paste"
+version = "1.0.11"
+authors = ["David Tolnay <dtolnay@gmail.com>"]
+description = "Macros for all your token pasting needs"
+readme = "README.md"
+keywords = ["macros"]
+categories = [
+ "development-tools",
+ "no-std",
+]
+license = "MIT OR Apache-2.0"
+repository = "https://github.com/dtolnay/paste"
+
+[package.metadata.docs.rs]
+targets = ["x86_64-unknown-linux-gnu"]
+
+[lib]
+proc-macro = true
+
+[dev-dependencies.paste-test-suite]
+version = "0"
+
+[dev-dependencies.rustversion]
+version = "1.0"
+
+[dev-dependencies.trybuild]
+version = "1.0.49"
+features = ["diff"]
diff --git a/third_party/rust/paste/LICENSE-APACHE b/third_party/rust/paste/LICENSE-APACHE
new file mode 100644
index 0000000000..16fe87b06e
--- /dev/null
+++ b/third_party/rust/paste/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/third_party/rust/paste/LICENSE-MIT b/third_party/rust/paste/LICENSE-MIT
new file mode 100644
index 0000000000..31aa79387f
--- /dev/null
+++ b/third_party/rust/paste/LICENSE-MIT
@@ -0,0 +1,23 @@
+Permission is hereby granted, free of charge, to any
+person obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the
+Software without restriction, including without
+limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software
+is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/third_party/rust/paste/README.md b/third_party/rust/paste/README.md
new file mode 100644
index 0000000000..c32d4245d0
--- /dev/null
+++ b/third_party/rust/paste/README.md
@@ -0,0 +1,157 @@
+Macros for all your token pasting needs
+=======================================
+
+[<img alt="github" src="https://img.shields.io/badge/github-dtolnay/paste-8da0cb?style=for-the-badge&labelColor=555555&logo=github" height="20">](https://github.com/dtolnay/paste)
+[<img alt="crates.io" src="https://img.shields.io/crates/v/paste.svg?style=for-the-badge&color=fc8d62&logo=rust" height="20">](https://crates.io/crates/paste)
+[<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-paste-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs" height="20">](https://docs.rs/paste)
+[<img alt="build status" src="https://img.shields.io/github/actions/workflow/status/dtolnay/paste/ci.yml?branch=master&style=for-the-badge" height="20">](https://github.com/dtolnay/paste/actions?query=branch%3Amaster)
+
+The nightly-only [`concat_idents!`] macro in the Rust standard library is
+notoriously underpowered in that its concatenated identifiers can only refer to
+existing items, they can never be used to define something new.
+
+[`concat_idents!`]: https://doc.rust-lang.org/std/macro.concat_idents.html
+
+This crate provides a flexible way to paste together identifiers in a macro,
+including using pasted identifiers to define new items.
+
+```toml
+[dependencies]
+paste = "1.0"
+```
+
+This approach works with any Rust compiler 1.31+.
+
+<br>
+
+## Pasting identifiers
+
+Within the `paste!` macro, identifiers inside `[<`...`>]` are pasted together to
+form a single identifier.
+
+```rust
+use paste::paste;
+
+paste! {
+ // Defines a const called `QRST`.
+ const [<Q R S T>]: &str = "success!";
+}
+
+fn main() {
+ assert_eq!(
+ paste! { [<Q R S T>].len() },
+ 8,
+ );
+}
+```
+
+<br>
+
+## More elaborate example
+
+The next example shows a macro that generates accessor methods for some struct
+fields. It demonstrates how you might find it useful to bundle a paste
+invocation inside of a macro\_rules macro.
+
+```rust
+use paste::paste;
+
+macro_rules! make_a_struct_and_getters {
+ ($name:ident { $($field:ident),* }) => {
+ // Define a struct. This expands to:
+ //
+ // pub struct S {
+ // a: String,
+ // b: String,
+ // c: String,
+ // }
+ pub struct $name {
+ $(
+ $field: String,
+ )*
+ }
+
+ // Build an impl block with getters. This expands to:
+ //
+ // impl S {
+ // pub fn get_a(&self) -> &str { &self.a }
+ // pub fn get_b(&self) -> &str { &self.b }
+ // pub fn get_c(&self) -> &str { &self.c }
+ // }
+ paste! {
+ impl $name {
+ $(
+ pub fn [<get_ $field>](&self) -> &str {
+ &self.$field
+ }
+ )*
+ }
+ }
+ }
+}
+
+make_a_struct_and_getters!(S { a, b, c });
+
+fn call_some_getters(s: &S) -> bool {
+ s.get_a() == s.get_b() && s.get_c().is_empty()
+}
+```
+
+<br>
+
+## Case conversion
+
+Use `$var:lower` or `$var:upper` in the segment list to convert an interpolated
+segment to lower- or uppercase as part of the paste. For example, `[<ld_
+$reg:lower _expr>]` would paste to `ld_bc_expr` if invoked with $reg=`Bc`.
+
+Use `$var:snake` to convert CamelCase input to snake\_case.
+Use `$var:camel` to convert snake\_case to CamelCase.
+These compose, so for example `$var:snake:upper` would give you SCREAMING\_CASE.
+
+The precise Unicode conversions are as defined by [`str::to_lowercase`] and
+[`str::to_uppercase`].
+
+[`str::to_lowercase`]: https://doc.rust-lang.org/std/primitive.str.html#method.to_lowercase
+[`str::to_uppercase`]: https://doc.rust-lang.org/std/primitive.str.html#method.to_uppercase
+
+<br>
+
+## Pasting documentation strings
+
+Within the `paste!` macro, arguments to a #\[doc ...\] attribute are implicitly
+concatenated together to form a coherent documentation string.
+
+```rust
+use paste::paste;
+
+macro_rules! method_new {
+ ($ret:ident) => {
+ paste! {
+ #[doc = "Create a new `" $ret "` object."]
+ pub fn new() -> $ret { todo!() }
+ }
+ };
+}
+
+pub struct Paste {}
+
+method_new!(Paste); // expands to #[doc = "Create a new `Paste` object"]
+```
+
+<br>
+
+#### License
+
+<sup>
+Licensed under either of <a href="LICENSE-APACHE">Apache License, Version
+2.0</a> or <a href="LICENSE-MIT">MIT license</a> at your option.
+</sup>
+
+<br>
+
+<sub>
+Unless you explicitly state otherwise, any contribution intentionally submitted
+for inclusion in this crate by you, as defined in the Apache-2.0 license, shall
+be dual licensed as above, without any additional terms or conditions.
+</sub>
diff --git a/third_party/rust/paste/build.rs b/third_party/rust/paste/build.rs
new file mode 100644
index 0000000000..349b5fce82
--- /dev/null
+++ b/third_party/rust/paste/build.rs
@@ -0,0 +1,33 @@
+use std::env;
+use std::process::Command;
+use std::str;
+
+fn main() {
+ println!("cargo:rerun-if-changed=build.rs");
+
+ let version = match rustc_version() {
+ Some(version) => version,
+ None => return,
+ };
+
+ if version.minor < 54 {
+ // https://github.com/rust-lang/rust/pull/84717
+ println!("cargo:rustc-cfg=no_literal_fromstr");
+ }
+}
+
+struct RustcVersion {
+ minor: u32,
+}
+
+fn rustc_version() -> Option<RustcVersion> {
+ let rustc = env::var_os("RUSTC")?;
+ let output = Command::new(rustc).arg("--version").output().ok()?;
+ let version = str::from_utf8(&output.stdout).ok()?;
+ let mut pieces = version.split('.');
+ if pieces.next() != Some("rustc 1") {
+ return None;
+ }
+ let minor = pieces.next()?.parse().ok()?;
+ Some(RustcVersion { minor })
+}
diff --git a/third_party/rust/paste/src/attr.rs b/third_party/rust/paste/src/attr.rs
new file mode 100644
index 0000000000..d66b843b27
--- /dev/null
+++ b/third_party/rust/paste/src/attr.rs
@@ -0,0 +1,164 @@
+use crate::error::Result;
+use crate::segment::{self, Segment};
+use proc_macro::{Delimiter, Group, Spacing, Span, TokenStream, TokenTree};
+use std::iter;
+use std::mem;
+use std::str::FromStr;
+
+pub fn expand_attr(
+ attr: TokenStream,
+ span: Span,
+ contains_paste: &mut bool,
+) -> Result<TokenStream> {
+ let mut tokens = attr.clone().into_iter();
+ let mut leading_colons = 0; // $(::)?
+ let mut leading_path = 0; // $($ident)::+
+
+ let mut token;
+ let group = loop {
+ token = tokens.next();
+ match token {
+ // colon after `$(:)?`
+ Some(TokenTree::Punct(ref punct))
+ if punct.as_char() == ':' && leading_colons < 2 && leading_path == 0 =>
+ {
+ leading_colons += 1;
+ }
+ // ident after `$(::)? $($ident ::)*`
+ Some(TokenTree::Ident(_)) if leading_colons != 1 && leading_path % 3 == 0 => {
+ leading_path += 1;
+ }
+ // colon after `$(::)? $($ident ::)* $ident $(:)?`
+ Some(TokenTree::Punct(ref punct)) if punct.as_char() == ':' && leading_path % 3 > 0 => {
+ leading_path += 1;
+ }
+ // eq+value after `$(::)? $($ident)::+`
+ Some(TokenTree::Punct(ref punct))
+ if punct.as_char() == '=' && leading_path % 3 == 1 =>
+ {
+ let mut count = 0;
+ if tokens.inspect(|_| count += 1).all(|tt| is_stringlike(&tt)) && count > 1 {
+ *contains_paste = true;
+ let leading = leading_colons + leading_path;
+ return do_paste_name_value_attr(attr, span, leading);
+ }
+ return Ok(attr);
+ }
+ // parens after `$(::)? $($ident)::+`
+ Some(TokenTree::Group(ref group))
+ if group.delimiter() == Delimiter::Parenthesis && leading_path % 3 == 1 =>
+ {
+ break group;
+ }
+ // bail out
+ _ => return Ok(attr),
+ }
+ };
+
+ // There can't be anything else after the first group in a valid attribute.
+ if tokens.next().is_some() {
+ return Ok(attr);
+ }
+
+ let mut group_contains_paste = false;
+ let mut expanded = TokenStream::new();
+ let mut nested_attr = TokenStream::new();
+ for tt in group.stream() {
+ match &tt {
+ TokenTree::Punct(punct) if punct.as_char() == ',' => {
+ expanded.extend(expand_attr(
+ nested_attr,
+ group.span(),
+ &mut group_contains_paste,
+ )?);
+ expanded.extend(iter::once(tt));
+ nested_attr = TokenStream::new();
+ }
+ _ => nested_attr.extend(iter::once(tt)),
+ }
+ }
+
+ if !nested_attr.is_empty() {
+ expanded.extend(expand_attr(
+ nested_attr,
+ group.span(),
+ &mut group_contains_paste,
+ )?);
+ }
+
+ if group_contains_paste {
+ *contains_paste = true;
+ let mut group = Group::new(Delimiter::Parenthesis, expanded);
+ group.set_span(span);
+ Ok(attr
+ .into_iter()
+ // Just keep the initial ident in `#[ident(...)]`.
+ .take(leading_colons + leading_path)
+ .chain(iter::once(TokenTree::Group(group)))
+ .collect())
+ } else {
+ Ok(attr)
+ }
+}
+
+fn do_paste_name_value_attr(attr: TokenStream, span: Span, leading: usize) -> Result<TokenStream> {
+ let mut expanded = TokenStream::new();
+ let mut tokens = attr.into_iter().peekable();
+ expanded.extend(tokens.by_ref().take(leading + 1)); // `doc =`
+
+ let mut segments = segment::parse(&mut tokens)?;
+
+ for segment in &mut segments {
+ if let Segment::String(string) = segment {
+ if let Some(open_quote) = string.value.find('"') {
+ if open_quote == 0 {
+ string.value.truncate(string.value.len() - 1);
+ string.value.remove(0);
+ } else {
+ let begin = open_quote + 1;
+ let end = string.value.rfind('"').unwrap();
+ let raw_string = mem::replace(&mut string.value, String::new());
+ for ch in raw_string[begin..end].chars() {
+ string.value.extend(ch.escape_default());
+ }
+ }
+ }
+ }
+ }
+
+ let mut lit = segment::paste(&segments)?;
+ lit.insert(0, '"');
+ lit.push('"');
+
+ let mut lit = TokenStream::from_str(&lit)
+ .unwrap()
+ .into_iter()
+ .next()
+ .unwrap();
+ lit.set_span(span);
+ expanded.extend(iter::once(lit));
+ Ok(expanded)
+}
+
+fn is_stringlike(token: &TokenTree) -> bool {
+ match token {
+ TokenTree::Ident(_) => true,
+ TokenTree::Literal(literal) => {
+ let repr = literal.to_string();
+ !repr.starts_with('b') && !repr.starts_with('\'')
+ }
+ TokenTree::Group(group) => {
+ if group.delimiter() != Delimiter::None {
+ return false;
+ }
+ let mut inner = group.stream().into_iter();
+ match inner.next() {
+ Some(first) => inner.next().is_none() && is_stringlike(&first),
+ None => false,
+ }
+ }
+ TokenTree::Punct(punct) => {
+ punct.as_char() == '\'' || punct.as_char() == ':' && punct.spacing() == Spacing::Alone
+ }
+ }
+}
diff --git a/third_party/rust/paste/src/error.rs b/third_party/rust/paste/src/error.rs
new file mode 100644
index 0000000000..7c5badb257
--- /dev/null
+++ b/third_party/rust/paste/src/error.rs
@@ -0,0 +1,47 @@
+use proc_macro::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree};
+use std::iter::FromIterator;
+
+pub type Result<T> = std::result::Result<T, Error>;
+
+pub struct Error {
+ begin: Span,
+ end: Span,
+ msg: String,
+}
+
+impl Error {
+ pub fn new(span: Span, msg: &str) -> Self {
+ Self::new2(span, span, msg)
+ }
+
+ pub fn new2(begin: Span, end: Span, msg: &str) -> Self {
+ Error {
+ begin,
+ end,
+ msg: msg.to_owned(),
+ }
+ }
+
+ pub fn to_compile_error(&self) -> TokenStream {
+ // compile_error! { $msg }
+ TokenStream::from_iter(vec![
+ TokenTree::Ident(Ident::new("compile_error", self.begin)),
+ TokenTree::Punct({
+ let mut punct = Punct::new('!', Spacing::Alone);
+ punct.set_span(self.begin);
+ punct
+ }),
+ TokenTree::Group({
+ let mut group = Group::new(Delimiter::Brace, {
+ TokenStream::from_iter(vec![TokenTree::Literal({
+ let mut string = Literal::string(&self.msg);
+ string.set_span(self.end);
+ string
+ })])
+ });
+ group.set_span(self.end);
+ group
+ }),
+ ])
+ }
+}
diff --git a/third_party/rust/paste/src/lib.rs b/third_party/rust/paste/src/lib.rs
new file mode 100644
index 0000000000..80c1b98f46
--- /dev/null
+++ b/third_party/rust/paste/src/lib.rs
@@ -0,0 +1,453 @@
+//! [![github]](https://github.com/dtolnay/paste)&ensp;[![crates-io]](https://crates.io/crates/paste)&ensp;[![docs-rs]](https://docs.rs/paste)
+//!
+//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
+//! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
+//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs
+//!
+//! <br>
+//!
+//! The nightly-only [`concat_idents!`] macro in the Rust standard library is
+//! notoriously underpowered in that its concatenated identifiers can only refer to
+//! existing items, they can never be used to define something new.
+//!
+//! [`concat_idents!`]: https://doc.rust-lang.org/std/macro.concat_idents.html
+//!
+//! This crate provides a flexible way to paste together identifiers in a macro,
+//! including using pasted identifiers to define new items.
+//!
+//! This approach works with any Rust compiler 1.31+.
+//!
+//! <br>
+//!
+//! # Pasting identifiers
+//!
+//! Within the `paste!` macro, identifiers inside `[<`...`>]` are pasted
+//! together to form a single identifier.
+//!
+//! ```
+//! use paste::paste;
+//!
+//! paste! {
+//! // Defines a const called `QRST`.
+//! const [<Q R S T>]: &str = "success!";
+//! }
+//!
+//! fn main() {
+//! assert_eq!(
+//! paste! { [<Q R S T>].len() },
+//! 8,
+//! );
+//! }
+//! ```
+//!
+//! <br><br>
+//!
+//! # More elaborate example
+//!
+//! The next example shows a macro that generates accessor methods for some
+//! struct fields. It demonstrates how you might find it useful to bundle a
+//! paste invocation inside of a macro\_rules macro.
+//!
+//! ```
+//! use paste::paste;
+//!
+//! macro_rules! make_a_struct_and_getters {
+//! ($name:ident { $($field:ident),* }) => {
+//! // Define a struct. This expands to:
+//! //
+//! // pub struct S {
+//! // a: String,
+//! // b: String,
+//! // c: String,
+//! // }
+//! pub struct $name {
+//! $(
+//! $field: String,
+//! )*
+//! }
+//!
+//! // Build an impl block with getters. This expands to:
+//! //
+//! // impl S {
+//! // pub fn get_a(&self) -> &str { &self.a }
+//! // pub fn get_b(&self) -> &str { &self.b }
+//! // pub fn get_c(&self) -> &str { &self.c }
+//! // }
+//! paste! {
+//! impl $name {
+//! $(
+//! pub fn [<get_ $field>](&self) -> &str {
+//! &self.$field
+//! }
+//! )*
+//! }
+//! }
+//! }
+//! }
+//!
+//! make_a_struct_and_getters!(S { a, b, c });
+//!
+//! fn call_some_getters(s: &S) -> bool {
+//! s.get_a() == s.get_b() && s.get_c().is_empty()
+//! }
+//! #
+//! # fn main() {}
+//! ```
+//!
+//! <br><br>
+//!
+//! # Case conversion
+//!
+//! Use `$var:lower` or `$var:upper` in the segment list to convert an
+//! interpolated segment to lower- or uppercase as part of the paste. For
+//! example, `[<ld_ $reg:lower _expr>]` would paste to `ld_bc_expr` if invoked
+//! with $reg=`Bc`.
+//!
+//! Use `$var:snake` to convert CamelCase input to snake\_case.
+//! Use `$var:camel` to convert snake\_case to CamelCase.
+//! These compose, so for example `$var:snake:upper` would give you SCREAMING\_CASE.
+//!
+//! The precise Unicode conversions are as defined by [`str::to_lowercase`] and
+//! [`str::to_uppercase`].
+//!
+//! [`str::to_lowercase`]: https://doc.rust-lang.org/std/primitive.str.html#method.to_lowercase
+//! [`str::to_uppercase`]: https://doc.rust-lang.org/std/primitive.str.html#method.to_uppercase
+//!
+//! <br>
+//!
+//! # Pasting documentation strings
+//!
+//! Within the `paste!` macro, arguments to a #\[doc ...\] attribute are
+//! implicitly concatenated together to form a coherent documentation string.
+//!
+//! ```
+//! use paste::paste;
+//!
+//! macro_rules! method_new {
+//! ($ret:ident) => {
+//! paste! {
+//! #[doc = "Create a new `" $ret "` object."]
+//! pub fn new() -> $ret { todo!() }
+//! }
+//! };
+//! }
+//!
+//! pub struct Paste {}
+//!
+//! method_new!(Paste); // expands to #[doc = "Create a new `Paste` object"]
+//! ```
+
+#![allow(
+ clippy::derive_partial_eq_without_eq,
+ clippy::doc_markdown,
+ clippy::match_same_arms,
+ clippy::module_name_repetitions,
+ clippy::needless_doctest_main,
+ clippy::too_many_lines
+)]
+
+extern crate proc_macro;
+
+mod attr;
+mod error;
+mod segment;
+
+use crate::attr::expand_attr;
+use crate::error::{Error, Result};
+use crate::segment::Segment;
+use proc_macro::{Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree};
+use std::char;
+use std::iter;
+use std::panic;
+
+#[proc_macro]
+pub fn paste(input: TokenStream) -> TokenStream {
+ let mut contains_paste = false;
+ let flatten_single_interpolation = true;
+ match expand(
+ input.clone(),
+ &mut contains_paste,
+ flatten_single_interpolation,
+ ) {
+ Ok(expanded) => {
+ if contains_paste {
+ expanded
+ } else {
+ input
+ }
+ }
+ Err(err) => err.to_compile_error(),
+ }
+}
+
+#[doc(hidden)]
+#[proc_macro]
+pub fn item(input: TokenStream) -> TokenStream {
+ paste(input)
+}
+
+#[doc(hidden)]
+#[proc_macro]
+pub fn expr(input: TokenStream) -> TokenStream {
+ paste(input)
+}
+
+fn expand(
+ input: TokenStream,
+ contains_paste: &mut bool,
+ flatten_single_interpolation: bool,
+) -> Result<TokenStream> {
+ let mut expanded = TokenStream::new();
+ let mut lookbehind = Lookbehind::Other;
+ let mut prev_none_group = None::<Group>;
+ let mut tokens = input.into_iter().peekable();
+ loop {
+ let token = tokens.next();
+ if let Some(group) = prev_none_group.take() {
+ if match (&token, tokens.peek()) {
+ (Some(TokenTree::Punct(fst)), Some(TokenTree::Punct(snd))) => {
+ fst.as_char() == ':' && snd.as_char() == ':' && fst.spacing() == Spacing::Joint
+ }
+ _ => false,
+ } {
+ expanded.extend(group.stream());
+ *contains_paste = true;
+ } else {
+ expanded.extend(iter::once(TokenTree::Group(group)));
+ }
+ }
+ match token {
+ Some(TokenTree::Group(group)) => {
+ let delimiter = group.delimiter();
+ let content = group.stream();
+ let span = group.span();
+ if delimiter == Delimiter::Bracket && is_paste_operation(&content) {
+ let segments = parse_bracket_as_segments(content, span)?;
+ let pasted = segment::paste(&segments)?;
+ let tokens = pasted_to_tokens(pasted, span)?;
+ expanded.extend(tokens);
+ *contains_paste = true;
+ } else if flatten_single_interpolation
+ && delimiter == Delimiter::None
+ && is_single_interpolation_group(&content)
+ {
+ expanded.extend(content);
+ *contains_paste = true;
+ } else {
+ let mut group_contains_paste = false;
+ let is_attribute = delimiter == Delimiter::Bracket
+ && (lookbehind == Lookbehind::Pound || lookbehind == Lookbehind::PoundBang);
+ let mut nested = expand(
+ content,
+ &mut group_contains_paste,
+ flatten_single_interpolation && !is_attribute,
+ )?;
+ if is_attribute {
+ nested = expand_attr(nested, span, &mut group_contains_paste)?;
+ }
+ let group = if group_contains_paste {
+ let mut group = Group::new(delimiter, nested);
+ group.set_span(span);
+ *contains_paste = true;
+ group
+ } else {
+ group.clone()
+ };
+ if delimiter != Delimiter::None {
+ expanded.extend(iter::once(TokenTree::Group(group)));
+ } else if lookbehind == Lookbehind::DoubleColon {
+ expanded.extend(group.stream());
+ *contains_paste = true;
+ } else {
+ prev_none_group = Some(group);
+ }
+ }
+ lookbehind = Lookbehind::Other;
+ }
+ Some(TokenTree::Punct(punct)) => {
+ lookbehind = match punct.as_char() {
+ ':' if lookbehind == Lookbehind::JointColon => Lookbehind::DoubleColon,
+ ':' if punct.spacing() == Spacing::Joint => Lookbehind::JointColon,
+ '#' => Lookbehind::Pound,
+ '!' if lookbehind == Lookbehind::Pound => Lookbehind::PoundBang,
+ _ => Lookbehind::Other,
+ };
+ expanded.extend(iter::once(TokenTree::Punct(punct)));
+ }
+ Some(other) => {
+ lookbehind = Lookbehind::Other;
+ expanded.extend(iter::once(other));
+ }
+ None => return Ok(expanded),
+ }
+ }
+}
+
+#[derive(PartialEq)]
+enum Lookbehind {
+ JointColon,
+ DoubleColon,
+ Pound,
+ PoundBang,
+ Other,
+}
+
+// https://github.com/dtolnay/paste/issues/26
+fn is_single_interpolation_group(input: &TokenStream) -> bool {
+ #[derive(PartialEq)]
+ enum State {
+ Init,
+ Ident,
+ Literal,
+ Apostrophe,
+ Lifetime,
+ Colon1,
+ Colon2,
+ }
+
+ let mut state = State::Init;
+ for tt in input.clone() {
+ state = match (state, &tt) {
+ (State::Init, TokenTree::Ident(_)) => State::Ident,
+ (State::Init, TokenTree::Literal(_)) => State::Literal,
+ (State::Init, TokenTree::Punct(punct)) if punct.as_char() == '\'' => State::Apostrophe,
+ (State::Apostrophe, TokenTree::Ident(_)) => State::Lifetime,
+ (State::Ident, TokenTree::Punct(punct))
+ if punct.as_char() == ':' && punct.spacing() == Spacing::Joint =>
+ {
+ State::Colon1
+ }
+ (State::Colon1, TokenTree::Punct(punct))
+ if punct.as_char() == ':' && punct.spacing() == Spacing::Alone =>
+ {
+ State::Colon2
+ }
+ (State::Colon2, TokenTree::Ident(_)) => State::Ident,
+ _ => return false,
+ };
+ }
+
+ state == State::Ident || state == State::Literal || state == State::Lifetime
+}
+
+fn is_paste_operation(input: &TokenStream) -> bool {
+ let mut tokens = input.clone().into_iter();
+
+ match &tokens.next() {
+ Some(TokenTree::Punct(punct)) if punct.as_char() == '<' => {}
+ _ => return false,
+ }
+
+ let mut has_token = false;
+ loop {
+ match &tokens.next() {
+ Some(TokenTree::Punct(punct)) if punct.as_char() == '>' => {
+ return has_token && tokens.next().is_none();
+ }
+ Some(_) => has_token = true,
+ None => return false,
+ }
+ }
+}
+
+fn parse_bracket_as_segments(input: TokenStream, scope: Span) -> Result<Vec<Segment>> {
+ let mut tokens = input.into_iter().peekable();
+
+ match &tokens.next() {
+ Some(TokenTree::Punct(punct)) if punct.as_char() == '<' => {}
+ Some(wrong) => return Err(Error::new(wrong.span(), "expected `<`")),
+ None => return Err(Error::new(scope, "expected `[< ... >]`")),
+ }
+
+ let mut segments = segment::parse(&mut tokens)?;
+
+ match &tokens.next() {
+ Some(TokenTree::Punct(punct)) if punct.as_char() == '>' => {}
+ Some(wrong) => return Err(Error::new(wrong.span(), "expected `>`")),
+ None => return Err(Error::new(scope, "expected `[< ... >]`")),
+ }
+
+ if let Some(unexpected) = tokens.next() {
+ return Err(Error::new(
+ unexpected.span(),
+ "unexpected input, expected `[< ... >]`",
+ ));
+ }
+
+ for segment in &mut segments {
+ if let Segment::String(string) = segment {
+ if string.value.starts_with("'\\u{") {
+ let hex = &string.value[4..string.value.len() - 2];
+ if let Ok(unsigned) = u32::from_str_radix(hex, 16) {
+ if let Some(ch) = char::from_u32(unsigned) {
+ string.value.clear();
+ string.value.push(ch);
+ continue;
+ }
+ }
+ }
+ if string.value.contains(&['#', '\\', '.', '+'][..])
+ || string.value.starts_with("b'")
+ || string.value.starts_with("b\"")
+ || string.value.starts_with("br\"")
+ {
+ return Err(Error::new(string.span, "unsupported literal"));
+ }
+ let mut range = 0..string.value.len();
+ if string.value.starts_with("r\"") {
+ range.start += 2;
+ range.end -= 1;
+ } else if string.value.starts_with(&['"', '\''][..]) {
+ range.start += 1;
+ range.end -= 1;
+ }
+ string.value = string.value[range].replace('-', "_");
+ }
+ }
+
+ Ok(segments)
+}
+
+fn pasted_to_tokens(mut pasted: String, span: Span) -> Result<TokenStream> {
+ let mut tokens = TokenStream::new();
+
+ #[cfg(not(no_literal_fromstr))]
+ {
+ use proc_macro::{LexError, Literal};
+ use std::str::FromStr;
+
+ if pasted.starts_with(|ch: char| ch.is_ascii_digit()) {
+ let literal = match panic::catch_unwind(|| Literal::from_str(&pasted)) {
+ Ok(Ok(literal)) => TokenTree::Literal(literal),
+ Ok(Err(LexError { .. })) | Err(_) => {
+ return Err(Error::new(
+ span,
+ &format!("`{:?}` is not a valid literal", pasted),
+ ));
+ }
+ };
+ tokens.extend(iter::once(literal));
+ return Ok(tokens);
+ }
+ }
+
+ if pasted.starts_with('\'') {
+ let mut apostrophe = TokenTree::Punct(Punct::new('\'', Spacing::Joint));
+ apostrophe.set_span(span);
+ tokens.extend(iter::once(apostrophe));
+ pasted.remove(0);
+ }
+
+ let ident = match panic::catch_unwind(|| Ident::new(&pasted, span)) {
+ Ok(ident) => TokenTree::Ident(ident),
+ Err(_) => {
+ return Err(Error::new(
+ span,
+ &format!("`{:?}` is not a valid identifier", pasted),
+ ));
+ }
+ };
+
+ tokens.extend(iter::once(ident));
+ Ok(tokens)
+}
diff --git a/third_party/rust/paste/src/segment.rs b/third_party/rust/paste/src/segment.rs
new file mode 100644
index 0000000000..592a047021
--- /dev/null
+++ b/third_party/rust/paste/src/segment.rs
@@ -0,0 +1,233 @@
+use crate::error::{Error, Result};
+use proc_macro::{token_stream, Delimiter, Ident, Span, TokenTree};
+use std::iter::Peekable;
+
+pub(crate) enum Segment {
+ String(LitStr),
+ Apostrophe(Span),
+ Env(LitStr),
+ Modifier(Colon, Ident),
+}
+
+pub(crate) struct LitStr {
+ pub value: String,
+ pub span: Span,
+}
+
+pub(crate) struct Colon {
+ pub span: Span,
+}
+
+pub(crate) fn parse(tokens: &mut Peekable<token_stream::IntoIter>) -> Result<Vec<Segment>> {
+ let mut segments = Vec::new();
+ while match tokens.peek() {
+ None => false,
+ Some(TokenTree::Punct(punct)) => punct.as_char() != '>',
+ Some(_) => true,
+ } {
+ match tokens.next().unwrap() {
+ TokenTree::Ident(ident) => {
+ let mut fragment = ident.to_string();
+ if fragment.starts_with("r#") {
+ fragment = fragment.split_off(2);
+ }
+ if fragment == "env"
+ && match tokens.peek() {
+ Some(TokenTree::Punct(punct)) => punct.as_char() == '!',
+ _ => false,
+ }
+ {
+ let bang = tokens.next().unwrap(); // `!`
+ let expect_group = tokens.next();
+ let parenthesized = match &expect_group {
+ Some(TokenTree::Group(group))
+ if group.delimiter() == Delimiter::Parenthesis =>
+ {
+ group
+ }
+ Some(wrong) => return Err(Error::new(wrong.span(), "expected `(`")),
+ None => {
+ return Err(Error::new2(
+ ident.span(),
+ bang.span(),
+ "expected `(` after `env!`",
+ ));
+ }
+ };
+ let mut inner = parenthesized.stream().into_iter();
+ let lit = match inner.next() {
+ Some(TokenTree::Literal(lit)) => lit,
+ Some(wrong) => {
+ return Err(Error::new(wrong.span(), "expected string literal"))
+ }
+ None => {
+ return Err(Error::new2(
+ ident.span(),
+ parenthesized.span(),
+ "expected string literal as argument to env! macro",
+ ))
+ }
+ };
+ let lit_string = lit.to_string();
+ if lit_string.starts_with('"')
+ && lit_string.ends_with('"')
+ && lit_string.len() >= 2
+ {
+ // TODO: maybe handle escape sequences in the string if
+ // someone has a use case.
+ segments.push(Segment::Env(LitStr {
+ value: lit_string[1..lit_string.len() - 1].to_owned(),
+ span: lit.span(),
+ }));
+ } else {
+ return Err(Error::new(lit.span(), "expected string literal"));
+ }
+ if let Some(unexpected) = inner.next() {
+ return Err(Error::new(
+ unexpected.span(),
+ "unexpected token in env! macro",
+ ));
+ }
+ } else {
+ segments.push(Segment::String(LitStr {
+ value: fragment,
+ span: ident.span(),
+ }));
+ }
+ }
+ TokenTree::Literal(lit) => {
+ segments.push(Segment::String(LitStr {
+ value: lit.to_string(),
+ span: lit.span(),
+ }));
+ }
+ TokenTree::Punct(punct) => match punct.as_char() {
+ '_' => segments.push(Segment::String(LitStr {
+ value: "_".to_owned(),
+ span: punct.span(),
+ })),
+ '\'' => segments.push(Segment::Apostrophe(punct.span())),
+ ':' => {
+ let colon_span = punct.span();
+ let colon = Colon { span: colon_span };
+ let ident = match tokens.next() {
+ Some(TokenTree::Ident(ident)) => ident,
+ wrong => {
+ let span = wrong.as_ref().map_or(colon_span, TokenTree::span);
+ return Err(Error::new(span, "expected identifier after `:`"));
+ }
+ };
+ segments.push(Segment::Modifier(colon, ident));
+ }
+ _ => return Err(Error::new(punct.span(), "unexpected punct")),
+ },
+ TokenTree::Group(group) => {
+ if group.delimiter() == Delimiter::None {
+ let mut inner = group.stream().into_iter().peekable();
+ let nested = parse(&mut inner)?;
+ if let Some(unexpected) = inner.next() {
+ return Err(Error::new(unexpected.span(), "unexpected token"));
+ }
+ segments.extend(nested);
+ } else {
+ return Err(Error::new(group.span(), "unexpected token"));
+ }
+ }
+ }
+ }
+ Ok(segments)
+}
+
+pub(crate) fn paste(segments: &[Segment]) -> Result<String> {
+ let mut evaluated = Vec::new();
+ let mut is_lifetime = false;
+
+ for segment in segments {
+ match segment {
+ Segment::String(segment) => {
+ evaluated.push(segment.value.clone());
+ }
+ Segment::Apostrophe(span) => {
+ if is_lifetime {
+ return Err(Error::new(*span, "unexpected lifetime"));
+ }
+ is_lifetime = true;
+ }
+ Segment::Env(var) => {
+ let resolved = match std::env::var(&var.value) {
+ Ok(resolved) => resolved,
+ Err(_) => {
+ return Err(Error::new(
+ var.span,
+ &format!("no such env var: {:?}", var.value),
+ ));
+ }
+ };
+ let resolved = resolved.replace('-', "_");
+ evaluated.push(resolved);
+ }
+ Segment::Modifier(colon, ident) => {
+ let last = match evaluated.pop() {
+ Some(last) => last,
+ None => {
+ return Err(Error::new2(colon.span, ident.span(), "unexpected modifier"))
+ }
+ };
+ match ident.to_string().as_str() {
+ "lower" => {
+ evaluated.push(last.to_lowercase());
+ }
+ "upper" => {
+ evaluated.push(last.to_uppercase());
+ }
+ "snake" => {
+ let mut acc = String::new();
+ let mut prev = '_';
+ for ch in last.chars() {
+ if ch.is_uppercase() && prev != '_' {
+ acc.push('_');
+ }
+ acc.push(ch);
+ prev = ch;
+ }
+ evaluated.push(acc.to_lowercase());
+ }
+ "camel" => {
+ let mut acc = String::new();
+ let mut prev = '_';
+ for ch in last.chars() {
+ if ch != '_' {
+ if prev == '_' {
+ for chu in ch.to_uppercase() {
+ acc.push(chu);
+ }
+ } else if prev.is_uppercase() {
+ for chl in ch.to_lowercase() {
+ acc.push(chl);
+ }
+ } else {
+ acc.push(ch);
+ }
+ }
+ prev = ch;
+ }
+ evaluated.push(acc);
+ }
+ _ => {
+ return Err(Error::new2(
+ colon.span,
+ ident.span(),
+ "unsupported modifier",
+ ));
+ }
+ }
+ }
+ }
+ }
+
+ let mut pasted = evaluated.into_iter().collect::<String>();
+ if is_lifetime {
+ pasted.insert(0, '\'');
+ }
+ Ok(pasted)
+}
diff --git a/third_party/rust/paste/tests/compiletest.rs b/third_party/rust/paste/tests/compiletest.rs
new file mode 100644
index 0000000000..7974a6249e
--- /dev/null
+++ b/third_party/rust/paste/tests/compiletest.rs
@@ -0,0 +1,7 @@
+#[rustversion::attr(not(nightly), ignore)]
+#[cfg_attr(miri, ignore)]
+#[test]
+fn ui() {
+ let t = trybuild::TestCases::new();
+ t.compile_fail("tests/ui/*.rs");
+}
diff --git a/third_party/rust/paste/tests/test_attr.rs b/third_party/rust/paste/tests/test_attr.rs
new file mode 100644
index 0000000000..c880eac328
--- /dev/null
+++ b/third_party/rust/paste/tests/test_attr.rs
@@ -0,0 +1,60 @@
+use paste::paste;
+use paste_test_suite::paste_test;
+
+#[test]
+fn test_attr() {
+ paste! {
+ #[paste_test(k = "val" "ue")]
+ struct A;
+
+ #[paste_test_suite::paste_test(k = "val" "ue")]
+ struct B;
+
+ #[::paste_test_suite::paste_test(k = "val" "ue")]
+ struct C;
+
+ #[paste_test(k = "va" [<l u>] e)]
+ struct D;
+ }
+
+ let _ = A;
+ let _ = B;
+ let _ = C;
+ let _ = D;
+}
+
+#[test]
+fn test_paste_cfg() {
+ macro_rules! m {
+ ($ret:ident, $width:expr) => {
+ paste! {
+ #[cfg(any(feature = "protocol_feature_" $ret:snake, target_pointer_width = "" $width))]
+ fn new() -> $ret { todo!() }
+ }
+ };
+ }
+
+ struct Paste;
+
+ #[cfg(target_pointer_width = "64")]
+ m!(Paste, 64);
+ #[cfg(target_pointer_width = "32")]
+ m!(Paste, 32);
+
+ let _ = new;
+}
+
+#[test]
+fn test_path_in_attr() {
+ macro_rules! m {
+ (#[x = $x:ty]) => {
+ stringify!($x)
+ };
+ }
+
+ let ty = paste! {
+ m!(#[x = foo::Bar])
+ };
+
+ assert_eq!("foo::Bar", ty);
+}
diff --git a/third_party/rust/paste/tests/test_doc.rs b/third_party/rust/paste/tests/test_doc.rs
new file mode 100644
index 0000000000..2e2b14dc8a
--- /dev/null
+++ b/third_party/rust/paste/tests/test_doc.rs
@@ -0,0 +1,77 @@
+use paste::paste;
+
+#[test]
+fn test_paste_doc() {
+ macro_rules! m {
+ ($ret:ident) => {
+ paste! {
+ #[doc = "Create a new [`" $ret "`] object."]
+ fn new() -> $ret { todo!() }
+ }
+ };
+ }
+
+ struct Paste;
+ m!(Paste);
+
+ let _ = new;
+}
+
+macro_rules! get_doc {
+ (#[doc = $literal:tt]) => {
+ $literal
+ };
+}
+
+#[test]
+fn test_escaping() {
+ let doc = paste! {
+ get_doc!(#[doc = "s\"" r#"r#""#])
+ };
+
+ let expected = "s\"r#\"";
+ assert_eq!(doc, expected);
+}
+
+#[test]
+fn test_literals() {
+ let doc = paste! {
+ get_doc!(#[doc = "int=" 0x1 " bool=" true " float=" 0.01])
+ };
+
+ let expected = "int=0x1 bool=true float=0.01";
+ assert_eq!(doc, expected);
+}
+
+#[test]
+fn test_case() {
+ let doc = paste! {
+ get_doc!(#[doc = "HTTP " get:upper "!"])
+ };
+
+ let expected = "HTTP GET!";
+ assert_eq!(doc, expected);
+}
+
+// https://github.com/dtolnay/paste/issues/63
+#[test]
+fn test_stringify() {
+ macro_rules! create {
+ ($doc:expr) => {
+ paste! {
+ #[doc = $doc]
+ pub struct Struct;
+ }
+ };
+ }
+
+ macro_rules! forward {
+ ($name:ident) => {
+ create!(stringify!($name));
+ };
+ }
+
+ forward!(documentation);
+
+ let _ = Struct;
+}
diff --git a/third_party/rust/paste/tests/test_expr.rs b/third_party/rust/paste/tests/test_expr.rs
new file mode 100644
index 0000000000..5ce2549e6e
--- /dev/null
+++ b/third_party/rust/paste/tests/test_expr.rs
@@ -0,0 +1,283 @@
+use paste::paste;
+
+#[test]
+fn test_shared_hygiene() {
+ paste! {
+ let [<a a>] = 1;
+ assert_eq!([<a a>], 1);
+ }
+}
+
+#[test]
+fn test_repeat() {
+ const ROCKET_A: &str = "/a";
+ const ROCKET_B: &str = "/b";
+
+ macro_rules! routes {
+ ($($route:ident),*) => {{
+ paste! {
+ vec![$( [<ROCKET_ $route>] ),*]
+ }
+ }}
+ }
+
+ let routes = routes!(A, B);
+ assert_eq!(routes, vec!["/a", "/b"]);
+}
+
+#[test]
+fn test_literal_to_identifier() {
+ const CONST0: &str = "const0";
+
+ let pasted = paste!([<CONST 0>]);
+ assert_eq!(pasted, CONST0);
+
+ let pasted = paste!([<CONST '0'>]);
+ assert_eq!(pasted, CONST0);
+
+ let pasted = paste!([<CONST "0">]);
+ assert_eq!(pasted, CONST0);
+
+ let pasted = paste!([<CONST r"0">]);
+ assert_eq!(pasted, CONST0);
+
+ let pasted = paste!([<CONST '\u{30}'>]);
+ assert_eq!(pasted, CONST0);
+}
+
+#[test]
+fn test_literal_suffix() {
+ macro_rules! literal {
+ ($bit:tt) => {
+ paste!([<1_u $bit>])
+ };
+ }
+
+ assert_eq!(literal!(32), 1);
+}
+
+#[test]
+fn test_underscore() {
+ paste! {
+ const A_B: usize = 0;
+ assert_eq!([<A _ B>], 0);
+ }
+}
+
+#[test]
+fn test_lifetime() {
+ paste! {
+ #[allow(dead_code)]
+ struct S<[<'d e>]> {
+ q: &[<'d e>] str,
+ }
+ }
+}
+
+#[test]
+fn test_keyword() {
+ paste! {
+ struct [<F move>];
+
+ let _ = Fmove;
+ }
+}
+
+#[test]
+fn test_literal_str() {
+ paste! {
+ #[allow(non_camel_case_types)]
+ struct [<Foo "Bar-Baz">];
+
+ let _ = FooBar_Baz;
+ }
+}
+
+#[test]
+fn test_env_literal() {
+ paste! {
+ struct [<Lib env bar>];
+
+ let _ = Libenvbar;
+ }
+}
+
+#[test]
+fn test_env_present() {
+ paste! {
+ struct [<Lib env!("CARGO_PKG_NAME")>];
+
+ let _ = Libpaste;
+ }
+}
+
+#[test]
+fn test_raw_identifier() {
+ paste! {
+ struct [<F r#move>];
+
+ let _ = Fmove;
+ }
+}
+
+#[test]
+fn test_false_start() {
+ trait Trait {
+ fn f() -> usize;
+ }
+
+ struct S;
+
+ impl Trait for S {
+ fn f() -> usize {
+ 0
+ }
+ }
+
+ paste! {
+ let x = [<S as Trait>::f()];
+ assert_eq!(x[0], 0);
+ }
+}
+
+#[test]
+fn test_local_variable() {
+ let yy = 0;
+
+ paste! {
+ assert_eq!([<y y>], 0);
+ }
+}
+
+#[test]
+fn test_empty() {
+ paste! {
+ assert_eq!(stringify!([<y y>]), "yy");
+ assert_eq!(stringify!([<>]).replace(' ', ""), "[<>]");
+ }
+}
+
+#[test]
+fn test_env_to_lower() {
+ paste! {
+ struct [<Lib env!("CARGO_PKG_NAME"):lower>];
+
+ let _ = Libpaste;
+ }
+}
+
+#[test]
+fn test_env_to_upper() {
+ paste! {
+ const [<LIB env!("CARGO_PKG_NAME"):upper>]: &str = "libpaste";
+
+ let _ = LIBPASTE;
+ }
+}
+
+#[test]
+fn test_env_to_snake() {
+ paste! {
+ const [<LIB env!("CARGO_PKG_NAME"):snake:upper>]: &str = "libpaste";
+
+ let _ = LIBPASTE;
+ }
+}
+
+#[test]
+fn test_env_to_camel() {
+ paste! {
+ #[allow(non_upper_case_globals)]
+ const [<LIB env!("CARGO_PKG_NAME"):camel>]: &str = "libpaste";
+
+ let _ = LIBPaste;
+ }
+}
+
+mod test_x86_feature_literal {
+ // work around https://github.com/rust-lang/rust/issues/72726
+
+ use paste::paste;
+
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ macro_rules! my_is_x86_feature_detected {
+ ($feat:literal) => {
+ paste! {
+ #[test]
+ fn test() {
+ let _ = is_x86_feature_detected!($feat);
+ }
+ }
+ };
+ }
+
+ #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
+ macro_rules! my_is_x86_feature_detected {
+ ($feat:literal) => {
+ #[ignore]
+ #[test]
+ fn test() {}
+ };
+ }
+
+ my_is_x86_feature_detected!("mmx");
+}
+
+#[rustversion::since(1.46)]
+mod test_local_setter {
+ // https://github.com/dtolnay/paste/issues/7
+
+ use paste::paste;
+
+ #[derive(Default)]
+ struct Test {
+ val: i32,
+ }
+
+ impl Test {
+ fn set_val(&mut self, arg: i32) {
+ self.val = arg;
+ }
+ }
+
+ macro_rules! setter {
+ ($obj:expr, $field:ident, $value:expr) => {
+ paste! { $obj.[<set_ $field>]($value); }
+ };
+
+ ($field:ident, $value:expr) => {{
+ let mut new = Test::default();
+ setter!(new, val, $value);
+ new
+ }};
+ }
+
+ #[test]
+ fn test_local_setter() {
+ let a = setter!(val, 42);
+ assert_eq!(a.val, 42);
+ }
+}
+
+// https://github.com/dtolnay/paste/issues/85
+#[test]
+fn test_top_level_none_delimiter() {
+ macro_rules! clone {
+ ($val:expr) => {
+ paste! {
+ $val.clone()
+ }
+ };
+ }
+
+ #[derive(Clone)]
+ struct A;
+
+ impl A {
+ fn consume_self(self) {
+ let _ = self;
+ }
+ }
+
+ clone!(&A).consume_self();
+}
diff --git a/third_party/rust/paste/tests/test_item.rs b/third_party/rust/paste/tests/test_item.rs
new file mode 100644
index 0000000000..3821510a5d
--- /dev/null
+++ b/third_party/rust/paste/tests/test_item.rs
@@ -0,0 +1,269 @@
+mod test_basic {
+ use paste::paste;
+
+ struct Struct;
+
+ paste! {
+ impl Struct {
+ fn [<a b c>]() {}
+ }
+ }
+
+ #[test]
+ fn test() {
+ Struct::abc();
+ }
+}
+
+mod test_in_impl {
+ use paste::paste;
+
+ struct Struct;
+
+ impl Struct {
+ paste! {
+ fn [<a b c>]() {}
+ }
+ }
+
+ #[test]
+ fn test() {
+ Struct::abc();
+ }
+}
+
+mod test_none_delimited_single_ident {
+ use paste::paste;
+
+ macro_rules! m {
+ ($id:ident) => {
+ paste! {
+ fn f() -> &'static str {
+ stringify!($id)
+ }
+ }
+ };
+ }
+
+ m!(i32x4);
+
+ #[test]
+ fn test() {
+ assert_eq!(f(), "i32x4");
+ }
+}
+
+mod test_none_delimited_single_lifetime {
+ use paste::paste;
+
+ macro_rules! m {
+ ($life:lifetime) => {
+ paste! {
+ pub struct S<$life>(&$life ());
+ impl<$life> S<$life> {
+ fn f() {}
+ }
+ }
+ };
+ }
+
+ m!('a);
+
+ #[test]
+ fn test() {
+ S::f();
+ }
+}
+
+mod test_to_lower {
+ use paste::paste;
+
+ macro_rules! m {
+ ($id:ident) => {
+ paste! {
+ fn [<my_ $id:lower _here>](_arg: u8) -> &'static str {
+ stringify!([<$id:lower>])
+ }
+ }
+ };
+ }
+
+ m!(Test);
+
+ #[test]
+ fn test_to_lower() {
+ assert_eq!(my_test_here(0), "test");
+ }
+}
+
+mod test_to_upper {
+ use paste::paste;
+
+ macro_rules! m {
+ ($id:ident) => {
+ paste! {
+ const [<MY_ $id:upper _HERE>]: &str = stringify!([<$id:upper>]);
+ }
+ };
+ }
+
+ m!(Test);
+
+ #[test]
+ fn test_to_upper() {
+ assert_eq!(MY_TEST_HERE, "TEST");
+ }
+}
+
+mod test_to_snake {
+ use paste::paste;
+
+ macro_rules! m {
+ ($id:ident) => {
+ paste! {
+ const DEFAULT_SNAKE: &str = stringify!([<$id:snake>]);
+ const LOWER_SNAKE: &str = stringify!([<$id:snake:lower>]);
+ const UPPER_SNAKE: &str = stringify!([<$id:snake:upper>]);
+ }
+ };
+ }
+
+ m!(ThisIsButATest);
+
+ #[test]
+ fn test_to_snake() {
+ assert_eq!(DEFAULT_SNAKE, "this_is_but_a_test");
+ assert_eq!(LOWER_SNAKE, "this_is_but_a_test");
+ assert_eq!(UPPER_SNAKE, "THIS_IS_BUT_A_TEST");
+ }
+}
+
+mod test_to_camel {
+ use paste::paste;
+
+ macro_rules! m {
+ ($id:ident) => {
+ paste! {
+ const DEFAULT_CAMEL: &str = stringify!([<$id:camel>]);
+ const LOWER_CAMEL: &str = stringify!([<$id:camel:lower>]);
+ const UPPER_CAMEL: &str = stringify!([<$id:camel:upper>]);
+ }
+ };
+ }
+
+ m!(this_is_but_a_test);
+
+ #[test]
+ fn test_to_camel() {
+ assert_eq!(DEFAULT_CAMEL, "ThisIsButATest");
+ assert_eq!(LOWER_CAMEL, "thisisbutatest");
+ assert_eq!(UPPER_CAMEL, "THISISBUTATEST");
+ }
+}
+
+mod test_doc_expr {
+ // https://github.com/dtolnay/paste/issues/29
+
+ use paste::paste;
+
+ macro_rules! doc_expr {
+ ($doc:expr) => {
+ paste! {
+ #[doc = $doc]
+ pub struct S;
+ }
+ };
+ }
+
+ doc_expr!(stringify!());
+
+ #[test]
+ fn test_doc_expr() {
+ let _: S;
+ }
+}
+
+mod test_type_in_path {
+ // https://github.com/dtolnay/paste/issues/31
+
+ use paste::paste;
+
+ mod keys {
+ #[derive(Default)]
+ pub struct Mib<T = ()>(std::marker::PhantomData<T>);
+ }
+
+ macro_rules! types {
+ ($mib:ty) => {
+ paste! {
+ #[derive(Default)]
+ pub struct S(pub keys::$mib);
+ }
+ };
+ }
+
+ macro_rules! write {
+ ($fn:ident, $field:ty) => {
+ paste! {
+ pub fn $fn() -> $field {
+ $field::default()
+ }
+ }
+ };
+ }
+
+ types! {Mib<[usize; 2]>}
+ write! {get_a, keys::Mib}
+ write! {get_b, usize}
+
+ #[test]
+ fn test_type_in_path() {
+ let _: S;
+ let _ = get_a;
+ let _ = get_b;
+ }
+}
+
+mod test_type_in_fn_arg {
+ // https://github.com/dtolnay/paste/issues/38
+
+ use paste::paste;
+
+ fn _jit_address(_node: ()) {}
+
+ macro_rules! jit_reexport {
+ ($fn:ident, $arg:ident : $typ:ty) => {
+ paste! {
+ pub fn $fn($arg: $typ) {
+ [<_jit_ $fn>]($arg);
+ }
+ }
+ };
+ }
+
+ jit_reexport!(address, node: ());
+
+ #[test]
+ fn test_type_in_fn_arg() {
+ let _ = address;
+ }
+}
+
+mod test_pat_in_expr_position {
+ // https://github.com/xiph/rav1e/pull/2324/files
+
+ use paste::paste;
+
+ macro_rules! rav1e_bad {
+ ($e:pat) => {
+ paste! {
+ #[test]
+ fn test() {
+ let _ = $e;
+ }
+ }
+ };
+ }
+
+ rav1e_bad!(std::fmt::Error);
+}
diff --git a/third_party/rust/paste/tests/ui/case-warning.rs b/third_party/rust/paste/tests/ui/case-warning.rs
new file mode 100644
index 0000000000..fdea4d619d
--- /dev/null
+++ b/third_party/rust/paste/tests/ui/case-warning.rs
@@ -0,0 +1,15 @@
+#![deny(warnings)]
+
+use paste::paste;
+
+macro_rules! m {
+ ($i:ident) => {
+ paste! {
+ pub fn [<foo $i>]() {}
+ }
+ };
+}
+
+m!(Bar);
+
+fn main() {}
diff --git a/third_party/rust/paste/tests/ui/case-warning.stderr b/third_party/rust/paste/tests/ui/case-warning.stderr
new file mode 100644
index 0000000000..83099129df
--- /dev/null
+++ b/third_party/rust/paste/tests/ui/case-warning.stderr
@@ -0,0 +1,16 @@
+error: function `fooBar` should have a snake case name
+ --> tests/ui/case-warning.rs:8:20
+ |
+8 | pub fn [<foo $i>]() {}
+ | ^^^^^^^^^^ help: convert the identifier to snake case: `foo_bar`
+...
+13 | m!(Bar);
+ | ------- in this macro invocation
+ |
+note: the lint level is defined here
+ --> tests/ui/case-warning.rs:1:9
+ |
+1 | #![deny(warnings)]
+ | ^^^^^^^^
+ = note: `#[deny(non_snake_case)]` implied by `#[deny(warnings)]`
+ = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/third_party/rust/paste/tests/ui/env-empty.rs b/third_party/rust/paste/tests/ui/env-empty.rs
new file mode 100644
index 0000000000..1e9f2d0a24
--- /dev/null
+++ b/third_party/rust/paste/tests/ui/env-empty.rs
@@ -0,0 +1,7 @@
+use paste::paste;
+
+paste! {
+ fn [<env!()>]() {}
+}
+
+fn main() {}
diff --git a/third_party/rust/paste/tests/ui/env-empty.stderr b/third_party/rust/paste/tests/ui/env-empty.stderr
new file mode 100644
index 0000000000..a1ef2e2f58
--- /dev/null
+++ b/third_party/rust/paste/tests/ui/env-empty.stderr
@@ -0,0 +1,5 @@
+error: expected string literal as argument to env! macro
+ --> tests/ui/env-empty.rs:4:10
+ |
+4 | fn [<env!()>]() {}
+ | ^^^^^^
diff --git a/third_party/rust/paste/tests/ui/env-non-string.rs b/third_party/rust/paste/tests/ui/env-non-string.rs
new file mode 100644
index 0000000000..55255ef902
--- /dev/null
+++ b/third_party/rust/paste/tests/ui/env-non-string.rs
@@ -0,0 +1,7 @@
+use paste::paste;
+
+paste! {
+ fn [<env!(1.31)>]() {}
+}
+
+fn main() {}
diff --git a/third_party/rust/paste/tests/ui/env-non-string.stderr b/third_party/rust/paste/tests/ui/env-non-string.stderr
new file mode 100644
index 0000000000..05b8deba83
--- /dev/null
+++ b/third_party/rust/paste/tests/ui/env-non-string.stderr
@@ -0,0 +1,5 @@
+error: expected string literal
+ --> tests/ui/env-non-string.rs:4:15
+ |
+4 | fn [<env!(1.31)>]() {}
+ | ^^^^
diff --git a/third_party/rust/paste/tests/ui/env-suffix.rs b/third_party/rust/paste/tests/ui/env-suffix.rs
new file mode 100644
index 0000000000..b5c60af450
--- /dev/null
+++ b/third_party/rust/paste/tests/ui/env-suffix.rs
@@ -0,0 +1,7 @@
+use paste::paste;
+
+paste! {
+ fn [<env!("VAR"suffix)>]() {}
+}
+
+fn main() {}
diff --git a/third_party/rust/paste/tests/ui/env-suffix.stderr b/third_party/rust/paste/tests/ui/env-suffix.stderr
new file mode 100644
index 0000000000..d723cbc7ed
--- /dev/null
+++ b/third_party/rust/paste/tests/ui/env-suffix.stderr
@@ -0,0 +1,5 @@
+error: expected string literal
+ --> tests/ui/env-suffix.rs:4:15
+ |
+4 | fn [<env!("VAR"suffix)>]() {}
+ | ^^^^^^^^^^^
diff --git a/third_party/rust/paste/tests/ui/env-unexpected.rs b/third_party/rust/paste/tests/ui/env-unexpected.rs
new file mode 100644
index 0000000000..39cb770d98
--- /dev/null
+++ b/third_party/rust/paste/tests/ui/env-unexpected.rs
@@ -0,0 +1,7 @@
+use paste::paste;
+
+paste! {
+ fn [<env!("VAR" "VAR")>]() {}
+}
+
+fn main() {}
diff --git a/third_party/rust/paste/tests/ui/env-unexpected.stderr b/third_party/rust/paste/tests/ui/env-unexpected.stderr
new file mode 100644
index 0000000000..25387b1140
--- /dev/null
+++ b/third_party/rust/paste/tests/ui/env-unexpected.stderr
@@ -0,0 +1,5 @@
+error: unexpected token in env! macro
+ --> tests/ui/env-unexpected.rs:4:21
+ |
+4 | fn [<env!("VAR" "VAR")>]() {}
+ | ^^^^^
diff --git a/third_party/rust/paste/tests/ui/invalid-ident.rs b/third_party/rust/paste/tests/ui/invalid-ident.rs
new file mode 100644
index 0000000000..6a8cf3c44b
--- /dev/null
+++ b/third_party/rust/paste/tests/ui/invalid-ident.rs
@@ -0,0 +1,15 @@
+use paste::paste;
+
+paste! {
+ fn [<0 f>]() {}
+}
+
+paste! {
+ fn [<f '"'>]() {}
+}
+
+paste! {
+ fn [<f "'">]() {}
+}
+
+fn main() {}
diff --git a/third_party/rust/paste/tests/ui/invalid-ident.stderr b/third_party/rust/paste/tests/ui/invalid-ident.stderr
new file mode 100644
index 0000000000..28593fb1a6
--- /dev/null
+++ b/third_party/rust/paste/tests/ui/invalid-ident.stderr
@@ -0,0 +1,21 @@
+error: expected identifier, found `0f`
+ --> tests/ui/invalid-ident.rs:3:1
+ |
+3 | / paste! {
+4 | | fn [<0 f>]() {}
+5 | | }
+ | |_^ expected identifier
+ |
+ = note: this error originates in the macro `paste` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: `"f\""` is not a valid identifier
+ --> tests/ui/invalid-ident.rs:8:8
+ |
+8 | fn [<f '"'>]() {}
+ | ^^^^^^^^^
+
+error: `"f'"` is not a valid identifier
+ --> tests/ui/invalid-ident.rs:12:8
+ |
+12 | fn [<f "'">]() {}
+ | ^^^^^^^^^
diff --git a/third_party/rust/paste/tests/ui/missing-paren-on-env.rs b/third_party/rust/paste/tests/ui/missing-paren-on-env.rs
new file mode 100644
index 0000000000..44fefbd3e0
--- /dev/null
+++ b/third_party/rust/paste/tests/ui/missing-paren-on-env.rs
@@ -0,0 +1,7 @@
+use paste::paste;
+
+paste! {
+ fn [<env! huh>]() {}
+}
+
+fn main() {}
diff --git a/third_party/rust/paste/tests/ui/missing-paren-on-env.stderr b/third_party/rust/paste/tests/ui/missing-paren-on-env.stderr
new file mode 100644
index 0000000000..7b4bc4604d
--- /dev/null
+++ b/third_party/rust/paste/tests/ui/missing-paren-on-env.stderr
@@ -0,0 +1,5 @@
+error: expected `(`
+ --> tests/ui/missing-paren-on-env.rs:4:15
+ |
+4 | fn [<env! huh>]() {}
+ | ^^^
diff --git a/third_party/rust/paste/tests/ui/no-env-var.rs b/third_party/rust/paste/tests/ui/no-env-var.rs
new file mode 100644
index 0000000000..c6d8c3d59c
--- /dev/null
+++ b/third_party/rust/paste/tests/ui/no-env-var.rs
@@ -0,0 +1,7 @@
+use paste::paste;
+
+paste! {
+ fn [<a env!("PASTE_UNKNOWN") b>]() {}
+}
+
+fn main() {}
diff --git a/third_party/rust/paste/tests/ui/no-env-var.stderr b/third_party/rust/paste/tests/ui/no-env-var.stderr
new file mode 100644
index 0000000000..60de9ede26
--- /dev/null
+++ b/third_party/rust/paste/tests/ui/no-env-var.stderr
@@ -0,0 +1,5 @@
+error: no such env var: "PASTE_UNKNOWN"
+ --> tests/ui/no-env-var.rs:4:17
+ |
+4 | fn [<a env!("PASTE_UNKNOWN") b>]() {}
+ | ^^^^^^^^^^^^^^^
diff --git a/third_party/rust/paste/tests/ui/no-ident-after-colon.rs b/third_party/rust/paste/tests/ui/no-ident-after-colon.rs
new file mode 100644
index 0000000000..50b3b0dd01
--- /dev/null
+++ b/third_party/rust/paste/tests/ui/no-ident-after-colon.rs
@@ -0,0 +1,7 @@
+use paste::paste;
+
+paste! {
+ fn [<name:0>]() {}
+}
+
+fn main() {}
diff --git a/third_party/rust/paste/tests/ui/no-ident-after-colon.stderr b/third_party/rust/paste/tests/ui/no-ident-after-colon.stderr
new file mode 100644
index 0000000000..9db91eb517
--- /dev/null
+++ b/third_party/rust/paste/tests/ui/no-ident-after-colon.stderr
@@ -0,0 +1,5 @@
+error: expected identifier after `:`
+ --> tests/ui/no-ident-after-colon.rs:4:15
+ |
+4 | fn [<name:0>]() {}
+ | ^
diff --git a/third_party/rust/paste/tests/ui/unexpected-group.rs b/third_party/rust/paste/tests/ui/unexpected-group.rs
new file mode 100644
index 0000000000..63ee5161c7
--- /dev/null
+++ b/third_party/rust/paste/tests/ui/unexpected-group.rs
@@ -0,0 +1,7 @@
+use paste::paste;
+
+paste! {
+ fn [<a {} b>]() {}
+}
+
+fn main() {}
diff --git a/third_party/rust/paste/tests/ui/unexpected-group.stderr b/third_party/rust/paste/tests/ui/unexpected-group.stderr
new file mode 100644
index 0000000000..f66f5c15e0
--- /dev/null
+++ b/third_party/rust/paste/tests/ui/unexpected-group.stderr
@@ -0,0 +1,5 @@
+error: unexpected token
+ --> tests/ui/unexpected-group.rs:4:12
+ |
+4 | fn [<a {} b>]() {}
+ | ^^
diff --git a/third_party/rust/paste/tests/ui/unexpected-modifier.rs b/third_party/rust/paste/tests/ui/unexpected-modifier.rs
new file mode 100644
index 0000000000..99fe68f1ff
--- /dev/null
+++ b/third_party/rust/paste/tests/ui/unexpected-modifier.rs
@@ -0,0 +1,7 @@
+use paste::paste;
+
+paste! {
+ fn [<:lower x>]() {}
+}
+
+fn main() {}
diff --git a/third_party/rust/paste/tests/ui/unexpected-modifier.stderr b/third_party/rust/paste/tests/ui/unexpected-modifier.stderr
new file mode 100644
index 0000000000..1eaba3147a
--- /dev/null
+++ b/third_party/rust/paste/tests/ui/unexpected-modifier.stderr
@@ -0,0 +1,5 @@
+error: unexpected modifier
+ --> tests/ui/unexpected-modifier.rs:4:10
+ |
+4 | fn [<:lower x>]() {}
+ | ^^^^^^
diff --git a/third_party/rust/paste/tests/ui/unexpected-punct.rs b/third_party/rust/paste/tests/ui/unexpected-punct.rs
new file mode 100644
index 0000000000..d0edb9243a
--- /dev/null
+++ b/third_party/rust/paste/tests/ui/unexpected-punct.rs
@@ -0,0 +1,7 @@
+use paste::paste;
+
+paste! {
+ fn [<a + b>]() {}
+}
+
+fn main() {}
diff --git a/third_party/rust/paste/tests/ui/unexpected-punct.stderr b/third_party/rust/paste/tests/ui/unexpected-punct.stderr
new file mode 100644
index 0000000000..1a74a61d73
--- /dev/null
+++ b/third_party/rust/paste/tests/ui/unexpected-punct.stderr
@@ -0,0 +1,5 @@
+error: unexpected punct
+ --> tests/ui/unexpected-punct.rs:4:12
+ |
+4 | fn [<a + b>]() {}
+ | ^
diff --git a/third_party/rust/paste/tests/ui/unsupported-literal.rs b/third_party/rust/paste/tests/ui/unsupported-literal.rs
new file mode 100644
index 0000000000..7a9c490d8c
--- /dev/null
+++ b/third_party/rust/paste/tests/ui/unsupported-literal.rs
@@ -0,0 +1,21 @@
+use paste::paste;
+
+paste! {
+ fn [<x 1e+100 z>]() {}
+}
+
+paste! {
+ // `xyz` is not correct. `xbyz` is certainly not correct. Maybe `x121z`
+ // would be justifiable but for now don't accept this.
+ fn [<x b'y' z>]() {}
+}
+
+paste! {
+ fn [<x b"y" z>]() {}
+}
+
+paste! {
+ fn [<x br"y" z>]() {}
+}
+
+fn main() {}
diff --git a/third_party/rust/paste/tests/ui/unsupported-literal.stderr b/third_party/rust/paste/tests/ui/unsupported-literal.stderr
new file mode 100644
index 0000000000..a802b4580f
--- /dev/null
+++ b/third_party/rust/paste/tests/ui/unsupported-literal.stderr
@@ -0,0 +1,23 @@
+error: unsupported literal
+ --> tests/ui/unsupported-literal.rs:4:12
+ |
+4 | fn [<x 1e+100 z>]() {}
+ | ^^^^^^
+
+error: unsupported literal
+ --> tests/ui/unsupported-literal.rs:10:12
+ |
+10 | fn [<x b'y' z>]() {}
+ | ^^^^
+
+error: unsupported literal
+ --> tests/ui/unsupported-literal.rs:14:12
+ |
+14 | fn [<x b"y" z>]() {}
+ | ^^^^
+
+error: unsupported literal
+ --> tests/ui/unsupported-literal.rs:18:12
+ |
+18 | fn [<x br"y" z>]() {}
+ | ^^^^^
diff --git a/third_party/rust/paste/tests/ui/unsupported-modifier.rs b/third_party/rust/paste/tests/ui/unsupported-modifier.rs
new file mode 100644
index 0000000000..a65b36ae80
--- /dev/null
+++ b/third_party/rust/paste/tests/ui/unsupported-modifier.rs
@@ -0,0 +1,7 @@
+use paste::paste;
+
+paste! {
+ fn [<a:pillow>]() {}
+}
+
+fn main() {}
diff --git a/third_party/rust/paste/tests/ui/unsupported-modifier.stderr b/third_party/rust/paste/tests/ui/unsupported-modifier.stderr
new file mode 100644
index 0000000000..3c7043541f
--- /dev/null
+++ b/third_party/rust/paste/tests/ui/unsupported-modifier.stderr
@@ -0,0 +1,5 @@
+error: unsupported modifier
+ --> tests/ui/unsupported-modifier.rs:4:11
+ |
+4 | fn [<a:pillow>]() {}
+ | ^^^^^^^