Repository: tokio-rs/prost (language: rust)

Issue:
prost-build define order in type attributes
Hi,

I'm using prost-build with a custom type attribute,
`prost_build.type_attribute(".", "#[pyclass]");`
but when I run it, I get:
`error: macro attributes must be placed before #[derive]`
and when I see the output I get:
```
#[derive(Clone, PartialEq, ::prost::Message)]
#[pyclass]
pub struct ...
```
Is there a way to establish the order of the attributes?

Thanks!


Discussion / hints:
Interesting, I wasn't aware of this requirement.  There's currently no way to define the ordering.  It sounds like `prost-build` should be sorting the type attributes, with `derive`s last, then applying them above the built-in derives.  This is where it happens: https://github.com/danburkert/prost/blob/v0.6.1/prost-build/src/code_generator.rs#L255-L265

Fix patch:
diff --git a/prost-build/src/code_generator.rs b/prost-build/src/code_generator.rs
index ebf18a8a7..7323eb613 100644
--- a/prost-build/src/code_generator.rs
+++ b/prost-build/src/code_generator.rs
@@ -171,10 +171,10 @@ impl<'a> CodeGenerator<'a> {
         assert_eq!(oneof_fields.len(), message.oneof_decl.len());
 
         self.append_doc();
+        self.append_type_attributes(&fq_message_name);
         self.push_indent();
         self.buf
             .push_str("#[derive(Clone, PartialEq, ::prost::Message)]\n");
-        self.append_type_attributes(&fq_message_name);
         self.push_indent();
         self.buf.push_str("pub struct ");
         self.buf.push_str(&to_upper_camel(&message_name));
@@ -483,11 +483,11 @@ impl<'a> CodeGenerator<'a> {
         self.path.pop();
         self.path.pop();
 
+        let oneof_name = format!("{}.{}", msg_name, oneof.name());
+        self.append_type_attributes(&oneof_name);
         self.push_indent();
         self.buf
             .push_str("#[derive(Clone, PartialEq, ::prost::Oneof)]\n");
-        let oneof_name = format!("{}.{}", msg_name, oneof.name());
-        self.append_type_attributes(&oneof_name);
         self.push_indent();
         self.buf.push_str("pub enum ");
         self.buf.push_str(&to_upper_camel(oneof.name()));
@@ -568,13 +568,13 @@ impl<'a> CodeGenerator<'a> {
         }
 
         self.append_doc();
+        self.append_type_attributes(&fq_enum_name);
         self.push_indent();
         self.buf.push_str(
             "#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]\n",
         );
         self.push_indent();
         self.buf.push_str("#[repr(i32)]\n");
-        self.append_type_attributes(&fq_enum_name);
         self.push_indent();
         self.buf.push_str("pub enum ");
         self.buf.push_str(&to_upper_camel(desc.name()));
@@ -689,6 +689,11 @@ impl<'a> CodeGenerator<'a> {
     }
 
     fn push_mod(&mut self, module: &str) {
+        self.push_indent();
+        self.buf.push_str("/// Nested message and enum types in `");
+        self.buf.push_str(module);
+        self.buf.push_str("`.\n");
+
         self.push_indent();
         self.buf.push_str("pub mod ");
         self.buf.push_str(&to_snake(module));
diff --git a/prost-build/src/lib.rs b/prost-build/src/lib.rs
index 733e31d68..31d919ed7 100644
--- a/prost-build/src/lib.rs
+++ b/prost-build/src/lib.rs
@@ -115,6 +115,7 @@ mod message_graph;
 use std::collections::HashMap;
 use std::default;
 use std::env;
+use std::fmt;
 use std::fs;
 use std::io::{Error, ErrorKind, Result};
 use std::path::{Path, PathBuf};
@@ -630,6 +631,20 @@ impl default::Default for Config {
     }
 }
 
+impl fmt::Debug for Config {
+    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+        fmt.debug_struct("Config")
+            .field("btree_map", &self.btree_map)
+            .field("type_attributes", &self.type_attributes)
+            .field("field_attributes", &self.field_attributes)
+            .field("prost_types", &self.prost_types)
+            .field("strip_enum_prefix", &self.strip_enum_prefix)
+            .field("out_dir", &self.out_dir)
+            .field("extern_paths", &self.extern_paths)
+            .finish()
+    }
+}
+
 /// Compile `.proto` files into Rust files during a Cargo build.
 ///
 /// The generated `.rs` files are written to the Cargo `OUT_DIR` directory, suitable for use with
diff --git a/prost-types/src/compiler.rs b/prost-types/src/compiler.rs
index 02e06ab08..4a4af06db 100644
--- a/prost-types/src/compiler.rs
+++ b/prost-types/src/compiler.rs
@@ -59,6 +59,7 @@ pub struct CodeGeneratorResponse {
     #[prost(message, repeated, tag="15")]
     pub file: ::prost::alloc::vec::Vec<code_generator_response::File>,
 }
+/// Nested message and enum types in `CodeGeneratorResponse`.
 pub mod code_generator_response {
     /// Represents a single generated file.
     #[derive(Clone, PartialEq, ::prost::Message)]
diff --git a/prost-types/src/protobuf.rs b/prost-types/src/protobuf.rs
index 65d4e6e57..6649f7634 100644
--- a/prost-types/src/protobuf.rs
+++ b/prost-types/src/protobuf.rs
@@ -72,6 +72,7 @@ pub struct DescriptorProto {
     #[prost(string, repeated, tag="10")]
     pub reserved_name: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
 }
+/// Nested message and enum types in `DescriptorProto`.
 pub mod descriptor_proto {
     #[derive(Clone, PartialEq, ::prost::Message)]
     pub struct ExtensionRange {
@@ -147,6 +148,7 @@ pub struct FieldDescriptorProto {
     #[prost(message, optional, tag="8")]
     pub options: ::core::option::Option<FieldOptions>,
 }
+/// Nested message and enum types in `FieldDescriptorProto`.
 pub mod field_descriptor_proto {
     #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
     #[repr(i32)]
@@ -220,6 +222,7 @@ pub struct EnumDescriptorProto {
     #[prost(string, repeated, tag="5")]
     pub reserved_name: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
 }
+/// Nested message and enum types in `EnumDescriptorProto`.
 pub mod enum_descriptor_proto {
     /// Range of reserved numeric values. Reserved values may not be used by
     /// entries in the same enum. Reserved ranges may not overlap.
@@ -418,6 +421,7 @@ pub struct FileOptions {
     #[prost(message, repeated, tag="999")]
     pub uninterpreted_option: ::prost::alloc::vec::Vec<UninterpretedOption>,
 }
+/// Nested message and enum types in `FileOptions`.
 pub mod file_options {
     /// Generated classes can be optimized for speed or code size.
     #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
@@ -564,6 +568,7 @@ pub struct FieldOptions {
     #[prost(message, repeated, tag="999")]
     pub uninterpreted_option: ::prost::alloc::vec::Vec<UninterpretedOption>,
 }
+/// Nested message and enum types in `FieldOptions`.
 pub mod field_options {
     #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
     #[repr(i32)]
@@ -654,6 +659,7 @@ pub struct MethodOptions {
     #[prost(message, repeated, tag="999")]
     pub uninterpreted_option: ::prost::alloc::vec::Vec<UninterpretedOption>,
 }
+/// Nested message and enum types in `MethodOptions`.
 pub mod method_options {
     /// Is this method side-effect-free (or safe in HTTP parlance), or idempotent,
     /// or neither? HTTP based RPC implementation may choose GET verb for safe
@@ -693,6 +699,7 @@ pub struct UninterpretedOption {
     #[prost(string, optional, tag="8")]
     pub aggregate_value: ::core::option::Option<::prost::alloc::string::String>,
 }
+/// Nested message and enum types in `UninterpretedOption`.
 pub mod uninterpreted_option {
     /// The name of the uninterpreted option.  Each string represents a segment in
     /// a dot-separated name.  is_extension is true iff a segment represents an
@@ -760,6 +767,7 @@ pub struct SourceCodeInfo {
     #[prost(message, repeated, tag="1")]
     pub location: ::prost::alloc::vec::Vec<source_code_info::Location>,
 }
+/// Nested message and enum types in `SourceCodeInfo`.
 pub mod source_code_info {
     #[derive(Clone, PartialEq, ::prost::Message)]
     pub struct Location {
@@ -860,6 +868,7 @@ pub struct GeneratedCodeInfo {
     #[prost(message, repeated, tag="1")]
     pub annotation: ::prost::alloc::vec::Vec<generated_code_info::Annotation>,
 }
+/// Nested message and enum types in `GeneratedCodeInfo`.
 pub mod generated_code_info {
     #[derive(Clone, PartialEq, ::prost::Message)]
     pub struct Annotation {
@@ -1064,6 +1073,7 @@ pub struct Field {
     #[prost(string, tag="11")]
     pub default_value: ::prost::alloc::string::String,
 }
+/// Nested message and enum types in `Field`.
 pub mod field {
     /// Basic field types.
     #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
@@ -1655,6 +1665,7 @@ pub struct Value {
     #[prost(oneof="value::Kind", tags="1, 2, 3, 4, 5, 6")]
     pub kind: ::core::option::Option<value::Kind>,
 }
+/// Nested message and enum types in `Value`.
 pub mod value {
     /// The kind of value.
     #[derive(Clone, PartialEq, ::prost::Oneof)]


Test patch: