c-family, tree: Allow nonstring attribute on multidimensional arrays [PR117178]

As requested in the PR117178 thread, the following patch allows nonstring
attribute also on multi-dimensional arrays (with cv char/signed char/unsigned
char as innermost element type) and pointers to such multi-dimensional arrays
or pointers to single-dimensional cv char/signed char/unsigned char arrays.
Given that (unfortunately) nonstring is a decl attribute rather than type
attribute, I think restricting it to single-dimensional arrays makes no
sense, even multi-dimensional ones can be used for storage of non-nul
terminated strings.

I really don't know what the kernel plans are, whether
they'll go with -Wno-unterminated-string-initialization added in Makefiles,
or whether the plan is to use nonstring attributes to quiet the warning.
In the latter case, some of the nonstring attributes will need to be
conditional on gcc version, because gcc before this patch will reject it
on multidimensional arrays.

2025-03-08  Jakub Jelinek  <jakub@redhat.com>

	PR c/117178
gcc/
	* tree.cc (get_attr_nonstring_decl): Look through all ARRAY_REFs, not
	just one and handle COMPONENT_REF and MEM_REF after skipping those
	rather than only when there wasn't ARRAY_REF.  Formatting fix.
gcc/c-family/
	* c-attribs.cc (handle_nonstring_attribute): Allow the attribute also
	on multi-dimensional arrays with char/signed char/unsigned char
	element type or pointers to such single and multi-dimensional arrays.
gcc/testsuite/
	* c-c++-common/attr-nonstring-7.c: Remove one xfail.
	* c-c++-common/attr-nonstring-9.c: New test.
	* c-c++-common/attr-nonstring-10.c: New test.
	* c-c++-common/attr-nonstring-11.c: New test.
	* c-c++-common/attr-nonstring-12.c: New test.
	* c-c++-common/attr-nonstring-13.c: New test.
	* c-c++-common/attr-nonstring-14.c: New test.
	* c-c++-common/attr-nonstring-15.c: New test.
	* c-c++-common/attr-nonstring-16.c: New test.
This commit is contained in:
Jakub Jelinek 2025-03-08 00:50:13 +01:00 committed by Jakub Jelinek
parent 622968990b
commit ab714e6087
11 changed files with 1237 additions and 11 deletions

View file

@ -5117,8 +5117,9 @@ handle_nonstring_attribute (tree *node, tree name, tree ARG_UNUSED (args),
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

View file

@ -0,0 +1,75 @@
/* 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);
}

View file

@ -0,0 +1,466 @@
/* 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));
}

View file

@ -0,0 +1,79 @@
/* 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);
}

View file

@ -0,0 +1,131 @@
/* 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" } */

View file

@ -0,0 +1,184 @@
/* 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" } */
}

View file

@ -0,0 +1,90 @@
/* 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;
}

View file

@ -0,0 +1,147 @@
/* 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" } */
}

View file

@ -56,7 +56,7 @@ test_memarray_cstidx_idx (const char *s)
/* 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;
}

View file

@ -0,0 +1,51 @@
/* 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;
}

View file

@ -15318,15 +15318,17 @@ get_attr_nonstring_decl (tree expr, tree *ref)
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;