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}