diff mbox series

[v2] c: add Wzero-as-null-pointer-constant [PR117059]

Message ID da61c1547f35025e2abf41e0104369ccd74b9304.camel@tugraz.at
State New
Headers show
Series [v2] c: add Wzero-as-null-pointer-constant [PR117059] | expand

Commit Message

Martin Uecker Nov. 9, 2024, 5:15 p.m. UTC
This patch enables the Wzero-as-null-pointer-constant for C.
The second version added more tests and fixes one condition
to not incorrectly warn for nullptr. 


Bootstrapped and regression tested on x86_64.


    c: add Wzero-as-null-pointer-constant [PR117059]
    
    Add warnings for the use of zero as a null pointer constant to the C FE.
    
            PR c/117059
    
    gcc/c-family/ChangeLog:
            * c.opt (Wzero-as-null-pointer-constant): Enable for C and ObjC.
    
    gcc/c/ChangeLog:
            * c-typeck.cc (parse_build_binary_op): Add warning.
            (build_conditional_expr): Add warning.
            (convert_for_assignment): Add warning.
    
    gcc/testsuite/ChangeLog:
            * gcc.dg/Wzero-as-null-pointer-constant.c: New test.


Suggested-by: Alejandro Colomar <alx@kernel.org>
Acked-by: Alejandro Colomar <alx@kernel.org>

Comments

Alejandro Colomar Nov. 9, 2024, 5:38 p.m. UTC | #1
Hi Martin,

