Suppress the `clippy::type_complexity` lint (#8313)
# Objective
The clippy lint `type_complexity` is known not to play well with bevy.
It frequently triggers when writing complex queries, and taking the
lint's advice of using a type alias almost always just obfuscates the
code with no benefit. Because of this, this lint is currently ignored in
CI, but unfortunately it still shows up when viewing bevy code in an
IDE.
As someone who's made a fair amount of pull requests to this repo, I
will say that this issue has been a consistent thorn in my side. Since
bevy code is filled with spurious, ignorable warnings, it can be very
difficult to spot the *real* warnings that must be fixed -- most of the
time I just ignore all warnings, only to later find out that one of them
was real after I'm done when CI runs.
## Solution
Suppress this lint in all bevy crates. This was previously attempted in
#7050, but the review process ended up making it more complicated than
it needs to be and landed on a subpar solution.
The discussion in https://github.com/rust-lang/rust-clippy/pull/10571
explores some better long-term solutions to this problem. Since there is
no timeline on when these solutions may land, we should resolve this
issue in the meantime by locally suppressing these lints.
### Unresolved issues
Currently, these lints are not suppressed in our examples, since that
would require suppressing the lint in every single source file. They are
still ignored in CI.
2023-04-06 21:27:36 +00:00
|
|
|
#![allow(clippy::type_complexity)]
|
|
|
|
|
2021-05-19 19:03:36 +00:00
|
|
|
extern crate proc_macro;
|
|
|
|
|
2021-10-03 19:23:44 +00:00
|
|
|
mod attrs;
|
2022-01-15 22:14:43 +00:00
|
|
|
mod shape;
|
2021-10-03 19:23:44 +00:00
|
|
|
mod symbol;
|
|
|
|
|
|
|
|
pub use attrs::*;
|
2022-01-15 22:14:43 +00:00
|
|
|
pub use shape::*;
|
2021-10-03 19:23:44 +00:00
|
|
|
pub use symbol::*;
|
|
|
|
|
2023-03-22 15:45:25 +00:00
|
|
|
use proc_macro::{TokenStream, TokenTree};
|
Add attribute to ignore fields of derived labels (#5366)
# Objective
Fixes #5362
## Solution
Add the attribute `#[label(ignore_fields)]` for `*Label` types.
```rust
#[derive(SystemLabel)]
pub enum MyLabel {
One,
// Previously this was not allowed since labels cannot contain data.
#[system_label(ignore_fields)]
Two(PhantomData<usize>),
}
```
## Notes
This label makes it possible for equality to behave differently depending on whether or not you are treating the type as a label. For example:
```rust
#[derive(SystemLabel, PartialEq, Eq)]
#[system_label(ignore_fields)]
pub struct Foo(usize);
```
If you compare it as a label, it will ignore the wrapped fields as the user requested. But if you compare it as a `Foo`, the derive will incorrectly compare the inner fields. I see a few solutions
1. Do nothing. This is technically intended behavior, but I think we should do our best to prevent footguns.
2. Generate impls of `PartialEq` and `Eq` along with the `#[derive(Label)]` macros. This is a breaking change as it requires all users to remove these derives from their types.
3. Only allow `PhantomData` to be used with `ignore_fields` -- seems needlessly prescriptive.
---
## Changelog
* Added the `ignore_fields` attribute to the derive macros for `*Label` types.
* Added an example showing off different forms of the derive macro.
<!--
## Migration Guide
> This section is optional. If there are no breaking changes, you can delete this section.
- If this PR is a breaking change (relative to the last release of Bevy), describe how a user might need to migrate their code to support these changes
- Simply adding new functionality is not a breaking change.
- Fixing behavior that was definitely a bug, rather than a questionable design choice is not a breaking change.
-->
2022-07-19 05:21:19 +00:00
|
|
|
use quote::{quote, quote_spanned};
|
2023-03-22 15:45:25 +00:00
|
|
|
use rustc_hash::FxHashSet;
|
2021-05-19 19:03:36 +00:00
|
|
|
use std::{env, path::PathBuf};
|
2023-03-22 15:45:25 +00:00
|
|
|
use syn::{spanned::Spanned, Ident};
|
2023-01-22 19:41:48 +00:00
|
|
|
use toml_edit::{Document, Item};
|
2021-05-19 19:03:36 +00:00
|
|
|
|
|
|
|
pub struct BevyManifest {
|
2023-01-22 19:41:48 +00:00
|
|
|
manifest: Document,
|
2021-05-19 19:03:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for BevyManifest {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
manifest: env::var_os("CARGO_MANIFEST_DIR")
|
|
|
|
.map(PathBuf::from)
|
|
|
|
.map(|mut path| {
|
|
|
|
path.push("Cargo.toml");
|
Add some more helpful errors to BevyManifest when it doesn't find Cargo.toml (#9207)
When building Bevy using Bazel, you don't need a 'Cargo.toml'... except
Bevy requires it currently. Hopefully this can help illuminate the
requirement.
# Objective
I recently started exploring Bazel and Buck2. Currently Bazel has some
great advantages over Cargo for me and I was pretty happy to find that
things generally work quite well!
Once I added a target to my test project that depended on bevy but
didn't use Cargo, I didn't create a Cargo.toml file for it and things
appeared to work, but as soon as I went to derive from Component the
build failed with the cryptic error:
```
ERROR: /Users/photex/workspaces/personal/mb-rogue/scratch/BUILD:24:12: Compiling Rust bin hello_bevy (0 files) failed: (Exit 1): process_wrapper failed: error executing command (from target //scratch:hello_bevy) bazel-out/darwin_arm64-opt-exec-2B5CBBC6/bin/external/rules_rust/util/process_wrapper/process_wrapper --arg-file ... (remaining 312 arguments skipped)
error: proc-macro derive panicked
--> scratch/hello_bevy.rs:5:10
|
5 | #[derive(Component)]
| ^^^^^^^^^
|
= help: message: called `Result::unwrap()` on an `Err` value: Os { code: 2, kind: NotFound, message: "No such file or directory" }
error: proc-macro derive panicked
--> scratch/hello_bevy.rs:8:10
|
8 | #[derive(Component)]
| ^^^^^^^^^
|
= help: message: called `Result::unwrap()` on an `Err` value: Os { code: 2, kind: NotFound, message: "No such file or directory" }
```
## Solution
After poking around I realized that the proc macros in Bevy all use
bevy_macro_utils::BevyManifest, which was attempting to load a Cargo
manifest that doesn't exist.
This PR doesn't address the Cargo requirement (I'd love to see if there
was a way to support more than Cargo transparently), but it *does*
replace some calls to unwrap with expect and hopefully the error
messages will be more helpful for other folks like me hoping to pat down
a new trail:
```
ERROR: /Users/photex/workspaces/personal/mb-rogue/scratch/BUILD:23:12: Compiling Rust bin hello_bevy (0 files) failed: (Exit 1): process_wrapper failed: error executing command (from target //scratch:hello_bevy) bazel-out/darwin_arm64-opt-exec-2B5CBBC6/bin/external/rules_rust/util/process_wrapper/process_wrapper --arg-file ... (remaining 312 arguments skipped)
error: proc-macro derive panicked
--> scratch/hello_bevy.rs:5:10
|
5 | #[derive(Component)]
| ^^^^^^^^^
|
= help: message: Unable to read cargo manifest: /private/var/tmp/_bazel_photex/135f23dc56826c24d6c3c9f6b688b2fe/execroot/__main__/scratch/Cargo.toml: Os { code: 2, kind: NotFound, message: "No such file or directory" }
error: proc-macro derive panicked
--> scratch/hello_bevy.rs:8:10
|
8 | #[derive(Component)]
| ^^^^^^^^^
|
= help: message: Unable to read cargo manifest: /private/var/tmp/_bazel_photex/135f23dc56826c24d6c3c9f6b688b2fe/execroot/__main__/scratch/Cargo.toml: Os { code: 2, kind: NotFound, message: "No such file or directory" }
```
Co-authored-by: Chip Collier <chip.collier@avid.com>
2023-07-19 12:05:04 +00:00
|
|
|
if !path.exists() {
|
|
|
|
panic!(
|
|
|
|
"No Cargo manifest found for crate. Expected: {}",
|
|
|
|
path.display()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
let manifest = std::fs::read_to_string(path.clone()).unwrap_or_else(|_| {
|
|
|
|
panic!("Unable to read cargo manifest: {}", path.display())
|
|
|
|
});
|
|
|
|
manifest.parse::<Document>().unwrap_or_else(|_| {
|
|
|
|
panic!("Failed to parse cargo manifest: {}", path.display())
|
|
|
|
})
|
2021-05-19 19:03:36 +00:00
|
|
|
})
|
Add some more helpful errors to BevyManifest when it doesn't find Cargo.toml (#9207)
When building Bevy using Bazel, you don't need a 'Cargo.toml'... except
Bevy requires it currently. Hopefully this can help illuminate the
requirement.
# Objective
I recently started exploring Bazel and Buck2. Currently Bazel has some
great advantages over Cargo for me and I was pretty happy to find that
things generally work quite well!
Once I added a target to my test project that depended on bevy but
didn't use Cargo, I didn't create a Cargo.toml file for it and things
appeared to work, but as soon as I went to derive from Component the
build failed with the cryptic error:
```
ERROR: /Users/photex/workspaces/personal/mb-rogue/scratch/BUILD:24:12: Compiling Rust bin hello_bevy (0 files) failed: (Exit 1): process_wrapper failed: error executing command (from target //scratch:hello_bevy) bazel-out/darwin_arm64-opt-exec-2B5CBBC6/bin/external/rules_rust/util/process_wrapper/process_wrapper --arg-file ... (remaining 312 arguments skipped)
error: proc-macro derive panicked
--> scratch/hello_bevy.rs:5:10
|
5 | #[derive(Component)]
| ^^^^^^^^^
|
= help: message: called `Result::unwrap()` on an `Err` value: Os { code: 2, kind: NotFound, message: "No such file or directory" }
error: proc-macro derive panicked
--> scratch/hello_bevy.rs:8:10
|
8 | #[derive(Component)]
| ^^^^^^^^^
|
= help: message: called `Result::unwrap()` on an `Err` value: Os { code: 2, kind: NotFound, message: "No such file or directory" }
```
## Solution
After poking around I realized that the proc macros in Bevy all use
bevy_macro_utils::BevyManifest, which was attempting to load a Cargo
manifest that doesn't exist.
This PR doesn't address the Cargo requirement (I'd love to see if there
was a way to support more than Cargo transparently), but it *does*
replace some calls to unwrap with expect and hopefully the error
messages will be more helpful for other folks like me hoping to pat down
a new trail:
```
ERROR: /Users/photex/workspaces/personal/mb-rogue/scratch/BUILD:23:12: Compiling Rust bin hello_bevy (0 files) failed: (Exit 1): process_wrapper failed: error executing command (from target //scratch:hello_bevy) bazel-out/darwin_arm64-opt-exec-2B5CBBC6/bin/external/rules_rust/util/process_wrapper/process_wrapper --arg-file ... (remaining 312 arguments skipped)
error: proc-macro derive panicked
--> scratch/hello_bevy.rs:5:10
|
5 | #[derive(Component)]
| ^^^^^^^^^
|
= help: message: Unable to read cargo manifest: /private/var/tmp/_bazel_photex/135f23dc56826c24d6c3c9f6b688b2fe/execroot/__main__/scratch/Cargo.toml: Os { code: 2, kind: NotFound, message: "No such file or directory" }
error: proc-macro derive panicked
--> scratch/hello_bevy.rs:8:10
|
8 | #[derive(Component)]
| ^^^^^^^^^
|
= help: message: Unable to read cargo manifest: /private/var/tmp/_bazel_photex/135f23dc56826c24d6c3c9f6b688b2fe/execroot/__main__/scratch/Cargo.toml: Os { code: 2, kind: NotFound, message: "No such file or directory" }
```
Co-authored-by: Chip Collier <chip.collier@avid.com>
2023-07-19 12:05:04 +00:00
|
|
|
.expect("CARGO_MANIFEST_DIR is not defined."),
|
2021-05-19 19:03:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-01-11 21:12:02 +00:00
|
|
|
const BEVY: &str = "bevy";
|
|
|
|
const BEVY_INTERNAL: &str = "bevy_internal";
|
2021-05-19 19:03:36 +00:00
|
|
|
|
|
|
|
impl BevyManifest {
|
2021-12-23 22:49:12 +00:00
|
|
|
pub fn maybe_get_path(&self, name: &str) -> Option<syn::Path> {
|
2023-01-22 19:41:48 +00:00
|
|
|
fn dep_package(dep: &Item) -> Option<&str> {
|
2022-04-27 19:08:11 +00:00
|
|
|
if dep.as_str().is_some() {
|
|
|
|
None
|
|
|
|
} else {
|
2023-01-22 19:41:48 +00:00
|
|
|
dep.get("package").map(|name| name.as_str().unwrap())
|
2022-04-27 19:08:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-22 19:41:48 +00:00
|
|
|
let find_in_deps = |deps: &Item| -> Option<syn::Path> {
|
Fix path used by macro not considering that we can use a sub-crate (#3178)
# Problem
Let's say I am writting a simple bevy plugin, and I want to depend on `bevy_ecs` crate instead of depending on the full `bevy`.
So I write the following:
*Cargo.toml*:
```toml
[dependencies]
bevy_ecs = { git = "https://github.com/bevyengine/bevy.git", rev = "94db0176fecfac7e7e9763f2dc7458a54c105886" }
```
*lib.rs*:
```rust
use bevy_ecs::prelude::*;
#[derive(Debug, Default, Component)
pub struct MyFancyComponent;
```
So far, so good. Everything works. But let's say I want to write some examples for using my plugin. And for theses I'd like to use the `bevy` crate, so that I can write complete examples (rendering stuff, etc.) that are simple and look like what the consumer of my plugin will do (`use bevy::prelude::*` and `DefaultPlugins`)
So I amend my *Cargo.toml*:
```toml
[dependencies]
bevy_ecs = { git = "https://github.com/bevyengine/bevy.git", rev = "94db0176fecfac7e7e9763f2dc7458a54c105886" }
[dev-dependencies]
bevy = { git = "https://github.com/bevyengine/bevy.git", rev = "94db0176fecfac7e7e9763f2dc7458a54c105886", default-features = false }
```
And that leads to a complilation error
```
error[E0433]: failed to resolve: use of undeclared crate or module `bevy`
```
Basically, because `bevy` is in the `dev-dependencies`, the macro (of the production code) decides to use the `bevy::ecs` path instead of `bevy_ecs`. But `bevy` is not available there.
## Solution
This PR fixes the problem. I amend the macro utility responsible of finding the path of a module.
If we try to find a path, we first test if this correspond to a crate that the user directly depend on. (Like, if we search for `bevy_ecs`, we first check if there is a `bevy_ecs` dependency). If yes, we can depend on that directly. Otherwise, we proceed with the existing logic (testing `bevy` and `bevy_internal`)
2021-11-29 23:10:31 +00:00
|
|
|
let package = if let Some(dep) = deps.get(name) {
|
2022-04-27 19:08:11 +00:00
|
|
|
return Some(Self::parse_str(dep_package(dep).unwrap_or(name)));
|
Fix path used by macro not considering that we can use a sub-crate (#3178)
# Problem
Let's say I am writting a simple bevy plugin, and I want to depend on `bevy_ecs` crate instead of depending on the full `bevy`.
So I write the following:
*Cargo.toml*:
```toml
[dependencies]
bevy_ecs = { git = "https://github.com/bevyengine/bevy.git", rev = "94db0176fecfac7e7e9763f2dc7458a54c105886" }
```
*lib.rs*:
```rust
use bevy_ecs::prelude::*;
#[derive(Debug, Default, Component)
pub struct MyFancyComponent;
```
So far, so good. Everything works. But let's say I want to write some examples for using my plugin. And for theses I'd like to use the `bevy` crate, so that I can write complete examples (rendering stuff, etc.) that are simple and look like what the consumer of my plugin will do (`use bevy::prelude::*` and `DefaultPlugins`)
So I amend my *Cargo.toml*:
```toml
[dependencies]
bevy_ecs = { git = "https://github.com/bevyengine/bevy.git", rev = "94db0176fecfac7e7e9763f2dc7458a54c105886" }
[dev-dependencies]
bevy = { git = "https://github.com/bevyengine/bevy.git", rev = "94db0176fecfac7e7e9763f2dc7458a54c105886", default-features = false }
```
And that leads to a complilation error
```
error[E0433]: failed to resolve: use of undeclared crate or module `bevy`
```
Basically, because `bevy` is in the `dev-dependencies`, the macro (of the production code) decides to use the `bevy::ecs` path instead of `bevy_ecs`. But `bevy` is not available there.
## Solution
This PR fixes the problem. I amend the macro utility responsible of finding the path of a module.
If we try to find a path, we first test if this correspond to a crate that the user directly depend on. (Like, if we search for `bevy_ecs`, we first check if there is a `bevy_ecs` dependency). If yes, we can depend on that directly. Otherwise, we proceed with the existing logic (testing `bevy` and `bevy_internal`)
2021-11-29 23:10:31 +00:00
|
|
|
} else if let Some(dep) = deps.get(BEVY) {
|
2022-04-27 19:08:11 +00:00
|
|
|
dep_package(dep).unwrap_or(BEVY)
|
2021-05-19 19:03:36 +00:00
|
|
|
} else if let Some(dep) = deps.get(BEVY_INTERNAL) {
|
2022-04-27 19:08:11 +00:00
|
|
|
dep_package(dep).unwrap_or(BEVY_INTERNAL)
|
2021-05-19 19:03:36 +00:00
|
|
|
} else {
|
|
|
|
return None;
|
|
|
|
};
|
|
|
|
|
2021-12-23 22:49:12 +00:00
|
|
|
let mut path = Self::parse_str::<syn::Path>(package);
|
2021-05-19 19:03:36 +00:00
|
|
|
if let Some(module) = name.strip_prefix("bevy_") {
|
2021-12-23 22:49:12 +00:00
|
|
|
path.segments.push(Self::parse_str(module));
|
2021-05-19 19:03:36 +00:00
|
|
|
}
|
|
|
|
Some(path)
|
|
|
|
};
|
|
|
|
|
2023-01-22 19:41:48 +00:00
|
|
|
let deps = self.manifest.get("dependencies");
|
|
|
|
let deps_dev = self.manifest.get("dev-dependencies");
|
2021-05-19 19:03:36 +00:00
|
|
|
|
|
|
|
deps.and_then(find_in_deps)
|
|
|
|
.or_else(|| deps_dev.and_then(find_in_deps))
|
|
|
|
}
|
bevy_reflect_derive: Tidying up the code (#4712)
# Objective
The `bevy_reflect_derive` crate is not the cleanest or easiest to follow/maintain. The `lib.rs` file is especially difficult with over 1000 lines of code written in a confusing order. This is just a result of growth within the crate and it would be nice to clean it up for future work.
## Solution
Split `bevy_reflect_derive` into many more submodules. The submodules include:
* `container_attributes` - Code relating to container attributes
* `derive_data` - Code relating to reflection-based derive metadata
* `field_attributes` - Code relating to field attributes
* `impls` - Code containing actual reflection implementations
* `reflect_value` - Code relating to reflection-based value metadata
* `registration` - Code relating to type registration
* `utility` - General-purpose utility functions
This leaves the `lib.rs` file to contain only the public macros, making it much easier to digest (and fewer than 200 lines).
By breaking up the code into smaller modules, we make it easier for future contributors to find the code they're looking for or identify which module best fits their own additions.
### Metadata Structs
This cleanup also adds two big metadata structs: `ReflectFieldAttr` and `ReflectDeriveData`. The former is used to store all attributes for a struct field (if any). The latter is used to store all metadata for struct-based derive inputs.
Both significantly reduce code duplication and make editing these macros much simpler. The tradeoff is that we may collect more metadata than needed. However, this is usually a small thing (such as checking for attributes when they're not really needed or creating a `ReflectFieldAttr` for every field regardless of whether they actually have an attribute).
We could try to remove these tradeoffs and squeeze some more performance out, but doing so might come at the cost of developer experience. Personally, I think it's much nicer to create a `ReflectFieldAttr` for every field since it means I don't have to do two `Option` checks. Others may disagree, though, and so we can discuss changing this either in this PR or in a future one.
### Out of Scope
_Some_ documentation has been added or improved, but ultimately good docs are probably best saved for a dedicated PR.
## 🔍 Focus Points (for reviewers)
I know it's a lot to sift through, so here is a list of **key points for reviewers**:
- The following files contain code that was mostly just relocated:
- `reflect_value.rs`
- `registration.rs`
- `container_attributes.rs` was also mostly moved but features some general cleanup (reducing nesting, removing hardcoded strings, etc.) and lots of doc comments
- Most impl logic was moved from `lib.rs` to `impls.rs`, but they have been significantly modified to use the new `ReflectDeriveData` metadata struct in order to reduce duplication.
- `derive_data.rs` and `field_attributes.rs` contain almost entirely new code and should probably be given the most attention.
- Likewise, `from_reflect.rs` saw major changes using `ReflectDeriveData` so it should also be given focus.
- There was no change to the `lib.rs` exports so the end-user API should be the same.
## Prior Work
This task was initially tackled by @NathanSWard in #2377 (which was closed in favor of this PR), so hats off to them for beating me to the punch by nearly a year!
---
## Changelog
* **[INTERNAL]** Split `bevy_reflect_derive` into smaller submodules
* **[INTERNAL]** Add `ReflectFieldAttr`
* **[INTERNAL]** Add `ReflectDeriveData`
* Add `BevyManifest::get_path_direct()` method (`bevy_macro_utils`)
Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
2022-05-12 19:43:23 +00:00
|
|
|
|
|
|
|
/// Returns the path for the crate with the given name.
|
|
|
|
///
|
|
|
|
/// This is a convenience method for constructing a [manifest] and
|
|
|
|
/// calling the [`get_path`] method.
|
|
|
|
///
|
|
|
|
/// This method should only be used where you just need the path and can't
|
|
|
|
/// cache the [manifest]. If caching is possible, it's recommended to create
|
|
|
|
/// the [manifest] yourself and use the [`get_path`] method.
|
|
|
|
///
|
|
|
|
/// [`get_path`]: Self::get_path
|
|
|
|
/// [manifest]: Self
|
|
|
|
pub fn get_path_direct(name: &str) -> syn::Path {
|
|
|
|
Self::default().get_path(name)
|
|
|
|
}
|
|
|
|
|
2021-12-23 22:49:12 +00:00
|
|
|
pub fn get_path(&self, name: &str) -> syn::Path {
|
|
|
|
self.maybe_get_path(name)
|
|
|
|
.unwrap_or_else(|| Self::parse_str(name))
|
|
|
|
}
|
2021-05-19 19:03:36 +00:00
|
|
|
|
2021-12-23 22:49:12 +00:00
|
|
|
pub fn parse_str<T: syn::parse::Parse>(path: &str) -> T {
|
|
|
|
syn::parse(path.parse::<TokenStream>().unwrap()).unwrap()
|
|
|
|
}
|
2023-01-11 21:12:02 +00:00
|
|
|
|
|
|
|
pub fn get_subcrate(&self, subcrate: &str) -> Option<syn::Path> {
|
|
|
|
self.maybe_get_path(BEVY)
|
|
|
|
.map(|bevy_path| {
|
|
|
|
let mut segments = bevy_path.segments;
|
|
|
|
segments.push(BevyManifest::parse_str(subcrate));
|
|
|
|
syn::Path {
|
|
|
|
leading_colon: None,
|
|
|
|
segments,
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.or_else(|| self.maybe_get_path(&format!("bevy_{subcrate}")))
|
|
|
|
}
|
2021-05-19 19:03:36 +00:00
|
|
|
}
|
2021-08-24 00:31:21 +00:00
|
|
|
|
2023-03-22 15:45:25 +00:00
|
|
|
/// Finds an identifier that will not conflict with the specified set of tokens.
|
|
|
|
/// If the identifier is present in `haystack`, extra characters will be added
|
|
|
|
/// to it until it no longer conflicts with anything.
|
|
|
|
///
|
|
|
|
/// Note that the returned identifier can still conflict in niche cases,
|
|
|
|
/// such as if an identifier in `haystack` is hidden behind an un-expanded macro.
|
|
|
|
pub fn ensure_no_collision(value: Ident, haystack: TokenStream) -> Ident {
|
|
|
|
// Collect all the identifiers in `haystack` into a set.
|
|
|
|
let idents = {
|
|
|
|
// List of token streams that will be visited in future loop iterations.
|
|
|
|
let mut unvisited = vec![haystack];
|
|
|
|
// Identifiers we have found while searching tokens.
|
|
|
|
let mut found = FxHashSet::default();
|
|
|
|
while let Some(tokens) = unvisited.pop() {
|
|
|
|
for t in tokens {
|
|
|
|
match t {
|
|
|
|
// Collect any identifiers we encounter.
|
|
|
|
TokenTree::Ident(ident) => {
|
|
|
|
found.insert(ident.to_string());
|
|
|
|
}
|
|
|
|
// Queue up nested token streams to be visited in a future loop iteration.
|
|
|
|
TokenTree::Group(g) => unvisited.push(g.stream()),
|
|
|
|
TokenTree::Punct(_) | TokenTree::Literal(_) => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
found
|
|
|
|
};
|
|
|
|
|
|
|
|
let span = value.span();
|
|
|
|
|
|
|
|
// If there's a collision, add more characters to the identifier
|
|
|
|
// until it doesn't collide with anything anymore.
|
|
|
|
let mut value = value.to_string();
|
|
|
|
while idents.contains(&value) {
|
|
|
|
value.push('X');
|
|
|
|
}
|
|
|
|
|
|
|
|
Ident::new(&value, span)
|
|
|
|
}
|
|
|
|
|
2023-01-17 01:39:17 +00:00
|
|
|
/// Derive a label trait
|
|
|
|
///
|
|
|
|
/// # Args
|
|
|
|
///
|
|
|
|
/// - `input`: The [`syn::DeriveInput`] for struct that is deriving the label trait
|
|
|
|
/// - `trait_path`: The path [`syn::Path`] to the label trait
|
|
|
|
pub fn derive_boxed_label(input: syn::DeriveInput, trait_path: &syn::Path) -> TokenStream {
|
2023-04-19 02:36:44 +00:00
|
|
|
let bevy_utils_path = BevyManifest::default().get_path("bevy_utils");
|
|
|
|
|
2023-01-17 01:39:17 +00:00
|
|
|
let ident = input.ident;
|
|
|
|
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
|
|
|
|
let mut where_clause = where_clause.cloned().unwrap_or_else(|| syn::WhereClause {
|
|
|
|
where_token: Default::default(),
|
|
|
|
predicates: Default::default(),
|
|
|
|
});
|
|
|
|
where_clause.predicates.push(
|
|
|
|
syn::parse2(quote! {
|
|
|
|
Self: 'static + Send + Sync + Clone + Eq + ::std::fmt::Debug + ::std::hash::Hash
|
|
|
|
})
|
|
|
|
.unwrap(),
|
|
|
|
);
|
|
|
|
|
|
|
|
(quote! {
|
|
|
|
impl #impl_generics #trait_path for #ident #ty_generics #where_clause {
|
|
|
|
fn dyn_clone(&self) -> std::boxed::Box<dyn #trait_path> {
|
|
|
|
std::boxed::Box::new(std::clone::Clone::clone(self))
|
|
|
|
}
|
2023-04-19 02:36:44 +00:00
|
|
|
|
|
|
|
fn as_dyn_eq(&self) -> &dyn #bevy_utils_path::label::DynEq {
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
fn dyn_hash(&self, mut state: &mut dyn ::std::hash::Hasher) {
|
|
|
|
let ty_id = #trait_path::inner_type_id(self);
|
|
|
|
::std::hash::Hash::hash(&ty_id, &mut state);
|
|
|
|
::std::hash::Hash::hash(self, &mut state);
|
|
|
|
}
|
2023-01-17 01:39:17 +00:00
|
|
|
}
|
2023-04-19 19:48:35 +00:00
|
|
|
|
|
|
|
impl #impl_generics ::std::convert::AsRef<dyn #trait_path> for #ident #ty_generics #where_clause {
|
|
|
|
fn as_ref(&self) -> &dyn #trait_path {
|
|
|
|
self
|
|
|
|
}
|
|
|
|
}
|
2023-01-17 01:39:17 +00:00
|
|
|
})
|
|
|
|
.into()
|
|
|
|
}
|
|
|
|
|
2021-08-24 00:31:21 +00:00
|
|
|
/// Derive a label trait
|
|
|
|
///
|
|
|
|
/// # Args
|
|
|
|
///
|
|
|
|
/// - `input`: The [`syn::DeriveInput`] for struct that is deriving the label trait
|
|
|
|
/// - `trait_path`: The path [`syn::Path`] to the label trait
|
Add attribute to ignore fields of derived labels (#5366)
# Objective
Fixes #5362
## Solution
Add the attribute `#[label(ignore_fields)]` for `*Label` types.
```rust
#[derive(SystemLabel)]
pub enum MyLabel {
One,
// Previously this was not allowed since labels cannot contain data.
#[system_label(ignore_fields)]
Two(PhantomData<usize>),
}
```
## Notes
This label makes it possible for equality to behave differently depending on whether or not you are treating the type as a label. For example:
```rust
#[derive(SystemLabel, PartialEq, Eq)]
#[system_label(ignore_fields)]
pub struct Foo(usize);
```
If you compare it as a label, it will ignore the wrapped fields as the user requested. But if you compare it as a `Foo`, the derive will incorrectly compare the inner fields. I see a few solutions
1. Do nothing. This is technically intended behavior, but I think we should do our best to prevent footguns.
2. Generate impls of `PartialEq` and `Eq` along with the `#[derive(Label)]` macros. This is a breaking change as it requires all users to remove these derives from their types.
3. Only allow `PhantomData` to be used with `ignore_fields` -- seems needlessly prescriptive.
---
## Changelog
* Added the `ignore_fields` attribute to the derive macros for `*Label` types.
* Added an example showing off different forms of the derive macro.
<!--
## Migration Guide
> This section is optional. If there are no breaking changes, you can delete this section.
- If this PR is a breaking change (relative to the last release of Bevy), describe how a user might need to migrate their code to support these changes
- Simply adding new functionality is not a breaking change.
- Fixing behavior that was definitely a bug, rather than a questionable design choice is not a breaking change.
-->
2022-07-19 05:21:19 +00:00
|
|
|
pub fn derive_label(
|
|
|
|
input: syn::DeriveInput,
|
|
|
|
trait_path: &syn::Path,
|
|
|
|
attr_name: &str,
|
|
|
|
) -> TokenStream {
|
|
|
|
// return true if the variant specified is an `ignore_fields` attribute
|
|
|
|
fn is_ignore(attr: &syn::Attribute, attr_name: &str) -> bool {
|
2023-05-16 01:24:17 +00:00
|
|
|
if attr.path().get_ident().as_ref().unwrap() != &attr_name {
|
Add attribute to ignore fields of derived labels (#5366)
# Objective
Fixes #5362
## Solution
Add the attribute `#[label(ignore_fields)]` for `*Label` types.
```rust
#[derive(SystemLabel)]
pub enum MyLabel {
One,
// Previously this was not allowed since labels cannot contain data.
#[system_label(ignore_fields)]
Two(PhantomData<usize>),
}
```
## Notes
This label makes it possible for equality to behave differently depending on whether or not you are treating the type as a label. For example:
```rust
#[derive(SystemLabel, PartialEq, Eq)]
#[system_label(ignore_fields)]
pub struct Foo(usize);
```
If you compare it as a label, it will ignore the wrapped fields as the user requested. But if you compare it as a `Foo`, the derive will incorrectly compare the inner fields. I see a few solutions
1. Do nothing. This is technically intended behavior, but I think we should do our best to prevent footguns.
2. Generate impls of `PartialEq` and `Eq` along with the `#[derive(Label)]` macros. This is a breaking change as it requires all users to remove these derives from their types.
3. Only allow `PhantomData` to be used with `ignore_fields` -- seems needlessly prescriptive.
---
## Changelog
* Added the `ignore_fields` attribute to the derive macros for `*Label` types.
* Added an example showing off different forms of the derive macro.
<!--
## Migration Guide
> This section is optional. If there are no breaking changes, you can delete this section.
- If this PR is a breaking change (relative to the last release of Bevy), describe how a user might need to migrate their code to support these changes
- Simply adding new functionality is not a breaking change.
- Fixing behavior that was definitely a bug, rather than a questionable design choice is not a breaking change.
-->
2022-07-19 05:21:19 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
syn::custom_keyword!(ignore_fields);
|
|
|
|
attr.parse_args_with(|input: syn::parse::ParseStream| {
|
|
|
|
let ignore = input.parse::<Option<ignore_fields>>()?.is_some();
|
|
|
|
Ok(ignore)
|
|
|
|
})
|
|
|
|
.unwrap()
|
|
|
|
}
|
|
|
|
|
|
|
|
let ident = input.ident.clone();
|
2021-08-24 00:31:21 +00:00
|
|
|
|
|
|
|
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
|
|
|
|
let mut where_clause = where_clause.cloned().unwrap_or_else(|| syn::WhereClause {
|
|
|
|
where_token: Default::default(),
|
|
|
|
predicates: Default::default(),
|
|
|
|
});
|
Simplify design for `*Label`s (#4957)
# Objective
- Closes #4954
- Reduce the complexity of the `{System, App, *}Label` APIs.
## Solution
For the sake of brevity I will only refer to `SystemLabel`, but everything applies to all of the other label types as well.
- Add `SystemLabelId`, a lightweight, `copy` struct.
- Convert custom types into `SystemLabelId` using the trait `SystemLabel`.
## Changelog
- String literals implement `SystemLabel` for now, but this should be changed with #4409 .
## Migration Guide
- Any previous use of `Box<dyn SystemLabel>` should be replaced with `SystemLabelId`.
- `AsSystemLabel` trait has been modified.
- No more output generics.
- Method `as_system_label` now returns `SystemLabelId`, removing an unnecessary level of indirection.
- If you *need* a label that is determined at runtime, you can use `Box::leak`. Not recommended.
## Questions for later
* Should we generate a `Debug` impl along with `#[derive(*Label)]`?
* Should we rename `as_str()`?
* Should we remove the extra derives (such as `Hash`) from builtin `*Label` types?
* Should we automatically derive types like `Clone, Copy, PartialEq, Eq`?
* More-ergonomic comparisons between `Label` and `LabelId`.
* Move `Dyn{Eq, Hash,Clone}` somewhere else.
* Some API to make interning dynamic labels easier.
* Optimize string representation
* Empty string for unit structs -- no debug info but faster comparisons
* Don't show enum types -- same tradeoffs as asbove.
2022-07-14 18:23:01 +00:00
|
|
|
where_clause
|
|
|
|
.predicates
|
|
|
|
.push(syn::parse2(quote! { Self: 'static }).unwrap());
|
|
|
|
|
|
|
|
let as_str = match input.data {
|
Add attribute to ignore fields of derived labels (#5366)
# Objective
Fixes #5362
## Solution
Add the attribute `#[label(ignore_fields)]` for `*Label` types.
```rust
#[derive(SystemLabel)]
pub enum MyLabel {
One,
// Previously this was not allowed since labels cannot contain data.
#[system_label(ignore_fields)]
Two(PhantomData<usize>),
}
```
## Notes
This label makes it possible for equality to behave differently depending on whether or not you are treating the type as a label. For example:
```rust
#[derive(SystemLabel, PartialEq, Eq)]
#[system_label(ignore_fields)]
pub struct Foo(usize);
```
If you compare it as a label, it will ignore the wrapped fields as the user requested. But if you compare it as a `Foo`, the derive will incorrectly compare the inner fields. I see a few solutions
1. Do nothing. This is technically intended behavior, but I think we should do our best to prevent footguns.
2. Generate impls of `PartialEq` and `Eq` along with the `#[derive(Label)]` macros. This is a breaking change as it requires all users to remove these derives from their types.
3. Only allow `PhantomData` to be used with `ignore_fields` -- seems needlessly prescriptive.
---
## Changelog
* Added the `ignore_fields` attribute to the derive macros for `*Label` types.
* Added an example showing off different forms of the derive macro.
<!--
## Migration Guide
> This section is optional. If there are no breaking changes, you can delete this section.
- If this PR is a breaking change (relative to the last release of Bevy), describe how a user might need to migrate their code to support these changes
- Simply adding new functionality is not a breaking change.
- Fixing behavior that was definitely a bug, rather than a questionable design choice is not a breaking change.
-->
2022-07-19 05:21:19 +00:00
|
|
|
syn::Data::Struct(d) => {
|
|
|
|
// see if the user tried to ignore fields incorrectly
|
|
|
|
if let Some(attr) = d
|
|
|
|
.fields
|
|
|
|
.iter()
|
|
|
|
.flat_map(|f| &f.attrs)
|
|
|
|
.find(|a| is_ignore(a, attr_name))
|
|
|
|
{
|
|
|
|
let err_msg = format!("`#[{attr_name}(ignore_fields)]` cannot be applied to fields individually: add it to the struct declaration");
|
|
|
|
return quote_spanned! {
|
|
|
|
attr.span() => compile_error!(#err_msg);
|
|
|
|
}
|
|
|
|
.into();
|
|
|
|
}
|
|
|
|
// Structs must either be fieldless, or explicitly ignore the fields.
|
|
|
|
let ignore_fields = input.attrs.iter().any(|a| is_ignore(a, attr_name));
|
|
|
|
if matches!(d.fields, syn::Fields::Unit) || ignore_fields {
|
Simplify design for `*Label`s (#4957)
# Objective
- Closes #4954
- Reduce the complexity of the `{System, App, *}Label` APIs.
## Solution
For the sake of brevity I will only refer to `SystemLabel`, but everything applies to all of the other label types as well.
- Add `SystemLabelId`, a lightweight, `copy` struct.
- Convert custom types into `SystemLabelId` using the trait `SystemLabel`.
## Changelog
- String literals implement `SystemLabel` for now, but this should be changed with #4409 .
## Migration Guide
- Any previous use of `Box<dyn SystemLabel>` should be replaced with `SystemLabelId`.
- `AsSystemLabel` trait has been modified.
- No more output generics.
- Method `as_system_label` now returns `SystemLabelId`, removing an unnecessary level of indirection.
- If you *need* a label that is determined at runtime, you can use `Box::leak`. Not recommended.
## Questions for later
* Should we generate a `Debug` impl along with `#[derive(*Label)]`?
* Should we rename `as_str()`?
* Should we remove the extra derives (such as `Hash`) from builtin `*Label` types?
* Should we automatically derive types like `Clone, Copy, PartialEq, Eq`?
* More-ergonomic comparisons between `Label` and `LabelId`.
* Move `Dyn{Eq, Hash,Clone}` somewhere else.
* Some API to make interning dynamic labels easier.
* Optimize string representation
* Empty string for unit structs -- no debug info but faster comparisons
* Don't show enum types -- same tradeoffs as asbove.
2022-07-14 18:23:01 +00:00
|
|
|
let lit = ident.to_string();
|
|
|
|
quote! { #lit }
|
Add attribute to ignore fields of derived labels (#5366)
# Objective
Fixes #5362
## Solution
Add the attribute `#[label(ignore_fields)]` for `*Label` types.
```rust
#[derive(SystemLabel)]
pub enum MyLabel {
One,
// Previously this was not allowed since labels cannot contain data.
#[system_label(ignore_fields)]
Two(PhantomData<usize>),
}
```
## Notes
This label makes it possible for equality to behave differently depending on whether or not you are treating the type as a label. For example:
```rust
#[derive(SystemLabel, PartialEq, Eq)]
#[system_label(ignore_fields)]
pub struct Foo(usize);
```
If you compare it as a label, it will ignore the wrapped fields as the user requested. But if you compare it as a `Foo`, the derive will incorrectly compare the inner fields. I see a few solutions
1. Do nothing. This is technically intended behavior, but I think we should do our best to prevent footguns.
2. Generate impls of `PartialEq` and `Eq` along with the `#[derive(Label)]` macros. This is a breaking change as it requires all users to remove these derives from their types.
3. Only allow `PhantomData` to be used with `ignore_fields` -- seems needlessly prescriptive.
---
## Changelog
* Added the `ignore_fields` attribute to the derive macros for `*Label` types.
* Added an example showing off different forms of the derive macro.
<!--
## Migration Guide
> This section is optional. If there are no breaking changes, you can delete this section.
- If this PR is a breaking change (relative to the last release of Bevy), describe how a user might need to migrate their code to support these changes
- Simply adding new functionality is not a breaking change.
- Fixing behavior that was definitely a bug, rather than a questionable design choice is not a breaking change.
-->
2022-07-19 05:21:19 +00:00
|
|
|
} else {
|
|
|
|
let err_msg = format!("Labels cannot contain data, unless explicitly ignored with `#[{attr_name}(ignore_fields)]`");
|
|
|
|
return quote_spanned! {
|
|
|
|
d.fields.span() => compile_error!(#err_msg);
|
|
|
|
}
|
|
|
|
.into();
|
Simplify design for `*Label`s (#4957)
# Objective
- Closes #4954
- Reduce the complexity of the `{System, App, *}Label` APIs.
## Solution
For the sake of brevity I will only refer to `SystemLabel`, but everything applies to all of the other label types as well.
- Add `SystemLabelId`, a lightweight, `copy` struct.
- Convert custom types into `SystemLabelId` using the trait `SystemLabel`.
## Changelog
- String literals implement `SystemLabel` for now, but this should be changed with #4409 .
## Migration Guide
- Any previous use of `Box<dyn SystemLabel>` should be replaced with `SystemLabelId`.
- `AsSystemLabel` trait has been modified.
- No more output generics.
- Method `as_system_label` now returns `SystemLabelId`, removing an unnecessary level of indirection.
- If you *need* a label that is determined at runtime, you can use `Box::leak`. Not recommended.
## Questions for later
* Should we generate a `Debug` impl along with `#[derive(*Label)]`?
* Should we rename `as_str()`?
* Should we remove the extra derives (such as `Hash`) from builtin `*Label` types?
* Should we automatically derive types like `Clone, Copy, PartialEq, Eq`?
* More-ergonomic comparisons between `Label` and `LabelId`.
* Move `Dyn{Eq, Hash,Clone}` somewhere else.
* Some API to make interning dynamic labels easier.
* Optimize string representation
* Empty string for unit structs -- no debug info but faster comparisons
* Don't show enum types -- same tradeoffs as asbove.
2022-07-14 18:23:01 +00:00
|
|
|
}
|
Add attribute to ignore fields of derived labels (#5366)
# Objective
Fixes #5362
## Solution
Add the attribute `#[label(ignore_fields)]` for `*Label` types.
```rust
#[derive(SystemLabel)]
pub enum MyLabel {
One,
// Previously this was not allowed since labels cannot contain data.
#[system_label(ignore_fields)]
Two(PhantomData<usize>),
}
```
## Notes
This label makes it possible for equality to behave differently depending on whether or not you are treating the type as a label. For example:
```rust
#[derive(SystemLabel, PartialEq, Eq)]
#[system_label(ignore_fields)]
pub struct Foo(usize);
```
If you compare it as a label, it will ignore the wrapped fields as the user requested. But if you compare it as a `Foo`, the derive will incorrectly compare the inner fields. I see a few solutions
1. Do nothing. This is technically intended behavior, but I think we should do our best to prevent footguns.
2. Generate impls of `PartialEq` and `Eq` along with the `#[derive(Label)]` macros. This is a breaking change as it requires all users to remove these derives from their types.
3. Only allow `PhantomData` to be used with `ignore_fields` -- seems needlessly prescriptive.
---
## Changelog
* Added the `ignore_fields` attribute to the derive macros for `*Label` types.
* Added an example showing off different forms of the derive macro.
<!--
## Migration Guide
> This section is optional. If there are no breaking changes, you can delete this section.
- If this PR is a breaking change (relative to the last release of Bevy), describe how a user might need to migrate their code to support these changes
- Simply adding new functionality is not a breaking change.
- Fixing behavior that was definitely a bug, rather than a questionable design choice is not a breaking change.
-->
2022-07-19 05:21:19 +00:00
|
|
|
}
|
Simplify design for `*Label`s (#4957)
# Objective
- Closes #4954
- Reduce the complexity of the `{System, App, *}Label` APIs.
## Solution
For the sake of brevity I will only refer to `SystemLabel`, but everything applies to all of the other label types as well.
- Add `SystemLabelId`, a lightweight, `copy` struct.
- Convert custom types into `SystemLabelId` using the trait `SystemLabel`.
## Changelog
- String literals implement `SystemLabel` for now, but this should be changed with #4409 .
## Migration Guide
- Any previous use of `Box<dyn SystemLabel>` should be replaced with `SystemLabelId`.
- `AsSystemLabel` trait has been modified.
- No more output generics.
- Method `as_system_label` now returns `SystemLabelId`, removing an unnecessary level of indirection.
- If you *need* a label that is determined at runtime, you can use `Box::leak`. Not recommended.
## Questions for later
* Should we generate a `Debug` impl along with `#[derive(*Label)]`?
* Should we rename `as_str()`?
* Should we remove the extra derives (such as `Hash`) from builtin `*Label` types?
* Should we automatically derive types like `Clone, Copy, PartialEq, Eq`?
* More-ergonomic comparisons between `Label` and `LabelId`.
* Move `Dyn{Eq, Hash,Clone}` somewhere else.
* Some API to make interning dynamic labels easier.
* Optimize string representation
* Empty string for unit structs -- no debug info but faster comparisons
* Don't show enum types -- same tradeoffs as asbove.
2022-07-14 18:23:01 +00:00
|
|
|
syn::Data::Enum(d) => {
|
Add attribute to ignore fields of derived labels (#5366)
# Objective
Fixes #5362
## Solution
Add the attribute `#[label(ignore_fields)]` for `*Label` types.
```rust
#[derive(SystemLabel)]
pub enum MyLabel {
One,
// Previously this was not allowed since labels cannot contain data.
#[system_label(ignore_fields)]
Two(PhantomData<usize>),
}
```
## Notes
This label makes it possible for equality to behave differently depending on whether or not you are treating the type as a label. For example:
```rust
#[derive(SystemLabel, PartialEq, Eq)]
#[system_label(ignore_fields)]
pub struct Foo(usize);
```
If you compare it as a label, it will ignore the wrapped fields as the user requested. But if you compare it as a `Foo`, the derive will incorrectly compare the inner fields. I see a few solutions
1. Do nothing. This is technically intended behavior, but I think we should do our best to prevent footguns.
2. Generate impls of `PartialEq` and `Eq` along with the `#[derive(Label)]` macros. This is a breaking change as it requires all users to remove these derives from their types.
3. Only allow `PhantomData` to be used with `ignore_fields` -- seems needlessly prescriptive.
---
## Changelog
* Added the `ignore_fields` attribute to the derive macros for `*Label` types.
* Added an example showing off different forms of the derive macro.
<!--
## Migration Guide
> This section is optional. If there are no breaking changes, you can delete this section.
- If this PR is a breaking change (relative to the last release of Bevy), describe how a user might need to migrate their code to support these changes
- Simply adding new functionality is not a breaking change.
- Fixing behavior that was definitely a bug, rather than a questionable design choice is not a breaking change.
-->
2022-07-19 05:21:19 +00:00
|
|
|
// check if the user put #[label(ignore_fields)] in the wrong place
|
|
|
|
if let Some(attr) = input.attrs.iter().find(|a| is_ignore(a, attr_name)) {
|
|
|
|
let err_msg = format!("`#[{attr_name}(ignore_fields)]` can only be applied to enum variants or struct declarations");
|
|
|
|
return quote_spanned! {
|
|
|
|
attr.span() => compile_error!(#err_msg);
|
|
|
|
}
|
|
|
|
.into();
|
|
|
|
}
|
|
|
|
let arms = d.variants.iter().map(|v| {
|
|
|
|
// Variants must either be fieldless, or explicitly ignore the fields.
|
|
|
|
let ignore_fields = v.attrs.iter().any(|a| is_ignore(a, attr_name));
|
|
|
|
if matches!(v.fields, syn::Fields::Unit) | ignore_fields {
|
Simplify design for `*Label`s (#4957)
# Objective
- Closes #4954
- Reduce the complexity of the `{System, App, *}Label` APIs.
## Solution
For the sake of brevity I will only refer to `SystemLabel`, but everything applies to all of the other label types as well.
- Add `SystemLabelId`, a lightweight, `copy` struct.
- Convert custom types into `SystemLabelId` using the trait `SystemLabel`.
## Changelog
- String literals implement `SystemLabel` for now, but this should be changed with #4409 .
## Migration Guide
- Any previous use of `Box<dyn SystemLabel>` should be replaced with `SystemLabelId`.
- `AsSystemLabel` trait has been modified.
- No more output generics.
- Method `as_system_label` now returns `SystemLabelId`, removing an unnecessary level of indirection.
- If you *need* a label that is determined at runtime, you can use `Box::leak`. Not recommended.
## Questions for later
* Should we generate a `Debug` impl along with `#[derive(*Label)]`?
* Should we rename `as_str()`?
* Should we remove the extra derives (such as `Hash`) from builtin `*Label` types?
* Should we automatically derive types like `Clone, Copy, PartialEq, Eq`?
* More-ergonomic comparisons between `Label` and `LabelId`.
* Move `Dyn{Eq, Hash,Clone}` somewhere else.
* Some API to make interning dynamic labels easier.
* Optimize string representation
* Empty string for unit structs -- no debug info but faster comparisons
* Don't show enum types -- same tradeoffs as asbove.
2022-07-14 18:23:01 +00:00
|
|
|
let mut path = syn::Path::from(ident.clone());
|
|
|
|
path.segments.push(v.ident.clone().into());
|
|
|
|
let lit = format!("{ident}::{}", v.ident.clone());
|
Add attribute to ignore fields of derived labels (#5366)
# Objective
Fixes #5362
## Solution
Add the attribute `#[label(ignore_fields)]` for `*Label` types.
```rust
#[derive(SystemLabel)]
pub enum MyLabel {
One,
// Previously this was not allowed since labels cannot contain data.
#[system_label(ignore_fields)]
Two(PhantomData<usize>),
}
```
## Notes
This label makes it possible for equality to behave differently depending on whether or not you are treating the type as a label. For example:
```rust
#[derive(SystemLabel, PartialEq, Eq)]
#[system_label(ignore_fields)]
pub struct Foo(usize);
```
If you compare it as a label, it will ignore the wrapped fields as the user requested. But if you compare it as a `Foo`, the derive will incorrectly compare the inner fields. I see a few solutions
1. Do nothing. This is technically intended behavior, but I think we should do our best to prevent footguns.
2. Generate impls of `PartialEq` and `Eq` along with the `#[derive(Label)]` macros. This is a breaking change as it requires all users to remove these derives from their types.
3. Only allow `PhantomData` to be used with `ignore_fields` -- seems needlessly prescriptive.
---
## Changelog
* Added the `ignore_fields` attribute to the derive macros for `*Label` types.
* Added an example showing off different forms of the derive macro.
<!--
## Migration Guide
> This section is optional. If there are no breaking changes, you can delete this section.
- If this PR is a breaking change (relative to the last release of Bevy), describe how a user might need to migrate their code to support these changes
- Simply adding new functionality is not a breaking change.
- Fixing behavior that was definitely a bug, rather than a questionable design choice is not a breaking change.
-->
2022-07-19 05:21:19 +00:00
|
|
|
quote! { #path { .. } => #lit }
|
|
|
|
} else {
|
|
|
|
let err_msg = format!("Label variants cannot contain data, unless explicitly ignored with `#[{attr_name}(ignore_fields)]`");
|
|
|
|
quote_spanned! {
|
|
|
|
v.fields.span() => _ => { compile_error!(#err_msg); }
|
|
|
|
}
|
Simplify design for `*Label`s (#4957)
# Objective
- Closes #4954
- Reduce the complexity of the `{System, App, *}Label` APIs.
## Solution
For the sake of brevity I will only refer to `SystemLabel`, but everything applies to all of the other label types as well.
- Add `SystemLabelId`, a lightweight, `copy` struct.
- Convert custom types into `SystemLabelId` using the trait `SystemLabel`.
## Changelog
- String literals implement `SystemLabel` for now, but this should be changed with #4409 .
## Migration Guide
- Any previous use of `Box<dyn SystemLabel>` should be replaced with `SystemLabelId`.
- `AsSystemLabel` trait has been modified.
- No more output generics.
- Method `as_system_label` now returns `SystemLabelId`, removing an unnecessary level of indirection.
- If you *need* a label that is determined at runtime, you can use `Box::leak`. Not recommended.
## Questions for later
* Should we generate a `Debug` impl along with `#[derive(*Label)]`?
* Should we rename `as_str()`?
* Should we remove the extra derives (such as `Hash`) from builtin `*Label` types?
* Should we automatically derive types like `Clone, Copy, PartialEq, Eq`?
* More-ergonomic comparisons between `Label` and `LabelId`.
* Move `Dyn{Eq, Hash,Clone}` somewhere else.
* Some API to make interning dynamic labels easier.
* Optimize string representation
* Empty string for unit structs -- no debug info but faster comparisons
* Don't show enum types -- same tradeoffs as asbove.
2022-07-14 18:23:01 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
quote! {
|
|
|
|
match self {
|
|
|
|
#(#arms),*
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
Add attribute to ignore fields of derived labels (#5366)
# Objective
Fixes #5362
## Solution
Add the attribute `#[label(ignore_fields)]` for `*Label` types.
```rust
#[derive(SystemLabel)]
pub enum MyLabel {
One,
// Previously this was not allowed since labels cannot contain data.
#[system_label(ignore_fields)]
Two(PhantomData<usize>),
}
```
## Notes
This label makes it possible for equality to behave differently depending on whether or not you are treating the type as a label. For example:
```rust
#[derive(SystemLabel, PartialEq, Eq)]
#[system_label(ignore_fields)]
pub struct Foo(usize);
```
If you compare it as a label, it will ignore the wrapped fields as the user requested. But if you compare it as a `Foo`, the derive will incorrectly compare the inner fields. I see a few solutions
1. Do nothing. This is technically intended behavior, but I think we should do our best to prevent footguns.
2. Generate impls of `PartialEq` and `Eq` along with the `#[derive(Label)]` macros. This is a breaking change as it requires all users to remove these derives from their types.
3. Only allow `PhantomData` to be used with `ignore_fields` -- seems needlessly prescriptive.
---
## Changelog
* Added the `ignore_fields` attribute to the derive macros for `*Label` types.
* Added an example showing off different forms of the derive macro.
<!--
## Migration Guide
> This section is optional. If there are no breaking changes, you can delete this section.
- If this PR is a breaking change (relative to the last release of Bevy), describe how a user might need to migrate their code to support these changes
- Simply adding new functionality is not a breaking change.
- Fixing behavior that was definitely a bug, rather than a questionable design choice is not a breaking change.
-->
2022-07-19 05:21:19 +00:00
|
|
|
syn::Data::Union(_) => {
|
|
|
|
return quote_spanned! {
|
|
|
|
input.span() => compile_error!("Unions cannot be used as labels.");
|
|
|
|
}
|
|
|
|
.into();
|
|
|
|
}
|
Simplify design for `*Label`s (#4957)
# Objective
- Closes #4954
- Reduce the complexity of the `{System, App, *}Label` APIs.
## Solution
For the sake of brevity I will only refer to `SystemLabel`, but everything applies to all of the other label types as well.
- Add `SystemLabelId`, a lightweight, `copy` struct.
- Convert custom types into `SystemLabelId` using the trait `SystemLabel`.
## Changelog
- String literals implement `SystemLabel` for now, but this should be changed with #4409 .
## Migration Guide
- Any previous use of `Box<dyn SystemLabel>` should be replaced with `SystemLabelId`.
- `AsSystemLabel` trait has been modified.
- No more output generics.
- Method `as_system_label` now returns `SystemLabelId`, removing an unnecessary level of indirection.
- If you *need* a label that is determined at runtime, you can use `Box::leak`. Not recommended.
## Questions for later
* Should we generate a `Debug` impl along with `#[derive(*Label)]`?
* Should we rename `as_str()`?
* Should we remove the extra derives (such as `Hash`) from builtin `*Label` types?
* Should we automatically derive types like `Clone, Copy, PartialEq, Eq`?
* More-ergonomic comparisons between `Label` and `LabelId`.
* Move `Dyn{Eq, Hash,Clone}` somewhere else.
* Some API to make interning dynamic labels easier.
* Optimize string representation
* Empty string for unit structs -- no debug info but faster comparisons
* Don't show enum types -- same tradeoffs as asbove.
2022-07-14 18:23:01 +00:00
|
|
|
};
|
2021-08-24 00:31:21 +00:00
|
|
|
|
|
|
|
(quote! {
|
|
|
|
impl #impl_generics #trait_path for #ident #ty_generics #where_clause {
|
Simplify design for `*Label`s (#4957)
# Objective
- Closes #4954
- Reduce the complexity of the `{System, App, *}Label` APIs.
## Solution
For the sake of brevity I will only refer to `SystemLabel`, but everything applies to all of the other label types as well.
- Add `SystemLabelId`, a lightweight, `copy` struct.
- Convert custom types into `SystemLabelId` using the trait `SystemLabel`.
## Changelog
- String literals implement `SystemLabel` for now, but this should be changed with #4409 .
## Migration Guide
- Any previous use of `Box<dyn SystemLabel>` should be replaced with `SystemLabelId`.
- `AsSystemLabel` trait has been modified.
- No more output generics.
- Method `as_system_label` now returns `SystemLabelId`, removing an unnecessary level of indirection.
- If you *need* a label that is determined at runtime, you can use `Box::leak`. Not recommended.
## Questions for later
* Should we generate a `Debug` impl along with `#[derive(*Label)]`?
* Should we rename `as_str()`?
* Should we remove the extra derives (such as `Hash`) from builtin `*Label` types?
* Should we automatically derive types like `Clone, Copy, PartialEq, Eq`?
* More-ergonomic comparisons between `Label` and `LabelId`.
* Move `Dyn{Eq, Hash,Clone}` somewhere else.
* Some API to make interning dynamic labels easier.
* Optimize string representation
* Empty string for unit structs -- no debug info but faster comparisons
* Don't show enum types -- same tradeoffs as asbove.
2022-07-14 18:23:01 +00:00
|
|
|
fn as_str(&self) -> &'static str {
|
|
|
|
#as_str
|
2021-08-24 00:31:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.into()
|
|
|
|
}
|