libdrgn: add macros for strict enum switch statements

There are several places where we'd like to enforce that every
enumeration is handled in a switch. Add SWITCH_ENUM() and
SWITCH_ENUM_DEFAULT() macros for that and use them.

Signed-off-by: Omar Sandoval <osandov@osandov.com>
This commit is contained in:
Omar Sandoval 2020-12-04 11:20:00 -08:00
parent a4dbd7bf95
commit 2710b4d2aa
5 changed files with 51 additions and 31 deletions

View File

@ -337,7 +337,7 @@ c_declare_variable(struct drgn_qualified_type qualified_type,
struct string_callback *name, size_t indent,
struct string_builder *sb)
{
switch (drgn_type_kind(qualified_type.type)) {
SWITCH_ENUM(drgn_type_kind(qualified_type.type),
case DRGN_TYPE_VOID:
case DRGN_TYPE_INT:
case DRGN_TYPE_BOOL:
@ -356,8 +356,7 @@ c_declare_variable(struct drgn_qualified_type qualified_type,
return c_declare_array(qualified_type, name, indent, sb);
case DRGN_TYPE_FUNCTION:
return c_declare_function(qualified_type, name, indent, sb);
}
UNREACHABLE();
)
}
static struct drgn_error *
@ -489,7 +488,7 @@ static struct drgn_error *
c_define_type(struct drgn_qualified_type qualified_type, size_t indent,
struct string_builder *sb)
{
switch (drgn_type_kind(qualified_type.type)) {
SWITCH_ENUM(drgn_type_kind(qualified_type.type),
case DRGN_TYPE_VOID:
case DRGN_TYPE_INT:
case DRGN_TYPE_BOOL:
@ -511,8 +510,7 @@ c_define_type(struct drgn_qualified_type qualified_type, size_t indent,
case DRGN_TYPE_FUNCTION:
return drgn_error_create(DRGN_ERROR_INVALID_ARGUMENT,
"function type cannot be formatted");
}
UNREACHABLE();
)
}
static struct drgn_error *
@ -1578,7 +1576,7 @@ c_format_object_impl(const struct drgn_object *obj, size_t indent,
one_line_columns = 0;
}
switch (drgn_type_kind(underlying_type)) {
SWITCH_ENUM(drgn_type_kind(underlying_type),
case DRGN_TYPE_VOID:
return drgn_error_create(DRGN_ERROR_TYPE,
"cannot format void object");
@ -1604,9 +1602,9 @@ c_format_object_impl(const struct drgn_object *obj, size_t indent,
multi_line_columns, flags, sb);
case DRGN_TYPE_FUNCTION:
return c_format_function_object(obj, sb);
default:
UNREACHABLE();
}
case DRGN_TYPE_TYPEDEF:
case DRGN_TYPE_POINTER:
)
}
struct drgn_error *c_format_object(const struct drgn_object *obj,

View File

@ -278,7 +278,7 @@ struct drgn_error *
drgn_byte_order_to_little_endian(struct drgn_program *prog,
enum drgn_byte_order byte_order, bool *ret)
{
switch (byte_order) {
SWITCH_ENUM_DEFAULT(byte_order,
case DRGN_BIG_ENDIAN:
*ret = false;
return NULL;
@ -290,7 +290,7 @@ drgn_byte_order_to_little_endian(struct drgn_program *prog,
default:
return drgn_error_create(DRGN_ERROR_INVALID_ARGUMENT,
"invalid byte order");
}
)
}
struct drgn_error *
@ -1109,18 +1109,16 @@ drgn_object_reinterpret(struct drgn_object *res,
return NULL;
}
switch (obj->kind) {
case DRGN_OBJECT_BUFFER:
err = drgn_object_slice_internal(res, obj, &type, kind,
bit_size, 0);
if (err)
return err;
res->value.little_endian = little_endian;
return NULL;
default:
if (obj->kind != DRGN_OBJECT_BUFFER) {
return drgn_error_create(DRGN_ERROR_INVALID_ARGUMENT,
"cannot reinterpret primitive value");
}
err = drgn_object_slice_internal(res, obj, &type, kind,
bit_size, 0);
if (err)
return err;
res->value.little_endian = little_endian;
return NULL;
}
LIBDRGN_PUBLIC struct drgn_error *

View File

@ -30,7 +30,7 @@ drgn_platform_create(enum drgn_architecture arch,
const struct drgn_architecture_info *arch_info;
struct drgn_platform *platform;
switch (arch) {
SWITCH_ENUM_DEFAULT(arch,
case DRGN_ARCH_UNKNOWN:
arch_info = &arch_info_unknown;
break;
@ -40,7 +40,7 @@ drgn_platform_create(enum drgn_architecture arch,
default:
return drgn_error_create(DRGN_ERROR_INVALID_ARGUMENT,
"invalid architecture");
}
)
if (flags == DRGN_PLATFORM_DEFAULT_FLAGS) {
if (arch == DRGN_ARCH_UNKNOWN) {
return drgn_error_create(DRGN_ERROR_INVALID_ARGUMENT,

View File

@ -1034,7 +1034,7 @@ static struct drgn_error *drgn_type_eq_impl(struct drgn_type *a,
drgn_type_is_complete(a) != drgn_type_is_complete(b))
goto out_false;
switch (drgn_type_kind(a)) {
SWITCH_ENUM(drgn_type_kind(a),
/*
* This types are uniquely deduplicated, so if their pointers did not
* compare equal then they are not equal.
@ -1062,7 +1062,7 @@ static struct drgn_error *drgn_type_eq_impl(struct drgn_type *a,
case DRGN_TYPE_ARRAY:
case DRGN_TYPE_FUNCTION:
break;
}
)
if (drgn_type_language(a) != drgn_type_language(b))
goto out_false;
@ -1212,7 +1212,7 @@ LIBDRGN_PUBLIC struct drgn_error *drgn_type_sizeof(struct drgn_type *type,
"cannot get size of incomplete %s type",
drgn_type_kind_spelling[kind]);
}
switch (kind) {
SWITCH_ENUM(kind,
case DRGN_TYPE_INT:
case DRGN_TYPE_BOOL:
case DRGN_TYPE_FLOAT:
@ -1243,8 +1243,7 @@ LIBDRGN_PUBLIC struct drgn_error *drgn_type_sizeof(struct drgn_type *type,
case DRGN_TYPE_FUNCTION:
return drgn_error_create(DRGN_ERROR_TYPE,
"cannot get size of function type");
}
UNREACHABLE();
)
}
struct drgn_error *drgn_type_bit_size(struct drgn_type *type, uint64_t *ret)
@ -1263,7 +1262,7 @@ struct drgn_error *drgn_type_bit_size(struct drgn_type *type, uint64_t *ret)
enum drgn_object_kind drgn_type_object_kind(struct drgn_type *type)
{
switch (drgn_type_kind(type)) {
SWITCH_ENUM(drgn_type_kind(type),
case DRGN_TYPE_INT:
return (drgn_type_is_signed(type) ? DRGN_OBJECT_SIGNED :
DRGN_OBJECT_UNSIGNED);
@ -1289,8 +1288,7 @@ enum drgn_object_kind drgn_type_object_kind(struct drgn_type *type)
case DRGN_TYPE_VOID:
case DRGN_TYPE_FUNCTION:
return DRGN_OBJECT_NONE;
}
UNREACHABLE();
)
}
struct drgn_error *drgn_type_error(const char *format, struct drgn_type *type)

View File

@ -29,6 +29,32 @@
#define UNREACHABLE() assert(!"reachable")
#endif
/**
* Switch statement with an enum controlling expression that must have a case
* for every enumeration value and a default case.
*/
#define SWITCH_ENUM_DEFAULT(expr, ...) { \
_Pragma("GCC diagnostic push"); \
_Pragma("GCC diagnostic error \"-Wswitch-enum\""); \
_Pragma("GCC diagnostic error \"-Wswitch-default\""); \
switch (expr) { \
__VA_ARGS__ \
} \
_Pragma("GCC diagnostic pop"); \
}
/**
* Switch statement with an enum controlling expression that must have a case
* for every enumeration value. The expression is assumed to have a valid
* enumeration value. Cases which are assumed not to be possible can be placed
* at the end of the statement.
*/
#define SWITCH_ENUM(expr, ...) \
SWITCH_ENUM_DEFAULT(expr, \
__VA_ARGS__ \
default: UNREACHABLE(); \
)
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)