Vendor dependencies for 0.3.0 release

This commit is contained in:
2025-09-27 10:29:08 -05:00
parent 0c8d39d483
commit 82ab7f317b
26803 changed files with 16134934 additions and 0 deletions

View File

@@ -0,0 +1 @@
{"files":{"Cargo.lock":"96bc588c888d8fd2a451b86425f51c2c6526554a24af84cc6d26adac0c25ff84","Cargo.toml":"2e692c5f3cdfaf3f7db88219f94ffce394bf21a748ea41c7547d9c9cafb33a42","LICENSE-APACHE":"a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9","LICENSE-MIT":"508a77d2e7b51d98adeed32648ad124b7b30241a8e70b2e72c99f92d8e5874d1","src/attribute_parser.rs":"ec4fc2badd5f5b86a0e44a7316b430e01149a72439265312b78a5cf7daa48186","src/container_attributes.rs":"70b37fdd7aebe9f4f3b82944ebaffb733285f769e8e48d16f6eb019ccb43b841","src/custom_attributes.rs":"381e0fdbbc1a4f47e6c7ec2c080f46f64fa2dd7415e3efdc05fe58ee3adb970a","src/derive_data.rs":"78e8493fa4ec56f8816edd67eb39e382c3c1613d475389e2032d8a26235ae5d2","src/documentation.rs":"c3068dd632bcf1242c1fa866fe5c79f586322a3a2c7ed608e0d4ae047984051d","src/enum_utility.rs":"8159e590dd6322700a11711f2dd8090a7de27ad2857eb0b2d8b620d9bb46ff2d","src/field_attributes.rs":"0e6515a5851ac210b4a8e3ca598890228d4ea8b3763a8c558396c20fd700a048","src/from_reflect.rs":"5ba92f89668b0b0856479275a2b6952e88289740a225719f799c4e0d47ed4a63","src/generics.rs":"6758e096ae7a352ee3ce2e0e909344c70b17632faa2864f996112ff9ec66795b","src/ident.rs":"f2701fa28f6b52cf5ef3ca2ec4d0e4ff49767342f4a467f2ca1249324b0d4fff","src/impls/assertions.rs":"73e8c2a70cd597c2d44a980394111bc68bcc56a04ead1a414afcafc3bcee971d","src/impls/common.rs":"21ad8380a0a3de9ce5bf124db4adab1dc331e92d2bb1ea6933a161e5525f52b6","src/impls/enums.rs":"24a45ea825be15fd1f44f2a0554743bfb7a64377bd5d8340fcc4b74ffcc2a984","src/impls/func/from_arg.rs":"beb11862437ed7a9ebcc32be08b477df1f6cc5d18c7bbc759376af23d5b7c621","src/impls/func/function_impls.rs":"221befe4f3747261cc5ba59f626924db898421679832472f0dd18f0c61e0aa81","src/impls/func/get_ownership.rs":"ac3fc06d566e6472b9a21640e61eb7b2d33b21e001a7ef50384cc3d699cca38d","src/impls/func/into_return.rs":"023d42fcb42f851b34247ba95444392a250ac2cb5831a58b576bb9d6d0c49e2e","src/impls/func/mod.rs":"f8a714aea7c252901ac07f4769e43ca469917ba875bdda535f3525ae859c10cc","src/impls/mod.rs":"67e396cead26316627490a6c21811f5c788de351fa327c17478c24f77637aa67","src/impls/opaque.rs":"62c907d12a0f09a9b8c6ebee8c2182cea670a93cc31da8272949d6c616b71b94","src/impls/structs.rs":"90736a6e12638f9859f39b6f2d8001061e9f663999045e8cba045d47e86d7709","src/impls/tuple_structs.rs":"7257646d2626003ef486ed097372cbeb092b7a5520b91ecf103c0164fe19da92","src/impls/typed.rs":"05a2cea56daa38820b6fd26b021bd480d189fdbb65dfe493577629894c4168c1","src/lib.rs":"83d446dd3e8838191f3d47c25c490b091ed32076ddc7222335ae2e9eef090ec3","src/meta.rs":"86095b70f58e7ad91056764bb945807e4b583e44840632a18e2e18f2b1d72dc6","src/reflect_opaque.rs":"6e932e0bf00a5ba414254ac6335fc089004736759a92e4f99b882400cba29631","src/registration.rs":"83713a6cd1f37a0dc03608886f43d3bd56f37692bfa941648d25f76da0dcc6da","src/remote.rs":"16b97c74f1c11198270a0b8293acdf0c487928d0a6fd215c536b2a826a3f1ec0","src/result_sifter.rs":"552a265046f63901a98610a6aa89a4098e9272566de60f0df05bd603fb91e3e3","src/serialization.rs":"4e97e6884ff8d7bd9e6b3e9220393fd8ae5dc0972805507e0688af42bd1c772d","src/string_expr.rs":"fbfcf24b76318b2fa23346c714f791368fd33ac641319d6b2d0e453e2b920d77","src/struct_utility.rs":"5e5ecce97502a42e4b34962e46982451852ac1b7df2642a1cda45644a708a39b","src/trait_reflection.rs":"ef11b39b720d3f32e8c74c04445e4f484daf517676364750273929703075301a","src/type_path.rs":"f9345bb63b7f2555923049f509fae27fee9a1b779220f31d3d1c502fb2af7eb1","src/where_clause_options.rs":"d6f5e9d6062c315d69beda1e8e3e8af04b799aefc6577f94aeff23665fe479fa"},"package":"40ddadc55fe16b45faaa54ab2f9cb00548013c74812e8b018aa172387103cce6"}

397
vendor/bevy_reflect_derive/Cargo.lock generated vendored Normal file
View File

@@ -0,0 +1,397 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "autocfg"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]]
name = "bevy_macro_utils"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "052eeebcb8e7e072beea5031b227d9a290f8a7fbbb947573ab6ec81df0fb94be"
dependencies = [
"parking_lot",
"proc-macro2",
"quote",
"syn",
"toml_edit",
]
[[package]]
name = "bevy_reflect_derive"
version = "0.16.1"
dependencies = [
"bevy_macro_utils",
"proc-macro2",
"quote",
"syn",
"uuid",
]
[[package]]
name = "bitflags"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
[[package]]
name = "bumpalo"
version = "3.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "getrandom"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
dependencies = [
"cfg-if",
"libc",
"r-efi",
"wasi",
]
[[package]]
name = "hashbrown"
version = "0.15.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3"
[[package]]
name = "indexmap"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "js-sys"
version = "0.3.77"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
dependencies = [
"once_cell",
"wasm-bindgen",
]
[[package]]
name = "libc"
version = "0.2.172"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
[[package]]
name = "lock_api"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
[[package]]
name = "memchr"
version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "once_cell"
version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "parking_lot"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-targets",
]
[[package]]
name = "proc-macro2"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
dependencies = [
"proc-macro2",
]
[[package]]
name = "r-efi"
version = "5.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
[[package]]
name = "redox_syscall"
version = "0.5.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af"
dependencies = [
"bitflags",
]
[[package]]
name = "rustversion"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "smallvec"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9"
[[package]]
name = "syn"
version = "2.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "toml_datetime"
version = "0.6.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3"
[[package]]
name = "toml_edit"
version = "0.22.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e"
dependencies = [
"indexmap",
"toml_datetime",
"winnow",
]
[[package]]
name = "unicode-ident"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]]
name = "uuid"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d"
dependencies = [
"getrandom",
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "wasi"
version = "0.14.2+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
dependencies = [
"wit-bindgen-rt",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
dependencies = [
"cfg-if",
"once_cell",
"rustversion",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
dependencies = [
"bumpalo",
"log",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
dependencies = [
"unicode-ident",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "winnow"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec"
dependencies = [
"memchr",
]
[[package]]
name = "wit-bindgen-rt"
version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
dependencies = [
"bitflags",
]

103
vendor/bevy_reflect_derive/Cargo.toml vendored Normal file
View File

@@ -0,0 +1,103 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2024"
name = "bevy_reflect_derive"
version = "0.16.1"
build = false
autolib = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "Derive implementations for bevy_reflect"
homepage = "https://bevyengine.org"
readme = false
keywords = ["bevy"]
license = "MIT OR Apache-2.0"
repository = "https://github.com/bevyengine/bevy"
resolver = "2"
[package.metadata.docs.rs]
all-features = true
rustdoc-args = [
"-Zunstable-options",
"--generate-link-to-definition",
]
[features]
default = []
documentation = []
functions = []
[lib]
name = "bevy_reflect_derive"
path = "src/lib.rs"
proc-macro = true
[dependencies.bevy_macro_utils]
version = "0.16.1"
[dependencies.proc-macro2]
version = "1.0"
[dependencies.quote]
version = "1.0"
[dependencies.syn]
version = "2.0"
features = ["full"]
[dependencies.uuid]
version = "1.13.1"
features = ["v4"]
[target.'cfg(target_arch = "wasm32")'.dependencies.uuid]
version = "1.13.1"
features = ["js"]
default-features = false
[lints.clippy]
alloc_instead_of_core = "warn"
allow_attributes = "warn"
allow_attributes_without_reason = "warn"
doc_markdown = "warn"
manual_let_else = "warn"
match_same_arms = "warn"
needless_lifetimes = "allow"
nonstandard_macro_braces = "warn"
print_stderr = "warn"
print_stdout = "warn"
ptr_as_ptr = "warn"
ptr_cast_constness = "warn"
redundant_closure_for_method_calls = "warn"
redundant_else = "warn"
ref_as_ptr = "warn"
semicolon_if_nothing_returned = "warn"
std_instead_of_alloc = "warn"
std_instead_of_core = "warn"
too_long_first_doc_paragraph = "allow"
too_many_arguments = "allow"
type_complexity = "allow"
undocumented_unsafe_blocks = "warn"
unwrap_or_default = "warn"
[lints.rust]
missing_docs = "warn"
unsafe_code = "deny"
unsafe_op_in_unsafe_fn = "warn"
unused_qualifications = "warn"
[lints.rust.unexpected_cfgs]
level = "warn"
priority = 0
check-cfg = ["cfg(docsrs_dep)"]

View File

@@ -0,0 +1,176 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

19
vendor/bevy_reflect_derive/LICENSE-MIT vendored Normal file
View File

@@ -0,0 +1,19 @@
MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,38 @@
use syn::{
parse::{Parse, ParseStream, Peek},
punctuated::Punctuated,
};
/// Returns a [`syn::parse::Parser`] which parses a stream of zero or more occurrences of `T`
/// separated by punctuation of type `P`, with optional trailing punctuation.
///
/// This is functionally the same as [`Punctuated::parse_terminated`],
/// but accepts a closure rather than a function pointer.
pub(crate) fn terminated_parser<T, P, F: FnMut(ParseStream) -> syn::Result<T>>(
terminator: P,
mut parser: F,
) -> impl FnOnce(ParseStream) -> syn::Result<Punctuated<T, P::Token>>
where
P: Peek,
P::Token: Parse,
{
let _ = terminator;
move |stream: ParseStream| {
let mut punctuated = Punctuated::new();
loop {
if stream.is_empty() {
break;
}
let value = parser(stream)?;
punctuated.push_value(value);
if stream.is_empty() {
break;
}
let punct = stream.parse()?;
punctuated.push_punct(punct);
}
Ok(punctuated)
}
}

View File

@@ -0,0 +1,620 @@
//! Contains code related to container attributes for reflected types.
//!
//! A container attribute is an attribute which applies to an entire struct or enum
//! as opposed to a particular field or variant. An example of such an attribute is
//! the derive helper attribute for `Reflect`, which looks like:
//! `#[reflect(PartialEq, Default, ...)]`.
use crate::{
attribute_parser::terminated_parser, custom_attributes::CustomAttributes,
derive_data::ReflectTraitToImpl,
};
use bevy_macro_utils::fq_std::{FQAny, FQClone, FQOption, FQResult};
use proc_macro2::{Ident, Span};
use quote::quote_spanned;
use syn::{
ext::IdentExt, parenthesized, parse::ParseStream, spanned::Spanned, token, Expr, LitBool,
MetaList, MetaNameValue, Path, Token, WhereClause,
};
mod kw {
syn::custom_keyword!(from_reflect);
syn::custom_keyword!(type_path);
syn::custom_keyword!(Debug);
syn::custom_keyword!(PartialEq);
syn::custom_keyword!(Hash);
syn::custom_keyword!(Clone);
syn::custom_keyword!(no_field_bounds);
syn::custom_keyword!(opaque);
}
// The "special" trait idents that are used internally for reflection.
// Received via attributes like `#[reflect(PartialEq, Hash, ...)]`
const DEBUG_ATTR: &str = "Debug";
const PARTIAL_EQ_ATTR: &str = "PartialEq";
const HASH_ATTR: &str = "Hash";
// The traits listed below are not considered "special" (i.e. they use the `ReflectMyTrait` syntax)
// but useful to know exist nonetheless
pub(crate) const REFLECT_DEFAULT: &str = "ReflectDefault";
// Attributes for `FromReflect` implementation
const FROM_REFLECT_ATTR: &str = "from_reflect";
// Attributes for `TypePath` implementation
const TYPE_PATH_ATTR: &str = "type_path";
// The error message to show when a trait/type is specified multiple times
const CONFLICTING_TYPE_DATA_MESSAGE: &str = "conflicting type data registration";
/// A marker for trait implementations registered via the `Reflect` derive macro.
#[derive(Clone, Default)]
pub(crate) enum TraitImpl {
/// The trait is not registered as implemented.
#[default]
NotImplemented,
/// The trait is registered as implemented.
Implemented(Span),
/// The trait is registered with a custom function rather than an actual implementation.
Custom(Path, Span),
}
impl TraitImpl {
/// Merges this [`TraitImpl`] with another.
///
/// Update `self` with whichever value is not [`TraitImpl::NotImplemented`].
/// If `other` is [`TraitImpl::NotImplemented`], then `self` is not modified.
/// An error is returned if neither value is [`TraitImpl::NotImplemented`].
pub fn merge(&mut self, other: TraitImpl) -> Result<(), syn::Error> {
match (&self, other) {
(TraitImpl::NotImplemented, value) => {
*self = value;
Ok(())
}
(_, TraitImpl::NotImplemented) => Ok(()),
(_, TraitImpl::Implemented(span) | TraitImpl::Custom(_, span)) => {
Err(syn::Error::new(span, CONFLICTING_TYPE_DATA_MESSAGE))
}
}
}
}
/// A collection of attributes used for deriving `FromReflect`.
#[derive(Clone, Default)]
pub(crate) struct FromReflectAttrs {
auto_derive: Option<LitBool>,
}
impl FromReflectAttrs {
/// Returns true if `FromReflect` should be automatically derived as part of the `Reflect` derive.
pub fn should_auto_derive(&self) -> bool {
self.auto_derive.as_ref().is_none_or(LitBool::value)
}
}
/// A collection of attributes used for deriving `TypePath` via the `Reflect` derive.
///
/// Note that this differs from the attributes used by the `TypePath` derive itself,
/// which look like `[type_path = "my_crate::foo"]`.
/// The attributes used by reflection take the form `#[reflect(type_path = false)]`.
///
/// These attributes should only be used for `TypePath` configuration specific to
/// deriving `Reflect`.
#[derive(Clone, Default)]
pub(crate) struct TypePathAttrs {
auto_derive: Option<LitBool>,
}
impl TypePathAttrs {
/// Returns true if `TypePath` should be automatically derived as part of the `Reflect` derive.
pub fn should_auto_derive(&self) -> bool {
self.auto_derive.as_ref().is_none_or(LitBool::value)
}
}
/// A collection of traits that have been registered for a reflected type.
///
/// This keeps track of a few traits that are utilized internally for reflection
/// (we'll call these traits _special traits_ within this context), but it
/// will also keep track of all registered traits. Traits are registered as part of the
/// `Reflect` derive macro using the helper attribute: `#[reflect(...)]`.
///
/// The list of special traits are as follows:
/// * `Debug`
/// * `Hash`
/// * `PartialEq`
///
/// When registering a trait, there are a few things to keep in mind:
/// * Traits must have a valid `Reflect{}` struct in scope. For example, `Default`
/// needs `bevy_reflect::prelude::ReflectDefault` in scope.
/// * Traits must be single path identifiers. This means you _must_ use `Default`
/// instead of `std::default::Default` (otherwise it will try to register `Reflectstd`!)
/// * A custom function may be supplied in place of an actual implementation
/// for the special traits (but still follows the same single-path identifier
/// rules as normal).
///
/// # Example
///
/// Registering the `Default` implementation:
///
/// ```ignore (bevy_reflect is not accessible from this crate)
/// // Import ReflectDefault so it's accessible by the derive macro
/// use bevy_reflect::prelude::ReflectDefault;
///
/// #[derive(Reflect, Default)]
/// #[reflect(Default)]
/// struct Foo;
/// ```
///
/// Registering the `Hash` implementation:
///
/// ```ignore (bevy_reflect is not accessible from this crate)
/// // `Hash` is a "special trait" and does not need (nor have) a ReflectHash struct
///
/// #[derive(Reflect, Hash)]
/// #[reflect(Hash)]
/// struct Foo;
/// ```
///
/// Registering the `Hash` implementation using a custom function:
///
/// ```ignore (bevy_reflect is not accessible from this crate)
/// // This function acts as our `Hash` implementation and
/// // corresponds to the `Reflect::reflect_hash` method.
/// fn get_hash(foo: &Foo) -> Option<u64> {
/// Some(123)
/// }
///
/// #[derive(Reflect)]
/// // Register the custom `Hash` function
/// #[reflect(Hash(get_hash))]
/// struct Foo;
/// ```
///
/// > __Note:__ Registering a custom function only works for special traits.
#[derive(Default, Clone)]
pub(crate) struct ContainerAttributes {
clone: TraitImpl,
debug: TraitImpl,
hash: TraitImpl,
partial_eq: TraitImpl,
from_reflect_attrs: FromReflectAttrs,
type_path_attrs: TypePathAttrs,
custom_where: Option<WhereClause>,
no_field_bounds: bool,
custom_attributes: CustomAttributes,
is_opaque: bool,
idents: Vec<Ident>,
}
impl ContainerAttributes {
/// Parse a comma-separated list of container attributes.
///
/// # Example
/// - `Hash, Debug(custom_debug), MyTrait`
pub fn parse_terminated(
&mut self,
input: ParseStream,
trait_: ReflectTraitToImpl,
) -> syn::Result<()> {
terminated_parser(Token![,], |stream| {
self.parse_container_attribute(stream, trait_)
})(input)?;
Ok(())
}
/// Parse the contents of a `#[reflect(...)]` attribute into a [`ContainerAttributes`] instance.
///
/// # Example
/// - `#[reflect(Hash, Debug(custom_debug), MyTrait)]`
/// - `#[reflect(no_field_bounds)]`
pub fn parse_meta_list(
&mut self,
meta: &MetaList,
trait_: ReflectTraitToImpl,
) -> syn::Result<()> {
meta.parse_args_with(|stream: ParseStream| self.parse_terminated(stream, trait_))
}
/// Parse a single container attribute.
fn parse_container_attribute(
&mut self,
input: ParseStream,
trait_: ReflectTraitToImpl,
) -> syn::Result<()> {
let lookahead = input.lookahead1();
if lookahead.peek(Token![@]) {
self.custom_attributes.parse_custom_attribute(input)
} else if lookahead.peek(Token![where]) {
self.parse_custom_where(input)
} else if lookahead.peek(kw::from_reflect) {
self.parse_from_reflect(input, trait_)
} else if lookahead.peek(kw::type_path) {
self.parse_type_path(input, trait_)
} else if lookahead.peek(kw::opaque) {
self.parse_opaque(input)
} else if lookahead.peek(kw::no_field_bounds) {
self.parse_no_field_bounds(input)
} else if lookahead.peek(kw::Clone) {
self.parse_clone(input)
} else if lookahead.peek(kw::Debug) {
self.parse_debug(input)
} else if lookahead.peek(kw::Hash) {
self.parse_hash(input)
} else if lookahead.peek(kw::PartialEq) {
self.parse_partial_eq(input)
} else if lookahead.peek(Ident::peek_any) {
self.parse_ident(input)
} else {
Err(lookahead.error())
}
}
/// Parse an ident (for registration).
///
/// Examples:
/// - `#[reflect(MyTrait)]` (registers `ReflectMyTrait`)
fn parse_ident(&mut self, input: ParseStream) -> syn::Result<()> {
let ident = input.parse::<Ident>()?;
if input.peek(token::Paren) {
return Err(syn::Error::new(ident.span(), format!(
"only [{DEBUG_ATTR:?}, {PARTIAL_EQ_ATTR:?}, {HASH_ATTR:?}] may specify custom functions",
)));
}
let ident_name = ident.to_string();
// Create the reflect ident
let mut reflect_ident = crate::ident::get_reflect_ident(&ident_name);
// We set the span to the old ident so any compile errors point to that ident instead
reflect_ident.set_span(ident.span());
add_unique_ident(&mut self.idents, reflect_ident)?;
Ok(())
}
/// Parse `clone` attribute.
///
/// Examples:
/// - `#[reflect(Clone)]`
/// - `#[reflect(Clone(custom_clone_fn))]`
fn parse_clone(&mut self, input: ParseStream) -> syn::Result<()> {
let ident = input.parse::<kw::Clone>()?;
if input.peek(token::Paren) {
let content;
parenthesized!(content in input);
let path = content.parse::<Path>()?;
self.clone.merge(TraitImpl::Custom(path, ident.span))?;
} else {
self.clone = TraitImpl::Implemented(ident.span);
}
Ok(())
}
/// Parse special `Debug` registration.
///
/// Examples:
/// - `#[reflect(Debug)]`
/// - `#[reflect(Debug(custom_debug_fn))]`
fn parse_debug(&mut self, input: ParseStream) -> syn::Result<()> {
let ident = input.parse::<kw::Debug>()?;
if input.peek(token::Paren) {
let content;
parenthesized!(content in input);
let path = content.parse::<Path>()?;
self.debug.merge(TraitImpl::Custom(path, ident.span))?;
} else {
self.debug = TraitImpl::Implemented(ident.span);
}
Ok(())
}
/// Parse special `PartialEq` registration.
///
/// Examples:
/// - `#[reflect(PartialEq)]`
/// - `#[reflect(PartialEq(custom_partial_eq_fn))]`
fn parse_partial_eq(&mut self, input: ParseStream) -> syn::Result<()> {
let ident = input.parse::<kw::PartialEq>()?;
if input.peek(token::Paren) {
let content;
parenthesized!(content in input);
let path = content.parse::<Path>()?;
self.partial_eq.merge(TraitImpl::Custom(path, ident.span))?;
} else {
self.partial_eq = TraitImpl::Implemented(ident.span);
}
Ok(())
}
/// Parse special `Hash` registration.
///
/// Examples:
/// - `#[reflect(Hash)]`
/// - `#[reflect(Hash(custom_hash_fn))]`
fn parse_hash(&mut self, input: ParseStream) -> syn::Result<()> {
let ident = input.parse::<kw::Hash>()?;
if input.peek(token::Paren) {
let content;
parenthesized!(content in input);
let path = content.parse::<Path>()?;
self.hash.merge(TraitImpl::Custom(path, ident.span))?;
} else {
self.hash = TraitImpl::Implemented(ident.span);
}
Ok(())
}
/// Parse `opaque` attribute.
///
/// Examples:
/// - `#[reflect(opaque)]`
fn parse_opaque(&mut self, input: ParseStream) -> syn::Result<()> {
input.parse::<kw::opaque>()?;
self.is_opaque = true;
Ok(())
}
/// Parse `no_field_bounds` attribute.
///
/// Examples:
/// - `#[reflect(no_field_bounds)]`
fn parse_no_field_bounds(&mut self, input: ParseStream) -> syn::Result<()> {
input.parse::<kw::no_field_bounds>()?;
self.no_field_bounds = true;
Ok(())
}
/// Parse `where` attribute.
///
/// Examples:
/// - `#[reflect(where T: Debug)]`
fn parse_custom_where(&mut self, input: ParseStream) -> syn::Result<()> {
self.custom_where = Some(input.parse()?);
Ok(())
}
/// Parse `from_reflect` attribute.
///
/// Examples:
/// - `#[reflect(from_reflect = false)]`
fn parse_from_reflect(
&mut self,
input: ParseStream,
trait_: ReflectTraitToImpl,
) -> syn::Result<()> {
let pair = input.parse::<MetaNameValue>()?;
let extracted_bool = extract_bool(&pair.value, |lit| {
// Override `lit` if this is a `FromReflect` derive.
// This typically means a user is opting out of the default implementation
// from the `Reflect` derive and using the `FromReflect` derive directly instead.
if trait_ == ReflectTraitToImpl::FromReflect {
LitBool::new(true, Span::call_site())
} else {
lit.clone()
}
})?;
if let Some(existing) = &self.from_reflect_attrs.auto_derive {
if existing.value() != extracted_bool.value() {
return Err(syn::Error::new(
extracted_bool.span(),
format!("`{FROM_REFLECT_ATTR}` already set to {}", existing.value()),
));
}
} else {
self.from_reflect_attrs.auto_derive = Some(extracted_bool);
}
Ok(())
}
/// Parse `type_path` attribute.
///
/// Examples:
/// - `#[reflect(type_path = false)]`
fn parse_type_path(
&mut self,
input: ParseStream,
trait_: ReflectTraitToImpl,
) -> syn::Result<()> {
let pair = input.parse::<MetaNameValue>()?;
let extracted_bool = extract_bool(&pair.value, |lit| {
// Override `lit` if this is a `FromReflect` derive.
// This typically means a user is opting out of the default implementation
// from the `Reflect` derive and using the `FromReflect` derive directly instead.
if trait_ == ReflectTraitToImpl::TypePath {
LitBool::new(true, Span::call_site())
} else {
lit.clone()
}
})?;
if let Some(existing) = &self.type_path_attrs.auto_derive {
if existing.value() != extracted_bool.value() {
return Err(syn::Error::new(
extracted_bool.span(),
format!("`{TYPE_PATH_ATTR}` already set to {}", existing.value()),
));
}
} else {
self.type_path_attrs.auto_derive = Some(extracted_bool);
}
Ok(())
}
/// Returns true if the given reflected trait name (i.e. `ReflectDefault` for `Default`)
/// is registered for this type.
pub fn contains(&self, name: &str) -> bool {
self.idents.iter().any(|ident| ident == name)
}
/// The list of reflected traits by their reflected ident (i.e. `ReflectDefault` for `Default`).
pub fn idents(&self) -> &[Ident] {
&self.idents
}
/// The `FromReflect` configuration found within `#[reflect(...)]` attributes on this type.
#[expect(
clippy::wrong_self_convention,
reason = "Method returns `FromReflectAttrs`, does not actually convert data."
)]
pub fn from_reflect_attrs(&self) -> &FromReflectAttrs {
&self.from_reflect_attrs
}
/// The `TypePath` configuration found within `#[reflect(...)]` attributes on this type.
pub fn type_path_attrs(&self) -> &TypePathAttrs {
&self.type_path_attrs
}
/// Returns the implementation of `PartialReflect::reflect_hash` as a `TokenStream`.
///
/// If `Hash` was not registered, returns `None`.
pub fn get_hash_impl(&self, bevy_reflect_path: &Path) -> Option<proc_macro2::TokenStream> {
match &self.hash {
&TraitImpl::Implemented(span) => Some(quote_spanned! {span=>
fn reflect_hash(&self) -> #FQOption<u64> {
use ::core::hash::{Hash, Hasher};
let mut hasher = #bevy_reflect_path::utility::reflect_hasher();
Hash::hash(&#FQAny::type_id(self), &mut hasher);
Hash::hash(self, &mut hasher);
#FQOption::Some(Hasher::finish(&hasher))
}
}),
&TraitImpl::Custom(ref impl_fn, span) => Some(quote_spanned! {span=>
fn reflect_hash(&self) -> #FQOption<u64> {
#FQOption::Some(#impl_fn(self))
}
}),
TraitImpl::NotImplemented => None,
}
}
/// Returns the implementation of `PartialReflect::reflect_partial_eq` as a `TokenStream`.
///
/// If `PartialEq` was not registered, returns `None`.
pub fn get_partial_eq_impl(
&self,
bevy_reflect_path: &Path,
) -> Option<proc_macro2::TokenStream> {
match &self.partial_eq {
&TraitImpl::Implemented(span) => Some(quote_spanned! {span=>
fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::PartialReflect) -> #FQOption<bool> {
let value = <dyn #bevy_reflect_path::PartialReflect>::try_downcast_ref::<Self>(value);
if let #FQOption::Some(value) = value {
#FQOption::Some(::core::cmp::PartialEq::eq(self, value))
} else {
#FQOption::Some(false)
}
}
}),
&TraitImpl::Custom(ref impl_fn, span) => Some(quote_spanned! {span=>
fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::PartialReflect) -> #FQOption<bool> {
#FQOption::Some(#impl_fn(self, value))
}
}),
TraitImpl::NotImplemented => None,
}
}
/// Returns the implementation of `PartialReflect::debug` as a `TokenStream`.
///
/// If `Debug` was not registered, returns `None`.
pub fn get_debug_impl(&self) -> Option<proc_macro2::TokenStream> {
match &self.debug {
&TraitImpl::Implemented(span) => Some(quote_spanned! {span=>
fn debug(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
::core::fmt::Debug::fmt(self, f)
}
}),
&TraitImpl::Custom(ref impl_fn, span) => Some(quote_spanned! {span=>
fn debug(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
#impl_fn(self, f)
}
}),
TraitImpl::NotImplemented => None,
}
}
pub fn get_clone_impl(&self, bevy_reflect_path: &Path) -> Option<proc_macro2::TokenStream> {
match &self.clone {
&TraitImpl::Implemented(span) => Some(quote_spanned! {span=>
#[inline]
fn reflect_clone(&self) -> #FQResult<#bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #bevy_reflect_path::Reflect>, #bevy_reflect_path::ReflectCloneError> {
#FQResult::Ok(#bevy_reflect_path::__macro_exports::alloc_utils::Box::new(#FQClone::clone(self)))
}
}),
&TraitImpl::Custom(ref impl_fn, span) => Some(quote_spanned! {span=>
#[inline]
fn reflect_clone(&self) -> #FQResult<#bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #bevy_reflect_path::Reflect>, #bevy_reflect_path::ReflectCloneError> {
#FQResult::Ok(#bevy_reflect_path::__macro_exports::alloc_utils::Box::new(#impl_fn(self)))
}
}),
TraitImpl::NotImplemented => None,
}
}
pub fn custom_attributes(&self) -> &CustomAttributes {
&self.custom_attributes
}
/// The custom where configuration found within `#[reflect(...)]` attributes on this type.
pub fn custom_where(&self) -> Option<&WhereClause> {
self.custom_where.as_ref()
}
/// Returns true if the `no_field_bounds` attribute was found on this type.
pub fn no_field_bounds(&self) -> bool {
self.no_field_bounds
}
/// Returns true if the `opaque` attribute was found on this type.
pub fn is_opaque(&self) -> bool {
self.is_opaque
}
}
/// Adds an identifier to a vector of identifiers if it is not already present.
///
/// Returns an error if the identifier already exists in the list.
fn add_unique_ident(idents: &mut Vec<Ident>, ident: Ident) -> Result<(), syn::Error> {
let ident_name = ident.to_string();
if idents.iter().any(|i| i == ident_name.as_str()) {
return Err(syn::Error::new(ident.span(), CONFLICTING_TYPE_DATA_MESSAGE));
}
idents.push(ident);
Ok(())
}
/// Extract a boolean value from an expression.
///
/// The mapper exists so that the caller can conditionally choose to use the given
/// value or supply their own.
fn extract_bool(
value: &Expr,
mut mapper: impl FnMut(&LitBool) -> LitBool,
) -> Result<LitBool, syn::Error> {
match value {
Expr::Lit(syn::ExprLit {
lit: syn::Lit::Bool(lit),
..
}) => Ok(mapper(lit)),
_ => Err(syn::Error::new(value.span(), "Expected a boolean value")),
}
}

