diff options
author | Elie Kheirallah <khei@google.com> | 2022-11-05 07:59:20 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2022-11-05 07:59:20 +0000 |
commit | 1011fac88b7c3a84af464d0ff50cc4876e253651 (patch) | |
tree | 9c35ee92ef47759998c2974967b2f27ab4276e49 | |
parent | e328d96f595b8fa163f5a0ef7af82d8d4a0db16e (diff) | |
parent | 03460c13ad9948b3972a1869e7f667e7ec5eae2f (diff) | |
download | static_assertions-1011fac88b7c3a84af464d0ff50cc4876e253651.tar.gz |
Initial import of static_assertions-1.1.0 am: 03460c13ad
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/static_assertions/+/2287738
Change-Id: Idb534468eaeb700093fd1fd267561cb3e6f281d4
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r-- | CHANGELOG.md | 181 | ||||
-rw-r--r-- | Cargo.toml | 28 | ||||
-rw-r--r-- | Cargo.toml.orig | 22 | ||||
l--------- | LICENSE | 1 | ||||
-rw-r--r-- | LICENSE-APACHE | 202 | ||||
-rw-r--r-- | METADATA | 20 | ||||
-rw-r--r-- | MODULE_LICENSE_APACHE2 | 0 | ||||
-rw-r--r-- | OWNERS | 1 | ||||
-rw-r--r-- | README.md | 188 | ||||
-rw-r--r-- | proc/Cargo.toml | 25 | ||||
-rw-r--r-- | proc/src/lib.rs | 92 | ||||
-rw-r--r-- | proc/tests/size.rs | 7 | ||||
-rw-r--r-- | rustfmt.toml | 15 | ||||
-rw-r--r-- | src/assert_align.rs | 200 | ||||
-rw-r--r-- | src/assert_cfg.rs | 49 | ||||
-rw-r--r-- | src/assert_fields.rs | 72 | ||||
-rw-r--r-- | src/assert_impl.rs | 385 | ||||
-rw-r--r-- | src/assert_obj_safe.rs | 76 | ||||
-rw-r--r-- | src/assert_size.rs | 168 | ||||
-rw-r--r-- | src/assert_trait.rs | 139 | ||||
-rw-r--r-- | src/assert_type.rs | 101 | ||||
-rw-r--r-- | src/bool.rs | 59 | ||||
-rw-r--r-- | src/const_assert.rs | 178 | ||||
-rw-r--r-- | src/does_impl.rs | 108 | ||||
-rw-r--r-- | src/lib.rs | 142 | ||||
-rw-r--r-- | src/util.rs | 7 | ||||
-rw-r--r-- | tests/const.rs | 15 | ||||
-rw-r--r-- | tests/eq_size.rs | 113 | ||||
-rw-r--r-- | tests/fields.rs | 32 | ||||
-rw-r--r-- | tests/obj_safe.rs | 10 | ||||
-rw-r--r-- | tests/proc_size.rs | 9 | ||||
-rw-r--r-- | tests/trait_impl.rs | 71 | ||||
-rw-r--r-- | tests/type_eq.rs | 15 | ||||
-rw-r--r-- | tests/type_ne.rs | 7 |
34 files changed, 2738 insertions, 0 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..80d5507 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,181 @@ +# Changelog [![Crates.io][crate-badge]][crate] +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog] and this project adheres to +[Semantic Versioning]. + +## [Unreleased] + +## [1.1.0] - 2019-11-03 +### Added +- `assert_impl_any!` macro +- `assert_impl_one!` macro +- `assert_trait_sub_all!` macro +- `assert_trait_super_all!` macro +- Frequently asked questions to `README.md` + +### Fixed +- `assert_eq_size_val!`, `const_assert_eq!`, and `const_assert_ne!` to export + their local inner macros. Not having this prevented them from working when + `use`d or called directly via `static_assertions::macro!(...)` + +### Removed +- Unused `_assert_obj_safe!` from pre-1.0 + +## [1.0.0] - 2019-10-02 +### Added +- `assert_eq_align!` macro + +### Removed +- **[breaking]** Labels from macros that needed them 🎉 + - Made possible by [`const _`] in Rust 1.37 +- **[breaking]** `assert_impl!` macro + +### Fixed +- `assert_fields!` now works for `enum` types with multiple variants + +### Changed +- **[breaking]** `const_assert!` macro to only take one expression + - Reasoning: when custom error messages are added in the future (via + [`assert!`]), having the macro allow for multiple comma-separated + expressions may lead to ambiguity +- **[breaking]** Trait assertions to use `Type: Trait` syntax +- **[breaking]** Field assertions to use `Type: field1, field2` syntax +- **[breaking]** Renamed `assert_{ne,eq}_type!` to `assert_type_{ne,eq}_all!` + +## [0.3.4] - 2019-06-12 +### Changed +- Aliased `assert_impl!` to `assert_impl_all!` and deprecated `assert_impl!` + +### Added +- `assert_impl_all!` as replacement to `assert_impl!` +- `assert_not_impl_all!` and `assert_not_impl_any!` macro counterparts to + `assert_impl_all!` + +### Fixed +- `assert_eq_type!` now works with types involving lifetimes + +## [0.3.3] - 2019-06-12 +### Added +- `const_assert_ne!` macro counterpart to `const_assert_eq!` + +### Fixed +- `assert_eq_type!` would pass when types can coerce via `Deref`, such as with + `str` and `String` + +## [0.3.2] - 2019-05-15 +### Added +- A `assert_eq_type!` macro that allows for checking whether inputs are the same + concrete type +- A `assert_ne_type!` macro for checking whether inputs all refer to different + types + +### Fixed +- `const_assert!` now only takes `bool` values whereas integer (or other type) + values could previously be passed + +## [0.3.1] - 2018-11-15 +### Fixed +- Macros that refer to other internal macros can now be imported when compiling + for Rust 2018 ([issue + #10](https://github.com/nvzqz/static-assertions-rs/issues/10)) + +## [0.3.0] - 2018-11-14 +### Changed +- Bumped minimum supported (automatically tested) Rust version to 1.24.0 +- Moved message parameter for `assert_cfg!()` to last argument position, making + it consistent with other macros + +### Removed +- No need to use `macro!(label; ...)` syntax when compiling on nightly Rust and + enabling the `nightly` feature flag + +## [0.2.5] - 2017-12-12 +### Changed +- `assert_eq_size_ptr` wraps its code inside of a closure, ensuring that the + unsafe code inside never runs +- Clippy no longer warns about `unneeded_field_pattern` within `assert_fields` + +### Added +- Much better documentation with test examples that are guaranteed to fail at + compile-time + +### Removed +- Removed testing features; compile failure tests are now done via doc tests + +## [0.2.4] - 2017-12-11 +### Removed +- Removed the actual call to `mem::transmute` while still utilizing it for size + verification ([Simon Sapin], [#5]) + +### Added +- `assert_cfg` macro that asserts that the given configuration is set +- `assert_fields` macro to assert that a struct type or enum variant has a given + field + +### Fixed +- Allow more generics flexibility in `assert_impl` + +## [0.2.3] - 2017-08-24 +### Fixed +- Trailing commas are now allowed + +### Removed +- Removed clippy warnings + +## [0.2.2] - 2017-08-13 +### Added +- Added `assert_impl` macro to ensure a type implements a given set of traits + +## [0.2.1] - 2017-08-13 +### Added +- Added `assert_obj_safe` macro for ensuring that a trait is object-safe + +## [0.2.0] - 2017-08-12 +### Added +- Added `assert_eq_size_ptr` macro + +### Fixed +- Allow `assert_eq_size`, `const_assert`, and `const_assert_eq` in non-function + contexts via providing a unique label [#1] + +### Removed +- **[Breaking]** Semicolon-separated `assert_eq_size` is no longer allowed + +## [0.1.1] - 2017-08-12 +### Added +- Added `const_assert_eq` macro + +## 0.1.0 - 2017-08-12 + +Initial release + +[Simon Sapin]: https://github.com/SimonSapin + +[`assert!`]: https://doc.rust-lang.org/stable/std/macro.assert.html +[`const _`]: https://github.com/rust-lang/rfcs/blob/master/text/2526-const-wildcard.md + +[#1]: https://github.com/nvzqz/static-assertions-rs/issues/1 +[#5]: https://github.com/nvzqz/static-assertions-rs/pull/5 + +[crate]: https://crates.io/crates/static_assertions +[crate-badge]: https://img.shields.io/crates/v/static_assertions.svg + +[Keep a Changelog]: http://keepachangelog.com/en/1.0.0/ +[Semantic Versioning]: http://semver.org/spec/v2.0.0.html + +[Unreleased]: https://github.com/nvzqz/static-assertions-rs/compare/v1.1.0...HEAD +[1.1.0]: https://github.com/nvzqz/static-assertions-rs/compare/v1.0.0...v1.1.0 +[1.0.0]: https://github.com/nvzqz/static-assertions-rs/compare/v0.3.4...v1.0.0 +[0.3.4]: https://github.com/nvzqz/static-assertions-rs/compare/v0.3.3...v0.3.4 +[0.3.3]: https://github.com/nvzqz/static-assertions-rs/compare/v0.3.2...v0.3.3 +[0.3.2]: https://github.com/nvzqz/static-assertions-rs/compare/v0.3.1...v0.3.2 +[0.3.1]: https://github.com/nvzqz/static-assertions-rs/compare/v0.3.0...v0.3.1 +[0.3.0]: https://github.com/nvzqz/static-assertions-rs/compare/v0.2.5...v0.3.0 +[0.2.5]: https://github.com/nvzqz/static-assertions-rs/compare/v0.2.4...v0.2.5 +[0.2.4]: https://github.com/nvzqz/static-assertions-rs/compare/v0.2.3...v0.2.4 +[0.2.3]: https://github.com/nvzqz/static-assertions-rs/compare/v0.2.2...v0.2.3 +[0.2.2]: https://github.com/nvzqz/static-assertions-rs/compare/v0.2.1...v0.2.2 +[0.2.1]: https://github.com/nvzqz/static-assertions-rs/compare/v0.2.0...v0.2.1 +[0.2.0]: https://github.com/nvzqz/static-assertions-rs/compare/v0.1.1...v0.2.0 +[0.1.1]: https://github.com/nvzqz/static-assertions-rs/compare/v0.1.0...v0.1.1 diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..39a4793 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "static_assertions" +version = "1.1.0" +authors = ["Nikolai Vazquez"] +license = "MIT OR Apache-2.0" +readme = "README.md" +homepage = "https://github.com/nvzqz/static-assertions-rs" +repository = "https://github.com/nvzqz/static-assertions-rs" +documentation = "https://docs.rs/static_assertions/" +categories = ["no-std", "rust-patterns", "development-tools::testing"] +keywords = ["assert", "static", "testing"] +description = "Compile-time assertions to ensure that invariants are met." +include = ["Cargo.toml", "src/**/*.rs", "README.md", "CHANGELOG.md", "LICENSE*"] + +[dependencies.proc_static_assertions] +version = "0.0.0" +path = "proc" +optional = true + +[workspace] +members = ["proc"] + +[features] +nightly = [] +proc = ["proc_static_assertions"] + +[badges] +maintenance = { status = "actively-maintained" } diff --git a/Cargo.toml.orig b/Cargo.toml.orig new file mode 100644 index 0000000..30248fc --- /dev/null +++ b/Cargo.toml.orig @@ -0,0 +1,22 @@ +[package] +name = "static_assertions" +version = "1.1.0" +authors = ["Nikolai Vazquez"] +license = "MIT OR Apache-2.0" +readme = "README.md" +homepage = "https://github.com/nvzqz/static-assertions-rs" +repository = "https://github.com/nvzqz/static-assertions-rs" +documentation = "https://docs.rs/static_assertions/" +categories = ["no-std", "rust-patterns", "development-tools::testing"] +keywords = ["assert", "static", "testing"] +description = "Compile-time assertions to ensure that invariants are met." +include = ["Cargo.toml", "src/**/*.rs", "README.md", "CHANGELOG.md", "LICENSE*"] + +[badges] +travis-ci = { repository = "nvzqz/static-assertions-rs" } +is-it-maintained-open-issues = { repository = "nvzqz/static-assertions-rs" } +is-it-maintained-issue-resolution = { repository = "nvzqz/static-assertions-rs" } +maintenance = { status = "passively-maintained" } + +[features] +nightly = [] @@ -0,0 +1 @@ +LICENSE-APACHE
\ No newline at end of file diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,202 @@ + + 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/METADATA b/METADATA new file mode 100644 index 0000000..09317f1 --- /dev/null +++ b/METADATA @@ -0,0 +1,20 @@ +name: "static_assertions" +description: "Compile-time assertions to ensure that invariants are met." +third_party { + url { + type: HOMEPAGE + value: "https://crates.io/crates/static_assertions" + } + url { + type: ARCHIVE + value: "https://static.crates.io/crates/static_assertions/static_assertions-1.1.0.crate" + } + version: "1.1.0" + # Dual-licensed, using the least restrictive per go/thirdpartylicenses#same. + license_type: NOTICE + last_upgrade_date { + year: 2022 + month: 11 + day: 2 + } +} diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/MODULE_LICENSE_APACHE2 @@ -0,0 +1 @@ +include platform/prebuilts/rust:master:/OWNERS diff --git a/README.md b/README.md new file mode 100644 index 0000000..75ddd70 --- /dev/null +++ b/README.md @@ -0,0 +1,188 @@ +[![Banner](https://raw.githubusercontent.com/nvzqz/static-assertions-rs/assets/Banner.png)](https://github.com/nvzqz/static-assertions-rs) + +<div align="center"> + <a href="https://crates.io/crates/static_assertions"> + <img src="https://img.shields.io/crates/v/static_assertions.svg" alt="Crates.io"> + <img src="https://img.shields.io/crates/d/static_assertions.svg" alt="Downloads"> + </a> + <a href="https://github.com/nvzqz/static-assertions-rs/actions?query=workflow%3ACI"> + <img src="https://github.com/nvzqz/static-assertions-rs/workflows/CI/badge.svg" alt="Build Status"> + </a> + <img src="https://img.shields.io/badge/rustc-^1.37.0-blue.svg" alt="rustc ^1.37.0"> + <br> + <a href="https://www.patreon.com/nvzqz"> + <img src="https://c5.patreon.com/external/logo/become_a_patron_button.png" alt="Become a Patron!" height="35"> + </a> + <a href="https://www.paypal.me/nvzqz"> + <img src="https://buymecoffee.intm.org/img/button-paypal-white.png" alt="Buy me a coffee" height="35"> + </a> +</div> + +Compile-time assertions for Rust, brought to you by +[Nikolai Vazquez](https://twitter.com/NikolaiVazquez). + +This library lets you ensure correct assumptions about constants, types, and +more. See the [docs] and [FAQ](#faq) for more info! + +## Installation + +This crate is available +[on crates.io](https://crates.io/crates/static_assertions) and can be used by +adding the following to your project's +[`Cargo.toml`](https://doc.rust-lang.org/cargo/reference/manifest.html): + +```toml +[dependencies] +static_assertions = "1.1.0" +``` + +and this to your crate root (`main.rs` or `lib.rs`): + +```rust +#[macro_use] +extern crate static_assertions; +``` + +## Usage + +This crate exposes the following macros: +- [`assert_cfg!`] +- [`assert_eq_align!`] +- [`assert_eq_size!`] +- [`assert_eq_size_ptr!`] +- [`assert_eq_size_val!`] +- [`assert_fields!`] +- [`assert_impl_all!`] +- [`assert_impl_any!`] +- [`assert_impl_one!`] +- [`assert_not_impl_all!`] +- [`assert_not_impl_any!`] +- [`assert_obj_safe!`] +- [`assert_trait_sub_all!`] +- [`assert_trait_super_all!`] +- [`assert_type_eq_all!`] +- [`assert_type_ne_all!`] +- [`const_assert!`] +- [`const_assert_eq!`] +- [`const_assert_ne!`] + +## FAQ + +- **Q:** When would I want to use this? + + **A:** This library is useful for when wanting to ensure properties of + constants, types, and traits. + + Basic examples: + + - With the release of 1.39, `str::len` can be called in a `const` + context. Using [`const_assert!`], one can check that a string generated from + elsewhere is of a given size: + + ```rust + const DATA: &str = include_str!("path/to/string.txt"); + + const_assert!(DATA.len() < 512); + ``` + + - Have a type that absolutely must implement certain traits? With + [`assert_impl_all!`], one can ensure this: + + ```rust + struct Foo { + value: // ... + } + + assert_impl_all!(Foo: Send, Sync); + ``` + +- **Q:** How can I contribute? + + **A:** A couple of ways! You can: + + - Attempt coming up with some form of static analysis that you'd like to see + implemented. Create a [new issue] and describe how you'd imagine your + assertion to work, with example code to demonstrate. + + - Implement your own static assertion and create a [pull request]. + + - Give feedback. What are some pain points? Where is it unpleasant? + + - Write docs. If you're familiar with how this library works, sharing your + knowledge with the rest its users would be great! + +- **Q:** Will this affect my compiled binary? + + **A:** Nope! There is zero runtime cost to using this because all checks are + at compile-time, and so no code is emitted to run. + +- **Q:** Will this affect my compile times? + + **A:** Likely not by anything perceivable. If this is a concern, this library + can be put in `dev-dependencies`: + + ```toml + [dev-dependencies] + static_assertions = "1.1.0" + ``` + + and then assertions can be conditionally run behind `#[cfg(test)]`: + + ```rust + #[cfg(test)] + const_assert_eq!(MEANING_OF_LIFE, 42); + ``` + + However, the assertions will only be checked when running `cargo test`. This + somewhat defeats the purpose of catching false static conditions up-front with + a compilation failure. + +- **Q:** What is `const _`? + + **A:** It's a way of creating an unnamed constant. This is used so that macros + can be called from a global scope without requiring a scope-unique label. This + library makes use of the side effects of evaluating the `const` expression. + See the feature's + [tracking issue](https://github.com/rust-lang/rust/issues/54912) + and + [issue #1](https://github.com/nvzqz/static-assertions-rs/issues/1) + for more info. + +## Changes + +See [`CHANGELOG.md`](https://github.com/nvzqz/static-assertions-rs/blob/master/CHANGELOG.md) +for a complete list of what has changed from one version to another. + +## License + +This project is released under either: + +- [MIT License](https://github.com/nvzqz/static-assertions-rs/blob/master/LICENSE-MIT) + +- [Apache License (Version 2.0)](https://github.com/nvzqz/static-assertions-rs/blob/master/LICENSE-APACHE) + +at your choosing. + +[new issue]: https://github.com/nvzqz/static-assertions-rs/issues/new +[pull request]: https://github.com/nvzqz/static-assertions-rs/pulls +[docs]: https://docs.rs/static_assertions + +[`assert_cfg!`]: https://docs.rs/static_assertions/1.1.0/static_assertions/macro.assert_cfg.html +[`assert_eq_align!`]: https://docs.rs/static_assertions/1.1.0/static_assertions/macro.assert_eq_align.html +[`assert_eq_size!`]: https://docs.rs/static_assertions/1.1.0/static_assertions/macro.assert_eq_size.html +[`assert_eq_size_ptr!`]: https://docs.rs/static_assertions/1.1.0/static_assertions/macro.assert_eq_size_ptr.html +[`assert_eq_size_val!`]: https://docs.rs/static_assertions/1.1.0/static_assertions/macro.assert_eq_size_val.html +[`assert_fields!`]: https://docs.rs/static_assertions/1.1.0/static_assertions/macro.assert_fields.html +[`assert_impl_all!`]: https://docs.rs/static_assertions/1.1.0/static_assertions/macro.assert_impl_all.html +[`assert_impl_any!`]: https://docs.rs/static_assertions/1.1.0/static_assertions/macro.assert_impl_any.html +[`assert_impl_one!`]: https://docs.rs/static_assertions/1.1.0/static_assertions/macro.assert_impl_one.html +[`assert_not_impl_all!`]: https://docs.rs/static_assertions/1.1.0/static_assertions/macro.assert_not_impl_all.html +[`assert_not_impl_any!`]: https://docs.rs/static_assertions/1.1.0/static_assertions/macro.assert_not_impl_any.html +[`assert_obj_safe!`]: https://docs.rs/static_assertions/1.1.0/static_assertions/macro.assert_obj_safe.html +[`assert_trait_sub_all!`]: https://docs.rs/static_assertions/1.1.0/static_assertions/macro.assert_trait_sub_all.html +[`assert_trait_super_all!`]: https://docs.rs/static_assertions/1.1.0/static_assertions/macro.assert_trait_super_all.html +[`assert_type_eq_all!`]: https://docs.rs/static_assertions/1.1.0/static_assertions/macro.assert_type_eq_all.html +[`assert_type_ne_all!`]: https://docs.rs/static_assertions/1.1.0/static_assertions/macro.assert_type_ne_all.html +[`const_assert!`]: https://docs.rs/static_assertions/1.1.0/static_assertions/macro.const_assert.html +[`const_assert_eq!`]: https://docs.rs/static_assertions/1.1.0/static_assertions/macro.const_assert_eq.html +[`const_assert_ne!`]: https://docs.rs/static_assertions/1.1.0/static_assertions/macro.const_assert_ne.html diff --git a/proc/Cargo.toml b/proc/Cargo.toml new file mode 100644 index 0000000..d1f7d69 --- /dev/null +++ b/proc/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "proc_static_assertions" +version = "0.0.0" +authors = ["Nikolai Vazquez"] +license = "MIT OR Apache-2.0" +readme = "../README.md" +homepage = "https://github.com/nvzqz/static-assertions-rs" +repository = "https://github.com/nvzqz/static-assertions-rs" +documentation = "https://docs.rs/proc_static_assertions/" +categories = ["no-std", "rust-patterns", "development-tools::testing"] +keywords = ["assert", "static", "testing"] +description = "Compile-time assertions via procedural macros." +include = ["Cargo.toml", "src/**/*.rs", "README.md", "CHANGELOG.md", "LICENSE*"] + +[lib] +proc-macro = true + +[badges] +travis-ci = { repository = "nvzqz/static-assertions-rs" } +is-it-maintained-open-issues = { repository = "nvzqz/static-assertions-rs" } +is-it-maintained-issue-resolution = { repository = "nvzqz/static-assertions-rs" } +maintenance = { status = "passively-maintained" } + +[features] +nightly = [] diff --git a/proc/src/lib.rs b/proc/src/lib.rs new file mode 100644 index 0000000..4812c3c --- /dev/null +++ b/proc/src/lib.rs @@ -0,0 +1,92 @@ +//! [![Banner](https://raw.githubusercontent.com/nvzqz/static-assertions-rs/assets/Banner.png)](https://github.com/nvzqz/static-assertions-rs) +//! +//! <div align="center"> +//! <a href="https://crates.io/crates/proc_static_assertions"> +//! <img src="https://img.shields.io/crates/d/proc_static_assertions.svg" alt="Downloads"> +//! </a> +//! <a href="https://travis-ci.org/nvzqz/static-assertions-rs"> +//! <img src="https://travis-ci.org/nvzqz/static-assertions-rs.svg?branch=master" alt="Build Status"> +//! </a> +//! <br><br> +//! </div> +//! +//! Procedural macro [compile-time] assertions as an extension of +//! [`static_assertions`]. +//! +//! # Usage +//! +//! There's two main ways of using this crate: as a direct dependency or +//! indirect dependency (via [`static_assertions`]). +//! +//! ## Direct Dependency +//! +//! This crate is available [on crates.io][crate] and can be used by adding the +//! following to your project's [`Cargo.toml`]: +//! +//! ```toml +//! [dependencies] +//! proc_static_assertions = "0.0.0" +//! ``` +//! +//! and this to your crate root (`main.rs` or `lib.rs`): +//! +//! ``` +//! #[macro_use] +//! extern crate proc_static_assertions; +//! # fn main() {} +//! ``` +//! +//! ## Indirect Dependency +//! +//! Add the following to your project's [`Cargo.toml`]: +//! +//! ```toml +//! [dependencies] +//! static_assertions = { version = "1.1.0", features = ["proc"] } +//! ``` +//! +//! and this to your crate root (`main.rs` or `lib.rs`): +//! +//! ```ignore +//! #[macro_use] +//! extern crate static_assertions; +//! ``` +//! +//! This will also import all macros in `proc_static_assertions`. +//! +//! # Donate +//! +//! This project is made freely available (as in free beer), but unfortunately +//! not all beer is free! So, if you would like to buy me a beer (or coffee or +//! *more*), then consider supporting my work that's benefited your project +//! and thousands of others. +//! +//! <a href="https://www.patreon.com/nvzqz"> +//! <img src="https://c5.patreon.com/external/logo/become_a_patron_button.png" alt="Become a Patron!" height="35"> +//! </a> +//! <a href="https://www.paypal.me/nvzqz"> +//! <img src="https://buymecoffee.intm.org/img/button-paypal-white.png" alt="Buy me a coffee" height="35"> +//! </a> +//! +//! [`static_assertions`]: https://github.com/nvzqz/static-assertions-rs +//! [crate]: https://crates.io/crates/static_assertions +//! [`Cargo.toml`]: https://doc.rust-lang.org/cargo/reference/manifest.html +//! [compile-time]: https://en.wikipedia.org/wiki/Compile_time + +#![doc(html_root_url = "https://docs.rs/proc_static_assertions/0.0.0")] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/nvzqz/static-assertions-rs/assets/Icon.png" +)] +#![deny(missing_docs)] + +extern crate proc_macro; +use proc_macro::TokenStream; + +/// Statically assert aspects of types, traits, and more. +/// +/// This currently does nothing. Create an issue if you have ideas for what this +/// could do! +#[proc_macro_attribute] +pub fn assert(_attr: TokenStream, _item: TokenStream) -> TokenStream { + TokenStream::new() +} diff --git a/proc/tests/size.rs b/proc/tests/size.rs new file mode 100644 index 0000000..1550547 --- /dev/null +++ b/proc/tests/size.rs @@ -0,0 +1,7 @@ +#[macro_use] +extern crate proc_static_assertions; + +#[assert(size == 4, align == 4)] +struct Foo { + value: i32, +} diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..c9cc71a --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,15 @@ +max_width = 80 +hard_tabs = false +tab_spaces = 4 +newline_style = "Unix" +use_small_heuristics = "Default" +reorder_imports = true +reorder_modules = true +remove_nested_parens = true +fn_args_layout = "Tall" +edition = "2018" +merge_derives = true +use_try_shorthand = false +use_field_init_shorthand = true +force_explicit_abi = true +print_misformatted_file_names = true diff --git a/src/assert_align.rs b/src/assert_align.rs new file mode 100644 index 0000000..f1de642 --- /dev/null +++ b/src/assert_align.rs @@ -0,0 +1,200 @@ +/// Asserts that the types' alignments are equal. +/// +/// This is useful when ensuring that pointer arithmetic is done correctly, or +/// when [FFI] requires a type to have the same alignment as some foreign type. +/// +/// # Examples +/// +/// A `usize` has the same alignment as any pointer type: +/// +/// ``` +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// assert_align_eq!(usize, *const u8, *mut u8); +/// ``` +/// +/// The following passes because `[i32; 4]` has the same alignment as `i32`: +/// +/// ``` +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// assert_align_eq!([i32; 4], i32); +/// ``` +/// +/// The following example fails to compile because `i32x4` explicitly has 4 +/// times the alignment as `[i32; 4]`: +/// +/// ```compile_fail +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// # #[allow(non_camel_case_types)] +/// #[repr(align(16))] +/// struct i32x4([i32; 4]); +/// +/// assert_align_eq!(i32x4, [i32; 4]); +/// ``` +/// +/// [FFI]: https://en.wikipedia.org/wiki/Foreign_function_interface +#[macro_export(local_inner_macros)] +macro_rules! assert_align_eq { + ($x:ty, $($y:ty),+ $(,)?) => { + const _: fn() = || { + use $crate::_core::mem::align_of; + const_assert_eq_usize!(align_of::<$x>() $(, align_of::<$y>())+); + }; + }; +} + +/// Asserts that the types' alignments are equal. +/// +/// This macro has been deprecated in favor of +/// [`assert_align_eq!`](macro.assert_align_eq.html). +#[deprecated( + since = "1.2.0", + note = "Please use the 'assert_align_eq' macro instead" +)] +#[macro_export(local_inner_macros)] +macro_rules! assert_eq_align { + ($($t:tt)*) => { + assert_align_eq!($($t)*); + }; +} + +/// Asserts that the types' alignments are **not** equal. +/// +/// # Examples +/// +/// A `u8` does not have the same alignment as a pointer: +/// +/// ``` +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// assert_align_ne!(u8, *const u8); +/// ``` +/// +/// The following example fails to compile because a `usize` has the same +/// alignment as a pointer: +/// +/// ```compile_fail +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// assert_align_ne!(*const u8, usize); +/// ``` +#[macro_export(local_inner_macros)] +macro_rules! assert_align_ne { + ($x:ty, $($y:ty),+ $(,)?) => { + const _: fn() = || { + use $crate::_core::mem::align_of; + const_assert_ne!(align_of::<$x>() $(, align_of::<$y>())+); + }; + }; +} + +/// Asserts that the types' alignments are less than each other. +/// +/// # Examples +/// +/// A `u8` has smaller alignment than `u16`, which has smaller alignment than +/// a pointer: +/// +/// ``` +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// assert_align_lt!(u8, u16, *const u8); +/// ``` +/// +/// The following example fails to compile because a `usize` has the same +/// alignment as a pointer: +/// +/// ```compile_fail +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// assert_align_lt!(*const u8, usize); +/// ``` +#[macro_export(local_inner_macros)] +macro_rules! assert_align_lt { + ($x:ty, $($y:ty),+ $(,)?) => { + const _: fn() = || { + use $crate::_core::mem::align_of; + const_assert_lt!(align_of::<$x>() $(, align_of::<$y>())+); + }; + }; +} + +/// Asserts that the types' alignments are less than or equal to each other. +/// +/// # Examples +/// +/// A `u8` and `i8` have smaller alignment than any pointer type: +/// +/// ``` +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// assert_align_le!(u8, i8, *const u8); +/// ``` +/// +/// The following example fails to compile because a `usize` has greater +/// alignment than `u8`: +/// +/// ```compile_fail +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// assert_align_le!(usize, u8); +/// ``` +#[macro_export(local_inner_macros)] +macro_rules! assert_align_le { + ($x:ty, $($y:ty),+ $(,)?) => { + const _: fn() = || { + use $crate::_core::mem::align_of; + const_assert_le!(align_of::<$x>() $(, align_of::<$y>())+); + }; + }; +} + +/// Asserts that the types' alignments are greater than each other. +/// +/// # Examples +/// +/// A pointer has greater alignment than `u16`, which has greater alignment than +/// `u8`: +/// +/// ``` +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// assert_align_gt!(*const u8, u16, u8); +/// ``` +/// +/// The following example fails to compile because a `usize` has the same +/// alignment as a pointer: +/// +/// ```compile_fail +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// assert_align_gt!(*const u8, usize); +/// ``` +#[macro_export(local_inner_macros)] +macro_rules! assert_align_gt { + ($x:ty, $($y:ty),+ $(,)?) => { + const _: fn() = || { + use $crate::_core::mem::align_of; + const_assert_gt!(align_of::<$x>() $(, align_of::<$y>())+); + }; + }; +} + +/// Asserts that the types' alignments are greater than or equal to each other. +/// +/// # Examples +/// +/// A pointer has greater alignment than `u8` and `i8`: +/// +/// ``` +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// assert_align_ge!(*const u8, u8, i8); +/// ``` +/// +/// The following example fails to compile because a `u8` has smaller alignment +/// than `usize`: +/// +/// ```compile_fail +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// assert_align_ge!(u8, usize); +/// ``` +#[macro_export(local_inner_macros)] +macro_rules! assert_align_ge { + ($x:ty, $($y:ty),+ $(,)?) => { + const _: fn() = || { + use $crate::_core::mem::align_of; + const_assert_ge!(align_of::<$x>() $(, align_of::<$y>())+); + }; + }; +} diff --git a/src/assert_cfg.rs b/src/assert_cfg.rs new file mode 100644 index 0000000..24282c1 --- /dev/null +++ b/src/assert_cfg.rs @@ -0,0 +1,49 @@ +/// Asserts that a given configuration is set. +/// +/// # Examples +/// +/// A project will simply fail to compile if the given configuration is not set. +/// +/// ``` +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// // We're not masochists +/// # #[cfg(not(target_pointer_width = "16"))] // Just in case +/// assert_cfg!(not(target_pointer_width = "16")); +/// ``` +/// +/// If a project does not support a set of configurations, you may want to +/// report why. There is the option of providing a compile error message string: +/// +/// ``` +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// # #[cfg(any(unix, windows))] +/// assert_cfg!(any(unix, windows), "There is only support for Unix or Windows"); +/// +/// // User needs to specify a database back-end +/// # #[cfg(target_pointer_width = "0")] // Impossible +/// assert_cfg!(all(not(all(feature = "mysql", feature = "mongodb")), +/// any( feature = "mysql", feature = "mongodb")), +/// "Must exclusively use MySQL or MongoDB as database back-end"); +/// ``` +/// +/// Some configurations are impossible. For example, we can't be compiling for +/// both macOS _and_ Windows simultaneously: +/// +/// ```compile_fail +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// assert_cfg!(all(target_os = "macos", +/// target_os = "windows"), +/// "No, that's not how it works! ಠ_ಠ"); +/// ``` +#[macro_export] +macro_rules! assert_cfg { + () => {}; + ($($cfg:meta)+, $msg:expr $(,)?) => { + #[cfg(not($($cfg)+))] + compile_error!($msg); + }; + ($($cfg:tt)*) => { + #[cfg(not($($cfg)*))] + compile_error!(concat!("Cfg does not pass: ", stringify!($($cfg)*))); + }; +} diff --git a/src/assert_fields.rs b/src/assert_fields.rs new file mode 100644 index 0000000..00f6242 --- /dev/null +++ b/src/assert_fields.rs @@ -0,0 +1,72 @@ +/// Asserts that the type has the given fields. +/// +/// # Examples +/// +/// One common use case is when types have fields defined multiple times as a +/// result of `#[cfg]`. This can be an issue when exposing a public API. +/// +/// ``` +/// # #[macro_use] extern crate static_assertions; +/// pub struct Ty { +/// #[cfg(windows)] +/// pub val1: u8, +/// #[cfg(not(windows))] +/// pub val1: usize, +/// +/// #[cfg(unix)] +/// pub val2: u32, +/// #[cfg(not(unix))] +/// pub val2: usize, +/// } +/// +/// // Always have `val2` regardless of OS +/// assert_fields!(Ty: val2); +/// ``` +/// +/// This macro even works with `enum` variants: +/// +/// ``` +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// enum Data { +/// Val { +/// id: i32, +/// name: String, +/// bytes: [u8; 128], +/// }, +/// Ptr(*const u8), +/// } +/// +/// assert_fields!(Data::Val: id, bytes); +/// ``` +/// +/// The following example fails to compile because [`Range`] does not have a field named `middle`: +/// +/// ```compile_fail +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// use std::ops::Range; +/// +/// assert_fields!(Range<u32>: middle); +/// ``` +/// +/// [`Range`]: https://doc.rust-lang.org/std/ops/struct.Range.html +#[macro_export] +macro_rules! assert_fields { + ($t:ident::$v:ident: $($f:ident),+) => { + #[allow(unknown_lints, unneeded_field_pattern)] + const _: fn() = || { + #[allow(dead_code, unreachable_patterns)] + fn assert(value: $t) { + match value { + $($t::$v { $f: _, .. } => {},)+ + _ => {} + } + } + }; + }; + ($t:path: $($f:ident),+) => { + #[allow(unknown_lints, unneeded_field_pattern)] + const _: fn() = || { + $(let $t { $f: _, .. };)+ + }; + }; +} diff --git a/src/assert_impl.rs b/src/assert_impl.rs new file mode 100644 index 0000000..83545ca --- /dev/null +++ b/src/assert_impl.rs @@ -0,0 +1,385 @@ +/// Asserts that the type implements exactly one in a set of traits. +/// +/// Related: +/// - [`assert_impl_any!`] +/// - [`assert_impl_all!`] +/// - [`assert_impl_not_all!`] +/// - [`assert_impl_not_any!`] +/// +/// # Examples +/// +/// Given some type `Foo`, it is expected to implement either `Snap`, `Crackle`, +/// or `Pop`: +/// +/// ```compile_fail +/// # use static_assertions::assert_impl_one; fn main() {} +/// struct Foo; +/// +/// trait Snap {} +/// trait Crackle {} +/// trait Pop {} +/// +/// assert_impl_one!(Foo: Snap, Crackle, Pop); +/// ``` +/// +/// If _only_ `Crackle` is implemented, the assertion passes: +/// +/// ``` +/// # use static_assertions::assert_impl_one; fn main() {} +/// # struct Foo; +/// # trait Snap {} +/// # trait Crackle {} +/// # trait Pop {} +/// impl Crackle for Foo {} +/// +/// assert_impl_one!(Foo: Snap, Crackle, Pop); +/// ``` +/// +/// If `Snap` or `Pop` is _also_ implemented, the assertion fails: +/// +/// ```compile_fail +/// # use static_assertions::assert_impl_one; fn main() {} +/// # struct Foo; +/// # trait Snap {} +/// # trait Crackle {} +/// # trait Pop {} +/// # impl Crackle for Foo {} +/// impl Pop for Foo {} +/// +/// assert_impl_one!(Foo: Snap, Crackle, Pop); +/// ``` +/// +/// [`assert_impl_any!`]: macro.assert_impl_any.html +/// [`assert_impl_all!`]: macro.assert_impl_all.html +/// [`assert_impl_not_all!`]: macro.assert_not_impl_all.html +/// [`assert_impl_not_any!`]: macro.assert_not_impl_any.html +#[macro_export] +macro_rules! assert_impl_one { + ($x:ty: $($t:path),+ $(,)?) => { + const _: fn() = || { + // Generic trait that must be implemented for `$x` exactly once. + trait AmbiguousIfMoreThanOne<A> { + // Required for actually being able to reference the trait. + fn some_item() {} + } + + // Creates multiple scoped `Token` types for each trait `$t`, over + // which a specialized `AmbiguousIfMoreThanOne<Token>` is + // implemented for every type that implements `$t`. + $({ + #[allow(dead_code)] + struct Token; + + impl<T: ?Sized + $t> AmbiguousIfMoreThanOne<Token> for T {} + })+ + + // If there is only one specialized trait impl, type inference with + // `_` can be resolved and this can compile. Fails to compile if + // `$x` implements more than one `AmbiguousIfMoreThanOne<Token>` or + // does not implement any at all. + let _ = <$x as AmbiguousIfMoreThanOne<_>>::some_item; + }; + }; +} + +/// Asserts that the type implements _all_ of the given traits. +/// +/// See [`assert_impl_not_all!`] for achieving the opposite effect. +/// +/// # Examples +/// +/// This can be used to ensure types implement auto traits such as [`Send`] and +/// [`Sync`], as well as traits with [blanket `impl`s][blanket]. +/// +/// ``` +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// assert_impl_all!(u32: Copy, Send); +/// assert_impl_all!(&str: Into<String>); +/// ``` +/// +/// The following example fails to compile because raw pointers do not implement +/// [`Send`] since they cannot be moved between threads safely: +/// +/// ```compile_fail +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// assert_impl_all!(*const u8: Send); +/// ``` +/// +/// [`assert_impl_not_all!`]: macro.assert_not_impl_all.html +/// [`Send`]: https://doc.rust-lang.org/std/marker/trait.Send.html +/// [`Sync`]: https://doc.rust-lang.org/std/marker/trait.Sync.html +/// [blanket]: https://doc.rust-lang.org/book/ch10-02-traits.html#using-trait-bounds-to-conditionally-implement-methods +#[macro_export(local_inner_macros)] +macro_rules! assert_impl_all { + ($ty:ty: $($traits:path),+ $(,)?) => { + assert_impl!($ty: $( ($traits) )&+); + }; +} + +/// Asserts that the type implements _any_ of the given traits. +/// +/// See [`assert_impl_not_any!`] for achieving the opposite effect. +/// +/// # Examples +/// +/// `u8` cannot be converted from `u16`, but it can be converted into `u16`: +/// +/// ``` +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// assert_impl_any!(u8: From<u16>, Into<u16>); +/// ``` +/// +/// The unit type cannot be converted from `u8` or `u16`, but it does implement +/// [`Send`]: +/// +/// ``` +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// assert_impl_any!((): From<u8>, From<u16>, Send); +/// ``` +/// +/// The following example fails to compile because raw pointers do not implement +/// [`Send`] or [`Sync`] since they cannot be moved or shared between threads +/// safely: +/// +/// ```compile_fail +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// assert_impl_any!(*const u8: Send, Sync); +/// ``` +/// +/// [`assert_impl_not_any!`]: macro.assert_not_impl_any.html +/// [`Send`]: https://doc.rust-lang.org/std/marker/trait.Send.html +/// [`Sync`]: https://doc.rust-lang.org/std/marker/trait.Sync.html +#[macro_export(local_inner_macros)] +macro_rules! assert_impl_any { + ($ty:ty: $($traits:path),+ $(,)?) => { + assert_impl!($ty: $( ($traits) )|+); + }; +} + +/// Asserts that the type does **not** implement _all_ of the given traits. +/// +/// This can be used to ensure types do not implement auto traits such as +/// [`Send`] and [`Sync`], as well as traits with [blanket `impl`s][blanket]. +/// +/// Note that the combination of all provided traits is required to not be +/// implemented. If you want to check that none of multiple traits are +/// implemented you should invoke [`assert_impl_not_any!`] instead. +/// +/// # Examples +/// +/// Although `u32` implements `From<u16>`, it does not implement `Into<usize>`: +/// +/// ``` +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// assert_impl_not_all!(u32: From<u16>, Into<usize>); +/// ``` +/// +/// The following example fails to compile since `u32` can be converted into +/// `u64`. +/// +/// ```compile_fail +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// assert_impl_not_all!(u32: Into<u64>); +/// ``` +/// +/// The following compiles because [`Cell`] is not both [`Sync`] _and_ [`Send`]: +/// +/// ``` +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// use std::cell::Cell; +/// +/// assert_impl_not_all!(Cell<u32>: Sync, Send); +/// ``` +/// +/// But it is [`Send`], so this fails to compile: +/// +/// ```compile_fail +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// # std::cell::Cell; +/// assert_impl_not_all!(Cell<u32>: Send); +/// ``` +/// +/// [`Send`]: https://doc.rust-lang.org/std/marker/trait.Send.html +/// [`Sync`]: https://doc.rust-lang.org/std/marker/trait.Sync.html +/// [`assert_impl_not_any!`]: macro.assert_impl_not_any.html +/// [`Cell`]: https://doc.rust-lang.org/std/cell/struct.Cell.html +/// [blanket]: https://doc.rust-lang.org/book/ch10-02-traits.html#using-trait-bounds-to-conditionally-implement-methods +#[macro_export(local_inner_macros)] +macro_rules! assert_impl_not_all { + ($ty:ty: $($traits:path),+ $(,)?) => { + assert_impl!($ty: !( $( ($traits) )&+ )); + }; +} + +/// Asserts that the type does **not** implement _all_ of the given traits. +/// +/// This macro has been deprecated in favor of +/// [`assert_impl_not_all!`](macro.assert_impl_not_all.html). +#[deprecated( + since = "1.2.0", + note = "Please use the 'assert_impl_not_all' macro instead" +)] +#[macro_export(local_inner_macros)] +macro_rules! assert_not_impl_all { + ($($t:tt)*) => { + assert_impl_not_all!($($t)*); + }; +} + +/// Asserts that the type does **not** implement _any_ of the given traits. +/// +/// This can be used to ensure types do not implement auto traits such as +/// [`Send`] and [`Sync`], as well as traits with [blanket `impl`s][blanket]. +/// +/// This macro causes a compilation failure if any of the provided individual +/// traits are implemented for the type. If you want to check that a combination +/// of traits is not implemented you should invoke [`assert_impl_not_all!`] +/// instead. For single traits both macros behave the same. +/// +/// # Examples +/// +/// If `u32` were to implement `Into` conversions for `usize` _and_ for `u8`, +/// the following would fail to compile: +/// +/// ``` +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// assert_impl_not_any!(u32: Into<usize>, Into<u8>); +/// ``` +/// +/// This is also good for simple one-off cases: +/// +/// ``` +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// assert_impl_not_any!(&'static mut u8: Copy); +/// ``` +/// +/// The following example fails to compile since `u32` can be converted into +/// `u64` even though it can not be converted into a `u16`: +/// +/// ```compile_fail +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// assert_impl_not_any!(u32: Into<u64>, Into<u16>); +/// ``` +/// +/// [`Send`]: https://doc.rust-lang.org/std/marker/trait.Send.html +/// [`Sync`]: https://doc.rust-lang.org/std/marker/trait.Sync.html +/// [`assert_impl_not_all!`]: macro.assert_impl_not_all.html +/// [blanket]: https://doc.rust-lang.org/book/ch10-02-traits.html#using-trait-bounds-to-conditionally-implement-methods +#[macro_export(local_inner_macros)] +macro_rules! assert_impl_not_any { + ($ty:ty: $($traits:path),+ $(,)?) => { + assert_impl!($ty: !( $( ($traits) )|+ )); + }; +} + +/// Asserts that the type does **not** implement _any_ of the given traits. +/// +/// This macro has been deprecated in favor of +/// [`assert_impl_not_any!`](macro.assert_impl_not_any.html). +#[deprecated( + since = "1.2.0", + note = "Please use the 'assert_impl_not_any' macro instead" +)] +#[macro_export(local_inner_macros)] +macro_rules! assert_not_impl_any { + ($($t:tt)*) => { + assert_impl_not_any!($($t)*); + }; +} + +/// Asserts that the type implements a logical trait expression. +/// +/// This macro causes a compilation failure if the expression is not satisfied. +/// +/// See [`does_impl!`](macro.does_impl.html) for simply getting a [`bool`] from +/// this condition without asserting it. +/// +/// # Syntax +/// +/// ```skip +/// assert_impl!(<type>: <trait_expr>); +/// assert_impl!(for(<type>: <bounds>) <type>: <trait_expr>); +/// ``` +/// +/// where: +/// +/// - `<type>` is a type (that must not depend on a generic parameter) +/// +/// - `<trait_expr>` is an expression made out of trait names, combined with `!` +/// for negation, `&` for conjunction, `|` for disjunction and parentheses for +/// grouping. +/// +/// - `<bounds>` is a trait bounds expression. +/// +/// For technical reasons: +/// +/// - Traits (like `Into<u8>`) that are not a single identifier must be +/// surrounded by parentheses. +/// +/// - The usual operator priority is not respected: `x & y | z` is parsed as +/// `x & (y | z)`. +/// +/// # Examples +/// +/// If `u32` were to implement `Into` conversions for `usize` _and_ for `u8`, +/// the following would fail to compile: +/// +/// ``` +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// assert_impl!(u32: !((Into<usize>) & (Into<u8>))); +/// ``` +/// +/// Check that a type is [`Send`] but not [`Sync`]. +/// +/// ``` +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// use std::cell::Cell; +/// +/// assert_impl!(Cell<u32>: Send & !Sync); +/// ``` +/// +/// Check simple one-off cases: +/// +/// ``` +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// assert_impl!(&'static mut u8: !Copy); +/// ``` +/// +/// Check that a type is _always_ [`Clone`] even when its parameter isn't: +/// +/// ``` +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// use std::rc::Rc; +/// +/// assert_impl!(for(T) Rc<T>: Clone); +/// ``` +/// +/// The following example fails to compile since `u64` cannot be converted into +/// either `u32` or `u16`: +/// +/// ```compile_fail +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// assert_impl!(u64: (Into<u32>) | (Into<u16>)); +/// ``` +/// +/// [`bool`]: https://doc.rust-lang.org/std/primitive.bool.html +/// [`Send`]: https://doc.rust-lang.org/std/marker/trait.Send.html +/// [`Sync`]: https://doc.rust-lang.org/std/marker/trait.Sync.html +/// [`Clone`]: https://doc.rust-lang.org/std/clone/trait.Clone.html +#[macro_export(local_inner_macros)] +macro_rules! assert_impl { + (for($($generic:tt)*) $ty:ty: $($rest:tt)*) => { + const _: () = { + fn assert_impl<$($generic)*>() { + // Construct an expression using `True`/`False` and their + // operators, that corresponds to the provided expression. + let _: $crate::True = $crate::_does_impl!($ty: $($rest)*); + } + }; + }; + ($ty:ty: $($rest:tt)*) => { + // Construct an expression using `True`/`False` and their operators, + // that corresponds to the provided expression. + const _: $crate::True = $crate::_does_impl!($ty: $($rest)*); + }; +} diff --git a/src/assert_obj_safe.rs b/src/assert_obj_safe.rs new file mode 100644 index 0000000..ecbba96 --- /dev/null +++ b/src/assert_obj_safe.rs @@ -0,0 +1,76 @@ +// FIXME: Link below is required to render in index +/// Asserts that the traits support dynamic dispatch +/// ([object-safety](https://doc.rust-lang.org/book/ch17-02-trait-objects.html#object-safety-is-required-for-trait-objects)). +/// +/// This is useful for when changes are made to a trait that accidentally +/// prevent it from being used as an [object]. Such a case would be adding a +/// generic method and forgetting to add `where Self: Sized` after it. If left +/// unnoticed, that mistake will affect crate users and break both forward and +/// backward compatibility. +/// +/// # Examples +/// +/// When exposing a public API, it's important that traits that could previously +/// use dynamic dispatch can still do so in future compatible crate versions. +/// +/// ``` +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// trait MySafeTrait { +/// fn foo(&self) -> u32; +/// } +/// +/// assert_obj_safe!(std::fmt::Write, MySafeTrait); +/// ``` +/// +/// Works with traits that are not in the calling module: +/// +/// ``` +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// mod inner { +/// pub trait BasicTrait { +/// fn bar(&self); +/// } +/// assert_obj_safe!(BasicTrait); +/// } +/// +/// assert_obj_safe!(inner::BasicTrait); +/// ``` +/// +/// The following example fails to compile because raw pointers cannot be sent +/// between threads safely: +/// +/// ```compile_fail +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// assert_impl!(*const u8, Send); +/// ``` +/// +/// The following example fails to compile because generics without +/// `where Self: Sized` are not allowed in [object-safe][object] trait methods: +/// +/// ```compile_fail +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// trait MyUnsafeTrait { +/// fn baz<T>(&self) -> T; +/// } +/// +/// assert_obj_safe!(MyUnsafeTrait); +/// ``` +/// +/// When we fix that, the previous code will compile: +/// +/// ``` +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// trait MyUnsafeTrait { +/// fn baz<T>(&self) -> T where Self: Sized; +/// } +/// +/// assert_obj_safe!(MyUnsafeTrait); +/// ``` +/// +/// [object]: https://doc.rust-lang.org/book/ch17-02-trait-objects.html#object-safety-is-required-for-trait-objects +#[macro_export] +macro_rules! assert_obj_safe { + ($($xs:path),+ $(,)?) => { + $(const _: Option<&$xs> = None;)+ + }; +} diff --git a/src/assert_size.rs b/src/assert_size.rs new file mode 100644 index 0000000..e1a7067 --- /dev/null +++ b/src/assert_size.rs @@ -0,0 +1,168 @@ +/// Asserts that types are equal in size. +/// +/// When performing operations such as pointer casts or dealing with [`usize`] +/// versus [`u64`] versus [`u32`], the size of your types matter. That is where +/// this macro comes into play. +/// +/// # Alternatives +/// +/// There also exists [`assert_size_eq_val`](macro.assert_size_eq_val.html) and +/// [`assert_size_eq_ptr`](macro.assert_size_eq_ptr.html). Instead of specifying +/// types to compare, values' sizes can be directly compared against each other. +/// +/// # Examples +/// +/// These three types, despite being very different, all have the same size: +/// +/// ``` +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// assert_size_eq!([u8; 4], (u16, u16), u32); +/// ``` +/// +/// The following example fails to compile because `u32` has 4 times the size of +/// `u8`: +/// +/// ```compile_fail +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// assert_size_eq!(u32, u8); +/// ``` +/// +/// [`usize`]: https://doc.rust-lang.org/std/primitive.usize.html +/// [`u64`]: https://doc.rust-lang.org/std/primitive.u64.html +/// [`u32`]: https://doc.rust-lang.org/std/primitive.u32.html +#[macro_export] +macro_rules! assert_size_eq { + ($x:ty, $($xs:ty),+ $(,)?) => { + const _: fn() = || { + $(let _ = $crate::_core::mem::transmute::<$x, $xs>;)+ + }; + }; +} + +/// Asserts that types are equal in alignment. +/// +/// This macro has been deprecated in favor of +/// [`assert_size_eq!`](macro.assert_size_eq.html). +#[deprecated( + since = "1.2.0", + note = "Please use the 'assert_size_eq' macro instead" +)] +#[macro_export(local_inner_macros)] +macro_rules! assert_eq_size { + ($($t:tt)*) => { + assert_size_eq!($($t)*); + }; +} + +/// Asserts that values pointed to are equal in size. +/// +/// # Examples +/// +/// This especially is useful for when coercing pointers between different types +/// and ensuring the underlying values are the same size. +/// +/// ``` +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// fn operation(x: &(u32, u32), y: &[u16; 4]) { +/// assert_size_eq_ptr!(x, y); +/// // ... +/// } +/// ``` +/// +/// The following example fails to compile because byte arrays of different +/// lengths have different sizes: +/// +/// ```compile_fail +/// # #[macro_use] extern crate static_assertions; +/// # fn main() { +/// static BYTES: &[u8; 4] = &[ +/// /* ... */ +/// # 0; 4 +/// ]; +/// +/// static TABLE: &[u8; 16] = &[ +/// /* ... */ +/// # 0; 16 +/// ]; +/// +/// assert_size_eq_ptr!(BYTES, TABLE); +/// ``` +#[macro_export] +macro_rules! assert_size_eq_ptr { + ($x:expr, $($xs:expr),+ $(,)?) => { + #[allow(unknown_lints, unsafe_code, forget_copy, useless_transmute)] + let _ = || unsafe { + use $crate::_core::{mem, ptr}; + let mut copy = ptr::read($x); + $(ptr::write(&mut copy, mem::transmute(ptr::read($xs)));)+ + mem::forget(copy); + }; + } +} + +/// Asserts that values pointed to are equal in size. +/// +/// This macro has been deprecated in favor of +/// [`assert_size_eq_ptr!`](macro.assert_size_eq_ptr.html). +#[deprecated( + since = "1.2.0", + note = "Please use the 'assert_size_eq_ptr' macro instead" +)] +#[macro_export(local_inner_macros)] +macro_rules! assert_eq_size_ptr { + ($($t:tt)*) => { + assert_size_eq_ptr!($($t)*); + }; +} + +/// Asserts that values are equal in size. +/// +/// This macro doesn't consume its arguments and thus works for +/// non-[`Clone`]able values. +/// +/// # Examples +/// +/// ``` +/// # #[macro_use] extern crate static_assertions; +/// # fn main() { +/// struct Byte(u8); +/// +/// let x = 10u8; +/// let y = Byte(42); // Works for non-cloneable types +/// +/// assert_size_eq_val!(x, y); +/// assert_size_eq_val!(x, y, 0u8); +/// # } +/// ``` +/// +/// Even though both values are 0, they are of types with different sizes: +/// +/// ```compile_fail +/// # #[macro_use] extern crate static_assertions; +/// # fn main() { +/// assert_size_eq_val!(0u8, 0u32); +/// # } +/// ``` +/// +/// [`Clone`]: https://doc.rust-lang.org/std/clone/trait.Clone.html +#[macro_export(local_inner_macros)] +macro_rules! assert_size_eq_val { + ($x:expr, $($xs:expr),+ $(,)?) => { + assert_size_eq_ptr!(&$x, $(&$xs),+); + } +} + +/// Asserts that values pointed to are equal in size. +/// +/// This macro has been deprecated in favor of +/// [`assert_size_eq_val!`](macro.assert_size_eq_val.html). +#[deprecated( + since = "1.2.0", + note = "Please use the 'assert_size_eq_val' macro instead" +)] +#[macro_export(local_inner_macros)] +macro_rules! assert_eq_size_val { + ($($t:tt)*) => { + assert_size_eq_val!($($t)*); + }; +} diff --git a/src/assert_trait.rs b/src/assert_trait.rs new file mode 100644 index 0000000..5c87ce6 --- /dev/null +++ b/src/assert_trait.rs @@ -0,0 +1,139 @@ +/// Asserts that the trait is a child of all of the other traits. +/// +/// Related: +/// - [`assert_trait_super_all!`] +/// +/// # Examples +/// +/// All types that implement [`Copy`] must implement [`Clone`]: +/// +/// ``` +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// assert_trait_sub_all!(Copy: Clone); +/// ``` +/// +/// All types that implement [`Ord`] must implement [`PartialEq`], [`Eq`], and +/// [`PartialOrd`]: +/// +/// ``` +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// assert_trait_sub_all!(Ord: PartialEq, Eq, PartialOrd); +/// ``` +/// +/// The following example fails to compile because [`Eq`] is not required for +/// [`PartialOrd`]: +/// +/// ```compile_fail +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// assert_trait_sub_all!(PartialOrd: Eq); +/// ``` +/// +/// [`assert_trait_super_all!`]: macro.assert_trait_super_all.html +/// +/// [`Copy`]: https://doc.rust-lang.org/std/marker/trait.Copy.html +/// [`Clone`]: https://doc.rust-lang.org/std/clone/trait.Clone.html +/// [`Ord`]: https://doc.rust-lang.org/std/cmp/trait.Ord.html +/// [`PartialOrd`]: https://doc.rust-lang.org/std/cmp/trait.PartialOrd.html +/// [`Eq`]: https://doc.rust-lang.org/std/cmp/trait.Eq.html +/// [`PartialEq`]: https://doc.rust-lang.org/std/cmp/trait.PartialEq.html +#[macro_export(local_inner_macros)] +macro_rules! assert_trait_sub_all { + ($sub:path: $($super:path),+ $(,)?) => { + assert_impl!(for(T: $sub) T: $( ($super) )&+); + }; +} + +/// Asserts that the trait is a parent of all of the other traits. +/// +/// Related: +/// - [`assert_trait_sub_all!`] +/// +/// # Examples +/// +/// With this, traits `A` and `B` can both be tested to require [`Copy`] on a +/// single line: +/// +/// ``` +/// # use static_assertions::assert_trait_super_all; +/// trait A: Copy {} +/// trait B: Copy {} +/// +/// assert_trait_super_all!(Copy: A, B); +/// ``` +/// +/// Otherwise, each sub-trait would require its own call to +/// [`assert_trait_sub_all!`]: +/// +/// ``` +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// # trait A: Copy {} +/// # trait B: Copy {} +/// assert_trait_sub_all!(A: Copy); +/// assert_trait_sub_all!(B: Copy); +/// ``` +/// +/// The following example fails to compile because trait `C` does not require +/// [`Copy`]: +/// +/// ```compile_fail +/// # use static_assertions::assert_trait_super_all; +/// # trait A: Copy {} +/// # trait B: Copy {} +/// trait C {} +/// +/// assert_trait_super_all!(Copy: A, B, C); +/// ``` +/// +/// [`assert_trait_sub_all!`]: macro.assert_trait_sub_all.html +/// +/// [`Copy`]: https://doc.rust-lang.org/std/marker/trait.Copy.html +#[macro_export(local_inner_macros)] +macro_rules! assert_trait_super_all { + ($super:path: $($sub:path),+ $(,)?) => { + $(assert_trait_sub_all!($sub: $super);)+ + }; +} + +/// Asserts that the trait is a child of one or more of the other traits. +/// +/// Related: +/// - [`assert_impl_any!`] +/// +/// # Examples +/// +/// All types that implement [`Copy`] must implement [`Clone`]: +/// +/// ``` +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// assert_trait_sub_any!(Copy: Clone); +/// ``` +/// +/// All types that implement [`Ord`] must implement [`Eq`], but don't have to implement [`Clone`]: +/// +/// ``` +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// assert_trait_sub_any!(Ord: Eq, Clone); +/// ``` +/// +/// The following example fails to compile because neither [`Eq`] nor [`Clone`] are required for +/// [`PartialOrd`]: +/// +/// ```compile_fail +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// assert_trait_sub_any!(PartialOrd: Eq, Clone); +/// ``` +/// +/// [`assert_impl_any!`]: macro.assert_impl_any.html +/// +/// [`Copy`]: https://doc.rust-lang.org/std/marker/trait.Copy.html +/// [`Clone`]: https://doc.rust-lang.org/std/clone/trait.Clone.html +/// [`Ord`]: https://doc.rust-lang.org/std/cmp/trait.Ord.html +/// [`PartialOrd`]: https://doc.rust-lang.org/std/cmp/trait.PartialOrd.html +/// [`Eq`]: https://doc.rust-lang.org/std/cmp/trait.Eq.html +/// [`PartialEq`]: https://doc.rust-lang.org/std/cmp/trait.PartialEq.html +#[macro_export(local_inner_macros)] +macro_rules! assert_trait_sub_any { + ($sub:path: $($super:path),+ $(,)?) => { + assert_impl!(for(T: $sub) T: $( ($super) )|+); + }; +} diff --git a/src/assert_type.rs b/src/assert_type.rs new file mode 100644 index 0000000..dd2dc2a --- /dev/null +++ b/src/assert_type.rs @@ -0,0 +1,101 @@ +/// Asserts that _all_ types in a list are equal to each other. +/// +/// # Examples +/// +/// Often times, type aliases are used to express usage semantics via naming. In +/// some cases, the underlying type may differ based on platform. However, other +/// types like [`c_float`] will always alias the same type. +/// +/// ``` +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// use std::os::raw::c_float; +/// +/// assert_type_eq_all!(c_float, f32); +/// ``` +/// +/// This macro can also be used to compare types that involve lifetimes! Just +/// use `'static` in that case: +/// +/// ``` +/// # #[macro_use] extern crate static_assertions; +/// # fn main() { +/// type Buf<'a> = &'a [u8]; +/// +/// assert_type_eq_all!(Buf<'static>, &'static [u8]); +/// # } +/// ``` +/// +/// The following example fails to compile because `String` and `str` do not +/// refer to the same type: +/// +/// ```compile_fail +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// assert_type_eq_all!(String, str); +/// ``` +/// +/// This should also work the other way around, regardless of [`Deref`] +/// implementations. +/// +/// ```compile_fail +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// assert_type_eq_all!(str, String); +/// ``` +/// +/// [`c_float`]: https://doc.rust-lang.org/std/os/raw/type.c_float.html +/// [`Deref`]: https://doc.rust-lang.org/std/ops/trait.Deref.html +#[macro_export] +macro_rules! assert_type_eq_all { + ($x:ty, $($xs:ty),+ $(,)*) => { + const _: fn() = || { $({ + trait TypeEq { + type This: ?Sized; + } + + impl<T: ?Sized> TypeEq for T { + type This = Self; + } + + fn assert_type_eq_all<T, U>() + where + T: ?Sized + TypeEq<This = U>, + U: ?Sized, + {} + + assert_type_eq_all::<$x, $xs>(); + })+ }; + }; +} + +/// Asserts that _all_ types are **not** equal to each other. +/// +/// # Examples +/// +/// Rust has all sorts of slices, but they represent different types of data: +/// +/// ``` +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// assert_type_ne_all!([u8], [u16], str); +/// ``` +/// +/// The following example fails to compile because [`c_uchar`] is a type alias +/// for [`u8`]: +/// +/// ```compile_fail +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// use std::os::raw::c_uchar; +/// +/// assert_type_ne_all!(c_uchar, u8, u32); +/// ``` +/// +/// [`c_uchar`]: https://doc.rust-lang.org/std/os/raw/type.c_uchar.html +/// [`u8`]: https://doc.rust-lang.org/std/primitive.u8.html +#[macro_export] +macro_rules! assert_type_ne_all { + ($x:ty, $($y:ty),+ $(,)?) => { + const _: fn() = || { + trait MutuallyExclusive {} + impl MutuallyExclusive for $x {} + $(impl MutuallyExclusive for $y {})+ + }; + }; +} diff --git a/src/bool.rs b/src/bool.rs new file mode 100644 index 0000000..296159f --- /dev/null +++ b/src/bool.rs @@ -0,0 +1,59 @@ +#[derive(Clone, Copy)] +pub struct True; +#[derive(Clone, Copy)] +pub struct False; + +impl True { + pub const fn not(&self) -> &'static False { + &False + } + pub const fn and<'a, T>(&self, other: &'a T) -> &'a T { + other + } + pub const fn or<T>(&self, _: &T) -> &'static True { + &True + } + pub const fn value(&self) -> bool { + true + } +} + +impl False { + pub const fn not(&self) -> &'static True { + &True + } + pub const fn and<T>(&self, _: &T) -> &'static False { + &False + } + pub const fn or<'a, T>(&self, other: &'a T) -> &'a T { + other + } + pub const fn value(&self) -> bool { + false + } +} + +pub trait ToBool: Sized { + type Bool: Sized; + const TO_BOOL: Self::Bool; +} + +impl ToBool for [(); 0] { + type Bool = False; + const TO_BOOL: Self::Bool = False; +} + +impl ToBool for [(); 1] { + type Bool = True; + const TO_BOOL: Self::Bool = True; +} + +/// Converts a `const bool` to a type-level boolean. +#[doc(hidden)] +#[macro_export] +macro_rules! _to_bool { + ($x:expr) => {{ + const X: bool = $x; + <[(); X as usize] as $crate::_bool::ToBool>::TO_BOOL + }}; +} diff --git a/src/const_assert.rs b/src/const_assert.rs new file mode 100644 index 0000000..39ad3eb --- /dev/null +++ b/src/const_assert.rs @@ -0,0 +1,178 @@ +/// Asserts that constant expressions evaluate to `true`. +/// +/// Constant expressions can be ensured to have certain properties via this +/// macro If the expression evaluates to `false`, the file will fail to compile. +/// This is synonymous to [`static_assert` in C++][static_assert]. +/// +/// # Alternatives +/// +/// There also exists [`const_assert_eq`](macro.const_assert_eq.html) for +/// validating whether a sequence of expressions are equal to one another. +/// +/// # Examples +/// +/// A common use case is to guarantee properties about a constant value that's +/// generated via meta-programming. +/// +/// ``` +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// const VALUE: i32 = // ... +/// # 3; +/// +/// const_assert!(VALUE >= 2); +/// ``` +/// +/// Inputs are type-checked as booleans: +/// +/// ```compile_fail +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// const_assert!(!0); +/// ``` +/// +/// Despite this being a macro, we see this produces a type error: +/// +/// ```txt +/// | const_assert!(!0); +/// | ^^ expected bool, found integral variable +/// | +/// = note: expected type `bool` +/// found type `{integer}` +/// ``` +/// +/// The following fails to compile because multiplying by 5 does not have an +/// identity property: +/// +/// ```compile_fail +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// const_assert!(5 * 5 == 5); +/// ``` +/// +/// [static_assert]: http://en.cppreference.com/w/cpp/language/static_assert +#[macro_export(local_inner_macros)] +macro_rules! const_assert { + ($x:expr $(,)?) => { + const _: $crate::True = _to_bool!($x); + }; +} + +/// Asserts that constants are equal in value. +/// +/// Use [`const_assert_eq_usize!`](macro.const_assert_eq_usize.html) for better +/// error messages when asserting +/// [`usize`](https://doc.rust-lang.org/std/primitive.usize.html) equality. +/// +/// # Examples +/// +/// This works as a shorthand for `const_assert!(a == b)`: +/// +/// ``` +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// const TWO: i32 = 2; +/// +/// const_assert_eq!(TWO * TWO, TWO + TWO); +/// ``` +/// +/// Just because 2 × 2 = 2 + 2 doesn't mean it holds true for other numbers: +/// +/// ```compile_fail +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// const_assert_eq!(4 + 4, 4 * 4); +/// ``` +#[macro_export(local_inner_macros)] +macro_rules! const_assert_eq { + ($x:expr, $($y:expr),+ $(,)?) => { + const_assert!($($x == $y)&&+); + }; +} + +/// Asserts that constants of type +/// [`usize`](https://doc.rust-lang.org/std/primitive.usize.html) are equal in +/// value. +/// +/// This is equivalent to [`const_assert_eq!`](macro.const_assert_eq.html) but +/// allows for inspecting the values in error messages. +#[macro_export] +macro_rules! const_assert_eq_usize { + ($x:expr, $($y:expr),+ $(,)?) => { + // Assigned instance must match the annotated type or else it will fail. + $(const _: [(); $x] = [(); $y];)+ + }; +} + +/// Asserts that constants are **not** equal in value. +/// +/// # Examples +/// +/// This works as a shorthand for `const_assert!(a != b)`: +/// +/// ``` +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// const NUM: usize = 32; +/// +/// const_assert_ne!(NUM * NUM, 64); +/// ``` +/// +/// The following example fails to compile because 2 is magic and 2 × 2 = 2 + 2: +/// +/// ```compile_fail +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// const_assert_ne!(2 + 2, 2 * 2); +/// ``` +#[macro_export(local_inner_macros)] +macro_rules! const_assert_ne { + ($x:expr, $($y:expr),+ $(,)?) => { + const_assert!($($x != $y)&&+); + }; +} + +/// Asserts that constants are less than each other. +#[macro_export(local_inner_macros)] +macro_rules! const_assert_lt { + ($x:expr, $($y:expr),+ $(,)?) => { + const_assert_lt!(@build $x, $($y),+); + }; + (@build $x:expr) => {}; + (@build $x:expr, $($y:expr),+) => { + const_assert!($x < _head!($($y),+)); + const_assert_lt!(@build $($y),+); + }; +} + +/// Asserts that constants are less than or equal to each other. +#[macro_export(local_inner_macros)] +macro_rules! const_assert_le { + ($x:expr, $($y:expr),+ $(,)?) => { + const_assert_le!(@build $x, $($y),+); + }; + (@build $x:expr) => {}; + (@build $x:expr, $($y:expr),+) => { + const_assert!($x <= _head!($($y),+)); + const_assert_le!(@build $($y),+); + }; +} + +/// Asserts that constants are greater than each other. +#[macro_export(local_inner_macros)] +macro_rules! const_assert_gt { + ($x:expr, $($y:expr),+ $(,)?) => { + const_assert_gt!(@build $x, $($y),+); + }; + (@build $x:expr) => {}; + (@build $x:expr, $($y:expr),+) => { + const_assert!($x > _head!($($y),+)); + const_assert_gt!(@build $($y),+); + }; +} + +/// Asserts that constants are less than or equal to each other. +#[macro_export(local_inner_macros)] +macro_rules! const_assert_ge { + ($x:expr, $($y:expr),+ $(,)?) => { + const_assert_ge!(@build $x, $($y),+); + }; + (@build $x:expr) => {}; + (@build $x:expr, $($y:expr),+) => { + const_assert!($x >= _head!($($y),+)); + const_assert_ge!(@build $($y),+); + }; +} diff --git a/src/does_impl.rs b/src/does_impl.rs new file mode 100644 index 0000000..f2d35fe --- /dev/null +++ b/src/does_impl.rs @@ -0,0 +1,108 @@ +/// Returns `true` if the type does implement a logical trait expression. +/// +/// # Examples +/// +/// One can mimic `assert_impl!` using this macro: +/// +/// ``` +/// # #[macro_use] extern crate static_assertions; fn main() {} +/// const CONDITION: bool = does_impl!(u32: From<u8>); +/// +/// const_assert!(CONDITION); +/// ``` +#[macro_export(local_inner_macros)] +macro_rules! does_impl { + ($ty:ty: $($trait_expr:tt)+) => { + _does_impl!($ty: $($trait_expr)+).value() + }; +} + +/// Returns `True` or `False` depending on whether the given type implements the +/// given trait boolean expression. Can be used in const contexts if it doesn't +/// depend on outer generic parameters. +/// +/// This is the core of `assert_impl`. +#[doc(hidden)] +#[macro_export(local_inner_macros)] +macro_rules! _does_impl { + ($ty:ty: $($rest:tt)*) => {{ + #[allow(unused_imports)] + use $crate::{ + _bool::{True, False}, + _core::{marker::PhantomData, ops::Deref}, + }; + + // Fallback trait that returns false if the type does not implement a + // given trait. + trait DoesntImpl { + const DOES_IMPL: False = False; + } + impl<T: ?Sized> DoesntImpl for T {} + + // Construct an expression using `True`/`False` and their operators, + // that corresponds to the provided expression. + *_does_impl!(@boolexpr($ty,) $($rest)*) + }}; + + (@boolexpr($($args:tt)*) ($($expr:tt)*)) => { + _does_impl!(@boolexpr($($args)*) $($expr)*) + }; + (@boolexpr($($args:tt)*) !($($expr:tt)*)) => { + _does_impl!(@boolexpr($($args)*) $($expr)*).not() + }; + (@boolexpr($($args:tt)*) ($($left:tt)*) | $($right:tt)*) => {{ + let left = _does_impl!(@boolexpr($($args)*) $($left)*); + let right = _does_impl!(@boolexpr($($args)*) $($right)*); + left.or(right) + }}; + (@boolexpr($($args:tt)*) ($($left:tt)*) & $($right:tt)*) => {{ + let left = _does_impl!(@boolexpr($($args)*) $($left)*); + let right = _does_impl!(@boolexpr($($args)*) $($right)*); + left.and(right) + }}; + (@boolexpr($($args:tt)*) !($($left:tt)*) | $($right:tt)*) => {{ + _does_impl!(@boolexpr($($args)*) (!($($left)*)) | $($right)*) + }}; + (@boolexpr($($args:tt)*) !($($left:tt)*) & $($right:tt)*) => {{ + _does_impl!(@boolexpr($($args)*) (!($($left)*)) & $($right)*) + }}; + (@boolexpr($($args:tt)*) !$left:ident | $($right:tt)*) => {{ + _does_impl!(@boolexpr($($args)*) !($left) | $($right)*) + }}; + (@boolexpr($($args:tt)*) !$left:ident & $($right:tt)*) => {{ + _does_impl!(@boolexpr($($args)*) !($left) & $($right)*) + }}; + (@boolexpr($($args:tt)*) $left:ident | $($right:tt)*) => { + _does_impl!(@boolexpr($($args)*) ($left) | $($right)*) + }; + (@boolexpr($($args:tt)*) $left:ident & $($right:tt)*) => {{ + _does_impl!(@boolexpr($($args)*) ($left) & $($right)*) + }}; + (@boolexpr($($args:tt)*) !$expr:ident) => { + _does_impl!(@boolexpr($($args)*) !($expr)) + }; + (@boolexpr($($args:tt)*) !$expr:path) => { + _does_impl!(@boolexpr($($args)*) !($expr)) + }; + (@boolexpr($($args:tt)*) $expr:ident) => { + _does_impl!(@base($($args)*) $expr) + }; + (@boolexpr($($args:tt)*) $expr:path) => { + _does_impl!(@base($($args)*) $expr) + }; + + (@base($ty:ty, $($args:tt)*) $($trait:tt)*) => {{ + // Base case: computes whether `ty` implements `trait`. + struct Wrapper<T: ?Sized>(PhantomData<T>); + + #[allow(dead_code)] + impl<T: ?Sized + $($trait)*> Wrapper<T> { + const DOES_IMPL: True = True; + } + + // If `$type: $trait`, the `_does_impl` inherent method on `Wrapper` + // will be called, and return `True`. Otherwise, the trait method will + // be called, which returns `False`. + &<Wrapper<$ty>>::DOES_IMPL + }}; +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..aa7e227 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,142 @@ +//! [![Banner](https://raw.githubusercontent.com/nvzqz/static-assertions-rs/assets/Banner.png)](https://github.com/nvzqz/static-assertions-rs) +//! +//! <div align="center"> +//! <a href="https://crates.io/crates/static_assertions"> +//! <img src="https://img.shields.io/crates/d/static_assertions.svg" alt="Downloads"> +//! </a> +//! <a href="https://github.com/nvzqz/static-assertions-rs/actions?query=workflow%3ACI"> +//! <img src="https://github.com/nvzqz/static-assertions-rs/workflows/CI/badge.svg" alt="Build Status"> +//! </a> +//! <img src="https://img.shields.io/badge/rustc-^1.37.0-blue.svg" alt="rustc ^1.37.0"> +//! <br><br> +//! </div> +//! +//! Assertions to ensure correct assumptions about constants, types, and more. +//! +//! _All_ checks provided by this crate are performed at [compile-time]. This +//! allows for finding errors quickly and early when it comes to ensuring +//! certain features or aspects of a codebase. These macros are especially +//! important when exposing a public API that requires types to be the same size +//! or implement certain traits. +//! +//! # Usage +//! +//! This crate is available [on crates.io][crate] and can be used by adding the +//! following to your project's [`Cargo.toml`]: +//! +//! ```toml +//! [dependencies] +//! static_assertions = "1.1.0" +//! ``` +//! +//! and this to your crate root (`main.rs` or `lib.rs`): +//! +//! ``` +//! # #[allow(unused_imports)] +//! #[macro_use] +//! extern crate static_assertions; +//! # fn main() {} +//! ``` +//! +//! When using [Rust 2018 edition][2018], the following shorthand can help if +//! having `#[macro_use]` is undesirable. +//! +//! ```edition2018 +//! extern crate static_assertions as sa; +//! +//! sa::const_assert!(true); +//! ``` +//! +//! ## Procedural Extensions +//! +//! As an extension crate [`proc_static_assertions`] adds a number of new +//! assertions to this. These are implemented as [procedural macros], hence the +//! "proc" prefix. As a result, they have a bit more visibility over what's +//! being asserted over than normal macros would. +//! +//! It can be enabled via the `proc` feature flag in your [`Cargo.toml`]: +//! +//! ```toml +//! [dependencies] +//! static_assertions = { version = "1.1.0", features = ["proc"] } +//! ``` +//! +//! # Examples +//! +//! Very thorough examples are provided in the docs for +//! [each individual macro](#macros). Failure case examples are also documented. +//! +//! # Changes +//! +//! See [`CHANGELOG.md`](https://github.com/nvzqz/static-assertions-rs/blob/master/CHANGELOG.md) +//! for an exhaustive list of what has changed from one version to another. +//! +//! # Donate +//! +//! This project is made freely available (as in free beer), but unfortunately +//! not all beer is free! So, if you would like to buy me a beer (or coffee or +//! *more*), then consider supporting my work that's benefited your project +//! and thousands of others. +//! +//! <a href="https://www.patreon.com/nvzqz"> +//! <img src="https://c5.patreon.com/external/logo/become_a_patron_button.png" alt="Become a Patron!" height="35"> +//! </a> +//! <a href="https://www.paypal.me/nvzqz"> +//! <img src="https://buymecoffee.intm.org/img/button-paypal-white.png" alt="Buy me a coffee" height="35"> +//! </a> +//! +//! [`proc_static_assertions`]: https://docs.rs/proc_static_assertions +//! [procedural macros]: https://doc.rust-lang.org/book/ch19-06-macros.html#procedural-macros-for-generating-code-from-attributes +//! [Rust 1.37]: https://blog.rust-lang.org/2019/08/15/Rust-1.37.0.html +//! [2018]: https://blog.rust-lang.org/2018/12/06/Rust-1.31-and-rust-2018.html#rust-2018 +//! [crate]: https://crates.io/crates/static_assertions +//! [compile-time]: https://en.wikipedia.org/wiki/Compile_time +//! [`Cargo.toml`]: https://doc.rust-lang.org/cargo/reference/manifest.html + +#![deny(missing_docs, unused_macros)] +#![doc( + html_root_url = "https://docs.rs/static_assertions/1.1.0", + html_logo_url = "https://raw.githubusercontent.com/nvzqz/static-assertions-rs/assets/Icon.png", + test(attr(deny(warnings), allow(dead_code))) +)] +#![no_std] + +#[cfg(feature = "proc_static_assertions")] +extern crate proc_static_assertions; +#[cfg(feature = "proc_static_assertions")] +pub use proc_static_assertions::assert; + +// This module should never be used publicly and is not part of this crate's +// semver requirements. +#[doc(hidden)] +pub extern crate core as _core; + +mod assert_align; +mod assert_cfg; +mod assert_fields; +mod assert_impl; +mod assert_obj_safe; +mod assert_size; +mod assert_trait; +mod assert_type; +mod const_assert; +mod does_impl; + +// Utility macros. +// +// These macros should also never be used publicly and are not part of this +// crate's semver requirements. +mod util; + +// Type-level booleans. +// +// This module should never be used publicly and is not part of this crate's +// semver requirements. +#[doc(hidden)] +#[path = "bool.rs"] +pub mod _bool; + +// These types should also never be used publicly and are not part of this +// crate's semver requirements. +#[doc(hidden)] +pub use _bool::{False, True}; diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 0000000..17dbe79 --- /dev/null +++ b/src/util.rs @@ -0,0 +1,7 @@ +#[doc(hidden)] +#[macro_export] +macro_rules! _head { + ($head:expr $(, $tail:expr)* $(,)?) => { + $head + }; +} diff --git a/tests/const.rs b/tests/const.rs new file mode 100644 index 0000000..cdd2d70 --- /dev/null +++ b/tests/const.rs @@ -0,0 +1,15 @@ +#![no_std] +#![deny(unsafe_code)] + +#[macro_use] +extern crate static_assertions; + +const_assert!(true && (true != false)); +const_assert!((true && true) != false); +const_assert_eq!(false, false); + +#[allow(dead_code)] +const FIVE: usize = 5; + +const_assert!(FIVE * 2 == 10); +const_assert!(FIVE > 2); diff --git a/tests/eq_size.rs b/tests/eq_size.rs new file mode 100644 index 0000000..3317b94 --- /dev/null +++ b/tests/eq_size.rs @@ -0,0 +1,113 @@ +#![no_std] +#![deny(unsafe_code)] + +#[macro_use] +extern crate static_assertions; + +assert_size_eq!(u8, u8, (u8,), [u8; 1]); + +mod assoc_type { + trait Trait { + type AssocItem: ?Sized; + } + + impl<T: ?Sized> Trait for T { + type AssocItem = Self; + } + + #[allow(dead_code)] + struct Value; + + assert_size_eq!(<Value as Trait>::AssocItem, Value); + + // TODO: Is this possible? + // pub fn test<T: Trait>() { + // assert_size_eq!(<T as Trait>::AssocItem, T); + // } +} + +// Placed in separate module so that DropCounter's fields are private +mod dc { + /// A type that acts like somewhat of a reference counter. + pub struct DropCounter<'a> { + count: &'a mut i32, + } + + impl<'a> DropCounter<'a> { + pub fn new(count: &'a mut i32) -> DropCounter<'a> { + *count += 1; + DropCounter { count } + } + + pub fn count(&self) -> i32 { + *self.count + } + } + + impl<'a> Drop for DropCounter<'a> { + fn drop(&mut self) { + *self.count -= 1 + } + } +} +use dc::*; + +/// A type that panics on drop. +#[allow(dead_code)] +struct PanicDrop<T>(T); + +impl<T> Drop for PanicDrop<T> { + fn drop(&mut self) { + panic!("Dropped!"); + } +} + +#[test] +fn test_eq_size() { + assert_size_eq!([u8; 2], u16); + assert_size_eq!([u8; 2], u16, (u8, u8)); + assert_size_eq!([u8; 4], u32, (u16, u8, u8), (u16, u16)); + + assert_size_eq_val!([0u8; 2], 0u16); + assert_size_eq_val!([0u8; 2], 0u16, (0u8, 0u8)); + assert_size_eq_val!([0u8; 4], 0u32, (0u16, 0u8, 0u8), (0u16, 0u16)); + + #[deny(unused_unsafe)] + { + assert_size_eq!(u8, u8); + assert_size_eq_val!(0u8, 0u8); + } + + let x = &mut 0; + assert_size_eq_ptr!(x, &0); + *x = 20; + assert_size_eq_ptr!(x, &0); + + // Should fail to compile (un-comment to test manually): + // assert_size_eq!(u8, u16); + // assert_size_eq_val!(0u8, 0u16); +} + +#[test] +fn test_eq_size_no_drop() { + assert_size_eq!(u32, PanicDrop<u32>); + assert_size_eq!(PanicDrop<u32>, u32); + assert_size_eq!(PanicDrop<u32>, PanicDrop<u32>); +} + +#[test] +fn test_eq_size_drop_count() { + let mut count = 0; + { + let dc = DropCounter::new(&mut count); + assert_eq!(dc.count(), 1); + assert_size_eq_val!(dc, 0usize); + assert_eq!(dc.count(), 1); + assert_size_eq_val!(dc, 0usize, dc); + assert_eq!(dc.count(), 1); + } + assert_eq!(count, 0); + + assert_size_eq_val!(DropCounter::new(&mut count), 0usize); + assert_eq!(count, 0); +} diff --git a/tests/fields.rs b/tests/fields.rs new file mode 100644 index 0000000..c465475 --- /dev/null +++ b/tests/fields.rs @@ -0,0 +1,32 @@ +#![no_std] +#![deny(unsafe_code)] + +#[macro_use] +extern crate static_assertions; + +#[allow(dead_code)] +enum Foo { + A { x: u8, y: u8 }, + B(u8), +} + +assert_fields!(Foo::A: x); +assert_fields!(Foo::A: x, x); +assert_fields!(Foo::A: x, y, x); + +// TODO: Make tuple field access possible +// assert_fields!(Foo::B, 0); + +mod m { + #[allow(dead_code)] + pub struct Bar<T: ?Sized> { + pub nul: (), + pub inner: T, + } +} + +#[allow(dead_code)] +use m::Bar as Baz; + +assert_fields!(m::Bar<str>: inner, nul); +assert_fields!(Baz<dyn Send>: inner, nul); diff --git a/tests/obj_safe.rs b/tests/obj_safe.rs new file mode 100644 index 0000000..97847b2 --- /dev/null +++ b/tests/obj_safe.rs @@ -0,0 +1,10 @@ +#![no_std] +#![deny(unsafe_code)] + +#[macro_use] +extern crate static_assertions; + +assert_obj_safe!(core::fmt::Debug, Send, Sync); + +trait ObjSafe {} +assert_obj_safe!(ObjSafe); diff --git a/tests/proc_size.rs b/tests/proc_size.rs new file mode 100644 index 0000000..b62a36f --- /dev/null +++ b/tests/proc_size.rs @@ -0,0 +1,9 @@ +#![cfg(feature = "proc")] + +#[macro_use] +extern crate static_assertions; + +#[assert(size == 4, align == 4)] +struct Foo { + value: i32, +} diff --git a/tests/trait_impl.rs b/tests/trait_impl.rs new file mode 100644 index 0000000..2a6fffd --- /dev/null +++ b/tests/trait_impl.rs @@ -0,0 +1,71 @@ +#![no_std] +#![deny(unsafe_code)] + +#[macro_use] +extern crate static_assertions; + +use core::ops::Range; + +trait Tri<A: ?Sized, B: ?Sized, C: ?Sized> {} + +impl<T, A: ?Sized, B: ?Sized, C: ?Sized> Tri<A, B, C> for T {} + +assert_impl_all!( + u64: Tri<[&'static u8], dyn Tri<dyn Send, dyn Sync, str>, (u16, u16)> +); +assert_impl_all!(u8: Send, Sync); +assert_impl_all!(&'static [u8]: IntoIterator<Item=&'static u8>); +assert_impl_all!(Range<u8>: Iterator<Item=u8>); +assert_impl_all!([u8]: Send, Sync, AsRef<[u8]>); +assert_impl_all!(str: Send, Sync, AsRef<[u8]>,); + +assert_impl_any!((): Send, Sync); +assert_impl_any!((): Send, From<u8>); +assert_impl_any!((): From<u8>, From<u16>, Send); + +#[allow(dead_code)] +struct Foo; + +trait A {} +trait B {} +trait C {} + +impl B for Foo {} + +assert_impl_one!(Foo: A, B); +assert_impl_one!(Foo: B, A); +assert_impl_one!(Foo: B, C); +assert_impl_one!(Foo: C, B); +assert_impl_one!(Foo: A, B, C); +assert_impl_one!(Foo: B, C, A); +assert_impl_one!(Foo: C, A, B); + +#[derive(Clone)] +struct Test; + +assert_impl!(u8: (From<u16>) | (Into<u16>)); +assert_impl!((): (From<u8>) | (From<u16>) | Send); +assert_impl!((): (!From<u8>) & !(From<u16>) & Send); +assert_impl!((): Copy | Clone); +assert_impl!((): Copy & Clone); +assert_impl!(Test: Copy | Clone); +assert_impl!(Test: !Copy | Clone); +assert_impl!(Test: !Copy & Clone); +assert_impl!(Test: !Copy & (Clone)); +assert_impl!(Test: !(Copy) & Clone); +assert_impl!(Test: !(!Clone)); +assert_impl!(Test: !(Copy) & !(!Clone)); +assert_impl!(Test: !(Copy & Clone)); +assert_impl!(str: !Copy & !Clone); + +#[derive(Clone)] +struct Box<T>(T); + +assert_impl!(for(T: Clone) Box<T>: Clone); +assert_impl!(for(T: Clone + Send) Box<T>: Clone & Send); +assert_impl!(for(T) Box<T>: (From<T>) & (Into<T>)); + +assert_impl!(for(T) PhantomData<T>: Clone); +assert_impl!(for(T: Copy) T: Clone); +assert_impl!(for(T: ?Sized) T: Clone | !Clone); +assert_impl!(for('a, T: 'a) &'a mut T: !Copy); diff --git a/tests/type_eq.rs b/tests/type_eq.rs new file mode 100644 index 0000000..e8036d7 --- /dev/null +++ b/tests/type_eq.rs @@ -0,0 +1,15 @@ +#![no_std] +#![deny(unsafe_code)] + +#[macro_use] +extern crate static_assertions; + +assert_type_eq_all!([u8], [u8]); + +#[allow(dead_code)] +type X = u8; + +#[allow(unused_parens)] +mod m { + assert_type_eq_all!(super::X, u8, (super::X)); +} diff --git a/tests/type_ne.rs b/tests/type_ne.rs new file mode 100644 index 0000000..3adfef0 --- /dev/null +++ b/tests/type_ne.rs @@ -0,0 +1,7 @@ +#![no_std] +#![deny(unsafe_code)] + +#[macro_use] +extern crate static_assertions; + +assert_type_ne_all!(u8, u16, u32); |