The caret location C++ -Wnonnull warnings is in the wrong place:
either under the closing parenthesis of a call to a function
declared nonnull, or under the whole call (when issued from
the middle end).  In addition, for member functions, the one-based
argument number mentioned in the warning starts with the implicit
this pointer, while in non-members it starts with the first argument.
That makes it difficult to tell which argument the warning is
complaining about (see the test + output below).

The attached patch improves things in a few minor ways:

1) by using the argument location when it has one to underline it
   (the location isn't always available at this point but that
   can be improved in a followup)
2) by using 1 for the first explicit argument, and by mentioning
   'this' for the this pointer
3) by changing the message issued by the front-end (and the C++
   member function handling) to match the corresponding message
   (and handling) in the middle end.

Finally, the patch also arranges to treat the C++ this pointer
as implicitly nonnull, regardless of whether the member function
is declared with the attribute.

Tested on x86_64-linux.

Martin

PS For this test case:

__attribute__ ((nonnull)) void f (const char*);

struct S
{
  __attribute__ ((nonnull)) void g (const char*) const;
};

void g (void)
{
  {
    const char* const null = 0;
    f (null);
  }

  {
    const S* const null = 0;
    null->g ("");
  }
}

GCC trunk outputs:

t.C: In function ‘void g()’:
t.C:12:12: warning: null argument where non-null required (argument 1) [-Wnonnull]
   12 |     f (null);
      |            ^
t.C:17:16: warning: null argument where non-null required (argument 1) [-Wnonnull]
   17 |     null->g ("");
      |                ^


With the patch the output is:

t.C: In function ‘void g()’:
t.C:12:8: warning: argument 1 null where non-null expected [-Wnonnull]
   12 |     f (null);
      |        ^~~~
t.C:1:32: note: in a call to function ‘void f(const char*)’ declared ‘nonnull’
    1 | __attribute__ ((nonnull)) void f (const char*);
      |                                ^
t.C:17:5: warning: ‘this’ pointer null [-Wnonnull]
   17 |     null->g ("");
      |     ^~~~
t.C:5:34: note: in a call to non-static member function ‘void S::g(const char*) const’
    5 |   __attribute__ ((nonnull)) void g (const char*) const;
      |                                  ^

PR c++/86568 - -Wnonnull warnings should highlight the relevant argument not the closing parenthesis

gcc/c-family/ChangeLog:

	PR c++/86568
	* c-common.c (struct nonnull_arg_ctx): Add members.
	(check_function_nonnull): Use nonnull_arg_ctx as argument.  Handle
	C++ member functions specially.  Consider the this pointer implicitly
	nonnull.
	(check_nonnull_arg): Use location of argument when available.
	(check_function_arguments): Use nonnull_arg_ctx as argument.

gcc/ChangeLog:

	PR c++/86568
	* calls.c (maybe_warn_rdwr_sizes): Use location of argument if
	available.
	* tree-ssa-ccp.c (pass_post_ipa_warn::execute): Same.  Adjust
	indentation.
	* tree.c (get_nonnull_args): Consider the this pointer implicitly
	nonnull.
	* gcc/var-tracking.c (deps_vec): New type.
	(var_loc_dep_vec): New function.
	(VAR_LOC_DEP_VEC): Use it.

gcc/testsuite/ChangeLog:

	PR c++/86568
	* g++.dg/warn/Wnonnull5.C: New test.
	* c-c++-common/pr28656.c: Adjust text of expected warning.
	* c-c++-common/pr66208.c: Same.
	* g++.dg/cpp0x/nullptr22.C: Same.
	* g++.dg/ext/attr-nonnull.C: Same.
	* g++.dg/ext/attrib49.C: Same.
	* g++.dg/pr71973-2.C: Same.
	* g++.dg/warn/Wnonnull3.C: Same.
	* g++.dg/warn/Wnonnull4.C: Same.
	* obj-c++.dg/attributes/method-nonnull-1.mm: Same.
	* objc.dg/attributes/method-nonnull-1.m: Same.

diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index b1379faa412..8bedd9dc910 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -5288,26 +5288,39 @@ c_determine_visibility (tree decl)
 
 struct nonnull_arg_ctx
 {
+  /* Location of the call.  */
   location_t loc;
+  /* The function whose arguments are being checked and its type (used
+     for calls through function pointers).  */
+  const_tree fndecl, fntype;
+  /* True if a warning has been issued.  */
   bool warned_p;
 };
 