View File

@@ -0,0 +1,41 @@
use proc_macro2::TokenStream;
use quote::quote;
use syn::{parse::ParseStream, Expr, Path, Token};
#[derive(Default, Clone)]
pub(crate) struct CustomAttributes {
attributes: Vec<Expr>,
}
impl CustomAttributes {
/// Generates a `TokenStream` for `CustomAttributes` construction.
pub fn to_tokens(&self, bevy_reflect_path: &Path) -> TokenStream {
let attributes = self.attributes.iter().map(|value| {
quote! {
.with_attribute(#value)
}
});
quote! {
#bevy_reflect_path::attributes::CustomAttributes::default()
#(#attributes)*
}
}
/// Inserts a custom attribute into the list.
pub fn push(&mut self, value: Expr) -> syn::Result<()> {
self.attributes.push(value);
Ok(())
}
/// Parse `@` (custom attribute) attribute.
///
/// Examples:
/// - `#[reflect(@Foo))]`
/// - `#[reflect(@Bar::baz("qux"))]`
/// - `#[reflect(@0..256u8)]`
pub fn parse_custom_attribute(&mut self, input: ParseStream) -> syn::Result<()> {
input.parse::<Token![@]>()?;
self.push(input.parse()?)
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,78 @@
//! Contains code related to documentation reflection (requires the `documentation` feature).
use bevy_macro_utils::fq_std::FQOption;
use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
use syn::{Attribute, Expr, ExprLit, Lit, Meta};
/// A struct used to represent a type's documentation, if any.
///
/// When converted to a [`TokenStream`], this will output an `Option<String>`
/// containing the collection of doc comments.
#[derive(Default, Clone)]
pub(crate) struct Documentation {
docs: Vec<String>,
}
impl Documentation {
/// Create a new [`Documentation`] from a type's attributes.
///
/// This will collect all `#[doc = "..."]` attributes, including the ones generated via `///` and `//!`.
pub fn from_attributes<'a>(attributes: impl IntoIterator<Item = &'a Attribute>) -> Self {
let docs = attributes
.into_iter()
.filter_map(|attr| match &attr.meta {
Meta::NameValue(pair) if pair.path.is_ident("doc") => {
if let Expr::Lit(ExprLit {
lit: Lit::Str(lit), ..
}) = &pair.value
{
Some(lit.value())
} else {
None
}
}
_ => None,
})
.collect();
Self { docs }
}
/// The full docstring, if any.
pub fn doc_string(&self) -> Option<String> {
if self.docs.is_empty() {
return None;
}
let len = self.docs.len();
Some(
self.docs
.iter()
.enumerate()
.map(|(index, doc)| {
if index < len - 1 {
format!("{doc}\n")
} else {
doc.to_owned()
}
})
.collect(),
)
}
/// Push a new docstring to the collection
pub fn push(&mut self, doc: String) {
self.docs.push(doc);
}
}
impl ToTokens for Documentation {
fn to_tokens(&self, tokens: &mut TokenStream) {
if let Some(doc) = self.doc_string() {
quote!(#FQOption::Some(#doc)).to_tokens(tokens);
} else {
quote!(#FQOption::None).to_tokens(tokens);
}
}
}

View File

@@ -0,0 +1,394 @@
use crate::field_attributes::CloneBehavior;
use crate::{
derive_data::ReflectEnum, derive_data::StructField, field_attributes::DefaultBehavior,
ident::ident_or_index,
};
use bevy_macro_utils::fq_std::{FQClone, FQDefault, FQOption, FQResult};
use proc_macro2::{Ident, TokenStream};
use quote::{format_ident, quote, ToTokens};
pub(crate) struct EnumVariantOutputData {
/// The names of each variant as a string.
///
/// For example, `Some` and `None` for the `Option` enum.
pub variant_names: Vec<String>,
/// The pattern matching portion of each variant.
///
/// For example, `Option::Some { 0: _0 }` and `Option::None {}` for the `Option` enum.
pub variant_patterns: Vec<TokenStream>,
/// The constructor portion of each variant.
///
/// For example, `Option::Some { 0: value }` and `Option::None {}` for the `Option` enum.
pub variant_constructors: Vec<TokenStream>,
}
#[derive(Copy, Clone)]
pub(crate) struct VariantField<'a, 'b> {
/// The alias for the field.
///
/// This should be used whenever the field needs to be referenced in a token stream.
pub alias: &'a Ident,
/// The name of the variant that contains the field.
pub variant_name: &'a str,
/// The field data.
pub field: &'a StructField<'b>,
}
/// Trait used to control how enum variants are built.
pub(crate) trait VariantBuilder: Sized {
/// Returns the enum data.
fn reflect_enum(&self) -> &ReflectEnum;
/// Returns a token stream that accesses a field of a variant as an `Option<dyn Reflect>`.
///
/// The default implementation of this method will return a token stream
/// which gets the field dynamically so as to support `dyn Enum`.
///
/// # Parameters
/// * `this`: The identifier of the enum
/// * `field`: The field to access
fn access_field(&self, this: &Ident, field: VariantField) -> TokenStream {
match &field.field.data.ident {
Some(field_ident) => {
let name = field_ident.to_string();
quote!(#this.field(#name))
}
None => {
if let Some(field_index) = field.field.reflection_index {
quote!(#this.field_at(#field_index))
} else {
quote!(::core::compile_error!(
"internal bevy_reflect error: field should be active"
))
}
}
}
}
/// Returns a token stream that unwraps a field of a variant as a `&dyn Reflect`
/// (from an `Option<dyn Reflect>`).
///
/// # Parameters
/// * `field`: The field to access
fn unwrap_field(&self, field: VariantField) -> TokenStream;
/// Returns a token stream that constructs a field of a variant as a concrete type
/// (from a `&dyn Reflect`).
///
/// # Parameters
/// * `field`: The field to access
fn construct_field(&self, field: VariantField) -> TokenStream;
/// Returns a token stream that constructs an instance of an active field.
///
/// # Parameters
/// * `this`: The identifier of the enum
/// * `field`: The field to access
fn on_active_field(&self, this: &Ident, field: VariantField) -> TokenStream {
let bevy_reflect_path = self.reflect_enum().meta().bevy_reflect_path();
let field_accessor = self.access_field(this, field);
let alias = field.alias;
let field_ty = field.field.reflected_type();
let field_constructor = self.construct_field(field);
let construction = match &field.field.attrs.default {
DefaultBehavior::Func(path) => quote! {
if let #FQOption::Some(#alias) = #field_accessor {
#field_constructor
} else {
#path()
}
},
DefaultBehavior::Default => quote! {
if let #FQOption::Some(#alias) = #field_accessor {
#field_constructor
} else {
#FQDefault::default()
}
},
DefaultBehavior::Required => {
let field_unwrapper = self.unwrap_field(field);
quote! {{
// `#alias` is used by both the unwrapper and constructor
let #alias = #field_accessor;
let #alias = #field_unwrapper;
#field_constructor
}}
}
};
if field.field.attrs().remote.is_some() {
quote! {
<#field_ty as #bevy_reflect_path::ReflectRemote>::into_remote(#construction)
}
} else {
construction
}
}
/// Returns a token stream that constructs an instance of an ignored field.
///
/// # Parameters
/// * `field`: The field to access
fn on_ignored_field(&self, field: VariantField) -> TokenStream {
match &field.field.attrs.default {
DefaultBehavior::Func(path) => quote! { #path() },
_ => quote! { #FQDefault::default() },
}
}
/// Builds the enum variant output data.
fn build(&self, this: &Ident) -> EnumVariantOutputData {
let variants = self.reflect_enum().variants();
let mut variant_names = Vec::with_capacity(variants.len());
let mut variant_patterns = Vec::with_capacity(variants.len());
let mut variant_constructors = Vec::with_capacity(variants.len());
for variant in variants {
let variant_ident = &variant.data.ident;
let variant_name = variant_ident.to_string();
let variant_path = self.reflect_enum().get_unit(variant_ident);
let fields = variant.fields();
let mut field_patterns = Vec::with_capacity(fields.len());
let mut field_constructors = Vec::with_capacity(fields.len());
for field in fields {
let member = ident_or_index(field.data.ident.as_ref(), field.declaration_index);
let alias = format_ident!("_{}", member);
let variant_field = VariantField {
alias: &alias,
variant_name: &variant_name,
field,
};
let value = if field.attrs.ignore.is_ignored() {
self.on_ignored_field(variant_field)
} else {
self.on_active_field(this, variant_field)
};
field_patterns.push(quote! {
#member: #alias
});
field_constructors.push(quote! {
#member: #value
});
}
let pattern = quote! {
#variant_path { #( #field_patterns ),* }
};
let constructor = quote! {
#variant_path {
#( #field_constructors ),*
}
};
variant_names.push(variant_name);
variant_patterns.push(pattern);
variant_constructors.push(constructor);
}
EnumVariantOutputData {
variant_names,
variant_patterns,
variant_constructors,
}
}
}
/// Generates the enum variant output data needed to build the `FromReflect::from_reflect` implementation.
pub(crate) struct FromReflectVariantBuilder<'a> {
reflect_enum: &'a ReflectEnum<'a>,
}
impl<'a> FromReflectVariantBuilder<'a> {
pub fn new(reflect_enum: &'a ReflectEnum) -> Self {
Self { reflect_enum }
}
}
impl<'a> VariantBuilder for FromReflectVariantBuilder<'a> {
fn reflect_enum(&self) -> &ReflectEnum {
self.reflect_enum
}
fn unwrap_field(&self, field: VariantField) -> TokenStream {
let alias = field.alias;
quote!(#alias?)
}
fn construct_field(&self, field: VariantField) -> TokenStream {
let bevy_reflect_path = self.reflect_enum.meta().bevy_reflect_path();
let field_ty = field.field.reflected_type();
let alias = field.alias;
quote! {
<#field_ty as #bevy_reflect_path::FromReflect>::from_reflect(#alias)?
}
}
}
/// Generates the enum variant output data needed to build the `PartialReflect::try_apply` implementation.
pub(crate) struct TryApplyVariantBuilder<'a> {
reflect_enum: &'a ReflectEnum<'a>,
}
impl<'a> TryApplyVariantBuilder<'a> {
pub fn new(reflect_enum: &'a ReflectEnum) -> Self {
Self { reflect_enum }
}
}
impl<'a> VariantBuilder for TryApplyVariantBuilder<'a> {
fn reflect_enum(&self) -> &ReflectEnum {
self.reflect_enum
}
fn unwrap_field(&self, field: VariantField) -> TokenStream {
let VariantField {
alias,
variant_name,
field,
..
} = field;
let bevy_reflect_path = self.reflect_enum.meta().bevy_reflect_path();
let field_name = match &field.data.ident {
Some(ident) => format!("{ident}"),
None => format!(".{}", field.declaration_index),
};
quote! {
#alias.ok_or(#bevy_reflect_path::ApplyError::MissingEnumField {
variant_name: ::core::convert::Into::into(#variant_name),
field_name: ::core::convert::Into::into(#field_name)
})?
}
}
fn construct_field(&self, field: VariantField) -> TokenStream {
let bevy_reflect_path = self.reflect_enum.meta().bevy_reflect_path();
let alias = field.alias;
let field_ty = field.field.reflected_type();
quote! {
<#field_ty as #bevy_reflect_path::FromReflect>::from_reflect(#alias)
.ok_or(#bevy_reflect_path::ApplyError::MismatchedTypes {
from_type: ::core::convert::Into::into(
#bevy_reflect_path::DynamicTypePath::reflect_type_path(#alias)
),
to_type: ::core::convert::Into::into(<#field_ty as #bevy_reflect_path::TypePath>::type_path())
})?
}
}
}
/// Generates the enum variant output data needed to build the `Reflect::reflect_clone` implementation.
pub(crate) struct ReflectCloneVariantBuilder<'a> {
reflect_enum: &'a ReflectEnum<'a>,
}
impl<'a> ReflectCloneVariantBuilder<'a> {
pub fn new(reflect_enum: &'a ReflectEnum) -> Self {
Self { reflect_enum }
}
}
impl<'a> VariantBuilder for ReflectCloneVariantBuilder<'a> {
fn reflect_enum(&self) -> &ReflectEnum {
self.reflect_enum
}
fn access_field(&self, _ident: &Ident, field: VariantField) -> TokenStream {
let alias = field.alias;
quote!(#FQOption::Some(#alias))
}
fn unwrap_field(&self, field: VariantField) -> TokenStream {
let alias = field.alias;
quote!(#alias.unwrap())
}
fn construct_field(&self, field: VariantField) -> TokenStream {
let bevy_reflect_path = self.reflect_enum.meta().bevy_reflect_path();
let field_ty = field.field.reflected_type();
let alias = field.alias;
let alias = match &field.field.attrs.remote {
Some(wrapper_ty) => {
quote! {
<#wrapper_ty as #bevy_reflect_path::ReflectRemote>::as_wrapper(#alias)
}
}
None => alias.to_token_stream(),
};
match &field.field.attrs.clone {
CloneBehavior::Default => {
quote! {
#bevy_reflect_path::PartialReflect::reflect_clone(#alias)?
.take()
.map_err(|value| #bevy_reflect_path::ReflectCloneError::FailedDowncast {
expected: #bevy_reflect_path::__macro_exports::alloc_utils::Cow::Borrowed(
<#field_ty as #bevy_reflect_path::TypePath>::type_path()
),
received: #bevy_reflect_path::__macro_exports::alloc_utils::Cow::Owned(
#bevy_reflect_path::__macro_exports::alloc_utils::ToString::to_string(
#bevy_reflect_path::DynamicTypePath::reflect_type_path(&*value)
)
),
})?
}
}
CloneBehavior::Trait => {
quote! {
#FQClone::clone(#alias)
}
}
CloneBehavior::Func(clone_fn) => {
quote! {
#clone_fn(#alias)
}
}
}
}
fn on_active_field(&self, _this: &Ident, field: VariantField) -> TokenStream {
self.construct_field(field)
}
fn on_ignored_field(&self, field: VariantField) -> TokenStream {
let bevy_reflect_path = self.reflect_enum.meta().bevy_reflect_path();
let variant_name = field.variant_name;
let alias = field.alias;
match &field.field.attrs.clone {
CloneBehavior::Default => {
let field_id = field.field.field_id(bevy_reflect_path);
quote! {
return #FQResult::Err(
#bevy_reflect_path::ReflectCloneError::FieldNotCloneable {
field: #field_id,
variant: #FQOption::Some(#bevy_reflect_path::__macro_exports::alloc_utils::Cow::Borrowed(#variant_name)),
container_type_path: #bevy_reflect_path::__macro_exports::alloc_utils::Cow::Borrowed(<Self as #bevy_reflect_path::TypePath>::type_path())
}
)
}
}
CloneBehavior::Trait => quote! { #FQClone::clone(#alias) },
CloneBehavior::Func(clone_fn) => quote! { #clone_fn() },
}
}
}

View File

@@ -0,0 +1,274 @@
//! Contains code related to field attributes for reflected types.
//!
//! A field attribute is an attribute which applies to particular field or variant
//! as opposed to an entire struct or enum. An example of such an attribute is
//! the derive helper attribute for `Reflect`, which looks like: `#[reflect(ignore)]`.
use crate::{
attribute_parser::terminated_parser, custom_attributes::CustomAttributes,
REFLECT_ATTRIBUTE_NAME,
};
use quote::ToTokens;
use syn::{parse::ParseStream, Attribute, LitStr, Meta, Token, Type};
mod kw {
syn::custom_keyword!(ignore);
syn::custom_keyword!(skip_serializing);
syn::custom_keyword!(clone);
syn::custom_keyword!(default);
syn::custom_keyword!(remote);
}
pub(crate) const IGNORE_SERIALIZATION_ATTR: &str = "skip_serializing";
pub(crate) const IGNORE_ALL_ATTR: &str = "ignore";
pub(crate) const DEFAULT_ATTR: &str = "default";
pub(crate) const CLONE_ATTR: &str = "clone";
/// Stores data about if the field should be visible via the Reflect and serialization interfaces
///
/// Note the relationship between serialization and reflection is such that a member must be reflected in order to be serialized.
/// In boolean logic this is described as: `is_serialized -> is_reflected`, this means we can reflect something without serializing it but not the other way round.
/// The `is_reflected` predicate is provided as `self.is_active()`
#[derive(Default, Clone, Copy, PartialEq, Eq)]
pub(crate) enum ReflectIgnoreBehavior {
/// Don't ignore, appear to all systems
#[default]
None,
/// Ignore when serializing but not when reflecting
IgnoreSerialization,
/// Ignore both when serializing and reflecting
IgnoreAlways,
}
impl ReflectIgnoreBehavior {
/// Returns `true` if the ignoring behavior implies member is included in the reflection API, and false otherwise.
pub fn is_active(self) -> bool {
match self {
ReflectIgnoreBehavior::None | ReflectIgnoreBehavior::IgnoreSerialization => true,
ReflectIgnoreBehavior::IgnoreAlways => false,
}
}
/// The exact logical opposite of `self.is_active()` returns true iff this member is not part of the reflection API whatsoever (neither serialized nor reflected)
pub fn is_ignored(self) -> bool {
!self.is_active()
}
}
#[derive(Default, Clone)]
pub(crate) enum CloneBehavior {
#[default]
Default,
Trait,
Func(syn::ExprPath),
}
/// Controls how the default value is determined for a field.
#[derive(Default, Clone)]
pub(crate) enum DefaultBehavior {
/// Field is required.
#[default]
Required,
/// Field can be defaulted using `Default::default()`.
Default,
/// Field can be created using the given function name.
///
/// This assumes the function is in scope, is callable with zero arguments,
/// and returns the expected type.
Func(syn::ExprPath),
}
/// A container for attributes defined on a reflected type's field.
#[derive(Default, Clone)]
pub(crate) struct FieldAttributes {
/// Determines how this field should be ignored if at all.
pub ignore: ReflectIgnoreBehavior,
/// Sets the clone behavior of this field.
pub clone: CloneBehavior,
/// Sets the default behavior of this field.
pub default: DefaultBehavior,
/// Custom attributes created via `#[reflect(@...)]`.
pub custom_attributes: CustomAttributes,
/// For defining the remote wrapper type that should be used in place of the field for reflection logic.
pub remote: Option<Type>,
}
impl FieldAttributes {
/// Parse all field attributes marked "reflect" (such as `#[reflect(ignore)]`).
pub fn parse_attributes(attrs: &[Attribute]) -> syn::Result<Self> {
let mut args = FieldAttributes::default();
attrs
.iter()
.filter_map(|attr| {
if !attr.path().is_ident(REFLECT_ATTRIBUTE_NAME) {
// Not a reflect attribute -> skip
return None;
}
let Meta::List(meta) = &attr.meta else {
return Some(syn::Error::new_spanned(attr, "expected meta list"));
};
// Parse all attributes inside the list, collecting any errors
meta.parse_args_with(terminated_parser(Token![,], |stream| {
args.parse_field_attribute(stream)
}))
.err()
})
.reduce(|mut acc, err| {
acc.combine(err);
acc
})
.map_or(Ok(args), Err)
}
/// Parses a single field attribute.
fn parse_field_attribute(&mut self, input: ParseStream) -> syn::Result<()> {
let lookahead = input.lookahead1();
if lookahead.peek(Token![@]) {
self.parse_custom_attribute(input)
} else if lookahead.peek(kw::ignore) {
self.parse_ignore(input)
} else if lookahead.peek(kw::skip_serializing) {
self.parse_skip_serializing(input)
} else if lookahead.peek(kw::clone) {
self.parse_clone(input)
} else if lookahead.peek(kw::default) {
self.parse_default(input)
} else if lookahead.peek(kw::remote) {
self.parse_remote(input)
} else {
Err(lookahead.error())
}
}
/// Parse `ignore` attribute.
///
/// Examples:
/// - `#[reflect(ignore)]`
fn parse_ignore(&mut self, input: ParseStream) -> syn::Result<()> {
if self.ignore != ReflectIgnoreBehavior::None {
return Err(input.error(format!(
"only one of {:?} is allowed",
[IGNORE_ALL_ATTR, IGNORE_SERIALIZATION_ATTR]
)));
}
input.parse::<kw::ignore>()?;
self.ignore = ReflectIgnoreBehavior::IgnoreAlways;
Ok(())
}
/// Parse `skip_serializing` attribute.
///
/// Examples:
/// - `#[reflect(skip_serializing)]`
fn parse_skip_serializing(&mut self, input: ParseStream) -> syn::Result<()> {
if self.ignore != ReflectIgnoreBehavior::None {
return Err(input.error(format!(
"only one of {:?} is allowed",
[IGNORE_ALL_ATTR, IGNORE_SERIALIZATION_ATTR]
)));
}
input.parse::<kw::skip_serializing>()?;
self.ignore = ReflectIgnoreBehavior::IgnoreSerialization;
Ok(())
}
/// Parse `clone` attribute.
///
/// Examples:
/// - `#[reflect(clone)]`
/// - `#[reflect(clone = "path::to::func")]`
fn parse_clone(&mut self, input: ParseStream) -> syn::Result<()> {
if !matches!(self.clone, CloneBehavior::Default) {
return Err(input.error(format!("only one of {:?} is allowed", [CLONE_ATTR])));
}
input.parse::<kw::clone>()?;
if input.peek(Token![=]) {
input.parse::<Token![=]>()?;
let lit = input.parse::<LitStr>()?;
self.clone = CloneBehavior::Func(lit.parse()?);
} else {
self.clone = CloneBehavior::Trait;
}
Ok(())
}
/// Parse `default` attribute.
///
/// Examples:
/// - `#[reflect(default)]`
/// - `#[reflect(default = "path::to::func")]`
fn parse_default(&mut self, input: ParseStream) -> syn::Result<()> {
if !matches!(self.default, DefaultBehavior::Required) {
return Err(input.error(format!("only one of {:?} is allowed", [DEFAULT_ATTR])));
}
input.parse::<kw::default>()?;
if input.peek(Token![=]) {
input.parse::<Token![=]>()?;
let lit = input.parse::<LitStr>()?;
self.default = DefaultBehavior::Func(lit.parse()?);
} else {
self.default = DefaultBehavior::Default;
}
Ok(())
}
/// Parse `@` (custom attribute) attribute.
///
/// Examples:
/// - `#[reflect(@(foo = "bar"))]`
/// - `#[reflect(@(min = 0.0, max = 1.0))]`
fn parse_custom_attribute(&mut self, input: ParseStream) -> syn::Result<()> {
self.custom_attributes.parse_custom_attribute(input)
}
/// Parse `remote` attribute.
///
/// Examples:
/// - `#[reflect(remote = path::to::RemoteType)]`
fn parse_remote(&mut self, input: ParseStream) -> syn::Result<()> {
if let Some(remote) = self.remote.as_ref() {
return Err(input.error(format!(
"remote type already specified as {}",
remote.to_token_stream()
)));
}
input.parse::<kw::remote>()?;
input.parse::<Token![=]>()?;
self.remote = Some(input.parse()?);
Ok(())
}
/// Returns `Some(true)` if the field has a generic remote type.
///
/// If the remote type is not generic, returns `Some(false)`.
///
/// If the field does not have a remote type, returns `None`.
pub fn is_remote_generic(&self) -> Option<bool> {
if let Type::Path(type_path) = self.remote.as_ref()? {
type_path
.path
.segments
.last()
.map(|segment| !segment.arguments.is_empty())
} else {
Some(false)
}
}
}

View File

@@ -0,0 +1,330 @@
use crate::{
container_attributes::REFLECT_DEFAULT,
derive_data::ReflectEnum,
enum_utility::{EnumVariantOutputData, FromReflectVariantBuilder, VariantBuilder},
field_attributes::DefaultBehavior,
ident::ident_or_index,
where_clause_options::WhereClauseOptions,
ReflectMeta, ReflectStruct,
};
use bevy_macro_utils::fq_std::{FQClone, FQDefault, FQOption};
use proc_macro2::Span;
use quote::{quote, ToTokens};
use syn::{Field, Ident, Lit, LitInt, LitStr, Member};
/// Implements `FromReflect` for the given struct
pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> proc_macro2::TokenStream {
impl_struct_internal(reflect_struct, false)
}
/// Implements `FromReflect` for the given tuple struct
pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> proc_macro2::TokenStream {
impl_struct_internal(reflect_struct, true)
}
pub(crate) fn impl_opaque(meta: &ReflectMeta) -> proc_macro2::TokenStream {
let type_path = meta.type_path();
let bevy_reflect_path = meta.bevy_reflect_path();
let (impl_generics, ty_generics, where_clause) = type_path.generics().split_for_impl();
let where_from_reflect_clause = WhereClauseOptions::new(meta).extend_where_clause(where_clause);
quote! {
impl #impl_generics #bevy_reflect_path::FromReflect for #type_path #ty_generics #where_from_reflect_clause {
fn from_reflect(reflect: &dyn #bevy_reflect_path::PartialReflect) -> #FQOption<Self> {
#FQOption::Some(
#FQClone::clone(
<dyn #bevy_reflect_path::PartialReflect>::try_downcast_ref::<#type_path #ty_generics>(reflect)?
)
)
}
}
}
}
/// Implements `FromReflect` for the given enum type
pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> proc_macro2::TokenStream {
let fqoption = FQOption.into_token_stream();
let enum_path = reflect_enum.meta().type_path();
let bevy_reflect_path = reflect_enum.meta().bevy_reflect_path();
let ref_value = Ident::new("__param0", Span::call_site());
let EnumVariantOutputData {
variant_names,
variant_constructors,
..
} = FromReflectVariantBuilder::new(reflect_enum).build(&ref_value);
let match_branches = if reflect_enum.meta().is_remote_wrapper() {
quote! {
#(#variant_names => #fqoption::Some(Self(#variant_constructors)),)*
}
} else {
quote! {
#(#variant_names => #fqoption::Some(#variant_constructors),)*
}
};
let (impl_generics, ty_generics, where_clause) = enum_path.generics().split_for_impl();
// Add FromReflect bound for each active field
let where_from_reflect_clause = reflect_enum
.where_clause_options()
.extend_where_clause(where_clause);
quote! {
impl #impl_generics #bevy_reflect_path::FromReflect for #enum_path #ty_generics #where_from_reflect_clause {
fn from_reflect(#ref_value: &dyn #bevy_reflect_path::PartialReflect) -> #FQOption<Self> {
if let #bevy_reflect_path::ReflectRef::Enum(#ref_value) =
#bevy_reflect_path::PartialReflect::reflect_ref(#ref_value)
{
match #bevy_reflect_path::Enum::variant_name(#ref_value) {
#match_branches
name => panic!("variant with name `{}` does not exist on enum `{}`", name, <Self as #bevy_reflect_path::TypePath>::type_path()),
}
} else {
#FQOption::None
}
}
}
}
}
/// Container for a struct's members (field name or index) and their
/// corresponding values.
struct MemberValuePair(Vec<Member>, Vec<proc_macro2::TokenStream>);
impl MemberValuePair {
pub fn new(items: (Vec<Member>, Vec<proc_macro2::TokenStream>)) -> Self {
Self(items.0, items.1)
}
}
fn impl_struct_internal(
reflect_struct: &ReflectStruct,
is_tuple: bool,
) -> proc_macro2::TokenStream {
let fqoption = FQOption.into_token_stream();
let struct_path = reflect_struct.meta().type_path();
let remote_ty = reflect_struct.meta().remote_ty();
let bevy_reflect_path = reflect_struct.meta().bevy_reflect_path();
let ref_struct = Ident::new("__ref_struct", Span::call_site());
let ref_struct_type = if is_tuple {
Ident::new("TupleStruct", Span::call_site())
} else {
Ident::new("Struct", Span::call_site())
};
let MemberValuePair(active_members, active_values) =
get_active_fields(reflect_struct, &ref_struct, &ref_struct_type, is_tuple);
let is_defaultable = reflect_struct.meta().attrs().contains(REFLECT_DEFAULT);
// The constructed "Self" ident
let __this = Ident::new("__this", Span::call_site());
// The reflected type: either `Self` or a remote type
let (reflect_ty, constructor, retval) = if let Some(remote_ty) = remote_ty {
let constructor = match remote_ty.as_expr_path() {
Ok(path) => path,
Err(err) => return err.into_compile_error(),
};
let remote_ty = remote_ty.type_path();
(
quote!(#remote_ty),
quote!(#constructor),
quote!(Self(#__this)),
)
} else {
(quote!(Self), quote!(Self), quote!(#__this))
};
let constructor = if is_defaultable {
quote! {
let mut #__this = <#reflect_ty as #FQDefault>::default();
#(
if let #fqoption::Some(__field) = #active_values() {
// Iff field exists -> use its value
#__this.#active_members = __field;
}
)*
#FQOption::Some(#retval)
}
} else {
let MemberValuePair(ignored_members, ignored_values) = get_ignored_fields(reflect_struct);
quote! {
let #__this = #constructor {
#(#active_members: #active_values()?,)*
#(#ignored_members: #ignored_values,)*
};
#FQOption::Some(#retval)
}
};
let (impl_generics, ty_generics, where_clause) = reflect_struct
.meta()
.type_path()
.generics()
.split_for_impl();
// Add FromReflect bound for each active field
let where_from_reflect_clause = reflect_struct
.where_clause_options()
.extend_where_clause(where_clause);
quote! {
impl #impl_generics #bevy_reflect_path::FromReflect for #struct_path #ty_generics #where_from_reflect_clause {
fn from_reflect(reflect: &dyn #bevy_reflect_path::PartialReflect) -> #FQOption<Self> {
if let #bevy_reflect_path::ReflectRef::#ref_struct_type(#ref_struct)
= #bevy_reflect_path::PartialReflect::reflect_ref(reflect)
{
#constructor
} else {
#FQOption::None
}
}
}
}
}
/// Get the collection of ignored field definitions
///
/// Each value of the `MemberValuePair` is a token stream that generates a
/// a default value for the ignored field.
fn get_ignored_fields(reflect_struct: &ReflectStruct) -> MemberValuePair {
MemberValuePair::new(
reflect_struct
.ignored_fields()
.map(|field| {
let member = ident_or_index(field.data.ident.as_ref(), field.declaration_index);
let value = match &field.attrs.default {
DefaultBehavior::Func(path) => quote! {#path()},
_ => quote! {#FQDefault::default()},
};
(member, value)
})
.unzip(),
)
}
/// Get the collection of active field definitions.
///
/// Each value of the `MemberValuePair` is a token stream that generates a
/// closure of type `fn() -> Option<T>` where `T` is that field's type.
fn get_active_fields(
reflect_struct: &ReflectStruct,
dyn_struct_name: &Ident,
struct_type: &Ident,
is_tuple: bool,
) -> MemberValuePair {
let bevy_reflect_path = reflect_struct.meta().bevy_reflect_path();
MemberValuePair::new(
reflect_struct
.active_fields()
.map(|field| {
let member = ident_or_index(field.data.ident.as_ref(), field.declaration_index);
let accessor = get_field_accessor(
field.data,
field.reflection_index.expect("field should be active"),
is_tuple,
);
let ty = field.reflected_type().clone();
let real_ty = &field.data.ty;
let get_field = quote! {
#bevy_reflect_path::#struct_type::field(#dyn_struct_name, #accessor)
};
let into_remote = |value: proc_macro2::TokenStream| {
if field.attrs.is_remote_generic().unwrap_or_default() {
quote! {
#FQOption::Some(
// SAFETY: The remote type should always be a `#[repr(transparent)]` for the actual field type
unsafe {
::core::mem::transmute_copy::<#ty, #real_ty>(
&::core::mem::ManuallyDrop::new(#value?)
)
}
)
}
} else if field.attrs().remote.is_some() {
quote! {
#FQOption::Some(
// SAFETY: The remote type should always be a `#[repr(transparent)]` for the actual field type
unsafe {
::core::mem::transmute::<#ty, #real_ty>(#value?)
}
)
}
} else {
value
}
};
let value = match &field.attrs.default {
DefaultBehavior::Func(path) => {
let value = into_remote(quote! {
<#ty as #bevy_reflect_path::FromReflect>::from_reflect(field)
});
quote! {
(||
if let #FQOption::Some(field) = #get_field {
#value
} else {
#FQOption::Some(#path())
}
)
}
}
DefaultBehavior::Default => {
let value = into_remote(quote! {
<#ty as #bevy_reflect_path::FromReflect>::from_reflect(field)
});
quote! {
(||
if let #FQOption::Some(field) = #get_field {
#value
} else {
#FQOption::Some(#FQDefault::default())
}
)
}
}
DefaultBehavior::Required => {
let value = into_remote(quote! {
<#ty as #bevy_reflect_path::FromReflect>::from_reflect(#get_field?)
});
quote! {
(|| #value)
}
}
};
(member, value)
})
.unzip(),
)
}
/// Returns the accessor for a given field of a struct or tuple struct.
///
/// This differs from a member in that it needs to be a number for tuple structs
/// and a string for standard structs.
fn get_field_accessor(field: &Field, index: usize, is_tuple: bool) -> Lit {
if is_tuple {
Lit::Int(LitInt::new(&index.to_string(), Span::call_site()))
} else {
field
.ident
.as_ref()
.map(|ident| Lit::Str(LitStr::new(&ident.to_string(), Span::call_site())))
.unwrap_or_else(|| Lit::Str(LitStr::new(&index.to_string(), Span::call_site())))
}
}

View File

@@ -0,0 +1,72 @@
use crate::derive_data::ReflectMeta;
use proc_macro2::TokenStream;
use quote::quote;
use syn::punctuated::Punctuated;
use syn::{GenericParam, Token};
/// Creates a `TokenStream` for generating an expression that creates a `Generics` instance.
///
/// Returns `None` if `Generics` cannot or should not be generated.
pub(crate) fn generate_generics(meta: &ReflectMeta) -> Option<TokenStream> {
if !meta.attrs().type_path_attrs().should_auto_derive() {
// Cannot verify that all generic parameters implement `TypePath`
return None;
}
let bevy_reflect_path = meta.bevy_reflect_path();
let generics = meta
.type_path()
.generics()
.params
.iter()
.filter_map(|param| match param {
GenericParam::Type(ty_param) => {
let ident = &ty_param.ident;
let name = ident.to_string();
let with_default = ty_param
.default
.as_ref()
.map(|default_ty| quote!(.with_default::<#default_ty>()));
Some(quote! {
#bevy_reflect_path::GenericInfo::Type(
#bevy_reflect_path::TypeParamInfo::new::<#ident>(
#bevy_reflect_path::__macro_exports::alloc_utils::Cow::Borrowed(#name),
)
#with_default
)
})
}
GenericParam::Const(const_param) => {
let ty = &const_param.ty;
let name = const_param.ident.to_string();
let with_default = const_param.default.as_ref().map(|default| {
// We add the `as #ty` to ensure that the correct type is inferred.
quote!(.with_default(#default as #ty))
});
Some(quote! {
#[allow(
clippy::unnecessary_cast,
reason = "reflection requires an explicit type hint for const generics"
)]
#bevy_reflect_path::GenericInfo::Const(
#bevy_reflect_path::ConstParamInfo::new::<#ty>(
#bevy_reflect_path::__macro_exports::alloc_utils::Cow::Borrowed(#name),
)
#with_default
)
})
}
GenericParam::Lifetime(_) => None,
})
.collect::<Punctuated<_, Token![,]>>();
if generics.is_empty() {
// No generics to generate
return None;
}
Some(quote!(#bevy_reflect_path::Generics::from_iter([ #generics ])))
}

45
vendor/bevy_reflect_derive/src/ident.rs vendored Normal file
View File

@@ -0,0 +1,45 @@
use proc_macro2::{Ident, Span};
use syn::Member;
/// Returns the "reflected" ident for a given string.
///
/// # Example
///
/// ```
/// # use proc_macro2::Ident;
/// # // We can't import this method because of its visibility.
/// # fn get_reflect_ident(name: &str) -> Ident {
/// # let reflected = format!("Reflect{name}");
/// # Ident::new(&reflected, proc_macro2::Span::call_site())
/// # }
/// let reflected: Ident = get_reflect_ident("Hash");
/// assert_eq!("ReflectHash", reflected.to_string());
/// ```
pub(crate) fn get_reflect_ident(name: &str) -> Ident {
let reflected = format!("Reflect{name}");
Ident::new(&reflected, Span::call_site())
}
/// Returns a [`Member`] made of `ident` or `index` if `ident` is `None`.
///
/// Rust struct syntax allows for `Struct { foo: "string" }` with explicitly
/// named fields. It allows the `Struct { 0: "string" }` syntax when the struct
/// is declared as a tuple struct.
///
/// ```
/// struct Foo { field: &'static str }
/// struct Bar(&'static str);
/// let Foo { field } = Foo { field: "hi" };
/// let Bar { 0: field } = Bar { 0: "hello" };
/// let Bar(field) = Bar("hello"); // more common syntax
/// ```
///
/// This function helps field access in contexts where you are declaring either
/// a tuple struct or a struct with named fields. If you don't have a field name,
/// it means that you must access the field through an index.
pub(crate) fn ident_or_index(ident: Option<&Ident>, index: usize) -> Member {
ident.map_or_else(
|| Member::Unnamed(index.into()),
|ident| Member::Named(ident.clone()),
)
}

View File

@@ -0,0 +1,13 @@
use crate::{derive_data::ReflectDerive, remote::generate_remote_assertions};
use quote::quote;
/// Generates an anonymous block containing compile-time assertions.
pub(crate) fn impl_assertions(derive_data: &ReflectDerive) -> proc_macro2::TokenStream {
let mut output = quote!();
if let Some(assertions) = generate_remote_assertions(derive_data) {
output.extend(assertions);
}
output
}

View File

@@ -0,0 +1,158 @@
use bevy_macro_utils::fq_std::{FQAny, FQOption, FQResult};
use quote::quote;
use crate::{derive_data::ReflectMeta, where_clause_options::WhereClauseOptions};
pub fn impl_full_reflect(
meta: &ReflectMeta,
where_clause_options: &WhereClauseOptions,
) -> proc_macro2::TokenStream {
let bevy_reflect_path = meta.bevy_reflect_path();
let type_path = meta.type_path();
let (impl_generics, ty_generics, where_clause) = type_path.generics().split_for_impl();
let where_reflect_clause = where_clause_options.extend_where_clause(where_clause);
let any_impls = if meta.is_remote_wrapper() {
quote! {
#[inline]
fn into_any(self: #bevy_reflect_path::__macro_exports::alloc_utils::Box<Self>) -> #bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #FQAny> {
#bevy_reflect_path::__macro_exports::alloc_utils::Box::new(self.0)
}
#[inline]
fn as_any(&self) -> &dyn #FQAny {
&self.0
}
#[inline]
fn as_any_mut(&mut self) -> &mut dyn #FQAny {
&mut self.0
}
}
} else {
quote! {
#[inline]
fn into_any(self: #bevy_reflect_path::__macro_exports::alloc_utils::Box<Self>) -> #bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #FQAny> {
self
}
#[inline]
fn as_any(&self) -> &dyn #FQAny {
self
}
#[inline]
fn as_any_mut(&mut self) -> &mut dyn #FQAny {
self
}
}
};
quote! {
impl #impl_generics #bevy_reflect_path::Reflect for #type_path #ty_generics #where_reflect_clause {
#any_impls
#[inline]
fn into_reflect(self: #bevy_reflect_path::__macro_exports::alloc_utils::Box<Self>) -> #bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #bevy_reflect_path::Reflect> {
self
}
#[inline]
fn as_reflect(&self) -> &dyn #bevy_reflect_path::Reflect {
self
}
#[inline]
fn as_reflect_mut(&mut self) -> &mut dyn #bevy_reflect_path::Reflect {
self
}
#[inline]
fn set(
&mut self,
value: #bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #bevy_reflect_path::Reflect>
) -> #FQResult<(), #bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #bevy_reflect_path::Reflect>> {
*self = <dyn #bevy_reflect_path::Reflect>::take(value)?;
#FQResult::Ok(())
}
}
}
}
pub fn common_partial_reflect_methods(
meta: &ReflectMeta,
default_partial_eq_delegate: impl FnOnce() -> Option<proc_macro2::TokenStream>,
default_hash_delegate: impl FnOnce() -> Option<proc_macro2::TokenStream>,
) -> proc_macro2::TokenStream {
let bevy_reflect_path = meta.bevy_reflect_path();
let debug_fn = meta.attrs().get_debug_impl();
let partial_eq_fn = meta
.attrs()
.get_partial_eq_impl(bevy_reflect_path)
.or_else(move || {
let default_delegate = default_partial_eq_delegate();
default_delegate.map(|func| {
quote! {
fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::PartialReflect) -> #FQOption<bool> {
(#func)(self, value)
}
}
})
});
let hash_fn = meta
.attrs()
.get_hash_impl(bevy_reflect_path)
.or_else(move || {
let default_delegate = default_hash_delegate();
default_delegate.map(|func| {
quote! {
fn reflect_hash(&self) -> #FQOption<u64> {
(#func)(self)
}
}
})
});
quote! {
#[inline]
fn try_into_reflect(
self: #bevy_reflect_path::__macro_exports::alloc_utils::Box<Self>
) -> #FQResult<#bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #bevy_reflect_path::Reflect>, #bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #bevy_reflect_path::PartialReflect>> {
#FQResult::Ok(self)
}
#[inline]
fn try_as_reflect(&self) -> #FQOption<&dyn #bevy_reflect_path::Reflect> {
#FQOption::Some(self)
}
#[inline]
fn try_as_reflect_mut(&mut self) -> #FQOption<&mut dyn #bevy_reflect_path::Reflect> {
#FQOption::Some(self)
}
#[inline]
fn into_partial_reflect(self: #bevy_reflect_path::__macro_exports::alloc_utils::Box<Self>) -> #bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #bevy_reflect_path::PartialReflect> {
self
}
#[inline]
fn as_partial_reflect(&self) -> &dyn #bevy_reflect_path::PartialReflect {
self
}
#[inline]
fn as_partial_reflect_mut(&mut self) -> &mut dyn #bevy_reflect_path::PartialReflect {
self
}
#hash_fn
#partial_eq_fn
#debug_fn
}
}

