Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_macro_utils/src/label.rs
6595 views
1
use proc_macro::{TokenStream, TokenTree};
2
use quote::{quote, quote_spanned};
3
use std::collections::HashSet;
4
use syn::{spanned::Spanned, Ident};
5
6
/// Finds an identifier that will not conflict with the specified set of tokens.
7
///
8
/// If the identifier is present in `haystack`, extra characters will be added
9
/// to it until it no longer conflicts with anything.
10
///
11
/// Note that the returned identifier can still conflict in niche cases,
12
/// such as if an identifier in `haystack` is hidden behind an un-expanded macro.
13
pub fn ensure_no_collision(value: Ident, haystack: TokenStream) -> Ident {
14
// Collect all the identifiers in `haystack` into a set.
15
let idents = {
16
// List of token streams that will be visited in future loop iterations.
17
let mut unvisited = vec![haystack];
18
// Identifiers we have found while searching tokens.
19
let mut found = HashSet::new();
20
while let Some(tokens) = unvisited.pop() {
21
for t in tokens {
22
match t {
23
// Collect any identifiers we encounter.
24
TokenTree::Ident(ident) => {
25
found.insert(ident.to_string());
26
}
27
// Queue up nested token streams to be visited in a future loop iteration.
28
TokenTree::Group(g) => unvisited.push(g.stream()),
29
TokenTree::Punct(_) | TokenTree::Literal(_) => {}
30
}
31
}
32
}
33
34
found
35
};
36
37
let span = value.span();
38
39
// If there's a collision, add more characters to the identifier
40
// until it doesn't collide with anything anymore.
41
let mut value = value.to_string();
42
while idents.contains(&value) {
43
value.push('X');
44
}
45
46
Ident::new(&value, span)
47
}
48
49
/// Derive a label trait
50
///
51
/// # Args
52
///
53
/// - `input`: The [`syn::DeriveInput`] for struct that is deriving the label trait
54
/// - `trait_name`: Name of the label trait
55
/// - `trait_path`: The [path](`syn::Path`) to the label trait
56
/// - `dyn_eq_path`: The [path](`syn::Path`) to the `DynEq` trait
57
pub fn derive_label(
58
input: syn::DeriveInput,
59
trait_name: &str,
60
trait_path: &syn::Path,
61
) -> TokenStream {
62
if let syn::Data::Union(_) = &input.data {
63
let message = format!("Cannot derive {trait_name} for unions.");
64
return quote_spanned! {
65
input.span() => compile_error!(#message);
66
}
67
.into();
68
}
69
70
let ident = input.ident.clone();
71
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
72
let mut where_clause = where_clause.cloned().unwrap_or_else(|| syn::WhereClause {
73
where_token: Default::default(),
74
predicates: Default::default(),
75
});
76
where_clause.predicates.push(
77
syn::parse2(quote! {
78
Self: 'static + Send + Sync + Clone + Eq + ::core::fmt::Debug + ::core::hash::Hash
79
})
80
.unwrap(),
81
);
82
quote! {
83
// To ensure alloc is available, but also prevent its name from clashing, we place the implementation inside an anonymous constant
84
const _: () = {
85
extern crate alloc;
86
87
impl #impl_generics #trait_path for #ident #ty_generics #where_clause {
88
fn dyn_clone(&self) -> alloc::boxed::Box<dyn #trait_path> {
89
alloc::boxed::Box::new(::core::clone::Clone::clone(self))
90
}
91
}
92
};
93
}
94
.into()
95
}
96
97