Skip to content

Commit

Permalink
Add support for #[deprecated].
Browse files Browse the repository at this point in the history
Closes #875.
Closes #860.
Closes #408.
  • Loading branch information
sevenc-nanashi authored and emilio committed Sep 4, 2023
1 parent d8355da commit 0fb5d07
Show file tree
Hide file tree
Showing 21 changed files with 769 additions and 2 deletions.
51 changes: 51 additions & 0 deletions docs.md
Expand Up @@ -680,6 +680,23 @@ args = "horizontal"
# default: nothing is emitted for must_use functions
must_use = "MUST_USE_FUNC"

# An optional string that should prefix function declarations which have been
# marked as `#[deprecated]` without note. For instance, "__attribute__((deprecated))"
# would be a reasonable value if targeting gcc/clang. A more portable solution
# would involve emitting the name of a macro which you define in a
# platform-specific way. e.g. "DEPRECATED_FUNC"
# default: nothing is emitted for deprecated functions
deprecated = "DEPRECATED_FUNC"

# An optional string that should prefix function declarations which have been
# marked as `#[deprecated(note = "reason")]`. `{}` will be replaced with the
# double-quoted string. For instance, "__attribute__((deprecated({})))"
# would be a reasonable value if targeting gcc/clang. A more portable solution
# would involve emitting the name of a macro which you define in a
# platform-specific way. e.g. "DEPRECATED_FUNC_WITH_NOTE(note)"
# default: nothing is emitted for deprecated functions
deprecated_with_notes = "DEPRECATED_FUNC_WITH_NOTE"

# An optional string that will be used in the attribute position for functions
# that don't return (that return `!` in Rust).
#
Expand Down Expand Up @@ -752,6 +769,23 @@ rename_fields = "PascalCase"
# default: nothing is emitted for must_use structs
must_use = "MUST_USE_STRUCT"

# An optional string that should come before the name of any struct which has been
# marked as `#[deprecated]` without note. For instance, "__attribute__((deprecated))"
# would be a reasonable value if targeting gcc/clang. A more portable solution
# would involve emitting the name of a macro which you define in a
# platform-specific way. e.g. "DEPRECATED_STRUCT"
# default: nothing is emitted for deprecated structs
deprecated = "DEPRECATED_STRUCT"

# An optional string that should come before the name of any struct which has been
# marked as `#[deprecated(note = "reason")]`. `{}` will be replaced with the
# double-quoted string. For instance, "__attribute__((deprecated({})))"
# would be a reasonable value if targeting gcc/clang. A more portable solution
# would involve emitting the name of a macro which you define in a
# platform-specific way. e.g. "DEPRECATED_STRUCT_WITH_NOTE(note)"
# default: nothing is emitted for deprecated structs
deprecated_with_notes = "DEPRECATED_STRUCT_WITH_NOTE"

# Whether a Rust type with associated consts should emit those consts inside the
# type's body. Otherwise they will be emitted trailing and with the type's name
# prefixed. This does nothing if the target is C, or if
Expand Down Expand Up @@ -865,6 +899,23 @@ cast_assert_name = "MOZ_RELEASE_ASSERT"
# default: nothing is emitted for must_use enums
must_use = "MUST_USE_ENUM"

# An optional string that should come before the name of any enum which has been
# marked as `#[deprecated]` without note. For instance, "__attribute__((deprecated))"
# would be a reasonable value if targeting gcc/clang. A more portable solution
# would involve emitting the name of a macro which you define in a
# platform-specific way. e.g. "DEPRECATED_ENUM"
# default: nothing is emitted for deprecated enums
deprecated = "DEPRECATED_ENUM"

# An optional string that should come before the name of any enum which has been
# marked as `#[deprecated(note = "reason")]`. `{}` will be replaced with the
# double-quoted string. For instance, "__attribute__((deprecated({})))"
# would be a reasonable value if targeting gcc/clang. A more portable solution
# would involve emitting the name of a macro which you define in a
# platform-specific way. e.g. "DEPRECATED_ENUM_WITH_NOTE(note)"
# default: nothing is emitted for deprecated enums
deprecated_with_notes = "DEPRECATED_ENUM_WITH_NOTE"

# Whether enums with fields should generate destructors. This exists so that generic
# enums can be properly instantiated with payloads that are C++ types with
# destructors. This isn't necessary for structs because C++ has rules to
Expand Down
3 changes: 3 additions & 0 deletions src/bindgen/cdecl.rs
Expand Up @@ -40,6 +40,7 @@ struct CDecl {
type_generic_args: Vec<GenericArgument>,
declarators: Vec<CDeclarator>,
type_ctype: Option<DeclarationType>,
deprecated: Option<String>,
}

impl CDecl {
Expand All @@ -50,6 +51,7 @@ impl CDecl {
type_generic_args: Vec::new(),
declarators: Vec::new(),
type_ctype: None,
deprecated: None,
}
}