View File

@@ -0,0 +1,439 @@
use crate::{
derive_data::{EnumVariantFields, ReflectEnum, StructField},
enum_utility::{EnumVariantOutputData, TryApplyVariantBuilder, VariantBuilder},
impls::{common_partial_reflect_methods, impl_full_reflect, impl_type_path, impl_typed},
};
use bevy_macro_utils::fq_std::{FQOption, FQResult};
use proc_macro2::{Ident, Span};
use quote::quote;
use syn::{Fields, Path};
pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> proc_macro2::TokenStream {
let bevy_reflect_path = reflect_enum.meta().bevy_reflect_path();
let enum_path = reflect_enum.meta().type_path();
let is_remote = reflect_enum.meta().is_remote_wrapper();
// For `match self` expressions where self is a reference
let match_this = if is_remote {
quote!(&self.0)
} else {
quote!(self)
};
// For `match self` expressions where self is a mutable reference
let match_this_mut = if is_remote {
quote!(&mut self.0)
} else {
quote!(self)
};
// For `*self` assignments
let deref_this = if is_remote {
quote!(self.0)
} else {
quote!(*self)
};
let ref_name = Ident::new("__name_param", Span::call_site());
let ref_index = Ident::new("__index_param", Span::call_site());
let ref_value = Ident::new("__value_param", Span::call_site());
let where_clause_options = reflect_enum.where_clause_options();
let EnumImpls {
enum_field,
enum_field_mut,
enum_field_at,
enum_field_at_mut,
enum_index_of,
enum_name_at,
enum_field_len,
enum_variant_name,
enum_variant_index,
enum_variant_type,
} = generate_impls(reflect_enum, &ref_index, &ref_name);
let EnumVariantOutputData {
variant_names,
variant_constructors,
..
} = TryApplyVariantBuilder::new(reflect_enum).build(&ref_value);
let typed_impl = impl_typed(
reflect_enum.meta(),
&where_clause_options,
reflect_enum.to_info_tokens(),
);
let type_path_impl = impl_type_path(reflect_enum.meta());
let full_reflect_impl = impl_full_reflect(reflect_enum.meta(), &where_clause_options);
let common_methods = common_partial_reflect_methods(
reflect_enum.meta(),
|| Some(quote!(#bevy_reflect_path::enum_partial_eq)),
|| Some(quote!(#bevy_reflect_path::enum_hash)),
);
let clone_fn = reflect_enum.get_clone_impl();
#[cfg(not(feature = "functions"))]
let function_impls = None::<proc_macro2::TokenStream>;
#[cfg(feature = "functions")]
let function_impls =
crate::impls::impl_function_traits(reflect_enum.meta(), &where_clause_options);
let get_type_registration_impl = reflect_enum.get_type_registration(&where_clause_options);
let (impl_generics, ty_generics, where_clause) =
reflect_enum.meta().type_path().generics().split_for_impl();
let where_reflect_clause = where_clause_options.extend_where_clause(where_clause);
quote! {
#get_type_registration_impl
#typed_impl
#type_path_impl
#full_reflect_impl
#function_impls
impl #impl_generics #bevy_reflect_path::Enum for #enum_path #ty_generics #where_reflect_clause {
fn field(&self, #ref_name: &str) -> #FQOption<&dyn #bevy_reflect_path::PartialReflect> {
match #match_this {
#(#enum_field,)*
_ => #FQOption::None,
}
}
fn field_at(&self, #ref_index: usize) -> #FQOption<&dyn #bevy_reflect_path::PartialReflect> {
match #match_this {
#(#enum_field_at,)*
_ => #FQOption::None,
}
}
fn field_mut(&mut self, #ref_name: &str) -> #FQOption<&mut dyn #bevy_reflect_path::PartialReflect> {
match #match_this_mut {
#(#enum_field_mut,)*
_ => #FQOption::None,
}
}
fn field_at_mut(&mut self, #ref_index: usize) -> #FQOption<&mut dyn #bevy_reflect_path::PartialReflect> {
match #match_this_mut {
#(#enum_field_at_mut,)*
_ => #FQOption::None,
}
}
fn index_of(&self, #ref_name: &str) -> #FQOption<usize> {
match #match_this {
#(#enum_index_of,)*
_ => #FQOption::None,
}
}
fn name_at(&self, #ref_index: usize) -> #FQOption<&str> {
match #match_this {
#(#enum_name_at,)*
_ => #FQOption::None,
}
}
fn iter_fields(&self) -> #bevy_reflect_path::VariantFieldIter {
#bevy_reflect_path::VariantFieldIter::new(self)
}
#[inline]
fn field_len(&self) -> usize {
match #match_this {
#(#enum_field_len,)*
_ => 0,
}
}
#[inline]
fn variant_name(&self) -> &str {
match #match_this {
#(#enum_variant_name,)*
_ => unreachable!(),
}
}
#[inline]
fn variant_index(&self) -> usize {
match #match_this {
#(#enum_variant_index,)*
_ => unreachable!(),
}
}
#[inline]
fn variant_type(&self) -> #bevy_reflect_path::VariantType {
match #match_this {
#(#enum_variant_type,)*
_ => unreachable!(),
}
}
fn to_dynamic_enum(&self) -> #bevy_reflect_path::DynamicEnum {
#bevy_reflect_path::DynamicEnum::from_ref::<Self>(self)
}
}
impl #impl_generics #bevy_reflect_path::PartialReflect for #enum_path #ty_generics #where_reflect_clause {
#[inline]
fn get_represented_type_info(&self) -> #FQOption<&'static #bevy_reflect_path::TypeInfo> {
#FQOption::Some(<Self as #bevy_reflect_path::Typed>::type_info())
}
#[inline]
fn try_apply(
&mut self,
#ref_value: &dyn #bevy_reflect_path::PartialReflect
) -> #FQResult<(), #bevy_reflect_path::ApplyError> {
if let #bevy_reflect_path::ReflectRef::Enum(#ref_value) =
#bevy_reflect_path::PartialReflect::reflect_ref(#ref_value) {
if #bevy_reflect_path::Enum::variant_name(self) == #bevy_reflect_path::Enum::variant_name(#ref_value) {
// Same variant -> just update fields
match #bevy_reflect_path::Enum::variant_type(#ref_value) {
#bevy_reflect_path::VariantType::Struct => {
for field in #bevy_reflect_path::Enum::iter_fields(#ref_value) {
let name = field.name().unwrap();
if let #FQOption::Some(v) = #bevy_reflect_path::Enum::field_mut(self, name) {
#bevy_reflect_path::PartialReflect::try_apply(v, field.value())?;
}
}
}
#bevy_reflect_path::VariantType::Tuple => {
for (index, field) in ::core::iter::Iterator::enumerate(#bevy_reflect_path::Enum::iter_fields(#ref_value)) {
if let #FQOption::Some(v) = #bevy_reflect_path::Enum::field_at_mut(self, index) {
#bevy_reflect_path::PartialReflect::try_apply(v, field.value())?;
}
}
}
_ => {}
}
} else {
// New variant -> perform a switch
match #bevy_reflect_path::Enum::variant_name(#ref_value) {
#(#variant_names => {
#deref_this = #variant_constructors
})*
name => {
return #FQResult::Err(
#bevy_reflect_path::ApplyError::UnknownVariant {
enum_name: ::core::convert::Into::into(#bevy_reflect_path::DynamicTypePath::reflect_type_path(self)),
variant_name: ::core::convert::Into::into(name),
}
);
}
}
}
} else {
return #FQResult::Err(
#bevy_reflect_path::ApplyError::MismatchedKinds {
from_kind: #bevy_reflect_path::PartialReflect::reflect_kind(#ref_value),
to_kind: #bevy_reflect_path::ReflectKind::Enum,
}
);
}
#FQResult::Ok(())
}
fn reflect_kind(&self) -> #bevy_reflect_path::ReflectKind {
#bevy_reflect_path::ReflectKind::Enum
}
fn reflect_ref(&self) -> #bevy_reflect_path::ReflectRef {
#bevy_reflect_path::ReflectRef::Enum(self)
}
fn reflect_mut(&mut self) -> #bevy_reflect_path::ReflectMut {
#bevy_reflect_path::ReflectMut::Enum(self)
}
fn reflect_owned(self: #bevy_reflect_path::__macro_exports::alloc_utils::Box<Self>) -> #bevy_reflect_path::ReflectOwned {
#bevy_reflect_path::ReflectOwned::Enum(self)
}
#common_methods
#clone_fn
}
}
}
struct EnumImpls {
enum_field: Vec<proc_macro2::TokenStream>,
enum_field_mut: Vec<proc_macro2::TokenStream>,
enum_field_at: Vec<proc_macro2::TokenStream>,
enum_field_at_mut: Vec<proc_macro2::TokenStream>,
enum_index_of: Vec<proc_macro2::TokenStream>,
enum_name_at: Vec<proc_macro2::TokenStream>,
enum_field_len: Vec<proc_macro2::TokenStream>,
enum_variant_name: Vec<proc_macro2::TokenStream>,
enum_variant_index: Vec<proc_macro2::TokenStream>,
enum_variant_type: Vec<proc_macro2::TokenStream>,
}
fn generate_impls(reflect_enum: &ReflectEnum, ref_index: &Ident, ref_name: &Ident) -> EnumImpls {
let bevy_reflect_path = reflect_enum.meta().bevy_reflect_path();
let mut enum_field = Vec::new();
let mut enum_field_mut = Vec::new();
let mut enum_field_at = Vec::new();
let mut enum_field_at_mut = Vec::new();
let mut enum_index_of = Vec::new();
let mut enum_name_at = Vec::new();
let mut enum_field_len = Vec::new();
let mut enum_variant_name = Vec::new();
let mut enum_variant_index = Vec::new();
let mut enum_variant_type = Vec::new();
for (variant_index, variant) in reflect_enum.variants().iter().enumerate() {
let ident = &variant.data.ident;
let name = ident.to_string();
let unit = reflect_enum.get_unit(ident);
let variant_type_ident = match variant.data.fields {
Fields::Unit => Ident::new("Unit", Span::call_site()),
Fields::Unnamed(..) => Ident::new("Tuple", Span::call_site()),
Fields::Named(..) => Ident::new("Struct", Span::call_site()),
};
enum_variant_name.push(quote! {
#unit{..} => #name
});
enum_variant_index.push(quote! {
#unit{..} => #variant_index
});
enum_variant_type.push(quote! {
#unit{..} => #bevy_reflect_path::VariantType::#variant_type_ident
});
fn process_fields(
fields: &[StructField],
mut f: impl FnMut(&StructField) + Sized,
) -> usize {
let mut field_len = 0;
for field in fields.iter() {
if field.attrs.ignore.is_ignored() {
// Ignored field
continue;
};
f(field);
field_len += 1;
}
field_len
}
/// Process the field value to account for remote types.
///
/// If the field is a remote type, then the value will be transmuted accordingly.
fn process_field_value(
ident: &Ident,
field: &StructField,
is_mutable: bool,
bevy_reflect_path: &Path,
) -> proc_macro2::TokenStream {
let method = if is_mutable {
quote!(as_wrapper_mut)
} else {
quote!(as_wrapper)
};
field
.attrs
.remote
.as_ref()
.map(|ty| quote!(<#ty as #bevy_reflect_path::ReflectRemote>::#method(#ident)))
.unwrap_or_else(|| quote!(#ident))
}
match &variant.fields {
EnumVariantFields::Unit => {
let field_len = process_fields(&[], |_| {});
enum_field_len.push(quote! {
#unit{..} => #field_len
});
}
EnumVariantFields::Unnamed(fields) => {
let field_len = process_fields(fields, |field: &StructField| {
let reflection_index = field
.reflection_index
.expect("reflection index should exist for active field");
let declare_field = syn::Index::from(field.declaration_index);
let __value = Ident::new("__value", Span::call_site());
let value_ref = process_field_value(&__value, field, false, bevy_reflect_path);
let value_mut = process_field_value(&__value, field, true, bevy_reflect_path);
enum_field_at.push(quote! {
#unit { #declare_field : #__value, .. } if #ref_index == #reflection_index => #FQOption::Some(#value_ref)
});
enum_field_at_mut.push(quote! {
#unit { #declare_field : #__value, .. } if #ref_index == #reflection_index => #FQOption::Some(#value_mut)
});
});
enum_field_len.push(quote! {
#unit{..} => #field_len
});
}
EnumVariantFields::Named(fields) => {
let field_len = process_fields(fields, |field: &StructField| {
let field_ident = field.data.ident.as_ref().unwrap();
let field_name = field_ident.to_string();
let reflection_index = field
.reflection_index
.expect("reflection index should exist for active field");
let __value = Ident::new("__value", Span::call_site());
let value_ref = process_field_value(&__value, field, false, bevy_reflect_path);
let value_mut = process_field_value(&__value, field, true, bevy_reflect_path);
enum_field.push(quote! {
#unit{ #field_ident: #__value, .. } if #ref_name == #field_name => #FQOption::Some(#value_ref)
});
enum_field_mut.push(quote! {
#unit{ #field_ident: #__value, .. } if #ref_name == #field_name => #FQOption::Some(#value_mut)
});
enum_field_at.push(quote! {
#unit{ #field_ident: #__value, .. } if #ref_index == #reflection_index => #FQOption::Some(#value_ref)
});
enum_field_at_mut.push(quote! {
#unit{ #field_ident: #__value, .. } if #ref_index == #reflection_index => #FQOption::Some(#value_mut)
});
enum_index_of.push(quote! {
#unit{ .. } if #ref_name == #field_name => #FQOption::Some(#reflection_index)
});
enum_name_at.push(quote! {
#unit{ .. } if #ref_index == #reflection_index => #FQOption::Some(#field_name)
});
});
enum_field_len.push(quote! {
#unit{..} => #field_len
});
}
};
}
EnumImpls {
enum_field,
enum_field_mut,
enum_field_at,
enum_field_at_mut,
enum_index_of,
enum_name_at,
enum_field_len,
enum_variant_name,
enum_variant_index,
enum_variant_type,
}
}

View File

@@ -0,0 +1,37 @@
use crate::{derive_data::ReflectMeta, where_clause_options::WhereClauseOptions};
use bevy_macro_utils::fq_std::FQResult;
use quote::quote;
pub(crate) fn impl_from_arg(
meta: &ReflectMeta,
where_clause_options: &WhereClauseOptions,
) -> proc_macro2::TokenStream {
let bevy_reflect = meta.bevy_reflect_path();
let type_path = meta.type_path();
let (impl_generics, ty_generics, where_clause) = type_path.generics().split_for_impl();
let where_reflect_clause = where_clause_options.extend_where_clause(where_clause);
quote! {
impl #impl_generics #bevy_reflect::func::args::FromArg for #type_path #ty_generics #where_reflect_clause {
type This<'from_arg> = #type_path #ty_generics;
fn from_arg(arg: #bevy_reflect::func::args::Arg) -> #FQResult<Self::This<'_>, #bevy_reflect::func::args::ArgError> {
arg.take_owned()
}
}
impl #impl_generics #bevy_reflect::func::args::FromArg for &'static #type_path #ty_generics #where_reflect_clause {
type This<'from_arg> = &'from_arg #type_path #ty_generics;
fn from_arg(arg: #bevy_reflect::func::args::Arg) -> #FQResult<Self::This<'_>, #bevy_reflect::func::args::ArgError> {
arg.take_ref()
}
}
impl #impl_generics #bevy_reflect::func::args::FromArg for &'static mut #type_path #ty_generics #where_reflect_clause {
type This<'from_arg> = &'from_arg mut #type_path #ty_generics;
fn from_arg(arg: #bevy_reflect::func::args::Arg) -> #FQResult<Self::This<'_>, #bevy_reflect::func::args::ArgError> {
arg.take_mut()
}
}
}
}

View File

@@ -0,0 +1,25 @@
use crate::{
derive_data::ReflectMeta,
impls::func::{
from_arg::impl_from_arg, get_ownership::impl_get_ownership, into_return::impl_into_return,
},
where_clause_options::WhereClauseOptions,
};
use quote::quote;
pub(crate) fn impl_function_traits(
meta: &ReflectMeta,
where_clause_options: &WhereClauseOptions,
) -> proc_macro2::TokenStream {
let get_ownership = impl_get_ownership(meta, where_clause_options);
let from_arg = impl_from_arg(meta, where_clause_options);
let into_return = impl_into_return(meta, where_clause_options);
quote! {
#get_ownership
#from_arg
#into_return
}
}

View File

@@ -0,0 +1,33 @@
use crate::{derive_data::ReflectMeta, where_clause_options::WhereClauseOptions};
use quote::quote;
pub(crate) fn impl_get_ownership(
meta: &ReflectMeta,
where_clause_options: &WhereClauseOptions,
) -> proc_macro2::TokenStream {
let bevy_reflect = meta.bevy_reflect_path();
let type_path = meta.type_path();
let (impl_generics, ty_generics, where_clause) = type_path.generics().split_for_impl();
let where_reflect_clause = where_clause_options.extend_where_clause(where_clause);
quote! {
impl #impl_generics #bevy_reflect::func::args::GetOwnership for #type_path #ty_generics #where_reflect_clause {
fn ownership() -> #bevy_reflect::func::args::Ownership {
#bevy_reflect::func::args::Ownership::Owned
}
}
impl #impl_generics #bevy_reflect::func::args::GetOwnership for &'_ #type_path #ty_generics #where_reflect_clause {
fn ownership() -> #bevy_reflect::func::args::Ownership {
#bevy_reflect::func::args::Ownership::Ref
}
}
impl #impl_generics #bevy_reflect::func::args::GetOwnership for &'_ mut #type_path #ty_generics #where_reflect_clause {
fn ownership() -> #bevy_reflect::func::args::Ownership {
#bevy_reflect::func::args::Ownership::Mut
}
}
}
}

