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
+                    }
+                }),
+        )
+    }
+}