[bindings] Figure out in-file structs and enums before processing
authorMatt Corallo <git@bluematt.me>
Mon, 4 Jan 2021 20:52:18 +0000 (15:52 -0500)
committerMatt Corallo <git@bluematt.me>
Tue, 2 Feb 2021 22:04:31 +0000 (17:04 -0500)
Previously, types which were declared and used in the same file
would fail if the use was before the declaration. This makes sense
in a few cases where a "parent" class returns a reference to a
"child" class and there's no reason we shouldn't support it.

This change adds a second pass to our file processing which gathers
the structs and enums whicha re declared in the file and adds them
to the type resolver first, before doing the real conversion.

c-bindings-gen/src/main.rs

index 6d6296c8f48e93dc661924cf08c239694c74896b..746952e14200da595dcbbe00b663210754396750 100644 (file)
@@ -500,21 +500,28 @@ fn writeln_opaque<W: std::io::Write>(w: &mut W, ident: &syn::Ident, struct_name:
        write_cpp_wrapper(cpp_headers, &format!("{}", ident), true);
 }
 
-/// Writes out all the relevant mappings for a Rust struct, deferring to writeln_opaque to generate
-/// the struct itself, and then writing getters and setters for public, understood-type fields and
-/// a constructor if every field is public.
-fn writeln_struct<'a, 'b, W: std::io::Write>(w: &mut W, s: &'a syn::ItemStruct, types: &mut TypeResolver<'b, 'a>, extra_headers: &mut File, cpp_headers: &mut File) {
-       let struct_name = &format!("{}", s.ident);
+fn declare_struct<'a, 'b>(s: &'a syn::ItemStruct, types: &mut TypeResolver<'b, 'a>) -> bool {
        let export = export_status(&s.attrs);
        match export {
                ExportStatus::Export => {},
-               ExportStatus::TestOnly => return,
+               ExportStatus::TestOnly => return false,
                ExportStatus::NoExport => {
                        types.struct_ignored(&s.ident);
-                       return;
+                       return false;
                }
        }
 
+       types.struct_imported(&s.ident, format!("{}", s.ident));
+       true
+}
+
+/// Writes out all the relevant mappings for a Rust struct, deferring to writeln_opaque to generate
+/// the struct itself, and then writing getters and setters for public, understood-type fields and
+/// a constructor if every field is public.
+fn writeln_struct<'a, 'b, W: std::io::Write>(w: &mut W, s: &'a syn::ItemStruct, types: &mut TypeResolver<'b, 'a>, extra_headers: &mut File, cpp_headers: &mut File) {
+       if !declare_struct(s, types) { return; }
+
+       let struct_name = &format!("{}", s.ident);
        writeln_opaque(w, &s.ident, struct_name, &s.generics, &s.attrs, types, extra_headers, cpp_headers);
 
        eprintln!("exporting fields for {}", struct_name);
@@ -598,8 +605,6 @@ fn writeln_struct<'a, 'b, W: std::io::Write>(w: &mut W, s: &'a syn::ItemStruct,
                        writeln!(w, "\t}})), is_owned: true }}\n}}").unwrap();
                }
        }
-
-       types.struct_imported(&s.ident, struct_name.clone());
 }
 
 /// Prints a relevant conversion for impl *
@@ -916,6 +921,19 @@ fn is_enum_opaque(e: &syn::ItemEnum) -> bool {
        false
 }
 
+fn declare_enum<'a, 'b>(e: &'a syn::ItemEnum, types: &mut TypeResolver<'b, 'a>) {
+       match export_status(&e.attrs) {
+               ExportStatus::Export => {},
+               ExportStatus::NoExport|ExportStatus::TestOnly => return,
+       }
+
+       if is_enum_opaque(e) {
+               types.enum_ignored(&e.ident);
+       } else {
+               types.mirrored_enum_declared(&e.ident);
+       }
+}
+
 /// Print a mapping of an enum. If all of the enum's fields are C-mapped in some form (or the enum
 /// is unitary), we generate an equivalent enum with all types replaced with their C mapped
 /// versions followed by conversion functions which map between the Rust version and the C mapped
@@ -929,7 +947,6 @@ fn writeln_enum<'a, 'b, W: std::io::Write>(w: &mut W, e: &'a syn::ItemEnum, type
        if is_enum_opaque(e) {
                eprintln!("Skipping enum {} as it contains non-unit fields", e.ident);
                writeln_opaque(w, &e.ident, &format!("{}", e.ident), &e.generics, &e.attrs, types, extra_headers, cpp_headers);
-               types.enum_ignored(&e.ident);
                return;
        }
        writeln_docs(w, &e.attrs, "");
@@ -937,7 +954,6 @@ fn writeln_enum<'a, 'b, W: std::io::Write>(w: &mut W, e: &'a syn::ItemEnum, type
        if e.generics.lt_token.is_some() {
                unimplemented!();
        }
-       types.mirrored_enum_declared(&e.ident);
 
        let mut needs_free = false;
 
@@ -1166,9 +1182,27 @@ fn convert_file<'a, 'b>(libast: &'a FullLibraryAST, crate_types: &mut CrateTypes
 
        let mut type_resolver = TypeResolver::new(orig_crate, module, crate_types);
 
+       // First pass over the items and fill in imports and file-declared objects in the type resolver
        for item in syntax.items.iter() {
                match item {
                        syn::Item::Use(u) => type_resolver.process_use(&mut out, &u),
+                       syn::Item::Struct(s) => {
+                               if let syn::Visibility::Public(_) = s.vis {
+                                       declare_struct(&s, &mut type_resolver);
+                               }
+                       },
+                       syn::Item::Enum(e) => {
+                               if let syn::Visibility::Public(_) = e.vis {
+                                       declare_enum(&e, &mut type_resolver);
+                               }
+                       },
+                       _ => {},
+               }
+       }
+
+       for item in syntax.items.iter() {
+               match item {
+                       syn::Item::Use(_) => {}, // Handled above
                        syn::Item::Static(_) => {},
                        syn::Item::Enum(e) => {
                                if let syn::Visibility::Public(_) = e.vis {