Skip to content

Commit

Permalink
feat: support #[deprecated] on enum variants
Browse files Browse the repository at this point in the history
While the #[deprecated] attribute was already used on structs, fns and
enums, it was not implemented for enum variants. The information about
enum variants is already available via `variant.body.annotations`, so
the support for the #[deprecated] attribute on enum-variant level is
more or less only another pair of entries within the `EnumConfig`.

This commit adds two new options within the `[enum]` settings:

- deprecated_variant, and
- deprecated_variant_with_notes

Both get active only on #[deprecated] variants, e.g.,

     #[repr(u8)]
     enum ApiLevel {
         #[deprecated(note = "Legacy Support until 2025")]
         L1 = 1,
         #[deprecated]
         L2 = 2,
         L3 = 3,
         L4 = 4,
    }

For enums with struct variants, the current struct deprecation methods
are already working good enough (see tests/expecations/deprecated*).
  • Loading branch information
bkaestner authored and emilio committed Apr 14, 2024
1 parent bde8ab4 commit 400f437
Show file tree
Hide file tree
Showing 15 changed files with 474 additions and 20 deletions.
17 changes: 17 additions & 0 deletions docs.md
Expand Up @@ -933,6 +933,23 @@ deprecated = "DEPRECATED_ENUM"
# default: nothing is emitted for deprecated enums
deprecated_with_notes = "DEPRECATED_ENUM_WITH_NOTE"

# An optional string that should come after the name of any enum variant 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_VARIANT"
# default: nothing is emitted for deprecated enum variants
deprecated_variant = "DEPRECATED_ENUM_VARIANT"