Expand Down Expand Up @@ -99,6 +101,7 @@ impl CDecl {
layout,
never_return: f.never_return,
});
self.deprecated = f.annotations.deprecated.clone();
self.build_type(&f.ret, false, config);
}

Expand Down
16 changes: 16 additions & 0 deletions src/bindgen/config.rs
Expand Up @@ -425,6 +425,10 @@ pub struct FunctionConfig {
pub postfix: Option<String>,
/// The way to annotation this function as #[must_use]
pub must_use: Option<String>,
/// The way to annotation this function as #[deprecated] without notes
pub deprecated: Option<String>,
/// The way to annotation this function as #[deprecated] with notes
pub deprecated_with_note: Option<String>,
/// The style to layout the args
pub args: Layout,
/// The rename rule to apply to function args
Expand All @@ -443,6 +447,8 @@ impl Default for FunctionConfig {
prefix: None,
postfix: None,
must_use: None,
deprecated: None,
deprecated_with_note: None,
args: Layout::Auto,
rename_args: RenameRule::None,
swift_name_macro: None,
Expand Down Expand Up @@ -498,6 +504,10 @@ pub struct StructConfig {
pub associated_constants_in_body: bool,
/// The way to annotate this struct as #[must_use].
pub must_use: Option<String>,
/// The way to annotation this function as #[deprecated] without notes
pub deprecated: Option<String>,
/// The way to annotation this function as #[deprecated] with notes
pub deprecated_with_note: Option<String>,
}

impl StructConfig {
Expand Down Expand Up @@ -581,6 +591,10 @@ pub struct EnumConfig {
pub cast_assert_name: Option<String>,
/// The way to annotation this enum as #[must_use].
pub must_use: Option<String>,
/// The way to annotation this function as #[deprecated] without notes
pub deprecated: Option<String>,
/// The way to annotation this function as #[deprecated] with notes
pub deprecated_with_note: Option<String>,
/// Whether to generate destructors of tagged enums.
pub derive_tagged_enum_destructor: bool,
/// Whether to generate copy-constructors of tagged enums.
Expand Down Expand Up @@ -612,6 +626,8 @@ impl Default for EnumConfig {
derive_mut_casts: false,
cast_assert_name: None,
must_use: None,
deprecated: None,
deprecated_with_note: None,
derive_tagged_enum_destructor: false,
derive_tagged_enum_copy_constructor: false,
derive_tagged_enum_copy_assignment: false,
Expand Down
26 changes: 25 additions & 1 deletion src/bindgen/ir/annotation.rs
Expand Up @@ -35,13 +35,15 @@ pub enum AnnotationValue {
pub struct AnnotationSet {
annotations: HashMap<String, AnnotationValue>,
pub must_use: bool,
pub deprecated: Option<String>,
}

impl AnnotationSet {
pub fn new() -> AnnotationSet {
AnnotationSet {
annotations: HashMap::new(),
must_use: false,
deprecated: None,
}
}

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

pub(crate) fn deprecated_note(&self, config: &Config) -> Option<&str> {
if config.language == Language::Cython {
return None;
}

self.deprecated.as_deref()
}

pub(crate) fn format_deprecated_note(
&self,
format_without_note: Option<&str>,
format_with_note: Option<&str>,
note: &str,
) -> Option<String> {
if note.is_empty() {
return format_without_note.map(|x| x.to_string());
}
format_with_note
.map(|format_with_note| format_with_note.replace("{}", format!("{:?}", note).as_str()))
}

pub fn load(attrs: &[syn::Attribute]) -> Result<AnnotationSet, String> {
let lines = attrs.get_comment_lines();
let lines: Vec<&str> = lines
Expand All @@ -68,7 +91,7 @@ impl AnnotationSet {
.collect();

let must_use = attrs.has_attr_word("must_use");

let deprecated = attrs.find_deprecated_note();
let mut annotations = HashMap::new();

// Look at each line for an annotation
Expand Down Expand Up @@ -118,6 +141,7 @@ impl AnnotationSet {
Ok(AnnotationSet {
annotations,
must_use,
deprecated,
})
}

Expand Down
41 changes: 40 additions & 1 deletion src/bindgen/ir/enumeration.rs
Expand Up @@ -753,7 +753,17 @@ impl Enum {
if let Some(prim) = size {
// If we need to specify size, then we have no choice but to create a typedef,
// so `config.style` is not respected.
write!(out, "enum {}", tag_name);
write!(out, "enum");
if let Some(note) = self.annotations.deprecated_note(config) {
if let Some(note) = self.annotations.format_deprecated_note(
config.enumeration.deprecated.as_deref(),
config.enumeration.deprecated_with_note.as_deref(),
note,
) {
write!(out, " {}", note);
}
}
write!(out, " {}", tag_name);

if config.cpp_compatible_c() {
out.new_line();
Expand All @@ -769,6 +779,15 @@ impl Enum {
out.write("typedef ");
}
out.write("enum");
if let Some(note) = self.annotations.deprecated_note(config) {
if let Some(note) = self.annotations.format_deprecated_note(
config.enumeration.deprecated.as_deref(),
config.enumeration.deprecated_with_note.as_deref(),
note,
) {
write!(out, " {}", note);
}
}
if config.style.generate_tag() {
write!(out, " {}", tag_name);
}
Expand All @@ -787,6 +806,16 @@ impl Enum {
}
}

if let Some(note) = self.annotations.deprecated_note(config) {
if let Some(note) = self.annotations.format_deprecated_note(
config.structure.deprecated.as_deref(),
config.structure.deprecated_with_note.as_deref(),
note,
) {
write!(out, " {}", note);
}
}

write!(out, " {}", tag_name);
if let Some(prim) = size {
write!(out, " : {}", prim);
Expand Down Expand Up @@ -865,6 +894,16 @@ impl Enum {
}
}

if let Some(note) = self.annotations.deprecated_note(config) {
if let Some(note) = self.annotations.format_deprecated_note(
config.enumeration.deprecated.as_deref(),
config.enumeration.deprecated_with_note.as_deref(),
note,
) {
write!(out, " {} ", note);
}
}

if config.language != Language::C || config.style.generate_tag() {
write!(out, " {}", self.export_name());
}
Expand Down
19 changes: 19 additions & 0 deletions src/bindgen/ir/function.rs
Expand Up @@ -241,6 +241,15 @@ impl Source for Function {
write!(out, "{} ", anno);
}
}
if let Some(note) = func.annotations.deprecated_note(config) {
if let Some(note) = func.annotations.format_deprecated_note(
config.function.deprecated.as_deref(),
config.function.deprecated_with_note.as_deref(),
note,
) {
write!(out, "{} ", note);
}
}
}
cdecl::write_func(out, func, Layout::Horizontal, config);

Expand Down Expand Up @@ -284,6 +293,16 @@ impl Source for Function {
out.new_line();
}
}
if let Some(note) = func.annotations.deprecated_note(config) {
if let Some(note) = func.annotations.format_deprecated_note(
config.function.deprecated.as_deref(),
config.function.deprecated_with_note.as_deref(),
note,
) {
write!(out, "{}", note);
out.new_line();
}
}
}
cdecl::write_func(out, func, Layout::Vertical, config);
if !func.extern_decl {
Expand Down
9 changes: 9 additions & 0 deletions src/bindgen/ir/structure.rs
Expand Up @@ -455,6 +455,15 @@ impl Source for Struct {
write!(out, " {}", anno);
}
}
if let Some(note) = self.annotations.deprecated_note(config) {
if let Some(note) = self.annotations.format_deprecated_note(
config.structure.deprecated.as_deref(),
config.structure.deprecated_with_note.as_deref(),
note,
) {
write!(out, " {}", note);
}
}

if config.language != Language::C || config.style.generate_tag() {
write!(out, " {}", self.export_name());
Expand Down
52 changes: 52 additions & 0 deletions src/bindgen/utilities.rs
Expand Up @@ -130,6 +130,58 @@ pub trait SynAttributeHelpers {
})
}

fn find_deprecated_note(&self) -> Option<String> {
let attrs = self.attrs();
// #[deprecated = ""]
if let Some(note) = attrs.attr_name_value_lookup("deprecated") {
return Some(note);
}

// #[deprecated]
if attrs.has_attr_word("deprecated") {
return Some("".to_string());
}

// #[deprecated(note = "")]
if let Some(attr) = attrs.iter().find(|attr| {
if let Ok(syn::Meta::List(list)) = attr.parse_meta() {
list.path.is_ident("deprecated")
} else {
false
}
}) {
let args: syn::punctuated::Punctuated<syn::MetaNameValue, Token![,]> =
match attr.parse_args_with(syn::punctuated::Punctuated::parse_terminated) {
Ok(args) => args,
Err(_) => {
warn!("couldn't parse deprecated attribute");
return None;
}
};

let lit = match args
.iter()
.find(|arg| arg.path.is_ident("note"))
.map(|arg| &arg.lit)
{
Some(lit) => lit,
None => {
warn!("couldn't parse deprecated attribute: no `note` field");
return None;
}
};

return if let syn::Lit::Str(lit) = lit {
Some(lit.value())
} else {
warn!("deprecated attribute must be a string");
None
};
}

None
}

fn is_no_mangle(&self) -> bool {
self.has_attr_word("no_mangle")
}
Expand Down

0 comments on commit 0fb5d07

Please sign in to comment.