-/* Check the argument list of a function call for null in argument slots
-   that are marked as requiring a non-null pointer argument.  The NARGS
-   arguments are passed in the array ARGARRAY.  Return true if we have
-   warned.  */
+/* Check the argument list of a function call to CTX.FNDECL of CTX.FNTYPE
+   for null in argument slots that are marked as requiring a non-null
+   pointer argument.  The NARGS arguments are passed in the array ARGARRAY.
+   Return true if we have warned.  */
 
 static bool
-check_function_nonnull (location_t loc, tree attrs, int nargs, tree *argarray)
+check_function_nonnull (nonnull_arg_ctx &ctx, int nargs, tree *argarray)
 {
-  tree a;
-  int i;
+  int firstarg = 0;
+  if (TREE_CODE (ctx.fntype) == METHOD_TYPE)
+    {
+      /* In calls to C++ non-static member functions check the this
+	 pointer regardless of whether the function is declared with
+	 attribute nonnull.  */
+      firstarg = 1;
+      check_function_arguments_recurse (check_nonnull_arg, &ctx, argarray[0],
+					firstarg);
+    }
 
-  attrs = lookup_attribute ("nonnull", attrs);
+  tree attrs = lookup_attribute ("nonnull", TYPE_ATTRIBUTES (ctx.fntype));
   if (attrs == NULL_TREE)
-    return false;
+    return ctx.warned_p;
 
-  a = attrs;
+  tree a = attrs;
   /* See if any of the nonnull attributes has no arguments.  If so,
      then every pointer argument is checked (in which case the check
      for pointer type is done in check_nonnull_arg).  */
@@ -5316,16 +5329,15 @@ check_function_nonnull (location_t loc, tree attrs, int nargs, tree *argarray)
       a = lookup_attribute ("nonnull", TREE_CHAIN (a));
     while (a != NULL_TREE && TREE_VALUE (a) != NULL_TREE);
 
-  struct nonnull_arg_ctx ctx = { loc, false };
   if (a != NULL_TREE)
-    for (i = 0; i < nargs; i++)
+    for (int i = firstarg; i < nargs; i++)
       check_function_arguments_recurse (check_nonnull_arg, &ctx, argarray[i],
 					i + 1);
   else
     {
       /* Walk the argument list.  If we encounter an argument number we
 	 should check for non-null, do it.  */
-      for (i = 0; i < nargs; i++)
+      for (int i = firstarg; i < nargs; i++)
 	{
 	  for (a = attrs; ; a = TREE_CHAIN (a))
 	    {
@@ -5495,12 +5507,38 @@ check_nonnull_arg (void *ctx, tree param, unsigned HOST_WIDE_INT param_num)
     return;
 
   /* Diagnose the simple cases of null arguments.  */
-  if (integer_zerop (fold_for_warn (param)))
+  if (!integer_zerop (fold_for_warn (param)))
+    return;
+
+  location_t loc
+    = EXPR_HAS_LOCATION (param) ? EXPR_LOCATION (param) : pctx->loc;
+
+  if (TREE_CODE (pctx->fntype) == METHOD_TYPE)
+    --param_num;
+
+  bool warned;
+  if (param_num == 0)
     {
-      warning_at (pctx->loc, OPT_Wnonnull, "null argument where non-null "
-		  "required (argument %lu)", (unsigned long) param_num);
-      pctx->warned_p = true;
+      warned = warning_at (loc, OPT_Wnonnull,
+			   "%qs pointer null", "this");
+      if (pctx->fndecl)
+	inform (DECL_SOURCE_LOCATION (pctx->fndecl),
+		"in a call to non-static member function %qD",
+		pctx->fndecl);
     }
+  else
+    {
+      warned = warning_at (loc, OPT_Wnonnull,
+			   "argument %u null where non-null expected",
+			   (unsigned) param_num);
+      if (pctx->fndecl)
+	inform (DECL_SOURCE_LOCATION (pctx->fndecl),
+		"in a call to function %qD declared %qs",
+		pctx->fndecl, "nonnull");
+    }
+
+  if (warned)
+    pctx->warned_p = true;
 }
 
 /* Helper for attribute handling; fetch the operand number from
@@ -5717,11 +5755,13 @@ check_function_arguments (location_t loc, const_tree fndecl, const_tree fntype,
   bool warned_p = false;
 
   /* Check for null being passed in a pointer argument that must be
-     non-null.  We also need to do this if format checking is enabled.  */
-
+     non-null.  In C++, this includes the this pointer.  We also need
+     to do this if format checking is enabled.  */
   if (warn_nonnull)
-    warned_p = check_function_nonnull (loc, TYPE_ATTRIBUTES (fntype),
-				       nargs, argarray);
+    {
+      nonnull_arg_ctx ctx = { loc, fndecl, fntype, false };
+      warned_p = check_function_nonnull (ctx, nargs, argarray);
+    }
 
   /* Check for errors in format strings.  */
 
diff --git a/gcc/calls.c b/gcc/calls.c
index d1c9c0b159a..3c6d32d5db9 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -2036,7 +2036,8 @@ maybe_warn_rdwr_sizes (rdwr_map *rwm, tree exp)
 	     attribute nonnull when the function accepts null pointers
 	     only when the corresponding size is zero.  */
 	  bool warned = false;
-	  location_t loc = EXPR_LOCATION (exp);
+	  location_t loc = (EXPR_HAS_LOCATION (ptr)
+			    ? EXPR_LOCATION (ptr) : EXPR_LOCATION (exp));
 	  if (tree_int_cst_equal (sizrng[0], sizrng[1]))
 	    warned = warning_at (loc, OPT_Wnonnull,
 				 "%Kargument %i is null but the corresponding "
diff --git a/gcc/testsuite/c-c++-common/pr28656.c b/gcc/testsuite/c-c++-common/pr28656.c
index 903d7e51f1d..ed97f6c4cb3 100644
--- a/gcc/testsuite/c-c++-common/pr28656.c
+++ b/gcc/testsuite/c-c++-common/pr28656.c
@@ -18,12 +18,12 @@ void
 foo (void)
 {
   memcpy (0, 0, 0);
-  /* { dg-warning "null argument where non-null required\[^\n\r\]*argument 1" "" { target *-*-* } .-1 } */
-  /* { dg-warning "null argument where non-null required\[^\n\r\]*argument 2" "" { target *-*-* } .-2 } */
+  /* { dg-warning "argument 1 null where non-null expected" "" { target *-*-* } .-1 } */
+  /* { dg-warning "argument 2 null where non-null expected" "" { target *-*-* } .-2 } */
 
   bar (0, 0, 0, 0, 0);
-  /* { dg-warning "null argument where non-null required\[^\n\r\]*argument 1" "" { target *-*-* } .-1 } */
-  /* { dg-warning "null argument where non-null required\[^\n\r\]*argument 3" "" { target *-*-* } .-2 } */
-  /* { dg-warning "null argument where non-null required\[^\n\r\]*argument 4" "" { target *-*-* } .-3 } */
-  /* { dg-warning "null argument where non-null required\[^\n\r\]*argument 5" "" { target *-*-* } .-4 } */
+  /* { dg-warning "argument 1 null where non-null expected" "" { target *-*-* } .-1 } */
+  /* { dg-warning "argument 3 null where non-null expected" "" { target *-*-* } .-2 } */
+  /* { dg-warning "argument 4 null where non-null expected" "" { target *-*-* } .-3 } */
+  /* { dg-warning "argument 5 null where non-null expected" "" { target *-*-* } .-4 } */
 }
diff --git a/gcc/testsuite/c-c++-common/pr66208.c b/gcc/testsuite/c-c++-common/pr66208.c
index d394c42b76d..fd67373042b 100644
--- a/gcc/testsuite/c-c++-common/pr66208.c
+++ b/gcc/testsuite/c-c++-common/pr66208.c
@@ -2,7 +2,7 @@
 /* { dg-options "-Wnonnull" } */
 
 void foox (char*, ...) __attribute__ ((nonnull (1)));
-#define foo(p) foox (p, "p is null") /* { dg-warning "null argument" } */
+#define foo(p) foox (p, "p is null") /* { dg-warning "argument 1 null" } */
 
 void baz (void)
 {
diff --git a/gcc/testsuite/g++.dg/cpp0x/nullptr22.C b/gcc/testsuite/g++.dg/cpp0x/nullptr22.C
index 5fbd124b32c..0b326fbb715 100644
--- a/gcc/testsuite/g++.dg/cpp0x/nullptr22.C
+++ b/gcc/testsuite/g++.dg/cpp0x/nullptr22.C
@@ -10,11 +10,11 @@ void f3(const char*, ...) __attribute__((sentinel));
 void f()
 {
   f1("%p", nullptr);
-  f2(nullptr); // { dg-warning "null argument where non-null required " }
+  f2(nullptr); // { dg-warning "argument 1 null where non-null expected " }
   f3("x", "y", __null); // { dg-warning "missing sentinel in function call" }
   f3("x", "y", nullptr);
   decltype(nullptr) mynull = 0;
   f1("%p", mynull);
-  f2(mynull); // { dg-warning "null argument where non-null required " }
+  f2(mynull); // { dg-warning "argument 1 null where non-null expected " }
   f3("x", "y", mynull);
 }
diff --git a/gcc/testsuite/g++.dg/ext/attr-nonnull.C b/gcc/testsuite/g++.dg/ext/attr-nonnull.C
index 5ef754ee377..c448bb07971 100644
--- a/gcc/testsuite/g++.dg/ext/attr-nonnull.C
+++ b/gcc/testsuite/g++.dg/ext/attr-nonnull.C
@@ -21,11 +21,11 @@ f<float>(float*, float*, float*);
 
 void test_nonnull (void)
 {
-  f<void>(0, 0, 0);           // { dg-warning "null argument where non-null required \\\(argument 1\\\)" }
+  f<void>(0, 0, 0);           // { dg-warning "argument 1 null where non-null expected" }
 
-  f<int>(0, 0, 0);            // { dg-bogus "null argument" }
+  f<int>(0, 0, 0);            // { dg-bogus "null" }
 
   f<float>(0, 0, 0);
-  // { dg-bogus "null argument where non-null required \\\(argument 1\\\)" "" { target *-*-* } .-1 }
-  // { dg-warning "null argument where non-null required \\\(argument 3\\\)" "" { target *-*-* } .-2 }
+  // { dg-bogus "argument 1 null where non-null expected" "" { target *-*-* } .-1 }
+  // { dg-warning "argument 3 null where non-null expected" "" { target *-*-* } .-2 }
 }
diff --git a/gcc/testsuite/g++.dg/ext/attrib49.C b/gcc/testsuite/g++.dg/ext/attrib49.C
index 99c6154f1a5..d0ba738494a 100644
--- a/gcc/testsuite/g++.dg/ext/attrib49.C
+++ b/gcc/testsuite/g++.dg/ext/attrib49.C
@@ -10,11 +10,11 @@ void (foo::*g) (int *) __attribute__ ((nonnull (2)));
 void
 fun1 (void (foo::*f) (int *) __attribute__ ((nonnull (2))))
 {
-    (x.*f) ((int *) 0); // { dg-warning "null argument" }
+    (x.*f) ((int *) 0); // { dg-warning "argument 1 null" }
 }
 
 void
 fun2 (void (foo::*f) () __attribute__ ((nonnull, unused))) // { dg-bogus "unused" }
 {
-    (x.*g) ((int *) 0); // { dg-warning "null argument" }
+    (x.*g) ((int *) 0); // { dg-warning "argument 1 null" }
 }
diff --git a/gcc/testsuite/g++.dg/pr71973-2.C b/gcc/testsuite/g++.dg/pr71973-2.C
index d8271b1d874..b0719b6a5a2 100644
--- a/gcc/testsuite/g++.dg/pr71973-2.C
+++ b/gcc/testsuite/g++.dg/pr71973-2.C
@@ -10,7 +10,7 @@ __attribute__ ((__nothrow__));
 
 void foo () throw ()
 {
-  strftime (0,0,0,0); // { dg-warning "null argument where non-null required" }
+  strftime (0,0,0,0); // { dg-warning "argument \(1|3|4\) null where non-null expected" }
   // { dg-warning "too many arguments for format" "" { target *-*-* } .-1 }
 }
 
diff --git a/gcc/testsuite/g++.dg/warn/Wnonnull3.C b/gcc/testsuite/g++.dg/warn/Wnonnull3.C
index d1918ef8e90..cebf36d581d 100644
--- a/gcc/testsuite/g++.dg/warn/Wnonnull3.C
+++ b/gcc/testsuite/g++.dg/warn/Wnonnull3.C
@@ -10,6 +10,6 @@ struct S2 { static const int i = 1; typedef void* U; };
 void
 g ()
 {
-  f<S1>(0); // { dg-warning "null argument where non-null required" }
-  f<S2>(0); // { dg-warning "null argument where non-null required" }
+  f<S1>(0); // { dg-warning "argument 1 null where non-null expected" }
+  f<S2>(0); // { dg-warning "argument 1 null where non-null expected" }
 }
diff --git a/gcc/testsuite/g++.dg/warn/Wnonnull4.C b/gcc/testsuite/g++.dg/warn/Wnonnull4.C
index d07a4452ddb..215781613b2 100644
--- a/gcc/testsuite/g++.dg/warn/Wnonnull4.C
+++ b/gcc/testsuite/g++.dg/warn/Wnonnull4.C
@@ -10,8 +10,8 @@ int
 main ()
 {
   int *const p = 0;
-  declared_not_defined (p);	// { dg-warning "null argument where non-null required" }
-  declared_and_defined (p);	// { dg-warning "null argument where non-null required" }
+  declared_not_defined (p);	// { dg-warning "argument 1 null where non-null expected" }
+  declared_and_defined (p);	// { dg-warning "argument 1 null where non-null expected" }
 }
 
 void *
diff --git a/gcc/testsuite/obj-c++.dg/attributes/method-nonnull-1.mm b/gcc/testsuite/obj-c++.dg/attributes/method-nonnull-1.mm
index 917416d74a1..83f918c5a3f 100644
--- a/gcc/testsuite/obj-c++.dg/attributes/method-nonnull-1.mm
+++ b/gcc/testsuite/obj-c++.dg/attributes/method-nonnull-1.mm
@@ -39,12 +39,12 @@ typedef __SIZE_TYPE__ my_size_t;
 void test (MyArray *object)
 {
   [object addObject: object];
-  [object addObject: nil]; /* { dg-warning "null argument where non-null required" } */
+  [object addObject: nil]; /* { dg-warning "\\\[-Wnonnull" } */
 
   [object insertObject: object atIndex: 4];
-  [object insertObject: nil    atIndex: 4]; /* { dg-warning "null argument where non-null required" } */
+  [object insertObject: nil    atIndex: 4]; /* { dg-warning "\\\[-Wnonnull" } */
 
   [object insertObject: object atIndex: 2 andObject: object atIndex: 3];
-  [object insertObject: nil    atIndex: 2 andObject: object atIndex: 3]; /* { dg-warning "null argument where non-null required" } */
-  [object insertObject: object atIndex: 2 andObject: nil    atIndex: 3]; /* { dg-warning "null argument where non-null required" } */
+  [object insertObject: nil    atIndex: 2 andObject: object atIndex: 3]; /* { dg-warning "\\\[-Wnonnull" } */
+  [object insertObject: object atIndex: 2 andObject: nil    atIndex: 3]; /* { dg-warning "\\\[-Wnonnull" } */
 }
diff --git a/gcc/testsuite/objc.dg/attributes/method-nonnull-1.m b/gcc/testsuite/objc.dg/attributes/method-nonnull-1.m
index e1974aa3cae..fe5f885b2d4 100644
--- a/gcc/testsuite/objc.dg/attributes/method-nonnull-1.m
+++ b/gcc/testsuite/objc.dg/attributes/method-nonnull-1.m
@@ -35,12 +35,12 @@
 void test (MyArray *object)
 {
   [object addObject: object];
-  [object addObject: nil]; /* { dg-warning "null argument where non-null required" } */
+  [object addObject: nil]; /* { dg-warning "\\\[-Wnonnull" } */
 
   [object insertObject: object atIndex: 4];
-  [object insertObject: nil    atIndex: 4]; /* { dg-warning "null argument where non-null required" } */
+  [object insertObject: nil    atIndex: 4]; /* { dg-warning "\\\[-Wnonnull" } */
 
   [object insertObject: object atIndex: 2 andObject: object atIndex: 3];
-  [object insertObject: nil    atIndex: 2 andObject: object atIndex: 3]; /* { dg-warning "null argument where non-null required" } */
-  [object insertObject: object atIndex: 2 andObject: nil    atIndex: 3]; /* { dg-warning "null argument where non-null required" } */
+  [object insertObject: nil    atIndex: 2 andObject: object atIndex: 3]; /* { dg-warning "\\\[-Wnonnull" } */
+  [object insertObject: object atIndex: 2 andObject: nil    atIndex: 3]; /* { dg-warning "\\\[-Wnonnull" } */
 }
diff --git a/gcc/tree-ssa-ccp.c b/gcc/tree-ssa-ccp.c
index e9d2f4bc27a..fc259f4eb5e 100644
--- a/gcc/tree-ssa-ccp.c
+++ b/gcc/tree-ssa-ccp.c
@@ -3540,43 +3540,58 @@ pass_post_ipa_warn::execute (function *fun)
 	  if (!is_gimple_call (stmt) || gimple_no_warning_p (stmt))
 	    continue;
 
-	  if (warn_nonnull)
+	  tree fntype = gimple_call_fntype (stmt);
+	  bitmap nonnullargs = get_nonnull_args (fntype);
+	  if (!nonnullargs)
+	    continue;
+
+	  tree fndecl = gimple_call_fndecl (stmt);
+
+	  for (unsigned i = 0; i < gimple_call_num_args (stmt); i++)
 	    {
-	      bitmap nonnullargs
-		= get_nonnull_args (gimple_call_fntype (stmt));
-	      if (nonnullargs)
+	      tree arg = gimple_call_arg (stmt, i);
+	      if (TREE_CODE (TREE_TYPE (arg)) != POINTER_TYPE)
+		continue;
+	      if (!integer_zerop (arg))
+		continue;
+	      if (!bitmap_empty_p (nonnullargs)
+		  && !bitmap_bit_p (nonnullargs, i))
+		continue;
+
+	      /* In C++ non-static member functions argument 0 refers
+		 to the implicit this pointer.  Use the same one-based
+		 numbering for ordinary arguments.  */
+	      unsigned argno = TREE_CODE (fntype) == METHOD_TYPE ? i : i + 1;
+	      location_t loc = (EXPR_HAS_LOCATION (arg)
+				? EXPR_LOCATION (arg)
+				: gimple_location (stmt));
+	      auto_diagnostic_group d;
+	      if (argno == 0)
 		{
-		  for (unsigned i = 0; i < gimple_call_num_args (stmt); i++)
-		    {
-		      tree arg = gimple_call_arg (stmt, i);
-		      if (TREE_CODE (TREE_TYPE (arg)) != POINTER_TYPE)
-			continue;
-		      if (!integer_zerop (arg))
-			continue;
-		      if (!bitmap_empty_p (nonnullargs)
-			  && !bitmap_bit_p (nonnullargs, i))
-			continue;
-
-		      location_t loc = gimple_location (stmt);
-		      auto_diagnostic_group d;
-		      if (warning_at (loc, OPT_Wnonnull,
-				      "%Gargument %u null where non-null "
-				      "expected", stmt, i + 1))
-			{
-			  tree fndecl = gimple_call_fndecl (stmt);
-			  if (fndecl && DECL_IS_BUILTIN (fndecl))
-			    inform (loc, "in a call to built-in function %qD",
-				    fndecl);
-			  else if (fndecl)
-			    inform (DECL_SOURCE_LOCATION (fndecl),
-				    "in a call to function %qD declared here",
-				    fndecl);
-
-			}
-		    }
-		  BITMAP_FREE (nonnullargs);
+		  if (warning_at (loc, OPT_Wnonnull,
+				  "%G%qs pointer null", stmt, "this")
+		      && fndecl)
+		    inform (DECL_SOURCE_LOCATION (fndecl),
+			    "in a call to non-static member function %qD",
+			    fndecl);
+		  continue;
 		}
+
+	      if (!warning_at (loc, OPT_Wnonnull,
+			       "%Gargument %u null where non-null "
+			       "expected", stmt, argno))
+		continue;
+
+	      tree fndecl = gimple_call_fndecl (stmt);
+	      if (fndecl && DECL_IS_BUILTIN (fndecl))
+		inform (loc, "in a call to built-in function %qD",
+			fndecl);
+	      else if (fndecl)
+		inform (DECL_SOURCE_LOCATION (fndecl),
+			"in a call to function %qD declared %qs",
+			fndecl, "nonnull");
 	    }
+	  BITMAP_FREE (nonnullargs);
 	}
     }
   return 0;
diff --git a/gcc/tree.c b/gcc/tree.c
index 7197b4720ce..1877e8f1fcd 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -14959,11 +14959,18 @@ get_nonnull_args (const_tree fntype)
   if (fntype == NULL_TREE)
     return NULL;
 
+  bitmap argmap = NULL;
+  if (TREE_CODE (fntype) == METHOD_TYPE)
+    {
+      /* The this pointer in C++ non-static member functions is
+	 implicitly nonnull whether or not it's declared as such.  */
+      argmap = BITMAP_ALLOC (NULL);
+      bitmap_set_bit (argmap, 0);
+    }
+
   tree attrs = TYPE_ATTRIBUTES (fntype);
   if (!attrs)
-    return NULL;
-
-  bitmap argmap = NULL;
+    return argmap;
 
   /* A function declaration can specify multiple attribute nonnull,
      each with zero or more arguments.  The loop below creates a bitmap
diff --git a/gcc/var-tracking.c b/gcc/var-tracking.c
index fc861a0d8ce..899a5c0290d 100644
--- a/gcc/var-tracking.c
+++ b/gcc/var-tracking.c
@@ -305,6 +305,9 @@ struct expand_depth
   int entryvals;
 };
 
+/* Type for dependencies actively used when expand FROM into cur_loc.  */
+typedef vec<loc_exp_dep, va_heap, vl_embed> deps_vec;
+
 /* This data structure is allocated for one-part variables at the time
    of emitting notes.  */
 struct onepart_aux
@@ -325,7 +328,7 @@ struct onepart_aux
   /* The depth of the cur_loc expression.  */
   expand_depth depth;
   /* Dependencies actively used when expand FROM into cur_loc.  */
-  vec<loc_exp_dep, va_heap, vl_embed> deps;
+  deps_vec deps;
 };
 
 /* Structure describing one part of variable.  */
@@ -434,10 +437,16 @@ int_mem_offset (const_rtx mem)
 			       : NULL)
 #define VAR_LOC_FROM(var) (VAR_LOC_1PAUX (var)->from)
 #define VAR_LOC_DEPTH(var) (VAR_LOC_1PAUX (var)->depth)
-#define VAR_LOC_DEP_VEC(var) (VAR_LOC_1PAUX (var)		  \
-			      ? &VAR_LOC_1PAUX (var)->deps	  \
-			      : NULL)
+#define VAR_LOC_DEP_VEC(var) var_loc_dep_vec (var)
+
+/* Implements the VAR_LOC_DEP_VEC above as a function to work around
+   a bogus -Wnonnull (PR c/95554). */
 