# An optional string that should come after the name of any enum variant 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 enum variants
deprecated_variant_with_notes = "DEPRECATED_ENUM_VARIANT_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
6 changes: 6 additions & 0 deletions src/bindgen/config.rs
Expand Up @@ -586,6 +586,10 @@ pub struct EnumConfig {
pub deprecated: Option<String>,
/// The way to annotation this function as #[deprecated] with notes
pub deprecated_with_note: Option<String>,
/// The way to annotate this enum variant as #[deprecated] without notes
pub deprecated_variant: Option<String>,
/// The way to annotate this enum variant as #[deprecated] with notes
pub deprecated_variant_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 @@ -619,6 +623,8 @@ impl Default for EnumConfig {
must_use: None,
deprecated: None,
deprecated_with_note: None,
deprecated_variant: None,
deprecated_variant_with_note: None,
derive_tagged_enum_destructor: false,
derive_tagged_enum_copy_constructor: false,
derive_tagged_enum_copy_assignment: false,
Expand Down
5 changes: 5 additions & 0 deletions src/bindgen/ir/annotation.rs
Expand Up @@ -43,6 +43,7 @@ pub enum DeprecatedNoteKind {
Function,
Struct,
Enum,
EnumVariant,
}

impl AnnotationSet {
Expand Down Expand Up @@ -76,13 +77,17 @@ impl AnnotationSet {
if note.is_empty() {
return Some(Cow::Borrowed(match kind {
DeprecatedNoteKind::Enum => config.enumeration.deprecated.as_deref()?,
DeprecatedNoteKind::EnumVariant => {
config.enumeration.deprecated_variant.as_deref()?
}
DeprecatedNoteKind::Function => config.function.deprecated.as_deref()?,
DeprecatedNoteKind::Struct => config.structure.deprecated.as_deref()?,
}));
}

let format = match kind {
DeprecatedNoteKind::Enum => &config.enumeration.deprecated_with_note,
DeprecatedNoteKind::EnumVariant => &config.enumeration.deprecated_variant_with_note,
DeprecatedNoteKind::Function => &config.function.deprecated_with_note,
DeprecatedNoteKind::Struct => &config.structure.deprecated_with_note,
}
Expand Down
9 changes: 9 additions & 0 deletions src/bindgen/ir/enumeration.rs
Expand Up @@ -301,6 +301,15 @@ impl Source for EnumVariant {
}
self.documentation.write(config, out);
write!(out, "{}", self.export_name);

if let Some(note) = self
.body
.annotations()
.deprecated_note(config, DeprecatedNoteKind::EnumVariant)
{
write!(out, " {}", note);
}

if let Some(discriminant) = &self.discriminant {
if config.language == Language::Cython {
// For extern Cython declarations the enumerator value is ignored,
Expand Down
45 changes: 43 additions & 2 deletions tests/expectations/deprecated.c
@@ -1,9 +1,11 @@
#define DEPRECATED_FUNC __attribute__((deprecated))
#define DEPRECATED_STRUCT __attribute__((deprecated))
#define DEPRECATED_ENUM __attribute__((deprecated))
#define DEPRECATED_ENUM_VARIANT __attribute__((deprecated))
#define DEPRECATED_FUNC_WITH_NOTE(...) __attribute__((deprecated(__VA_ARGS__)))
#define DEPRECATED_STRUCT_WITH_NOTE(...) __attribute__((deprecated(__VA_ARGS__)))
#define DEPRECATED_ENUM_WITH_NOTE(...) __attribute__((deprecated(__VA_ARGS__)))
#define DEPRECATED_ENUM_VARIANT_WITH_NOTE(...) __attribute__((deprecated(__VA_ARGS__)))


#include <stdarg.h>
Expand All @@ -21,6 +23,14 @@ enum DEPRECATED_ENUM_WITH_NOTE("This is a note") DeprecatedEnumWithNote {
};
typedef int32_t DeprecatedEnumWithNote;

enum EnumWithDeprecatedVariants {
C = 0,
D DEPRECATED_ENUM_VARIANT = 1,
E DEPRECATED_ENUM_VARIANT_WITH_NOTE("This is a note") = 2,
F DEPRECATED_ENUM_VARIANT_WITH_NOTE("This is a note") = 3,
};
typedef int32_t EnumWithDeprecatedVariants;

typedef struct DEPRECATED_STRUCT {
int32_t a;
} DeprecatedStruct;
Expand All @@ -29,6 +39,35 @@ typedef struct DEPRECATED_STRUCT_WITH_NOTE("This is a note") {
int32_t a;
} DeprecatedStructWithNote;

enum EnumWithDeprecatedStructVariants_Tag {
Foo,
Bar DEPRECATED_ENUM_VARIANT,
Baz DEPRECATED_ENUM_VARIANT_WITH_NOTE("This is a note"),
};
typedef uint8_t EnumWithDeprecatedStructVariants_Tag;

typedef struct DEPRECATED_STRUCT {
EnumWithDeprecatedStructVariants_Tag tag;
uint8_t x;
int16_t y;
} Bar_Body;

typedef struct DEPRECATED_STRUCT_WITH_NOTE("This is a note") {
EnumWithDeprecatedStructVariants_Tag tag;
uint8_t x;
uint8_t y;
} Baz_Body;

typedef union {
EnumWithDeprecatedStructVariants_Tag tag;
struct {
EnumWithDeprecatedStructVariants_Tag foo_tag;
int16_t foo;
};
Bar_Body bar;
Baz_Body baz;
} EnumWithDeprecatedStructVariants;

DEPRECATED_FUNC void deprecated_without_note(void);

DEPRECATED_FUNC_WITH_NOTE("This is a note") void deprecated_without_bracket(void);
Expand All @@ -42,5 +81,7 @@ void deprecated_with_note_which_requires_to_be_escaped(void);

void dummy(DeprecatedEnum a,
DeprecatedEnumWithNote b,
DeprecatedStruct c,
DeprecatedStructWithNote d);
EnumWithDeprecatedVariants c,
DeprecatedStruct d,
DeprecatedStructWithNote e,
EnumWithDeprecatedStructVariants f);
57 changes: 55 additions & 2 deletions tests/expectations/deprecated.compat.c
@@ -1,9 +1,11 @@
#define DEPRECATED_FUNC __attribute__((deprecated))
#define DEPRECATED_STRUCT __attribute__((deprecated))
#define DEPRECATED_ENUM __attribute__((deprecated))
#define DEPRECATED_ENUM_VARIANT __attribute__((deprecated))
#define DEPRECATED_FUNC_WITH_NOTE(...) __attribute__((deprecated(__VA_ARGS__)))
#define DEPRECATED_STRUCT_WITH_NOTE(...) __attribute__((deprecated(__VA_ARGS__)))
#define DEPRECATED_ENUM_WITH_NOTE(...) __attribute__((deprecated(__VA_ARGS__)))
#define DEPRECATED_ENUM_VARIANT_WITH_NOTE(...) __attribute__((deprecated(__VA_ARGS__)))


#include <stdarg.h>
Expand Down Expand Up @@ -33,6 +35,20 @@ enum DEPRECATED_ENUM_WITH_NOTE("This is a note") DeprecatedEnumWithNote
typedef int32_t DeprecatedEnumWithNote;
#endif // __cplusplus

enum EnumWithDeprecatedVariants
#ifdef __cplusplus
: int32_t
#endif // __cplusplus
{
C = 0,
D DEPRECATED_ENUM_VARIANT = 1,
E DEPRECATED_ENUM_VARIANT_WITH_NOTE("This is a note") = 2,
F DEPRECATED_ENUM_VARIANT_WITH_NOTE("This is a note") = 3,
};
#ifndef __cplusplus
typedef int32_t EnumWithDeprecatedVariants;
#endif // __cplusplus

typedef struct DEPRECATED_STRUCT {
int32_t a;
} DeprecatedStruct;
Expand All @@ -41,6 +57,41 @@ typedef struct DEPRECATED_STRUCT_WITH_NOTE("This is a note") {
int32_t a;
} DeprecatedStructWithNote;

enum EnumWithDeprecatedStructVariants_Tag
#ifdef __cplusplus
: uint8_t
#endif // __cplusplus
{
Foo,
Bar DEPRECATED_ENUM_VARIANT,
Baz DEPRECATED_ENUM_VARIANT_WITH_NOTE("This is a note"),
};
#ifndef __cplusplus
typedef uint8_t EnumWithDeprecatedStructVariants_Tag;
#endif // __cplusplus

typedef struct DEPRECATED_STRUCT {
EnumWithDeprecatedStructVariants_Tag tag;
uint8_t x;
int16_t y;
} Bar_Body;

typedef struct DEPRECATED_STRUCT_WITH_NOTE("This is a note") {
EnumWithDeprecatedStructVariants_Tag tag;
uint8_t x;
uint8_t y;
} Baz_Body;

typedef union {
EnumWithDeprecatedStructVariants_Tag tag;
struct {
EnumWithDeprecatedStructVariants_Tag foo_tag;
int16_t foo;
};
Bar_Body bar;
Baz_Body baz;
} EnumWithDeprecatedStructVariants;

#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
Expand All @@ -58,8 +109,10 @@ void deprecated_with_note_which_requires_to_be_escaped(void);

void dummy(DeprecatedEnum a,
DeprecatedEnumWithNote b,
DeprecatedStruct c,
DeprecatedStructWithNote d);
EnumWithDeprecatedVariants c,
DeprecatedStruct d,
DeprecatedStructWithNote e,
EnumWithDeprecatedStructVariants f);

#ifdef __cplusplus
} // extern "C"
Expand Down
47 changes: 45 additions & 2 deletions tests/expectations/deprecated.cpp
@@ -1,9 +1,11 @@
#define DEPRECATED_FUNC __attribute__((deprecated))
#define DEPRECATED_STRUCT __attribute__((deprecated))
#define DEPRECATED_ENUM __attribute__((deprecated))
#define DEPRECATED_ENUM_VARIANT __attribute__((deprecated))
#define DEPRECATED_FUNC_WITH_NOTE(...) __attribute__((deprecated(__VA_ARGS__)))
#define DEPRECATED_STRUCT_WITH_NOTE(...) __attribute__((deprecated(__VA_ARGS__)))
#define DEPRECATED_ENUM_WITH_NOTE(...) __attribute__((deprecated(__VA_ARGS__)))
#define DEPRECATED_ENUM_VARIANT_WITH_NOTE(...) __attribute__((deprecated(__VA_ARGS__)))


#include <cstdarg>
Expand All @@ -20,6 +22,13 @@ enum class DEPRECATED_ENUM_WITH_NOTE("This is a note") DeprecatedEnumWithNote :
B = 0,
};

enum class EnumWithDeprecatedVariants : int32_t {
C = 0,
D DEPRECATED_ENUM_VARIANT = 1,
E DEPRECATED_ENUM_VARIANT_WITH_NOTE("This is a note") = 2,
F DEPRECATED_ENUM_VARIANT_WITH_NOTE("This is a note") = 3,
};

struct DEPRECATED_STRUCT DeprecatedStruct {
int32_t a;
};
Expand All @@ -28,6 +37,38 @@ struct DEPRECATED_STRUCT_WITH_NOTE("This is a note") DeprecatedStructWithNote {
int32_t a;
};

union EnumWithDeprecatedStructVariants {
enum class Tag : uint8_t {
Foo,
Bar DEPRECATED_ENUM_VARIANT,
Baz DEPRECATED_ENUM_VARIANT_WITH_NOTE("This is a note"),
};

struct Foo_Body {
Tag tag;
int16_t _0;
};

struct DEPRECATED_STRUCT Bar_Body {
Tag tag;
uint8_t x;
int16_t y;
};

struct DEPRECATED_STRUCT_WITH_NOTE("This is a note") Baz_Body {
Tag tag;
uint8_t x;
uint8_t y;
};

struct {
Tag tag;
};
Foo_Body foo;
Bar_Body bar;
Baz_Body baz;
};

extern "C" {

DEPRECATED_FUNC void deprecated_without_note();
Expand All @@ -43,7 +84,9 @@ void deprecated_with_note_which_requires_to_be_escaped();

void dummy(DeprecatedEnum a,
DeprecatedEnumWithNote b,
DeprecatedStruct c,
DeprecatedStructWithNote d);
EnumWithDeprecatedVariants c,
DeprecatedStruct d,
DeprecatedStructWithNote e,
EnumWithDeprecatedStructVariants f);

} // extern "C"
37 changes: 35 additions & 2 deletions tests/expectations/deprecated.pyx
@@ -1,9 +1,11 @@
#define DEPRECATED_FUNC __attribute__((deprecated))
#define DEPRECATED_STRUCT __attribute__((deprecated))
#define DEPRECATED_ENUM __attribute__((deprecated))
#define DEPRECATED_ENUM_VARIANT __attribute__((deprecated))
#define DEPRECATED_FUNC_WITH_NOTE(...) __attribute__((deprecated(__VA_ARGS__)))
#define DEPRECATED_STRUCT_WITH_NOTE(...) __attribute__((deprecated(__VA_ARGS__)))
#define DEPRECATED_ENUM_WITH_NOTE(...) __attribute__((deprecated(__VA_ARGS__)))
#define DEPRECATED_ENUM_VARIANT_WITH_NOTE(...) __attribute__((deprecated(__VA_ARGS__)))


from libc.stdint cimport int8_t, int16_t, int32_t, int64_t, intptr_t
Expand All @@ -22,12 +24,41 @@ cdef extern from *:
B # = 0,
ctypedef int32_t DeprecatedEnumWithNote;

cdef enum:
C # = 0,
D # = 1,
E # = 2,
F # = 3,
ctypedef int32_t EnumWithDeprecatedVariants;

ctypedef struct DeprecatedStruct:
int32_t a;

ctypedef struct DeprecatedStructWithNote:
int32_t a;

cdef enum:
Foo,
Bar,
Baz,
ctypedef uint8_t EnumWithDeprecatedStructVariants_Tag;

ctypedef struct Bar_Body:
EnumWithDeprecatedStructVariants_Tag tag;
uint8_t x;
int16_t y;

ctypedef struct Baz_Body:
EnumWithDeprecatedStructVariants_Tag tag;
uint8_t x;
uint8_t y;

ctypedef union EnumWithDeprecatedStructVariants:
EnumWithDeprecatedStructVariants_Tag tag;
int16_t foo;
Bar_Body bar;
Baz_Body baz;

void deprecated_without_note();

void deprecated_without_bracket();
Expand All @@ -40,5 +71,7 @@ cdef extern from *:

void dummy(DeprecatedEnum a,
DeprecatedEnumWithNote b,
DeprecatedStruct c,
DeprecatedStructWithNote d);
EnumWithDeprecatedVariants c,
DeprecatedStruct d,
DeprecatedStructWithNote e,
EnumWithDeprecatedStructVariants f);

0 comments on commit 400f437

Please sign in to comment.