PR c/85931 -  -Wsizeof-pointer-memaccess for strncpy with size of source

gcc/c-family/ChangeLog:

	PR c/85931
	* c-warn.c (sizeof_pointer_memaccess_warning): Avoid warning when
	sizeof source and destination yields the same value.

gcc/ChangeLog:

	PR c/85931
	* fold-const.c (operand_equal_p): Handle SAVE_EXPR.

gcc/testsuite/ChangeLog:

	PR c/85931
	* gcc.dg/Wstringop-truncation-3.c: New test.

diff --git a/gcc/c-family/c-warn.c b/gcc/c-family/c-warn.c
index e7bcbb1..96a56d4 100644
--- a/gcc/c-family/c-warn.c
+++ b/gcc/c-family/c-warn.c
@@ -792,13 +792,31 @@ sizeof_pointer_memaccess_warning (location_t *sizeof_arg_loc, tree callee,
     {
       /* The argument type may be an array.  Diagnose bounded string
 	 copy functions that specify the bound in terms of the source
-	 argument rather than the destination.  */
+	 argument rather than the destination unless they are equal
+	 to one another.  Handle constant sizes and also try to handle
+	 sizeof expressions involving VLAs.  */
       if (strop && !cmp && fncode != BUILT_IN_STRNDUP && src)
 	{
 	  tem = tree_strip_nop_conversions (src);
 	  if (TREE_CODE (tem) == ADDR_EXPR)
 	    tem = TREE_OPERAND (tem, 0);
-	  if (operand_equal_p (tem, sizeof_arg[idx], OEP_ADDRESS_OF))
+
+	  tree d = tree_strip_nop_conversions (dest);
+	  if (TREE_CODE (d) == ADDR_EXPR)
+	    d = TREE_OPERAND (d, 0);
+
+	  tree dstsz = TYPE_SIZE_UNIT (TREE_TYPE (d));
+	  tree srcsz = TYPE_SIZE_UNIT (TREE_TYPE (tem));
+
+	  if ((!dstsz
+	       || !srcsz
+	       || (TREE_CODE (dstsz) != INTEGER_CST
+		   && TREE_CODE (srcsz) != INTEGER_CST
+		   && !operand_equal_p (dstsz, srcsz, OEP_LEXICOGRAPHIC))
+	       || (TREE_CODE (dstsz) == INTEGER_CST
+		   && TREE_CODE (srcsz) == INTEGER_CST
+		   && !tree_int_cst_equal (dstsz, srcsz)))
+	      && operand_equal_p (tem, sizeof_arg[idx], OEP_ADDRESS_OF))
 	    warning_at (sizeof_arg_loc[idx], OPT_Wsizeof_pointer_memaccess,
 			"argument to %<sizeof%> in %qD call is the same "
 			"expression as the source; did you mean to use "
diff --git a/gcc/fold-const.c b/gcc/fold-const.c
index 3258aad..ead6e53 100644
--- a/gcc/fold-const.c
+++ b/gcc/fold-const.c
@@ -3358,6 +3358,7 @@ operand_equal_p (const_tree arg0, const_tree arg1, unsigned int flags)
 
 	case CLEANUP_POINT_EXPR:
 	case EXPR_STMT:
+	case SAVE_EXPR:
 	  if (flags & OEP_LEXICOGRAPHIC)
 	    return OP_SAME (0);
 	  return 0;
diff --git a/gcc/testsuite/gcc.dg/Wstringop-truncation-3.c b/gcc/testsuite/gcc.dg/Wstringop-truncation-3.c
new file mode 100644
index 0000000..57f4d64
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-truncation-3.c
@@ -0,0 +1,59 @@
+/* PR c/85931 - -Wsizeof-pointer-memaccess for strncpy with size of source
+   { dg-do compile }
+   { dg-options "-O2 -Wall -Wstringop-truncation -ftrack-macro-expansion=0" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+extern char* strncpy (char*, const char*, size_t);
+
+extern char a3[3], b3[3];
+extern char a5[5], b5[5];
+extern char ax[], bx[];
+
+struct SA
+{
+  char a3[3], b3[3];
+  char a5[5], b5[5];
+  char ax[];
+};
+
+void sink (void*, ...);
+
+#define T(d, s, n)   sink (strncpy (d, s, n))
+
+void test_array (unsigned n)
+{
+  T (a3, b3, 3);
+  /* For the following statemenmt, GCC 8.1 issues warning:
+
+       argument to ‘sizeof’ in ‘strncpy’ call is the same expression
+       as the source; did you mean to use the size of the destination?
+
+     Since the size of both the source and destination the warning
+     isn't helpful.  Verify that it isn't issued.  */
+  T (a3, b3, sizeof b3);    /* { dg-bogus "\\\[-Wsizeof-pointer-memaccess" } */
+
+  T (a3, ax, sizeof a3);    /* { dg-warning "\\\[-Wstringop-truncation" } */
+  T (ax, a3, sizeof a3);    /* { dg-warning "argument to .sizeof. in .strncpy. call is the same expression as the source" } */
+
+  char an[n], bn[n];
+  sink (an, bn);
+
+  T (an, bn, sizeof bn);    /* { dg-bogus "\\\[-Wsizeof-pointer-memaccess" } */
+}
+
+void test_member_array (struct SA *sa, unsigned n)
+{
+  T (sa->a3, sa->b3, 3);
+  T (sa->a3, sa->b3, sizeof sa->b3);  /* { dg-bogus "\\\[-Wsizeof-pointer-memaccess" } */
+
+  T (sa->a3, sa->ax, sizeof sa->a3);  /* { dg-warning "\\\[-Wstringop-truncation" } */
+  T (sa->ax, sa->a3, sizeof sa->a3);  /* { dg-warning "argument to .sizeof. in .strncpy. call is the same expression as the source" } */
+
+  struct VarLenStruct {
+    char an[n], bn[n];
+  } x;
+
+  sink (&x);
+  T (x.an, x.bn, sizeof x.bn);        /* { dg-bogus "\\\[-Wsizeof-pointer-memaccess" } */
+}
