diff --git a/Cargo.lock b/Cargo.lock index 5223e3b0..99cf6ca0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -99,24 +99,52 @@ dependencies = [ "libloading", ] +[[package]] +name = "dissimilar" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8975ffdaa0ef3661bfe02dbdcc06c9f829dfafe6a3c474de366a8d5e44276921" + [[package]] name = "either" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + [[package]] name = "glob" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +[[package]] +name = "hashbrown" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" + [[package]] name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "indexmap" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "instant" version = "0.1.12" @@ -135,6 +163,12 @@ dependencies = [ "either", ] +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + [[package]] name = "lazy_static" version = "1.4.0" @@ -154,7 +188,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", - "windows-targets", + "windows-targets 0.48.5", ] [[package]] @@ -231,9 +265,16 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +version = "1.0.15" +dependencies = [ + "paste-test-suite", + "rustversion", + "trybuild", +] + +[[package]] +name = "paste-test-suite" +version = "0.0.0" [[package]] name = "prettyplease" @@ -307,6 +348,18 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustversion" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + [[package]] name = "scopeguard" version = "1.2.0" @@ -343,6 +396,27 @@ dependencies = [ "syn 2.0.50", ] +[[package]] +name = "serde_json" +version = "1.0.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + [[package]] name = "serial_test" version = "0.5.1" @@ -399,12 +473,84 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "target-triple" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ac9aa371f599d22256307c24a9d748c041e548cbf599f35d890f9d365361790" + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + [[package]] name = "testresult" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "614b328ff036a4ef882c61570f72918f7e9c5bee1da33f8e7f91e01daee7e56c" +[[package]] +name = "toml" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "toml_write", + "winnow", +] + +[[package]] +name = "toml_write" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076" + +[[package]] +name = "trybuild" +version = "1.0.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c9bf9513a2f4aeef5fdac8677d7d349c79fdbcc03b9c86da6e9d254f1e43be2" +dependencies = [ + "dissimilar", + "glob", + "serde", + "serde_derive", + "serde_json", + "target-triple", + "termcolor", + "toml", +] + [[package]] name = "unicode-ident" version = "1.0.12" @@ -427,25 +573,59 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-targets" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] @@ -454,42 +634,99 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" +dependencies = [ + "memchr", +] + [[package]] name = "zeroize" version = "1.7.0" diff --git a/Cargo.toml b/Cargo.toml index d35aaab5..f75a3d70 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,3 @@ [workspace] -members = ["cryptoki", "cryptoki-sys"] +members = ["cryptoki", "cryptoki-sys", "paste"] resolver = "2" diff --git a/cryptoki/Cargo.toml b/cryptoki/Cargo.toml index 1bf288de..1b154db8 100644 --- a/cryptoki/Cargo.toml +++ b/cryptoki/Cargo.toml @@ -17,7 +17,7 @@ bitflags = "1.3" libloading = "0.8.6" log = "0.4.14" cryptoki-sys = { path = "../cryptoki-sys", version = "0.3.0" } -paste = "1.0.6" +paste = { path = "../paste", version = "1.0.6" } secrecy = "0.8.0" [dev-dependencies] diff --git a/paste/.gitignore b/paste/.gitignore new file mode 100644 index 00000000..69369904 --- /dev/null +++ b/paste/.gitignore @@ -0,0 +1,3 @@ +/target +**/*.rs.bk +Cargo.lock diff --git a/paste/Cargo.toml b/paste/Cargo.toml new file mode 100644 index 00000000..f7273758 --- /dev/null +++ b/paste/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "paste" +version = "1.0.15" +authors = ["David Tolnay "] +categories = ["development-tools", "no-std", "no-std::no-alloc"] +description = "Macros for all your token pasting needs" +documentation = "https://docs.rs/paste" +edition = "2018" +keywords = ["macros"] +license = "MIT OR Apache-2.0" +repository = "https://github.com/dtolnay/paste" +rust-version = "1.31" + +[lib] +proc-macro = true + +[dev-dependencies] +paste-test-suite = { version = "0", path = "tests/macros" } +rustversion = "1.0" +trybuild = { version = "1.0.49", features = ["diff"] } + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] +rustdoc-args = ["--generate-link-to-definition"] diff --git a/paste/LICENSE-APACHE b/paste/LICENSE-APACHE new file mode 100644 index 00000000..1b5ec8b7 --- /dev/null +++ b/paste/LICENSE-APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/paste/LICENSE-MIT b/paste/LICENSE-MIT new file mode 100644 index 00000000..31aa7938 --- /dev/null +++ b/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/paste/README.md b/paste/README.md new file mode 100644 index 00000000..729b4e07 --- /dev/null +++ b/paste/README.md @@ -0,0 +1,159 @@ +Macros for all your token pasting needs +======================================= + +[github](https://github.com/dtolnay/paste) +[crates.io](https://crates.io/crates/paste) +[docs.rs](https://docs.rs/paste) +[build status](https://github.com/dtolnay/paste/actions?query=branch%3Amaster) + +_(This project is no longer maintained.)_ + +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+. + +
+ +## 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 []: &str = "success!"; +} + +fn main() { + assert_eq!( + paste! { [].len() }, + 8, + ); +} +``` + +
+ +## 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 [](&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() +} +``` + +
+ +## 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, `[]` 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 + +
+ +## 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"] +``` + +
+ +#### License + + +Licensed under either of Apache License, Version +2.0 or MIT license at your option. + + +
+ + +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. + diff --git a/paste/build.rs b/paste/build.rs new file mode 100644 index 00000000..c9e5fe1c --- /dev/null +++ b/paste/build.rs @@ -0,0 +1,38 @@ +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 >= 80 { + println!("cargo:rustc-check-cfg=cfg(no_literal_fromstr)"); + println!("cargo:rustc-check-cfg=cfg(feature, values(\"protocol_feature_paste\"))"); + } + + 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 { + 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/paste/src/attr.rs b/paste/src/attr.rs new file mode 100644 index 00000000..d66b843b --- /dev/null +++ b/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 { + 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 { + 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/paste/src/error.rs b/paste/src/error.rs new file mode 100644 index 00000000..7c5badb2 --- /dev/null +++ b/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 = std::result::Result; + +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/paste/src/lib.rs b/paste/src/lib.rs new file mode 100644 index 00000000..9bb86bfe --- /dev/null +++ b/paste/src/lib.rs @@ -0,0 +1,454 @@ +//! [![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 +//! +//!
+//! +//! 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+. +//! +//!
+//! +//! # 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 []: &str = "success!"; +//! } +//! +//! fn main() { +//! assert_eq!( +//! paste! { [].len() }, +//! 8, +//! ); +//! } +//! ``` +//! +//!

+//! +//! # 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 [](&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() {} +//! ``` +//! +//!

+//! +//! # 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, `[]` 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 +//! +//!
+//! +//! # 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"] +//! ``` + +#![doc(html_root_url = "https://docs.rs/paste/1.0.15")] +#![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 { + let mut expanded = TokenStream::new(); + let mut lookbehind = Lookbehind::Other; + let mut prev_none_group = None::; + 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> { + 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 { + 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/paste/src/segment.rs b/paste/src/segment.rs new file mode 100644 index 00000000..592a0470 --- /dev/null +++ b/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) -> Result> { + 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 { + 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::(); + if is_lifetime { + pasted.insert(0, '\''); + } + Ok(pasted) +} diff --git a/paste/tests/compiletest.rs b/paste/tests/compiletest.rs new file mode 100644 index 00000000..23a6a065 --- /dev/null +++ b/paste/tests/compiletest.rs @@ -0,0 +1,7 @@ +#[rustversion::attr(not(nightly), ignore = "requires nightly")] +#[cfg_attr(miri, ignore = "incompatible with miri")] +#[test] +fn ui() { + let t = trybuild::TestCases::new(); + t.compile_fail("tests/ui/*.rs"); +} diff --git a/paste/tests/macros/Cargo.toml b/paste/tests/macros/Cargo.toml new file mode 100644 index 00000000..9ed32244 --- /dev/null +++ b/paste/tests/macros/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "paste-test-suite" +version = "0.0.0" +authors = ["David Tolnay "] +edition = "2018" +publish = false + +[lib] +path = "lib.rs" +proc-macro = true diff --git a/paste/tests/macros/lib.rs b/paste/tests/macros/lib.rs new file mode 100644 index 00000000..b49a5b40 --- /dev/null +++ b/paste/tests/macros/lib.rs @@ -0,0 +1,25 @@ +extern crate proc_macro; + +use proc_macro::{TokenStream, TokenTree}; + +#[proc_macro_attribute] +pub fn paste_test(args: TokenStream, input: TokenStream) -> TokenStream { + let mut iter = args.clone().into_iter(); + match iter.next() { + Some(TokenTree::Ident(_)) => {} + _ => panic!("{}", args), + } + match iter.next() { + Some(TokenTree::Punct(ref punct)) if punct.as_char() == '=' => {} + _ => panic!("{}", args), + } + match iter.next() { + Some(TokenTree::Literal(ref literal)) if literal.to_string().starts_with('"') => {} + _ => panic!("{}", args), + } + match iter.next() { + None => {} + _ => panic!("{}", args), + } + input +} diff --git a/paste/tests/test_attr.rs b/paste/tests/test_attr.rs new file mode 100644 index 00000000..6c498086 --- /dev/null +++ b/paste/tests/test_attr.rs @@ -0,0 +1,62 @@ +#![allow(clippy::let_underscore_untyped)] + +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" [] 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/paste/tests/test_doc.rs b/paste/tests/test_doc.rs new file mode 100644 index 00000000..3dd66aab --- /dev/null +++ b/paste/tests/test_doc.rs @@ -0,0 +1,79 @@ +#![allow(clippy::let_underscore_untyped)] + +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/paste/tests/test_expr.rs b/paste/tests/test_expr.rs new file mode 100644 index 00000000..a8e6bd86 --- /dev/null +++ b/paste/tests/test_expr.rs @@ -0,0 +1,285 @@ +#![allow(clippy::let_underscore_untyped)] + +use paste::paste; + +#[test] +fn test_shared_hygiene() { + paste! { + let [] = 1; + assert_eq!([], 1); + } +} + +#[test] +fn test_repeat() { + const ROCKET_A: &str = "/a"; + const ROCKET_B: &str = "/b"; + + macro_rules! routes { + ($($route:ident),*) => {{ + paste! { + vec![$( [] ),*] + } + }} + } + + let routes = routes!(A, B); + assert_eq!(routes, vec!["/a", "/b"]); +} + +#[test] +fn test_literal_to_identifier() { + const CONST0: &str = "const0"; + + let pasted = paste!([]); + assert_eq!(pasted, CONST0); + + let pasted = paste!([]); + assert_eq!(pasted, CONST0); + + let pasted = paste!([]); + assert_eq!(pasted, CONST0); + + let pasted = paste!([]); + assert_eq!(pasted, CONST0); + + let pasted = paste!([]); + 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!([], 0); + } +} + +#[test] +fn test_lifetime() { + paste! { + #[allow(dead_code)] + struct S<[<'d e>]> { + q: &[<'d e>] str, + } + } +} + +#[test] +fn test_keyword() { + paste! { + struct []; + + let _ = Fmove; + } +} + +#[test] +fn test_literal_str() { + paste! { + #[allow(non_camel_case_types)] + struct []; + + let _ = FooBar_Baz; + } +} + +#[test] +fn test_env_literal() { + paste! { + struct []; + + let _ = Libenvbar; + } +} + +#[test] +fn test_env_present() { + paste! { + struct []; + + let _ = Libpaste; + } +} + +#[test] +fn test_raw_identifier() { + paste! { + struct []; + + 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 = [::f()]; + assert_eq!(x[0], 0); + } +} + +#[test] +fn test_local_variable() { + let yy = 0; + + paste! { + assert_eq!([], 0); + } +} + +#[test] +fn test_empty() { + paste! { + assert_eq!(stringify!([]), "yy"); + assert_eq!(stringify!([<>]).replace(' ', ""), "[<>]"); + } +} + +#[test] +fn test_env_to_lower() { + paste! { + struct []; + + let _ = Libpaste; + } +} + +#[test] +fn test_env_to_upper() { + paste! { + const []: &str = "libpaste"; + + let _ = LIBPASTE; + } +} + +#[test] +fn test_env_to_snake() { + paste! { + const []: &str = "libpaste"; + + let _ = LIBPASTE; + } +} + +#[test] +fn test_env_to_camel() { + paste! { + #[allow(non_upper_case_globals)] + const []: &str = "libpaste"; + + let _ = LIBPaste; + } +} + +mod test_x86_feature_literal { + // work around https://github.com/rust-lang/rust/issues/72726 + + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + macro_rules! my_is_x86_feature_detected { + ($feat:literal) => { + use paste::paste; + + 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.[]($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/paste/tests/test_item.rs b/paste/tests/test_item.rs new file mode 100644 index 00000000..a3dff488 --- /dev/null +++ b/paste/tests/test_item.rs @@ -0,0 +1,271 @@ +#![allow(clippy::let_underscore_untyped)] + +mod test_basic { + use paste::paste; + + struct Struct; + + paste! { + impl Struct { + fn []() {} + } + } + + #[test] + fn test() { + Struct::abc(); + } +} + +mod test_in_impl { + use paste::paste; + + struct Struct; + + impl Struct { + paste! { + fn []() {} + } + } + + #[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>(#[allow(dead_code)] pub &$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 [](_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 []: &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(std::marker::PhantomData); + } + + 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) { + []($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/paste/tests/ui/case-warning.rs b/paste/tests/ui/case-warning.rs new file mode 100644 index 00000000..fdea4d61 --- /dev/null +++ b/paste/tests/ui/case-warning.rs @@ -0,0 +1,15 @@ +#![deny(warnings)] + +use paste::paste; + +macro_rules! m { + ($i:ident) => { + paste! { + pub fn []() {} + } + }; +} + +m!(Bar); + +fn main() {} diff --git a/paste/tests/ui/case-warning.stderr b/paste/tests/ui/case-warning.stderr new file mode 100644 index 00000000..83099129 --- /dev/null +++ b/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 []() {} + | ^^^^^^^^^^ 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/paste/tests/ui/env-empty.rs b/paste/tests/ui/env-empty.rs new file mode 100644 index 00000000..1e9f2d0a --- /dev/null +++ b/paste/tests/ui/env-empty.rs @@ -0,0 +1,7 @@ +use paste::paste; + +paste! { + fn []() {} +} + +fn main() {} diff --git a/paste/tests/ui/env-empty.stderr b/paste/tests/ui/env-empty.stderr new file mode 100644 index 00000000..a1ef2e2f --- /dev/null +++ b/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 []() {} + | ^^^^^^ diff --git a/paste/tests/ui/env-non-string.rs b/paste/tests/ui/env-non-string.rs new file mode 100644 index 00000000..55255ef9 --- /dev/null +++ b/paste/tests/ui/env-non-string.rs @@ -0,0 +1,7 @@ +use paste::paste; + +paste! { + fn []() {} +} + +fn main() {} diff --git a/paste/tests/ui/env-non-string.stderr b/paste/tests/ui/env-non-string.stderr new file mode 100644 index 00000000..05b8deba --- /dev/null +++ b/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 []() {} + | ^^^^ diff --git a/paste/tests/ui/env-suffix.rs b/paste/tests/ui/env-suffix.rs new file mode 100644 index 00000000..b5c60af4 --- /dev/null +++ b/paste/tests/ui/env-suffix.rs @@ -0,0 +1,7 @@ +use paste::paste; + +paste! { + fn []() {} +} + +fn main() {} diff --git a/paste/tests/ui/env-suffix.stderr b/paste/tests/ui/env-suffix.stderr new file mode 100644 index 00000000..d723cbc7 --- /dev/null +++ b/paste/tests/ui/env-suffix.stderr @@ -0,0 +1,5 @@ +error: expected string literal + --> tests/ui/env-suffix.rs:4:15 + | +4 | fn []() {} + | ^^^^^^^^^^^ diff --git a/paste/tests/ui/env-unexpected.rs b/paste/tests/ui/env-unexpected.rs new file mode 100644 index 00000000..39cb770d --- /dev/null +++ b/paste/tests/ui/env-unexpected.rs @@ -0,0 +1,7 @@ +use paste::paste; + +paste! { + fn []() {} +} + +fn main() {} diff --git a/paste/tests/ui/env-unexpected.stderr b/paste/tests/ui/env-unexpected.stderr new file mode 100644 index 00000000..25387b11 --- /dev/null +++ b/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 []() {} + | ^^^^^ diff --git a/paste/tests/ui/invalid-ident.rs b/paste/tests/ui/invalid-ident.rs new file mode 100644 index 00000000..6a8cf3c4 --- /dev/null +++ b/paste/tests/ui/invalid-ident.rs @@ -0,0 +1,15 @@ +use paste::paste; + +paste! { + fn [<0 f>]() {} +} + +paste! { + fn []() {} +} + +paste! { + fn []() {} +} + +fn main() {} diff --git a/paste/tests/ui/invalid-ident.stderr b/paste/tests/ui/invalid-ident.stderr new file mode 100644 index 00000000..ca1585e8 --- /dev/null +++ b/paste/tests/ui/invalid-ident.stderr @@ -0,0 +1,26 @@ +error: expected identifier, found `0f` + --> tests/ui/invalid-ident.rs:3:1 + | +3 | / paste! { +4 | | fn [<0 f>]() {} +5 | | } + | |_^ expected identifier + | +help: identifiers cannot start with a number + --> tests/ui/invalid-ident.rs:3:1 + | +3 | paste! { + | ^ + = 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 []() {} + | ^^^^^^^^^ + +error: `"f'"` is not a valid identifier + --> tests/ui/invalid-ident.rs:12:8 + | +12 | fn []() {} + | ^^^^^^^^^ diff --git a/paste/tests/ui/missing-paren-on-env.rs b/paste/tests/ui/missing-paren-on-env.rs new file mode 100644 index 00000000..44fefbd3 --- /dev/null +++ b/paste/tests/ui/missing-paren-on-env.rs @@ -0,0 +1,7 @@ +use paste::paste; + +paste! { + fn []() {} +} + +fn main() {} diff --git a/paste/tests/ui/missing-paren-on-env.stderr b/paste/tests/ui/missing-paren-on-env.stderr new file mode 100644 index 00000000..7b4bc460 --- /dev/null +++ b/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 []() {} + | ^^^ diff --git a/paste/tests/ui/no-env-var.rs b/paste/tests/ui/no-env-var.rs new file mode 100644 index 00000000..c6d8c3d5 --- /dev/null +++ b/paste/tests/ui/no-env-var.rs @@ -0,0 +1,7 @@ +use paste::paste; + +paste! { + fn []() {} +} + +fn main() {} diff --git a/paste/tests/ui/no-env-var.stderr b/paste/tests/ui/no-env-var.stderr new file mode 100644 index 00000000..60de9ede --- /dev/null +++ b/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 []() {} + | ^^^^^^^^^^^^^^^ diff --git a/paste/tests/ui/no-ident-after-colon.rs b/paste/tests/ui/no-ident-after-colon.rs new file mode 100644 index 00000000..50b3b0dd --- /dev/null +++ b/paste/tests/ui/no-ident-after-colon.rs @@ -0,0 +1,7 @@ +use paste::paste; + +paste! { + fn []() {} +} + +fn main() {} diff --git a/paste/tests/ui/no-ident-after-colon.stderr b/paste/tests/ui/no-ident-after-colon.stderr new file mode 100644 index 00000000..9db91eb5 --- /dev/null +++ b/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 []() {} + | ^ diff --git a/paste/tests/ui/unexpected-group.rs b/paste/tests/ui/unexpected-group.rs new file mode 100644 index 00000000..63ee5161 --- /dev/null +++ b/paste/tests/ui/unexpected-group.rs @@ -0,0 +1,7 @@ +use paste::paste; + +paste! { + fn []() {} +} + +fn main() {} diff --git a/paste/tests/ui/unexpected-group.stderr b/paste/tests/ui/unexpected-group.stderr new file mode 100644 index 00000000..f66f5c15 --- /dev/null +++ b/paste/tests/ui/unexpected-group.stderr @@ -0,0 +1,5 @@ +error: unexpected token + --> tests/ui/unexpected-group.rs:4:12 + | +4 | fn []() {} + | ^^ diff --git a/paste/tests/ui/unexpected-modifier.rs b/paste/tests/ui/unexpected-modifier.rs new file mode 100644 index 00000000..99fe68f1 --- /dev/null +++ b/paste/tests/ui/unexpected-modifier.rs @@ -0,0 +1,7 @@ +use paste::paste; + +paste! { + fn [<:lower x>]() {} +} + +fn main() {} diff --git a/paste/tests/ui/unexpected-modifier.stderr b/paste/tests/ui/unexpected-modifier.stderr new file mode 100644 index 00000000..1eaba314 --- /dev/null +++ b/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/paste/tests/ui/unexpected-punct.rs b/paste/tests/ui/unexpected-punct.rs new file mode 100644 index 00000000..d0edb924 --- /dev/null +++ b/paste/tests/ui/unexpected-punct.rs @@ -0,0 +1,7 @@ +use paste::paste; + +paste! { + fn []() {} +} + +fn main() {} diff --git a/paste/tests/ui/unexpected-punct.stderr b/paste/tests/ui/unexpected-punct.stderr new file mode 100644 index 00000000..1a74a61d --- /dev/null +++ b/paste/tests/ui/unexpected-punct.stderr @@ -0,0 +1,5 @@ +error: unexpected punct + --> tests/ui/unexpected-punct.rs:4:12 + | +4 | fn []() {} + | ^ diff --git a/paste/tests/ui/unsupported-literal.rs b/paste/tests/ui/unsupported-literal.rs new file mode 100644 index 00000000..7a9c490d --- /dev/null +++ b/paste/tests/ui/unsupported-literal.rs @@ -0,0 +1,21 @@ +use paste::paste; + +paste! { + fn []() {} +} + +paste! { + // `xyz` is not correct. `xbyz` is certainly not correct. Maybe `x121z` + // would be justifiable but for now don't accept this. + fn []() {} +} + +paste! { + fn []() {} +} + +paste! { + fn []() {} +} + +fn main() {} diff --git a/paste/tests/ui/unsupported-literal.stderr b/paste/tests/ui/unsupported-literal.stderr new file mode 100644 index 00000000..a802b458 --- /dev/null +++ b/paste/tests/ui/unsupported-literal.stderr @@ -0,0 +1,23 @@ +error: unsupported literal + --> tests/ui/unsupported-literal.rs:4:12 + | +4 | fn []() {} + | ^^^^^^ + +error: unsupported literal + --> tests/ui/unsupported-literal.rs:10:12 + | +10 | fn []() {} + | ^^^^ + +error: unsupported literal + --> tests/ui/unsupported-literal.rs:14:12 + | +14 | fn []() {} + | ^^^^ + +error: unsupported literal + --> tests/ui/unsupported-literal.rs:18:12 + | +18 | fn []() {} + | ^^^^^ diff --git a/paste/tests/ui/unsupported-modifier.rs b/paste/tests/ui/unsupported-modifier.rs new file mode 100644 index 00000000..a65b36ae --- /dev/null +++ b/paste/tests/ui/unsupported-modifier.rs @@ -0,0 +1,7 @@ +use paste::paste; + +paste! { + fn []() {} +} + +fn main() {} diff --git a/paste/tests/ui/unsupported-modifier.stderr b/paste/tests/ui/unsupported-modifier.stderr new file mode 100644 index 00000000..3c704354 --- /dev/null +++ b/paste/tests/ui/unsupported-modifier.stderr @@ -0,0 +1,5 @@ +error: unsupported modifier + --> tests/ui/unsupported-modifier.rs:4:11 + | +4 | fn []() {} + | ^^^^^^^