View File

@@ -0,0 +1,33 @@
use crate::{derive_data::ReflectMeta, where_clause_options::WhereClauseOptions};
use quote::quote;
pub(crate) fn impl_into_return(
meta: &ReflectMeta,
where_clause_options: &WhereClauseOptions,
) -> proc_macro2::TokenStream {
let bevy_reflect = meta.bevy_reflect_path();
let type_path = meta.type_path();
let (impl_generics, ty_generics, where_clause) = type_path.generics().split_for_impl();
let where_reflect_clause = where_clause_options.extend_where_clause(where_clause);
quote! {
impl #impl_generics #bevy_reflect::func::IntoReturn for #type_path #ty_generics #where_reflect_clause {
fn into_return<'into_return>(self) -> #bevy_reflect::func::Return<'into_return> where Self: 'into_return {
#bevy_reflect::func::Return::Owned(#bevy_reflect::__macro_exports::alloc_utils::Box::new(self))
}
}
impl #impl_generics #bevy_reflect::func::IntoReturn for &#type_path #ty_generics #where_reflect_clause {
fn into_return<'into_return>(self) -> #bevy_reflect::func::Return<'into_return> where Self: 'into_return {
#bevy_reflect::func::Return::Ref(self)
}
}
impl #impl_generics #bevy_reflect::func::IntoReturn for &mut #type_path #ty_generics #where_reflect_clause {
fn into_return<'into_return>(self) -> #bevy_reflect::func::Return<'into_return> where Self: 'into_return {
#bevy_reflect::func::Return::Mut(self)
}
}
}
}