On Sat, Nov 09, 2024 at 06:15:35PM GMT, Martin Uecker wrote:
> 
> This patch enables the Wzero-as-null-pointer-constant for C.
> The second version added more tests and fixes one condition
> to not incorrectly warn for nullptr. 
> 
> 
> Bootstrapped and regression tested on x86_64.
> 
> 
>     c: add Wzero-as-null-pointer-constant [PR117059]
>     
>     Add warnings for the use of zero as a null pointer constant to the C FE.
>     
>             PR c/117059
>     
>     gcc/c-family/ChangeLog:
>             * c.opt (Wzero-as-null-pointer-constant): Enable for C and ObjC.
>     
>     gcc/c/ChangeLog:
>             * c-typeck.cc (parse_build_binary_op): Add warning.
>             (build_conditional_expr): Add warning.
>             (convert_for_assignment): Add warning.
>     
>     gcc/testsuite/ChangeLog:
>             * gcc.dg/Wzero-as-null-pointer-constant.c: New test.
> 
> 
> Suggested-by: Alejandro Colomar <alx@kernel.org>
> Acked-by: Alejandro Colomar <alx@kernel.org>
> 
> 
> diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
> index 9b9f5e744f6..b4e967ce000 100644
> --- a/gcc/c-family/c.opt
> +++ b/gcc/c-family/c.opt
> @@ -1632,7 +1632,7 @@ C ObjC C++ ObjC++ Var(warn_xor_used_as_pow) Warning Init(1)
>  Warn about xor operators where it appears the user meant exponentiation.
>  
>  Wzero-as-null-pointer-constant
> -C++ ObjC++ Var(warn_zero_as_null_pointer_constant) Warning
> +C ObjC C++ ObjC++ Var(warn_zero_as_null_pointer_constant) Warning
>  Warn when a literal '0' is used as null pointer.
>  
>  Wzero-length-bounds
> diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
> index 3fdf3437bf5..ced58c2be84 100644
> --- a/gcc/c/c-typeck.cc
> +++ b/gcc/c/c-typeck.cc
> @@ -4600,15 +4600,24 @@ parser_build_binary_op (location_t location, enum tree_code code,
>  		"comparison with string literal results in unspecified "
>  		"behavior");
>  
> -  if (true
> -      && TREE_CODE_CLASS (code) == tcc_comparison
> -      && ((TREE_CODE (type1) == POINTER_TYPE
> -	   && TREE_CODE (type2) == INTEGER_TYPE
> -	   && null_pointer_constant_p (arg2.value))
> -	  || (TREE_CODE (type2) == POINTER_TYPE
> -	      && TREE_CODE (type1) == INTEGER_TYPE
> -	       && null_pointer_constant_p (arg1.value))))
> -     warning_at (location, 0, "zero as null pointer constant");
> +  if (warn_zero_as_null_pointer_constant
> +      && c_inhibit_evaluation_warnings == 0
> +      && TREE_CODE_CLASS (code) == tcc_comparison)
> +    {
> +      if ((TREE_CODE (type1) == POINTER_TYPE
> +	   || TREE_CODE (type1) == NULLPTR_TYPE)
> +	  && TREE_CODE (type2) == INTEGER_TYPE
> +	  && null_pointer_constant_p (arg2.value))
> +	warning_at (arg2.get_location(), OPT_Wzero_as_null_pointer_constant,
> +		    "zero as null pointer constant");
> +
> +      if ((TREE_CODE (type2) == POINTER_TYPE
> +	   || TREE_CODE (type2) == NULLPTR_TYPE)
> +	  && TREE_CODE (type1) == INTEGER_TYPE
> +	  && null_pointer_constant_p (arg1.value))
> +	warning_at (arg1.get_location(), OPT_Wzero_as_null_pointer_constant,
> +		    "zero as null pointer constant");
> +    }
>  
>    if (warn_array_compare
>        && TREE_CODE_CLASS (code) == tcc_comparison
> @@ -5975,6 +5984,20 @@ build_conditional_expr (location_t colon_loc, tree ifexp, bool ifexp_bcp,
>  		    t1, t2);
>      }
>  
> +  if (warn_zero_as_null_pointer_constant
> +      && c_inhibit_evaluation_warnings == 0)
> +    {
> +      if ((code1 == POINTER_TYPE || code1 == NULLPTR_TYPE)
> +	  && code2 == INTEGER_TYPE && null_pointer_constant_p (orig_op2))
> +	warning_at (op2_loc, OPT_Wzero_as_null_pointer_constant,
> +		    "zero as null pointer constant");
> +
> +      if ((code2 == POINTER_TYPE || code2 == NULLPTR_TYPE)
> +	  && code1 == INTEGER_TYPE && null_pointer_constant_p (orig_op1))
> +	warning_at (op1_loc, OPT_Wzero_as_null_pointer_constant,
> +		    "zero as null pointer constant");
> +    }
> +
>    /* Quickly detect the usual case where op1 and op2 have the same type
>       after promotion.  */
>    if (TYPE_MAIN_VARIANT (type1) == TYPE_MAIN_VARIANT (type2))
> @@ -8406,8 +8429,10 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
>  	       || coder == NULLPTR_TYPE
>  	       || coder == BITINT_TYPE))
>      {
> -      if (null_pointer_constant)
> -	warning_at (location, 0, "zero as null pointer constant");
> +      if (null_pointer_constant && c_inhibit_evaluation_warnings == 0
> +	  && coder == INTEGER_TYPE)
> +	warning_at (location, OPT_Wzero_as_null_pointer_constant,
> +		    "zero as null pointer constant");
>  
>        /* An explicit constant 0 or type nullptr_t can convert to a pointer,
>  	 or one that results from arithmetic, even including a cast to
> diff --git a/gcc/testsuite/gcc.dg/Wzero-as-null-pointer-constant.c b/gcc/testsuite/gcc.dg/Wzero-as-null-pointer-constant.c
> new file mode 100644
> index 00000000000..372756faeb9
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/Wzero-as-null-pointer-constant.c
> @@ -0,0 +1,37 @@
> +/* { dg-do compile } */
> +/* { dg-options "-std=c23 -Wzero-as-null-pointer-constant" } */
> +
> +#define NULL (void*)0
> +
> +void foo(void*);
> +void bar()
> +{
> +	foo(0);				/* { dg-warning "zero as null pointer constant" } */
> +	foo(NULL);
> +	foo(nullptr);
> +
> +	void *p = 0;			/* { dg-warning "zero as null pointer constant" } */
> +	void *r = NULL;
> +	void *t = nullptr;
> +	void *q = { 0 };		/* { dg-warning "zero as null pointer constant" } */
> +	void *s = { NULL };
> +	void *u = { nullptr };
> +	struct { void *q; } x = { 0 };	/* { dg-warning "zero as null pointer constant" } */
> +	struct { void *q; } y = { NULL };
> +	struct { void *q; } z = { nullptr };
> +	struct { int a; void *b; } a = { 0 };
> +	struct { int a; int b; void *c; } b = { 0, 0 };
> +
> +	1 ? 0 : p;			/* { dg-warning "zero as null pointer constant" } */
> +	1 ? p : 0;			/* { dg-warning "zero as null pointer constant" } */
> +	1 ? 0 : NULL;			/* { dg-warning "zero as null pointer constant" } */
> +	1 ? NULL : 0;			/* { dg-warning "zero as null pointer constant" } */
> +
> +	if (p == 0);			/* { dg-warning "zero as null pointer constant" } */
> +	if (0 == p);			/* { dg-warning "zero as null pointer constant" } */
> +	if (NULL == 0);			/* { dg-warning "zero as null pointer constant" } */
> +	if (0 == NULL);			/* { dg-warning "zero as null pointer constant" } */
> +	if (0 == nullptr);		/* { dg-warning "zero as null pointer constant" } */
> +	if (nullptr == 0);		/* { dg-warning "zero as null pointer constant" } */

Maybe we should also check a few obvious cases that shouldn't be warned
because 0 is an int?  If some bug causes these to trigger, we'd know
something is very wrong.

	if (0 == 0);

	1 ? 0 : 0;

	int i = 0;

It might also be interesting to have at least one test where we use a
hardcoded (void*)0 without the name NULL.

Cheers,
Alex

> +}
> +
>
Joseph Myers Nov. 11, 2024, 3:17 p.m. UTC | #2
Other cases to test: the != operator (should warn in same cases as ==); 
boolean uses of pointers such as if (p), !p, p?x:y, and implicit or 
explicit conversion to bool (none of those boolean uses of pointers should 
warn since there's no explicit integer null pointer constant involved in 
the implicit comparison to 0).  Also test that initializing a pointer with 
empty braces does not warn.
diff mbox series

Patch

diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 9b9f5e744f6..b4e967ce000 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -1632,7 +1632,7 @@  C ObjC C++ ObjC++ Var(warn_xor_used_as_pow) Warning Init(1)
 Warn about xor operators where it appears the user meant exponentiation.
 
 Wzero-as-null-pointer-constant
-C++ ObjC++ Var(warn_zero_as_null_pointer_constant) Warning
+C ObjC C++ ObjC++ Var(warn_zero_as_null_pointer_constant) Warning
 Warn when a literal '0' is used as null pointer.
 
 Wzero-length-bounds
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index 3fdf3437bf5..ced58c2be84 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -4600,15 +4600,24 @@  parser_build_binary_op (location_t location, enum tree_code code,
 		"comparison with string literal results in unspecified "
 		"behavior");
 
-  if (true
-      && TREE_CODE_CLASS (code) == tcc_comparison
-      && ((TREE_CODE (type1) == POINTER_TYPE
-	   && TREE_CODE (type2) == INTEGER_TYPE
-	   && null_pointer_constant_p (arg2.value))
-	  || (TREE_CODE (type2) == POINTER_TYPE
-	      && TREE_CODE (type1) == INTEGER_TYPE
-	       && null_pointer_constant_p (arg1.value))))
-     warning_at (location, 0, "zero as null pointer constant");
+  if (warn_zero_as_null_pointer_constant
+      && c_inhibit_evaluation_warnings == 0
+      && TREE_CODE_CLASS (code) == tcc_comparison)
+    {
+      if ((TREE_CODE (type1) == POINTER_TYPE
+	   || TREE_CODE (type1) == NULLPTR_TYPE)
+	  && TREE_CODE (type2) == INTEGER_TYPE
+	  && null_pointer_constant_p (arg2.value))
+	warning_at (arg2.get_location(), OPT_Wzero_as_null_pointer_constant,
+		    "zero as null pointer constant");
+
+      if ((TREE_CODE (type2) == POINTER_TYPE
+	   || TREE_CODE (type2) == NULLPTR_TYPE)
+	  && TREE_CODE (type1) == INTEGER_TYPE
+	  && null_pointer_constant_p (arg1.value))
+	warning_at (arg1.get_location(), OPT_Wzero_as_null_pointer_constant,
+		    "zero as null pointer constant");
+    }
 
   if (warn_array_compare
       && TREE_CODE_CLASS (code) == tcc_comparison
@@ -5975,6 +5984,20 @@  build_conditional_expr (location_t colon_loc, tree ifexp, bool ifexp_bcp,
 		    t1, t2);
     }
 
+  if (warn_zero_as_null_pointer_constant
+      && c_inhibit_evaluation_warnings == 0)
+    {
+      if ((code1 == POINTER_TYPE || code1 == NULLPTR_TYPE)
+	  && code2 == INTEGER_TYPE && null_pointer_constant_p (orig_op2))
+	warning_at (op2_loc, OPT_Wzero_as_null_pointer_constant,
+		    "zero as null pointer constant");
+
+      if ((code2 == POINTER_TYPE || code2 == NULLPTR_TYPE)
+	  && code1 == INTEGER_TYPE && null_pointer_constant_p (orig_op1))
+	warning_at (op1_loc, OPT_Wzero_as_null_pointer_constant,
+		    "zero as null pointer constant");
+    }
+
   /* Quickly detect the usual case where op1 and op2 have the same type
      after promotion.  */
   if (TYPE_MAIN_VARIANT (type1) == TYPE_MAIN_VARIANT (type2))
@@ -8406,8 +8429,10 @@  convert_for_assignment (location_t location, location_t expr_loc, tree type,
 	       || coder == NULLPTR_TYPE
 	       || coder == BITINT_TYPE))
     {
-      if (null_pointer_constant)
-	warning_at (location, 0, "zero as null pointer constant");
+      if (null_pointer_constant && c_inhibit_evaluation_warnings == 0
+	  && coder == INTEGER_TYPE)
+	warning_at (location, OPT_Wzero_as_null_pointer_constant,
+		    "zero as null pointer constant");
 
       /* An explicit constant 0 or type nullptr_t can convert to a pointer,
 	 or one that results from arithmetic, even including a cast to
diff --git a/gcc/testsuite/gcc.dg/Wzero-as-null-pointer-constant.c b/gcc/testsuite/gcc.dg/Wzero-as-null-pointer-constant.c
new file mode 100644
index 00000000000..372756faeb9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wzero-as-null-pointer-constant.c
@@ -0,0 +1,37 @@ 
+/* { dg-do compile } */
+/* { dg-options "-std=c23 -Wzero-as-null-pointer-constant" } */
+
+#define NULL (void*)0
+
+void foo(void*);
+void bar()
+{
+	foo(0);				/* { dg-warning "zero as null pointer constant" } */
+	foo(NULL);
+	foo(nullptr);
+
+	void *p = 0;			/* { dg-warning "zero as null pointer constant" } */
+	void *r = NULL;
+	void *t = nullptr;
+	void *q = { 0 };		/* { dg-warning "zero as null pointer constant" } */
+	void *s = { NULL };
+	void *u = { nullptr };
+	struct { void *q; } x = { 0 };	/* { dg-warning "zero as null pointer constant" } */
+	struct { void *q; } y = { NULL };
+	struct { void *q; } z = { nullptr };
+	struct { int a; void *b; } a = { 0 };
+	struct { int a; int b; void *c; } b = { 0, 0 };
+
+	1 ? 0 : p;			/* { dg-warning "zero as null pointer constant" } */
+	1 ? p : 0;			/* { dg-warning "zero as null pointer constant" } */
+	1 ? 0 : NULL;			/* { dg-warning "zero as null pointer constant" } */
+	1 ? NULL : 0;			/* { dg-warning "zero as null pointer constant" } */
+
+	if (p == 0);			/* { dg-warning "zero as null pointer constant" } */
+	if (0 == p);			/* { dg-warning "zero as null pointer constant" } */
+	if (NULL == 0);			/* { dg-warning "zero as null pointer constant" } */
+	if (0 == NULL);			/* { dg-warning "zero as null pointer constant" } */
+	if (0 == nullptr);		/* { dg-warning "zero as null pointer constant" } */
+	if (nullptr == 0);		/* { dg-warning "zero as null pointer constant" } */
+}
+