diff --git a/src/file.c b/src/file.c index c924c2f..ca7de36 100644 --- a/src/file.c +++ b/src/file.c @@ -6,8 +6,8 @@ void f(enum E1 e) { switch(e) { - case A:break; - case 12:break; + case 2:break; + case 2:break; } } int main(){} diff --git a/src/lib.c b/src/lib.c index 124de44..a76db14 100644 --- a/src/lib.c +++ b/src/lib.c @@ -11449,7 +11449,7 @@ struct report int error_count; int warnings_count; int info_count; - + bool test_mode; int test_failed; int test_succeeded; @@ -11462,6 +11462,23 @@ struct report bool ignore_this_report; }; +struct switch_value +{ + long long value; + struct switch_value* next; +}; + +struct switch_value_list +{ + struct switch_value * head; + struct switch_value * tail; + struct switch_value * p_default; +}; + +void switch_value_list_push(struct switch_value_list* list, struct switch_value* pnew); +struct switch_value * switch_value_list_find(struct switch_value_list* list, long long value); + + struct parser_ctx { struct options options; @@ -11482,11 +11499,11 @@ struct parser_ctx const struct try_statement* p_current_try_statement_opt; /* - * Points to the select_stament we're in. Or null. + * Points to the selection_statement we're in. Or null. */ - const struct selection_statement * p_current_selection_statement; - + const struct selection_statement* p_current_selection_statement; + struct switch_value_list *p_switch_value_list; FILE* owner sarif_file; @@ -11502,7 +11519,7 @@ struct parser_ctx bool inside_generic_association; - + struct report* p_report; }; @@ -11578,7 +11595,7 @@ struct declaration_specifiers enum type_specifier_flags type_specifier_flags; enum type_qualifier_flags type_qualifier_flags; enum storage_class_specifier_flags storage_class_specifier_flags; - + struct attribute_specifier_sequence* owner p_attribute_specifier_sequence_opt; /*shortcuts*/ @@ -11618,7 +11635,7 @@ struct static_assert_declaration "static_debug" ( constant-expression ) ; "static_set" ( constant-expression , string-literal) ; */ - + struct token* first_token; struct token* last_token; struct expression* owner constant_expression; @@ -11811,7 +11828,7 @@ struct declaration struct token* first_token; struct token* last_token; - + struct declaration* owner next; }; void declaration_delete(struct declaration* owner opt p); @@ -11819,10 +11836,10 @@ struct declaration* owner external_declaration(struct parser_ctx* ctx); struct simple_declaration { - /* + /* This is an extension to support C++ 17 if with initialization - - simple-declaration: + + simple-declaration: declaration-specifiers init-declarator-list opt ; attribute-specifier-sequence declaration-specifiers init-declarator-list ; */ @@ -11850,14 +11867,14 @@ struct condition { struct expression* owner expression; struct attribute_specifier_sequence* owner p_attribute_specifier_sequence_opt; struct declaration_specifiers* owner p_declaration_specifiers; - + /* OBS: We must use p_init_declarator because it is kept on the scope as init_declarator when we are trying to parse init-statement or condition that are very similar */ - struct init_declarator * owner p_init_declarator; + struct init_declarator* owner p_init_declarator; struct token* first_token; struct token* last_token; @@ -11887,7 +11904,7 @@ struct atomic_type_specifier /* atomic-type-specifier: "_Atomic" ( type-name ) - */ + */ struct token* token; struct type_name* owner type_name; }; @@ -11941,6 +11958,10 @@ struct enum_specifier* owner enum_specifier(struct parser_ctx*); void enum_specifier_delete(struct enum_specifier* owner opt p); const struct enum_specifier* get_complete_enum_specifier(const struct enum_specifier* p_enum_specifier); +const struct enumerator* find_enumerator_by_value(const struct enum_specifier* p_enum_specifier, long long value); + + + struct member_declaration_list { /* @@ -12072,10 +12093,10 @@ struct declarator int num_uses; /*used to show not used warnings*/ /*user by flow analysis*/ - struct flow_object * p_object; + struct flow_object* p_object; /*final declarator type (after auto, typeof etc)*/ - struct type type; + struct type type; }; enum type_specifier_flags declarator_get_type_specifier_flags(const struct declarator* p); @@ -12478,11 +12499,11 @@ struct selection_statement selection-statement: "if" ( init-statement opt condition ) secondary-block "if" ( init-statement opt condition ) secondary-block "else" secondary-block - switch ( init-statement opt condition ) secondary-block + switch ( init-statement opt condition ) secondary-block */ struct init_statement* owner p_init_statement; struct condition* owner condition; - + struct secondary_block* owner secondary_block; struct secondary_block* owner else_secondary_block_opt; @@ -12638,7 +12659,7 @@ struct secondary_block }; void secondary_block_delete(struct secondary_block* owner opt p); -bool secondary_block_ends_with_jump(struct secondary_block * p_secondary_block); +bool secondary_block_ends_with_jump(struct secondary_block* p_secondary_block); struct unlabeled_statement { @@ -24959,6 +24980,34 @@ void scope_list_pop(struct scope_list* list) p->previous = NULL; } +void switch_value_list_push(struct switch_value_list* list, struct switch_value* pnew) +{ + if (list->head == NULL) + { + list->head = pnew; + list->tail = pnew; + } + else + { + list->tail->next = pnew; + list->tail = pnew; + } +} + +struct switch_value* switch_value_list_find(struct switch_value_list* list, long long value) +{ + struct switch_value* p = list->head; + while (p) + { + if (p->value == value) + { + return p; + } + p = p->next; + } + return NULL; +} + void parser_ctx_destroy(struct parser_ctx* obj_owner ctx) { if (ctx->sarif_file) @@ -28559,6 +28608,18 @@ struct type_specifier_qualifier* owner type_specifier_qualifier(struct parser_ct return type_specifier_qualifier; } +const struct enumerator* find_enumerator_by_value(const struct enum_specifier* p_enum_specifier, long long value) +{ + struct enumerator* p = p_enum_specifier->enumerator_list.head; + while (p) + { + if (p->value == value) + return p; + p = p->next; + } + return NULL; +} + void enum_specifier_delete(struct enum_specifier* owner opt p) { if (p) @@ -31132,10 +31193,29 @@ struct label* owner label(struct parser_ctx* ctx) p_label->constant_expression = constant_expression(ctx, true); if (parser_match_tk(ctx, ':') != 0) throw; + + long long case_value = constant_value_to_ll(&p_label->constant_expression->constant_value); + + struct switch_value* p_switch_value = switch_value_list_find(ctx->p_switch_value_list, case_value); + + if (p_switch_value) + { + compiler_diagnostic_message(W_NOT_DEFINED50, + ctx, + ctx->current, + "duplicate case value '%lld'", case_value); + } + + struct switch_value* newvalue = calloc(1, sizeof * newvalue); + if (newvalue == NULL) throw; + + newvalue->value = case_value; + switch_value_list_push(ctx->p_switch_value_list, newvalue); + if (p_label->constant_expression && - ctx->p_current_selection_statement && - ctx->p_current_selection_statement->condition && - ctx->p_current_selection_statement->condition->expression) + ctx->p_current_selection_statement && + ctx->p_current_selection_statement->condition && + ctx->p_current_selection_statement->condition->expression) { if (type_is_enum(&ctx->p_current_selection_statement->condition->expression->type)) { @@ -31145,18 +31225,43 @@ struct label* owner label(struct parser_ctx* ctx) p_label->constant_expression->first_token, p_label->constant_expression, ctx->p_current_selection_statement->condition->expression, - "switch case between enumerations of different types."); + "mismatch in enumeration types"); } else { //enum and something else... } } + + const struct enum_specifier* p_enum_specifier = + get_complete_enum_specifier(ctx->p_current_selection_statement->condition->expression->type.enum_specifier); + if (p_enum_specifier) + { + const struct enumerator* p_enumerator = find_enumerator_by_value(p_enum_specifier, case_value); + if (p_enumerator == NULL) + { + compiler_diagnostic_message(W_ENUN_CONVERSION, + ctx, + p_label->constant_expression->first_token, + "case value '%lld' not in enumerated type 'enum %s'", + case_value, + p_enum_specifier->tag_name); + } + else + { + + } + } } } else if (ctx->current->type == TK_KEYWORD_DEFAULT) { + struct switch_value* p_default = calloc(1, sizeof * p_default); + if (p_default == NULL) throw; + ctx->p_switch_value_list->p_default = p_default; + + parser_match(ctx); if (parser_match_tk(ctx, ':') != 0) throw; @@ -31686,9 +31791,45 @@ struct selection_statement* owner selection_statement(struct parser_ctx* ctx) const struct selection_statement* previous = ctx->p_current_selection_statement; ctx->p_current_selection_statement = p_selection_statement; + + struct switch_value_list* previous_switch_value_list = ctx->p_switch_value_list; + struct switch_value_list switch_value_list = { 0 }; + ctx->p_switch_value_list = &switch_value_list; + p_selection_statement->secondary_block = secondary_block(ctx); + + if (p_selection_statement->first_token->type == TK_KEYWORD_SWITCH) + { + //switch of enum without default, then we check if all items were used + if (switch_value_list.p_default == NULL) + { + const struct enum_specifier* p_enum_specifier = + get_complete_enum_specifier(ctx->p_current_selection_statement->condition->expression->type.enum_specifier); + if (p_enum_specifier) + { + struct enumerator* p = p_enum_specifier->enumerator_list.head; + while (p) + { + bool found = false; + struct switch_value* p_used = switch_value_list_find(&switch_value_list, p->value); + + if (p_used == NULL) + { + compiler_diagnostic_message(W_NOT_DEFINED50, + ctx, + ctx->current, + "enumeration value '%s' not handled in switch", p->token->lexeme); + } + p = p->next; + } + } + } + } + ctx->p_current_selection_statement = previous; + ctx->p_switch_value_list = previous_switch_value_list; + if (p_selection_statement->secondary_block == NULL) throw; @@ -32966,7 +33107,7 @@ static int create_multiple_paths(const char* root, const char* outdir) #else return -1; #endif -} + } int compile(int argc, const char** argv, struct report* report) { diff --git a/src/options.c b/src/options.c index a679732..83c324f 100644 --- a/src/options.c +++ b/src/options.c @@ -33,7 +33,7 @@ s_warnings[] = { {W_STYLE, "style"}, {W_COMMENT,"comment"}, {W_LINE_SLICING,"line-slicing"}, - + {W_SWITCH, "switch"}, {W_DISCARDED_QUALIFIERS, "discarded-qualifiers"}, {W_UNINITIALZED, "uninitialized"}, diff --git a/src/options.h b/src/options.h index b741c03..ddccc8a 100644 --- a/src/options.h +++ b/src/options.h @@ -72,7 +72,7 @@ enum diagnostic_id { W_ASSIGNMENT_OF_ARRAY_PARAMETER, W_CONDITIONAL_IS_CONSTANT, - W_NOT_DEFINED42, + W_SWITCH, W_NOT_DEFINED43, W_NOT_DEFINED44, W_NOT_DEFINED45, diff --git a/src/parser.c b/src/parser.c index 26ad76d..1a524e1 100644 --- a/src/parser.c +++ b/src/parser.c @@ -217,6 +217,45 @@ void scope_list_pop(struct scope_list* list) p->previous = NULL; } +void switch_value_destroy(struct switch_value_list* obj_owner p) +{ + struct switch_value* owner item = p->head; + while (item) + { + struct switch_value* owner next = item->next; + item->next = NULL; + free(item); + item = next; + } +} +void switch_value_list_push(struct switch_value_list* list, struct switch_value* pnew) +{ + if (list->head == NULL) + { + list->head = pnew; + list->tail = pnew; + } + else + { + list->tail->next = pnew; + list->tail = pnew; + } +} + +struct switch_value* switch_value_list_find(struct switch_value_list* list, long long value) +{ + struct switch_value* p = list->head; + while (p) + { + if (p->value == value) + { + return p; + } + p = p->next; + } + return NULL; +} + void parser_ctx_destroy(struct parser_ctx* obj_owner ctx) { if (ctx->sarif_file) @@ -6403,31 +6442,32 @@ struct label* owner label(struct parser_ctx* ctx) if (parser_match_tk(ctx, ':') != 0) throw; - long long case_value = constant_value_to_ll(&p_label->constant_expression->constant_value); + const long long case_value = constant_value_to_ll(&p_label->constant_expression->constant_value); - struct switch_value* p_switch_value = ctx->switch_value; - while (p_switch_value) + struct switch_value* p_switch_value = switch_value_list_find(ctx->p_switch_value_list, case_value); + + if (p_switch_value) { - if (p_switch_value->value == case_value) - { - //already handled - break; - } - p_switch_value = p_switch_value->next; + compiler_diagnostic_message(W_SWITCH, + ctx, + p_label->constant_expression->first_token, + "duplicate case value '%lld'", case_value); + + compiler_diagnostic_message(W_LOCATION, + ctx, + p_switch_value->p_label->constant_expression->first_token, "previous declaration"); } struct switch_value* newvalue = calloc(1, sizeof * newvalue); - if (newvalue) - { - newvalue->value = case_value; - newvalue->next = ctx->switch_value ? ctx->switch_value->next : NULL; - ctx->switch_value = newvalue; - } + if (newvalue == NULL) throw; + newvalue->p_label = p_label; + newvalue->value = case_value; + switch_value_list_push(ctx->p_switch_value_list, newvalue); if (p_label->constant_expression && - ctx->p_current_selection_statement && - ctx->p_current_selection_statement->condition && - ctx->p_current_selection_statement->condition->expression) + ctx->p_current_selection_statement && + ctx->p_current_selection_statement->condition && + ctx->p_current_selection_statement->condition->expression) { if (type_is_enum(&ctx->p_current_selection_statement->condition->expression->type)) { @@ -6469,6 +6509,11 @@ struct label* owner label(struct parser_ctx* ctx) } else if (ctx->current->type == TK_KEYWORD_DEFAULT) { + struct switch_value* p_default = calloc(1, sizeof * p_default); + if (p_default == NULL) throw; + p_default->p_label = p_label; + ctx->p_switch_value_list->p_default = p_default; + parser_match(ctx); if (parser_match_tk(ctx, ':') != 0) throw; @@ -6998,14 +7043,45 @@ struct selection_statement* owner selection_statement(struct parser_ctx* ctx) const struct selection_statement* previous = ctx->p_current_selection_statement; ctx->p_current_selection_statement = p_selection_statement; - - struct switch_value* previous_switch_value = ctx->switch_value; - + + struct switch_value_list* previous_switch_value_list = ctx->p_switch_value_list; + struct switch_value_list switch_value_list = { 0 }; + ctx->p_switch_value_list = &switch_value_list; + p_selection_statement->secondary_block = secondary_block(ctx); + + if (p_selection_statement->first_token->type == TK_KEYWORD_SWITCH) + { + //switch of enum without default, then we check if all items were used + if (switch_value_list.p_default == NULL) + { + const struct enum_specifier* p_enum_specifier = + get_complete_enum_specifier(ctx->p_current_selection_statement->condition->expression->type.enum_specifier); + if (p_enum_specifier) + { + struct enumerator* p = p_enum_specifier->enumerator_list.head; + while (p) + { + struct switch_value* p_used = switch_value_list_find(&switch_value_list, p->value); + + if (p_used == NULL) + { + compiler_diagnostic_message(W_SWITCH, + ctx, + ctx->current, + "enumeration value '%s' not handled in switch", p->token->lexeme); + } + p = p->next; + } + } + } + } + ctx->p_current_selection_statement = previous; - //ver auqias naoi foram usados - ctx->switch_value = previous_switch_value; + ctx->p_switch_value_list = previous_switch_value_list; + + switch_value_destroy(&switch_value_list); if (p_selection_statement->secondary_block == NULL) throw; @@ -7826,7 +7902,7 @@ int generate_config_file(const char* configpath) path[len - 1] = '\0'; fprintf(outfile, "#pragma dir \"%s\"\n", p); -} + } } fprintf(outfile, "\n"); @@ -8275,11 +8351,11 @@ static int create_multiple_paths(const char* root, const char* outdir) printf("error creating output folder '%s' - %s\n", temp, get_posix_error_message(er)); return er; } - } + } if (*p == '\0') break; p++; -} + } return 0; #else return -1; diff --git a/src/parser.h b/src/parser.h index a5277f3..48fc578 100644 --- a/src/parser.h +++ b/src/parser.h @@ -58,6 +58,24 @@ struct report bool ignore_this_report; }; +struct switch_value +{ + long long value; + struct label* p_label; + struct switch_value* next; +}; + +struct switch_value_list +{ + struct switch_value * head; + struct switch_value * tail; + struct switch_value * p_default; +}; + +void switch_value_destroy(struct switch_value_list* obj_owner list); +void switch_value_list_push(struct switch_value_list* list, struct switch_value* pnew); +struct switch_value * switch_value_list_find(struct switch_value_list* list, long long value); + struct parser_ctx { struct options options; @@ -78,13 +96,11 @@ struct parser_ctx const struct try_statement* p_current_try_statement_opt; /* - * Points to the select_stament we're in. Or null. + * Points to the selection_statement we're in. Or null. */ const struct selection_statement* p_current_selection_statement; - struct switch_value { long long value; struct switch_value* next; }; - struct switch_value* switch_value; - + struct switch_value_list *p_switch_value_list; FILE* owner sarif_file; diff --git a/src/tokenizer.c b/src/tokenizer.c index 76f0684..bc2ade8 100644 --- a/src/tokenizer.c +++ b/src/tokenizer.c @@ -287,7 +287,7 @@ struct include_dir* include_dir_add(struct include_dir_list* list, const char* p not ending with \, we add it */ p_new_include_dir->path = malloc(len + 2); - snprintf(p_new_include_dir->path, len + 2, "%s/", path); + snprintf((char*)p_new_include_dir->path, len + 2, "%s/", path); } else {