View File

@@ -0,0 +1,6 @@
pub(crate) use function_impls::impl_function_traits;
mod from_arg;
mod function_impls;
mod get_ownership;
mod into_return;

View File

@@ -0,0 +1,19 @@
mod assertions;
mod common;
mod enums;
#[cfg(feature = "functions")]
mod func;
mod opaque;
mod structs;
mod tuple_structs;
mod typed;
pub(crate) use assertions::impl_assertions;
pub(crate) use common::{common_partial_reflect_methods, impl_full_reflect};
pub(crate) use enums::impl_enum;
#[cfg(feature = "functions")]
pub(crate) use func::impl_function_traits;
pub(crate) use opaque::impl_opaque;
pub(crate) use structs::impl_struct;
pub(crate) use tuple_structs::impl_tuple_struct;
pub(crate) use typed::{impl_type_path, impl_typed};

View File

@@ -0,0 +1,125 @@
use crate::{
impls::{common_partial_reflect_methods, impl_full_reflect, impl_type_path, impl_typed},
where_clause_options::WhereClauseOptions,
ReflectMeta,
};
use bevy_macro_utils::fq_std::{FQClone, FQOption, FQResult};
use quote::quote;
/// Implements `GetTypeRegistration` and `Reflect` for the given type data.
pub(crate) fn impl_opaque(meta: &ReflectMeta) -> proc_macro2::TokenStream {
let bevy_reflect_path = meta.bevy_reflect_path();
let type_path = meta.type_path();
#[cfg(feature = "documentation")]
let with_docs = {
let doc = quote::ToTokens::to_token_stream(meta.doc());
Some(quote!(.with_docs(#doc)))
};
#[cfg(not(feature = "documentation"))]
let with_docs: Option<proc_macro2::TokenStream> = None;
let where_clause_options = WhereClauseOptions::new(meta);
let typed_impl = impl_typed(
meta,
&where_clause_options,
quote! {
let info = #bevy_reflect_path::OpaqueInfo::new::<Self>() #with_docs;
#bevy_reflect_path::TypeInfo::Opaque(info)
},
);
let type_path_impl = impl_type_path(meta);
let full_reflect_impl = impl_full_reflect(meta, &where_clause_options);
let common_methods = common_partial_reflect_methods(meta, || None, || None);
let clone_fn = meta.attrs().get_clone_impl(bevy_reflect_path);
let apply_impl = if let Some(remote_ty) = meta.remote_ty() {
let ty = remote_ty.type_path();
quote! {
if let #FQOption::Some(value) = <dyn #bevy_reflect_path::PartialReflect>::try_downcast_ref::<#ty>(value) {
*self = Self(#FQClone::clone(value));
return #FQResult::Ok(());
}
}
} else {
quote! {
if let #FQOption::Some(value) = <dyn #bevy_reflect_path::PartialReflect>::try_downcast_ref::<Self>(value) {
*self = #FQClone::clone(value);
return #FQResult::Ok(());
}
}
};
#[cfg(not(feature = "functions"))]
let function_impls = None::<proc_macro2::TokenStream>;
#[cfg(feature = "functions")]
let function_impls = crate::impls::impl_function_traits(meta, &where_clause_options);
let (impl_generics, ty_generics, where_clause) = type_path.generics().split_for_impl();
let where_reflect_clause = where_clause_options.extend_where_clause(where_clause);
let get_type_registration_impl = meta.get_type_registration(&where_clause_options);
quote! {
#get_type_registration_impl
#type_path_impl
#typed_impl
#full_reflect_impl
#function_impls
impl #impl_generics #bevy_reflect_path::PartialReflect for #type_path #ty_generics #where_reflect_clause {
#[inline]
fn get_represented_type_info(&self) -> #FQOption<&'static #bevy_reflect_path::TypeInfo> {
#FQOption::Some(<Self as #bevy_reflect_path::Typed>::type_info())
}
#[inline]
fn to_dynamic(&self) -> #bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #bevy_reflect_path::PartialReflect> {
#bevy_reflect_path::__macro_exports::alloc_utils::Box::new(#FQClone::clone(self))
}
#[inline]
fn try_apply(
&mut self,
value: &dyn #bevy_reflect_path::PartialReflect
) -> #FQResult<(), #bevy_reflect_path::ApplyError> {
#apply_impl
#FQResult::Err(
#bevy_reflect_path::ApplyError::MismatchedTypes {
from_type: ::core::convert::Into::into(#bevy_reflect_path::DynamicTypePath::reflect_type_path(value)),
to_type: ::core::convert::Into::into(<Self as #bevy_reflect_path::TypePath>::type_path()),
}
)
}
#[inline]
fn reflect_kind(&self) -> #bevy_reflect_path::ReflectKind {
#bevy_reflect_path::ReflectKind::Opaque
}
#[inline]
fn reflect_ref(&self) -> #bevy_reflect_path::ReflectRef {
#bevy_reflect_path::ReflectRef::Opaque(self)
}
#[inline]
fn reflect_mut(&mut self) -> #bevy_reflect_path::ReflectMut {
#bevy_reflect_path::ReflectMut::Opaque(self)
}
#[inline]
fn reflect_owned(self: #bevy_reflect_path::__macro_exports::alloc_utils::Box<Self>) -> #bevy_reflect_path::ReflectOwned {
#bevy_reflect_path::ReflectOwned::Opaque(self)
}
#common_methods
#clone_fn
}
}
}

View File

@@ -0,0 +1,182 @@
use crate::{
impls::{common_partial_reflect_methods, impl_full_reflect, impl_type_path, impl_typed},
struct_utility::FieldAccessors,
ReflectStruct,
};
use bevy_macro_utils::fq_std::{FQDefault, FQOption, FQResult};
use quote::{quote, ToTokens};
/// Implements `Struct`, `GetTypeRegistration`, and `Reflect` for the given derive data.
pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> proc_macro2::TokenStream {
let fqoption = FQOption.into_token_stream();
let bevy_reflect_path = reflect_struct.meta().bevy_reflect_path();
let struct_path = reflect_struct.meta().type_path();
let field_names = reflect_struct
.active_fields()
.map(|field| {
field
.data
.ident
.as_ref()
.map(ToString::to_string)
.unwrap_or_else(|| field.declaration_index.to_string())
})
.collect::<Vec<String>>();
let FieldAccessors {
fields_ref,
fields_mut,
field_indices,
field_count,
..
} = FieldAccessors::new(reflect_struct);
let where_clause_options = reflect_struct.where_clause_options();
let typed_impl = impl_typed(
reflect_struct.meta(),
&where_clause_options,
reflect_struct.to_info_tokens(false),
);
let type_path_impl = impl_type_path(reflect_struct.meta());
let full_reflect_impl = impl_full_reflect(reflect_struct.meta(), &where_clause_options);
let common_methods = common_partial_reflect_methods(
reflect_struct.meta(),
|| Some(quote!(#bevy_reflect_path::struct_partial_eq)),
|| None,
);
let clone_fn = reflect_struct.get_clone_impl();
#[cfg(not(feature = "functions"))]
let function_impls = None::<proc_macro2::TokenStream>;
#[cfg(feature = "functions")]
let function_impls =
crate::impls::impl_function_traits(reflect_struct.meta(), &where_clause_options);
let get_type_registration_impl = reflect_struct.get_type_registration(&where_clause_options);
let (impl_generics, ty_generics, where_clause) = reflect_struct
.meta()
.type_path()
.generics()
.split_for_impl();
let where_reflect_clause = where_clause_options.extend_where_clause(where_clause);
quote! {
#get_type_registration_impl
#typed_impl
#type_path_impl
#full_reflect_impl
#function_impls
impl #impl_generics #bevy_reflect_path::Struct for #struct_path #ty_generics #where_reflect_clause {
fn field(&self, name: &str) -> #FQOption<&dyn #bevy_reflect_path::PartialReflect> {
match name {
#(#field_names => #fqoption::Some(#fields_ref),)*
_ => #FQOption::None,
}
}
fn field_mut(&mut self, name: &str) -> #FQOption<&mut dyn #bevy_reflect_path::PartialReflect> {
match name {
#(#field_names => #fqoption::Some(#fields_mut),)*
_ => #FQOption::None,
}
}
fn field_at(&self, index: usize) -> #FQOption<&dyn #bevy_reflect_path::PartialReflect> {
match index {
#(#field_indices => #fqoption::Some(#fields_ref),)*
_ => #FQOption::None,
}
}
fn field_at_mut(&mut self, index: usize) -> #FQOption<&mut dyn #bevy_reflect_path::PartialReflect> {
match index {
#(#field_indices => #fqoption::Some(#fields_mut),)*
_ => #FQOption::None,
}
}
fn name_at(&self, index: usize) -> #FQOption<&str> {
match index {
#(#field_indices => #fqoption::Some(#field_names),)*
_ => #FQOption::None,
}
}
fn field_len(&self) -> usize {
#field_count
}
fn iter_fields(&self) -> #bevy_reflect_path::FieldIter {
#bevy_reflect_path::FieldIter::new(self)
}
fn to_dynamic_struct(&self) -> #bevy_reflect_path::DynamicStruct {
let mut dynamic: #bevy_reflect_path::DynamicStruct = #FQDefault::default();
dynamic.set_represented_type(#bevy_reflect_path::PartialReflect::get_represented_type_info(self));
#(dynamic.insert_boxed(#field_names, #bevy_reflect_path::PartialReflect::to_dynamic(#fields_ref));)*
dynamic
}
}
impl #impl_generics #bevy_reflect_path::PartialReflect for #struct_path #ty_generics #where_reflect_clause {
#[inline]
fn get_represented_type_info(&self) -> #FQOption<&'static #bevy_reflect_path::TypeInfo> {
#FQOption::Some(<Self as #bevy_reflect_path::Typed>::type_info())
}
#[inline]
fn try_apply(
&mut self,
value: &dyn #bevy_reflect_path::PartialReflect
) -> #FQResult<(), #bevy_reflect_path::ApplyError> {
if let #bevy_reflect_path::ReflectRef::Struct(struct_value)
= #bevy_reflect_path::PartialReflect::reflect_ref(value) {
for (i, value) in ::core::iter::Iterator::enumerate(#bevy_reflect_path::Struct::iter_fields(struct_value)) {
let name = #bevy_reflect_path::Struct::name_at(struct_value, i).unwrap();
if let #FQOption::Some(v) = #bevy_reflect_path::Struct::field_mut(self, name) {
#bevy_reflect_path::PartialReflect::try_apply(v, value)?;
}
}
} else {
return #FQResult::Err(
#bevy_reflect_path::ApplyError::MismatchedKinds {
from_kind: #bevy_reflect_path::PartialReflect::reflect_kind(value),
to_kind: #bevy_reflect_path::ReflectKind::Struct
}
);
}
#FQResult::Ok(())
}
#[inline]
fn reflect_kind(&self) -> #bevy_reflect_path::ReflectKind {
#bevy_reflect_path::ReflectKind::Struct
}
#[inline]
fn reflect_ref(&self) -> #bevy_reflect_path::ReflectRef {
#bevy_reflect_path::ReflectRef::Struct(self)
}
#[inline]
fn reflect_mut(&mut self) -> #bevy_reflect_path::ReflectMut {
#bevy_reflect_path::ReflectMut::Struct(self)
}
#[inline]
fn reflect_owned(self: #bevy_reflect_path::__macro_exports::alloc_utils::Box<Self>) -> #bevy_reflect_path::ReflectOwned {
#bevy_reflect_path::ReflectOwned::Struct(self)
}
#common_methods
#clone_fn
}
}
}

View File

@@ -0,0 +1,148 @@
use crate::{
impls::{common_partial_reflect_methods, impl_full_reflect, impl_type_path, impl_typed},
struct_utility::FieldAccessors,
ReflectStruct,
};
use bevy_macro_utils::fq_std::{FQDefault, FQOption, FQResult};
use quote::{quote, ToTokens};
/// Implements `TupleStruct`, `GetTypeRegistration`, and `Reflect` for the given derive data.
pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> proc_macro2::TokenStream {
let fqoption = FQOption.into_token_stream();
let bevy_reflect_path = reflect_struct.meta().bevy_reflect_path();
let struct_path = reflect_struct.meta().type_path();
let FieldAccessors {
fields_ref,
fields_mut,
field_indices,
field_count,
..
} = FieldAccessors::new(reflect_struct);
let where_clause_options = reflect_struct.where_clause_options();
let get_type_registration_impl = reflect_struct.get_type_registration(&where_clause_options);
let typed_impl = impl_typed(
reflect_struct.meta(),
&where_clause_options,
reflect_struct.to_info_tokens(true),
);
let type_path_impl = impl_type_path(reflect_struct.meta());
let full_reflect_impl = impl_full_reflect(reflect_struct.meta(), &where_clause_options);
let common_methods = common_partial_reflect_methods(
reflect_struct.meta(),
|| Some(quote!(#bevy_reflect_path::tuple_struct_partial_eq)),
|| None,
);
let clone_fn = reflect_struct.get_clone_impl();
#[cfg(not(feature = "functions"))]
let function_impls = None::<proc_macro2::TokenStream>;
#[cfg(feature = "functions")]
let function_impls =
crate::impls::impl_function_traits(reflect_struct.meta(), &where_clause_options);
let (impl_generics, ty_generics, where_clause) = reflect_struct
.meta()
.type_path()
.generics()
.split_for_impl();
let where_reflect_clause = where_clause_options.extend_where_clause(where_clause);
quote! {
#get_type_registration_impl
#typed_impl
#type_path_impl
#full_reflect_impl
#function_impls
impl #impl_generics #bevy_reflect_path::TupleStruct for #struct_path #ty_generics #where_reflect_clause {
fn field(&self, index: usize) -> #FQOption<&dyn #bevy_reflect_path::PartialReflect> {
match index {
#(#field_indices => #fqoption::Some(#fields_ref),)*
_ => #FQOption::None,
}
}
fn field_mut(&mut self, index: usize) -> #FQOption<&mut dyn #bevy_reflect_path::PartialReflect> {
match index {
#(#field_indices => #fqoption::Some(#fields_mut),)*
_ => #FQOption::None,
}
}
#[inline]
fn field_len(&self) -> usize {
#field_count
}
#[inline]
fn iter_fields(&self) -> #bevy_reflect_path::TupleStructFieldIter {
#bevy_reflect_path::TupleStructFieldIter::new(self)
}
fn to_dynamic_tuple_struct(&self) -> #bevy_reflect_path::DynamicTupleStruct {
let mut dynamic: #bevy_reflect_path::DynamicTupleStruct = #FQDefault::default();
dynamic.set_represented_type(#bevy_reflect_path::PartialReflect::get_represented_type_info(self));
#(dynamic.insert_boxed(#bevy_reflect_path::PartialReflect::to_dynamic(#fields_ref));)*
dynamic
}
}
impl #impl_generics #bevy_reflect_path::PartialReflect for #struct_path #ty_generics #where_reflect_clause {
#[inline]
fn get_represented_type_info(&self) -> #FQOption<&'static #bevy_reflect_path::TypeInfo> {
#FQOption::Some(<Self as #bevy_reflect_path::Typed>::type_info())
}
#[inline]
fn try_apply(
&mut self,
value: &dyn #bevy_reflect_path::PartialReflect
) -> #FQResult<(), #bevy_reflect_path::ApplyError> {
if let #bevy_reflect_path::ReflectRef::TupleStruct(struct_value) =
#bevy_reflect_path::PartialReflect::reflect_ref(value) {
for (i, value) in ::core::iter::Iterator::enumerate(#bevy_reflect_path::TupleStruct::iter_fields(struct_value)) {
if let #FQOption::Some(v) = #bevy_reflect_path::TupleStruct::field_mut(self, i) {
#bevy_reflect_path::PartialReflect::try_apply(v, value)?;
}
}
} else {
return #FQResult::Err(
#bevy_reflect_path::ApplyError::MismatchedKinds {
from_kind: #bevy_reflect_path::PartialReflect::reflect_kind(value),
to_kind: #bevy_reflect_path::ReflectKind::TupleStruct,
}
);
}
#FQResult::Ok(())
}
#[inline]
fn reflect_kind(&self) -> #bevy_reflect_path::ReflectKind {
#bevy_reflect_path::ReflectKind::TupleStruct
}
#[inline]
fn reflect_ref(&self) -> #bevy_reflect_path::ReflectRef {
#bevy_reflect_path::ReflectRef::TupleStruct(self)
}
#[inline]
fn reflect_mut(&mut self) -> #bevy_reflect_path::ReflectMut {
#bevy_reflect_path::ReflectMut::TupleStruct(self)
}
#[inline]
fn reflect_owned(self: #bevy_reflect_path::__macro_exports::alloc_utils::Box<Self>) -> #bevy_reflect_path::ReflectOwned {
#bevy_reflect_path::ReflectOwned::TupleStruct(self)
}
#common_methods
#clone_fn
}
}
}

View File

@@ -0,0 +1,174 @@
use crate::{
derive_data::{ReflectMeta, ReflectTypePath},
string_expr::StringExpr,
where_clause_options::WhereClauseOptions,
};
use bevy_macro_utils::fq_std::FQOption;
use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
/// Returns an expression for a `NonGenericTypeCell` or `GenericTypeCell` to generate `'static` references.
fn static_type_cell(
meta: &ReflectMeta,
property: TypedProperty,
generator: TokenStream,
) -> TokenStream {
let bevy_reflect_path = meta.bevy_reflect_path();
if meta.type_path().impl_is_generic() {
let cell_type = match property {
TypedProperty::TypePath => quote!(GenericTypePathCell),
TypedProperty::TypeInfo => quote!(GenericTypeInfoCell),
};
quote! {
static CELL: #bevy_reflect_path::utility::#cell_type = #bevy_reflect_path::utility::#cell_type::new();
CELL.get_or_insert::<Self, _>(|| {
#generator
})
}
} else {
let cell_type = match property {
TypedProperty::TypePath => unreachable!(
"Cannot have a non-generic type path cell. Use string literals and core::concat instead."
),
TypedProperty::TypeInfo => quote!(NonGenericTypeInfoCell),
};
quote! {
static CELL: #bevy_reflect_path::utility::#cell_type = #bevy_reflect_path::utility::#cell_type::new();
CELL.get_or_set(|| {
#generator
})
}
}
}
#[derive(Clone, Copy)]
pub(crate) enum TypedProperty {
TypeInfo,
TypePath,
}
pub(crate) fn impl_type_path(meta: &ReflectMeta) -> TokenStream {
let where_clause_options = WhereClauseOptions::new(meta);
if !meta.attrs().type_path_attrs().should_auto_derive() {
return TokenStream::new();
}
let type_path = meta.type_path();
let bevy_reflect_path = meta.bevy_reflect_path();
let (long_type_path, short_type_path) = if type_path.impl_is_generic() {
let long_path_cell = static_type_cell(
meta,
TypedProperty::TypePath,
type_path.long_type_path(bevy_reflect_path).into_owned(),
);
let short_path_cell = static_type_cell(
meta,
TypedProperty::TypePath,
type_path.short_type_path(bevy_reflect_path).into_owned(),
);
(
long_path_cell.to_token_stream(),
short_path_cell.to_token_stream(),
)
} else {
(
type_path.long_type_path(bevy_reflect_path).into_borrowed(),
type_path.short_type_path(bevy_reflect_path).into_borrowed(),
)
};
let type_ident = wrap_in_option(type_path.type_ident().map(StringExpr::into_borrowed));
let module_path = wrap_in_option(type_path.module_path().map(StringExpr::into_borrowed));
let crate_name = wrap_in_option(type_path.crate_name().map(StringExpr::into_borrowed));
let primitive_assert = if let ReflectTypePath::Primitive(_) = type_path {
Some(quote! {
const _: () = {
mod private_scope {
// Compiles if it can be named when there are no imports.
type AssertIsPrimitive = #type_path;
}
};
})
} else {
None
};
let (impl_generics, ty_generics, where_clause) = type_path.generics().split_for_impl();
// Add Typed bound for each active field
let where_reflect_clause = where_clause_options.extend_where_clause(where_clause);
quote! {
#primitive_assert
// To ensure alloc is available, but also prevent its name from clashing, we place the implementation inside an anonymous constant
const _: () = {
extern crate alloc;
use alloc::string::ToString;
impl #impl_generics #bevy_reflect_path::TypePath for #type_path #ty_generics #where_reflect_clause {
fn type_path() -> &'static str {
#long_type_path
}
fn short_type_path() -> &'static str {
#short_type_path
}
fn type_ident() -> Option<&'static str> {
#type_ident
}
fn crate_name() -> Option<&'static str> {
#crate_name
}
fn module_path() -> Option<&'static str> {
#module_path
}
}
};
}
}
pub(crate) fn impl_typed(
meta: &ReflectMeta,
where_clause_options: &WhereClauseOptions,
type_info_generator: TokenStream,
) -> TokenStream {
let type_path = meta.type_path();
let bevy_reflect_path = meta.bevy_reflect_path();
let type_info_cell = static_type_cell(meta, TypedProperty::TypeInfo, type_info_generator);
let (impl_generics, ty_generics, where_clause) = type_path.generics().split_for_impl();
let where_reflect_clause = where_clause_options.extend_where_clause(where_clause);
quote! {
impl #impl_generics #bevy_reflect_path::Typed for #type_path #ty_generics #where_reflect_clause {
#[inline]
fn type_info() -> &'static #bevy_reflect_path::TypeInfo {
#type_info_cell
}
}
}
}
/// Turns an `Option<TokenStream>` into a `TokenStream` for an `Option`.
fn wrap_in_option(tokens: Option<TokenStream>) -> TokenStream {
match tokens {
Some(tokens) => quote! {
#FQOption::Some(#tokens)
},
None => quote! {
#FQOption::None
},
}
}

