Squashed 'third_party/autocxx/' content from commit 629e8fa53
git-subtree-dir: third_party/autocxx
git-subtree-split: 629e8fa531a633164c0b52e2a3cab536d4cd0849
Signed-off-by: Brian Silverman <bsilver16384@gmail.com>
Change-Id: I62a03b0049f49adf029e0204639cdb5468dde1a1
diff --git a/parser/src/directives.rs b/parser/src/directives.rs
new file mode 100644
index 0000000..160829e
--- /dev/null
+++ b/parser/src/directives.rs
@@ -0,0 +1,540 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use indexmap::map::IndexMap as HashMap;
+
+use once_cell::sync::OnceCell;
+use proc_macro2::Span;
+use proc_macro2::{Ident, TokenStream};
+use quote::{quote, ToTokens};
+use syn::parse::ParseStream;
+
+use crate::config::{Allowlist, AllowlistErr};
+use crate::directive_names::{EXTERN_RUST_FUN, EXTERN_RUST_TYPE, SUBCLASS};
+use crate::{AllowlistEntry, IncludeCppConfig};
+use crate::{ParseResult, RustFun, RustPath};
+
+pub(crate) struct DirectivesMap {
+ pub(crate) need_hexathorpe: HashMap<String, Box<dyn Directive>>,
+ pub(crate) need_exclamation: HashMap<String, Box<dyn Directive>>,
+}
+
+static DIRECTIVES: OnceCell<DirectivesMap> = OnceCell::new();
+
+pub(crate) fn get_directives() -> &'static DirectivesMap {
+ DIRECTIVES.get_or_init(|| {
+ let mut need_hexathorpe: HashMap<String, Box<dyn Directive>> = HashMap::new();
+ need_hexathorpe.insert("include".into(), Box::new(Inclusion));
+ let mut need_exclamation: HashMap<String, Box<dyn Directive>> = HashMap::new();
+ need_exclamation.insert("generate".into(), Box::new(Generate(false)));
+ need_exclamation.insert("generate_pod".into(), Box::new(Generate(true)));
+ need_exclamation.insert("generate_ns".into(), Box::new(GenerateNs));
+ need_exclamation.insert("generate_all".into(), Box::new(GenerateAll));
+ need_exclamation.insert("safety".into(), Box::new(Safety));
+ need_exclamation.insert(
+ "pod".into(),
+ Box::new(StringList(
+ |config| &mut config.pod_requests,
+ |config| &config.pod_requests,
+ )),
+ );
+ need_exclamation.insert(
+ "block".into(),
+ Box::new(StringList(
+ |config| &mut config.blocklist,
+ |config| &config.blocklist,
+ )),
+ );
+ need_exclamation.insert(
+ "block_constructors".into(),
+ Box::new(StringList(
+ |config| &mut config.constructor_blocklist,
+ |config| &config.constructor_blocklist,
+ )),
+ );
+ need_exclamation.insert(
+ "instantiable".into(),
+ Box::new(StringList(
+ |config| &mut config.instantiable,
+ |config| &config.instantiable,
+ )),
+ );
+ need_exclamation.insert(
+ "parse_only".into(),
+ Box::new(BoolFlag(
+ |config| &mut config.parse_only,
+ |config| &config.parse_only,
+ )),
+ );
+ need_exclamation.insert(
+ "exclude_impls".into(),
+ Box::new(BoolFlag(
+ |config| &mut config.exclude_impls,
+ |config| &config.exclude_impls,
+ )),
+ );
+ need_exclamation.insert(
+ "exclude_utilities".into(),
+ Box::new(BoolFlag(
+ |config| &mut config.exclude_utilities,
+ |config| &config.exclude_utilities,
+ )),
+ );
+ need_exclamation.insert("name".into(), Box::new(ModName));
+ need_exclamation.insert("concrete".into(), Box::new(Concrete));
+ need_exclamation.insert("rust_type".into(), Box::new(RustType { output: false }));
+ need_exclamation.insert(EXTERN_RUST_TYPE.into(), Box::new(RustType { output: true }));
+ need_exclamation.insert(SUBCLASS.into(), Box::new(Subclass));
+ need_exclamation.insert(EXTERN_RUST_FUN.into(), Box::new(ExternRustFun));
+ need_exclamation.insert(
+ "extern_cpp_type".into(),
+ Box::new(ExternCppType { opaque: false }),
+ );
+ need_exclamation.insert(
+ "extern_cpp_opaque_type".into(),
+ Box::new(ExternCppType { opaque: true }),
+ );
+
+ DirectivesMap {
+ need_hexathorpe,
+ need_exclamation,
+ }
+ })
+}
+
+/// Trait for handling an `include_cpp!` configuration directive.
+pub(crate) trait Directive: Send + Sync {
+ fn parse(
+ &self,
+ args: ParseStream,
+ config: &mut IncludeCppConfig,
+ span: &Span,
+ ) -> ParseResult<()>;
+ fn output<'a>(
+ &self,
+ config: &'a IncludeCppConfig,
+ ) -> Box<dyn Iterator<Item = TokenStream> + 'a>;
+}
+
+struct Inclusion;
+
+impl Directive for Inclusion {
+ fn parse(
+ &self,
+ args: ParseStream,
+ config: &mut IncludeCppConfig,
+ _span: &Span,
+ ) -> ParseResult<()> {
+ let hdr: syn::LitStr = args.parse()?;
+ config.inclusions.push(hdr.value());
+ Ok(())
+ }
+
+ fn output<'a>(
+ &self,
+ config: &'a IncludeCppConfig,
+ ) -> Box<dyn Iterator<Item = TokenStream> + 'a> {
+ Box::new(config.inclusions.iter().map(|val| quote! { #val }))
+ }
+}
+
+/// Directive for either `generate!` (false) or `generate_pod!` (true).
+struct Generate(bool);
+
+impl Directive for Generate {
+ fn parse(
+ &self,
+ args: ParseStream,
+ config: &mut IncludeCppConfig,
+ span: &Span,
+ ) -> ParseResult<()> {
+ let generate: syn::LitStr = args.parse()?;
+ config
+ .allowlist
+ .push(AllowlistEntry::Item(generate.value()))
+ .map_err(|e| allowlist_err_to_syn_err(e, span))?;
+ if self.0 {
+ config.pod_requests.push(generate.value());
+ }
+ Ok(())
+ }
+
+ fn output<'a>(
+ &self,
+ config: &'a IncludeCppConfig,
+ ) -> Box<dyn Iterator<Item = TokenStream> + 'a> {
+ match &config.allowlist {
+ Allowlist::Specific(items) if !self.0 => Box::new(
+ items
+ .iter()
+ .flat_map(|i| match i {
+ AllowlistEntry::Item(s) => Some(s),
+ _ => None,
+ })
+ .map(|s| quote! { #s }),
+ ),
+ Allowlist::Unspecified(_) => panic!("Allowlist mode not yet determined"),
+ _ => Box::new(std::iter::empty()),
+ }
+ }
+}
+
+struct GenerateNs;
+
+impl Directive for GenerateNs {
+ fn parse(
+ &self,
+ args: ParseStream,
+ config: &mut IncludeCppConfig,
+ span: &Span,
+ ) -> ParseResult<()> {
+ let generate: syn::LitStr = args.parse()?;
+ config
+ .allowlist
+ .push(AllowlistEntry::Namespace(generate.value()))
+ .map_err(|e| allowlist_err_to_syn_err(e, span))?;
+ Ok(())
+ }
+
+ fn output<'a>(
+ &self,
+ config: &'a IncludeCppConfig,
+ ) -> Box<dyn Iterator<Item = TokenStream> + 'a> {
+ match &config.allowlist {
+ Allowlist::Specific(items) => Box::new(
+ items
+ .iter()
+ .flat_map(|i| match i {
+ AllowlistEntry::Namespace(s) => Some(s),
+ _ => None,
+ })
+ .map(|s| quote! { #s }),
+ ),
+ Allowlist::Unspecified(_) => panic!("Allowlist mode not yet determined"),
+ _ => Box::new(std::iter::empty()),
+ }
+ }
+}
+
+struct GenerateAll;
+
+impl Directive for GenerateAll {
+ fn parse(
+ &self,
+ _args: ParseStream,
+ config: &mut IncludeCppConfig,
+ span: &Span,
+ ) -> ParseResult<()> {
+ config
+ .allowlist
+ .set_all()
+ .map_err(|e| allowlist_err_to_syn_err(e, span))?;
+ Ok(())
+ }
+
+ fn output<'a>(
+ &self,
+ config: &'a IncludeCppConfig,
+ ) -> Box<dyn Iterator<Item = TokenStream> + 'a> {
+ match &config.allowlist {
+ Allowlist::All => Box::new(std::iter::once(TokenStream::new())),
+ Allowlist::Unspecified(_) => panic!("Allowlist mode not yet determined"),
+ _ => Box::new(std::iter::empty()),
+ }
+ }
+}
+
+struct Safety;
+
+impl Directive for Safety {
+ fn parse(
+ &self,
+ args: ParseStream,
+ config: &mut IncludeCppConfig,
+ _ident_span: &Span,
+ ) -> ParseResult<()> {
+ config.unsafe_policy = args.parse()?;
+ Ok(())
+ }
+
+ fn output<'a>(
+ &self,
+ config: &'a IncludeCppConfig,
+ ) -> Box<dyn Iterator<Item = TokenStream> + 'a> {
+ let policy = &config.unsafe_policy;
+ match config.unsafe_policy {
+ crate::UnsafePolicy::AllFunctionsSafe => {
+ Box::new(std::iter::once(policy.to_token_stream()))
+ }
+ crate::UnsafePolicy::AllFunctionsUnsafe => Box::new(std::iter::empty()),
+ }
+ }
+}
+
+fn allowlist_err_to_syn_err(err: AllowlistErr, span: &Span) -> syn::Error {
+ syn::Error::new(*span, format!("{}", err))
+}
+
+struct StringList<SET, GET>(SET, GET)
+where
+ SET: Fn(&mut IncludeCppConfig) -> &mut Vec<String>,
+ GET: Fn(&IncludeCppConfig) -> &Vec<String>;
+
+impl<SET, GET> Directive for StringList<SET, GET>
+where
+ SET: Fn(&mut IncludeCppConfig) -> &mut Vec<String> + Sync + Send,
+ GET: Fn(&IncludeCppConfig) -> &Vec<String> + Sync + Send,
+{
+ fn parse(
+ &self,
+ args: ParseStream,
+ config: &mut IncludeCppConfig,
+ _ident_span: &Span,
+ ) -> ParseResult<()> {
+ let val: syn::LitStr = args.parse()?;
+ self.0(config).push(val.value());
+ Ok(())
+ }
+
+ fn output<'a>(
+ &self,
+ config: &'a IncludeCppConfig,
+ ) -> Box<dyn Iterator<Item = TokenStream> + 'a> {
+ Box::new(self.1(config).iter().map(|val| {
+ quote! {
+ #val
+ }
+ }))
+ }
+}
+
+struct BoolFlag<SET, GET>(SET, GET)
+where
+ SET: Fn(&mut IncludeCppConfig) -> &mut bool,
+ GET: Fn(&IncludeCppConfig) -> &bool;
+
+impl<SET, GET> Directive for BoolFlag<SET, GET>
+where
+ SET: Fn(&mut IncludeCppConfig) -> &mut bool + Sync + Send,
+ GET: Fn(&IncludeCppConfig) -> &bool + Sync + Send,
+{
+ fn parse(
+ &self,
+ _args: ParseStream,
+ config: &mut IncludeCppConfig,
+ _ident_span: &Span,
+ ) -> ParseResult<()> {
+ *self.0(config) = true;
+ Ok(())
+ }
+
+ fn output<'a>(
+ &self,
+ config: &'a IncludeCppConfig,
+ ) -> Box<dyn Iterator<Item = TokenStream> + 'a> {
+ if *self.1(config) {
+ Box::new(std::iter::once(quote! {}))
+ } else {
+ Box::new(std::iter::empty())
+ }
+ }
+}
+
+struct ModName;
+
+impl Directive for ModName {
+ fn parse(
+ &self,
+ args: ParseStream,
+ config: &mut IncludeCppConfig,
+ _ident_span: &Span,
+ ) -> ParseResult<()> {
+ let id: Ident = args.parse()?;
+ config.mod_name = Some(id);
+ Ok(())
+ }
+
+ fn output<'a>(
+ &self,
+ config: &'a IncludeCppConfig,
+ ) -> Box<dyn Iterator<Item = TokenStream> + 'a> {
+ match &config.mod_name {
+ None => Box::new(std::iter::empty()),
+ Some(id) => Box::new(std::iter::once(quote! { #id })),
+ }
+ }
+}
+
+struct Concrete;
+
+impl Directive for Concrete {
+ fn parse(
+ &self,
+ args: ParseStream,
+ config: &mut IncludeCppConfig,
+ _ident_span: &Span,
+ ) -> ParseResult<()> {
+ let definition: syn::LitStr = args.parse()?;
+ args.parse::<syn::token::Comma>()?;
+ let rust_id: syn::Ident = args.parse()?;
+ config.concretes.0.insert(definition.value(), rust_id);
+ Ok(())
+ }
+
+ fn output<'a>(
+ &self,
+ config: &'a IncludeCppConfig,
+ ) -> Box<dyn Iterator<Item = TokenStream> + 'a> {
+ Box::new(config.concretes.0.iter().map(|(k, v)| {
+ quote! {
+ #k,#v
+ }
+ }))
+ }
+}
+
+struct RustType {
+ output: bool,
+}
+
+impl Directive for RustType {
+ fn parse(
+ &self,
+ args: ParseStream,
+ config: &mut IncludeCppConfig,
+ _ident_span: &Span,
+ ) -> ParseResult<()> {
+ let id: Ident = args.parse()?;
+ config.rust_types.push(RustPath::new_from_ident(id));
+ Ok(())
+ }
+
+ fn output<'a>(
+ &self,
+ config: &'a IncludeCppConfig,
+ ) -> Box<dyn Iterator<Item = TokenStream> + 'a> {
+ if self.output {
+ Box::new(config.rust_types.iter().map(|rp| rp.to_token_stream()))
+ } else {
+ Box::new(std::iter::empty())
+ }
+ }
+}
+
+struct Subclass;
+
+impl Directive for Subclass {
+ fn parse(
+ &self,
+ args: ParseStream,
+ config: &mut IncludeCppConfig,
+ _ident_span: &Span,
+ ) -> ParseResult<()> {
+ let superclass: syn::LitStr = args.parse()?;
+ args.parse::<syn::token::Comma>()?;
+ let subclass: syn::Ident = args.parse()?;
+ config.subclasses.push(crate::config::Subclass {
+ superclass: superclass.value(),
+ subclass,
+ });
+ Ok(())
+ }
+
+ fn output<'a>(
+ &self,
+ config: &'a IncludeCppConfig,
+ ) -> Box<dyn Iterator<Item = TokenStream> + 'a> {
+ Box::new(config.subclasses.iter().map(|sc| {
+ let superclass = &sc.superclass;
+ let subclass = &sc.subclass;
+ quote! {
+ #superclass,#subclass
+ }
+ }))
+ }
+}
+
+struct ExternRustFun;
+
+impl Directive for ExternRustFun {
+ fn parse(
+ &self,
+ args: ParseStream,
+ config: &mut IncludeCppConfig,
+ _ident_span: &Span,
+ ) -> ParseResult<()> {
+ let path: RustPath = args.parse()?;
+ args.parse::<syn::token::Comma>()?;
+ let sig: syn::Signature = args.parse()?;
+ config.extern_rust_funs.push(RustFun {
+ path,
+ sig,
+ receiver: None,
+ });
+ Ok(())
+ }
+
+ fn output<'a>(
+ &self,
+ config: &'a IncludeCppConfig,
+ ) -> Box<dyn Iterator<Item = TokenStream> + 'a> {
+ Box::new(config.extern_rust_funs.iter().map(|erf| {
+ let p = &erf.path;
+ let s = &erf.sig;
+ quote! { #p,#s }
+ }))
+ }
+}
+
+struct ExternCppType {
+ opaque: bool,
+}
+
+impl Directive for ExternCppType {
+ fn parse(
+ &self,
+ args: ParseStream,
+ config: &mut IncludeCppConfig,
+ _ident_span: &Span,
+ ) -> ParseResult<()> {
+ let definition: syn::LitStr = args.parse()?;
+ args.parse::<syn::token::Comma>()?;
+ let rust_path: syn::TypePath = args.parse()?;
+ config.externs.0.insert(
+ definition.value(),
+ crate::config::ExternCppType {
+ rust_path,
+ opaque: self.opaque,
+ },
+ );
+ Ok(())
+ }
+
+ fn output<'a>(
+ &self,
+ config: &'a IncludeCppConfig,
+ ) -> Box<dyn Iterator<Item = TokenStream> + 'a> {
+ let opaque_needed = self.opaque;
+ Box::new(
+ config
+ .externs
+ .0
+ .iter()
+ .filter_map(move |(definition, details)| {
+ if details.opaque == opaque_needed {
+ let rust_path = &details.rust_path;
+ Some(quote! {
+ #definition, #rust_path
+ })
+ } else {
+ None
+ }
+ }),
+ )
+ }
+}