+static inline deps_vec*
+var_loc_dep_vec (variable *var)
+{
+  return VAR_LOC_1PAUX (var) ? &VAR_LOC_1PAUX (var)->deps : NULL;
+}
 
 
 typedef unsigned int dvuid;
@@ -8112,7 +8121,7 @@ loc_exp_dep_alloc (variable *var, int count)
     return;
 
   allocsize = offsetof (struct onepart_aux, deps)
-	      + vec<loc_exp_dep, va_heap, vl_embed>::embedded_size (count);
+	      + deps_vec::embedded_size (count);
 
   if (VAR_LOC_1PAUX (var))
     {
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wnonnull5.C
@@ -0,0 +1,108 @@
+/* PR c++/86568 - -Wnonnull warnings should highlight the relevant argument
+   not the closing parenthesis.
+   { dg-do compile }
+   { dg-options "-O2 -Wall" } */
+
+#define NONNULL __attribute__ ((nonnull))
+
+#if __cplusplus < 201103L
+#  define nullptr __null
+#endif
+
+struct S
+{
+  void
+  f0 (const void*) const;         // { dg-message "in a call to non-static member function 'void S::f0\\(const void\\*\\) const'" }
+
+  void
+  f1 (const void*) const;         // { dg-message "in a call to non-static member function 'void S::f1\\(const void\\*\\) const'" }
+
+  void
+  f2 (const void*) const;         // { dg-message "in a call to non-static member function 'void S::f2\\(const void\\*\\) const'" }
+
+  NONNULL void
+  f3 (const void*, const void*);  // { dg-message "in a call to function 'void S::f3\\(const void\\*, const void\\*\\)' declared 'nonnull'" }
+
+  NONNULL void
+  f4 (const void*, const void*);  // { dg-message "in a call to function 'void S::f4\\(const void\\*, const void\\*\\)' declared 'nonnull'" }
+
+  NONNULL void
+  f5 (const void*, const void*);  // { dg-message "in a call to function 'void S::f5\\\(const void\\*, const void\\*\\)' declared 'nonnull'" }
+
+  NONNULL void
+  f6 (const void*, const void*);  // { dg-message "in a call to function 'void S::f6\\\(const void\\*, const void\\*\\)' declared 'nonnull'" }
+};
+
+void warn_nullptr_this ()
+{
+  ((S*)nullptr)->f0 ("");        // { dg-warning "3:'this' pointer null" "pr86568" { xfail *-*-* } }
+                                 // { dg-warning "this' pointer null" "pr86568" { target *-*-* } .-1 }
+}
+
+void warn_null_this_cst ()
+{
+  S* const null = 0;
+  null->f1 ("");                  // { dg-warning "3:'this' pointer null" }
+}
+
+void warn_null_this_var ()
+{
+  S* null = 0;
+  null->f2 (&null);               // { dg-warning "3:'this' pointer null" "pr86568" { xfail *-*-* } }
+                                  // { dg-warning "'this' pointer null" "pr86568" { target *-*-* } .-1 }
+}
+
+void warn_nullptr (S s)
+{
+  s.f3 (nullptr, &s);              // { dg-warning "9:argument 1 null where non-null expected" "pr86568" { xfail *-*-* } }
+                                   // { dg-warning "argument 1 null where non-null expected" "pr86568" { target *-*-* } .-1 }
+  s.f3 (&s, nullptr);              // { dg-warning "13:argument 2 null where non-null expected" "pr86568" { xfail *-*-* } }
+                                   // { dg-warning "argument 2 null where non-null expected" "pr86568" { target *-*-* } .-1 }
+}
+
+
+void warn_null_cst (S s)
+{
+  void* const null = 0;
+  s.f4 (null, &s);                 // { dg-warning "9:argument 1 null where non-null expected" }
+  s.f4 (&s, null);                 // { dg-warning "13:argument 2 null where non-null expected" }
+}
+
+void warn_null_var (S s)
+{
+  void* null = 0;
+  s.f5 (null, &s);                // { dg-warning "9:argument 1 null where non-null expected" "pr86568" { xfail *-*-* } }
+                                  // { dg-warning "argument 1 null where non-null expected" "pr86568" { target *-*-* } .-1 }
+  s.f5 (&s, null);                // { dg-warning "16:argument 2 null where non-null expected" "pr86568" { xfail *-*-* } }
+                                  // { dg-warning "argument 2 null where non-null expected" "pr86568" { target *-*-* } .-1 }
+}
+
+void warn_null_cond (S s, void *null)
+{
+  if (null)
+    return;
+
+  s.f6 (null, &s);                // { dg-warning "9:argument 1 null where non-null expected" "pr86568" { xfail *-*-* } }
+                                  // { dg-warning "argument 1 null where non-null expected" "pr86568" { target *-*-* } .-1 }
+  s.f6 (&s, null);                // { dg-warning "13:argument 2 null where non-null expected" "pr86568" { xfail *-*-* } }
+                                  // { dg-warning "argument 2 null where non-null expected" "pr86568" { target *-*-* } .-1 }
+}
+
+
+typedef NONNULL void Fvp (const void*, const void*);
+
+void warn_fptr_null_cst (Fvp *p)
+{
+  void* const null = 0;
+  p (null, "");                   // { dg-warning "6:argument 1 null where non-null expected" }
+  p ("", null);                   // { dg-warning "10:argument 2 null where non-null expected" }
+}
+
+typedef NONNULL void (S::*SMemFvp) (const void*, const void*);
+
+void warn_memfptr_null_cst (S *p, SMemFvp pmf)
+{
+  void* const null = 0;
+  (p->*pmf) (null, "");           // { dg-warning "14:argument 1 null where non-null expected" }
+  (p->*pmf) ("", null);           // { dg-warning "18:argument 2 null where non-null expected" }
+}

Reply via email to