845
vendor/bevy_reflect_derive/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,845 @@
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
//! This crate contains macros used by Bevy's `Reflect` API.
//!
//! The main export of this crate is the derive macro for [`Reflect`]. This allows
//! types to easily implement `Reflect` along with other `bevy_reflect` traits,
//! such as `Struct`, `GetTypeRegistration`, and more— all with a single derive!
//!
//! Some other noteworthy exports include the derive macros for [`FromReflect`] and
//! [`TypePath`], as well as the [`reflect_trait`] attribute macro.
//!
//! [`Reflect`]: crate::derive_reflect
//! [`FromReflect`]: crate::derive_from_reflect
//! [`TypePath`]: crate::derive_type_path
//! [`reflect_trait`]: macro@reflect_trait
extern crate proc_macro;
mod attribute_parser;
mod container_attributes;
mod custom_attributes;
mod derive_data;
#[cfg(feature = "documentation")]
mod documentation;
mod enum_utility;
mod field_attributes;
mod from_reflect;
mod generics;
mod ident;
mod impls;
mod meta;
mod reflect_opaque;
mod registration;
mod remote;
mod result_sifter;
mod serialization;
mod string_expr;
mod struct_utility;
mod trait_reflection;
mod type_path;
mod where_clause_options;
use crate::derive_data::{ReflectDerive, ReflectMeta, ReflectStruct};
use container_attributes::ContainerAttributes;
use derive_data::{ReflectImplSource, ReflectProvenance, ReflectTraitToImpl, ReflectTypePath};
use proc_macro::TokenStream;
use quote::quote;
use reflect_opaque::ReflectOpaqueDef;
use syn::{parse_macro_input, DeriveInput};
use type_path::NamedTypePathDef;
pub(crate) static REFLECT_ATTRIBUTE_NAME: &str = "reflect";
pub(crate) static TYPE_PATH_ATTRIBUTE_NAME: &str = "type_path";
pub(crate) static TYPE_NAME_ATTRIBUTE_NAME: &str = "type_name";
/// Used both for [`impl_reflect`] and [`derive_reflect`].
///
/// [`impl_reflect`]: macro@impl_reflect
/// [`derive_reflect`]: derive_reflect()
fn match_reflect_impls(ast: DeriveInput, source: ReflectImplSource) -> TokenStream {
let derive_data = match ReflectDerive::from_input(
&ast,
ReflectProvenance {
source,
trait_: ReflectTraitToImpl::Reflect,
},
) {
Ok(data) => data,
Err(err) => return err.into_compile_error().into(),
};
let assertions = impls::impl_assertions(&derive_data);
let (reflect_impls, from_reflect_impl) = match derive_data {
ReflectDerive::Struct(struct_data) | ReflectDerive::UnitStruct(struct_data) => (
impls::impl_struct(&struct_data),
if struct_data.meta().from_reflect().should_auto_derive() {
Some(from_reflect::impl_struct(&struct_data))
} else {
None
},
),
ReflectDerive::TupleStruct(struct_data) => (
impls::impl_tuple_struct(&struct_data),
if struct_data.meta().from_reflect().should_auto_derive() {
Some(from_reflect::impl_tuple_struct(&struct_data))
} else {
None
},
),
ReflectDerive::Enum(enum_data) => (
impls::impl_enum(&enum_data),
if enum_data.meta().from_reflect().should_auto_derive() {
Some(from_reflect::impl_enum(&enum_data))
} else {
None
},
),
ReflectDerive::Opaque(meta) => (
impls::impl_opaque(&meta),
if meta.from_reflect().should_auto_derive() {
Some(from_reflect::impl_opaque(&meta))
} else {
None
},
),
};
TokenStream::from(quote! {
const _: () = {
#reflect_impls
#from_reflect_impl
#assertions
};
})
}
/// The main derive macro used by `bevy_reflect` for deriving its `Reflect` trait.
///
/// This macro can be used on all structs and enums (unions are not supported).
/// It will automatically generate implementations for `Reflect`, `Typed`, `GetTypeRegistration`, and `FromReflect`.
/// And, depending on the item's structure, will either implement `Struct`, `TupleStruct`, or `Enum`.
///
/// See the [`FromReflect`] derive macro for more information on how to customize the `FromReflect` implementation.
///
/// # Container Attributes
///
/// This macro comes with some helper attributes that can be added to the container item
/// in order to provide additional functionality or alter the generated implementations.
///
/// In addition to those listed, this macro can also use the attributes for [`TypePath`] derives.
///
/// ## `#[reflect(Ident)]`
///
/// The `#[reflect(Ident)]` attribute is used to add type data registrations to the `GetTypeRegistration`
/// implementation corresponding to the given identifier, prepended by `Reflect`.
///
/// For example, `#[reflect(Foo, Bar)]` would add two registrations:
/// one for `ReflectFoo` and another for `ReflectBar`.
/// This assumes these types are indeed in-scope wherever this macro is called.
///
/// This is often used with traits that have been marked by the [`#[reflect_trait]`](macro@reflect_trait)
/// macro in order to register the type's implementation of that trait.
///
/// ### Default Registrations
///
/// The following types are automatically registered when deriving `Reflect`:
///
/// * `ReflectFromReflect` (unless opting out of `FromReflect`)
/// * `SerializationData`
/// * `ReflectFromPtr`
///
/// ### Special Identifiers
///
/// There are a few "special" identifiers that work a bit differently:
///
/// * `#[reflect(Clone)]` will force the implementation of `Reflect::reflect_clone` to rely on
/// the type's [`Clone`] implementation.
/// A custom implementation may be provided using `#[reflect(Clone(my_clone_func))]` where
/// `my_clone_func` is the path to a function matching the signature:
/// `(&Self) -> Self`.
/// * `#[reflect(Debug)]` will force the implementation of `Reflect::reflect_debug` to rely on
/// the type's [`Debug`] implementation.
/// A custom implementation may be provided using `#[reflect(Debug(my_debug_func))]` where
/// `my_debug_func` is the path to a function matching the signature:
/// `(&Self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result`.
/// * `#[reflect(PartialEq)]` will force the implementation of `Reflect::reflect_partial_eq` to rely on
/// the type's [`PartialEq`] implementation.
/// A custom implementation may be provided using `#[reflect(PartialEq(my_partial_eq_func))]` where
/// `my_partial_eq_func` is the path to a function matching the signature:
/// `(&Self, value: &dyn #bevy_reflect_path::Reflect) -> bool`.
/// * `#[reflect(Hash)]` will force the implementation of `Reflect::reflect_hash` to rely on
/// the type's [`Hash`] implementation.
/// A custom implementation may be provided using `#[reflect(Hash(my_hash_func))]` where
/// `my_hash_func` is the path to a function matching the signature: `(&Self) -> u64`.
/// * `#[reflect(Default)]` will register the `ReflectDefault` type data as normal.
/// However, it will also affect how certain other operations are performed in order
/// to improve performance and/or robustness.
/// An example of where this is used is in the [`FromReflect`] derive macro,
/// where adding this attribute will cause the `FromReflect` implementation to create
/// a base value using its [`Default`] implementation avoiding issues with ignored fields
/// (for structs and tuple structs only).
///
/// ## `#[reflect(opaque)]`
///
/// The `#[reflect(opaque)]` attribute denotes that the item should implement `Reflect` as an opaque type,
/// hiding its structure and fields from the reflection API.
/// This means that it will forgo implementing `Struct`, `TupleStruct`, or `Enum`.
///
/// Furthermore, it requires that the type implements [`Clone`].
/// If planning to serialize this type using the reflection serializers,
/// then the `Serialize` and `Deserialize` traits will need to be implemented and registered as well.
///
/// ## `#[reflect(from_reflect = false)]`
///
/// This attribute will opt-out of the default `FromReflect` implementation.
///
/// This is useful for when a type can't or shouldn't implement `FromReflect`,
/// or if a manual implementation is desired.
///
/// Note that in the latter case, `ReflectFromReflect` will no longer be automatically registered.
///
/// ## `#[reflect(type_path = false)]`
///
/// This attribute will opt-out of the default `TypePath` implementation.
///
/// This is useful for when a type can't or shouldn't implement `TypePath`,
/// or if a manual implementation is desired.
///
/// ## `#[reflect(no_field_bounds)]`
///
/// This attribute will opt-out of the default trait bounds added to all field types
/// for the generated reflection trait impls.
///
/// Normally, all fields will have the bounds `TypePath`, and either `FromReflect` or `Reflect`
/// depending on if `#[reflect(from_reflect = false)]` is used.
/// However, this might not always be desirable, and so this attribute may be used to remove those bounds.
///
/// ### Example
///
/// If a type is recursive the default bounds will cause an overflow error when building:
///
/// ```ignore (bevy_reflect is not accessible from this crate)
/// #[derive(Reflect)] // ERROR: overflow evaluating the requirement `Foo: FromReflect`
/// struct Foo {
/// foo: Vec<Foo>,
/// }
///
/// // Generates a where clause like:
/// // impl bevy_reflect::Reflect for Foo
/// // where
/// // Self: Any + Send + Sync,
/// // Vec<Foo>: FromReflect + TypePath,
/// ```
///
/// In this case, `Foo` is given the bounds `Vec<Foo>: FromReflect + TypePath`,
/// which requires that `Foo` implements `FromReflect`,
/// which requires that `Vec<Foo>` implements `FromReflect`,
/// and so on, resulting in the error.
///
/// To fix this, we can add `#[reflect(no_field_bounds)]` to `Foo` to remove the bounds on `Vec<Foo>`:
///
/// ```ignore (bevy_reflect is not accessible from this crate)
/// #[derive(Reflect)]
/// #[reflect(no_field_bounds)]
/// struct Foo {
/// foo: Vec<Foo>,
/// }
///
/// // Generates a where clause like:
/// // impl bevy_reflect::Reflect for Foo
/// // where
/// // Self: Any + Send + Sync,
/// ```
///
/// ## `#[reflect(where T: Trait, U::Assoc: Trait, ...)]`
///
/// This attribute can be used to add additional bounds to the generated reflection trait impls.
///
/// This is useful for when a type needs certain bounds only applied to the reflection impls
/// that are not otherwise automatically added by the derive macro.
///
/// ### Example
///
/// In the example below, we want to enforce that `T::Assoc: List` is required in order for
/// `Foo<T>` to be reflectable, but we don't want it to prevent `Foo<T>` from being used
/// in places where `T::Assoc: List` is not required.
///
/// ```ignore
/// trait Trait {
/// type Assoc;
/// }
///
/// #[derive(Reflect)]
/// #[reflect(where T::Assoc: List)]
/// struct Foo<T: Trait> where T::Assoc: Default {
/// value: T::Assoc,
/// }
///
/// // Generates a where clause like:
/// //
/// // impl<T: Trait> bevy_reflect::Reflect for Foo<T>
/// // where
/// // Self: Any + Send + Sync,
/// // T::Assoc: Default,
/// // T: TypePath,
/// // T::Assoc: FromReflect + TypePath,
/// // T::Assoc: List,
/// // {/* ... */}
/// ```
///
/// ## `#[reflect(@...)]`
///
/// This attribute can be used to register custom attributes to the type's `TypeInfo`.
///
/// It accepts any expression after the `@` symbol that resolves to a value which implements `Reflect`.
///
/// Any number of custom attributes may be registered, however, each the type of each attribute must be unique.
/// If two attributes of the same type are registered, the last one will overwrite the first.
///
/// ### Example
///
/// ```ignore
/// #[derive(Reflect)]
/// struct Required;
///
/// #[derive(Reflect)]
/// struct EditorTooltip(String);
///
/// impl EditorTooltip {
/// fn new(text: &str) -> Self {
/// Self(text.to_string())
/// }
/// }
///
/// #[derive(Reflect)]
/// // Specify a "required" status and tooltip:
/// #[reflect(@Required, @EditorTooltip::new("An ID is required!"))]
/// struct Id(u8);
/// ```
///
/// # Field Attributes
///
/// Along with the container attributes, this macro comes with some attributes that may be applied
/// to the contained fields themselves.
///
/// ## `#[reflect(ignore)]`
///
/// This attribute simply marks a field to be ignored by the reflection API.
///
/// This allows fields to completely opt-out of reflection,
/// which may be useful for maintaining invariants, keeping certain data private,
/// or allowing the use of types that do not implement `Reflect` within the container.
///
/// ## `#[reflect(skip_serializing)]`
///
/// This works similar to `#[reflect(ignore)]`, but rather than opting out of _all_ of reflection,
/// it simply opts the field out of both serialization and deserialization.
/// This can be useful when a field should be accessible via reflection, but may not make
/// sense in a serialized form, such as computed data.
///
/// What this does is register the `SerializationData` type within the `GetTypeRegistration` implementation,
/// which will be used by the reflection serializers to determine whether or not the field is serializable.
///
/// ## `#[reflect(clone)]`
///
/// This attribute affects the `Reflect::reflect_clone` implementation.
///
/// Without this attribute, the implementation will rely on the field's own `Reflect::reflect_clone` implementation.
/// When this attribute is present, the implementation will instead use the field's `Clone` implementation directly.
///
/// The attribute may also take the path to a custom function like `#[reflect(clone = "path::to::my_clone_func")]`,
/// where `my_clone_func` matches the signature `(&Self) -> Self`.
///
/// This attribute does nothing if the containing struct/enum has the `#[reflect(Clone)]` attribute.
///
/// ## `#[reflect(@...)]`
///
/// This attribute can be used to register custom attributes to the field's `TypeInfo`.
///
/// It accepts any expression after the `@` symbol that resolves to a value which implements `Reflect`.
///
/// Any number of custom attributes may be registered, however, each the type of each attribute must be unique.
/// If two attributes of the same type are registered, the last one will overwrite the first.
///
/// ### Example
///
/// ```ignore
/// #[derive(Reflect)]
/// struct EditorTooltip(String);
///
/// impl EditorTooltip {
/// fn new(text: &str) -> Self {
/// Self(text.to_string())
/// }
/// }
///
/// #[derive(Reflect)]
/// struct Slider {
/// // Specify a custom range and tooltip:
/// #[reflect(@0.0..=1.0, @EditorTooltip::new("Must be between 0 and 1"))]
/// value: f32,
/// }
/// ```
///
/// [`reflect_trait`]: macro@reflect_trait
#[proc_macro_derive(Reflect, attributes(reflect, type_path, type_name))]
pub fn derive_reflect(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
match_reflect_impls(ast, ReflectImplSource::DeriveLocalType)
}
/// Derives the `FromReflect` trait.
///
/// # Field Attributes
///
/// ## `#[reflect(ignore)]`
///
/// The `#[reflect(ignore)]` attribute is shared with the [`#[derive(Reflect)]`](Reflect) macro and has much of the same
/// functionality in that it denotes that a field will be ignored by the reflection API.
///
/// The only major difference is that using it with this derive requires that the field implements [`Default`].
/// Without this requirement, there would be no way for `FromReflect` to automatically construct missing fields
/// that have been ignored.
///
/// ## `#[reflect(default)]`
///
/// If a field cannot be read, this attribute specifies a default value to be used in its place.
///
/// By default, this attribute denotes that the field's type implements [`Default`].
/// However, it can also take in a path string to a user-defined function that will return the default value.
/// This takes the form: `#[reflect(default = "path::to::my_function")]` where `my_function` is a parameterless
/// function that must return some default value for the type.
///
/// Specifying a custom default can be used to give different fields their own specialized defaults,
/// or to remove the `Default` requirement on fields marked with `#[reflect(ignore)]`.
/// Additionally, either form of this attribute can be used to fill in fields that are simply missing,
/// such as when converting a partially-constructed dynamic type to a concrete one.
#[proc_macro_derive(FromReflect, attributes(reflect))]
pub fn derive_from_reflect(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
let derive_data = match ReflectDerive::from_input(
&ast,
ReflectProvenance {
source: ReflectImplSource::DeriveLocalType,
trait_: ReflectTraitToImpl::FromReflect,
},
) {
Ok(data) => data,
Err(err) => return err.into_compile_error().into(),
};
let from_reflect_impl = match derive_data {
ReflectDerive::Struct(struct_data) | ReflectDerive::UnitStruct(struct_data) => {
from_reflect::impl_struct(&struct_data)
}
ReflectDerive::TupleStruct(struct_data) => from_reflect::impl_tuple_struct(&struct_data),
ReflectDerive::Enum(meta) => from_reflect::impl_enum(&meta),
ReflectDerive::Opaque(meta) => from_reflect::impl_opaque(&meta),
};
TokenStream::from(quote! {
const _: () = {
#from_reflect_impl
};
})
}
/// Derives the `TypePath` trait, providing a stable alternative to [`std::any::type_name`].
///
/// # Container Attributes
///
/// ## `#[type_path = "my_crate::foo"]`
///
/// Optionally specifies a custom module path to use instead of [`module_path`].
///
/// This path does not include the final identifier.
///
/// ## `#[type_name = "RenamedType"]`
///
/// Optionally specifies a new terminating identifier for `TypePath`.
///
/// To use this attribute, `#[type_path = "..."]` must also be specified.
#[proc_macro_derive(TypePath, attributes(type_path, type_name))]
pub fn derive_type_path(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
let derive_data = match ReflectDerive::from_input(
&ast,
ReflectProvenance {
source: ReflectImplSource::DeriveLocalType,
trait_: ReflectTraitToImpl::TypePath,
},
) {
Ok(data) => data,
Err(err) => return err.into_compile_error().into(),
};
let type_path_impl = impls::impl_type_path(derive_data.meta());
TokenStream::from(quote! {
const _: () = {
#type_path_impl
};
})
}
/// A macro that automatically generates type data for traits, which their implementors can then register.
///
/// The output of this macro is a struct that takes reflected instances of the implementor's type
/// and returns the value as a trait object.
/// Because of this, **it can only be used on [object-safe] traits.**
///
/// For a trait named `MyTrait`, this will generate the struct `ReflectMyTrait`.
/// The generated struct can be created using `FromType` with any type that implements the trait.
/// The creation and registration of this generated struct as type data can be automatically handled
/// by [`#[derive(Reflect)]`](Reflect).
///
/// # Example
///
/// ```ignore (bevy_reflect is not accessible from this crate)
/// # use std::any::TypeId;
/// # use bevy_reflect_derive::{Reflect, reflect_trait};
/// #[reflect_trait] // Generates `ReflectMyTrait`
/// trait MyTrait {
/// fn print(&self) -> &str;
/// }
///
/// #[derive(Reflect)]
/// #[reflect(MyTrait)] // Automatically registers `ReflectMyTrait`
/// struct SomeStruct;
///
/// impl MyTrait for SomeStruct {
/// fn print(&self) -> &str {
/// "Hello, World!"
/// }
/// }
///
/// // We can create the type data manually if we wanted:
/// let my_trait: ReflectMyTrait = FromType::<SomeStruct>::from_type();
///
/// // Or we can simply get it from the registry:
/// let mut registry = TypeRegistry::default();
/// registry.register::<SomeStruct>();
/// let my_trait = registry
/// .get_type_data::<ReflectMyTrait>(TypeId::of::<SomeStruct>())
/// .unwrap();
///
/// // Then use it on reflected data
/// let reflected: Box<dyn Reflect> = Box::new(SomeStruct);
/// let reflected_my_trait: &dyn MyTrait = my_trait.get(&*reflected).unwrap();
/// assert_eq!("Hello, World!", reflected_my_trait.print());
/// ```
///
/// [object-safe]: https://doc.rust-lang.org/reference/items/traits.html#object-safety
#[proc_macro_attribute]
pub fn reflect_trait(args: TokenStream, input: TokenStream) -> TokenStream {
trait_reflection::reflect_trait(&args, input)
}
/// Generates a wrapper type that can be used to "derive `Reflect`" for remote types.
///
/// This works by wrapping the remote type in a generated wrapper that has the `#[repr(transparent)]` attribute.
/// This allows the two types to be safely [transmuted] back-and-forth.
///
/// # Defining the Wrapper
///
/// Before defining the wrapper type, please note that it is _required_ that all fields of the remote type are public.
/// The generated code will, at times, need to access or mutate them,
/// and we do not currently have a way to assign getters/setters to each field
/// (but this may change in the future).
///
/// The wrapper definition should match the remote type 1-to-1.
/// This includes the naming and ordering of the fields and variants.
///
/// Generics and lifetimes do _not_ need to have the same names, however, they _do_ need to follow the same order.
/// Additionally, whether generics are inlined or placed in a where clause should not matter.
///
/// Lastly, all macros and doc-comments should be placed __below__ this attribute.
/// If they are placed above, they will not be properly passed to the generated wrapper type.
///
/// # Example
///
/// Given a remote type, `RemoteType`:
///
/// ```
/// #[derive(Default)]
/// struct RemoteType<T>
/// where
/// T: Default + Clone,
/// {
/// pub foo: T,
/// pub bar: usize
/// }
/// ```
///
/// We would define our wrapper type as such:
///
/// ```ignore
/// use external_crate::RemoteType;
///
/// #[reflect_remote(RemoteType<T>)]
/// #[derive(Default)]
/// pub struct WrapperType<T: Default + Clone> {
/// pub foo: T,
/// pub bar: usize
/// }
/// ```
///
/// Apart from all the reflection trait implementations, this generates something like the following:
///
/// ```ignore
/// use external_crate::RemoteType;
///
/// #[derive(Default)]
/// #[repr(transparent)]
/// pub struct Wrapper<T: Default + Clone>(RemoteType<T>);
/// ```
///
/// # Usage as a Field
///
/// You can tell `Reflect` to use a remote type's wrapper internally on fields of a struct or enum.
/// This allows the real type to be used as usual while `Reflect` handles everything internally.
/// To do this, add the `#[reflect(remote = path::to::MyType)]` attribute to your field:
///
/// ```ignore
/// #[derive(Reflect)]
/// struct SomeStruct {
/// #[reflect(remote = RemoteTypeWrapper)]
/// data: RemoteType
/// }
/// ```
///
/// ## Safety
///
/// When using the `#[reflect(remote = path::to::MyType)]` field attribute, be sure you are defining the correct wrapper type.
/// Internally, this field will be unsafely [transmuted], and is only sound if using a wrapper generated for the remote type.
/// This also means keeping your wrapper definitions up-to-date with the remote types.
///
/// [transmuted]: std::mem::transmute
#[proc_macro_attribute]
pub fn reflect_remote(args: TokenStream, input: TokenStream) -> TokenStream {
remote::reflect_remote(args, input)
}
/// A macro used to generate reflection trait implementations for the given type.
///
/// This is functionally the same as [deriving `Reflect`] using the `#[reflect(opaque)]` container attribute.
///
/// The only reason for this macro's existence is so that `bevy_reflect` can easily implement the reflection traits
/// on primitives and other opaque types internally.
///
/// Since this macro also implements `TypePath`, the type path must be explicit.
/// See [`impl_type_path!`] for the exact syntax.
///
/// # Examples
///
/// Types can be passed with or without registering type data:
///
/// ```ignore (bevy_reflect is not accessible from this crate)
/// impl_reflect_opaque!(my_crate::Foo);
/// impl_reflect_opaque!(my_crate::Bar(Debug, Default, Serialize, Deserialize));
/// ```
///
/// Generic types can also specify their parameters and bounds:
///
/// ```ignore (bevy_reflect is not accessible from this crate)
/// impl_reflect_opaque!(my_crate::Foo<T1, T2: Baz> where T1: Bar (Default, Serialize, Deserialize));
/// ```
///
/// Custom type paths can be specified:
///
/// ```ignore (bevy_reflect is not accessible from this crate)
/// impl_reflect_opaque!((in not_my_crate as NotFoo) Foo(Debug, Default));
/// ```
///
/// [deriving `Reflect`]: Reflect
#[proc_macro]
pub fn impl_reflect_opaque(input: TokenStream) -> TokenStream {
let def = parse_macro_input!(input with ReflectOpaqueDef::parse_reflect);
let default_name = &def.type_path.segments.last().unwrap().ident;
let type_path = if def.type_path.leading_colon.is_none() && def.custom_path.is_none() {
ReflectTypePath::Primitive(default_name)
} else {
ReflectTypePath::External {
path: &def.type_path,
custom_path: def.custom_path.map(|path| path.into_path(default_name)),
generics: &def.generics,
}
};
let meta = ReflectMeta::new(type_path, def.traits.unwrap_or_default());
#[cfg(feature = "documentation")]
let meta = meta.with_docs(documentation::Documentation::from_attributes(&def.attrs));
let reflect_impls = impls::impl_opaque(&meta);
let from_reflect_impl = from_reflect::impl_opaque(&meta);
TokenStream::from(quote! {
const _: () = {
#reflect_impls
#from_reflect_impl
};
})
}
/// A replacement for `#[derive(Reflect)]` to be used with foreign types which
/// the definitions of cannot be altered.
///
/// This macro is an alternative to [`impl_reflect_opaque!`] and [`impl_from_reflect_opaque!`]
/// which implement foreign types as Opaque types. Note that there is no `impl_from_reflect`,
/// as this macro will do the job of both. This macro implements them using one of the reflect
/// variant traits (`bevy_reflect::{Struct, TupleStruct, Enum}`, etc.),
/// which have greater functionality. The type being reflected must be in scope, as you cannot
/// qualify it in the macro as e.g. `bevy::prelude::Vec3`.
///
/// It is necessary to add a `#[type_path = "my_crate::foo"]` attribute to all types.
///
/// It may be necessary to add `#[reflect(Default)]` for some types, specifically non-constructible
/// foreign types. Without `Default` reflected for such types, you will usually get an arcane
/// error message and fail to compile. If the type does not implement `Default`, it may not
/// be possible to reflect without extending the macro.
///
///
/// # Example
/// Implementing `Reflect` for `bevy::prelude::Vec3` as a struct type:
/// ```ignore (bevy_reflect is not accessible from this crate)
/// use bevy::prelude::Vec3;
///
/// impl_reflect!(
/// #[reflect(PartialEq, Serialize, Deserialize, Default)]
/// #[type_path = "bevy::prelude"]
/// struct Vec3 {
/// x: f32,
/// y: f32,
/// z: f32
/// }
/// );
/// ```
#[proc_macro]
pub fn impl_reflect(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
match_reflect_impls(ast, ReflectImplSource::ImplRemoteType)
}
/// A macro used to generate a `FromReflect` trait implementation for the given type.
///
/// This is functionally the same as [deriving `FromReflect`] on a type that [derives `Reflect`] using
/// the `#[reflect(opaque)]` container attribute.
///
/// The only reason this macro exists is so that `bevy_reflect` can easily implement `FromReflect` on
/// primitives and other opaque types internally.
///
/// Please note that this macro will not work with any type that [derives `Reflect`] normally
/// or makes use of the [`impl_reflect_opaque!`] macro, as those macros also implement `FromReflect`
/// by default.
///
/// # Examples
///
/// ```ignore (bevy_reflect is not accessible from this crate)
/// impl_from_reflect_opaque!(foo<T1, T2: Baz> where T1: Bar);
/// ```
///
/// [deriving `FromReflect`]: FromReflect
/// [derives `Reflect`]: Reflect
#[proc_macro]
pub fn impl_from_reflect_opaque(input: TokenStream) -> TokenStream {
let def = parse_macro_input!(input with ReflectOpaqueDef::parse_from_reflect);
let default_name = &def.type_path.segments.last().unwrap().ident;
let type_path = if def.type_path.leading_colon.is_none()
&& def.custom_path.is_none()
&& def.generics.params.is_empty()
{
ReflectTypePath::Primitive(default_name)
} else {
ReflectTypePath::External {
path: &def.type_path,
custom_path: def.custom_path.map(|alias| alias.into_path(default_name)),
generics: &def.generics,
}
};
let from_reflect_impl =
from_reflect::impl_opaque(&ReflectMeta::new(type_path, def.traits.unwrap_or_default()));
TokenStream::from(quote! {
const _: () = {
#from_reflect_impl
};
})
}
/// A replacement for [deriving `TypePath`] for use on foreign types.
///
/// Since (unlike the derive) this macro may be invoked in a different module to where the type is defined,
/// it requires an 'absolute' path definition.
///
/// Specifically, a leading `::` denoting a global path must be specified
/// or a preceding `(in my_crate::foo)` to specify the custom path must be used.
///
/// # Examples
///
/// Implementing `TypePath` on a foreign type:
/// ```ignore (bevy_reflect is not accessible from this crate)
/// impl_type_path!(::foreign_crate::foo::bar::Baz);
/// ```
///
/// On a generic type (this can also accept trait bounds):
/// ```ignore (bevy_reflect is not accessible from this crate)
/// impl_type_path!(::foreign_crate::Foo<T>);
/// impl_type_path!(::foreign_crate::Goo<T: ?Sized>);
/// ```
///
/// On a primitive (note this will not compile for a non-primitive type):
/// ```ignore (bevy_reflect is not accessible from this crate)
/// impl_type_path!(bool);
/// ```
///
/// With a custom type path:
/// ```ignore (bevy_reflect is not accessible from this crate)
/// impl_type_path!((in other_crate::foo::bar) Baz);
/// ```
///
/// With a custom type path and a custom type name:
/// ```ignore (bevy_reflect is not accessible from this crate)
/// impl_type_path!((in other_crate::foo as Baz) Bar);
/// ```
///
/// [deriving `TypePath`]: TypePath
#[proc_macro]
pub fn impl_type_path(input: TokenStream) -> TokenStream {
let def = parse_macro_input!(input as NamedTypePathDef);
let type_path = match def {
NamedTypePathDef::External {
ref path,
custom_path,
ref generics,
} => {
let default_name = &path.segments.last().unwrap().ident;
ReflectTypePath::External {
path,
custom_path: custom_path.map(|path| path.into_path(default_name)),
generics,
}
}
NamedTypePathDef::Primitive(ref ident) => ReflectTypePath::Primitive(ident),
};
let meta = ReflectMeta::new(type_path, ContainerAttributes::default());
let type_path_impl = impls::impl_type_path(&meta);
TokenStream::from(quote! {
const _: () = {
#type_path_impl
};
})
}

