11f5207b7SJohn Levon /*
21f5207b7SJohn Levon * Copyright (C) 2014 Oracle.
31f5207b7SJohn Levon *
41f5207b7SJohn Levon * This program is free software; you can redistribute it and/or
51f5207b7SJohn Levon * modify it under the terms of the GNU General Public License
61f5207b7SJohn Levon * as published by the Free Software Foundation; either version 2
71f5207b7SJohn Levon * of the License, or (at your option) any later version.
81f5207b7SJohn Levon *
91f5207b7SJohn Levon * This program is distributed in the hope that it will be useful,
101f5207b7SJohn Levon * but WITHOUT ANY WARRANTY; without even the implied warranty of
111f5207b7SJohn Levon * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
121f5207b7SJohn Levon * GNU General Public License for more details.
131f5207b7SJohn Levon *
141f5207b7SJohn Levon * You should have received a copy of the GNU General Public License
151f5207b7SJohn Levon * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt
161f5207b7SJohn Levon */
171f5207b7SJohn Levon
181f5207b7SJohn Levon #include "smatch.h"
191f5207b7SJohn Levon
201f5207b7SJohn Levon static int my_id;
211f5207b7SJohn Levon
221f5207b7SJohn Levon static int print_unreached = 1;
231f5207b7SJohn Levon static struct string_list *turn_off_names;
241f5207b7SJohn Levon static struct string_list *ignore_names;
251f5207b7SJohn Levon
empty_statement(struct statement * stmt)261f5207b7SJohn Levon static int empty_statement(struct statement *stmt)
271f5207b7SJohn Levon {
281f5207b7SJohn Levon if (!stmt)
291f5207b7SJohn Levon return 0;
301f5207b7SJohn Levon if (stmt->type == STMT_EXPRESSION && !stmt->expression)
311f5207b7SJohn Levon return 1;
321f5207b7SJohn Levon return 0;
331f5207b7SJohn Levon }
341f5207b7SJohn Levon
is_last_stmt(struct statement * cur_stmt)351f5207b7SJohn Levon static int is_last_stmt(struct statement *cur_stmt)
361f5207b7SJohn Levon {
371f5207b7SJohn Levon struct symbol *fn = get_base_type(cur_func_sym);
381f5207b7SJohn Levon struct statement *stmt;
391f5207b7SJohn Levon
401f5207b7SJohn Levon if (!fn)
411f5207b7SJohn Levon return 0;
421f5207b7SJohn Levon stmt = fn->stmt;
431f5207b7SJohn Levon if (!stmt)
441f5207b7SJohn Levon stmt = fn->inline_stmt;
451f5207b7SJohn Levon if (!stmt || stmt->type != STMT_COMPOUND)
461f5207b7SJohn Levon return 0;
471f5207b7SJohn Levon stmt = last_ptr_list((struct ptr_list *)stmt->stmts);
481f5207b7SJohn Levon if (stmt == cur_stmt)
491f5207b7SJohn Levon return 1;
501f5207b7SJohn Levon return 0;
511f5207b7SJohn Levon }
521f5207b7SJohn Levon
print_unreached_initializers(struct symbol_list * sym_list)531f5207b7SJohn Levon static void print_unreached_initializers(struct symbol_list *sym_list)
541f5207b7SJohn Levon {
551f5207b7SJohn Levon struct symbol *sym;
561f5207b7SJohn Levon
571f5207b7SJohn Levon FOR_EACH_PTR(sym_list, sym) {
581f5207b7SJohn Levon if (sym->initializer && !(sym->ctype.modifiers & MOD_STATIC))
591f5207b7SJohn Levon sm_msg("info: '%s' is not actually initialized (unreached code).",
601f5207b7SJohn Levon (sym->ident ? sym->ident->name : "this variable"));
611f5207b7SJohn Levon } END_FOR_EACH_PTR(sym);
621f5207b7SJohn Levon }
631f5207b7SJohn Levon
is_ignored_macro(struct statement * stmt)641f5207b7SJohn Levon static int is_ignored_macro(struct statement *stmt)
651f5207b7SJohn Levon {
661f5207b7SJohn Levon char *name;
671f5207b7SJohn Levon char *tmp;
681f5207b7SJohn Levon
691f5207b7SJohn Levon name = get_macro_name(stmt->pos);
701f5207b7SJohn Levon if (!name)
711f5207b7SJohn Levon return 0;
721f5207b7SJohn Levon
731f5207b7SJohn Levon FOR_EACH_PTR(ignore_names, tmp) {
741f5207b7SJohn Levon if (strcmp(tmp, name) == 0)
751f5207b7SJohn Levon return 1;
761f5207b7SJohn Levon } END_FOR_EACH_PTR(tmp);
771f5207b7SJohn Levon
781f5207b7SJohn Levon return 0;
791f5207b7SJohn Levon }
801f5207b7SJohn Levon
prev_line_was_endif(struct statement * stmt)811f5207b7SJohn Levon static int prev_line_was_endif(struct statement *stmt)
821f5207b7SJohn Levon {
831f5207b7SJohn Levon struct token *token;
841f5207b7SJohn Levon struct position pos = stmt->pos;
851f5207b7SJohn Levon
861f5207b7SJohn Levon pos.line--;
871f5207b7SJohn Levon pos.pos = 2;
881f5207b7SJohn Levon
891f5207b7SJohn Levon token = pos_get_token(pos);
901f5207b7SJohn Levon if (token && token_type(token) == TOKEN_IDENT &&
911f5207b7SJohn Levon strcmp(show_ident(token->ident), "endif") == 0)
921f5207b7SJohn Levon return 1;
931f5207b7SJohn Levon
941f5207b7SJohn Levon pos.line--;
951f5207b7SJohn Levon token = pos_get_token(pos);
961f5207b7SJohn Levon if (token && token_type(token) == TOKEN_IDENT &&
971f5207b7SJohn Levon strcmp(show_ident(token->ident), "endif") == 0)
981f5207b7SJohn Levon return 1;
991f5207b7SJohn Levon
1001f5207b7SJohn Levon return 0;
1011f5207b7SJohn Levon }
1021f5207b7SJohn Levon
we_jumped_into_the_middle_of_a_loop(struct statement * stmt)1031f5207b7SJohn Levon static int we_jumped_into_the_middle_of_a_loop(struct statement *stmt)
1041f5207b7SJohn Levon {
1051f5207b7SJohn Levon struct statement *prev;
1061f5207b7SJohn Levon
1071f5207b7SJohn Levon /*
1081f5207b7SJohn Levon * Smatch doesn't handle loops correctly and this is a hack. What we
1091f5207b7SJohn Levon * do is that if the first unreachable statement is a loop and the
1101f5207b7SJohn Levon * previous statement was a goto then it's probably code like this:
1111f5207b7SJohn Levon * goto first;
1121f5207b7SJohn Levon * for (;;) {
1131f5207b7SJohn Levon * frob();
1141f5207b7SJohn Levon * first:
1151f5207b7SJohn Levon * more_frob();
1161f5207b7SJohn Levon * }
1171f5207b7SJohn Levon * Every statement is reachable but only on the second iteration.
1181f5207b7SJohn Levon */
1191f5207b7SJohn Levon
1201f5207b7SJohn Levon if (stmt->type != STMT_ITERATOR)
1211f5207b7SJohn Levon return 0;
1221f5207b7SJohn Levon prev = get_prev_statement();
1231f5207b7SJohn Levon if (prev && prev->type == STMT_GOTO)
1241f5207b7SJohn Levon return 1;
1251f5207b7SJohn Levon return 0;
1261f5207b7SJohn Levon }
1271f5207b7SJohn Levon
unreachable_stmt(struct statement * stmt)1281f5207b7SJohn Levon static void unreachable_stmt(struct statement *stmt)
1291f5207b7SJohn Levon {
1301f5207b7SJohn Levon
1311f5207b7SJohn Levon if (__inline_fn)
1321f5207b7SJohn Levon return;
1331f5207b7SJohn Levon
1341f5207b7SJohn Levon if (!__path_is_null()) {
1351f5207b7SJohn Levon print_unreached = 1;
1361f5207b7SJohn Levon return;
1371f5207b7SJohn Levon }
1381f5207b7SJohn Levon
1391f5207b7SJohn Levon /* if we hit a label then assume there is a matching goto */
1401f5207b7SJohn Levon if (stmt->type == STMT_LABEL)
1411f5207b7SJohn Levon print_unreached = 0;
1421f5207b7SJohn Levon if (prev_line_was_endif(stmt))
1431f5207b7SJohn Levon print_unreached = 0;
1441f5207b7SJohn Levon if (we_jumped_into_the_middle_of_a_loop(stmt))
1451f5207b7SJohn Levon print_unreached = 0;
1461f5207b7SJohn Levon
1471f5207b7SJohn Levon if (!print_unreached)
1481f5207b7SJohn Levon return;
1491f5207b7SJohn Levon if (empty_statement(stmt))
1501f5207b7SJohn Levon return;
1511f5207b7SJohn Levon if (is_ignored_macro(stmt))
1521f5207b7SJohn Levon return;
1531f5207b7SJohn Levon
1541f5207b7SJohn Levon switch (stmt->type) {
1551f5207b7SJohn Levon case STMT_COMPOUND: /* after a switch before a case stmt */
1561f5207b7SJohn Levon case STMT_RANGE:
1571f5207b7SJohn Levon case STMT_CASE:
1581f5207b7SJohn Levon return;
1591f5207b7SJohn Levon case STMT_DECLARATION: /* switch (x) { int a; case foo: ... */
1601f5207b7SJohn Levon print_unreached_initializers(stmt->declaration);
1611f5207b7SJohn Levon return;
1621f5207b7SJohn Levon case STMT_RETURN: /* gcc complains if you don't have a return statement */
1631f5207b7SJohn Levon if (is_last_stmt(stmt))
1641f5207b7SJohn Levon return;
1651f5207b7SJohn Levon break;
1661f5207b7SJohn Levon case STMT_GOTO:
1671f5207b7SJohn Levon /* people put extra breaks inside switch statements */
1681f5207b7SJohn Levon if (stmt->goto_label && stmt->goto_label->type == SYM_NODE &&
1691f5207b7SJohn Levon strcmp(stmt->goto_label->ident->name, "break") == 0)
1701f5207b7SJohn Levon return;
1711f5207b7SJohn Levon break;
1721f5207b7SJohn Levon default:
1731f5207b7SJohn Levon break;
1741f5207b7SJohn Levon }
175*44bf619dSJohn Levon sm_warning("ignoring unreachable code.");
1761f5207b7SJohn Levon print_unreached = 0;
1771f5207b7SJohn Levon }
1781f5207b7SJohn Levon
is_turn_off(char * name)1791f5207b7SJohn Levon static int is_turn_off(char *name)
1801f5207b7SJohn Levon {
1811f5207b7SJohn Levon char *tmp;
1821f5207b7SJohn Levon
1831f5207b7SJohn Levon if (!name)
1841f5207b7SJohn Levon return 0;
1851f5207b7SJohn Levon
1861f5207b7SJohn Levon FOR_EACH_PTR(turn_off_names, tmp) {
1871f5207b7SJohn Levon if (strcmp(tmp, name) == 0)
1881f5207b7SJohn Levon return 1;
1891f5207b7SJohn Levon } END_FOR_EACH_PTR(tmp);
1901f5207b7SJohn Levon
1911f5207b7SJohn Levon return 0;
1921f5207b7SJohn Levon }
1931f5207b7SJohn Levon
get_function_name(struct statement * stmt)1941f5207b7SJohn Levon static char *get_function_name(struct statement *stmt)
1951f5207b7SJohn Levon {
1961f5207b7SJohn Levon struct expression *expr;
1971f5207b7SJohn Levon
1981f5207b7SJohn Levon if (stmt->type != STMT_EXPRESSION)
1991f5207b7SJohn Levon return NULL;
2001f5207b7SJohn Levon expr = stmt->expression;
2011f5207b7SJohn Levon if (!expr || expr->type != EXPR_CALL)
2021f5207b7SJohn Levon return NULL;
2031f5207b7SJohn Levon if (expr->fn->type != EXPR_SYMBOL || !expr->fn->symbol_name)
2041f5207b7SJohn Levon return NULL;
2051f5207b7SJohn Levon return expr->fn->symbol_name->name;
2061f5207b7SJohn Levon }
2071f5207b7SJohn Levon
turn_off_unreachable(struct statement * stmt)2081f5207b7SJohn Levon static void turn_off_unreachable(struct statement *stmt)
2091f5207b7SJohn Levon {
2101f5207b7SJohn Levon char *name;
2111f5207b7SJohn Levon
2121f5207b7SJohn Levon name = get_macro_name(stmt->pos);
2131f5207b7SJohn Levon if (is_turn_off(name)) {
2141f5207b7SJohn Levon print_unreached = 0;
2151f5207b7SJohn Levon return;
2161f5207b7SJohn Levon }
2171f5207b7SJohn Levon
2181f5207b7SJohn Levon if (stmt->type == STMT_IF &&
2191f5207b7SJohn Levon known_condition_true(stmt->if_conditional) && __path_is_null()) {
2201f5207b7SJohn Levon print_unreached = 0;
2211f5207b7SJohn Levon return;
2221f5207b7SJohn Levon }
2231f5207b7SJohn Levon
2241f5207b7SJohn Levon name = get_function_name(stmt);
2251f5207b7SJohn Levon if (is_turn_off(name))
2261f5207b7SJohn Levon print_unreached = 0;
2271f5207b7SJohn Levon }
2281f5207b7SJohn Levon
register_turn_off_macros(void)2291f5207b7SJohn Levon static void register_turn_off_macros(void)
2301f5207b7SJohn Levon {
2311f5207b7SJohn Levon struct token *token;
2321f5207b7SJohn Levon char *macro;
2331f5207b7SJohn Levon char name[256];
2341f5207b7SJohn Levon
2351f5207b7SJohn Levon if (option_project == PROJ_NONE)
2361f5207b7SJohn Levon strcpy(name, "unreachable.turn_off");
2371f5207b7SJohn Levon else
2381f5207b7SJohn Levon snprintf(name, 256, "%s.unreachable.turn_off", option_project_str);
2391f5207b7SJohn Levon
2401f5207b7SJohn Levon token = get_tokens_file(name);
2411f5207b7SJohn Levon if (!token)
2421f5207b7SJohn Levon return;
2431f5207b7SJohn Levon if (token_type(token) != TOKEN_STREAMBEGIN)
2441f5207b7SJohn Levon return;
2451f5207b7SJohn Levon token = token->next;
2461f5207b7SJohn Levon while (token_type(token) != TOKEN_STREAMEND) {
2471f5207b7SJohn Levon if (token_type(token) != TOKEN_IDENT)
2481f5207b7SJohn Levon return;
2491f5207b7SJohn Levon macro = alloc_string(show_ident(token->ident));
2501f5207b7SJohn Levon add_ptr_list(&turn_off_names, macro);
2511f5207b7SJohn Levon token = token->next;
2521f5207b7SJohn Levon }
2531f5207b7SJohn Levon clear_token_alloc();
2541f5207b7SJohn Levon }
2551f5207b7SJohn Levon
register_ignored_macros(void)2561f5207b7SJohn Levon static void register_ignored_macros(void)
2571f5207b7SJohn Levon {
2581f5207b7SJohn Levon struct token *token;
2591f5207b7SJohn Levon char *macro;
2601f5207b7SJohn Levon char name[256];
2611f5207b7SJohn Levon
2621f5207b7SJohn Levon if (option_project == PROJ_NONE)
2631f5207b7SJohn Levon strcpy(name, "unreachable.ignore");
2641f5207b7SJohn Levon else
2651f5207b7SJohn Levon snprintf(name, 256, "%s.unreachable.ignore", option_project_str);
2661f5207b7SJohn Levon
2671f5207b7SJohn Levon token = get_tokens_file(name);
2681f5207b7SJohn Levon if (!token)
2691f5207b7SJohn Levon return;
2701f5207b7SJohn Levon if (token_type(token) != TOKEN_STREAMBEGIN)
2711f5207b7SJohn Levon return;
2721f5207b7SJohn Levon token = token->next;
2731f5207b7SJohn Levon while (token_type(token) != TOKEN_STREAMEND) {
2741f5207b7SJohn Levon if (token_type(token) != TOKEN_IDENT)
2751f5207b7SJohn Levon return;
2761f5207b7SJohn Levon macro = alloc_string(show_ident(token->ident));
2771f5207b7SJohn Levon add_ptr_list(&ignore_names, macro);
2781f5207b7SJohn Levon token = token->next;
2791f5207b7SJohn Levon }
2801f5207b7SJohn Levon clear_token_alloc();
2811f5207b7SJohn Levon }
2821f5207b7SJohn Levon
check_unreachable(int id)2831f5207b7SJohn Levon void check_unreachable(int id)
2841f5207b7SJohn Levon {
2851f5207b7SJohn Levon my_id = id;
2861f5207b7SJohn Levon
2871f5207b7SJohn Levon register_turn_off_macros();
2881f5207b7SJohn Levon register_ignored_macros();
2891f5207b7SJohn Levon add_hook(&unreachable_stmt, STMT_HOOK);
2901f5207b7SJohn Levon add_hook(&turn_off_unreachable, STMT_HOOK_AFTER);
2911f5207b7SJohn Levon }
292