blob: 01ccd69e80a9b2cc6dcff18a990712bda1a38f90 [file] [log] [blame]
Brian Silverman4e662aa2022-05-11 23:10:19 -07001// Copyright 2020 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9#![forbid(unsafe_code)]
10
11use autocxx_parser::{IncludeCpp, SubclassAttrs};
12use proc_macro::TokenStream;
13use proc_macro2::{Ident, Span};
14use proc_macro_error::{abort, proc_macro_error};
15use quote::quote;
16use syn::parse::Parser;
17use syn::{parse_macro_input, parse_quote, Fields, Item, ItemStruct, Visibility};
18
19/// Implementation of the `include_cpp` macro. See documentation for `autocxx` crate.
20#[proc_macro_error]
21#[proc_macro]
22pub fn include_cpp_impl(input: TokenStream) -> TokenStream {
23 let include_cpp = parse_macro_input!(input as IncludeCpp);
24 TokenStream::from(include_cpp.generate_rs())
25}
26
27/// Attribute to state that a Rust `struct` is a C++ subclass.
28/// This adds an additional field to the struct which autocxx uses to
29/// track a C++ instantiation of this Rust subclass.
30#[proc_macro_error]
31#[proc_macro_attribute]
32pub fn subclass(attr: TokenStream, item: TokenStream) -> TokenStream {
33 let mut s: ItemStruct =
34 syn::parse(item).unwrap_or_else(|_| abort!(Span::call_site(), "Expected a struct"));
35 if !matches!(s.vis, Visibility::Public(..)) {
36 use syn::spanned::Spanned;
37 abort!(s.vis.span(), "Rust subclasses of C++ types must by public");
38 }
39 let id = &s.ident;
40 let cpp_ident = Ident::new(&format!("{}Cpp", id), Span::call_site());
41 let input = quote! {
42 cpp_peer: autocxx::subclass::CppSubclassCppPeerHolder<ffi:: #cpp_ident>
43 };
44 let parser = syn::Field::parse_named;
45 let new_field = parser.parse2(input).unwrap();
46 s.fields = match &mut s.fields {
47 Fields::Named(fields) => {
48 fields.named.push(new_field);
49 s.fields
50 },
51 Fields::Unit => Fields::Named(parse_quote! {
52 {
53 #new_field
54 }
55 }),
56 _ => abort!(Span::call_site(), "Expect a struct with named fields - use struct A{} or struct A; as opposed to struct A()"),
57 };
58 let subclass_attrs: SubclassAttrs = syn::parse(attr)
59 .unwrap_or_else(|_| abort!(Span::call_site(), "Unable to parse attributes"));
60 let self_owned_bit = if subclass_attrs.self_owned {
61 Some(quote! {
62 impl autocxx::subclass::CppSubclassSelfOwned<ffi::#cpp_ident> for #id {}
63 })
64 } else {
65 None
66 };
67 let toks = quote! {
68 #s
69
70 impl autocxx::subclass::CppSubclass<ffi::#cpp_ident> for #id {
71 fn peer_holder_mut(&mut self) -> &mut autocxx::subclass::CppSubclassCppPeerHolder<ffi::#cpp_ident> {
72 &mut self.cpp_peer
73 }
74 fn peer_holder(&self) -> &autocxx::subclass::CppSubclassCppPeerHolder<ffi::#cpp_ident> {
75 &self.cpp_peer
76 }
77 }
78
79 #self_owned_bit
80 };
81 toks.into()
82}
83
84/// Attribute to state that a Rust type is to be exported to C++
85/// in the `extern "Rust"` section of the generated `cxx` bindings.
86#[proc_macro_error]
87#[proc_macro_attribute]
88pub fn extern_rust_type(attr: TokenStream, input: TokenStream) -> TokenStream {
89 if !attr.is_empty() {
90 abort!(Span::call_site(), "Expected no attributes");
91 }
92 let i: Item =
93 syn::parse(input.clone()).unwrap_or_else(|_| abort!(Span::call_site(), "Expected an item"));
94 match i {
95 Item::Struct(..) | Item::Enum(..) | Item::Fn(..) => {}
96 _ => abort!(Span::call_site(), "Expected a struct or enum"),
97 }
98 input
99}
100
101/// Attribute to state that a Rust function is to be exported to C++
102/// in the `extern "Rust"` section of the generated `cxx` bindings.
103#[proc_macro_error]
104#[proc_macro_attribute]
105pub fn extern_rust_function(attr: TokenStream, input: TokenStream) -> TokenStream {
106 if !attr.is_empty() {
107 abort!(Span::call_site(), "Expected no attributes");
108 }
109 let i: Item =
110 syn::parse(input.clone()).unwrap_or_else(|_| abort!(Span::call_site(), "Expected an item"));
111 match i {
112 Item::Fn(..) => {}
113 _ => abort!(Span::call_site(), "Expected a function"),
114 }
115 input
116}
117
118/// Attribute which should never be encountered in real life.
119/// This is something which features in the Rust source code generated
120/// by autocxx-bindgen and passed to autocxx-engine, which should never
121/// normally be compiled by rustc before it undergoes further processing.
122#[proc_macro_error]
123#[proc_macro_attribute]
124pub fn cpp_semantics(_attr: TokenStream, _input: TokenStream) -> TokenStream {
125 abort!(
126 Span::call_site(),
127 "Please do not attempt to compile this code. \n\
128 This code is the output from the autocxx-specific version of bindgen, \n\
129 and should be interpreted by autocxx-engine before further usage."
130 );
131}