if (POINTER_TYPE_P (type) || TREE_CODE (type) == ARRAY_TYPE)
{
/* Accept the attribute on arrays and pointers to all three
- narrow character types. */
- tree eltype = TREE_TYPE (type);
+ narrow character types, including multi-dimensional arrays
+ or pointers to them. */
+ tree eltype = strip_array_types (TREE_TYPE (type));
eltype = TYPE_MAIN_VARIANT (eltype);
if (eltype == char_type_node
|| eltype == signed_char_type_node
--- /dev/null
+/* Test to exercise attribute "nonstring".
+ { dg-do compile }
+ { dg-options "-O2 -Wattributes -Wstringop-truncation -ftrack-macro-expansion=0" } */
+
+#define ATTR(list) __attribute__ (list)
+#define NONSTR ATTR ((nonstring))
+#define strncpy(d, s, n) (__builtin_strncpy ((d), (s), (n)), sink (d))
+
+void sink (void*);
+
+/* Global string with an known bound. */
+extern char gns3[][3] NONSTR;
+
+/* Global non-string pointers. */
+extern NONSTR char (*pns_1)[1];
+extern char (* NONSTR pns_2)[2];
+extern char (*pns_3)[3] NONSTR;
+
+struct MemArrays
+{
+ NONSTR char ma3[2][3];
+ char NONSTR ma4[3][4];
+ char ma5[4][5] NONSTR;
+};
+
+
+void test_array (const char *s, unsigned n)
+{
+ const char s7[] = "1234567";
+
+ strncpy (gns3[2], "", 0);
+ strncpy (gns3[2], "a", 1);
+ strncpy (gns3[2], "a", 2);
+ strncpy (gns3[2], "a", 3);
+ strncpy (gns3[2], "ab", 3);
+ strncpy (gns3[2], "abc", 3);
+}
+
+
+void test_pointer (const char *s, unsigned n)
+{
+ const char s7[] = "1234567";
+
+ strncpy (*pns_1, "a", 1);
+ strncpy (*pns_2, "ab", 2);
+ strncpy (*pns_3, "abc", 3);
+ strncpy (*pns_3, s7, 3);
+
+ strncpy (*pns_1, s, 1);
+ strncpy (*pns_2, s, 1);
+ strncpy (*pns_3, s, 1);
+
+ strncpy (*pns_1, s, n);
+ strncpy (*pns_2, s, n);
+ strncpy (*pns_3, s, n);
+}
+
+
+void test_member_array (struct MemArrays *p, const char *s, unsigned n)
+{
+ const char s7[] = "1234567";
+
+ strncpy (p->ma3[1], "", 0);
+ strncpy (p->ma3[1], "a", 1);
+ strncpy (p->ma4[2], "ab", 2);
+ strncpy (p->ma5[3], "abc", 3);
+
+ strncpy (p->ma3[1], s, 1);
+ strncpy (p->ma4[2], s, 1);
+ strncpy (p->ma5[3], s, 1);
+
+ strncpy (p->ma3[1], s7, n);
+ strncpy (p->ma4[2], s7, n);
+ strncpy (p->ma5[3], s7, n);
+}
--- /dev/null
+/* Test to exercise warnings when an array declared with attribute "nonstring"
+ is passed to a function that expects a nul-terminated string as an argument.
+ { dg-do compile }
+ { dg-options "-O2 -Wattributes -Wstringop-overflow -ftrack-macro-expansion=0" } */
+
+typedef __SIZE_TYPE__ size_t;
+typedef __builtin_va_list va_list;
+
+#if __cplusplus
+extern "C" {
+#endif
+
+void* memchr (const void*, int, size_t);
+int memcmp (const void*, const void*, size_t);
+void* memcpy (void*, const void*, size_t);
+void* memmove (void*, const void*, size_t);
+
+int printf (const char*, ...);
+int puts (const char*);
+int puts_unlocked (const char*);
+int sprintf (char*, const char*, ...);
+int snprintf (char*, size_t, const char*, ...);
+int vsprintf (char*, const char*, va_list);
+int vsnprintf (char*, size_t, const char*, va_list);
+
+int strcmp (const char*, const char*);
+int strncmp (const char*, const char*, size_t);
+
+char* stpcpy (char*, const char*);
+char* stpncpy (char*, const char*, size_t);
+
+char* strcat (char*, const char*);
+char* strncat (char*, const char*, size_t);
+
+char* strcpy (char*, const char*);
+char* strncpy (char*, const char*, size_t);
+
+char* strchr (const char*, int);
+char* strdup (const char*);
+size_t strlen (const char*);
+size_t strnlen (const char*, size_t);
+char* strndup (const char*, size_t);
+
+#if __cplusplus
+} /* extern "C" */
+#endif
+
+#define NONSTRING __attribute__ ((nonstring))
+
+/* STR needs to be bigger than ARR to trigger warnings, otherwise
+ since STR must be a string, using both in a string function
+ can be assumed to be safe even if ARR isn't nul-terminated. */
+char str[3][5];
+char arr[3][4] NONSTRING;
+
+char (*ptr)[6];
+char (*parr)[6] NONSTRING;
+
+struct MemArrays
+{
+ char str[5][5];
+ char arr[4][4] NONSTRING;
+ char (*parr)[4] NONSTRING;
+};
+
+void sink (int, ...);
+
+
+#define T(call) sink (0, call)
+
+void test_printf (struct MemArrays *p)
+{
+ T (printf (str[2]));
+ T (printf (arr[2])); /* { dg-warning "argument 1 declared attribute .nonstring." } */
+
+ T (printf (*ptr));
+ T (printf (*parr)); /* { dg-warning "argument 1 declared attribute .nonstring." } */
+
+ T (printf (p->str[2]));
+ T (printf (p->arr[2])); /* { dg-warning "argument 1 declared attribute .nonstring." } */
+}
+
+
+void test_puts (struct MemArrays *p)
+{
+ T (puts (str[2]));
+ T (puts (arr[2])); /* { dg-warning "argument 1 declared attribute .nonstring." } */
+
+ T (puts (*ptr));
+ T (puts (*parr)); /* { dg-warning "argument 1 declared attribute .nonstring." } */
+
+ T (puts (p->str[2]));
+ T (puts (p->arr[2])); /* { dg-warning "argument 1 declared attribute .nonstring." } */
+}
+
+
+void test_snprintf (char *d, size_t n, struct MemArrays *p)
+{
+ T (snprintf (d, n, str[2]));
+ T (snprintf (d, n, arr[2])); /* { dg-warning "argument 3 declared attribute .nonstring." } */
+
+ T (snprintf (d, n, *ptr));
+ T (snprintf (d, n, *parr)); /* { dg-warning "argument 3 declared attribute .nonstring." } */
+
+ T (snprintf (d, n, p->str[2]));
+ T (snprintf (d, n, p->arr[2])); /* { dg-warning "argument 3 declared attribute .nonstring." } */
+}
+
+
+void test_sprintf (char *d, struct MemArrays *p)
+{
+ T (sprintf (d, str[2]));
+ T (sprintf (d, arr[2])); /* { dg-warning "argument 2 declared attribute .nonstring." } */
+
+ T (sprintf (d, *ptr));
+ T (sprintf (d, *parr)); /* { dg-warning "argument 2 declared attribute .nonstring." } */
+
+ T (sprintf (d, p->str[2]));
+ T (sprintf (d, p->arr[2])); /* { dg-warning "argument 2 declared attribute .nonstring." } */
+}
+
+
+void test_vsnprintf (char *d, size_t n, struct MemArrays *p, va_list va)
+{
+ T (vsnprintf (d, n, str[2], va));
+ T (vsnprintf (d, n, arr[2], va)); /* { dg-warning "argument 3 declared attribute .nonstring." } */
+
+ T (vsnprintf (d, n, *ptr, va));
+ T (vsnprintf (d, n, *parr, va)); /* { dg-warning "argument 3 declared attribute .nonstring." } */
+
+ T (vsnprintf (d, n, p->str[2], va));
+ T (vsnprintf (d, n, p->arr[2], va)); /* { dg-warning "argument 3 declared attribute .nonstring." } */
+}
+
+
+void test_vsprintf (char *d, struct MemArrays *p, va_list va)
+{
+ T (vsprintf (d, str[2], va));
+ T (vsprintf (d, arr[2], va)); /* { dg-warning "argument 2 declared attribute .nonstring." } */
+
+ T (vsprintf (d, *ptr, va));
+ T (vsprintf (d, *parr, va)); /* { dg-warning "argument 2 declared attribute .nonstring." } */
+
+ T (vsprintf (d, p->str[2], va));
+ T (vsprintf (d, p->arr[2], va)); /* { dg-warning "argument 2 declared attribute .nonstring." } */
+}
+
+
+void test_strcmp (struct MemArrays *p)
+{
+ T (strcmp (str[2], str[2]));
+ T (strcmp (str[2], arr[2])); /* { dg-warning "argument 2 declared attribute .nonstring." } */
+ T (strcmp (arr[2], str[2])); /* { dg-warning "argument 1 declared attribute .nonstring." } */
+
+ T (strcmp (str[2], *ptr));
+ T (strcmp (str[2], *parr)); /* { dg-warning "argument 2 declared attribute .nonstring." } */
+ T (strcmp (*parr, str[2])); /* { dg-warning "argument 1 declared attribute .nonstring." } */
+
+ T (strcmp (p->str[2], p->arr[2])); /* { dg-warning "argument 2 declared attribute .nonstring." } */
+ T (strcmp (p->arr[2], p->str[2])); /* { dg-warning "argument 1 declared attribute .nonstring." } */
+ T (strcmp (*p->parr, p->str[2])); /* { dg-warning "argument 1 declared attribute .nonstring." } */
+}
+
+
+void test_strncmp_warn (struct MemArrays *p)
+{
+ enum { N = sizeof arr[2] };
+ T (strncmp (str[2], arr[2], N)); /* { dg-bogus "argument 2 declared attribute 'nonstring' is smaller than the specified bound 4" "" { xfail *-*-* } } */
+ T (strncmp (arr[2], str[2], N)); /* { dg-bogus "argument 1 declared attribute 'nonstring' is smaller than the specified bound 4" "" { xfail *-*-* } } */
+
+ T (strncmp (str[2], arr[2], N + 1)); /* { dg-warning "argument 2 declared attribute .nonstring. is smaller than the specified bound 5" } */
+ T (strncmp (arr[2], str[2], N + 1)); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound 5" } */
+
+ T (strncmp (str[2], *parr, N + 1));
+ T (strncmp (*parr, str[2], N + 1));
+
+ T (strncmp (p->str[2], p->arr[2], N));
+ T (strncmp (p->arr[2], p->str[2], N));
+ T (strncmp (*p->parr, p->str[2], N));
+
+ T (strncmp (p->str[2], p->arr[2], N));
+ T (strncmp (p->arr[2], p->str[2], N));
+ T (strncmp (*p->parr, p->str[2], N));
+}
+
+
+void test_strncmp_nowarn (struct MemArrays *p, size_t n)
+{
+ T (strncmp (str[2], str[2], n));
+ T (strncmp (str[2], arr[2], n));
+ T (strncmp (arr[2], str[2], n));
+
+ T (strncmp (str[2], *ptr, n));
+ T (strncmp (str[2], *parr, n));
+ T (strncmp (*parr, str[2], n));
+
+ T (strncmp (p->str[2], p->arr[2], n));
+ T (strncmp (p->arr[2], p->str[2], n));
+ T (strncmp (*p->parr, p->str[2], n));
+}
+
+
+void test_stpcpy (struct MemArrays *p)
+{
+ T (stpcpy (str[2], str[2]));
+ T (stpcpy (str[2], arr[2])); /* { dg-warning "argument 2 declared attribute .nonstring." } */
+ T (stpcpy (arr[2], str[2]));
+
+ T (stpcpy (str[2], *ptr));
+ T (stpcpy (str[2], *parr)); /* { dg-warning "argument 2 declared attribute .nonstring." } */
+ T (stpcpy (*parr, str[2]));
+
+ T (stpcpy (p->str[2], p->arr[2])); /* { dg-warning "argument 2 declared attribute .nonstring." } */
+ T (stpcpy (p->arr[2], p->str[2]));
+ T (stpcpy (*p->parr, p->str[2]));
+}
+
+
+void test_stpncpy_nowarn (struct MemArrays *p, unsigned n)
+{
+ T (stpncpy (str[2], str[2], n));
+ T (stpncpy (str[2], arr[2], n));
+ T (stpncpy (arr[2], str[2], n));
+
+ T (stpncpy (str[2], *ptr, n));
+ T (stpncpy (str[2], *parr, n));
+ T (stpncpy (*parr, str[2], n));
+
+ T (stpncpy (p->str[2], p->arr[2], n));
+ T (stpncpy (p->arr[2], p->str[2], n));
+ T (stpncpy (*p->parr, p->str[2], n));
+}
+
+
+void test_stpncpy_warn (struct MemArrays *p, unsigned n)
+{
+ enum { N = sizeof arr[2] };
+
+ T (stpncpy (str[2], str[2], N));
+ T (stpncpy (str[2], arr[2], N)); /* { dg-bogus "argument 2 declared attribute 'nonstring' is smaller than the specified bound 4" "" { xfail *-*-* } } */
+ T (stpncpy (arr[2], str[2], N));
+
+ T (stpncpy (str[2], *ptr, N));
+ T (stpncpy (str[2], *parr, N));
+ T (stpncpy (*parr, str[2], N));
+
+ T (stpncpy (p->str[2], p->arr[2], N));
+ T (stpncpy (p->arr[2], p->str[2], N));
+ T (stpncpy (*p->parr, p->str[2], N));
+
+ T (stpncpy (*ptr, str[2], N + 1));
+ T (stpncpy (*ptr, arr[2], N + 1)); /* { dg-warning "argument 2 declared attribute .nonstring. is smaller than the specified bound 5" } */
+ T (stpncpy (arr[2], str[2], N + 1)); /* { dg-warning "writing 5 bytes into a region of size 4 overflows " } */
+
+ T (stpncpy (*ptr, *ptr, N + 1));
+ T (stpncpy (*ptr, *parr, N + 1));
+ T (stpncpy (*parr, str[2], N + 1));
+
+ T (stpncpy (*ptr, p->arr[2], N + 1)); /* { dg-warning "argument 2 declared attribute .nonstring. is smaller" } */
+ T (stpncpy (p->arr[2], p->str[2], N + 1)); /* { dg-warning "writing 5 bytes into a region of size 4 overflows " } */
+ T (stpncpy (*p->parr, p->str[2], N + 1));
+}
+
+
+void test_strcat (struct MemArrays *p)
+{
+ T (strcat (str[2], str[2]));
+ T (strcat (str[2], arr[2])); /* { dg-warning "argument 2 declared attribute .nonstring." } */
+ T (strcat (arr[2], str[2]));
+
+ T (strcat (str[2], *ptr));
+ T (strcat (str[2], *parr)); /* { dg-warning "argument 2 declared attribute .nonstring." } */
+ T (strcat (*parr, str[2]));
+
+ T (strcat (p->str[2], p->arr[2])); /* { dg-warning "argument 2 declared attribute .nonstring." } */
+ T (strcat (p->arr[2], p->str[2]));
+ T (strcat (*p->parr, p->str[2]));
+}
+
+
+void test_strncat (struct MemArrays *p, unsigned n)
+{
+ T (strncat (str[2], str[2], n));
+ T (strncat (str[2], arr[2], n)); /* { dg-warning "argument 2 declared attribute .nonstring." } */
+ T (strncat (arr[2], str[2], n));
+
+ T (strncat (str[2], *ptr, n));
+ T (strncat (str[2], *parr, n)); /* { dg-warning "argument 2 declared attribute .nonstring." } */
+ T (strncat (*parr, str[2], n));
+
+ T (strncat (p->str[2], p->arr[2], n)); /* { dg-warning "argument 2 declared attribute .nonstring." } */
+ T (strncat (p->arr[2], p->str[2], n));
+ T (strncat (*p->parr, p->str[2], n));
+}
+
+
+void test_strcpy (struct MemArrays *p)
+{
+ T (strcpy (str[2], str[2]));
+ T (strcpy (str[2], arr[2])); /* { dg-warning "argument 2 declared attribute .nonstring." } */
+ T (strcpy (arr[2], str[2]));
+
+ T (strcpy (str[2], *ptr));
+ T (strcpy (str[2], *parr)); /* { dg-warning "argument 2 declared attribute .nonstring." } */
+ T (strcpy (*parr, str[2]));
+
+ T (strcpy (p->str[2], p->arr[2])); /* { dg-warning "argument 2 declared attribute .nonstring." } */
+ T (strcpy (p->arr[2], p->str[2]));
+ T (strcpy (*p->parr, p->str[2]));
+}
+
+
+void test_strncpy (struct MemArrays *p, unsigned n)
+{
+ T (strncpy (str[2], str[2], n));
+ T (strncpy (str[2], arr[2], n));
+ T (strncpy (arr[2], str[2], n));
+
+ T (strncpy (str[2], *ptr, n));
+ T (strncpy (str[2], *parr, n));
+ T (strncpy (*parr, str[2], n));
+
+ T (strncpy (p->str[2], p->arr[2], n));
+ T (strncpy (p->arr[2], p->str[2], n));
+ T (strncpy (*p->parr, p->str[2], n));
+}
+
+
+void test_strchr (struct MemArrays *p, int c)
+{
+ T (strchr (str[2], c));
+ T (strchr (arr[2], c)); /* { dg-warning "argument 1 declared attribute .nonstring." } */
+
+ T (strchr (*ptr, c));
+ T (strchr (*parr, c)); /* { dg-warning "argument 1 declared attribute .nonstring." } */
+
+ T (strchr (p->str[2], c));
+ T (strchr (p->arr[2], c)); /* { dg-warning "argument 1 declared attribute .nonstring." } */
+}
+
+
+void test_strdup (struct MemArrays *p)
+{
+ T (strdup (str[2]));
+ T (strdup (arr[2])); /* { dg-warning "argument 1 declared attribute .nonstring." } */
+
+ T (strdup (*ptr));
+ T (strdup (*parr)); /* { dg-warning "argument 1 declared attribute .nonstring." } */
+
+ T (strdup (p->str[2]));
+ T (strdup (p->arr[2])); /* { dg-warning "argument 1 declared attribute .nonstring." } */
+}
+
+
+void test_stnrdup_nowarn (struct MemArrays *p, size_t n)
+{
+ T (strndup (str[2], n));
+ T (strndup (arr[2], n));
+
+ T (strndup (*ptr, n));
+ T (strndup (*parr, n));
+
+ T (strndup (p->str[2], n));
+ T (strndup (p->arr[2], n));
+}
+
+
+void test_stnrdup_warn (struct MemArrays *p)
+{
+ enum { N = sizeof arr[2] };
+
+ T (strndup (str[2], N));
+ T (strndup (arr[2], N)); /* { dg-bogus "argument 1 declared attribute 'nonstring' is smaller than the specified bound 4" "" { xfail *-*-* } } */
+
+ T (strndup (*ptr, N));
+ T (strndup (*parr, N));
+
+ T (strndup (p->str[2], N));
+ T (strndup (p->arr[2], N));
+
+
+ T (strndup (arr[2], N + 1)); /* { dg-warning "argument 1 declared attribute 'nonstring' is smaller than the specified bound 5|specified bound 5 exceeds source size 4" } */
+ T (strndup (*parr, N + 1));
+ T (strndup (p->arr[2], N + 1)); /* { dg-warning "argument 1 declared attribute 'nonstring' is smaller than the specified bound 5|specified bound 5 exceeds source size 4" } */
+ T (strndup (*p->parr, N + 1));
+}
+
+
+void test_strlen (struct MemArrays *p, char *s NONSTRING, size_t n)
+{
+ T (strlen (str[2]));
+ T (strlen (arr[2])); /* { dg-warning "argument 1 declared attribute .nonstring." } */
+
+ T (strlen (*ptr));
+ T (strlen (*parr)); /* { dg-warning "argument 1 declared attribute .nonstring." } */
+
+ T (strlen (p->str[2]));
+ T (strlen (p->arr[2])); /* { dg-warning "argument 1 declared attribute .nonstring." } */
+
+ T (strlen (s)); /* { dg-warning "argument 1 declared attribute .nonstring." } */
+ {
+ strcpy (s, "123");
+ T (strlen (s));
+ }
+
+ {
+ char a[][3] __attribute__ ((nonstring)) = { { 1, 2, 3 } };
+
+ T (strlen (a[0])); /* { dg-warning "argument 1 declared attribute .nonstring." } */
+ }
+
+ {
+ char a[][4] __attribute__ ((nonstring)) = { { 1, 2, 3, 4 } };
+
+ strcpy (a[0], "12");
+ T (strlen (a[0]));
+ }
+}
+
+
+void test_strnlen (struct MemArrays *p, size_t n)
+{
+ T (strnlen (str[2], n));
+ T (strnlen (arr[2], n));
+
+ T (strnlen (*ptr, n));
+ T (strnlen (*parr, n));
+
+ T (strnlen (p->str[2], n));
+ T (strnlen (p->arr[2], n));
+}
+
+
+/* Verify no warnings are issued for raw mempory functions. */
+
+void test_mem_functions (struct MemArrays *p, int c, size_t n)
+{
+ T (memchr (arr[2], c, n));
+ T (memchr (*parr, c, n));
+ T (memchr (p->arr[2], c, n));
+ T (memchr (*p->parr, c, n));
+
+ T (memcmp (str[2], arr[2], n));
+ T (memcmp (arr[2], str[2], n));
+ T (memcmp (str[2], *parr, n));
+ T (memcmp (*parr, str[2], n));
+ T (memcmp (p->str[2], p->arr[2], n));
+ T (memcmp (p->arr[2], p->str[2], n));
+ T (memcmp (*p->parr, p->str[2], n));
+
+ T (memcpy (str[2], arr[2], n));
+ T (memcpy (arr[2], str[2], n));
+ T (memcpy (str[2], *parr, n));
+ T (memcpy (*parr, str[2], n));
+ T (memcpy (p->str[2], p->arr[2], n));
+ T (memcpy (p->arr[2], p->str[2], n));
+ T (memcpy (*p->parr, p->str[2], n));
+
+ T (memmove (str[2], arr[2], n));
+ T (memmove (arr[2], str[2], n));
+ T (memmove (str[2], *parr, n));
+ T (memmove (*parr, str[2], n));
+ T (memmove (p->str[2], p->arr[2], n));
+ T (memmove (p->arr[2], p->str[2], n));
+ T (memmove (*p->parr, p->str[2], n));
+}
--- /dev/null
+/* PR middle-end/83131 - c-c++/common/attr-nonstring-3 failure for strcmp
+ tests on PowerPC
+ { dg-do compile }
+ { dg-options "-O2 -Wstringop-overflow -ftrack-macro-expansion=0" } */
+
+#if __cplusplus
+extern "C" {
+#endif
+
+typedef __SIZE_TYPE__ size_t;
+
+extern int strcmp (const char*, const char*);
+extern int strncmp (const char*, const char*, size_t);
+
+#if __cplusplus
+} /* extern "C" */
+#endif
+
+extern char arx[][18] __attribute__ ((nonstring));
+extern char ar5[5][5] __attribute__ ((nonstring));
+extern char str[][18];
+
+enum { N = sizeof ar5[2] };
+enum { X = sizeof ar5[2] + 1 };
+
+
+int warn_strcmp_cst_1 (void)
+{
+ return strcmp ("bar", arx[3]); /* { dg-warning "argument 2 declared attribute .nonstring." } */
+}
+
+int warn_strcmp_cst_2 (void)
+{
+ return strcmp (arx[3], "foo"); /* { dg-warning "argument 1 declared attribute .nonstring." } */
+}
+
+
+int warn_strncmp_cst_1 (void)
+{
+ return strncmp ("12345", ar5[2], X); /* { dg-warning "argument 2 declared attribute .nonstring." } */
+}
+
+int warn_strncmp_cst_2 (void)
+{
+ return strncmp (ar5[2], "12345", X); /* { dg-warning "argument 1 declared attribute .nonstring." } */
+}
+
+
+int nowarn_strncmp_cst_1 (void)
+{
+ return strncmp ("12345", ar5[2], N);
+}
+
+int nowarn_strncmp_cst_2 (void)
+{
+ return strncmp (ar5[2], "12345", N);
+}
+
+
+int warn_strncmp_var_1 (void)
+{
+ return strncmp (str[5], ar5[2], X); /* { dg-warning "argument 2 declared attribute .nonstring." } */
+}
+
+int warn_strncmp_var_2 (void)
+{
+ return strncmp (ar5[2], str[5], X); /* { dg-warning "argument 1 declared attribute .nonstring." } */
+}
+
+
+int nowarn_strncmp_var_1 (void)
+{
+ return strncmp (str[5], ar5[2], N);
+}
+
+int nowarn_strncmp_var_2 (void)
+{
+ return strncmp (ar5[2], str[5], N);
+}
--- /dev/null
+/* PR middle-end/84725 - enable attribute nonstring for all narrow character
+ types
+ Verify that using attribute nonstring with all three narrow character
+ types is accepted and using arrays and pointers to characters of all
+ three types (including their qualified forms) declared with the
+ attributes doesn't trigger -Wstringop-truncation warnings.
+ { dg-do compile }
+ { dg-options "-O -Wall -Wstringop-truncation" } */
+
+#if __cplusplus
+extern "C"
+#endif
+char* strncpy (char*, const char*, __SIZE_TYPE__);
+
+#define NONSTR __attribute__ ((nonstring))
+
+#define S "1234"
+
+struct Arrays
+{
+ char NONSTR a[4][4];
+ signed char NONSTR b[4][4];
+ unsigned char NONSTR c[4][4];
+};
+
+void test_arrays (struct Arrays *p, const char *s)
+{
+ strncpy (p->a[2], s, sizeof p->a[2]);
+ strncpy ((char*)p->b[2], s, sizeof p->b[2]);
+ strncpy ((char*)p->c[2], s, sizeof p->c[2]);
+}
+
+struct Pointers
+{
+ char NONSTR (*p)[4];
+ signed char NONSTR (*q)[4];
+ unsigned char NONSTR (*r)[4];
+};
+
+void test_pointers (struct Pointers *p)
+{
+ strncpy (*p->p, S, sizeof S - 1);
+ strncpy ((char*)*p->q, S, sizeof S - 1);
+ strncpy ((char*)*p->r, S, sizeof S - 1);
+}
+
+struct ConstArrays
+{
+ const char NONSTR a[4][4];
+ const signed char NONSTR b[4][4];
+ const unsigned char NONSTR c[4][4];
+};
+
+void test_const_arrays (struct ConstArrays *p, const char *s)
+{
+ strncpy ((char*)p->a[2], s, sizeof p->a[2]);
+ strncpy ((char*)p->b[2], s, sizeof p->b[2]);
+ strncpy ((char*)p->c[2], s, sizeof p->c[2]);
+}
+
+struct ConstPointers
+{
+ const char NONSTR (*p)[4];
+ const signed char NONSTR (*q)[4];
+ const unsigned char NONSTR (*r)[4];
+};
+
+void test_const_pointers (struct ConstPointers *p)
+{
+ strncpy ((char*)*p->p, S, sizeof S - 1);
+ strncpy ((char*)*p->q, S, sizeof S - 1);
+ strncpy ((char*)*p->r, S, sizeof S - 1);
+}
+
+struct VolatileArrays
+{
+ volatile char NONSTR a[4][4];
+ volatile signed char NONSTR b[4][4];
+ volatile unsigned char NONSTR c[4][4];
+};
+
+void test_volatile_arrays (struct VolatileArrays *p, const char *s)
+{
+ strncpy ((char*)p->a[2], s, sizeof p->a[2]);
+ strncpy ((char*)p->b[2], s, sizeof p->b[2]);
+ strncpy ((char*)p->c[2], s, sizeof p->c[2]);
+}
+
+struct VolatilePointers
+{
+ volatile char NONSTR (*p)[4];
+ volatile signed char NONSTR (*q)[4];
+ volatile unsigned char NONSTR (*r)[4];
+};
+
+void test_volatile_pointers (struct VolatilePointers *p)
+{
+ strncpy ((char*)*p->p, S, sizeof S - 1);
+ strncpy ((char*)*p->q, S, sizeof S - 1);
+ strncpy ((char*)*p->r, S, sizeof S - 1);
+}
+
+struct ConstVolatileArrays
+{
+ const volatile char NONSTR a[4][4];
+ const volatile signed char NONSTR b[4][4];
+ const volatile unsigned char NONSTR c[4][4];
+};
+
+void test_const_volatile_arrays (struct ConstVolatileArrays *p, const char *s)
+{
+ strncpy ((char*)p->a[2], s, sizeof p->a[2]);
+ strncpy ((char*)p->b[2], s, sizeof p->b[2]);
+ strncpy ((char*)p->c[2], s, sizeof p->c[2]);
+}
+
+struct ConstVolatilePointers
+{
+ const volatile char NONSTR (*p)[4];
+ const volatile signed char NONSTR (*q)[4];
+ const volatile unsigned char NONSTR (*r)[4];
+};
+
+void test_const_volatile_pointers (struct ConstVolatilePointers *p)
+{
+ strncpy ((char*)*p->p, S, sizeof S - 1);
+ strncpy ((char*)*p->q, S, sizeof S - 1);
+ strncpy ((char*)*p->r, S, sizeof S - 1);
+}
+
+/* { dg-prune-output "-Wdiscarded-qualifiers" } */
--- /dev/null
+/* PR 85623 - strncmp() warns about attribute 'nonstring' incorrectly
+ in -Wstringop-overflow
+ { dg-do compile }
+ { dg-options "-O2 -Wstringop-overread -ftrack-macro-expansion=0" } */
+
+#include "../gcc.dg/range.h"
+
+#if __cplusplus
+extern "C" {
+#endif
+
+extern int strcmp (const char*, const char*);
+extern int strncmp (const char*, const char*, size_t);
+extern int strncasecmp (const char*, const char*, size_t);
+
+extern size_t strspn (const char*, const char*);
+extern size_t strcspn (const char*, const char*);
+
+#if __cplusplus
+}
+#endif
+
+#define S26 "0123456789abcdefghijklmnopqrstuvwxyz"
+#define S(n) (S26 + sizeof S26 - 1 - (n))
+
+char __attribute__ ((nonstring)) a3[3][3];
+
+void sink (int);
+
+#define T(call) sink (call)
+
+void test_strcmp_cst (void)
+{
+ /* Verify that no warning is issued for strcmp() calls with a non-string
+ array argument when the other argument is a string whose length is
+ less than the size of the array. Because the function stops reading
+ at the first nul character there is no chance that it will read past
+ the end of the array. */
+ T (strcmp (S (0), a3[2]));
+ T (strcmp (S (1), a3[2]));
+ T (strcmp (S (2), a3[2]));
+ /* The following reads a3[2][3]. */
+ T (strcmp (S (3), a3[2])); /* { dg-warning "\\\[-Wstringop-overread" } */
+ /* The following also reads past the end of a3[2]. */
+ T (strcmp (S (9), a3[2])); /* { dg-warning "\\\[-Wstringop-overread" } */
+
+ T (strcmp (a3[2], S (0)));
+ T (strcmp (a3[2], S (1)));
+ T (strcmp (a3[2], S (2)));
+ T (strcmp (a3[2], S (3))); /* { dg-warning "\\\[-Wstringop-overread" } */
+ T (strcmp (a3[2], S (9))); /* { dg-warning "\\\[-Wstringop-overread" } */
+}
+
+
+void test_strcmp_range (const char *s)
+{
+ s = signed_value () < 0 ? S (0) : S (1);
+ T (strcmp (a3[2], s));
+
+ s = signed_value () < 0 ? S (0) : S (2);
+ T (strcmp (a3[2], s));
+
+ s = signed_value () < 0 ? S (0) : S (3);
+ T (strcmp (a3[2], s)); /* { dg-warning "\\\[-Wstringop-overread" } */
+
+ s = signed_value () < 0 ? S (1) : S (2);
+ T (strcmp (a3[2], s));
+
+ s = signed_value () < 0 ? S (1) : S (3);
+ T (strcmp (a3[2], s)); /* { dg-warning "\\\[-Wstringop-overread" } */
+
+ s = signed_value () < 0 ? S (3) : S (4);
+ T (strcmp (a3[2], s)); /* { dg-warning "\\\[-Wstringop-overread" } */
+}
+
+
+void test_strncmp_cst (void)
+{
+ T (strncmp (S (0), a3[2], 1));
+ T (strncmp (S (1), a3[2], 2));
+ T (strncmp (S (2), a3[2], 3));
+ T (strncmp (S (3), a3[2], 3));
+ T (strncmp (S (3), a3[2], 4)); /* { dg-warning "\\\[-Wstringop-overread" } */
+
+ T (strncmp (S (9), a3[2], 3));
+ T (strncmp (S (9), a3[2], 4)); /* { dg-warning "\\\[-Wstringop-overread" } */
+ T (strncmp (S (9), a3[2], 5)); /* { dg-warning "\\\[-Wstringop-overread" } */
+
+ T (strncmp (a3[2], S (0), 1));
+ T (strncmp (a3[2], S (1), 2));
+ T (strncmp (a3[2], S (2), 3));
+ T (strncmp (a3[2], S (3), 3));
+ T (strncmp (a3[2], S (3), 4)); /* { dg-warning "\\\[-Wstringop-overread" } */
+
+ T (strncmp (a3[2], S (9), 3));
+ T (strncmp (a3[2], S (9), 4)); /* { dg-warning "\\\[-Wstringop-overread" } */
+ T (strncmp (a3[2], S (9), 5)); /* { dg-warning "\\\[-Wstringop-overread" } */
+}
+
+void test_strncmp_range (const char *s)
+{
+ T (strncmp (a3[2], S (2), UR (0, 3)));
+ T (strncmp (a3[2], S (2), UR (1, 4)));
+ T (strncmp (a3[2], S (2), UR (2, 5)));
+ T (strncmp (a3[2], S (2), UR (3, 6)));
+ T (strncmp (a3[2], S (2), UR (4, 7)));
+
+ T (strncmp (a3[2], S (5), UR (0, 3)));
+ T (strncmp (a3[2], S (5), UR (1, 4)));
+ T (strncmp (a3[2], S (5), UR (2, 5)));
+ T (strncmp (a3[2], S (5), UR (3, 6)));
+ T (strncmp (a3[2], S (5), UR (4, 7))); /* { dg-warning "\\\[-Wstringop-overread" } */
+ T (strncmp (a3[2], S (5), UR (7, 9))); /* { dg-warning "\\\[-Wstringop-overread" } */
+
+ s = signed_value () < 0 ? S (0) : S (1);
+ T (strncmp (a3[2], s, UR (1, 3)));
+ T (strncmp (a3[2], s, UR (2, 5)));
+
+ s = signed_value () < 0 ? S (2) : S (5);
+ T (strncmp (a3[2], s, UR (1, 3)));
+
+ s = signed_value () < 0 ? S (2) : S (5);
+ T (strncmp (a3[2], s, UR (1, 4)));
+ T (strncmp (a3[2], s, UR (2, 5)));
+ T (strncmp (a3[2], s, UR (3, 6)));
+ T (strncmp (a3[2], s, UR (4, 7))); /* { dg-warning "\\\[-Wstringop-overread" } */
+}
+
+void test_strncasecmp (void)
+{
+ T (strncasecmp (S (0), a3[2], 1));
+ T (strncasecmp (S (1), a3[2], 2));
+ T (strncasecmp (S (2), a3[2], 3));
+ T (strncasecmp (S (3), a3[2], 3));
+ T (strncasecmp (S (3), a3[2], 4)); /* { dg-warning "\\\[-Wstringop-overread" } */
+
+ T (strncasecmp (S (9), a3[2], 3));
+ T (strncasecmp (S (9), a3[2], 4)); /* { dg-warning "\\\[-Wstringop-overread" } */
+ T (strncasecmp (S (9), a3[2], 5)); /* { dg-warning "\\\[-Wstringop-overread" } */
+
+ T (strncasecmp (a3[2], S (0), 1));
+ T (strncasecmp (a3[2], S (1), 2));
+ T (strncasecmp (a3[2], S (2), 3));
+ T (strncasecmp (a3[2], S (3), 3));
+ T (strncasecmp (a3[2], S (3), 4)); /* { dg-warning "\\\[-Wstringop-overread" } */
+
+ T (strncasecmp (a3[2], S (9), 3));
+ T (strncasecmp (a3[2], S (9), 4)); /* { dg-warning "\\\[-Wstringop-overread" } */
+ T (strncasecmp (a3[2], S (9), 5)); /* { dg-warning "\\\[-Wstringop-overread" } */
+}
+
+void test_strspn (void)
+{
+ /* strspn must traverse all characters in the second argument except
+ when the first string is empty. */
+ T (strspn (S (0), a3[2]));
+ T (strspn (S (1), a3[2])); /* { dg-warning "\\\[-Wstringop-overread" } */
+ T (strspn (S (2), a3[2])); /* { dg-warning "\\\[-Wstringop-overread" } */
+ T (strspn (S (3), a3[2])); /* { dg-warning "\\\[-Wstringop-overread" } */
+ T (strspn (S (9), a3[2])); /* { dg-warning "\\\[-Wstringop-overread" } */
+
+ /* Similarly, strspn must traverse all characters in the first argument
+ except when the second string is empty. */
+ T (strspn (a3[2], S (0)));
+ T (strspn (a3[2], S (1))); /* { dg-warning "\\\[-Wstringop-overread" } */
+ T (strspn (a3[2], S (2))); /* { dg-warning "\\\[-Wstringop-overread" } */
+ T (strspn (a3[2], S (3))); /* { dg-warning "\\\[-Wstringop-overread" } */
+ T (strspn (a3[2], S (9))); /* { dg-warning "\\\[-Wstringop-overread" } */
+}
+
+void test_strcspn (void)
+{
+ T (strcspn (S (0), a3[2]));
+ T (strcspn (S (1), a3[2])); /* { dg-warning "\\\[-Wstringop-overread" } */
+ T (strcspn (S (2), a3[2])); /* { dg-warning "\\\[-Wstringop-overread" } */
+ T (strcspn (S (3), a3[2])); /* { dg-warning "\\\[-Wstringop-overread" } */
+ T (strcspn (S (9), a3[2])); /* { dg-warning "\\\[-Wstringop-overread" } */
+
+ T (strcspn (a3[2], S (0))); /* { dg-warning "\\\[-Wstringop-overread" } */
+ T (strcspn (a3[2], S (1))); /* { dg-warning "\\\[-Wstringop-overread" } */
+ T (strcspn (a3[2], S (2))); /* { dg-warning "\\\[-Wstringop-overread" } */
+ T (strcspn (a3[2], S (3))); /* { dg-warning "\\\[-Wstringop-overread" } */
+ T (strcspn (a3[2], S (9))); /* { dg-warning "\\\[-Wstringop-overread" } */
+}
--- /dev/null
+/* PR 85643 - attribute nonstring fails to squash -Wstringop-truncation
+ warning
+ { dg-do compile }
+ { dg-options "-O2 -Wall -ftrack-macro-expansion=0" } */
+
+#define strncpy __builtin_strncpy
+
+struct A {
+ char a[2][16 + 1];
+};
+
+struct B {
+ char a[2][16] __attribute__ ((__nonstring__));
+};
+
+struct B*
+test_memarray (const struct A *s)
+{
+ static struct B b;
+ strncpy (b.a[1], s->a[1], sizeof b.a[1]);
+ return &b;
+}
+
+const char*
+test_array (const char *s)
+{
+ static char a[2][80] __attribute__ ((__nonstring__));
+ strncpy (a[1], s, sizeof a[1]);
+ return a[1];
+}
+
+const char*
+test_array_idx (const char *s)
+{
+ static char a[2][80] __attribute__ ((__nonstring__));
+ char *p __attribute__ ((__nonstring__)) = &a[1][20];
+ strncpy (p, s, 60); /* { dg-bogus "-Wstringop-truncation" } */
+ return a[1];
+}
+
+const char*
+test_array_off (const char *s)
+{
+ static char a[2][80] __attribute__ ((__nonstring__));
+ char *p __attribute__ ((__nonstring__)) = a[1] + 20;
+ strncpy (p, s, 60); /* { dg-bogus "-Wstringop-truncation" } */
+ return a[1];
+}
+
+struct B*
+test_memarray_cstidx_idx (const char *s)
+{
+ static struct B b[2];
+ char *p __attribute__ ((__nonstring__)) = &b[1].a[1][4];
+
+ /* The destination below is represented as &MEM[(void *)&a + 20B] and
+ which (in general) doesn't make it possible to determine what member
+ it refers to. */
+ strncpy (p, s, sizeof b[1].a[1] - 4); /* { dg-bogus "-Wstringop-truncation" } */
+ return b;
+}
+
+struct B*
+test_memarray_cstidx_off (const char *s)
+{
+ static struct B b[2];
+ char *p __attribute__ ((__nonstring__)) = b[1].a[1] + 4;
+
+ /* Same as above. */
+ strncpy (p, s, sizeof b[1].a[1] - 4); /* { dg-bogus "-Wstringop-truncation" "" { xfail *-*-*} } */
+ return b;
+}
+
+struct B*
+test_memarray_varidx_idx (const char *s, int i)
+{
+ static struct B b[3];
+ char *p __attribute__ ((__nonstring__)) = &b[i].a[1][4];
+ strncpy (p, s, sizeof b[i].a[1] - 4);
+ return b;
+}
+
+struct B*
+test_memarray_varidx_off (const char *s, int i)
+{
+ static struct B b[3];
+ char *p __attribute__ ((__nonstring__)) = b[i].a[1] + 4;
+ strncpy (p, s, sizeof b[i].a[1] - 4);
+ return b;
+}
--- /dev/null
+/* PR middle-end/85602 - -Wsizeof-pointer-memaccess for strncat with size
+ of source
+ { dg-do compile }
+ { dg-options "-O2 -Wno-array-bounds -Wsizeof-pointer-memaccess -Wstringop-truncation -ftrack-macro-expansion=0" } */
+
+#include "../gcc.dg/range.h"
+
+typedef __SIZE_TYPE__ size_t;
+
+#if __cplusplus
+extern "C" {
+#endif
+
+char* strcpy (char*, const char*);
+size_t strlen (const char*);
+char* strncat (char*, const char*, __SIZE_TYPE__);
+char* strncpy (char*, const char*, __SIZE_TYPE__);
+
+#if __cplusplus
+}
+#endif
+
+#define NONSTR __attribute__ ((nonstring))
+
+NONSTR char nd3[3][3];
+NONSTR char nd4[4][4];
+NONSTR char nd5[5][5];
+
+NONSTR char ns3[3][3];
+NONSTR char ns4[4][4];
+NONSTR char ns5[5][5];
+
+NONSTR char* pns;
+
+void sink (void*, ...);
+
+#define T(call) sink (call)
+
+/* Verify that for a nonstring source array of an unknown length
+ a warning is issued only when the bound exceeds the array size. */
+
+void test_strncat_nonstring_cst (char *d)
+{
+ T (strncat (d, ns3[1], 1));
+ T (strncat (d, ns3[1], 2));
+ T (strncat (d, ns3[1], 3));
+ T (strncat (d, ns3[1], sizeof ns3[2]));
+ T (strncat (d, ns3[1], 4)); /* { dg-warning "argument 2 declared attribute .nonstring. is smaller than the specified bound 4" } */
+
+ T (strncat (d, ns4[1], 1));
+ T (strncat (d, ns4[1], 2));
+ T (strncat (d, ns4[1], 3));
+ T (strncat (d, ns4[1], 4));
+ T (strncat (d, ns4[1], sizeof ns4[2]));
+ T (strncat (d, ns4[1], 5)); /* { dg-warning "argument 2 declared attribute .nonstring. is smaller than the specified bound 5" } */
+
+ T (strncat (nd3[1], ns3[1], 1));
+ T (strncat (nd3[1], ns3[1], 2));
+ T (strncat (nd3[1], ns3[1], 3)); /* { dg-warning "specified bound 3 equals destination size" } */
+ /* Either of the two warnings below is fine. */
+ T (strncat (nd3[1], ns3[1], 4)); /* { dg-warning "argument 2 declared attribute .nonstring. is smaller than the specified bound 4|specified bound 4 exceeds destination size 3" } */
+
+ T (strncat (d, pns, sizeof pns)); /* { dg-warning "argument to .sizeof. in .\[^\n\r\]*strncat\[^\n\r\]*. call is the same expression as the source" } */
+}
+
+
+void test_strncat_nonstring_var (char *d, size_t n)
+{
+ /* In the following the bound coulld apply to either the destination
+ or the source. The expected use of strncat() is to pass it as
+ the bound DSIZE - strlen(D) - 1 so the call below is diagnosed. */
+ T (strncat (d, ns3[1], n)); /* { dg-warning "argument 2 declared attribute .nonstring." } */
+
+ T (strncat (d, ns3[1], UR (0, 1)));
+ T (strncat (d, ns3[1], UR (1, 2)));
+ T (strncat (d, ns3[1], UR (2, 3)));
+ T (strncat (d, ns3[1], UR (3, 4))); /* { dg-warning "argument 2 declared attribute 'nonstring' may be smaller than the specified bound \\\[3, 4]" } */
+ T (strncat (d, ns3[1], UR (4, 5))); /* { dg-warning "argument 2 declared attribute 'nonstring' is smaller than the specified bound \\\[4, 5]" } */
+
+ /* Verify that the call below (the intended use of strncat()) is
+ also diagnosed. */
+ T (strncat (d, ns3[1], 256 - strlen (d) - 1)); /* { dg-warning "argument 2 declared attribute .nonstring." } */
+
+ T (strncat (nd3[1], ns5[1], UR (0, 1)));
+ T (strncat (nd3[1], ns5[1], UR (1, 2)));
+ T (strncat (nd3[1], ns5[1], UR (2, 3)));
+ T (strncat (nd3[1], ns5[1], UR (3, 4)));
+ T (strncat (nd3[1], ns5[1], UR (4, 5))); /* { dg-warning "specified bound \\\[4, 5] exceeds destination size 3" } */
+
+ T (strncat (nd5[1], ns3[1], UR (0, 1)));
+ T (strncat (nd5[1], ns3[1], UR (1, 2)));
+ T (strncat (nd5[1], ns3[1], UR (2, 3)));
+ T (strncat (nd5[1], ns3[1], UR (3, 4))); /* { dg-warning "argument 2 declared attribute 'nonstring' may be smaller than the specified bound \\\[3, 4]" } */
+}
+
+/* Verify that for a nonstring source array of a known length (i.e.,
+ a nonstring array containing a nul-terminated string) a warning
+ is issued only for certain truncation.
+
+ The test cases are split up to work around bug 81343 (or one like
+ it). */
+
+void test_strncat_string_1_1 (char *d)
+{
+ strcpy (ns3[1], "1");
+ T (strncat (d, ns3[1], 1)); /* { dg-warning "output truncated before terminating nul copying 1 byte from a string of the same length" } */
+}
+
+void test_strncat_string_1_2 (char *d)
+{
+ strcpy (ns3[1], "1");
+ T (strncat (d, ns3[1], 2));
+}
+
+void test_strncat_string_1_3 (char *d)
+{
+ strcpy (ns3[1], "1");
+ T (strncat (d, ns3[1], 3));
+}
+
+void test_strncat_string_2_1 (char *d)
+{
+ strcpy (ns3[1], "12");
+ T (strncat (d, ns3[1], 1)); /* { dg-warning "output truncated copying 1 byte from a string of length 2" } */
+}
+
+void test_strncat_string_2_2 (char *d)
+{
+ strcpy (ns3[1], "12");
+ T (strncat (d, ns3[1], 2)); /* { dg-warning "output truncated before terminating nul copying 2 bytes from a string of the same length" } */
+}
+
+void test_strncat_string_2_3 (char *d)
+{
+ strcpy (ns3[1], "12");
+ T (strncat (d, ns3[1], 3));
+}
+
+
+void test_strcncpy_nonstring_cst (char *d)
+{
+ T (strncpy (d, ns3[1], 1));
+ T (strncpy (d, ns3[1], 2));
+ T (strncpy (d, ns3[1], 3));
+ T (strncpy (d, ns3[1], sizeof ns3[2]));
+ T (strncpy (d, ns3[1], 4)); /* { dg-warning "argument 2 declared attribute .nonstring. is smaller than the specified bound 4" } */
+}
/* The destination below is represented as &MEM[(void *)&a + 20B] and
which (in general) doesn't make it possible to determine what member
it refers to. */
- strncpy (p, s, sizeof b[1].a - 4); /* { dg-bogus "-Wstringop-truncation" "" { xfail *-*-*} } */
+ strncpy (p, s, sizeof b[1].a - 4); /* { dg-bogus "-Wstringop-truncation" } */
return b;
}
--- /dev/null
+/* Test to exercise attribute "nonstring" syntax.
+ { dg-do compile }
+ { dg-options "-Wattributes" } */
+
+#define ATTR(list) __attribute__ (list)
+#define NONSTR ATTR ((nonstring))
+
+/* Verify it's accepted on char[] arrays. */
+extern NONSTR char nsx_1[][3];
+extern char NONSTR nsx_2[][3];
+extern char nsx_3[][3] NONSTR;
+
+extern NONSTR char ns1[1][4];
+extern char NONSTR ns2[3][5];
+extern char ns3[5][6] NONSTR;
+
+extern NONSTR char ns4[1][2][3][4];
+extern char NONSTR ns5[2][3][4][5][6];
+extern char ns6[1][2][3][1][2][3][1][2][3] NONSTR;
+
+/* Verify it's accepted on char[] pointers. */
+extern NONSTR char (*pns_1)[3];
+extern char NONSTR (*pns_2)[4];
+extern char (*NONSTR pns_3)[5];
+
+extern NONSTR char (*pns_4)[1][2];
+extern char NONSTR (*pns_5)[2][3][1][7][4];
+extern char (*NONSTR pns_6)[1][1][1][2][1][1][1][2][1][1][1][2][1][1][7];
+
+struct S
+{
+/* Verify it's accepted on char[] member pointers. */
+ NONSTR char (*mpns_1)[3];
+ char NONSTR (*mpns_2)[4];
+ char (*NONSTR mpns_3)[5];
+
+/* Verify it's accepted on char[] member arrays. */
+ NONSTR char mns1[1][2];
+ char NONSTR mns3[3][3];
+ char mns5[5][4] NONSTR;
+
+/* Verify it's accepted on char[] flexible array members. */
+ char mnsx[][5] NONSTR;
+};
+
+void func (NONSTR char (*pns1)[2], char NONSTR (*pns2)[3], char (* NONSTR pns3)[4])
+{
+ (void)pns1;
+ (void)pns2;
+ (void)pns3;
+}
DECL. */
if (var)
decl = var;
- else if (TREE_CODE (decl) == ARRAY_REF)
- decl = TREE_OPERAND (decl, 0);
- else if (TREE_CODE (decl) == COMPONENT_REF)
- decl = TREE_OPERAND (decl, 1);
- else if (TREE_CODE (decl) == MEM_REF)
- return get_attr_nonstring_decl (TREE_OPERAND (decl, 0), ref);
+ else
+ {
+ while (TREE_CODE (decl) == ARRAY_REF)
+ decl = TREE_OPERAND (decl, 0);
+ if (TREE_CODE (decl) == COMPONENT_REF)
+ decl = TREE_OPERAND (decl, 1);
+ else if (TREE_CODE (decl) == MEM_REF)
+ return get_attr_nonstring_decl (TREE_OPERAND (decl, 0), ref);
+ }
- if (DECL_P (decl)
- && lookup_attribute ("nonstring", DECL_ATTRIBUTES (decl)))
+ if (DECL_P (decl) && lookup_attribute ("nonstring", DECL_ATTRIBUTES (decl)))
return decl;
return NULL_TREE;