diff options
Diffstat (limited to 'third_party/rust/paste')
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) [![crates-io]](https://crates.io/crates/paste) [![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>]() {} + | ^^^^^^^ |