/*
 * 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
 */

/*
 * Check for things which are signed but probably should be unsigned.
 *
 * Hm...  It seems like at this point in the processing, sparse makes all
 * bitfields unsigned.  Which is logical but not what GCC does.
 *
 */

#include "smatch.h"
#include "smatch_extra.h"

static int my_id;

#define VAR_ON_RIGHT 0
#define VAR_ON_LEFT 1

static void match_assign(struct expression *expr)
{
	struct symbol *sym;
	sval_t sval;
	sval_t max;
	sval_t min;
	char *left_name, *right_name;

	if (__in_fake_assign)
		return;
	if (is_fake_var_assign(expr))
		return;
	if (expr->op == SPECIAL_AND_ASSIGN || expr->op == SPECIAL_OR_ASSIGN)
		return;

	sym = get_type(expr->left);
	if (!sym || sym->type != SYM_BASETYPE) {
		//sm_msg("could not get type");
		return;
	}
	if (type_bits(sym) < 0 || type_bits(sym) >= 32) /* max_val limits this */
		return;
	if (!get_implied_value(expr->right, &sval))
		return;
	max = sval_type_max(sym);
	if (sym != &bool_ctype && sym != &uchar_ctype &&
	    sval_cmp(max, sval) < 0 &&
	    !(sval.value < 256 && max.value == 127)) {
		left_name = expr_to_str(expr->left);
		right_name = expr_to_str(expr->right);
		sm_warning("'%s' %s can't fit into %s '%s'",
		       right_name, sval_to_numstr(sval), sval_to_numstr(max), left_name);
		free_string(left_name);
	}
	min = sval_type_min(sym);
	if (sval_cmp_t(&llong_ctype, min, sval) > 0) {
		if (min.value == 0 && sval.value == -1) /* assigning -1 to unsigned variables is idiomatic */
			return;
		if (expr->right->type == EXPR_PREOP && expr->right->op == '~')
			return;
		if (expr->op == SPECIAL_SUB_ASSIGN || expr->op == SPECIAL_ADD_ASSIGN)
			return;
		if (sval_positive_bits(sval) == 7)
			return;
		left_name = expr_to_str(expr->left);
		if (min.value == 0) {
			sm_warning("assigning %s to unsigned variable '%s'",
			       sval_to_str(sval), left_name);
		} else {
			sm_warning("value %s can't fit into %s '%s'",
			       sval_to_str(sval), sval_to_str(min), left_name);
		}
		free_string(left_name);
	}
}

static int cap_gt_zero_and_lt(struct expression *expr)
{

	struct expression *var = expr->left;
	struct expression *tmp;
	char *name1 = NULL;
	char *name2 = NULL;
	sval_t known;
	int ret = 0;
	int i;

	if (!get_value(expr->right, &known) || known.value != 0)
		return 0;

	i = 0;
	FOR_EACH_PTR_REVERSE(big_expression_stack, tmp) {
		if (!i++)
			continue;
		if (tmp->op == SPECIAL_LOGICAL_AND) {
			struct expression *right = strip_expr(tmp->right);

			if (right->op != '<' &&
			    right->op != SPECIAL_UNSIGNED_LT &&
			    right->op != SPECIAL_LTE &&
			    right->op != SPECIAL_UNSIGNED_LTE)
				return 0;

			name1 = expr_to_str(var);
			if (!name1)
				goto free;

			name2 = expr_to_str(right->left);
			if (!name2)
				goto free;
			if (!strcmp(name1, name2))
				ret = 1;
			goto free;

		}
		return 0;
	} END_FOR_EACH_PTR_REVERSE(tmp);

free:
	free_string(name1);
	free_string(name2);
	return ret;
}

static int cap_lt_zero_or_gt(struct expression *expr)
{

	struct expression *var = expr->left;
	struct expression *tmp;
	char *name1 = NULL;
	char *name2 = NULL;
	sval_t known;
	int ret = 0;
	int i;

	if (!get_value(expr->right, &known) || known.value != 0)
		return 0;

	i = 0;
	FOR_EACH_PTR_REVERSE(big_expression_stack, tmp) {
		if (!i++)
			continue;
		if (tmp->op == SPECIAL_LOGICAL_OR) {
			struct expression *right = strip_expr(tmp->right);

			if (right->op != '>' &&
			    right->op != SPECIAL_UNSIGNED_GT &&
			    right->op != SPECIAL_GTE &&
			    right->op != SPECIAL_UNSIGNED_GTE)
				return 0;

			name1 = expr_to_str(var);
			if (!name1)
				goto free;

			name2 = expr_to_str(right->left);
			if (!name2)
				goto free;
			if (!strcmp(name1, name2))
				ret = 1;
			goto free;

		}
		return 0;
	} END_FOR_EACH_PTR_REVERSE(tmp);

free:
	free_string(name1);
	free_string(name2);
	return ret;
}

