Hi! As has been reported today on gcc@, the new -Wnonnull warning addition warns even about nonnull parameters that were changed (or could be changed) in the function. IMHO the nonnull attribute only talks about the value of the parameter upon entry to the function, if you assign something else to the argument afterwards and test that for NULL or non-NULL, it is like any other variable. But, we don't have the infrastructure to detect this in the FE, so this patch moves the warning soon after we go into SSA form. As -Wnonnull is a C/C++/ObjC/ObjC++ only warning, I've added a new -Wnonnull-compare switch for it too (and enable it for C/C++/ObjC/ObjC++ from -Wall).
Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? 2016-02-16 Jakub Jelinek <ja...@redhat.com> PR c/69835 * common.opt (Wnonnull-compare): New warning. * doc/invoke.texi (-Wnonnull): Remove text about comparison of arguments against NULL. (-Wnonnull-compare): Document. * tree-ssa-uninit.c (warn_uninitialized_vars): Add warn_nonnull_cmp argument, if true, warn about SSA_NAME (D) of nonnull_arg_p comparisons against NULL pointers. (pass_late_warn_uninitialized::execute): Adjust caller. (execute_early_warn_uninitialized): Likewise. (gate_early_warn_uninitialized): New function. (pass_early_warn_uninitialized::gate): Call it instead of gate_warn_uninitialized. c-family/ * c.opt (Wnonnull-compare): Enable for -Wall. c/ * c-typeck.c (build_binary_op): Revert 2015-09-09 change. cp/ * typeck.c (cp_build_binary_op): Revert 2015-09-09 change. testsuite/ * c-c++-common/nonnull-1.c: Use -Wnonnull-compare instead of -Wnonnull in dg-options. * c-c++-common/nonnull-2.c: New test. --- gcc/common.opt.jj 2016-01-27 19:47:35.000000000 +0100 +++ gcc/common.opt 2016-02-16 11:52:42.641623690 +0100 @@ -616,6 +616,10 @@ Wlarger-than= Common RejectNegative Joined UInteger Warning -Wlarger-than=<number> Warn if an object is larger than <number> bytes. +Wnonnull-compare +Var(warn_nonnull_compare) Warning +Warn if comparing pointer parameter with nonnull attribute with NULL. + Wnull-dereference Common Var(warn_null_dereference) Warning Warn if dereferencing a NULL pointer may lead to erroneous or undefined behavior. --- gcc/doc/invoke.texi.jj 2016-02-08 18:39:16.000000000 +0100 +++ gcc/doc/invoke.texi 2016-02-16 12:31:42.037232875 +0100 @@ -276,7 +276,8 @@ Objective-C and Objective-C++ Dialects}. -Wmain -Wmaybe-uninitialized -Wmemset-transposed-args @gol -Wmisleading-indentation -Wmissing-braces @gol -Wmissing-field-initializers -Wmissing-include-dirs @gol --Wno-multichar -Wnonnull -Wnormalized=@r{[}none@r{|}id@r{|}nfc@r{|}nfkc@r{]} @gol +-Wno-multichar -Wnonnull -Wnonnull-compare @gol +-Wnormalized=@r{[}none@r{|}id@r{|}nfc@r{|}nfkc@r{]} @gol -Wnull-dereference -Wodr -Wno-overflow -Wopenmp-simd @gol -Woverride-init-side-effects -Woverlength-strings @gol -Wpacked -Wpacked-bitfield-compat -Wpadded @gol @@ -3537,6 +3538,7 @@ Options} and @ref{Objective-C and Object -Wmissing-braces @r{(only for C/ObjC)} @gol -Wnarrowing @r{(only for C++)} @gol -Wnonnull @gol +-Wnonnull-compare @gol -Wopenmp-simd @gol -Wparentheses @gol -Wpointer-sign @gol @@ -3795,12 +3797,18 @@ formats that may yield only a two-digit Warn about passing a null pointer for arguments marked as requiring a non-null value by the @code{nonnull} function attribute. -Also warns when comparing an argument marked with the @code{nonnull} -function attribute against null inside the function. - @option{-Wnonnull} is included in @option{-Wall} and @option{-Wformat}. It can be disabled with the @option{-Wno-nonnull} option. +@item -Wnonnull-compare +@opindex Wnonnull-compare +@opindex Wno-nonnull-compare +Warn when comparing an argument marked with the @code{nonnull} +function attribute against null inside the function. + +@option{-Wnonnull-compare} is included in @option{-Wall}. It +can be disabled with the @option{-Wno-nonnull-compare} option. + @item -Wnull-dereference @opindex Wnull-dereference @opindex Wno-null-dereference --- gcc/tree-ssa-uninit.c.jj 2016-01-13 13:28:41.000000000 +0100 +++ gcc/tree-ssa-uninit.c 2016-02-16 12:50:30.390619823 +0100 @@ -171,7 +171,8 @@ warn_uninit (enum opt_code wc, tree t, t } static unsigned int -warn_uninitialized_vars (bool warn_possibly_uninitialized) +warn_uninitialized_vars (bool warn_possibly_uninitialized, + bool warn_nonnull_cmp) { gimple_stmt_iterator gsi; basic_block bb; @@ -190,6 +191,60 @@ warn_uninitialized_vars (bool warn_possi if (is_gimple_debug (stmt)) continue; + if (warn_nonnull_cmp) + { + tree op0 = NULL_TREE, op1 = NULL_TREE; + location_t loc = gimple_location (stmt); + if (gimple_code (stmt) == GIMPLE_COND) + switch (gimple_cond_code (stmt)) + { + case EQ_EXPR: + case NE_EXPR: + op0 = gimple_cond_lhs (stmt); + op1 = gimple_cond_rhs (stmt); + break; + default: + break; + } + else if (is_gimple_assign (stmt)) + switch (gimple_assign_rhs_code (stmt)) + { + case EQ_EXPR: + case NE_EXPR: + op0 = gimple_assign_rhs1 (stmt); + op1 = gimple_assign_rhs2 (stmt); + break; + case COND_EXPR: + switch (TREE_CODE (gimple_assign_rhs1 (stmt))) + { + case EQ_EXPR: + case NE_EXPR: + op1 = gimple_assign_rhs1 (stmt); + loc = EXPR_LOC_OR_LOC (op1, loc); + op0 = TREE_OPERAND (op1, 0); + op1 = TREE_OPERAND (op1, 1); + break; + default: + break; + } + break; + default: + break; + } + if (op0 + && TREE_CODE (op0) == SSA_NAME + && SSA_NAME_IS_DEFAULT_DEF (op0) + && TREE_CODE (SSA_NAME_VAR (op0)) == PARM_DECL + && (POINTER_TYPE_P (TREE_TYPE (op0)) + || TREE_CODE (TREE_TYPE (op0)) == OFFSET_TYPE) + && nonnull_arg_p (SSA_NAME_VAR (op0)) + && (POINTER_TYPE_P (TREE_TYPE (op0)) + ? integer_zerop (op1) : integer_minus_onep (op1))) + warning_at (gimple_location (stmt), OPT_Wnonnull_compare, + "nonnull argument %qD compared to NULL", + SSA_NAME_VAR (op0)); + } + /* We only do data flow with SSA_NAMEs, so that's all we can warn about. */ FOR_EACH_SSA_USE_OPERAND (use_p, stmt, op_iter, SSA_OP_USE) @@ -2416,7 +2471,7 @@ pass_late_warn_uninitialized::execute (f /* Re-do the plain uninitialized variable check, as optimization may have straightened control flow. Do this first so that we don't accidentally get a "may be" warning when we'd have seen an "is" warning later. */ - warn_uninitialized_vars (/*warn_possibly_uninitialized=*/1); + warn_uninitialized_vars (/*warn_possibly_uninitialized=*/true, false); timevar_push (TV_TREE_UNINIT); @@ -2478,6 +2533,14 @@ make_pass_late_warn_uninitialized (gcc:: } +static bool +gate_early_warn_uninitialized (void) +{ + return (warn_uninitialized + || warn_maybe_uninitialized + || warn_nonnull_compare); +} + static unsigned int execute_early_warn_uninitialized (void) { @@ -2488,7 +2551,8 @@ execute_early_warn_uninitialized (void) optimization we need to warn here about "may be uninitialized". */ calculate_dominance_info (CDI_POST_DOMINATORS); - warn_uninitialized_vars (/*warn_possibly_uninitialized=*/!optimize); + warn_uninitialized_vars (/*warn_possibly_uninitialized=*/!optimize, + warn_nonnull_compare); /* Post-dominator information can not be reliably updated. Free it after the use. */ @@ -2521,7 +2585,7 @@ public: {} /* opt_pass methods: */ - virtual bool gate (function *) { return gate_warn_uninitialized (); } + virtual bool gate (function *) { return gate_early_warn_uninitialized (); } virtual unsigned int execute (function *) { return execute_early_warn_uninitialized (); --- gcc/c-family/c.opt.jj 2016-02-08 18:39:17.000000000 +0100 +++ gcc/c-family/c.opt 2016-02-16 12:30:00.299641823 +0100 @@ -677,6 +677,10 @@ Wnonnull C ObjC C++ ObjC++ LangEnabledBy(C ObjC C++ ObjC++,Wall) ; +Wnonnull-compare +C ObjC C++ ObjC++ LangEnabledBy(C ObjC C++ ObjC++,Wall) +; + Wnormalized C ObjC C++ ObjC++ Warning Alias(Wnormalized=,nfc,none) ; --- gcc/c/c-typeck.c.jj 2016-02-12 10:23:50.000000000 +0100 +++ gcc/c/c-typeck.c 2016-02-16 11:40:08.474048701 +0100 @@ -11086,11 +11086,6 @@ build_binary_op (location_t location, en short_compare = 1; else if (code0 == POINTER_TYPE && null_pointer_constant_p (orig_op1)) { - if (warn_nonnull - && TREE_CODE (op0) == PARM_DECL && nonnull_arg_p (op0)) - warning_at (location, OPT_Wnonnull, - "nonnull argument %qD compared to NULL", op0); - if (TREE_CODE (op0) == ADDR_EXPR && decl_with_nonnull_addr_p (TREE_OPERAND (op0, 0))) { @@ -11111,11 +11106,6 @@ build_binary_op (location_t location, en } else if (code1 == POINTER_TYPE && null_pointer_constant_p (orig_op0)) { - if (warn_nonnull - && TREE_CODE (op1) == PARM_DECL && nonnull_arg_p (op1)) - warning_at (location, OPT_Wnonnull, - "nonnull argument %qD compared to NULL", op1); - if (TREE_CODE (op1) == ADDR_EXPR && decl_with_nonnull_addr_p (TREE_OPERAND (op1, 0))) { --- gcc/cp/typeck.c.jj 2016-02-12 10:23:50.000000000 +0100 +++ gcc/cp/typeck.c 2016-02-16 11:40:37.783643278 +0100 @@ -4514,11 +4514,6 @@ cp_build_binary_op (location_t location, || (code0 == POINTER_TYPE && TYPE_PTR_P (type1) && integer_zerop (op1))) { - if (warn_nonnull - && TREE_CODE (op0) == PARM_DECL && nonnull_arg_p (op0)) - warning_at (location, OPT_Wnonnull, - "nonnull argument %qD compared to NULL", op0); - if (TYPE_PTR_P (type1)) result_type = composite_pointer_type (type0, type1, op0, op1, CPO_COMPARISON, complain); @@ -4558,11 +4553,6 @@ cp_build_binary_op (location_t location, || (code1 == POINTER_TYPE && TYPE_PTR_P (type0) && integer_zerop (op0))) { - if (warn_nonnull - && TREE_CODE (op1) == PARM_DECL && nonnull_arg_p (op1)) - warning_at (location, OPT_Wnonnull, - "nonnull argument %qD compared to NULL", op1); - if (TYPE_PTR_P (type0)) result_type = composite_pointer_type (type0, type1, op0, op1, CPO_COMPARISON, complain); --- gcc/testsuite/c-c++-common/nonnull-1.c.jj 2015-10-11 19:11:06.000000000 +0200 +++ gcc/testsuite/c-c++-common/nonnull-1.c 2016-02-16 12:38:53.917256278 +0100 @@ -1,7 +1,7 @@ /* Test for the bad usage of "nonnull" function attribute parms. */ /* */ /* { dg-do compile } */ -/* { dg-options "-Wnonnull" } */ +/* { dg-options "-Wnonnull-compare" } */ #include <stddef.h> #include <stdlib.h> --- gcc/testsuite/c-c++-common/nonnull-2.c.jj 2016-02-16 12:52:17.149142596 +0100 +++ gcc/testsuite/c-c++-common/nonnull-2.c 2016-02-16 12:56:29.904645198 +0100 @@ -0,0 +1,26 @@ +/* Test for the bad usage of "nonnull" function attribute parms. */ +/* { dg-do compile } */ +/* { dg-options "-Wnonnull-compare" } */ + +void bar (char **); + +__attribute__((nonnull (1, 3))) int +foo (char *cp1, char *cp2, char *cp3, char *cp4) +{ + if (cp1 == (char *) 0) /* { dg-warning "nonnull argument" "cp1 compared to NULL" } */ + return 1; + + cp1 = cp2; + if (cp1 == (char *) 0) /* { dg-bogus "nonnull argument" } */ + return 2; + + if (!cp4) /* { dg-bogus "nonnull argument" } */ + return 3; + + char **p = &cp3; + bar (p); + if (cp3 == (char *) 0) /* { dg-bogus "nonnull argument" } */ + return 4; + + return 5; +} Jakub