Skip to content

Commit 0fb5d07

Browse files
sevenc-nanashiemilio
authored andcommitted
Add support for #[deprecated].
Closes #875. Closes #860. Closes #408.
1 parent d8355da commit 0fb5d07

21 files changed

+769
-2
lines changed

docs.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,23 @@ args = "horizontal"
680680
# default: nothing is emitted for must_use functions
681681
must_use = "MUST_USE_FUNC"
682682

683+
# An optional string that should prefix function declarations which have been
684+
# marked as `#[deprecated]` without note. For instance, "__attribute__((deprecated))"
685+
# would be a reasonable value if targeting gcc/clang. A more portable solution
686+
# would involve emitting the name of a macro which you define in a
687+
# platform-specific way. e.g. "DEPRECATED_FUNC"
688+
# default: nothing is emitted for deprecated functions
689+
deprecated = "DEPRECATED_FUNC"
690+
691+
# An optional string that should prefix function declarations which have been
692+
# marked as `#[deprecated(note = "reason")]`. `{}` will be replaced with the
693+
# double-quoted string. For instance, "__attribute__((deprecated({})))"
694+
# would be a reasonable value if targeting gcc/clang. A more portable solution
695+
# would involve emitting the name of a macro which you define in a
696+
# platform-specific way. e.g. "DEPRECATED_FUNC_WITH_NOTE(note)"
697+
# default: nothing is emitted for deprecated functions
698+
deprecated_with_notes = "DEPRECATED_FUNC_WITH_NOTE"
699+
683700
# An optional string that will be used in the attribute position for functions
684701
# that don't return (that return `!` in Rust).
685702
#
@@ -752,6 +769,23 @@ rename_fields = "PascalCase"
752769
# default: nothing is emitted for must_use structs
753770
must_use = "MUST_USE_STRUCT"
754771

772+
# An optional string that should come before the name of any struct which has been
773+
# marked as `#[deprecated]` without note. For instance, "__attribute__((deprecated))"
774+
# would be a reasonable value if targeting gcc/clang. A more portable solution
775+
# would involve emitting the name of a macro which you define in a
776+
# platform-specific way. e.g. "DEPRECATED_STRUCT"
777+
# default: nothing is emitted for deprecated structs
778+
deprecated = "DEPRECATED_STRUCT"
779+
780+
# An optional string that should come before the name of any struct which has been
781+
# marked as `#[deprecated(note = "reason")]`. `{}` will be replaced with the
782+
# double-quoted string. For instance, "__attribute__((deprecated({})))"
783+
# would be a reasonable value if targeting gcc/clang. A more portable solution
784+
# would involve emitting the name of a macro which you define in a
785+
# platform-specific way. e.g. "DEPRECATED_STRUCT_WITH_NOTE(note)"
786+
# default: nothing is emitted for deprecated structs
787+
deprecated_with_notes = "DEPRECATED_STRUCT_WITH_NOTE"
788+
755789
# Whether a Rust type with associated consts should emit those consts inside the
756790
# type's body. Otherwise they will be emitted trailing and with the type's name
757791
# prefixed. This does nothing if the target is C, or if
@@ -865,6 +899,23 @@ cast_assert_name = "MOZ_RELEASE_ASSERT"
865899
# default: nothing is emitted for must_use enums
866900
must_use = "MUST_USE_ENUM"
867901

902+
# An optional string that should come before the name of any enum which has been
903+
# marked as `#[deprecated]` without note. For instance, "__attribute__((deprecated))"
904+
# would be a reasonable value if targeting gcc/clang. A more portable solution
905+
# would involve emitting the name of a macro which you define in a
906+
# platform-specific way. e.g. "DEPRECATED_ENUM"
907+
# default: nothing is emitted for deprecated enums
908+
deprecated = "DEPRECATED_ENUM"
909+
910+
# An optional string that should come before the name of any enum which has been
911+
# marked as `#[deprecated(note = "reason")]`. `{}` will be replaced with the
912+
# double-quoted string. For instance, "__attribute__((deprecated({})))"
913+
# would be a reasonable value if targeting gcc/clang. A more portable solution
914+
# would involve emitting the name of a macro which you define in a
915+
# platform-specific way. e.g. "DEPRECATED_ENUM_WITH_NOTE(note)"
916+
# default: nothing is emitted for deprecated enums
917+
deprecated_with_notes = "DEPRECATED_ENUM_WITH_NOTE"
918+
868919
# Whether enums with fields should generate destructors. This exists so that generic
869920
# enums can be properly instantiated with payloads that are C++ types with
870921
# destructors. This isn't necessary for structs because C++ has rules to

