Skip to content

Commit 6f3c60f

Browse files
hockeybuggydanburkert
authored andcommitted
Add support for deprecated attributes on fields.
Protobufs support `deprecated` field options. These field options allow the field to continue to be supported, but allow for language specific code generators to include that the field has been deprecated in the generated code. As discussed in https://github.com/danburkert/prost/issues/221 we should consider supporting this in `prost`. This commit adds support for this feature in `prost-build` by checking the field options, and if present emitting a `#[deprecated]` attribute. One short coming of this change is that it is difficult to test that the `deprecated` attribute is correctly added with an assertion since the attribute is not accessible at runtime.
1 parent 9ec3614 commit 6f3c60f

File tree

6 files changed

+60
-1
lines changed

6 files changed

+60
-1
lines changed

prost-build/src/code_generator.rs

+17-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ use prost_types::field_descriptor_proto::{Label, Type};
1010
use prost_types::source_code_info::Location;
1111
use prost_types::{
1212
DescriptorProto, EnumDescriptorProto, EnumValueDescriptorProto, FieldDescriptorProto,
13-
FileDescriptorProto, OneofDescriptorProto, ServiceDescriptorProto, SourceCodeInfo,
13+
FieldOptions, FileDescriptorProto, OneofDescriptorProto, ServiceDescriptorProto,
14+
SourceCodeInfo,
1415
};
1516

1617
use crate::ast::{Comments, Method, Service};
@@ -279,6 +280,7 @@ impl<'a> CodeGenerator<'a> {
279280
fn append_field(&mut self, msg_name: &str, field: FieldDescriptorProto) {
280281
let type_ = field.r#type();
281282
let repeated = field.label == Some(Label::Repeated as i32);
283+
let deprecated = self.deprecated(&field);
282284
let optional = self.optional(&field);
283285
let ty = self.resolve_type(&field);
284286

@@ -294,6 +296,12 @@ impl<'a> CodeGenerator<'a> {
294296
);
295297

296298
self.append_doc();
299+
300+
if deprecated {
301+
self.push_indent();
302+
self.buf.push_str("#[deprecated]\n");
303+
}
304+
297305
self.push_indent();
298306
self.buf.push_str("#[prost(");
299307
let type_tag = self.field_type_tag(&field);
@@ -792,6 +800,14 @@ impl<'a> CodeGenerator<'a> {
792800
_ => self.syntax == Syntax::Proto2,
793801
}
794802
}
803+
804+
/// Returns `true` if the field options includes the `deprecated` option.
805+
fn deprecated(&self, field: &FieldDescriptorProto) -> bool {
806+
field
807+
.options
808+
.as_ref()
809+
.map_or(false, FieldOptions::deprecated)
810+
}
795811
}
796812

797813
/// Returns `true` if the repeated field type can be packed.

prost-types/src/protobuf.rs

+1
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,7 @@ pub struct FileOptions {
333333
#[prost(bool, optional, tag="10", default="false")]
334334
pub java_multiple_files: ::std::option::Option<bool>,
335335
/// This option does nothing.
336+
#[deprecated]
336337
#[prost(bool, optional, tag="20")]
337338
pub java_generate_equals_and_hash: ::std::option::Option<bool>,
338339
/// If set true, then the Java2 code generator will generate code that

tests/src/build.rs

+4
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ fn main() {
7777
.compile_protos(&[src.join("groups.proto")], includes)
7878
.unwrap();
7979

80+
config
81+
.compile_protos(&[src.join("deprecated_field.proto")], includes)
82+
.unwrap();
83+
8084
config
8185
.compile_protos(&[src.join("well_known_types.proto")], includes)
8286
.unwrap();

tests/src/deprecated_field.proto

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
syntax = "proto3";
2+
3+
package deprecated_field;
4+
5+
message Test {
6+
string not_outdated = 1;
7+
string outdated = 2 [deprecated = true];
8+
}

tests/src/deprecated_field.rs

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
mod deprecated_field {
2+
// #![deny(unused_results)]
3+
include!(concat!(env!("OUT_DIR"), "/deprecated_field.rs"));
4+
}
5+
6+
#[test]
7+
fn test_warns_when_using_fields_with_deprecated_field() {
8+
#[allow(deprecated)]
9+
deprecated_field::Test {
10+
not_outdated: ".ogg".to_string(),
11+
outdated: ".wav".to_string(),
12+
};
13+
// This test relies on the `#[allow(deprecated)]` attribute to ignore the warning that should
14+
// be raised by the compiler.
15+
// This test has a shortcoming since it doesn't explicitly check for the presence of the
16+
// `deprecated` attribute since it doesn't exist at runtime. If complied without the `allow`
17+
// attribute the following warning would be raised:
18+
//
19+
// warning: use of deprecated item 'deprecated_field::deprecated_field::Test::outdated'
20+
// --> tests/src/deprecated_field.rs:11:9
21+
// |
22+
// 11 | outdated: ".wav".to_string(),
23+
// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
24+
// |
25+
// = note: `#[warn(deprecated)]` on by default
26+
27+
assert!(true);
28+
}

tests/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ mod bootstrap;
2323
#[cfg(test)]
2424
mod debug;
2525
#[cfg(test)]
26+
mod deprecated_field;
27+
#[cfg(test)]
2628
mod message_encoding;
2729
#[cfg(test)]
2830
mod no_unused_results;

0 commit comments

Comments
 (0)