static int cap_both_sides(struct expression *expr)
{
	switch (expr->op) {
	case '<':
	case SPECIAL_UNSIGNED_LT:
	case SPECIAL_LTE:
	case SPECIAL_UNSIGNED_LTE:
		return cap_lt_zero_or_gt(expr);
	case '>':
	case SPECIAL_UNSIGNED_GT:
	case SPECIAL_GTE:
	case SPECIAL_UNSIGNED_GTE:
		return cap_gt_zero_and_lt(expr);
	}
	return 0;
}

static int compare_against_macro(struct expression *expr)
{
	sval_t known;

	if (expr->op != SPECIAL_UNSIGNED_LT)
		return 0;

	if (!get_value(expr->right, &known) || known.value != 0)
		return 0;
	return !!get_macro_name(expr->right->pos);
}

static int print_unsigned_never_less_than_zero(struct expression *expr)
{
	sval_t known;
	char *name;

	if (expr->op != SPECIAL_UNSIGNED_LT)
		return 0;

	if (!get_value(expr->right, &known) || known.value != 0)
		return 0;

	name = expr_to_str(expr->left);
	sm_warning("unsigned '%s' is never less than zero.", name);
	free_string(name);
	return 1;
}

static bool check_is_ulong_max_recursive(struct expression *expr)
{
	sval_t sval;

	expr = strip_expr(expr);

	if (!get_value(expr, &sval))
		return false;

	if (expr->type == EXPR_BINOP) {
		if (check_is_ulong_max_recursive(expr->left))
			return true;
		return false;
	}

	if (sval_cmp(sval, sval_type_max(&ulong_ctype)) == 0)
		return true;
	return false;
}

static bool is_u64_vs_ulongmax(struct expression *expr)
{
	struct symbol *left, *right;

	if (expr->op != '>' && expr->op != SPECIAL_UNSIGNED_GT)
		return false;
	if (!check_is_ulong_max_recursive(expr->right))
		return false;

	left = get_type(expr->left);
	right = get_type(expr->right);

	if (left == right)
		return true;
	if (type_positive_bits(left) < type_positive_bits(right))
		return true;

	if (type_bits(left) != 64)
		return false;
	if (right != &ulong_ctype && right != &uint_ctype)
		return false;

	return true;
}

static void match_condition(struct expression *expr)
{
	struct symbol *type;
	sval_t known;
	sval_t min, max;
	struct range_list *rl_left_orig, *rl_right_orig;
	struct range_list *rl_left, *rl_right;

	if (expr->type != EXPR_COMPARE)
		return;

	type = get_type(expr);
	if (!type)
		return;

	/* screw it.  I am writing this to mark yoda code as buggy.
	 * Valid comparisons between an unsigned and zero are:
	 * 1) inside a macro.
	 * 2) foo < LOWER_BOUND where LOWER_BOUND is a macro.
	 * 3) foo < 0 || foo > X in exactly this format.  No Yoda.
	 * 4) foo >= 0 && foo < X
	 */
	if (get_macro_name(expr->pos))
		return;
	if (compare_against_macro(expr))
		return;
	if (cap_both_sides(expr))
		return;

	/* This is a special case for the common error */
	if (print_unsigned_never_less_than_zero(expr))
		return;

	/* check that one and only one side is known */
	if (get_value(expr->left, &known)) {
		if (get_value(expr->right, &known))
			return;
		rl_left_orig = alloc_rl(known, known);
		rl_left = cast_rl(type, rl_left_orig);

		min = sval_type_min(get_type(expr->right));
		max = sval_type_max(get_type(expr->right));
		rl_right_orig = alloc_rl(min, max);
		rl_right = cast_rl(type, rl_right_orig);
	} else if (get_value(expr->right, &known)) {
		rl_right_orig = alloc_rl(known, known);
		rl_right = cast_rl(type, rl_right_orig);

		min = sval_type_min(get_type(expr->left));
		max = sval_type_max(get_type(expr->left));
		rl_left_orig = alloc_rl(min, max);
		rl_left = cast_rl(type, rl_left_orig);
	} else {
		return;
	}

	if (!possibly_true_rl(rl_left, expr->op, rl_right) &&
	    !is_u64_vs_ulongmax(expr)) {
		char *name = expr_to_str(expr);

		sm_warning("impossible condition '(%s) => (%s %s %s)'", name,
		       show_rl(rl_left), show_special(expr->op),
		       show_rl(rl_right));
		free_string(name);
	}

	if (!possibly_false_rl(rl_left, expr->op, rl_right) &&
	    !is_unconstant_macro(expr->left) &&
	    !is_unconstant_macro(expr->right)) {
		char *name = expr_to_str(expr);

		sm_warning("always true condition '(%s) => (%s %s %s)'", name,
		       show_rl(rl_left_orig), show_special(expr->op),
		       show_rl(rl_right_orig));
		free_string(name);
	}
}

void check_signed(int id)
{
	my_id = id;

	add_hook(&match_assign, ASSIGNMENT_HOOK);
	add_hook(&match_condition, CONDITION_HOOK);
}
