From bffd2d94ad072b9b6fe880ca96ce9a14d5fb4898 Mon Sep 17 00:00:00 2001 From: Martijn Reicher Date: Fri, 24 Sep 2021 11:31:44 +0200 Subject: [PATCH 1/5] Fix bug in tokenizing scoped names - The parser was not proceeding correctly in the case of the state being IDL_SCAN_ANNOTATION_APPL_SCOPED_NAME Signed-off-by: Martijn Reicher --- src/idl/src/scanner.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/idl/src/scanner.c b/src/idl/src/scanner.c index 9068d950df..35a5c1264c 100644 --- a/src/idl/src/scanner.c +++ b/src/idl/src/scanner.c @@ -753,6 +753,8 @@ tokenize( /* annotation names cannot be keywords, i.e. "@default" */ if (pstate->scanner.state == IDL_SCAN_ANNOTATION_APPL_NAME) goto identifier; + if (pstate->scanner.state == IDL_SCAN_ANNOTATION_APPL_SCOPED_NAME) + goto identifier; if (pstate->scanner.state == IDL_SCAN_ANNOTATION_NAME) goto identifier; if ((code = idl_iskeyword(pstate, str, !(pstate->flags & IDL_FLAG_CASE_SENSITIVE)))) From 2b1d67e0621eb3e8e0570910707dca4d2b04ff7e Mon Sep 17 00:00:00 2001 From: Martijn Reicher Date: Fri, 24 Sep 2021 11:33:38 +0200 Subject: [PATCH 2/5] Fix bug in evaluating bool, char and string literals - The parser was setting the node mask to IDL_ANY in stead of the correct mask for bool, char and string literals Signed-off-by: Martijn Reicher --- src/idl/src/expression.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/idl/src/expression.c b/src/idl/src/expression.c index 09f4734670..97b40c77b5 100644 --- a/src/idl/src/expression.c +++ b/src/idl/src/expression.c @@ -759,7 +759,7 @@ idl_evaluate( } } - if ((ret = idl_create_literal(pstate, idl_location(const_expr), type, nodep))) + if ((ret = idl_create_literal(pstate, idl_location(const_expr), implicit, nodep))) return ret; (*((idl_literal_t **)nodep))->value = temporary.value; done: From 0adc5f926cc27b4311616e4eb72627b1a36898db Mon Sep 17 00:00:00 2001 From: Martijn Reicher Date: Mon, 30 Aug 2021 10:28:47 +0200 Subject: [PATCH 3/5] Add @optional annotation parsing - Reject @optional annotations on anything other than members - Reject mixing @key and @optional fields - Add idl_is_optional function which checks for the @optional annotation for idl_members and idl_declarators on members - Add unittests for @optional field parsing Signed-off-by: Martijn Reicher --- src/idl/include/idl/tree.h | 1 + src/idl/src/annotation.c | 50 ++++++++++++++++++++++++++++++---- src/idl/tests/annotation.c | 56 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+), 6 deletions(-) diff --git a/src/idl/include/idl/tree.h b/src/idl/include/idl/tree.h index 87e223c512..68bc155ec8 100644 --- a/src/idl/include/idl/tree.h +++ b/src/idl/include/idl/tree.h @@ -282,6 +282,7 @@ struct idl_member { idl_declarator_t *declarators; /* metadata */ IDL_ANNOTATABLE(bool) key; + IDL_ANNOTATABLE(bool) optional; IDL_ANNOTATABLE(uint32_t) id; }; diff --git a/src/idl/src/annotation.c b/src/idl/src/annotation.c index f5f31f0ef7..720dd01c2d 100644 --- a/src/idl/src/annotation.c +++ b/src/idl/src/annotation.c @@ -129,6 +129,40 @@ annotate_autoid( return IDL_RETCODE_OK; } +static idl_retcode_t +annotate_optional( + idl_pstate_t *pstate, + idl_annotation_appl_t *annotation_appl, + idl_node_t *node) +{ + const idl_const_expr_t *const_expr; + idl_member_t *mem = (idl_member_t*)node; + bool value = true; + + assert(annotation_appl); + + if (!idl_is_member(node)) { + idl_error(pstate, idl_location(annotation_appl), + "@optional can only be assigned to members"); + return IDL_RETCODE_SEMANTIC_ERROR; + } else if (mem->key.value) { + idl_error(pstate, idl_location(annotation_appl), + "@optional cannot be assigned to key members"); + return IDL_RETCODE_SEMANTIC_ERROR; + } + + if (annotation_appl->parameters) { + const_expr = annotation_appl->parameters->const_expr; + assert(const_expr); + value = ((const idl_literal_t*)const_expr)->value.bln; + } + + mem->optional.annotation = annotation_appl; + mem->optional.value = value; + + return IDL_RETCODE_OK; +} + static idl_retcode_t annotate_value( idl_pstate_t *pstate, @@ -250,8 +284,13 @@ annotate_key( } if (idl_mask(node) & IDL_MEMBER) { - ((idl_member_t *)node)->key.annotation = annotation_appl; - ((idl_member_t *)node)->key.value = key; + if (((idl_member_t*)node)->optional.value) { + idl_error(pstate, idl_location(annotation_appl), + "@key cannot be applied to optional members"); + return IDL_RETCODE_SEMANTIC_ERROR; + } + ((idl_member_t*)node)->key.annotation = annotation_appl; + ((idl_member_t*)node)->key.value = key; } else if (idl_mask(node) & IDL_SWITCH_TYPE_SPEC) { ((idl_switch_type_spec_t *)node)->key.annotation = annotation_appl; ((idl_switch_type_spec_t *)node)->key.value = key; @@ -430,12 +469,11 @@ static const idl_builtin_annotation_t annotations[] = { "instructs to automatically allocate identifiers to elements that have " "not been assigned a 32-bit unsigned identifiers explicitly.

", .callback = &annotate_autoid }, -#if 0 { .syntax = "@annotation optional { boolean value default TRUE; };", .summary = - "

Set optionality on any element that makes sense to be optional.

", - .callback = annotate_optional }, -#endif + "

Indicates that the annotated member may be in a NULL state, not" + "containing any value.

", + .callback = &annotate_optional }, { .syntax = "@annotation value { any value; };", .summary = "

Set a constant value to any element that may be given a constant " diff --git a/src/idl/tests/annotation.c b/src/idl/tests/annotation.c index c3f377e4ed..90b7ba4928 100644 --- a/src/idl/tests/annotation.c +++ b/src/idl/tests/annotation.c @@ -45,6 +45,62 @@ parse_string(uint32_t flags, const char *str, idl_pstate_t **pstatep) return ret; } +typedef struct optional_test { + const char *str; + idl_retcode_t ret; + bool optionals[16]; +} optional_test_t; + +static void test_optional(optional_test_t test) +{ + idl_pstate_t *pstate = NULL; + idl_retcode_t ret = parse_string(IDL_FLAG_ANNOTATIONS, test.str, &pstate); + CU_ASSERT_EQUAL_FATAL(ret, test.ret); + + if (pstate) { + idl_node_t *node; + int nstructs = 0; + IDL_FOREACH(node, pstate->root) { + if (!idl_is_struct(node)) + continue; + nstructs++; + idl_struct_t *s = (idl_struct_t *)node; + CU_ASSERT_PTR_NOT_NULL_FATAL(s); + CU_ASSERT_FATAL(idl_is_struct(s)); + assert(s); + idl_member_t *m = NULL; + int n = 0; + IDL_FOREACH(m, s->members) { + CU_ASSERT_EQUAL(m->optional.value, test.optionals[n]); + n++; + } + } + CU_ASSERT_EQUAL(nstructs, 1); + idl_delete_pstate(pstate); + } +} + +CU_Test(idl_annotation, optional) +{ + static const optional_test_t tests[] = { + {"struct s { char c; };", IDL_RETCODE_OK, {false} }, //default (not optional) + {"struct s { @optional char c; };", IDL_RETCODE_OK, {true} }, //implicit true + {"struct s { @optional(false) char c; };", IDL_RETCODE_OK, {false} }, //explicit false + {"struct s { @optional(true) char c; };", IDL_RETCODE_OK, {true} }, //explicit true + {"struct s { @optional(true) char c_1, c_2; char c_3; };", IDL_RETCODE_OK, {true, false} }, //set on both declarators + {"struct s { @optional sequence s_d; };", IDL_RETCODE_OK, {true} }, //set on sequence + {"typedef sequence seq_long;\n" + "struct s { @optional seq_long s_l_a[15]; };", IDL_RETCODE_OK, {true} }, //set on typedef + {"struct s { @optional @key char c; };", IDL_RETCODE_SEMANTIC_ERROR, {0} }, //optional not allowed on key members + {"@optional struct s { char c; };", IDL_RETCODE_SEMANTIC_ERROR, {0} }, //only allowed on member declarators + {"enum e { e_0, @optional e_1};", IDL_RETCODE_SEMANTIC_ERROR, {0} } //only allowed on members + }; + + for (size_t i = 0; i < sizeof(tests)/sizeof(tests[0]); i++) { + test_optional(tests[i]); + } +} + CU_Test(idl_annotation, key) { idl_retcode_t ret; From 9c17082500c1d84fc6a56dc6bd9eaf60db3d1166 Mon Sep 17 00:00:00 2001 From: Martijn Reicher Date: Thu, 2 Sep 2021 16:34:15 +0200 Subject: [PATCH 4/5] Add @default parsing - Add parsing of @default annotation fields - Reject mixing @default and @optional fields - Add idl_has_default function returns a pointer to the idl_literal_t representing the default value assigned to the node, or NULL if there is none - Add unittests for @default annotation Signed-off-by: Martijn Reicher --- src/idl/include/idl/tree.h | 2 + src/idl/src/annotation.c | 58 +++++++++++++++++++++++-- src/idl/src/tree.c | 13 ++++++ src/idl/tests/annotation.c | 86 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 155 insertions(+), 4 deletions(-) diff --git a/src/idl/include/idl/tree.h b/src/idl/include/idl/tree.h index 68bc155ec8..f6ba0d14c9 100644 --- a/src/idl/include/idl/tree.h +++ b/src/idl/include/idl/tree.h @@ -283,6 +283,7 @@ struct idl_member { /* metadata */ IDL_ANNOTATABLE(bool) key; IDL_ANNOTATABLE(bool) optional; + IDL_ANNOTATABLE(const idl_literal_t*) value; IDL_ANNOTATABLE(uint32_t) id; }; @@ -482,6 +483,7 @@ IDL_EXPORT bool idl_identifier_is(const void *node, const char *identifier); IDL_EXPORT const idl_name_t *idl_name(const void *node); IDL_EXPORT uint32_t idl_array_size(const void *node); IDL_EXPORT uint32_t idl_bound(const void *node); +IDL_EXPORT const idl_literal_t *idl_default_value(const void *node); /* navigation */ IDL_EXPORT void *idl_ancestor(const void *node, size_t levels); diff --git a/src/idl/src/annotation.c b/src/idl/src/annotation.c index 720dd01c2d..de6b903b55 100644 --- a/src/idl/src/annotation.c +++ b/src/idl/src/annotation.c @@ -149,6 +149,10 @@ annotate_optional( idl_error(pstate, idl_location(annotation_appl), "@optional cannot be assigned to key members"); return IDL_RETCODE_SEMANTIC_ERROR; + } else if (mem->value.annotation) { + idl_error(pstate, idl_location(annotation_appl), + "@optional cannot be assigned to members with explicit default values"); + return IDL_RETCODE_SEMANTIC_ERROR; } if (annotation_appl->parameters) { @@ -345,6 +349,51 @@ set_nested( return IDL_RETCODE_OK; } +static idl_retcode_t +annotate_default( + idl_pstate_t *pstate, + idl_annotation_appl_t *annotation_appl, + idl_node_t *node) +{ + idl_const_expr_t *value; + idl_member_t *mem = (idl_member_t*)node; + idl_type_spec_t *mem_spec = mem->type_spec; + + assert(annotation_appl); + assert(annotation_appl->parameters); + + if (!idl_is_member(node)) { + idl_error(pstate, idl_location(annotation_appl), + "@default can only be assigned to members"); + return IDL_RETCODE_SEMANTIC_ERROR; + } else if (mem->optional.value) { + idl_error(pstate, idl_location(annotation_appl), + "@default cannot be set on optional members"); + return IDL_RETCODE_SEMANTIC_ERROR; + } + + value = annotation_appl->parameters->const_expr; + assert(idl_is_literal(value)); + + /*check whether type of literal matches and falls inside spec of member*/ + idl_type_t mem_type = idl_type(mem_spec); + if (mem_type != idl_type(value)) { + idl_retcode_t ret = IDL_RETCODE_OK; + idl_literal_t *literal = NULL; + if ((ret = idl_evaluate(pstate, value, mem_type, &literal))) + return ret; + + assert(literal); + annotation_appl->parameters->const_expr = literal; + literal->node.parent = (idl_node_t*)annotation_appl->parameters; + } + + ((idl_member_t *)node)->value.annotation = annotation_appl; + ((idl_member_t *)node)->value.value = annotation_appl->parameters->const_expr; + + return IDL_RETCODE_OK; +} + static idl_retcode_t annotate_nested( idl_pstate_t *pstate, @@ -504,6 +553,11 @@ static const idl_builtin_annotation_t annotations[] = { "

Specify a data member is part of the key for the object whose type " "is the constructed data type owning this element.

", .callback = annotate_key }, + { .syntax = "@annotation default { any value; };", + .summary = + "

Specify the value with which the annotated member should be default" + "initialized.

", + .callback = &annotate_default }, #if 0 { .syntax = "@annotation must_understand { boolean value default TRUE; };", .summary = @@ -511,10 +565,6 @@ static const idl_builtin_annotation_t annotations[] = { "making use of that piece of data.

", .callback = annotate_must_understand }, /* units and ranges */ - { .syntax = "@annotation default { any value; };", - .summary = - "

Specify a default value for the annotated element.

", - .callback = annotate_default }, { .syntax = "@annotation range { any min; any max; };", .summary = "

Specify a range of allowed value for the annotated element.

", diff --git a/src/idl/src/tree.c b/src/idl/src/tree.c index 6954ec838e..3bb7135a03 100644 --- a/src/idl/src/tree.c +++ b/src/idl/src/tree.c @@ -784,6 +784,19 @@ uint32_t idl_bound(const void *node) return 0u; } +const idl_literal_t *idl_default_value(const void *node) +{ + if (idl_is_member(node)) { + return ((const idl_member_t*)node)->value.value; + } else if (idl_is_declarator(node)) { + const idl_node_t *parent = idl_parent(node); + if (idl_is_member(parent)) + return idl_default_value(parent); + } + + return NULL; +} + bool idl_is_sequence(const void *ptr) { idl_mask_t mask; diff --git a/src/idl/tests/annotation.c b/src/idl/tests/annotation.c index 90b7ba4928..3bf916e3e2 100644 --- a/src/idl/tests/annotation.c +++ b/src/idl/tests/annotation.c @@ -101,6 +101,92 @@ CU_Test(idl_annotation, optional) } } +typedef struct idl_default_test { + const char *str; + idl_retcode_t ret; + bool has_default; + idl_type_t default_type; + const void *default_val_ptr; +} idl_default_test_t; + +static void test_default( + idl_default_test_t test) +{ + idl_pstate_t *pstate = NULL; + idl_retcode_t ret = parse_string(IDL_FLAG_ANNOTATIONS, test.str, &pstate); + CU_ASSERT_EQUAL_FATAL(ret, test.ret); + if (pstate) { + idl_struct_t *s = (idl_struct_t *)pstate->root; + CU_ASSERT_FATAL(idl_is_struct(s)); + assert(s); + idl_member_t *m = NULL; + IDL_FOREACH(m, s->members) { + const idl_literal_t *def = idl_default_value(m); + if (test.has_default) { + CU_ASSERT_EQUAL_FATAL(idl_type(def), test.default_type); + switch (test.default_type) { + case IDL_LONG: + CU_ASSERT_EQUAL(def->value.int32, *(const int32_t*)test.default_val_ptr); + break; + case IDL_ULONG: + CU_ASSERT_EQUAL(def->value.uint32, *(const uint32_t*)test.default_val_ptr); + break; + case IDL_DOUBLE: + CU_ASSERT_EQUAL(def->value.dbl, *(const double*)test.default_val_ptr); + break; + case IDL_CHAR: + CU_ASSERT_EQUAL(def->value.chr, *(const char*)test.default_val_ptr); + break; + case IDL_STRING: + CU_ASSERT_STRING_EQUAL(def->value.str, *(const char**)test.default_val_ptr); + break; + case IDL_BOOL: + CU_ASSERT_EQUAL(def->value.bln, *(const bool*)test.default_val_ptr); + break; + default: + break; + } + } else { + CU_ASSERT_PTR_NULL_FATAL(def); + } + } + idl_delete_pstate(pstate); + } +} + +CU_Test(idl_annotation, idl_default) +{ + static const int32_t t1 = -123456789; + static const double t2 = 987.654321; + static const char t3 = 'a'; + static const bool t4 = true; + static const char *t5 = "hello world!"; + static const uint32_t t6 = 123456789; + static const idl_default_test_t tests[] = { + {"struct s { long l; };", IDL_RETCODE_OK, false, IDL_NULL, NULL}, //no default whatsoever + {"struct s { @default(-123456789) long l; };", IDL_RETCODE_OK, true, IDL_LONG, &t1}, //default long + {"struct s { @default(987.654321) double d; };", IDL_RETCODE_OK, true, IDL_DOUBLE, &t2}, //default double + {"struct s { @default('a') char c; };", IDL_RETCODE_OK, true, IDL_CHAR, &t3}, //default char + {"struct s { @default(true) boolean b; };", IDL_RETCODE_OK, true, IDL_BOOL, &t4}, //default bool + {"struct s { @default(\"hello world!\") string str; };", IDL_RETCODE_OK, true, IDL_STRING, &t5}, //default string + {"struct s { @default(123456789) unsigned long l; };", IDL_RETCODE_OK, true, IDL_ULONG, &t6}, //default unsigned long + {"struct s { @default(123) @optional long l; };", IDL_RETCODE_SEMANTIC_ERROR, false, IDL_NULL, NULL}, //mixing default and optional + {"struct s { @default long l; };", IDL_RETCODE_SEMANTIC_ERROR, false, IDL_NULL, NULL}, //misssing parameter + {"struct s { @default(123) string str; };", IDL_RETCODE_ILLEGAL_EXPRESSION, false, IDL_NULL, NULL}, //parameter type mismatch (int vs string) + {"struct s { @default(\"false\") boolean b; };", IDL_RETCODE_ILLEGAL_EXPRESSION, false, IDL_NULL, NULL}, //parameter type mismatch (string vs bool) + {"struct s { @default(123) boolean b; };", IDL_RETCODE_ILLEGAL_EXPRESSION, false, IDL_NULL, NULL}, //parameter type mismatch (int vs bool) + {"struct s { @default(-123) unsigned long l; };", IDL_RETCODE_OUT_OF_RANGE, false, IDL_NULL, NULL}, //parameter type mismatch (unsigned vs signed) + /* skipping this test as idl_create_annotation_appl leaks memory if idl_resolve cannot resolve the scoped name (https://github.com/eclipse-cyclonedds/cyclonedds/issues/950) + {"@default(e_0) enum e { e_0, e_1, e_2, e_3 };", IDL_RETCODE_SEMANTIC_ERROR, false, IDL_NULL, NULL} //setting default on enums is done through @default_literal + */ + }; + + for (size_t i = 0; i < sizeof(tests)/sizeof(tests[0]); i++) { + test_default(tests[i]); + } + +} + CU_Test(idl_annotation, key) { idl_retcode_t ret; From 010289ea1db92dbe894d3733e43c3dc3cafcc4ad Mon Sep 17 00:00:00 2001 From: Martijn Reicher Date: Thu, 9 Sep 2021 13:39:51 +0200 Subject: [PATCH 5/5] Add explicit enumerator defaults - Set trough the @default_literal annotation on enumerators - Add the default_enumerator pointer in idl_enum, which is set to the enumerator with @default_literal annotation or the first if none have the annotation - Add unittests Signed-off-by: Martijn Reicher --- src/idl/include/idl/tree.h | 3 ++ src/idl/src/annotation.c | 24 ++++++++++++++++ src/idl/src/tree.c | 22 ++++++++++++++- src/idl/tests/annotation.c | 57 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 105 insertions(+), 1 deletion(-) diff --git a/src/idl/include/idl/tree.h b/src/idl/include/idl/tree.h index f6ba0d14c9..272dcc29a2 100644 --- a/src/idl/include/idl/tree.h +++ b/src/idl/include/idl/tree.h @@ -61,6 +61,8 @@ /* if no explicit default is specified and range is not covered */ #define IDL_IMPLICIT_DEFAULT_CASE_LABEL (IDL_DEFAULT_CASE_LABEL | 2u) #define IDL_ENUMERATOR (1llu<<26) +#define IDL_DEFAULT_ENUMERATOR (IDL_ENUMERATOR | 1u) +#define IDL_IMPLICIT_DEFAULT_ENUMERATOR (IDL_DEFAULT_ENUMERATOR | 2u) #define IDL_DECLARATOR (1llu<<25) /* annotations */ #define IDL_ANNOTATION (1llu<<24) @@ -387,6 +389,7 @@ struct idl_enum { idl_node_t node; struct idl_name *name; idl_enumerator_t *enumerators; + idl_enumerator_t *default_enumerator; IDL_ANNOTATABLE(idl_extensibility_t) extensibility; }; diff --git a/src/idl/src/annotation.c b/src/idl/src/annotation.c index de6b903b55..431f5855f4 100644 --- a/src/idl/src/annotation.c +++ b/src/idl/src/annotation.c @@ -394,6 +394,25 @@ annotate_default( return IDL_RETCODE_OK; } +static idl_retcode_t +annotate_default_literal( + idl_pstate_t *pstate, + idl_annotation_appl_t *annotation_appl, + idl_node_t *node) +{ + assert(pstate); + + if (!idl_is_enumerator(node)) { + idl_error(pstate, idl_location(annotation_appl), + "@default_literal can only be assigned to enumerators"); + return IDL_RETCODE_SEMANTIC_ERROR; + } + + node->mask |= IDL_DEFAULT_ENUMERATOR; + + return IDL_RETCODE_OK; +} + static idl_retcode_t annotate_nested( idl_pstate_t *pstate, @@ -558,6 +577,11 @@ static const idl_builtin_annotation_t annotations[] = { "

Specify the value with which the annotated member should be default" "initialized.

", .callback = &annotate_default }, + { .syntax = "@annotation default_literal { };", + .summary = + "

Explicity sets the default value for an enum to the annotated enumerator" + "instead of the first entry.

", + .callback = &annotate_default_literal }, #if 0 { .syntax = "@annotation must_understand { boolean value default TRUE; };", .summary = diff --git a/src/idl/src/tree.c b/src/idl/src/tree.c index 3bb7135a03..dd2fb46342 100644 --- a/src/idl/src/tree.c +++ b/src/idl/src/tree.c @@ -2029,6 +2029,23 @@ idl_create_enum( ret = IDL_RETCODE_SEMANTIC_ERROR; goto err_clash; } + if (idl_mask(e1) == IDL_DEFAULT_ENUMERATOR) { + if (node->default_enumerator) { + idl_error(pstate, idl_location(e1), + "Assigning default to enumerator '%s' clashes with '%s' already being set as default.", + e1->name->identifier, node->default_enumerator->name->identifier); + ret = IDL_RETCODE_SEMANTIC_ERROR; + goto err_defaults; + } else { + node->default_enumerator = e1; + } + } + } + + //fallback to the first entry + if (!node->default_enumerator) { + node->default_enumerator = enumerators; + node->default_enumerator->node.mask = IDL_IMPLICIT_DEFAULT_ENUMERATOR; } if ((ret = idl_declare(pstate, kind, name, node, NULL, NULL))) @@ -2038,6 +2055,7 @@ idl_create_enum( return IDL_RETCODE_OK; err_declare: err_clash: +err_defaults: free(node); err_alloc: return ret; @@ -2075,7 +2093,9 @@ static void *iterate_enumerator(const void *ptr, const void *cur) static const char *describe_enumerator(const void *ptr) { (void)ptr; - assert(idl_mask(ptr) == IDL_ENUMERATOR); + assert(idl_mask(ptr) == IDL_ENUMERATOR + || idl_mask(ptr) == IDL_DEFAULT_ENUMERATOR + || idl_mask(ptr) == IDL_IMPLICIT_DEFAULT_ENUMERATOR); return "enumerator"; } diff --git a/src/idl/tests/annotation.c b/src/idl/tests/annotation.c index 3bf916e3e2..f096b95c03 100644 --- a/src/idl/tests/annotation.c +++ b/src/idl/tests/annotation.c @@ -187,6 +187,63 @@ CU_Test(idl_annotation, idl_default) } +typedef struct enum_default_test { + const char *str; + idl_retcode_t ret; + uint32_t default_index; + const char *default_name; + uint32_t default_mask; +} enum_default_test_t; + +static void test_enum_default(enum_default_test_t test) { + idl_pstate_t *pstate = NULL; + idl_retcode_t ret = parse_string(IDL_FLAG_ANNOTATIONS, test.str, &pstate); + CU_ASSERT_EQUAL(ret, test.ret); + if (test.ret == ret + && ret == IDL_RETCODE_OK) { + CU_ASSERT_PTR_NOT_NULL_FATAL(pstate); + assert(pstate); + idl_enum_t *e = (idl_enum_t *)pstate->root; + CU_ASSERT(idl_is_enum(e)); + if (idl_is_enum(e)) { + assert(e); + idl_enumerator_t *en = e->default_enumerator; + CU_ASSERT_PTR_NOT_NULL_FATAL(en); + if (en) { + CU_ASSERT_EQUAL(en->value.value, test.default_index); + CU_ASSERT_EQUAL(idl_mask(en), test.default_mask); + CU_ASSERT_STRING_EQUAL(en->name->identifier, test.default_name); + } + + IDL_FOREACH(en, e->enumerators) { + if (en == e->default_enumerator) { + CU_ASSERT_EQUAL(idl_mask(en), test.default_mask); + } else { + CU_ASSERT_EQUAL(idl_mask(en), IDL_ENUMERATOR); + } + } + } + + idl_delete_pstate(pstate); + } +} + +CU_Test(idl_annotation, default_literal) +{ + static const enum_default_test_t tests[] = { + {"enum e { e_0, e_1, e_2, e_3 };", IDL_RETCODE_OK, 0, "e_0", IDL_IMPLICIT_DEFAULT_ENUMERATOR }, //implicit defaults to the first entry + {"enum e { e_0, @default_literal e_1, e_2, e_3 };", IDL_RETCODE_OK, 1, "e_1", IDL_DEFAULT_ENUMERATOR }, //setting an explicit default through the annotation + {"enum e { @value(123) e_0, @default_literal e_1, e_2, e_3 };", IDL_RETCODE_OK, 124, "e_1", IDL_DEFAULT_ENUMERATOR }, //starting at a different id + {"enum e { e_0, e_1, @default_literal @default_literal e_2, e_3 };", IDL_RETCODE_OK, 2, "e_2", IDL_DEFAULT_ENUMERATOR }, //re-annotate + {"enum e { e_0, @default_literal e_1, @default_literal e_2, e_3 };", IDL_RETCODE_SEMANTIC_ERROR, 0, NULL, 0 }, //more than one enumerator set as default + {"struct s { @default_literal long l; double d; };\n", IDL_RETCODE_SEMANTIC_ERROR, 0, NULL, 0 }, //this annotation is only allowed on enumerator fields + }; + + for (size_t i = 0; i < sizeof(tests)/sizeof(tests[0]); i++) { + test_enum_default(tests[i]); + } +} + CU_Test(idl_annotation, key) { idl_retcode_t ret;