src/bindgen/cdecl.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ struct CDecl {
4040
type_generic_args: Vec<GenericArgument>,
4141
declarators: Vec<CDeclarator>,
4242
type_ctype: Option<DeclarationType>,
43+
deprecated: Option<String>,
4344
}
4445

4546
impl CDecl {
@@ -50,6 +51,7 @@ impl CDecl {
5051
type_generic_args: Vec::new(),
5152
declarators: Vec::new(),
5253
type_ctype: None,
54+
deprecated: None,
5355
}
5456
}
5557

@@ -99,6 +101,7 @@ impl CDecl {
99101
layout,
100102
never_return: f.never_return,
101103
});
104+
self.deprecated = f.annotations.deprecated.clone();
102105
self.build_type(&f.ret, false, config);
103106
}
104107

src/bindgen/config.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,10 @@ pub struct FunctionConfig {
425425
pub postfix: Option<String>,
426426
/// The way to annotation this function as #[must_use]
427427
pub must_use: Option<String>,
428+
/// The way to annotation this function as #[deprecated] without notes
429+
pub deprecated: Option<String>,
430+
/// The way to annotation this function as #[deprecated] with notes
431+
pub deprecated_with_note: Option<String>,
428432
/// The style to layout the args
429433
pub args: Layout,
430434
/// The rename rule to apply to function args
@@ -443,6 +447,8 @@ impl Default for FunctionConfig {
443447
prefix: None,
444448
postfix: None,
445449
must_use: None,
450+
deprecated: None,
451+
deprecated_with_note: None,
446452
args: Layout::Auto,
447453
rename_args: RenameRule::None,
448454
swift_name_macro: None,
@@ -498,6 +504,10 @@ pub struct StructConfig {
498504
pub associated_constants_in_body: bool,
499505
/// The way to annotate this struct as #[must_use].
500506
pub must_use: Option<String>,
507+
/// The way to annotation this function as #[deprecated] without notes
508+
pub deprecated: Option<String>,
509+
/// The way to annotation this function as #[deprecated] with notes
510+
pub deprecated_with_note: Option<String>,
501511
}
502512

503513
impl StructConfig {
@@ -581,6 +591,10 @@ pub struct EnumConfig {
581591
pub cast_assert_name: Option<String>,
582592
/// The way to annotation this enum as #[must_use].
583593
pub must_use: Option<String>,
594+
/// The way to annotation this function as #[deprecated] without notes
595+
pub deprecated: Option<String>,
596+
/// The way to annotation this function as #[deprecated] with notes
597+
pub deprecated_with_note: Option<String>,
584598
/// Whether to generate destructors of tagged enums.
585599
pub derive_tagged_enum_destructor: bool,
586600
/// Whether to generate copy-constructors of tagged enums.
@@ -612,6 +626,8 @@ impl Default for EnumConfig {
612626
derive_mut_casts: false,
613627
cast_assert_name: None,
614628
must_use: None,
629+
deprecated: None,
630+
deprecated_with_note: None,
615631
derive_tagged_enum_destructor: false,
616632
derive_tagged_enum_copy_constructor: false,
617633
derive_tagged_enum_copy_assignment: false,

src/bindgen/ir/annotation.rs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,15 @@ pub enum AnnotationValue {
3535
pub struct AnnotationSet {
3636
annotations: HashMap<String, AnnotationValue>,
3737
pub must_use: bool,
38+
pub deprecated: Option<String>,
3839
}
3940

4041
impl AnnotationSet {
4142
pub fn new() -> AnnotationSet {
4243
AnnotationSet {
4344
annotations: HashMap::new(),
4445
must_use: false,
46+
deprecated: None,
4547
}
4648
}
4749

@@ -53,6 +55,27 @@ impl AnnotationSet {
5355
self.must_use && config.language != Language::Cython
5456
}
5557

58+
pub(crate) fn deprecated_note(&self, config: &Config) -> Option<&str> {
59+
if config.language == Language::Cython {
60+
return None;
61+
}
62+
63+
self.deprecated.as_deref()
64+
}
65+
66+
pub(crate) fn format_deprecated_note(
67+
&self,
68+
format_without_note: Option<&str>,
69+
format_with_note: Option<&str>,
70+
note: &str,
71+
) -> Option<String> {
72+
if note.is_empty() {
73+
return format_without_note.map(|x| x.to_string());
74+
}
75+
format_with_note
76+
.map(|format_with_note| format_with_note.replace("{}", format!("{:?}", note).as_str()))
77+
}
78+
5679
pub fn load(attrs: &[syn::Attribute]) -> Result<AnnotationSet, String> {
5780
let lines = attrs.get_comment_lines();
5881
let lines: Vec<&str> = lines
@@ -68,7 +91,7 @@ impl AnnotationSet {
6891
.collect();
6992

7093
let must_use = attrs.has_attr_word("must_use");
71-
94+
let deprecated = attrs.find_deprecated_note();
7295
let mut annotations = HashMap::new();
7396

7497
// Look at each line for an annotation
@@ -118,6 +141,7 @@ impl AnnotationSet {
118141
Ok(AnnotationSet {
119142
annotations,
120143
must_use,
144+
deprecated,
121145
})
122146
}
123147

src/bindgen/ir/enumeration.rs

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -753,7 +753,17 @@ impl Enum {
753753
if let Some(prim) = size {
754754
// If we need to specify size, then we have no choice but to create a typedef,
755755
// so `config.style` is not respected.
756-
write!(out, "enum {}", tag_name);
756+
write!(out, "enum");
757+
if let Some(note) = self.annotations.deprecated_note(config) {
758+
if let Some(note) = self.annotations.format_deprecated_note(
759+
config.enumeration.deprecated.as_deref(),
760+
config.enumeration.deprecated_with_note.as_deref(),
761+
note,
762+
) {
763+
write!(out, " {}", note);
764+
}
765+
}
766+
write!(out, " {}", tag_name);
757767

758768
if config.cpp_compatible_c() {
759769
out.new_line();
@@ -769,6 +779,15 @@ impl Enum {
769779
out.write("typedef ");
770780
}
771781
out.write("enum");
782+
if let Some(note) = self.annotations.deprecated_note(config) {
783+
if let Some(note) = self.annotations.format_deprecated_note(
784+
config.enumeration.deprecated.as_deref(),
785+
config.enumeration.deprecated_with_note.as_deref(),
786+
note,
787+
) {
788+
write!(out, " {}", note);
789+
}
790+
}
772791
if config.style.generate_tag() {
773792
write!(out, " {}", tag_name);
774793
}
@@ -787,6 +806,16 @@ impl Enum {
787806
}
788807
}
789808

809+
if let Some(note) = self.annotations.deprecated_note(config) {
810+
if let Some(note) = self.annotations.format_deprecated_note(
811+
config.structure.deprecated.as_deref(),
812+
config.structure.deprecated_with_note.as_deref(),
813+
note,
814+
) {
815+
write!(out, " {}", note);
816+
}
817+
}
818+
790819
write!(out, " {}", tag_name);
791820
if let Some(prim) = size {
792821
write!(out, " : {}", prim);
@@ -865,6 +894,16 @@ impl Enum {
865894
}
866895
}
867896

897+
if let Some(note) = self.annotations.deprecated_note(config) {
898+
if let Some(note) = self.annotations.format_deprecated_note(
899+
config.enumeration.deprecated.as_deref(),
900+
config.enumeration.deprecated_with_note.as_deref(),
901+
note,
902+
) {
903+
write!(out, " {} ", note);
904+
}
905+
}
906+
868907
if config.language != Language::C || config.style.generate_tag() {
869908
write!(out, " {}", self.export_name());
870909
}

src/bindgen/ir/function.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,15 @@ impl Source for Function {
241241
write!(out, "{} ", anno);
242242
}
243243
}
244+
if let Some(note) = func.annotations.deprecated_note(config) {
245+
if let Some(note) = func.annotations.format_deprecated_note(
246+
config.function.deprecated.as_deref(),
247+
config.function.deprecated_with_note.as_deref(),
248+
note,
249+
) {
250+
write!(out, "{} ", note);
251+
}
252+
}
244253
}
245254
cdecl::write_func(out, func, Layout::Horizontal, config);
246255

@@ -284,6 +293,16 @@ impl Source for Function {
284293
out.new_line();
285294
}
286295
}
296+
if let Some(note) = func.annotations.deprecated_note(config) {
297+
if let Some(note) = func.annotations.format_deprecated_note(
298+
config.function.deprecated.as_deref(),
299+
config.function.deprecated_with_note.as_deref(),
300+
note,
301+
) {
302+
write!(out, "{}", note);
303+
out.new_line();
304+
}
305+
}
287306
}
288307
cdecl::write_func(out, func, Layout::Vertical, config);
289308
if !func.extern_decl {

src/bindgen/ir/structure.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,15 @@ impl Source for Struct {
455455
write!(out, " {}", anno);
456456
}
457457
}
458+
if let Some(note) = self.annotations.deprecated_note(config) {
459+
if let Some(note) = self.annotations.format_deprecated_note(
460+
config.structure.deprecated.as_deref(),
461+
config.structure.deprecated_with_note.as_deref(),
462+
note,
463+
) {
464+
write!(out, " {}", note);
465+
}
466+
}
458467

459468
if config.language != Language::C || config.style.generate_tag() {
460469
write!(out, " {}", self.export_name());

src/bindgen/utilities.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,58 @@ pub trait SynAttributeHelpers {
130130
})
131131
}
132132

133+
fn find_deprecated_note(&self) -> Option<String> {
134+
let attrs = self.attrs();
135+
// #[deprecated = ""]
136+
if let Some(note) = attrs.attr_name_value_lookup("deprecated") {
137+
return Some(note);
138+
}
139+
140+
// #[deprecated]
141+
if attrs.has_attr_word("deprecated") {
142+
return Some("".to_string());
143+
}
144+
145+
// #[deprecated(note = "")]
146+
if let Some(attr) = attrs.iter().find(|attr| {
147+
if let Ok(syn::Meta::List(list)) = attr.parse_meta() {
148+
list.path.is_ident("deprecated")
149+
} else {
150+
false
151+
}
152+
}) {
153+
let args: syn::punctuated::Punctuated<syn::MetaNameValue, Token![,]> =
154+
match attr.parse_args_with(syn::punctuated::Punctuated::parse_terminated) {
155+
Ok(args) => args,
156+
Err(_) => {
157+
warn!("couldn't parse deprecated attribute");
158+
return None;
159+
}
160+
};
161+
162+
let lit = match args
163+
.iter()
164+
.find(|arg| arg.path.is_ident("note"))
165+
.map(|arg| &arg.lit)
166+
{
167+
Some(lit) => lit,
168+
None => {
169+
warn!("couldn't parse deprecated attribute: no `note` field");
170+
return None;
171+
}
172+
};
173+
174+
return if let syn::Lit::Str(lit) = lit {
175+
Some(lit.value())
176+
} else {
177+
warn!("deprecated attribute must be a string");
178+
None
179+
};
180+
}
181+
182+
None
183+
}
184+
133185
fn is_no_mangle(&self) -> bool {
134186
self.has_attr_word("no_mangle")
135187
}

0 commit comments

Comments
 (0)