From b1ecb86569f63f897f6a95049c4ccf400bddeaad Mon Sep 17 00:00:00 2001 From: Martin Sebor Date: Mon, 16 Nov 2020 19:47:39 -0700 Subject: [PATCH] PR middle-end/95673 - missing -Wstring-compare for an impossible strncmp test gcc/ChangeLog: PR middle-end/95673 * tree-ssa-strlen.c (used_only_for_zero_equality): Rename... (use_in_zero_equality): ...to this. Add a default argument. (handle_builtin_memcmp): Adjust to the name change above. (handle_builtin_string_cmp): Same. (maybe_warn_pointless_strcmp): Same. Pass in an explicit argument. gcc/testsuite/ChangeLog: PR middle-end/95673 * gcc.dg/Wstring-compare-3.c: New test. --- gcc/testsuite/gcc.dg/Wstring-compare-3.c | 106 +++++++++++++++++++++++ gcc/tree-ssa-strlen.c | 53 ++++++++---- 2 files changed, 142 insertions(+), 17 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/Wstring-compare-3.c diff --git a/gcc/testsuite/gcc.dg/Wstring-compare-3.c b/gcc/testsuite/gcc.dg/Wstring-compare-3.c new file mode 100644 index 00000000000..d4d7121dba7 --- /dev/null +++ b/gcc/testsuite/gcc.dg/Wstring-compare-3.c @@ -0,0 +1,106 @@ +/* PR middle-end/95673 - missing -Wstring-compare for an impossible strncmp test + { dg-do compile } + { dg-options "-O2 -Wall -Wstring-compare -ftrack-macro-expansion=0" } */ + +typedef __SIZE_TYPE__ size_t; + +extern int strcmp (const char*, const char*); +extern int strncmp (const char*, const char*, size_t); + +void sink (int, ...); + +extern char a3[3]; + +int nowarn_strcmp_one_use_ltz (int c) +{ + const char *s = c ? "1234" : a3; + int n = strcmp (s, "123"); + return n < 0; +} + + +int nowarn_strcmp_one_use_eqnz (int c) +{ + const char *s = c ? "12345" : a3; + int n = strcmp (s, "123"); + return n == 1; +} + + +int warn_strcmp_one_use_eqz (int c) +{ + const char *s = c ? "123456" : a3; + int n = strcmp (s, "123"); // { dg-warning "'strcmp' of a string of length 3 and an array of size 3 evaluates to nonzero" } + return n == 0; // { dg-message "in this expression" } +} + + +int warn_strcmp_one_use_bang (int c) +{ + const char *s = c ? "1234567" : a3; + int n = strcmp (s, "123"); // { dg-warning "'strcmp' of a string of length 3 and an array of size 3 evaluates to nonzero" } + return !n; // { dg-message "in this expression" } +} + + +int warn_strcmp_one_use_bang_bang (int c) +{ + const char *s = c ? "12345678" : a3; + int n = strcmp (s, "123"); // { dg-warning "'strcmp' of a string of length 3 and an array of size 3 evaluates to nonzero" } + return !!n; // { dg-message "in this expression" } +} + + +_Bool warn_one_use_bool (int c) +{ + const char *s = c ? "123456789" : a3; + int n = strcmp (s, "123"); // { dg-warning "'strcmp' of a string of length 3 and an array of size 3 evaluates to nonzero" } + return (_Bool)n; // { dg-message "in this expression" } +} + + +int warn_strcmp_one_use_cond (int c) +{ + const char *s = c ? "1234567890" : a3; + int n = strcmp (s, "123"); // { dg-warning "'strcmp' of a string of length 3 and an array of size 3 evaluates to nonzero" } + return n ? 3 : 5; // { dg-message "in this expression" } +} + + +int nowarn_strcmp_multiple_uses (int c) +{ + const char *s = c ? "1234" : a3; + int n = strcmp (s, "123"); + sink (n < 0); + sink (n > 0); + sink (n <= 0); + sink (n >= 0); + sink (n + 1); + return n; +} + + +int warn_strcmp_multiple_uses (int c) +{ + const char *s = c ? "12345" : a3; + int n = strcmp (s, "123"); // { dg-warning "'strcmp' of a string of length 3 and an array of size 3 evaluates to nonzero" } + sink (n < 0); + sink (n > 0); + sink (n <= 0); + sink (n >= 0); + sink (n == 0); // { dg-message "in this expression" } + return n; +} + + +int warn_strncmp_multiple_uses (int c) +{ + const char *s = a3; + int n = strncmp (s, "1234", 4); // { dg-warning "'strncmp' of a string of length 4, an array of size 3 and bound of 4 evaluates to nonzero" } + sink (n < 0); + sink (n > 0); + sink (n <= 0); + sink (n >= 0); + sink (n == 0); // { dg-message "in this expression" } + return n; +} diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c index ebb17cd852c..a5e78a89e65 100644 --- a/gcc/tree-ssa-strlen.c +++ b/gcc/tree-ssa-strlen.c @@ -3989,11 +3989,13 @@ handle_builtin_memset (gimple_stmt_iterator *gsi, bool *zero_write, return true; } -/* Return a pointer to the first such equality expression if RES is used - only in expressions testing its equality to zero, and null otherwise. */ +/* Return first such statement if RES is used in statements testing its + equality to zero, and null otherwise. If EXCLUSIVE is true, return + nonnull if and only RES is used in such expressions exclusively and + in none other. */ static gimple * -used_only_for_zero_equality (tree res) +use_in_zero_equality (tree res, bool exclusive = true) { gimple *first_use = NULL; @@ -4006,6 +4008,7 @@ used_only_for_zero_equality (tree res) if (is_gimple_debug (use_stmt)) continue; + if (gimple_code (use_stmt) == GIMPLE_ASSIGN) { tree_code code = gimple_assign_rhs_code (use_stmt); @@ -4015,25 +4018,41 @@ used_only_for_zero_equality (tree res) if ((TREE_CODE (cond_expr) != EQ_EXPR && (TREE_CODE (cond_expr) != NE_EXPR)) || !integer_zerop (TREE_OPERAND (cond_expr, 1))) - return NULL; + { + if (exclusive) + return NULL; + continue; + } } else if (code == EQ_EXPR || code == NE_EXPR) { if (!integer_zerop (gimple_assign_rhs2 (use_stmt))) - return NULL; + { + if (exclusive) + return NULL; + continue; + } } - else + else if (exclusive) return NULL; + else + continue; } else if (gimple_code (use_stmt) == GIMPLE_COND) { tree_code code = gimple_cond_code (use_stmt); if ((code != EQ_EXPR && code != NE_EXPR) || !integer_zerop (gimple_cond_rhs (use_stmt))) - return NULL; + { + if (exclusive) + return NULL; + continue; + } } + else if (exclusive) + return NULL; else - return NULL; + continue; if (!first_use) first_use = use_stmt; @@ -4053,7 +4072,7 @@ handle_builtin_memcmp (gimple_stmt_iterator *gsi) gcall *stmt = as_a (gsi_stmt (*gsi)); tree res = gimple_call_lhs (stmt); - if (!res || !used_only_for_zero_equality (res)) + if (!res || !use_in_zero_equality (res)) return false; tree arg1 = gimple_call_arg (stmt, 0); @@ -4317,7 +4336,7 @@ maybe_warn_pointless_strcmp (gimple *stmt, HOST_WIDE_INT bound, unsigned HOST_WIDE_INT siz) { tree lhs = gimple_call_lhs (stmt); - gimple *use = used_only_for_zero_equality (lhs); + gimple *use = use_in_zero_equality (lhs, /* exclusive = */ false); if (!use) return; @@ -4367,12 +4386,12 @@ maybe_warn_pointless_strcmp (gimple *stmt, HOST_WIDE_INT bound, stmt, callee, minlen, siz, bound); } - if (warned) - { - location_t use_loc = gimple_location (use); - if (LOCATION_LINE (stmt_loc) != LOCATION_LINE (use_loc)) - inform (use_loc, "in this expression"); - } + if (!warned) + return; + + location_t use_loc = gimple_location (use); + if (LOCATION_LINE (stmt_loc) != LOCATION_LINE (use_loc)) + inform (use_loc, "in this expression"); } @@ -4507,7 +4526,7 @@ handle_builtin_string_cmp (gimple_stmt_iterator *gsi, range_query *rvals) /* The size of the array in which the unknown string is stored. */ HOST_WIDE_INT varsiz = arysiz1 < 0 ? arysiz2 : arysiz1; - if ((varsiz < 0 || cmpsiz < varsiz) && used_only_for_zero_equality (lhs)) + if ((varsiz < 0 || cmpsiz < varsiz) && use_in_zero_equality (lhs)) { /* If the known length is less than the size of the other array and the strcmp result is only used to test equality to zero, -- 2.30.2