View File

@@ -0,0 +1,7 @@
use bevy_macro_utils::BevyManifest;
use syn::Path;
/// Returns the correct path for `bevy_reflect`.
pub(crate) fn get_bevy_reflect_path() -> Path {
BevyManifest::shared().get_path("bevy_reflect")
}

View File

@@ -0,0 +1,75 @@
use crate::{
container_attributes::ContainerAttributes, derive_data::ReflectTraitToImpl,
type_path::CustomPathDef,
};
use syn::{parenthesized, parse::ParseStream, token::Paren, Attribute, Generics, Path};
/// A struct used to define a simple reflection-opaque types (including primitives).
///
/// This takes the form:
///
/// ```ignore (Method expecting TokenStream is better represented with raw tokens)
/// // Standard
/// ::my_crate::foo::Bar(TraitA, TraitB)
///
/// // With generics
/// ::my_crate::foo::Bar<T1: Bar, T2>(TraitA, TraitB)
///
/// // With generics and where clause
/// ::my_crate::foo::Bar<T1, T2> where T1: Bar (TraitA, TraitB)
///
/// // With a custom path (not with impl_from_reflect_opaque)
/// (in my_crate::bar) Bar(TraitA, TraitB)
/// ```
pub(crate) struct ReflectOpaqueDef {
#[cfg_attr(
not(feature = "documentation"),
expect(
dead_code,
reason = "The is used when the `documentation` feature is enabled.",
)
)]
pub attrs: Vec<Attribute>,
pub type_path: Path,
pub generics: Generics,
pub traits: Option<ContainerAttributes>,
pub custom_path: Option<CustomPathDef>,
}
impl ReflectOpaqueDef {
pub fn parse_reflect(input: ParseStream) -> syn::Result<Self> {
Self::parse(input, ReflectTraitToImpl::Reflect)
}
pub fn parse_from_reflect(input: ParseStream) -> syn::Result<Self> {
Self::parse(input, ReflectTraitToImpl::FromReflect)
}
fn parse(input: ParseStream, trait_: ReflectTraitToImpl) -> syn::Result<Self> {
let attrs = input.call(Attribute::parse_outer)?;
let custom_path = CustomPathDef::parse_parenthesized(input)?;
let type_path = Path::parse_mod_style(input)?;
let mut generics = input.parse::<Generics>()?;
generics.where_clause = input.parse()?;
let mut traits = None;
if input.peek(Paren) {
let content;
parenthesized!(content in input);
traits = Some({
let mut attrs = ContainerAttributes::default();
attrs.parse_terminated(&content, trait_)?;
attrs
});
}
Ok(Self {
attrs,
type_path,
generics,
traits,
custom_path,
})
}
}

View File

@@ -0,0 +1,63 @@
//! Contains code related specifically to Bevy's type registration.
use crate::{
derive_data::ReflectMeta, serialization::SerializationDataDef,
where_clause_options::WhereClauseOptions,
};
use quote::quote;
use syn::Type;
/// Creates the `GetTypeRegistration` impl for the given type data.
pub(crate) fn impl_get_type_registration<'a>(
meta: &ReflectMeta,
where_clause_options: &WhereClauseOptions,
serialization_data: Option<&SerializationDataDef>,
type_dependencies: Option<impl Iterator<Item = &'a Type>>,
) -> proc_macro2::TokenStream {
let type_path = meta.type_path();
let bevy_reflect_path = meta.bevy_reflect_path();
let registration_data = meta.attrs().idents();
let type_deps_fn = type_dependencies.map(|deps| {
quote! {
#[inline(never)]
fn register_type_dependencies(registry: &mut #bevy_reflect_path::TypeRegistry) {
#(<#deps as #bevy_reflect_path::__macro_exports::RegisterForReflection>::__register(registry);)*
}
}
});
let (impl_generics, ty_generics, where_clause) = type_path.generics().split_for_impl();
let where_reflect_clause = where_clause_options.extend_where_clause(where_clause);
let from_reflect_data = if meta.from_reflect().should_auto_derive() {
Some(quote! {
registration.insert::<#bevy_reflect_path::ReflectFromReflect>(#bevy_reflect_path::FromType::<Self>::from_type());
})
} else {
None
};
let serialization_data = serialization_data.map(|data| {
let serialization_data = data.as_serialization_data(bevy_reflect_path);
quote! {
registration.insert::<#bevy_reflect_path::serde::SerializationData>(#serialization_data);
}
});
quote! {
#[allow(unused_mut)]
impl #impl_generics #bevy_reflect_path::GetTypeRegistration for #type_path #ty_generics #where_reflect_clause {
fn get_type_registration() -> #bevy_reflect_path::TypeRegistration {
let mut registration = #bevy_reflect_path::TypeRegistration::of::<Self>();
registration.insert::<#bevy_reflect_path::ReflectFromPtr>(#bevy_reflect_path::FromType::<Self>::from_type());
#from_reflect_data
#serialization_data
#(registration.insert::<#registration_data>(#bevy_reflect_path::FromType::<Self>::from_type());)*
registration
}
#type_deps_fn
}
}
}

502
vendor/bevy_reflect_derive/src/remote.rs vendored Normal file
View File

@@ -0,0 +1,502 @@
use crate::{
derive_data::{ReflectImplSource, ReflectProvenance, ReflectTraitToImpl},
from_reflect,
ident::ident_or_index,
impls,
impls::impl_assertions,
ReflectDerive, REFLECT_ATTRIBUTE_NAME,
};
use bevy_macro_utils::fq_std::FQOption;
use proc_macro::TokenStream;
use proc_macro2::{Ident, Span};
use quote::{format_ident, quote, quote_spanned};
use syn::{
parse::{Parse, ParseStream},
parse_macro_input,
spanned::Spanned,
token::PathSep,
DeriveInput, ExprPath, Generics, Member, PathArguments, Type, TypePath,
};
/// Generates the remote wrapper type and implements all the necessary traits.
pub(crate) fn reflect_remote(args: TokenStream, input: TokenStream) -> TokenStream {
let remote_args = match syn::parse::<RemoteArgs>(args) {
Ok(path) => path,
Err(err) => return err.to_compile_error().into(),
};
let remote_ty = remote_args.remote_ty;
let ast = parse_macro_input!(input as DeriveInput);
let wrapper_definition = generate_remote_wrapper(&ast, &remote_ty);
let mut derive_data = match ReflectDerive::from_input(
&ast,
ReflectProvenance {
source: ReflectImplSource::RemoteReflect,
trait_: ReflectTraitToImpl::Reflect,
},
) {
Ok(data) => data,
Err(err) => return err.into_compile_error().into(),
};
derive_data.set_remote(Some(RemoteType::new(&remote_ty)));
let assertions = impl_assertions(&derive_data);
let definition_assertions = generate_remote_definition_assertions(&derive_data);
let reflect_remote_impl = impl_reflect_remote(&derive_data, &remote_ty);
let (reflect_impls, from_reflect_impl) = match derive_data {
ReflectDerive::Struct(struct_data) | ReflectDerive::UnitStruct(struct_data) => (
impls::impl_struct(&struct_data),
if struct_data.meta().from_reflect().should_auto_derive() {
Some(from_reflect::impl_struct(&struct_data))
} else {
None
},
),
ReflectDerive::TupleStruct(struct_data) => (
impls::impl_tuple_struct(&struct_data),
if struct_data.meta().from_reflect().should_auto_derive() {
Some(from_reflect::impl_tuple_struct(&struct_data))
} else {
None
},
),
ReflectDerive::Enum(enum_data) => (
impls::impl_enum(&enum_data),
if enum_data.meta().from_reflect().should_auto_derive() {
Some(from_reflect::impl_enum(&enum_data))
} else {
None
},
),
ReflectDerive::Opaque(meta) => (
impls::impl_opaque(&meta),
if meta.from_reflect().should_auto_derive() {
Some(from_reflect::impl_opaque(&meta))
} else {
None
},
),
};
TokenStream::from(quote! {
#wrapper_definition
const _: () = {
#reflect_remote_impl
#reflect_impls
#from_reflect_impl
#definition_assertions
#assertions
};
})
}
/// Generates the remote wrapper type.
///
/// # Example
///
/// If the supplied remote type is `Bar<T>`, then the wrapper type— named `Foo<T>`— would look like:
///
/// ```
/// # struct Bar<T>(T);
///
/// #[repr(transparent)]
/// struct Foo<T>(Bar<T>);
/// ```
fn generate_remote_wrapper(input: &DeriveInput, remote_ty: &TypePath) -> proc_macro2::TokenStream {
let ident = &input.ident;
let vis = &input.vis;
let ty_generics = &input.generics;
let where_clause = &input.generics.where_clause;
let attrs = input
.attrs
.iter()
.filter(|attr| !attr.path().is_ident(REFLECT_ATTRIBUTE_NAME));
quote! {
#(#attrs)*
#[repr(transparent)]
#[doc(hidden)]
#vis struct #ident #ty_generics (pub #remote_ty) #where_clause;
}
}
/// Generates the implementation of the `ReflectRemote` trait for the given derive data and remote type.
///
/// # Note to Developers
///
/// The `ReflectRemote` trait could likely be made with default method implementations.
/// However, this makes it really easy for a user to accidentally implement this trait in an unsafe way.
/// To prevent this, we instead generate the implementation through a macro using this function.
fn impl_reflect_remote(input: &ReflectDerive, remote_ty: &TypePath) -> proc_macro2::TokenStream {
let bevy_reflect_path = input.meta().bevy_reflect_path();
let type_path = input.meta().type_path();
let (impl_generics, ty_generics, where_clause) =
input.meta().type_path().generics().split_for_impl();
let where_reflect_clause = input
.where_clause_options()
.extend_where_clause(where_clause);
quote! {
// SAFE: The generated wrapper type is guaranteed to be valid and repr(transparent) over the remote type.
impl #impl_generics #bevy_reflect_path::ReflectRemote for #type_path #ty_generics #where_reflect_clause {
type Remote = #remote_ty;
fn as_remote(&self) -> &Self::Remote {
&self.0
}
fn as_remote_mut(&mut self) -> &mut Self::Remote {
&mut self.0
}
fn into_remote(self) -> Self::Remote
{
// SAFE: The wrapper type should be repr(transparent) over the remote type
unsafe {
// Unfortunately, we have to use `transmute_copy` to avoid a compiler error:
// ```
// error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
// |
// | core::mem::transmute::<A, B>(a)
// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
// |
// = note: source type: `A` (this type does not have a fixed size)
// = note: target type: `B` (this type does not have a fixed size)
// ```
::core::mem::transmute_copy::<Self, Self::Remote>(
// `ManuallyDrop` is used to prevent double-dropping `self`
&::core::mem::ManuallyDrop::new(self)
)
}
}
fn as_wrapper(remote: &Self::Remote) -> &Self {
// SAFE: The wrapper type should be repr(transparent) over the remote type
unsafe { ::core::mem::transmute::<&Self::Remote, &Self>(remote) }
}
fn as_wrapper_mut(remote: &mut Self::Remote) -> &mut Self {
// SAFE: The wrapper type should be repr(transparent) over the remote type
unsafe { ::core::mem::transmute::<&mut Self::Remote, &mut Self>(remote) }
}
fn into_wrapper(remote: Self::Remote) -> Self
{
// SAFE: The wrapper type should be repr(transparent) over the remote type
unsafe {
// Unfortunately, we have to use `transmute_copy` to avoid a compiler error:
// ```
// error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
// |
// | core::mem::transmute::<A, B>(a)
// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
// |
// = note: source type: `A` (this type does not have a fixed size)
// = note: target type: `B` (this type does not have a fixed size)
// ```
::core::mem::transmute_copy::<Self::Remote, Self>(
// `ManuallyDrop` is used to prevent double-dropping `self`
&::core::mem::ManuallyDrop::new(remote)
)
}
}
}
}
}
/// Generates compile-time assertions for remote fields.
///
/// This prevents types from using an invalid remote type.
/// this works by generating a struct, `RemoteFieldAssertions`, with methods that
/// will result in compile-time failure if types are mismatched.
/// The output of this function is best placed within an anonymous context to maintain hygiene.
///
/// # Example
///
/// The following would fail to compile due to an incorrect `#[reflect(remote = ...)]` value.
///
/// ```ignore
/// mod external_crate {
/// pub struct TheirFoo(pub u32);
/// pub struct TheirBar(pub i32);
/// }
///
/// #[reflect_remote(external_crate::TheirFoo)]
/// struct MyFoo(pub u32);
/// #[reflect_remote(external_crate::TheirBar)]
/// struct MyBar(pub i32);
///
/// #[derive(Reflect)]
/// struct MyStruct {
/// #[reflect(remote = MyBar)] // ERROR: expected type `TheirFoo` but found struct `TheirBar`
/// foo: external_crate::TheirFoo
/// }
/// ```
pub(crate) fn generate_remote_assertions(
derive_data: &ReflectDerive,
) -> Option<proc_macro2::TokenStream> {
struct RemoteAssertionData<'a> {
ident: Member,
variant: Option<&'a Ident>,
ty: &'a Type,
generics: &'a Generics,
remote_ty: &'a Type,
}
let bevy_reflect_path = derive_data.meta().bevy_reflect_path();
let fields: Box<dyn Iterator<Item = RemoteAssertionData>> = match derive_data {
ReflectDerive::Struct(data)
| ReflectDerive::TupleStruct(data)
| ReflectDerive::UnitStruct(data) => Box::new(data.active_fields().filter_map(|field| {
field
.attrs
.remote
.as_ref()
.map(|remote_ty| RemoteAssertionData {
ident: ident_or_index(field.data.ident.as_ref(), field.declaration_index),
variant: None,
ty: &field.data.ty,
generics: data.meta().type_path().generics(),
remote_ty,
})
})),
ReflectDerive::Enum(data) => Box::new(data.variants().iter().flat_map(|variant| {
variant.active_fields().filter_map(|field| {
field
.attrs
.remote
.as_ref()
.map(|remote_ty| RemoteAssertionData {
ident: ident_or_index(field.data.ident.as_ref(), field.declaration_index),
variant: Some(&variant.data.ident),
ty: &field.data.ty,
generics: data.meta().type_path().generics(),
remote_ty,
})
})
})),
_ => return None,
};
let assertions = fields
.map(move |field| {
let ident = if let Some(variant) = field.variant {
format_ident!("{}__{}", variant, field.ident)
} else {
match field.ident {
Member::Named(ident) => ident,
Member::Unnamed(index) => format_ident!("field_{}", index),
}
};
let (impl_generics, _, where_clause) = field.generics.split_for_impl();
let where_reflect_clause = derive_data
.where_clause_options()
.extend_where_clause(where_clause);
let ty = &field.ty;
let remote_ty = field.remote_ty;
let assertion_ident = format_ident!("assert__{}__is_valid_remote", ident);
let span = create_assertion_span(remote_ty.span());
quote_spanned! {span=>
#[allow(non_snake_case)]
#[allow(clippy::multiple_bound_locations)]
fn #assertion_ident #impl_generics () #where_reflect_clause {
let _: <#remote_ty as #bevy_reflect_path::ReflectRemote>::Remote = (|| -> #FQOption<#ty> {
None
})().unwrap();
}
}
})
.collect::<proc_macro2::TokenStream>();
if assertions.is_empty() {
None
} else {
Some(quote! {
struct RemoteFieldAssertions;
impl RemoteFieldAssertions {
#assertions
}
})
}
}
/// Generates compile-time assertions that ensure a remote wrapper definition matches up with the
/// remote type it's wrapping.
///
/// Note: This currently results in "backwards" error messages like:
///
/// ```ignore
/// expected: <WRAPPER_FIELD_TYPE>
/// found: <REMOTE_FIELD_TYPE>
/// ```
///
/// Ideally it would be the other way around, but there's no easy way of doing this without
/// generating a copy of the struct/enum definition and using that as the base instead of the remote type.
fn generate_remote_definition_assertions(derive_data: &ReflectDerive) -> proc_macro2::TokenStream {
let meta = derive_data.meta();
let self_ident = format_ident!("__remote__");
let self_ty = derive_data.remote_ty().unwrap().type_path();
let self_expr_path = derive_data.remote_ty().unwrap().as_expr_path().unwrap();
let (impl_generics, _, where_clause) = meta.type_path().generics().split_for_impl();
let where_reflect_clause = derive_data
.where_clause_options()
.extend_where_clause(where_clause);
let assertions = match derive_data {
ReflectDerive::Struct(data)
| ReflectDerive::TupleStruct(data)
| ReflectDerive::UnitStruct(data) => {
let mut output = proc_macro2::TokenStream::new();
for field in data.fields() {
let field_member =
ident_or_index(field.data.ident.as_ref(), field.declaration_index);
let field_ty = &field.data.ty;
let span = create_assertion_span(field_ty.span());
output.extend(quote_spanned! {span=>
#self_ident.#field_member = (|| -> #FQOption<#field_ty> {None})().unwrap();
});
}
output
}
ReflectDerive::Enum(data) => {
let variants = data.variants().iter().map(|variant| {
let ident = &variant.data.ident;
let mut output = proc_macro2::TokenStream::new();
if variant.fields().is_empty() {
return quote!(#self_expr_path::#ident => {});
}
for field in variant.fields() {
let field_member =
ident_or_index(field.data.ident.as_ref(), field.declaration_index);
let field_ident = format_ident!("field_{}", field_member);
let field_ty = &field.data.ty;
let span = create_assertion_span(field_ty.span());
output.extend(quote_spanned! {span=>
#self_expr_path::#ident {#field_member: mut #field_ident, ..} => {
#field_ident = (|| -> #FQOption<#field_ty> {None})().unwrap();
}
});
}
output
});
quote! {
match #self_ident {
#(#variants)*
}
}
}
ReflectDerive::Opaque(_) => {
// No assertions needed since there are no fields to check
proc_macro2::TokenStream::new()
}
};
quote! {
const _: () = {
#[allow(non_snake_case)]
#[allow(unused_variables)]
#[allow(unused_assignments)]
#[allow(unreachable_patterns)]
#[allow(clippy::multiple_bound_locations)]
fn assert_wrapper_definition_matches_remote_type #impl_generics (mut #self_ident: #self_ty) #where_reflect_clause {
#assertions
}
};
}
}
/// Creates a span located around the given one, but resolves to the assertion's context.
///
/// This should allow the compiler to point back to the line and column in the user's code,
/// while still attributing the error to the macro.
fn create_assertion_span(span: Span) -> Span {
Span::call_site().located_at(span)
}
/// A reflected type's remote type.
///
/// This is a wrapper around [`TypePath`] that allows it to be paired with other remote-specific logic.
#[derive(Copy, Clone)]
pub(crate) struct RemoteType<'a> {
path: &'a TypePath,
}
impl<'a> RemoteType<'a> {
pub fn new(path: &'a TypePath) -> Self {
Self { path }
}
/// Returns the [type path](TypePath) of this remote type.
pub fn type_path(&self) -> &'a TypePath {
self.path
}
/// Attempts to convert the [type path](TypePath) of this remote type into an [expression path](ExprPath).
///
/// For example, this would convert `foo::Bar<T>` into `foo::Bar::<T>` to be used as part of an expression.
///
/// This will return an error for types that are parenthesized, such as in `Fn() -> Foo`.
pub fn as_expr_path(&self) -> Result<ExprPath, syn::Error> {
let mut expr_path = self.path.clone();
if let Some(segment) = expr_path.path.segments.last_mut() {
match &mut segment.arguments {
PathArguments::None => {}
PathArguments::AngleBracketed(arg) => {
arg.colon2_token = Some(PathSep::default());
}
PathArguments::Parenthesized(arg) => {
return Err(syn::Error::new(
arg.span(),
"cannot use parenthesized type as remote type",
))
}
}
}
Ok(ExprPath {
path: expr_path.path,
qself: expr_path.qself,
attrs: Vec::new(),
})
}
}
/// Metadata from the arguments defined in the `reflect_remote` attribute.
///
/// The syntax for the arguments is: `#[reflect_remote(REMOTE_TYPE_PATH)]`.
struct RemoteArgs {
remote_ty: TypePath,
}
impl Parse for RemoteArgs {
fn parse(input: ParseStream) -> syn::Result<Self> {
Ok(Self {
remote_ty: input.parse()?,
})
}
}

