#include "smatch.h"
static int my_id;
static int print_unreached = 1;
static struct string_list *turn_off_names;
static struct string_list *ignore_names;
static int empty_statement(struct statement *stmt)
{
if (!stmt)
return 0;
if (stmt->type == STMT_EXPRESSION && !stmt->expression)
return 1;
return 0;
}
static int is_last_stmt(struct statement *cur_stmt)
{
struct symbol *fn = get_base_type(cur_func_sym);
struct statement *stmt;
if (!fn)
return 0;
stmt = fn->stmt;
if (!stmt)
stmt = fn->inline_stmt;
if (!stmt || stmt->type != STMT_COMPOUND)
return 0;
stmt = last_ptr_list((struct ptr_list *)stmt->stmts);
if (stmt == cur_stmt)
return 1;
return 0;
}
static void print_unreached_initializers(struct symbol_list *sym_list)
{
struct symbol *sym;
FOR_EACH_PTR(sym_list, sym) {
if (sym->initializer && !(sym->ctype.modifiers & MOD_STATIC))
sm_msg("info: '%s' is not actually initialized (unreached code).",
(sym->ident ? sym->ident->name : "this variable"));
} END_FOR_EACH_PTR(sym);
}
static int is_ignored_macro(struct statement *stmt)
{
char *name;
char *tmp;
name = get_macro_name(stmt->pos);
if (!name)
return 0;
FOR_EACH_PTR(ignore_names, tmp) {
if (strcmp(tmp, name) == 0)
return 1;
} END_FOR_EACH_PTR(tmp);
return 0;
}
static int prev_line_was_endif(struct statement *stmt)
{
struct token *token;
struct position pos = stmt->pos;
pos.line--;
pos.pos = 2;
token = pos_get_token(pos);
if (token && token_type(token) == TOKEN_IDENT &&
strcmp(show_ident(token->ident), "endif") == 0)
return 1;
pos.line--;
token = pos_get_token(pos);
if (token && token_type(token) == TOKEN_IDENT &&
strcmp(show_ident(token->ident), "endif") == 0)
return 1;
return 0;
}
static int we_jumped_into_the_middle_of_a_loop(struct statement *stmt)
{
struct statement *prev;
if (stmt->type != STMT_ITERATOR)
return 0;
prev = get_prev_statement();
if (prev && prev->type == STMT_GOTO)
return 1;
return 0;
}
static void unreachable_stmt(struct statement *stmt)
{
if (__inline_fn)
return;
if (!__path_is_null()) {
print_unreached = 1;
return;
}
if (stmt->type == STMT_LABEL)
print_unreached = 0;
if (prev_line_was_endif(stmt))
print_unreached = 0;
if (we_jumped_into_the_middle_of_a_loop(stmt))
print_unreached = 0;
if (!print_unreached)
return;
if (empty_statement(stmt))
return;
if (is_ignored_macro(stmt))
return;
switch (stmt->type) {
case STMT_COMPOUND:
case STMT_RANGE:
case STMT_CASE:
return;
case STMT_DECLARATION:
print_unreached_initializers(stmt->declaration);
return;
case STMT_RETURN:
if (is_last_stmt(stmt))
return;
break;
case STMT_GOTO:
if (stmt->goto_label && stmt->goto_label->type == SYM_NODE &&
strcmp(stmt->goto_label->ident->name, "break") == 0)
return;
break;
default:
break;
}
sm_warning("ignoring unreachable code.");
print_unreached = 0;
}
static int is_turn_off(char *name)
{
char *tmp;
if (!name)
return 0;
FOR_EACH_PTR(turn_off_names, tmp) {
if (strcmp(tmp, name) == 0)
return 1;
} END_FOR_EACH_PTR(tmp);
return 0;
}
static char *get_function_name(struct statement *stmt)
{
struct expression *expr;
if (stmt->type != STMT_EXPRESSION)
return NULL;
expr = stmt->expression;
if (!expr || expr->type != EXPR_CALL)
return NULL;
if (expr->fn->type != EXPR_SYMBOL || !expr->fn->symbol_name)
return NULL;
return expr->fn->symbol_name->name;
}
static void turn_off_unreachable(struct statement *stmt)
{
char *name;
name = get_macro_name(stmt->pos);
if (is_turn_off(name)) {
print_unreached = 0;
return;
}
if (stmt->type == STMT_IF &&
known_condition_true(stmt->if_conditional) && __path_is_null()) {
print_unreached = 0;
return;
}
name = get_function_name(stmt);
if (is_turn_off(name))
print_unreached = 0;
}
static void register_turn_off_macros(void)
{
struct token *token;
char *macro;
char name[256];
if (option_project == PROJ_NONE)
strcpy(name, "unreachable.turn_off");
else
snprintf(name, 256, "%s.unreachable.turn_off", option_project_str);
token = get_tokens_file(name);
if (!token)
return;
if (token_type(token) != TOKEN_STREAMBEGIN)
return;
token = token->next;
while (token_type(token) != TOKEN_STREAMEND) {
if (token_type(token) != TOKEN_IDENT)
return;
macro = alloc_string(show_ident(token->ident));
add_ptr_list(&turn_off_names, macro);
token = token->next;
}
clear_token_alloc();
}
static void register_ignored_macros(void)
{
struct token *token;
char *macro;
char name[256];
if (option_project == PROJ_NONE)
strcpy(name, "unreachable.ignore");
else
snprintf(name, 256, "%s.unreachable.ignore", option_project_str);
token = get_tokens_file(name);
if (!token)
return;
if (token_type(token) != TOKEN_STREAMBEGIN)
return;
token = token->next;
while (token_type(token) != TOKEN_STREAMEND) {
if (token_type(token) != TOKEN_IDENT)
return;
macro = alloc_string(show_ident(token->ident));
add_ptr_list(&ignore_names, macro);
token = token->next;
}
clear_token_alloc();
}
void check_unreachable(int id)
{
my_id = id;
register_turn_off_macros();
register_ignored_macros();
add_hook(&unreachable_stmt, STMT_HOOK);
add_hook(&turn_off_unreachable, STMT_HOOK_AFTER);
}