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