objc2_proc_macros/lib.rs
1//! Procedural macros for the [`objc2` project](https://github.com/madsmtm/objc2).
2//!
3//! You should not need to use this crate directly, all its public items are
4//! exported in other crates.
5
6#![warn(missing_docs)]
7#![warn(missing_debug_implementations)]
8#![warn(clippy::missing_errors_doc)]
9#![warn(clippy::missing_panics_doc)]
10// Update in Cargo.toml as well.
11#![doc(html_root_url = "https://docs.rs/objc2-proc-macros/0.2.0")]
12
13use core::hash::{Hash, Hasher};
14
15use proc_macro::Ident;
16use proc_macro::Literal;
17use proc_macro::TokenStream;
18use proc_macro::TokenTree;
19
20/// Extract all identifiers in the given tokenstream.
21fn get_idents(input: TokenStream) -> impl Iterator<Item = Ident> {
22 input.into_iter().flat_map(|token| {
23 match token {
24 TokenTree::Group(group) => get_idents(group.stream()).collect::<Vec<_>>(),
25 TokenTree::Ident(ident) => {
26 vec![ident]
27 }
28 TokenTree::Punct(_) | TokenTree::Literal(_) => {
29 vec![]
30 }
31 }
32 .into_iter()
33 })
34}
35
36/// Creates a hash from the input and source code locations in the provided
37/// idents.
38///
39/// This hash is not guaranteed to be stable across compiler versions.
40///
41/// Tests are in [`objc2::__macro_helpers`].
42#[proc_macro]
43#[doc(hidden)]
44pub fn __hash_idents(input: TokenStream) -> TokenStream {
45 // Create the hasher
46 let mut hasher = std::collections::hash_map::DefaultHasher::new();
47
48 // Hash each ident
49 for ident in get_idents(input) {
50 ident.to_string().hash(&mut hasher);
51
52 // Hash the source code location of the ident
53 //
54 // HACK: the only somewhat-reasonable way to get "unique" data in a
55 // proc macro right now is from the `Debug` formatter for spans which
56 // includes the source code location... so just hash the whole `Debug`
57 // format output of the span
58 //
59 // Prior art in the `defmt` crate, see here:
60 // https://github.com/knurling-rs/defmt/blob/defmt-v0.3.1/macros/src/construct.rs
61 format!("{:?}", ident.span()).hash(&mut hasher);
62 }
63
64 // Get the hash from the hasher and return it as 16 hexadecimal characters
65 let s = format!("{:016x}", hasher.finish());
66 TokenTree::Literal(Literal::string(&s)).into()
67}