View File

@@ -0,0 +1,46 @@
/// Helper struct used to process an iterator of `Result<Vec<T>, syn::Error>`,
/// combining errors into one along the way.
pub(crate) struct ResultSifter<T> {
items: Vec<T>,
errors: Option<syn::Error>,
}
impl<T> Default for ResultSifter<T> {
fn default() -> Self {
Self {
items: Vec::new(),
errors: None,
}
}
}
impl<T> ResultSifter<T> {
/// Sift the given result, combining errors if necessary.
pub fn sift(&mut self, result: Result<T, syn::Error>) {
match result {
Ok(data) => self.items.push(data),
Err(err) => {
if let Some(ref mut errors) = self.errors {
errors.combine(err);
} else {
self.errors = Some(err);
}
}
}
}
/// Associated method that provides a convenient implementation for [`Iterator::fold`].
pub fn fold(mut sifter: Self, result: Result<T, syn::Error>) -> Self {
sifter.sift(result);
sifter
}
/// Complete the sifting process and return the final result.
pub fn finish(self) -> Result<Vec<T>, syn::Error> {
if let Some(errors) = self.errors {
Err(errors)
} else {
Ok(self.items)
}
}
}

View File

@@ -0,0 +1,95 @@
use crate::{
derive_data::StructField,
field_attributes::{DefaultBehavior, ReflectIgnoreBehavior},
};
use bevy_macro_utils::fq_std::FQDefault;
use quote::quote;
use std::collections::HashMap;
use syn::{spanned::Spanned, Path};
type ReflectionIndex = usize;
/// Collected serialization data used to generate a `SerializationData` type.
pub(crate) struct SerializationDataDef {
/// Maps a field's _reflection_ index to its [`SkippedFieldDef`] if marked as `#[reflect(skip_serializing)]`.
skipped: HashMap<ReflectionIndex, SkippedFieldDef>,
}
impl SerializationDataDef {
/// Attempts to create a new `SerializationDataDef` from the given collection of fields.
///
/// Returns `Ok(Some(data))` if there are any fields needing to be skipped during serialization.
/// Otherwise, returns `Ok(None)`.
pub fn new(
fields: &[StructField<'_>],
bevy_reflect_path: &Path,
) -> Result<Option<Self>, syn::Error> {
let mut skipped = <HashMap<_, _>>::default();
for field in fields {
match field.attrs.ignore {
ReflectIgnoreBehavior::IgnoreSerialization => {
skipped.insert(
field.reflection_index.ok_or_else(|| {
syn::Error::new(
field.data.span(),
"internal error: field is missing a reflection index",
)
})?,
SkippedFieldDef::new(field, bevy_reflect_path)?,
);
}
_ => continue,
}
}
if skipped.is_empty() {
Ok(None)
} else {
Ok(Some(Self { skipped }))
}
}
/// Returns a `TokenStream` containing an initialized `SerializationData` type.
pub fn as_serialization_data(&self, bevy_reflect_path: &Path) -> proc_macro2::TokenStream {
let fields =
self.skipped
.iter()
.map(|(reflection_index, SkippedFieldDef { default_fn })| {
quote! {(
#reflection_index,
#bevy_reflect_path::serde::SkippedField::new(#default_fn)
)}
});
quote! {
#bevy_reflect_path::serde::SerializationData::new(
::core::iter::IntoIterator::into_iter([#(#fields),*])
)
}
}
}
/// Collected field data used to generate a `SkippedField` type.
pub(crate) struct SkippedFieldDef {
/// The default function for this field.
///
/// This is of type `fn() -> Box<dyn Reflect>`.
default_fn: proc_macro2::TokenStream,
}
impl SkippedFieldDef {
pub fn new(field: &StructField<'_>, bevy_reflect_path: &Path) -> Result<Self, syn::Error> {
let ty = &field.data.ty;
let default_fn = match &field.attrs.default {
DefaultBehavior::Func(func) => quote! {
|| { #bevy_reflect_path::__macro_exports::alloc_utils::Box::new(#func()) }
},
_ => quote! {
|| { #bevy_reflect_path::__macro_exports::alloc_utils::Box::new(<#ty as #FQDefault>::default()) }
},
};
Ok(Self { default_fn })
}
}

View File

@@ -0,0 +1,108 @@
use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
use syn::{spanned::Spanned, LitStr};
/// Contains tokens representing different kinds of string.
#[derive(Clone)]
pub(crate) enum StringExpr {
/// A string that is valid at compile time.
///
/// This is either a string literal like `"mystring"`,
/// or a string created by a macro like [`module_path`]
/// or [`concat`].
Const(TokenStream),
/// A [string slice](str) that is borrowed for a `'static` lifetime.
Borrowed(TokenStream),
/// An [owned string](String).
Owned(TokenStream),
}
impl<T: ToString + Spanned> From<T> for StringExpr {
fn from(value: T) -> Self {
Self::from_lit(&LitStr::new(&value.to_string(), value.span()))
}
}
impl StringExpr {
/// Creates a [constant] [`StringExpr`] from a [`struct@LitStr`].
///
/// [constant]: StringExpr::Const
pub fn from_lit(lit: &LitStr) -> Self {
Self::Const(lit.to_token_stream())
}
/// Creates a [constant] [`StringExpr`] by interpreting a [string slice][str] as a [`struct@LitStr`].
///
/// [constant]: StringExpr::Const
pub fn from_str(string: &str) -> Self {
Self::Const(string.into_token_stream())
}
/// Returns tokens for an [owned string](String).
///
/// The returned expression will allocate unless the [`StringExpr`] is [already owned].
///
/// [already owned]: StringExpr::Owned
pub fn into_owned(self) -> TokenStream {
let bevy_reflect_path = crate::meta::get_bevy_reflect_path();
match self {
Self::Const(tokens) | Self::Borrowed(tokens) => quote! {
#bevy_reflect_path::__macro_exports::alloc_utils::ToString::to_string(#tokens)
},
Self::Owned(owned) => owned,
}
}
/// Returns tokens for a statically borrowed [string slice](str).
pub fn into_borrowed(self) -> TokenStream {
match self {
Self::Const(tokens) | Self::Borrowed(tokens) => tokens,
Self::Owned(owned) => quote! {
&#owned
},
}
}
/// Appends a [`StringExpr`] to another.
///
/// If both expressions are [`StringExpr::Const`] this will use [`concat`] to merge them.
pub fn appended_by(mut self, other: StringExpr) -> Self {
if let Self::Const(tokens) = self {
if let Self::Const(more) = other {
return Self::Const(quote! {
::core::concat!(#tokens, #more)
});
}
self = Self::Const(tokens);
}
let owned = self.into_owned();
let borrowed = other.into_borrowed();
Self::Owned(quote! {
::core::ops::Add::<&str>::add(#owned, #borrowed)
})
}
}
impl Default for StringExpr {
fn default() -> Self {
StringExpr::from_str("")
}
}
impl FromIterator<StringExpr> for StringExpr {
fn from_iter<T: IntoIterator<Item = StringExpr>>(iter: T) -> Self {
let mut iter = iter.into_iter();
match iter.next() {
Some(mut expr) => {
for next in iter {
expr = expr.appended_by(next);
}
expr
}
None => Default::default(),
}
}
}

View File

@@ -0,0 +1,42 @@
use crate::ReflectStruct;
/// A helper struct for creating remote-aware field accessors.
///
/// These are "remote-aware" because when a field is a remote field, it uses a [`transmute`] internally
/// to access the field.
///
/// [`transmute`]: std::mem::transmute
pub(crate) struct FieldAccessors {
/// The referenced field accessors, such as `&self.foo`.
pub fields_ref: Vec<proc_macro2::TokenStream>,
/// The mutably referenced field accessors, such as `&mut self.foo`.
pub fields_mut: Vec<proc_macro2::TokenStream>,
/// The ordered set of field indices (basically just the range of [0, `field_count`).
pub field_indices: Vec<usize>,
/// The number of fields in the reflected struct.
pub field_count: usize,
}
impl FieldAccessors {
pub fn new(reflect_struct: &ReflectStruct) -> Self {
let (fields_ref, fields_mut): (Vec<_>, Vec<_>) = reflect_struct
.active_fields()
.map(|field| {
(
reflect_struct.access_for_field(field, false),
reflect_struct.access_for_field(field, true),
)
})
.unzip();
let field_count = fields_ref.len();
let field_indices = (0..field_count).collect();
Self {
fields_ref,
fields_mut,
field_indices,
field_count,
}
}
}

View File

@@ -0,0 +1,93 @@
use bevy_macro_utils::fq_std::{FQClone, FQOption, FQResult};
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse::Parse, parse_macro_input, Attribute, ItemTrait, Token};
pub(crate) struct TraitInfo {
item_trait: ItemTrait,
}
impl Parse for TraitInfo {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let attrs = input.call(Attribute::parse_outer)?;
let lookahead = input.lookahead1();
if lookahead.peek(Token![pub]) || lookahead.peek(Token![trait]) {
let mut item_trait: ItemTrait = input.parse()?;
item_trait.attrs = attrs;
Ok(TraitInfo { item_trait })
} else {
Err(lookahead.error())
}
}
}
/// A trait attribute macro that allows a reflected type to be downcast to a trait object.
///
/// This generates a struct that takes the form `ReflectMyTrait`. An instance of this struct can then be
/// used to perform the conversion.
pub(crate) fn reflect_trait(_args: &TokenStream, input: TokenStream) -> TokenStream {
let trait_info = parse_macro_input!(input as TraitInfo);
let item_trait = &trait_info.item_trait;
let trait_ident = &item_trait.ident;
let trait_vis = &item_trait.vis;
let reflect_trait_ident = crate::ident::get_reflect_ident(&item_trait.ident.to_string());
let bevy_reflect_path = crate::meta::get_bevy_reflect_path();
let struct_doc = format!(
" A type generated by the #[reflect_trait] macro for the `{trait_ident}` trait.\n\n This allows casting from `dyn Reflect` to `dyn {trait_ident}`.",
);
let get_doc = format!(
" Downcast a `&dyn Reflect` type to `&dyn {trait_ident}`.\n\n If the type cannot be downcast, `None` is returned.",
);
let get_mut_doc = format!(
" Downcast a `&mut dyn Reflect` type to `&mut dyn {trait_ident}`.\n\n If the type cannot be downcast, `None` is returned.",
);
let get_box_doc = format!(
" Downcast a `Box<dyn Reflect>` type to `Box<dyn {trait_ident}>`.\n\n If the type cannot be downcast, this will return `Err(Box<dyn Reflect>)`.",
);
TokenStream::from(quote! {
#item_trait
#[doc = #struct_doc]
#[derive(#FQClone)]
#trait_vis struct #reflect_trait_ident {
get_func: fn(&dyn #bevy_reflect_path::Reflect) -> #FQOption<&dyn #trait_ident>,
get_mut_func: fn(&mut dyn #bevy_reflect_path::Reflect) -> #FQOption<&mut dyn #trait_ident>,
get_boxed_func: fn(#bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #bevy_reflect_path::Reflect>) -> #FQResult<#bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #trait_ident>, #bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #bevy_reflect_path::Reflect>>,
}
impl #reflect_trait_ident {
#[doc = #get_doc]
pub fn get<'a>(&self, reflect_value: &'a dyn #bevy_reflect_path::Reflect) -> #FQOption<&'a dyn #trait_ident> {
(self.get_func)(reflect_value)
}
#[doc = #get_mut_doc]
pub fn get_mut<'a>(&self, reflect_value: &'a mut dyn #bevy_reflect_path::Reflect) -> #FQOption<&'a mut dyn #trait_ident> {
(self.get_mut_func)(reflect_value)
}
#[doc = #get_box_doc]
pub fn get_boxed(&self, reflect_value: #bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #bevy_reflect_path::Reflect>) -> #FQResult<#bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #trait_ident>, #bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #bevy_reflect_path::Reflect>> {
(self.get_boxed_func)(reflect_value)
}
}
impl<T: #trait_ident + #bevy_reflect_path::Reflect> #bevy_reflect_path::FromType<T> for #reflect_trait_ident {
fn from_type() -> Self {
Self {
get_func: |reflect_value| {
<dyn #bevy_reflect_path::Reflect>::downcast_ref::<T>(reflect_value).map(|value| value as &dyn #trait_ident)
},
get_mut_func: |reflect_value| {
<dyn #bevy_reflect_path::Reflect>::downcast_mut::<T>(reflect_value).map(|value| value as &mut dyn #trait_ident)
},
get_boxed_func: |reflect_value| {
<dyn #bevy_reflect_path::Reflect>::downcast::<T>(reflect_value).map(|value| value as #bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #trait_ident>)
}
}
}
}
})
}

View File

@@ -0,0 +1,102 @@
use proc_macro2::Ident;
use syn::{
parenthesized,
parse::{Parse, ParseStream},
token::Paren,
Generics, Path, PathSegment, Token,
};
pub(crate) fn parse_path_no_leading_colon(input: ParseStream) -> syn::Result<Path> {
if input.peek(Token![::]) {
return Err(input.error("did not expect a leading double colon (`::`)"));
}
let path = Path::parse_mod_style(input)?;
if path.segments.is_empty() {
Err(input.error("expected a path"))
} else {
Ok(path)
}
}
/// An alias for a `TypePath`.
///
/// This is the parenthesized part of `(in my_crate::foo as MyType) SomeType`.
pub(crate) struct CustomPathDef {
path: Path,
name: Option<Ident>,
}
impl CustomPathDef {
pub fn into_path(mut self, default_name: &Ident) -> Path {
let name = PathSegment::from(self.name.unwrap_or_else(|| default_name.clone()));
self.path.segments.push(name);
self.path
}
pub fn parse_parenthesized(input: ParseStream) -> syn::Result<Option<Self>> {
if input.peek(Paren) {
let path;
parenthesized!(path in input);
Ok(Some(path.call(Self::parse)?))
} else {
Ok(None)
}
}
fn parse(input: ParseStream) -> syn::Result<Self> {
input.parse::<Token![in]>()?;
let custom_path = parse_path_no_leading_colon(input)?;
if !input.peek(Token![as]) {
return Ok(Self {
path: custom_path,
name: None,
});
}
input.parse::<Token![as]>()?;
let custom_name: Ident = input.parse()?;
Ok(Self {
path: custom_path,
name: Some(custom_name),
})
}
}
pub(crate) enum NamedTypePathDef {
External {
path: Path,
generics: Generics,
custom_path: Option<CustomPathDef>,
},
Primitive(Ident),
}
impl Parse for NamedTypePathDef {
fn parse(input: ParseStream) -> syn::Result<Self> {
let custom_path = CustomPathDef::parse_parenthesized(input)?;
let path = Path::parse_mod_style(input)?;
let mut generics = input.parse::<Generics>()?;
generics.where_clause = input.parse()?;
if path.leading_colon.is_none() && custom_path.is_none() {
if path.segments.len() == 1 {
let ident = path.segments.into_iter().next().unwrap().ident;
Ok(NamedTypePathDef::Primitive(ident))
} else {
Err(input.error("non-customized paths must start with a double colon (`::`)"))
}
} else {
Ok(NamedTypePathDef::External {
path,
generics,
custom_path,
})
}
}
}

View File

@@ -0,0 +1,198 @@
use crate::derive_data::ReflectMeta;
use bevy_macro_utils::fq_std::{FQAny, FQSend, FQSync};
use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
use syn::{punctuated::Punctuated, Token, Type, WhereClause};
/// Options defining how to extend the `where` clause for reflection.
pub(crate) struct WhereClauseOptions<'a, 'b> {
meta: &'a ReflectMeta<'b>,
active_fields: Box<[Type]>,
}
impl<'a, 'b> WhereClauseOptions<'a, 'b> {
pub fn new(meta: &'a ReflectMeta<'b>) -> Self {
Self {
meta,
active_fields: Box::new([]),
}
}
pub fn new_with_fields(meta: &'a ReflectMeta<'b>, active_fields: Box<[Type]>) -> Self {
Self {
meta,
active_fields,
}
}
/// Extends the `where` clause for a type with additional bounds needed for the reflection impls.
///
/// The default bounds added are as follows:
/// - `Self` has the bounds `Any + Send + Sync`
/// - Type parameters have the bound `TypePath` unless `#[reflect(type_path = false)]` is present
/// - Active fields have the bounds `TypePath` and either `PartialReflect` if `#[reflect(from_reflect = false)]` is present
/// or `FromReflect` otherwise (or no bounds at all if `#[reflect(no_field_bounds)]` is present)
///
/// When the derive is used with `#[reflect(where)]`, the bounds specified in the attribute are added as well.
///
/// # Example
///
/// ```ignore (bevy_reflect is not accessible from this crate)
/// #[derive(Reflect)]
/// struct Foo<T, U> {
/// a: T,
/// #[reflect(ignore)]
/// b: U
/// }
/// ```
///
/// Generates the following where clause:
///
/// ```ignore (bevy_reflect is not accessible from this crate)
/// where
/// // `Self` bounds:
/// Self: Any + Send + Sync,
/// // Type parameter bounds:
/// T: TypePath,
/// U: TypePath,
/// // Field bounds
/// T: FromReflect + TypePath,
/// ```
///
/// If we had added `#[reflect(where T: MyTrait)]` to the type, it would instead generate:
///
/// ```ignore (bevy_reflect is not accessible from this crate)
/// where
/// // `Self` bounds:
/// Self: Any + Send + Sync,
/// // Type parameter bounds:
/// T: TypePath,
/// U: TypePath,
/// // Field bounds
/// T: FromReflect + TypePath,
/// // Custom bounds
/// T: MyTrait,
/// ```
///
/// And if we also added `#[reflect(no_field_bounds)]` to the type, it would instead generate:
///
/// ```ignore (bevy_reflect is not accessible from this crate)
/// where
/// // `Self` bounds:
/// Self: Any + Send + Sync,
/// // Type parameter bounds:
/// T: TypePath,
/// U: TypePath,
/// // Custom bounds
/// T: MyTrait,
/// ```
pub fn extend_where_clause(&self, where_clause: Option<&WhereClause>) -> TokenStream {
// We would normally just use `Self`, but that won't work for generating things like assertion functions
// and trait impls for a type's reference (e.g. `impl FromArg for &MyType`)
let this = self.meta.type_path().true_type();
let required_bounds = self.required_bounds();
// Maintain existing where clause, if any.
let mut generic_where_clause = if let Some(where_clause) = where_clause {
let predicates = where_clause.predicates.iter();
quote! {where #this: #required_bounds, #(#predicates,)*}
} else {
quote!(where #this: #required_bounds,)
};
// Add additional reflection trait bounds
let predicates = self.predicates();
generic_where_clause.extend(quote! {
#predicates
});
generic_where_clause
}
/// Returns an iterator the where clause predicates to extended the where clause with.
fn predicates(&self) -> Punctuated<TokenStream, Token![,]> {
let mut predicates = Punctuated::new();
if let Some(type_param_predicates) = self.type_param_predicates() {
predicates.extend(type_param_predicates);
}
if let Some(field_predicates) = self.active_field_predicates() {
predicates.extend(field_predicates);
}
if let Some(custom_where) = self.meta.attrs().custom_where() {
predicates.push(custom_where.predicates.to_token_stream());
}
predicates
}
/// Returns an iterator over the where clause predicates for the type parameters
/// if they require one.
fn type_param_predicates(&self) -> Option<impl Iterator<Item = TokenStream> + '_> {
self.type_path_bound().map(|type_path_bound| {
self.meta
.type_path()
.generics()
.type_params()
.map(move |param| {
let ident = &param.ident;
quote!(#ident : #type_path_bound)
})
})
}
/// Returns an iterator over the where clause predicates for the active fields.
fn active_field_predicates(&self) -> Option<impl Iterator<Item = TokenStream> + '_> {
if self.meta.attrs().no_field_bounds() {
None
} else {
let bevy_reflect_path = self.meta.bevy_reflect_path();
let reflect_bound = self.reflect_bound();
// `TypePath` is always required for active fields since they are used to
// construct `NamedField` and `UnnamedField` instances for the `Typed` impl.
// Likewise, `GetTypeRegistration` is always required for active fields since
// they are used to register the type's dependencies.
Some(self.active_fields.iter().map(move |ty| {
quote!(
#ty : #reflect_bound
+ #bevy_reflect_path::TypePath
// Needed for `Typed` impls
+ #bevy_reflect_path::MaybeTyped
// Needed for `GetTypeRegistration` impls
+ #bevy_reflect_path::__macro_exports::RegisterForReflection
)
}))
}
}
/// The `PartialReflect` or `FromReflect` bound to use based on `#[reflect(from_reflect = false)]`.
fn reflect_bound(&self) -> TokenStream {
let bevy_reflect_path = self.meta.bevy_reflect_path();
if self.meta.from_reflect().should_auto_derive() {
quote!(#bevy_reflect_path::FromReflect)
} else {
quote!(#bevy_reflect_path::PartialReflect)
}
}
/// The `TypePath` bounds to use based on `#[reflect(type_path = false)]`.
fn type_path_bound(&self) -> Option<TokenStream> {
if self.meta.type_path_attrs().should_auto_derive() {
let bevy_reflect_path = self.meta.bevy_reflect_path();
Some(quote!(#bevy_reflect_path::TypePath))
} else {
None
}
}
/// The minimum required bounds for a type to be reflected.
fn required_bounds(&self) -> TokenStream {
quote!(#FQAny + #FQSend + #FQSync)
}
}