blob: 9a4fe4750776792d61f91391ce4871cbe6523899 [file] [log] [blame]
Devin Jeanpierre3dd5f4d2022-03-31 02:18:36 -07001// Part of the Crubit project, under the Apache License v2.0 with LLVM
2// Exceptions. See /LICENSE for license information.
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4
5use proc_macro::TokenStream;
6use quote::quote;
7
8#[proc_macro]
9pub fn symbol(input: TokenStream) -> TokenStream {
10 let input = syn::parse_macro_input!(input as syn::LitStr);
11 let string = input.value();
12 let parts = string.chars().map(|c| {
13 let c = syn::LitChar::new(c, input.span());
14 quote! {
Devin Jeanpierre12dfb9a2023-01-18 14:54:41 -080015 ::forward_declare::internal::C<#c>
Devin Jeanpierre3dd5f4d2022-03-31 02:18:36 -070016 }
17 });
18
19 TokenStream::from(quote! {
Devin Jeanpierre12dfb9a2023-01-18 14:54:41 -080020 ::forward_declare::internal::Symbol<(
Devin Jeanpierre3dd5f4d2022-03-31 02:18:36 -070021 #(#parts,)*
22 )>
23 })
24}
25
26struct ForwardDeclareArgs {
27 vis: syn::Visibility,
28 ident: syn::Ident,
29 #[allow(unused)] // honestly just here to make parsing code simpler.
30 sep: syn::Token![=],
31 symbol: syn::Type,
32}
33
34impl syn::parse::Parse for ForwardDeclareArgs {
35 fn parse(input: syn::parse::ParseStream) -> syn::parse::Result<Self> {
36 Ok(ForwardDeclareArgs {
37 vis: input.parse()?,
38 ident: input.parse()?,
39 sep: input.parse()?,
40 symbol: input.parse()?,
41 })
42 }
43}
44
45/// Generates a unique forward-declared IncompleteType.
46///
47/// Usage: `forward_declare!(Alias = "path");`.
48///
49/// This creates a type alias `type Alias = IncompleteType<symbol!("path"),
50/// ???>`, with some unique type for `???`.
51#[proc_macro]
52pub fn forward_declare(input: TokenStream) -> TokenStream {
53 let ForwardDeclareArgs { vis, ident, sep: _, symbol } =
54 syn::parse_macro_input!(input as ForwardDeclareArgs);
55 // Generate a unique struct for the second type parameter to Incomplete.
56 // This is the real reason we need to use a proc macro -- there's no way to do
57 // this in a non-proc macro. See https://github.com/rust-lang/rust/issues/29599.
58 //
59 // It basically suffices to just prefix forward_declare_ at the front and assume
60 // nobody else will do that. If someone forward-declares the same type twice
61 // in the same scope then this will be an error, but that would be true even
62 // if we made a totally random identifier. (Because the type alias we create
63 // with this will be duplicated.)
64 let param_ident = syn::Ident::new(
65 &format!("_forward_declare_{}", ident.to_string()),
66 proc_macro2::Span::call_site(),
67 );
68
69 TokenStream::from(quote! {
70 #vis struct #param_ident;
71 #vis type #ident = ::forward_declare::Incomplete<#symbol, #param_ident>;
72 })
73}