--- /dev/null	Fri Dec 21 15:00:09 2018
+++ new/usr/src/tools/smatch/src/check_unused_ret.c	Fri Dec 21 15:00:09 2018
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2009 Dan Carpenter.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt
+ */
+
+/*
+ * This check is supposed to find places like this:
+ * err = foo();
+ * err = bar();
+ * if (err)
+ *         return err;
+ * (the first assignment isn't used)
+ *
+ * How the check works is that every assignment gets an ID.
+ * We store that assignment ID in a list of assignments that
+ * haven't been used.  We also set the state of 'err' from
+ * the example above to be.  Then when we use 'err' we remove
+ * it from the list.  At the end of the function we print
+ * a list of assignments that still haven't been used.
+ *
+ * Note that this check only works for assignments to
+ * EXPR_SYMBOL.  Maybe it could be modified to cover other
+ * assignments later but then you would have to deal with
+ * scope issues.
+ *
+ * Also this state is quite tied to the order the callbacks
+ * are called in smatch_flow.c.  (If the order changed it
+ * would break).
+ *
+ */
+
+#include "smatch.h"
+#include "smatch_slist.h"
+#include "smatch_function_hashtable.h"
+
+static int my_id;
+
+struct assignment {
+	int assign_id;
+	char *name;
+	char *function;
+	int line;
+};
+ALLOCATOR(assignment, "assignment id");
+DECLARE_PTR_LIST(assignment_list, struct assignment);
+static struct assignment_list *assignment_list;
+
+static struct expression *skip_this;
+static int assign_id;
+
+static DEFINE_HASHTABLE_INSERT(insert_func, char, int);
+static DEFINE_HASHTABLE_SEARCH(search_func, char, int);
+static struct hashtable *ignored_funcs;
+
+static const char *kernel_ignored[] = {
+	"inb",
+	"inl",
+	"inw",
+	"readb",
+	"readl",
+	"readw",
+};
+
+static char *get_fn_name(struct expression *expr)
+{
+	if (expr->type != EXPR_CALL)
+		return NULL;
+	if (expr->fn->type != EXPR_SYMBOL)
+		return NULL;
+	return expr_to_var(expr->fn);
+}
+
+static int ignored_function(struct expression *expr)
+{
+	char *func;
+	int ret = 0;
+
+	func = get_fn_name(expr);
+	if (!func)
+		return 0;
+	if (search_func(ignored_funcs, func))
+		ret = 1;
+	free_string(func);
+	return ret;
+}
+
+static void match_assign_call(struct expression *expr)
+{
+	struct expression *left;
+	struct assignment *assign;
+
+	if (final_pass)
+		return;
+	if (in_condition())
+		return;
+	if (expr->op != '=')
+		return;
+	if (unreachable())
+		return;
+	if (ignored_function(expr->right))
+		return;
+	left = strip_expr(expr->left);
+	if (!left || left->type != EXPR_SYMBOL)
+		return;
+	if (left->symbol->ctype.modifiers & (MOD_TOPLEVEL | MOD_EXTERN | MOD_STATIC))
+		return;
+
+	skip_this = left;
+
+	set_state_expr(my_id, left, alloc_state_num(assign_id));
+
+	assign = __alloc_assignment(0);
+	assign->assign_id = assign_id++;
+	assign->name = expr_to_var(left);
+	assign->function = get_fn_name(expr->right);
+	assign->line = get_lineno();
+	add_ptr_list(&assignment_list, assign);
+}
+
+static void match_assign(struct expression *expr)
+{
+	struct expression *left;
+
+	if (expr->op != '=')
+		return;
+	left = strip_expr(expr->left);
+	if (!left || left->type != EXPR_SYMBOL)
+		return;
+	set_state_expr(my_id, left, &undefined);
+}
+
+static void delete_used(int assign_id)
+{
+	struct assignment *tmp;
+
+ 	FOR_EACH_PTR(assignment_list, tmp) {
+		if (tmp->assign_id == assign_id) {
+			DELETE_CURRENT_PTR(tmp);
+			return;
+		}
+	} END_FOR_EACH_PTR(tmp);
+}
+
+static void delete_used_symbols(struct state_list *possible)
+{
+	struct sm_state *tmp;
+
+ 	FOR_EACH_PTR(possible, tmp) {
+		delete_used(PTR_INT(tmp->state->data));
+	} END_FOR_EACH_PTR(tmp);
+}
+
+static void match_symbol(struct expression *expr)
+{
+	struct sm_state *sm;
+
+	expr = strip_expr(expr);
+	if (expr == skip_this)
+		return;
+	sm = get_sm_state_expr(my_id, expr);
+	if (!sm)
+		return;
+	delete_used_symbols(sm->possible);
+	set_state_expr(my_id, expr, &undefined);
+}
+
+static void match_end_func(struct symbol *sym)
+{
+	struct assignment *tmp;
+
+	if (__inline_fn)
+		return;
+ 	FOR_EACH_PTR(assignment_list, tmp) {
+		sm_printf("%s:%d %s() ", get_filename(), tmp->line, get_function());
+		sm_printf("warn: unused return: %s = %s()\n",
+			tmp->name, tmp->function);
+	} END_FOR_EACH_PTR(tmp);
+}
+
+static void match_after_func(struct symbol *sym)
+{
+	if (__inline_fn)
+		return;
+	clear_assignment_alloc();
+	__free_ptr_list((struct ptr_list **)&assignment_list);
+}
+
+void check_unused_ret(int id)
+{
+	my_id = id;
+
+	/* It turns out that this test is worthless unless you use --two-passes.  */
+	if (!option_two_passes)
+		return;
+	add_hook(&match_assign_call, CALL_ASSIGNMENT_HOOK);
+	add_hook(&match_assign, ASSIGNMENT_HOOK);
+	add_hook(&match_symbol, SYM_HOOK);
+	add_hook(&match_end_func, END_FUNC_HOOK);
+	add_hook(&match_after_func, AFTER_FUNC_HOOK);
+	ignored_funcs = create_function_hashtable(100);
+	if (option_project == PROJ_KERNEL) {
+		int i;
+
+		for (i = 0; i < ARRAY_SIZE(kernel_ignored); i++)
+			insert_func(ignored_funcs, (char *)kernel_ignored[i], (int *)1);
+	}
+}
