[v4] attribs: Implement -Wno-attributes=vendor::attr [PR101940]

Message ID YVN5HttpwZbh0Poz@redhat.com
State New
Headers
Series [v4] attribs: Implement -Wno-attributes=vendor::attr [PR101940] |

Commit Message

Marek Polacek Sept. 28, 2021, 8:20 p.m. UTC
  On Thu, Sep 23, 2021 at 02:25:16PM -0400, Jason Merrill wrote:
> On 9/20/21 18:59, Marek Polacek via Gcc-patches wrote:
> > +void
> > +handle_ignored_attributes_option (vec<char *> *v)
> > +{
> > +  if (v == nullptr)
> > +    return;
> > +
> > +  for (auto opt : v)
> > +    {
> > +      if (strcmp (opt, "clang") == 0)
> > +	{
> > +	  // TODO
> > +	  continue;
> > +	}
> 
> If this doesn't work yet, let's not accept it at all for now.

Ok.
 
> > +      char *q = strstr (opt, "::");
> > +      /* We don't accept '::attr'.  */
> > +      if (q == nullptr || q == opt)
> > +	{
> > +	  error ("wrong argument to ignored attributes");
> > +	  inform (input_location, "valid format is %<ns::attr%>, %<ns::%>, "
> > +		  "or %<clang%>");
> 
> ...or even mention it.  Users can ignore clang:: instead, it doesn't matter
> to us if clang attributes are misspelled.

Removed.

> > +	  continue;
> > +	}
> > +      /* Cut off the vendor part.  */
> > +      *q = '\0';
> > +      char *vendor = opt;
> > +      char *attr = q + 2;
> > +      /* Verify that they look valid.  */
> > +      auto valid_p = [](const char *s) {
> > +	for (; *s != '\0'; ++s)
> > +	  if (!ISALNUM (*s) && *s != '_')
> > +	    return false;
> > +	return true;
> > +      };
> > +      if (!valid_p (vendor) || !valid_p (attr))
> > +	{
> > +	  error ("wrong argument to ignored attributes");
> > +	  continue;
> > +	}
> > +      /* Turn "__attr__" into "attr" so that we have a canonical form of
> > +	 attribute names.  Likewise for vendor.  */
> > +      auto strip = [](char *&s) {
> > +	const size_t l = strlen (s);
> > +	if (l > 4 && s[0] == '_' && s[1] == '_'
> > +	    && s[l - 1] == '_' && s[l - 2] == '_')
> > +	  {
> > +	    s[l - 2] = '\0';
> > +	    s += 2;
> > +	  }
> > +      };
> > +      strip (attr);
> > +      strip (vendor);
> > +      /* If we've already seen this vendor::attr, ignore it.  Attempting to
> > +	 register it twice would lead to a crash.  */
> > +      if (lookup_scoped_attribute_spec (get_identifier (vendor),
> > +					get_identifier (attr)))
> > +	continue;
> > +      /* In the "vendor::" case, we should ignore *any* attribute coming
> > +	 from this attribute namespace.  */
> > +      const bool ignored_ns = attr[0] == '\0';
> 
> Maybe set attr to nullptr instead of declaring ignored_ns?
> 
> > +      /* Create a table with extra attributes which we will register.
> > +	 We can't free it here, so squirrel away the pointers.  */
> > +      attribute_spec *table = new attribute_spec[2];
> > +      ignored_attributes_table.safe_push (table);
> > +      table[0] = { ignored_ns ? nullptr : attr, 0, 0, false, false,
> 
> ...so this can just use attr.

I also need ignored_ns...
 
> > +		   false, false, nullptr, nullptr };
> > +      table[1] = { nullptr, 0, 0, false, false, false, false, nullptr, nullptr };
> > +      register_scoped_attributes (table, vendor, ignored_ns);

...here, but I tweaked this a bit to get rid of the bool.

> > +    }
> > +}
> > +
> > +/* Free data we might have allocated when adding extra attributes.  */
> > +
> > +void
> > +free_attr_data ()
> > +{
> > +  for (auto x : ignored_attributes_table)
> > +    delete[] x;
> > +}
> 
> You probably also want to zero out ignored_attributes_table at this point.

Done.

> >   /* Initialize attribute tables, and make some sanity checks if checking is
> >      enabled.  */
> > @@ -252,6 +353,9 @@ init_attributes (void)
> >       /* Put all the GNU attributes into the "gnu" namespace.  */
> >       register_scoped_attributes (attribute_tables[i], "gnu");
> > +  vec<char *> *ignored = (vec<char *> *) flag_ignored_attributes;
> > +  handle_ignored_attributes_option (ignored);
> > +
> >     invoke_plugin_callbacks (PLUGIN_ATTRIBUTES, NULL);
> >     attributes_initialized = true;
> >   }
> > @@ -456,6 +560,19 @@ diag_attr_exclusions (tree last_decl, tree node, tree attrname,
> >     return found;
> >   }
> > +/* Return true iff we should not complain about unknown attributes
> > +   coming from the attribute namespace NS.  This is the case for
> > +   the -Wno-attributes=ns:: command-line option.  */
> > +
> > +static bool
> > +attr_namespace_ignored_p (tree ns)
> > +{
> > +  if (ns == NULL_TREE)
> > +    return false;
> > +  scoped_attributes *r = find_attribute_namespace (IDENTIFIER_POINTER (ns));
> > +  return r && r->ignored_p;
> > +}
> > +
> >   /* Process the attributes listed in ATTRIBUTES and install them in *NODE,
> >      which is either a DECL (including a TYPE_DECL) or a TYPE.  If a DECL,
> >      it should be modified in place; if a TYPE, a copy should be created
> > @@ -556,7 +673,8 @@ decl_attributes (tree *node, tree attributes, int flags,
> >         if (spec == NULL)
> >   	{
> > -	  if (!(flags & (int) ATTR_FLAG_BUILT_IN))
> > +	  if (!(flags & (int) ATTR_FLAG_BUILT_IN)
> > +	      && !attr_namespace_ignored_p (ns))
> >   	    {
> >   	      if (ns == NULL_TREE || !cxx11_attr_p)
> >   		warning (OPT_Wattributes, "%qE attribute directive ignored",
> > diff --git a/gcc/attribs.h b/gcc/attribs.h
> > index 138c509bce1..96a527f67a9 100644
> > --- a/gcc/attribs.h
> > +++ b/gcc/attribs.h
> > @@ -21,6 +21,7 @@ along with GCC; see the file COPYING3.  If not see
> >   #define GCC_ATTRIBS_H
> >   extern const struct attribute_spec *lookup_attribute_spec (const_tree);
> > +extern void free_attr_data ();
> >   extern void init_attributes (void);
> >   /* Process the attributes listed in ATTRIBUTES and install them in *NODE,
> > @@ -40,12 +41,14 @@ extern void apply_tm_attr (tree, tree);
> >   extern tree make_attribute (const char *, const char *, tree);
> >   extern struct scoped_attributes* register_scoped_attributes (const struct attribute_spec *,
> > -							     const char *);
> > +							     const char *,
> > +							     bool = false);
> >   extern char *sorted_attr_string (tree);
> >   extern bool common_function_versions (tree, tree);
> >   extern tree make_dispatcher_decl (const tree);
> >   extern bool is_function_default_version (const tree);
> > +extern void handle_ignored_attributes_option (vec<char *> *);
> >   /* Return a type like TTYPE except that its TYPE_ATTRIBUTES
> >      is ATTRIBUTE.
> > diff --git a/gcc/c-family/c-pragma.c b/gcc/c-family/c-pragma.c
> > index a9be8df0384..6beba5503f0 100644
> > --- a/gcc/c-family/c-pragma.c
> > +++ b/gcc/c-family/c-pragma.c
> > @@ -764,7 +764,7 @@ handle_pragma_diagnostic(cpp_reader *ARG_UNUSED(dummy))
> >     if (token != CPP_NAME)
> >       {
> >         warning_at (loc, OPT_Wpragmas,
> > -		  "missing [error|warning|ignored|push|pop]"
> > +		  "missing [error|warning|ignored|push|pop|ignored_attributes]"
> >   		  " after %<#pragma GCC diagnostic%>");
> >         return;
> >       }
> > @@ -787,10 +787,43 @@ handle_pragma_diagnostic(cpp_reader *ARG_UNUSED(dummy))
> >         diagnostic_pop_diagnostics (global_dc, input_location);
> >         return;
> >       }
> > +  else if (strcmp (kind_string, "ignored_attributes") == 0)
> > +    {
> > +      token = pragma_lex (&x, &loc);
> > +      if (token != CPP_STRING)
> > +	{
> > +	  warning_at (loc, OPT_Wpragmas,
> > +		      "missing attribute name after %<#pragma GCC diagnostic "
> > +		      "ignored_attributes%>");
> > +	  return;
> > +	}
> > +      char *args = xstrdup (TREE_STRING_POINTER (x));
> > +      const size_t l = strlen (args);
> > +      if (l == 0)
> > +	{
> > +	  warning_at (loc, OPT_Wpragmas, "missing argument to %<#pragma GCC "
> > +		      "diagnostic ignored_attributes%>");
> > +	  free (args);
> > +	  return;
> > +	}
> > +      else if (args[l - 1] == ',')
> > +	{
> > +	  warning_at (loc, OPT_Wpragmas, "trailing %<,%> in arguments for "
> > +		      "%<#pragma GCC diagnostic ignored_attributes%>");
> > +	  free (args);
> > +	  return;
> > +	}
> > +      auto_vec<char *> v;
> > +      for (char *p = strtok (args, ","); p; p = strtok (NULL, ","))
> > +	v.safe_push (p);
> > +      handle_ignored_attributes_option (&v);
> > +      /* ??? We can't free (args); here.  */
> 
> Perhaps we want to copy strings in handle_ignored_attributes_option rather
> than here?

Well, the other use doesn't need copying, so I left it be.
 
> > +      return;
> > +    }
> >     else
> >       {
> >         warning_at (loc, OPT_Wpragmas,
> > -		  "expected [error|warning|ignored|push|pop]"
> > +		  "expected [error|warning|ignored|push|pop|ignored_attributes]"
> >   		  " after %<#pragma GCC diagnostic%>");
> >         return;
> >       }
> > diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c
> > index 771efa3eadf..345c8f9d620 100644
> > --- a/gcc/c/c-decl.c
> > +++ b/gcc/c/c-decl.c
> > @@ -12295,6 +12295,8 @@ c_parse_final_cleanups (void)
> >       c_write_global_declarations_1 (BLOCK_VARS (DECL_INITIAL (t)));
> >     c_write_global_declarations_1 (BLOCK_VARS (ext_block));
> > +  free_attr_data ();
> 
> Since handle_ignored_attributes_option is in language-independent code,
> shouldn't this call be as well?

Good point, I've moved it into compile_file.  I don't where else it could
have gone.

Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?

-- >8 --
It is desirable for -Wattributes to warn about e.g.

[[deprecate]] void g(); // typo, should warn

However, -Wattributes also warns about vendor-specific attributes
(that's because lookup_scoped_attribute_spec -> find_attribute_namespace
finds nothing), which, with -Werror, causes grief.  We don't want the
-Wattributes warning for

[[company::attr]] void f();

GCC warns because it doesn't know the "company" namespace; it only knows
the "gnu" and "omp" namespaces.  We could entirely disable warning about
attributes in unknown scopes but then the compiler would also miss typos
like

  [[company::attrx]] void f();

or

  [[gmu::warn_used_result]] int write();

so that is not a viable solution.  A workaround is to use a #pragma:

  #pragma GCC diagnostic push
  #pragma GCC diagnostic ignored "-Wattributes"
  [[company::attr]] void f() {}
  #pragma GCC diagnostic pop

but that's a mouthful and awkward to use and could also hide typos.  In
fact, any macro-based solution doesn't seem like a way forward.

This patch implements -Wno-attributes=, which takes these arguments:

company::attr
company::

This option should go well with using @file: the user could have a file
containing
-Wno-attributes=vendor::attr1,vendor::attr2
and then invoke gcc with '@attrs' or similar.

I've also added a new pragma which has the same effect:

The pragma along with the new option should help with various static
analysis tools.

	PR c++/101940

gcc/ChangeLog:

	* attribs.c (struct scoped_attributes): Add a bool member.
	(lookup_scoped_attribute_spec): Forward declare.
	(register_scoped_attributes): New bool parameter, defaulted to
	false.  Use it.
	(handle_ignored_attributes_option): New function.
	(free_attr_data): New function.
	(init_attributes): Call handle_ignored_attributes_option.
	(attr_namespace_ignored_p): New function.
	(decl_attributes): Check attr_namespace_ignored_p before
	warning.
	* attribs.h (free_attr_data): Declare.
	(register_scoped_attributes): Adjust declaration.
	(handle_ignored_attributes_option): Declare.
	* common.opt (Wattributes=): New option with a variable.
	* doc/extend.texi: Document #pragma GCC diagnostic ignored_attributes.
	* doc/invoke.texi: Document -Wno-attributes=.
	* opts.c (common_handle_option) <case OPT_Wattributes_>: Handle.
	* plugin.h (register_scoped_attributes): Adjust declaration.

gcc/c-family/ChangeLog:

	* c-pragma.c (handle_pragma_diagnostic): Handle #pragma GCC diagnostic
	ignored_attributes.

gcc/c/ChangeLog:

	* c-decl.c (c_parse_final_cleanups): Call free_attr_data.

gcc/cp/ChangeLog:

	* decl2.c (c_parse_final_cleanups): Call free_attr_data.

gcc/testsuite/ChangeLog:

	* c-c++-common/Wno-attributes-1.c: New test.
	* c-c++-common/Wno-attributes-2.c: New test.
---
 gcc/attribs.c                                 | 120 +++++++++++++++++-
 gcc/attribs.h                                 |   5 +-
 gcc/c-family/c-pragma.c                       |  37 +++++-
 gcc/common.opt                                |   9 +-
 gcc/doc/extend.texi                           |  19 +++
 gcc/doc/invoke.texi                           |  20 +++
 gcc/opts.c                                    |  20 +++
 gcc/plugin.h                                  |   4 +-
 gcc/testsuite/c-c++-common/Wno-attributes-1.c |  55 ++++++++
 gcc/testsuite/c-c++-common/Wno-attributes-2.c |  56 ++++++++
 gcc/toplev.c                                  |   2 +
 11 files changed, 338 insertions(+), 9 deletions(-)
 create mode 100644 gcc/testsuite/c-c++-common/Wno-attributes-1.c
 create mode 100644 gcc/testsuite/c-c++-common/Wno-attributes-2.c


base-commit: a11052d98db2f2a61841f0c5ee84de4ca1b3e296
  

Comments

Marek Polacek Oct. 11, 2021, 3:17 p.m. UTC | #1
Ping.

On Tue, Sep 28, 2021 at 04:20:46PM -0400, Marek Polacek wrote:
> On Thu, Sep 23, 2021 at 02:25:16PM -0400, Jason Merrill wrote:
> > On 9/20/21 18:59, Marek Polacek via Gcc-patches wrote:
> > > +void
> > > +handle_ignored_attributes_option (vec<char *> *v)
> > > +{
> > > +  if (v == nullptr)
> > > +    return;
> > > +
> > > +  for (auto opt : v)
> > > +    {
> > > +      if (strcmp (opt, "clang") == 0)
> > > +	{
> > > +	  // TODO
> > > +	  continue;
> > > +	}
> > 
> > If this doesn't work yet, let's not accept it at all for now.
> 
> Ok.
>  
> > > +      char *q = strstr (opt, "::");
> > > +      /* We don't accept '::attr'.  */
> > > +      if (q == nullptr || q == opt)
> > > +	{
> > > +	  error ("wrong argument to ignored attributes");
> > > +	  inform (input_location, "valid format is %<ns::attr%>, %<ns::%>, "
> > > +		  "or %<clang%>");
> > 
> > ...or even mention it.  Users can ignore clang:: instead, it doesn't matter
> > to us if clang attributes are misspelled.
> 
> Removed.
> 
> > > +	  continue;
> > > +	}
> > > +      /* Cut off the vendor part.  */
> > > +      *q = '\0';
> > > +      char *vendor = opt;
> > > +      char *attr = q + 2;
> > > +      /* Verify that they look valid.  */
> > > +      auto valid_p = [](const char *s) {
> > > +	for (; *s != '\0'; ++s)
> > > +	  if (!ISALNUM (*s) && *s != '_')
> > > +	    return false;
> > > +	return true;
> > > +      };
> > > +      if (!valid_p (vendor) || !valid_p (attr))
> > > +	{
> > > +	  error ("wrong argument to ignored attributes");
> > > +	  continue;
> > > +	}
> > > +      /* Turn "__attr__" into "attr" so that we have a canonical form of
> > > +	 attribute names.  Likewise for vendor.  */
> > > +      auto strip = [](char *&s) {
> > > +	const size_t l = strlen (s);
> > > +	if (l > 4 && s[0] == '_' && s[1] == '_'
> > > +	    && s[l - 1] == '_' && s[l - 2] == '_')
> > > +	  {
> > > +	    s[l - 2] = '\0';
> > > +	    s += 2;
> > > +	  }
> > > +      };
> > > +      strip (attr);
> > > +      strip (vendor);
> > > +      /* If we've already seen this vendor::attr, ignore it.  Attempting to
> > > +	 register it twice would lead to a crash.  */
> > > +      if (lookup_scoped_attribute_spec (get_identifier (vendor),
> > > +					get_identifier (attr)))
> > > +	continue;
> > > +      /* In the "vendor::" case, we should ignore *any* attribute coming
> > > +	 from this attribute namespace.  */
> > > +      const bool ignored_ns = attr[0] == '\0';
> > 
> > Maybe set attr to nullptr instead of declaring ignored_ns?
> > 
> > > +      /* Create a table with extra attributes which we will register.
> > > +	 We can't free it here, so squirrel away the pointers.  */
> > > +      attribute_spec *table = new attribute_spec[2];
> > > +      ignored_attributes_table.safe_push (table);
> > > +      table[0] = { ignored_ns ? nullptr : attr, 0, 0, false, false,
> > 
> > ...so this can just use attr.
> 
> I also need ignored_ns...
>  
> > > +		   false, false, nullptr, nullptr };
> > > +      table[1] = { nullptr, 0, 0, false, false, false, false, nullptr, nullptr };
> > > +      register_scoped_attributes (table, vendor, ignored_ns);
> 
> ...here, but I tweaked this a bit to get rid of the bool.
> 
> > > +    }
> > > +}
> > > +
> > > +/* Free data we might have allocated when adding extra attributes.  */
> > > +
> > > +void
> > > +free_attr_data ()
> > > +{
> > > +  for (auto x : ignored_attributes_table)
> > > +    delete[] x;
> > > +}
> > 
> > You probably also want to zero out ignored_attributes_table at this point.
> 
> Done.
> 
> > >   /* Initialize attribute tables, and make some sanity checks if checking is
> > >      enabled.  */
> > > @@ -252,6 +353,9 @@ init_attributes (void)
> > >       /* Put all the GNU attributes into the "gnu" namespace.  */
> > >       register_scoped_attributes (attribute_tables[i], "gnu");
> > > +  vec<char *> *ignored = (vec<char *> *) flag_ignored_attributes;
> > > +  handle_ignored_attributes_option (ignored);
> > > +
> > >     invoke_plugin_callbacks (PLUGIN_ATTRIBUTES, NULL);
> > >     attributes_initialized = true;
> > >   }
> > > @@ -456,6 +560,19 @@ diag_attr_exclusions (tree last_decl, tree node, tree attrname,
> > >     return found;
> > >   }
> > > +/* Return true iff we should not complain about unknown attributes
> > > +   coming from the attribute namespace NS.  This is the case for
> > > +   the -Wno-attributes=ns:: command-line option.  */
> > > +
> > > +static bool
> > > +attr_namespace_ignored_p (tree ns)
> > > +{
> > > +  if (ns == NULL_TREE)
> > > +    return false;
> > > +  scoped_attributes *r = find_attribute_namespace (IDENTIFIER_POINTER (ns));
> > > +  return r && r->ignored_p;
> > > +}
> > > +
> > >   /* Process the attributes listed in ATTRIBUTES and install them in *NODE,
> > >      which is either a DECL (including a TYPE_DECL) or a TYPE.  If a DECL,
> > >      it should be modified in place; if a TYPE, a copy should be created
> > > @@ -556,7 +673,8 @@ decl_attributes (tree *node, tree attributes, int flags,
> > >         if (spec == NULL)
> > >   	{
> > > -	  if (!(flags & (int) ATTR_FLAG_BUILT_IN))
> > > +	  if (!(flags & (int) ATTR_FLAG_BUILT_IN)
> > > +	      && !attr_namespace_ignored_p (ns))
> > >   	    {
> > >   	      if (ns == NULL_TREE || !cxx11_attr_p)
> > >   		warning (OPT_Wattributes, "%qE attribute directive ignored",
> > > diff --git a/gcc/attribs.h b/gcc/attribs.h
> > > index 138c509bce1..96a527f67a9 100644
> > > --- a/gcc/attribs.h
> > > +++ b/gcc/attribs.h
> > > @@ -21,6 +21,7 @@ along with GCC; see the file COPYING3.  If not see
> > >   #define GCC_ATTRIBS_H
> > >   extern const struct attribute_spec *lookup_attribute_spec (const_tree);
> > > +extern void free_attr_data ();
> > >   extern void init_attributes (void);
> > >   /* Process the attributes listed in ATTRIBUTES and install them in *NODE,
> > > @@ -40,12 +41,14 @@ extern void apply_tm_attr (tree, tree);
> > >   extern tree make_attribute (const char *, const char *, tree);
> > >   extern struct scoped_attributes* register_scoped_attributes (const struct attribute_spec *,
> > > -							     const char *);
> > > +							     const char *,
> > > +							     bool = false);
> > >   extern char *sorted_attr_string (tree);
> > >   extern bool common_function_versions (tree, tree);
> > >   extern tree make_dispatcher_decl (const tree);
> > >   extern bool is_function_default_version (const tree);
> > > +extern void handle_ignored_attributes_option (vec<char *> *);
> > >   /* Return a type like TTYPE except that its TYPE_ATTRIBUTES
> > >      is ATTRIBUTE.
> > > diff --git a/gcc/c-family/c-pragma.c b/gcc/c-family/c-pragma.c
> > > index a9be8df0384..6beba5503f0 100644
> > > --- a/gcc/c-family/c-pragma.c
> > > +++ b/gcc/c-family/c-pragma.c
> > > @@ -764,7 +764,7 @@ handle_pragma_diagnostic(cpp_reader *ARG_UNUSED(dummy))
> > >     if (token != CPP_NAME)
> > >       {
> > >         warning_at (loc, OPT_Wpragmas,
> > > -		  "missing [error|warning|ignored|push|pop]"
> > > +		  "missing [error|warning|ignored|push|pop|ignored_attributes]"
> > >   		  " after %<#pragma GCC diagnostic%>");
> > >         return;
> > >       }
> > > @@ -787,10 +787,43 @@ handle_pragma_diagnostic(cpp_reader *ARG_UNUSED(dummy))
> > >         diagnostic_pop_diagnostics (global_dc, input_location);
> > >         return;
> > >       }
> > > +  else if (strcmp (kind_string, "ignored_attributes") == 0)
> > > +    {
> > > +      token = pragma_lex (&x, &loc);
> > > +      if (token != CPP_STRING)
> > > +	{
> > > +	  warning_at (loc, OPT_Wpragmas,
> > > +		      "missing attribute name after %<#pragma GCC diagnostic "
> > > +		      "ignored_attributes%>");
> > > +	  return;
> > > +	}
> > > +      char *args = xstrdup (TREE_STRING_POINTER (x));
> > > +      const size_t l = strlen (args);
> > > +      if (l == 0)
> > > +	{
> > > +	  warning_at (loc, OPT_Wpragmas, "missing argument to %<#pragma GCC "
> > > +		      "diagnostic ignored_attributes%>");
> > > +	  free (args);
> > > +	  return;
> > > +	}
> > > +      else if (args[l - 1] == ',')
> > > +	{
> > > +	  warning_at (loc, OPT_Wpragmas, "trailing %<,%> in arguments for "
> > > +		      "%<#pragma GCC diagnostic ignored_attributes%>");
> > > +	  free (args);
> > > +	  return;
> > > +	}
> > > +      auto_vec<char *> v;
> > > +      for (char *p = strtok (args, ","); p; p = strtok (NULL, ","))
> > > +	v.safe_push (p);
> > > +      handle_ignored_attributes_option (&v);
> > > +      /* ??? We can't free (args); here.  */
> > 
> > Perhaps we want to copy strings in handle_ignored_attributes_option rather
> > than here?
> 
> Well, the other use doesn't need copying, so I left it be.
>  
> > > +      return;
> > > +    }
> > >     else
> > >       {
> > >         warning_at (loc, OPT_Wpragmas,
> > > -		  "expected [error|warning|ignored|push|pop]"
> > > +		  "expected [error|warning|ignored|push|pop|ignored_attributes]"
> > >   		  " after %<#pragma GCC diagnostic%>");
> > >         return;
> > >       }
> > > diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c
> > > index 771efa3eadf..345c8f9d620 100644
> > > --- a/gcc/c/c-decl.c
> > > +++ b/gcc/c/c-decl.c
> > > @@ -12295,6 +12295,8 @@ c_parse_final_cleanups (void)
> > >       c_write_global_declarations_1 (BLOCK_VARS (DECL_INITIAL (t)));
> > >     c_write_global_declarations_1 (BLOCK_VARS (ext_block));
> > > +  free_attr_data ();
> > 
> > Since handle_ignored_attributes_option is in language-independent code,
> > shouldn't this call be as well?
> 
> Good point, I've moved it into compile_file.  I don't where else it could
> have gone.
> 
> Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
> 
> -- >8 --
> It is desirable for -Wattributes to warn about e.g.
> 
> [[deprecate]] void g(); // typo, should warn
> 
> However, -Wattributes also warns about vendor-specific attributes
> (that's because lookup_scoped_attribute_spec -> find_attribute_namespace
> finds nothing), which, with -Werror, causes grief.  We don't want the
> -Wattributes warning for
> 
> [[company::attr]] void f();
> 
> GCC warns because it doesn't know the "company" namespace; it only knows
> the "gnu" and "omp" namespaces.  We could entirely disable warning about
> attributes in unknown scopes but then the compiler would also miss typos
> like
> 
>   [[company::attrx]] void f();
> 
> or
> 
>   [[gmu::warn_used_result]] int write();
> 
> so that is not a viable solution.  A workaround is to use a #pragma:
> 
>   #pragma GCC diagnostic push
>   #pragma GCC diagnostic ignored "-Wattributes"
>   [[company::attr]] void f() {}
>   #pragma GCC diagnostic pop
> 
> but that's a mouthful and awkward to use and could also hide typos.  In
> fact, any macro-based solution doesn't seem like a way forward.
> 
> This patch implements -Wno-attributes=, which takes these arguments:
> 
> company::attr
> company::
> 
> This option should go well with using @file: the user could have a file
> containing
> -Wno-attributes=vendor::attr1,vendor::attr2
> and then invoke gcc with '@attrs' or similar.
> 
> I've also added a new pragma which has the same effect:
> 
> The pragma along with the new option should help with various static
> analysis tools.
> 
> 	PR c++/101940
> 
> gcc/ChangeLog:
> 
> 	* attribs.c (struct scoped_attributes): Add a bool member.
> 	(lookup_scoped_attribute_spec): Forward declare.
> 	(register_scoped_attributes): New bool parameter, defaulted to
> 	false.  Use it.
> 	(handle_ignored_attributes_option): New function.
> 	(free_attr_data): New function.
> 	(init_attributes): Call handle_ignored_attributes_option.
> 	(attr_namespace_ignored_p): New function.
> 	(decl_attributes): Check attr_namespace_ignored_p before
> 	warning.
> 	* attribs.h (free_attr_data): Declare.
> 	(register_scoped_attributes): Adjust declaration.
> 	(handle_ignored_attributes_option): Declare.
> 	* common.opt (Wattributes=): New option with a variable.
> 	* doc/extend.texi: Document #pragma GCC diagnostic ignored_attributes.
> 	* doc/invoke.texi: Document -Wno-attributes=.
> 	* opts.c (common_handle_option) <case OPT_Wattributes_>: Handle.
> 	* plugin.h (register_scoped_attributes): Adjust declaration.
> 
> gcc/c-family/ChangeLog:
> 
> 	* c-pragma.c (handle_pragma_diagnostic): Handle #pragma GCC diagnostic
> 	ignored_attributes.
> 
> gcc/c/ChangeLog:
> 
> 	* c-decl.c (c_parse_final_cleanups): Call free_attr_data.
> 
> gcc/cp/ChangeLog:
> 
> 	* decl2.c (c_parse_final_cleanups): Call free_attr_data.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* c-c++-common/Wno-attributes-1.c: New test.
> 	* c-c++-common/Wno-attributes-2.c: New test.
> ---
>  gcc/attribs.c                                 | 120 +++++++++++++++++-
>  gcc/attribs.h                                 |   5 +-
>  gcc/c-family/c-pragma.c                       |  37 +++++-
>  gcc/common.opt                                |   9 +-
>  gcc/doc/extend.texi                           |  19 +++
>  gcc/doc/invoke.texi                           |  20 +++
>  gcc/opts.c                                    |  20 +++
>  gcc/plugin.h                                  |   4 +-
>  gcc/testsuite/c-c++-common/Wno-attributes-1.c |  55 ++++++++
>  gcc/testsuite/c-c++-common/Wno-attributes-2.c |  56 ++++++++
>  gcc/toplev.c                                  |   2 +
>  11 files changed, 338 insertions(+), 9 deletions(-)
>  create mode 100644 gcc/testsuite/c-c++-common/Wno-attributes-1.c
>  create mode 100644 gcc/testsuite/c-c++-common/Wno-attributes-2.c
> 
> diff --git a/gcc/attribs.c b/gcc/attribs.c
> index 83fafc98b7d..d5fba7f4bbb 100644
> --- a/gcc/attribs.c
> +++ b/gcc/attribs.c
> @@ -87,6 +87,8 @@ struct scoped_attributes
>    const char *ns;
>    vec<attribute_spec> attributes;
>    hash_table<attribute_hasher> *attribute_hash;
> +  /* True if we should not warn about unknown attributes in this NS.  */
> +  bool ignored_p;
>  };
>  
>  /* The table of scope attributes.  */
> @@ -95,6 +97,8 @@ static vec<scoped_attributes> attributes_table;
>  static scoped_attributes* find_attribute_namespace (const char*);
>  static void register_scoped_attribute (const struct attribute_spec *,
>  				       scoped_attributes *);
> +static const struct attribute_spec *lookup_scoped_attribute_spec (const_tree,
> +								  const_tree);
>  
>  static bool attributes_initialized = false;
>  
> @@ -121,12 +125,14 @@ extract_attribute_substring (struct substring *str)
>  
>  /* Insert an array of attributes ATTRIBUTES into a namespace.  This
>     array must be NULL terminated.  NS is the name of attribute
> -   namespace.  The function returns the namespace into which the
> -   attributes have been registered.  */
> +   namespace.  IGNORED_P is true iff all unknown attributes in this
> +   namespace should be ignored for the purposes of -Wattributes.  The
> +   function returns the namespace into which the attributes have been
> +   registered.  */
>  
>  scoped_attributes *
>  register_scoped_attributes (const struct attribute_spec *attributes,
> -			    const char *ns)
> +			    const char *ns, bool ignored_p /*=false*/)
>  {
>    scoped_attributes *result = NULL;
>  
> @@ -144,9 +150,12 @@ register_scoped_attributes (const struct attribute_spec *attributes,
>        memset (&sa, 0, sizeof (sa));
>        sa.ns = ns;
>        sa.attributes.create (64);
> +      sa.ignored_p = ignored_p;
>        result = attributes_table.safe_push (sa);
>        result->attribute_hash = new hash_table<attribute_hasher> (200);
>      }
> +  else
> +    result->ignored_p |= ignored_p;
>  
>    /* Really add the attributes to their namespace now.  */
>    for (unsigned i = 0; attributes[i].name != NULL; ++i)
> @@ -224,6 +233,92 @@ check_attribute_tables (void)
>  				 attribute_tables[j][l].name));
>  }
>  
> +/* Used to stash pointers to allocated memory so that we can free them at
> +   the end of parsing of all TUs. */
> +static vec<attribute_spec *> ignored_attributes_table;
> +
> +/* Parse arguments ARGS of -Wno-attributes=.
> +   Currently we accept:
> +     vendor::attr
> +     vendor::
> +   This functions also registers the parsed attributes so that we don't
> +   warn that we don't recognize them.  */
> +
> +void
> +handle_ignored_attributes_option (vec<char *> *v)
> +{
> +  if (v == nullptr)
> +    return;
> +
> +  for (auto opt : v)
> +    {
> +      char *q = strstr (opt, "::");
> +      /* We don't accept '::attr'.  */
> +      if (q == nullptr || q == opt)
> +	{
> +	  error ("wrong argument to ignored attributes");
> +	  inform (input_location, "valid format is %<ns::attr%> or %<ns::%>");
> +	  continue;
> +	}
> +      /* Cut off the vendor part.  */
> +      *q = '\0';
> +      char *vendor = opt;
> +      char *attr = q + 2;
> +      /* Verify that they look valid.  */
> +      auto valid_p = [](const char *s) {
> +	for (; *s != '\0'; ++s)
> +	  if (!ISALNUM (*s) && *s != '_')
> +	    return false;
> +	return true;
> +      };
> +      if (!valid_p (vendor) || !valid_p (attr))
> +	{
> +	  error ("wrong argument to ignored attributes");
> +	  continue;
> +	}
> +      /* Turn "__attr__" into "attr" so that we have a canonical form of
> +	 attribute names.  Likewise for vendor.  */
> +      auto strip = [](char *&s) {
> +	const size_t l = strlen (s);
> +	if (l > 4 && s[0] == '_' && s[1] == '_'
> +	    && s[l - 1] == '_' && s[l - 2] == '_')
> +	  {
> +	    s[l - 2] = '\0';
> +	    s += 2;
> +	  }
> +      };
> +      strip (attr);
> +      strip (vendor);
> +      /* If we've already seen this vendor::attr, ignore it.  Attempting to
> +	 register it twice would lead to a crash.  */
> +      if (lookup_scoped_attribute_spec (get_identifier (vendor),
> +					get_identifier (attr)))
> +	continue;
> +      /* In the "vendor::" case, we should ignore *any* attribute coming
> +	 from this attribute namespace.  */
> +      if (attr[0] == '\0')
> +	attr = nullptr;
> +      /* Create a table with extra attributes which we will register.
> +	 We can't free it here, so squirrel away the pointers.  */
> +      attribute_spec *table = new attribute_spec[2];
> +      ignored_attributes_table.safe_push (table);
> +      table[0] = { attr, 0, 0, false, false, false, false, nullptr, nullptr };
> +      table[1] = { nullptr, 0, 0, false, false, false, false, nullptr,
> +		   nullptr };
> +      register_scoped_attributes (table, vendor, !attr);
> +    }
> +}
> +
> +/* Free data we might have allocated when adding extra attributes.  */
> +
> +void
> +free_attr_data ()
> +{
> +  for (auto x : ignored_attributes_table)
> +    delete[] x;
> +  ignored_attributes_table.release ();
> +}
> +
>  /* Initialize attribute tables, and make some sanity checks if checking is
>     enabled.  */
>  
> @@ -252,6 +347,9 @@ init_attributes (void)
>      /* Put all the GNU attributes into the "gnu" namespace.  */
>      register_scoped_attributes (attribute_tables[i], "gnu");
>  
> +  vec<char *> *ignored = (vec<char *> *) flag_ignored_attributes;
> +  handle_ignored_attributes_option (ignored);
> +
>    invoke_plugin_callbacks (PLUGIN_ATTRIBUTES, NULL);
>    attributes_initialized = true;
>  }
> @@ -456,6 +554,19 @@ diag_attr_exclusions (tree last_decl, tree node, tree attrname,
>    return found;
>  }
>  
> +/* Return true iff we should not complain about unknown attributes
> +   coming from the attribute namespace NS.  This is the case for
> +   the -Wno-attributes=ns:: command-line option.  */
> +
> +static bool
> +attr_namespace_ignored_p (tree ns)
> +{
> +  if (ns == NULL_TREE)
> +    return false;
> +  scoped_attributes *r = find_attribute_namespace (IDENTIFIER_POINTER (ns));
> +  return r && r->ignored_p;
> +}
> +
>  /* Process the attributes listed in ATTRIBUTES and install them in *NODE,
>     which is either a DECL (including a TYPE_DECL) or a TYPE.  If a DECL,
>     it should be modified in place; if a TYPE, a copy should be created
> @@ -556,7 +667,8 @@ decl_attributes (tree *node, tree attributes, int flags,
>  
>        if (spec == NULL)
>  	{
> -	  if (!(flags & (int) ATTR_FLAG_BUILT_IN))
> +	  if (!(flags & (int) ATTR_FLAG_BUILT_IN)
> +	      && !attr_namespace_ignored_p (ns))
>  	    {
>  	      if (ns == NULL_TREE || !cxx11_attr_p)
>  		warning (OPT_Wattributes, "%qE attribute directive ignored",
> diff --git a/gcc/attribs.h b/gcc/attribs.h
> index 138c509bce1..96a527f67a9 100644
> --- a/gcc/attribs.h
> +++ b/gcc/attribs.h
> @@ -21,6 +21,7 @@ along with GCC; see the file COPYING3.  If not see
>  #define GCC_ATTRIBS_H
>  
>  extern const struct attribute_spec *lookup_attribute_spec (const_tree);
> +extern void free_attr_data ();
>  extern void init_attributes (void);
>  
>  /* Process the attributes listed in ATTRIBUTES and install them in *NODE,
> @@ -40,12 +41,14 @@ extern void apply_tm_attr (tree, tree);
>  extern tree make_attribute (const char *, const char *, tree);
>  
>  extern struct scoped_attributes* register_scoped_attributes (const struct attribute_spec *,
> -							     const char *);
> +							     const char *,
> +							     bool = false);
>  
>  extern char *sorted_attr_string (tree);
>  extern bool common_function_versions (tree, tree);
>  extern tree make_dispatcher_decl (const tree);
>  extern bool is_function_default_version (const tree);
> +extern void handle_ignored_attributes_option (vec<char *> *);
>  
>  /* Return a type like TTYPE except that its TYPE_ATTRIBUTES
>     is ATTRIBUTE.
> diff --git a/gcc/c-family/c-pragma.c b/gcc/c-family/c-pragma.c
> index a9be8df0384..6beba5503f0 100644
> --- a/gcc/c-family/c-pragma.c
> +++ b/gcc/c-family/c-pragma.c
> @@ -764,7 +764,7 @@ handle_pragma_diagnostic(cpp_reader *ARG_UNUSED(dummy))
>    if (token != CPP_NAME)
>      {
>        warning_at (loc, OPT_Wpragmas,
> -		  "missing [error|warning|ignored|push|pop]"
> +		  "missing [error|warning|ignored|push|pop|ignored_attributes]"
>  		  " after %<#pragma GCC diagnostic%>");
>        return;
>      }
> @@ -787,10 +787,43 @@ handle_pragma_diagnostic(cpp_reader *ARG_UNUSED(dummy))
>        diagnostic_pop_diagnostics (global_dc, input_location);
>        return;
>      }
> +  else if (strcmp (kind_string, "ignored_attributes") == 0)
> +    {
> +      token = pragma_lex (&x, &loc);
> +      if (token != CPP_STRING)
> +	{
> +	  warning_at (loc, OPT_Wpragmas,
> +		      "missing attribute name after %<#pragma GCC diagnostic "
> +		      "ignored_attributes%>");
> +	  return;
> +	}
> +      char *args = xstrdup (TREE_STRING_POINTER (x));
> +      const size_t l = strlen (args);
> +      if (l == 0)
> +	{
> +	  warning_at (loc, OPT_Wpragmas, "missing argument to %<#pragma GCC "
> +		      "diagnostic ignored_attributes%>");
> +	  free (args);
> +	  return;
> +	}
> +      else if (args[l - 1] == ',')
> +	{
> +	  warning_at (loc, OPT_Wpragmas, "trailing %<,%> in arguments for "
> +		      "%<#pragma GCC diagnostic ignored_attributes%>");
> +	  free (args);
> +	  return;
> +	}
> +      auto_vec<char *> v;
> +      for (char *p = strtok (args, ","); p; p = strtok (NULL, ","))
> +	v.safe_push (p);
> +      handle_ignored_attributes_option (&v);
> +      /* ??? We can't free (args); here.  */
> +      return;
> +    }
>    else
>      {
>        warning_at (loc, OPT_Wpragmas,
> -		  "expected [error|warning|ignored|push|pop]"
> +		  "expected [error|warning|ignored|push|pop|ignored_attributes]"
>  		  " after %<#pragma GCC diagnostic%>");
>        return;
>      }
> diff --git a/gcc/common.opt b/gcc/common.opt
> index a9644cae4f0..c429e43c6c1 100644
> --- a/gcc/common.opt
> +++ b/gcc/common.opt
> @@ -83,7 +83,7 @@ int flag_gen_aux_info = 0
>  Variable
>  int flag_shlib
>  
> -; These two are really VEC(char_p,heap) *.
> +; These three are really VEC(char_p,heap) *.
>  
>  Variable
>  void *flag_instrument_functions_exclude_functions
> @@ -91,6 +91,9 @@ void *flag_instrument_functions_exclude_functions
>  Variable
>  void *flag_instrument_functions_exclude_files
>  
> +Variable
> +void *flag_ignored_attributes
> +
>  ; Generic structs (e.g. templates not explicitly specialized)
>  ; may not have a compilation unit associated with them, and so
>  ; may need to be treated differently from ordinary structs.
> @@ -549,6 +552,10 @@ Wattributes
>  Common Var(warn_attributes) Init(1) Warning
>  Warn about inappropriate attribute usage.
>  
> +Wattributes=
> +Common Joined
> +Do not warn about specified attributes.
> +
>  Wattribute-alias
>  Common Alias(Wattribute_alias=, 1, 0) Warning
>  Warn about type safety and similar errors and mismatches in declarations with alias attributes.
> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
> index b4c5376223a..28fd0cc6dd5 100644
> --- a/gcc/doc/extend.texi
> +++ b/gcc/doc/extend.texi
> @@ -23761,6 +23761,25 @@ restored.
>    foo(d);                       /* depends on command-line options */
>  @end smallexample
>  
> +@item #pragma GCC diagnostic ignored_attributes
> +
> +Similarly to @option{-Wno-attributes=}, this pragma allows users to suppress
> +warnings about unknown scoped attributes (in C++11 and C2X).  For example,
> +@code{#pragma GCC diagnostic ignored_attributes "vendor::attr"} disables
> +warning about the following declaration:
> +
> +@smallexample
> +[[vendor::attr]] void f();
> +@end smallexample
> +
> +whereas @code{#pragma GCC diagnostic ignored_attributes "vendor::"} prevents
> +warning about both of these declarations:
> +
> +@smallexample
> +[[vendor::safe]] void f();
> +[[vendor::unsafe]] void f2();
> +@end smallexample
> +
>  @end table
>  
>  GCC also offers a simple mechanism for printing messages during
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index 5b016166972..fe2cb768c61 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -8623,6 +8623,26 @@ unrecognized attributes, function attributes applied to variables,
>  etc.  This does not stop errors for incorrect use of supported
>  attributes.
>  
> +Additionally, using @option{-Wno-attributes=}, it is possible to suppress
> +warnings about unknown scoped attributes (in C++11 and C2X).  For example,
> +@option{-Wno-attributes=vendor::attr} disables warning about the following
> +declaration:
> +
> +@smallexample
> +[[vendor::attr]] void f();
> +@end smallexample
> +
> +It is also possible to disable warning about all attributes in a namespace
> +using @option{-Wno-attributes=vendor::} which prevents warning about both
> +of these declarations:
> +
> +@smallexample
> +[[vendor::safe]] void f();
> +[[vendor::unsafe]] void f2();
> +@end smallexample
> +
> +Note that @option{-Wno-attributes=} does not imply @option{-Wno-attributes}.
> +
>  @item -Wno-builtin-declaration-mismatch
>  @opindex Wno-builtin-declaration-mismatch
>  @opindex Wbuiltin-declaration-mismatch
> diff --git a/gcc/opts.c b/gcc/opts.c
> index 6503980cd33..e3b66ad0d43 100644
> --- a/gcc/opts.c
> +++ b/gcc/opts.c
> @@ -2558,6 +2558,26 @@ common_handle_option (struct gcc_options *opts,
>        /* Currently handled in a prescan.  */
>        break;
>  
> +    case OPT_Wattributes_:
> +      if (lang_mask == CL_DRIVER)
> +	break;
> +
> +      if (value)
> +	{
> +	  error_at (loc, "arguments ignored for %<-Wattributes=%>; use "
> +		    "%<-Wno-attributes=%> instead");
> +	  break;
> +	}
> +      else if (arg[strlen (arg) - 1] == ',')
> +	{
> +	  error_at (loc, "trailing %<,%> in arguments for "
> +		    "%<-Wno-attributes=%>");
> +	  break;
> +	}
> +
> +      add_comma_separated_to_vector (&opts->x_flag_ignored_attributes, arg);
> +      break;
> +
>      case OPT_Werror:
>        dc->warning_as_error_requested = value;
>        break;
> diff --git a/gcc/plugin.h b/gcc/plugin.h
> index 1640e253ca5..5556763d1bf 100644
> --- a/gcc/plugin.h
> +++ b/gcc/plugin.h
> @@ -197,7 +197,9 @@ invoke_plugin_callbacks (int event ATTRIBUTE_UNUSED,
>  /* In attribs.c.  */
>  
>  extern void register_attribute (const struct attribute_spec *attr);
> +/* The default argument for the third parameter is given in attribs.h.  */
>  extern struct scoped_attributes* register_scoped_attributes (const struct attribute_spec *,
> -							     const char *);
> +							     const char *,
> +							     bool);
>  
>  #endif /* PLUGIN_H */
> diff --git a/gcc/testsuite/c-c++-common/Wno-attributes-1.c b/gcc/testsuite/c-c++-common/Wno-attributes-1.c
> new file mode 100644
> index 00000000000..aac1a69fd85
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/Wno-attributes-1.c
> @@ -0,0 +1,55 @@
> +/* PR c++/101940 */
> +/* { dg-do compile } */
> +/* { dg-additional-options "-std=c++11" { target c++ } } */
> +/* { dg-additional-options "-Wno-attributes=company::,yoyodyne::attr" } */
> +/* { dg-additional-options "-Wno-attributes=c1::attr,c1::attr,c1::__attr__" } */
> +/* { dg-additional-options "-Wno-attributes=c2::,c2::attr" } */
> +/* { dg-additional-options "-Wno-attributes=c3::attr,c3::" } */
> +/* { dg-additional-options "-Wno-attributes=x::" } */
> +/* { dg-additional-options "-Wno-attributes=yoyodyne::attr_new" } */
> +/* { dg-additional-options "-Wno-attributes=c4::__attr__" } */
> +/* { dg-additional-options "-Wno-attributes=c5::attr" } */
> +/* { dg-additional-options "-Wno-attributes=__c6__::attr" } */
> +
> +[[company::attr]] void f1();
> +[[company::attr2]] void f2();
> +
> +[[yoyodyne::attr]] void f3();
> +[[yoyodyne::__attr__]] void f3__();
> +[[yoyodyne::attrx]] void f4(); /* { dg-warning "ignored" } */
> +[[yoyodyne::__attrx__]] void f4__(); /* { dg-warning "ignored" } */
> +
> +[[c1::attr]] void f5();
> +
> +[[c2::attr]] void f6();
> +[[c2::attrx]] void f7();
> +[[c2::__attr__]] void f6__();
> +[[c2::__attrx__]] void f7__();
> +
> +[[c3::attr]] void f8();
> +[[c3::attrx]] void f9();
> +
> +[[x::x]] void f10();
> +
> +[[yoyodyne::attr_new]] void f11();
> +[[yoyodyne::__attr_new__]] void f11__();
> +[[yoyodyne::attr_mew]] void f12(); /* { dg-warning "ignored" } */
> +[[yoyodyne::__attr_mew__]] void f12__(); /* { dg-warning "ignored" } */
> +
> +[[c4::attr]] void f13();
> +[[c4::__attr__]] void f13__();
> +[[c4::attrx]] void f14(); /* { dg-warning "ignored" } */
> +
> +[[c5::attr]] void f15();
> +[[c5::__attr__]] void f15__();
> +[[__c5__::attr]] void __f15();
> +[[__c5__::__attr__]] void __f15__();
> +[[c5::attrx]] void f15x(); /* { dg-warning "ignored" } */
> +[[__c5__::attrx]] void f15x(); /* { dg-warning "ignored" } */
> +
> +[[c6::attr]] void f16();
> +[[c6::__attr__]] void f16__();
> +[[__c6__::attr]] void __f16();
> +[[__c6__::__attr__]] void __f16__();
> +[[c6::attrx]] void f16x(); /* { dg-warning "ignored" } */
> +[[__c6__::attrx]] void f16x(); /* { dg-warning "ignored" } */
> diff --git a/gcc/testsuite/c-c++-common/Wno-attributes-2.c b/gcc/testsuite/c-c++-common/Wno-attributes-2.c
> new file mode 100644
> index 00000000000..4307c74b048
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/Wno-attributes-2.c
> @@ -0,0 +1,56 @@
> +/* PR c++/101940 */
> +/* { dg-do compile } */
> +/* { dg-additional-options "-std=c++11" { target c++ } } */
> +
> +#pragma GCC diagnostic ignored_attributes "company::,yoyodyne::attr"
> +#pragma GCC diagnostic ignored_attributes "c1::attr,c1::attr,c1::__attr__"
> +#pragma GCC diagnostic ignored_attributes "c2::,c2::attr"
> +#pragma GCC diagnostic ignored_attributes "c3::attr,c3::"
> +#pragma GCC diagnostic ignored_attributes "x::"
> +#pragma GCC diagnostic ignored_attributes "yoyodyne::attr_new"
> +#pragma GCC diagnostic ignored_attributes "c4::__attr__"
> +#pragma GCC diagnostic ignored_attributes "c5::attr"
> +#pragma GCC diagnostic ignored_attributes "__c6__::attr"
> +
> +[[company::attr]] void f1();
> +[[company::attr2]] void f2();
> +
> +[[yoyodyne::attr]] void f3();
> +[[yoyodyne::__attr__]] void f3__();
> +[[yoyodyne::attrx]] void f4(); /* { dg-warning "ignored" } */
> +[[yoyodyne::__attrx__]] void f4__(); /* { dg-warning "ignored" } */
> +
> +[[c1::attr]] void f5();
> +
> +[[c2::attr]] void f6();
> +[[c2::attrx]] void f7();
> +[[c2::__attr__]] void f6__();
> +[[c2::__attrx__]] void f7__();
> +
> +[[c3::attr]] void f8();
> +[[c3::attrx]] void f9();
> +
> +[[x::x]] void f10();
> +
> +[[yoyodyne::attr_new]] void f11();
> +[[yoyodyne::__attr_new__]] void f11__();
> +[[yoyodyne::attr_mew]] void f12(); /* { dg-warning "ignored" } */
> +[[yoyodyne::__attr_mew__]] void f12__(); /* { dg-warning "ignored" } */
> +
> +[[c4::attr]] void f13();
> +[[c4::__attr__]] void f13__();
> +[[c4::attrx]] void f14(); /* { dg-warning "ignored" } */
> +
> +[[c5::attr]] void f15();
> +[[c5::__attr__]] void f15__();
> +[[__c5__::attr]] void __f15();
> +[[__c5__::__attr__]] void __f15__();
> +[[c5::attrx]] void f15x(); /* { dg-warning "ignored" } */
> +[[__c5__::attrx]] void f15x(); /* { dg-warning "ignored" } */
> +
> +[[c6::attr]] void f16();
> +[[c6::__attr__]] void f16__();
> +[[__c6__::attr]] void __f16();
> +[[__c6__::__attr__]] void __f16__();
> +[[c6::attrx]] void f16x(); /* { dg-warning "ignored" } */
> +[[__c6__::attrx]] void f16x(); /* { dg-warning "ignored" } */
> diff --git a/gcc/toplev.c b/gcc/toplev.c
> index e1688aae724..f5ccaf8f799 100644
> --- a/gcc/toplev.c
> +++ b/gcc/toplev.c
> @@ -463,6 +463,8 @@ compile_file (void)
>    if (flag_dump_locations)
>      dump_location_info (stderr);
>  
> +  free_attr_data ();
> +
>    /* Compilation is now finished except for writing
>       what's left of the symbol table output.  */
>  
> 
> base-commit: a11052d98db2f2a61841f0c5ee84de4ca1b3e296
> -- 
> 2.31.1
> 

Marek
  
Marek Polacek Oct. 29, 2021, 4:47 p.m. UTC | #2
Ping.

On Mon, Oct 11, 2021 at 11:17:11AM -0400, Marek Polacek wrote:
> Ping.
> 
> On Tue, Sep 28, 2021 at 04:20:46PM -0400, Marek Polacek wrote:
> > On Thu, Sep 23, 2021 at 02:25:16PM -0400, Jason Merrill wrote:
> > > On 9/20/21 18:59, Marek Polacek via Gcc-patches wrote:
> > > > +void
> > > > +handle_ignored_attributes_option (vec<char *> *v)
> > > > +{
> > > > +  if (v == nullptr)
> > > > +    return;
> > > > +
> > > > +  for (auto opt : v)
> > > > +    {
> > > > +      if (strcmp (opt, "clang") == 0)
> > > > +	{
> > > > +	  // TODO
> > > > +	  continue;
> > > > +	}
> > > 
> > > If this doesn't work yet, let's not accept it at all for now.
> > 
> > Ok.
> >  
> > > > +      char *q = strstr (opt, "::");
> > > > +      /* We don't accept '::attr'.  */
> > > > +      if (q == nullptr || q == opt)
> > > > +	{
> > > > +	  error ("wrong argument to ignored attributes");
> > > > +	  inform (input_location, "valid format is %<ns::attr%>, %<ns::%>, "
> > > > +		  "or %<clang%>");
> > > 
> > > ...or even mention it.  Users can ignore clang:: instead, it doesn't matter
> > > to us if clang attributes are misspelled.
> > 
> > Removed.
> > 
> > > > +	  continue;
> > > > +	}
> > > > +      /* Cut off the vendor part.  */
> > > > +      *q = '\0';
> > > > +      char *vendor = opt;
> > > > +      char *attr = q + 2;
> > > > +      /* Verify that they look valid.  */
> > > > +      auto valid_p = [](const char *s) {
> > > > +	for (; *s != '\0'; ++s)
> > > > +	  if (!ISALNUM (*s) && *s != '_')
> > > > +	    return false;
> > > > +	return true;
> > > > +      };
> > > > +      if (!valid_p (vendor) || !valid_p (attr))
> > > > +	{
> > > > +	  error ("wrong argument to ignored attributes");
> > > > +	  continue;
> > > > +	}
> > > > +      /* Turn "__attr__" into "attr" so that we have a canonical form of
> > > > +	 attribute names.  Likewise for vendor.  */
> > > > +      auto strip = [](char *&s) {
> > > > +	const size_t l = strlen (s);
> > > > +	if (l > 4 && s[0] == '_' && s[1] == '_'
> > > > +	    && s[l - 1] == '_' && s[l - 2] == '_')
> > > > +	  {
> > > > +	    s[l - 2] = '\0';
> > > > +	    s += 2;
> > > > +	  }
> > > > +      };
> > > > +      strip (attr);
> > > > +      strip (vendor);
> > > > +      /* If we've already seen this vendor::attr, ignore it.  Attempting to
> > > > +	 register it twice would lead to a crash.  */
> > > > +      if (lookup_scoped_attribute_spec (get_identifier (vendor),
> > > > +					get_identifier (attr)))
> > > > +	continue;
> > > > +      /* In the "vendor::" case, we should ignore *any* attribute coming
> > > > +	 from this attribute namespace.  */
> > > > +      const bool ignored_ns = attr[0] == '\0';
> > > 
> > > Maybe set attr to nullptr instead of declaring ignored_ns?
> > > 
> > > > +      /* Create a table with extra attributes which we will register.
> > > > +	 We can't free it here, so squirrel away the pointers.  */
> > > > +      attribute_spec *table = new attribute_spec[2];
> > > > +      ignored_attributes_table.safe_push (table);
> > > > +      table[0] = { ignored_ns ? nullptr : attr, 0, 0, false, false,
> > > 
> > > ...so this can just use attr.
> > 
> > I also need ignored_ns...
> >  
> > > > +		   false, false, nullptr, nullptr };
> > > > +      table[1] = { nullptr, 0, 0, false, false, false, false, nullptr, nullptr };
> > > > +      register_scoped_attributes (table, vendor, ignored_ns);
> > 
> > ...here, but I tweaked this a bit to get rid of the bool.
> > 
> > > > +    }
> > > > +}
> > > > +
> > > > +/* Free data we might have allocated when adding extra attributes.  */
> > > > +
> > > > +void
> > > > +free_attr_data ()
> > > > +{
> > > > +  for (auto x : ignored_attributes_table)
> > > > +    delete[] x;
> > > > +}
> > > 
> > > You probably also want to zero out ignored_attributes_table at this point.
> > 
> > Done.
> > 
> > > >   /* Initialize attribute tables, and make some sanity checks if checking is
> > > >      enabled.  */
> > > > @@ -252,6 +353,9 @@ init_attributes (void)
> > > >       /* Put all the GNU attributes into the "gnu" namespace.  */
> > > >       register_scoped_attributes (attribute_tables[i], "gnu");
> > > > +  vec<char *> *ignored = (vec<char *> *) flag_ignored_attributes;
> > > > +  handle_ignored_attributes_option (ignored);
> > > > +
> > > >     invoke_plugin_callbacks (PLUGIN_ATTRIBUTES, NULL);
> > > >     attributes_initialized = true;
> > > >   }
> > > > @@ -456,6 +560,19 @@ diag_attr_exclusions (tree last_decl, tree node, tree attrname,
> > > >     return found;
> > > >   }
> > > > +/* Return true iff we should not complain about unknown attributes
> > > > +   coming from the attribute namespace NS.  This is the case for
> > > > +   the -Wno-attributes=ns:: command-line option.  */
> > > > +
> > > > +static bool
> > > > +attr_namespace_ignored_p (tree ns)
> > > > +{
> > > > +  if (ns == NULL_TREE)
> > > > +    return false;
> > > > +  scoped_attributes *r = find_attribute_namespace (IDENTIFIER_POINTER (ns));
> > > > +  return r && r->ignored_p;
> > > > +}
> > > > +
> > > >   /* Process the attributes listed in ATTRIBUTES and install them in *NODE,
> > > >      which is either a DECL (including a TYPE_DECL) or a TYPE.  If a DECL,
> > > >      it should be modified in place; if a TYPE, a copy should be created
> > > > @@ -556,7 +673,8 @@ decl_attributes (tree *node, tree attributes, int flags,
> > > >         if (spec == NULL)
> > > >   	{
> > > > -	  if (!(flags & (int) ATTR_FLAG_BUILT_IN))
> > > > +	  if (!(flags & (int) ATTR_FLAG_BUILT_IN)
> > > > +	      && !attr_namespace_ignored_p (ns))
> > > >   	    {
> > > >   	      if (ns == NULL_TREE || !cxx11_attr_p)
> > > >   		warning (OPT_Wattributes, "%qE attribute directive ignored",
> > > > diff --git a/gcc/attribs.h b/gcc/attribs.h
> > > > index 138c509bce1..96a527f67a9 100644
> > > > --- a/gcc/attribs.h
> > > > +++ b/gcc/attribs.h
> > > > @@ -21,6 +21,7 @@ along with GCC; see the file COPYING3.  If not see
> > > >   #define GCC_ATTRIBS_H
> > > >   extern const struct attribute_spec *lookup_attribute_spec (const_tree);
> > > > +extern void free_attr_data ();
> > > >   extern void init_attributes (void);
> > > >   /* Process the attributes listed in ATTRIBUTES and install them in *NODE,
> > > > @@ -40,12 +41,14 @@ extern void apply_tm_attr (tree, tree);
> > > >   extern tree make_attribute (const char *, const char *, tree);
> > > >   extern struct scoped_attributes* register_scoped_attributes (const struct attribute_spec *,
> > > > -							     const char *);
> > > > +							     const char *,
> > > > +							     bool = false);
> > > >   extern char *sorted_attr_string (tree);
> > > >   extern bool common_function_versions (tree, tree);
> > > >   extern tree make_dispatcher_decl (const tree);
> > > >   extern bool is_function_default_version (const tree);
> > > > +extern void handle_ignored_attributes_option (vec<char *> *);
> > > >   /* Return a type like TTYPE except that its TYPE_ATTRIBUTES
> > > >      is ATTRIBUTE.
> > > > diff --git a/gcc/c-family/c-pragma.c b/gcc/c-family/c-pragma.c
> > > > index a9be8df0384..6beba5503f0 100644
> > > > --- a/gcc/c-family/c-pragma.c
> > > > +++ b/gcc/c-family/c-pragma.c
> > > > @@ -764,7 +764,7 @@ handle_pragma_diagnostic(cpp_reader *ARG_UNUSED(dummy))
> > > >     if (token != CPP_NAME)
> > > >       {
> > > >         warning_at (loc, OPT_Wpragmas,
> > > > -		  "missing [error|warning|ignored|push|pop]"
> > > > +		  "missing [error|warning|ignored|push|pop|ignored_attributes]"
> > > >   		  " after %<#pragma GCC diagnostic%>");
> > > >         return;
> > > >       }
> > > > @@ -787,10 +787,43 @@ handle_pragma_diagnostic(cpp_reader *ARG_UNUSED(dummy))
> > > >         diagnostic_pop_diagnostics (global_dc, input_location);
> > > >         return;
> > > >       }
> > > > +  else if (strcmp (kind_string, "ignored_attributes") == 0)
> > > > +    {
> > > > +      token = pragma_lex (&x, &loc);
> > > > +      if (token != CPP_STRING)
> > > > +	{
> > > > +	  warning_at (loc, OPT_Wpragmas,
> > > > +		      "missing attribute name after %<#pragma GCC diagnostic "
> > > > +		      "ignored_attributes%>");
> > > > +	  return;
> > > > +	}
> > > > +      char *args = xstrdup (TREE_STRING_POINTER (x));
> > > > +      const size_t l = strlen (args);
> > > > +      if (l == 0)
> > > > +	{
> > > > +	  warning_at (loc, OPT_Wpragmas, "missing argument to %<#pragma GCC "
> > > > +		      "diagnostic ignored_attributes%>");
> > > > +	  free (args);
> > > > +	  return;
> > > > +	}
> > > > +      else if (args[l - 1] == ',')
> > > > +	{
> > > > +	  warning_at (loc, OPT_Wpragmas, "trailing %<,%> in arguments for "
> > > > +		      "%<#pragma GCC diagnostic ignored_attributes%>");
> > > > +	  free (args);
> > > > +	  return;
> > > > +	}
> > > > +      auto_vec<char *> v;
> > > > +      for (char *p = strtok (args, ","); p; p = strtok (NULL, ","))
> > > > +	v.safe_push (p);
> > > > +      handle_ignored_attributes_option (&v);
> > > > +      /* ??? We can't free (args); here.  */
> > > 
> > > Perhaps we want to copy strings in handle_ignored_attributes_option rather
> > > than here?
> > 
> > Well, the other use doesn't need copying, so I left it be.
> >  
> > > > +      return;
> > > > +    }
> > > >     else
> > > >       {
> > > >         warning_at (loc, OPT_Wpragmas,
> > > > -		  "expected [error|warning|ignored|push|pop]"
> > > > +		  "expected [error|warning|ignored|push|pop|ignored_attributes]"
> > > >   		  " after %<#pragma GCC diagnostic%>");
> > > >         return;
> > > >       }
> > > > diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c
> > > > index 771efa3eadf..345c8f9d620 100644
> > > > --- a/gcc/c/c-decl.c
> > > > +++ b/gcc/c/c-decl.c
> > > > @@ -12295,6 +12295,8 @@ c_parse_final_cleanups (void)
> > > >       c_write_global_declarations_1 (BLOCK_VARS (DECL_INITIAL (t)));
> > > >     c_write_global_declarations_1 (BLOCK_VARS (ext_block));
> > > > +  free_attr_data ();
> > > 
> > > Since handle_ignored_attributes_option is in language-independent code,
> > > shouldn't this call be as well?
> > 
> > Good point, I've moved it into compile_file.  I don't where else it could
> > have gone.
> > 
> > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
> > 
> > -- >8 --
> > It is desirable for -Wattributes to warn about e.g.
> > 
> > [[deprecate]] void g(); // typo, should warn
> > 
> > However, -Wattributes also warns about vendor-specific attributes
> > (that's because lookup_scoped_attribute_spec -> find_attribute_namespace
> > finds nothing), which, with -Werror, causes grief.  We don't want the
> > -Wattributes warning for
> > 
> > [[company::attr]] void f();
> > 
> > GCC warns because it doesn't know the "company" namespace; it only knows
> > the "gnu" and "omp" namespaces.  We could entirely disable warning about
> > attributes in unknown scopes but then the compiler would also miss typos
> > like
> > 
> >   [[company::attrx]] void f();
> > 
> > or
> > 
> >   [[gmu::warn_used_result]] int write();
> > 
> > so that is not a viable solution.  A workaround is to use a #pragma:
> > 
> >   #pragma GCC diagnostic push
> >   #pragma GCC diagnostic ignored "-Wattributes"
> >   [[company::attr]] void f() {}
> >   #pragma GCC diagnostic pop
> > 
> > but that's a mouthful and awkward to use and could also hide typos.  In
> > fact, any macro-based solution doesn't seem like a way forward.
> > 
> > This patch implements -Wno-attributes=, which takes these arguments:
> > 
> > company::attr
> > company::
> > 
> > This option should go well with using @file: the user could have a file
> > containing
> > -Wno-attributes=vendor::attr1,vendor::attr2
> > and then invoke gcc with '@attrs' or similar.
> > 
> > I've also added a new pragma which has the same effect:
> > 
> > The pragma along with the new option should help with various static
> > analysis tools.
> > 
> > 	PR c++/101940
> > 
> > gcc/ChangeLog:
> > 
> > 	* attribs.c (struct scoped_attributes): Add a bool member.
> > 	(lookup_scoped_attribute_spec): Forward declare.
> > 	(register_scoped_attributes): New bool parameter, defaulted to
> > 	false.  Use it.
> > 	(handle_ignored_attributes_option): New function.
> > 	(free_attr_data): New function.
> > 	(init_attributes): Call handle_ignored_attributes_option.
> > 	(attr_namespace_ignored_p): New function.
> > 	(decl_attributes): Check attr_namespace_ignored_p before
> > 	warning.
> > 	* attribs.h (free_attr_data): Declare.
> > 	(register_scoped_attributes): Adjust declaration.
> > 	(handle_ignored_attributes_option): Declare.
> > 	* common.opt (Wattributes=): New option with a variable.
> > 	* doc/extend.texi: Document #pragma GCC diagnostic ignored_attributes.
> > 	* doc/invoke.texi: Document -Wno-attributes=.
> > 	* opts.c (common_handle_option) <case OPT_Wattributes_>: Handle.
> > 	* plugin.h (register_scoped_attributes): Adjust declaration.
> > 
> > gcc/c-family/ChangeLog:
> > 
> > 	* c-pragma.c (handle_pragma_diagnostic): Handle #pragma GCC diagnostic
> > 	ignored_attributes.
> > 
> > gcc/c/ChangeLog:
> > 
> > 	* c-decl.c (c_parse_final_cleanups): Call free_attr_data.
> > 
> > gcc/cp/ChangeLog:
> > 
> > 	* decl2.c (c_parse_final_cleanups): Call free_attr_data.
> > 
> > gcc/testsuite/ChangeLog:
> > 
> > 	* c-c++-common/Wno-attributes-1.c: New test.
> > 	* c-c++-common/Wno-attributes-2.c: New test.
> > ---
> >  gcc/attribs.c                                 | 120 +++++++++++++++++-
> >  gcc/attribs.h                                 |   5 +-
> >  gcc/c-family/c-pragma.c                       |  37 +++++-
> >  gcc/common.opt                                |   9 +-
> >  gcc/doc/extend.texi                           |  19 +++
> >  gcc/doc/invoke.texi                           |  20 +++
> >  gcc/opts.c                                    |  20 +++
> >  gcc/plugin.h                                  |   4 +-
> >  gcc/testsuite/c-c++-common/Wno-attributes-1.c |  55 ++++++++
> >  gcc/testsuite/c-c++-common/Wno-attributes-2.c |  56 ++++++++
> >  gcc/toplev.c                                  |   2 +
> >  11 files changed, 338 insertions(+), 9 deletions(-)
> >  create mode 100644 gcc/testsuite/c-c++-common/Wno-attributes-1.c
> >  create mode 100644 gcc/testsuite/c-c++-common/Wno-attributes-2.c
> > 
> > diff --git a/gcc/attribs.c b/gcc/attribs.c
> > index 83fafc98b7d..d5fba7f4bbb 100644
> > --- a/gcc/attribs.c
> > +++ b/gcc/attribs.c
> > @@ -87,6 +87,8 @@ struct scoped_attributes
> >    const char *ns;
> >    vec<attribute_spec> attributes;
> >    hash_table<attribute_hasher> *attribute_hash;
> > +  /* True if we should not warn about unknown attributes in this NS.  */
> > +  bool ignored_p;
> >  };
> >  
> >  /* The table of scope attributes.  */
> > @@ -95,6 +97,8 @@ static vec<scoped_attributes> attributes_table;
> >  static scoped_attributes* find_attribute_namespace (const char*);
> >  static void register_scoped_attribute (const struct attribute_spec *,
> >  				       scoped_attributes *);
> > +static const struct attribute_spec *lookup_scoped_attribute_spec (const_tree,
> > +								  const_tree);
> >  
> >  static bool attributes_initialized = false;
> >  
> > @@ -121,12 +125,14 @@ extract_attribute_substring (struct substring *str)
> >  
> >  /* Insert an array of attributes ATTRIBUTES into a namespace.  This
> >     array must be NULL terminated.  NS is the name of attribute
> > -   namespace.  The function returns the namespace into which the
> > -   attributes have been registered.  */
> > +   namespace.  IGNORED_P is true iff all unknown attributes in this
> > +   namespace should be ignored for the purposes of -Wattributes.  The
> > +   function returns the namespace into which the attributes have been
> > +   registered.  */
> >  
> >  scoped_attributes *
> >  register_scoped_attributes (const struct attribute_spec *attributes,
> > -			    const char *ns)
> > +			    const char *ns, bool ignored_p /*=false*/)
> >  {
> >    scoped_attributes *result = NULL;
> >  
> > @@ -144,9 +150,12 @@ register_scoped_attributes (const struct attribute_spec *attributes,
> >        memset (&sa, 0, sizeof (sa));
> >        sa.ns = ns;
> >        sa.attributes.create (64);
> > +      sa.ignored_p = ignored_p;
> >        result = attributes_table.safe_push (sa);
> >        result->attribute_hash = new hash_table<attribute_hasher> (200);
> >      }
> > +  else
> > +    result->ignored_p |= ignored_p;
> >  
> >    /* Really add the attributes to their namespace now.  */
> >    for (unsigned i = 0; attributes[i].name != NULL; ++i)
> > @@ -224,6 +233,92 @@ check_attribute_tables (void)
> >  				 attribute_tables[j][l].name));
> >  }
> >  
> > +/* Used to stash pointers to allocated memory so that we can free them at
> > +   the end of parsing of all TUs. */
> > +static vec<attribute_spec *> ignored_attributes_table;
> > +
> > +/* Parse arguments ARGS of -Wno-attributes=.
> > +   Currently we accept:
> > +     vendor::attr
> > +     vendor::
> > +   This functions also registers the parsed attributes so that we don't
> > +   warn that we don't recognize them.  */
> > +
> > +void
> > +handle_ignored_attributes_option (vec<char *> *v)
> > +{
> > +  if (v == nullptr)
> > +    return;
> > +
> > +  for (auto opt : v)
> > +    {
> > +      char *q = strstr (opt, "::");
> > +      /* We don't accept '::attr'.  */
> > +      if (q == nullptr || q == opt)
> > +	{
> > +	  error ("wrong argument to ignored attributes");
> > +	  inform (input_location, "valid format is %<ns::attr%> or %<ns::%>");
> > +	  continue;
> > +	}
> > +      /* Cut off the vendor part.  */
> > +      *q = '\0';
> > +      char *vendor = opt;
> > +      char *attr = q + 2;
> > +      /* Verify that they look valid.  */
> > +      auto valid_p = [](const char *s) {
> > +	for (; *s != '\0'; ++s)
> > +	  if (!ISALNUM (*s) && *s != '_')
> > +	    return false;
> > +	return true;
> > +      };
> > +      if (!valid_p (vendor) || !valid_p (attr))
> > +	{
> > +	  error ("wrong argument to ignored attributes");
> > +	  continue;
> > +	}
> > +      /* Turn "__attr__" into "attr" so that we have a canonical form of
> > +	 attribute names.  Likewise for vendor.  */
> > +      auto strip = [](char *&s) {
> > +	const size_t l = strlen (s);
> > +	if (l > 4 && s[0] == '_' && s[1] == '_'
> > +	    && s[l - 1] == '_' && s[l - 2] == '_')
> > +	  {
> > +	    s[l - 2] = '\0';
> > +	    s += 2;
> > +	  }
> > +      };
> > +      strip (attr);
> > +      strip (vendor);
> > +      /* If we've already seen this vendor::attr, ignore it.  Attempting to
> > +	 register it twice would lead to a crash.  */
> > +      if (lookup_scoped_attribute_spec (get_identifier (vendor),
> > +					get_identifier (attr)))
> > +	continue;
> > +      /* In the "vendor::" case, we should ignore *any* attribute coming
> > +	 from this attribute namespace.  */
> > +      if (attr[0] == '\0')
> > +	attr = nullptr;
> > +      /* Create a table with extra attributes which we will register.
> > +	 We can't free it here, so squirrel away the pointers.  */
> > +      attribute_spec *table = new attribute_spec[2];
> > +      ignored_attributes_table.safe_push (table);
> > +      table[0] = { attr, 0, 0, false, false, false, false, nullptr, nullptr };
> > +      table[1] = { nullptr, 0, 0, false, false, false, false, nullptr,
> > +		   nullptr };
> > +      register_scoped_attributes (table, vendor, !attr);
> > +    }
> > +}
> > +
> > +/* Free data we might have allocated when adding extra attributes.  */
> > +
> > +void
> > +free_attr_data ()
> > +{
> > +  for (auto x : ignored_attributes_table)
> > +    delete[] x;
> > +  ignored_attributes_table.release ();
> > +}
> > +
> >  /* Initialize attribute tables, and make some sanity checks if checking is
> >     enabled.  */
> >  
> > @@ -252,6 +347,9 @@ init_attributes (void)
> >      /* Put all the GNU attributes into the "gnu" namespace.  */
> >      register_scoped_attributes (attribute_tables[i], "gnu");
> >  
> > +  vec<char *> *ignored = (vec<char *> *) flag_ignored_attributes;
> > +  handle_ignored_attributes_option (ignored);
> > +
> >    invoke_plugin_callbacks (PLUGIN_ATTRIBUTES, NULL);
> >    attributes_initialized = true;
> >  }
> > @@ -456,6 +554,19 @@ diag_attr_exclusions (tree last_decl, tree node, tree attrname,
> >    return found;
> >  }
> >  
> > +/* Return true iff we should not complain about unknown attributes
> > +   coming from the attribute namespace NS.  This is the case for
> > +   the -Wno-attributes=ns:: command-line option.  */
> > +
> > +static bool
> > +attr_namespace_ignored_p (tree ns)
> > +{
> > +  if (ns == NULL_TREE)
> > +    return false;
> > +  scoped_attributes *r = find_attribute_namespace (IDENTIFIER_POINTER (ns));
> > +  return r && r->ignored_p;
> > +}
> > +
> >  /* Process the attributes listed in ATTRIBUTES and install them in *NODE,
> >     which is either a DECL (including a TYPE_DECL) or a TYPE.  If a DECL,
> >     it should be modified in place; if a TYPE, a copy should be created
> > @@ -556,7 +667,8 @@ decl_attributes (tree *node, tree attributes, int flags,
> >  
> >        if (spec == NULL)
> >  	{
> > -	  if (!(flags & (int) ATTR_FLAG_BUILT_IN))
> > +	  if (!(flags & (int) ATTR_FLAG_BUILT_IN)
> > +	      && !attr_namespace_ignored_p (ns))
> >  	    {
> >  	      if (ns == NULL_TREE || !cxx11_attr_p)
> >  		warning (OPT_Wattributes, "%qE attribute directive ignored",
> > diff --git a/gcc/attribs.h b/gcc/attribs.h
> > index 138c509bce1..96a527f67a9 100644
> > --- a/gcc/attribs.h
> > +++ b/gcc/attribs.h
> > @@ -21,6 +21,7 @@ along with GCC; see the file COPYING3.  If not see
> >  #define GCC_ATTRIBS_H
> >  
> >  extern const struct attribute_spec *lookup_attribute_spec (const_tree);
> > +extern void free_attr_data ();
> >  extern void init_attributes (void);
> >  
> >  /* Process the attributes listed in ATTRIBUTES and install them in *NODE,
> > @@ -40,12 +41,14 @@ extern void apply_tm_attr (tree, tree);
> >  extern tree make_attribute (const char *, const char *, tree);
> >  
> >  extern struct scoped_attributes* register_scoped_attributes (const struct attribute_spec *,
> > -							     const char *);
> > +							     const char *,
> > +							     bool = false);
> >  
> >  extern char *sorted_attr_string (tree);
> >  extern bool common_function_versions (tree, tree);
> >  extern tree make_dispatcher_decl (const tree);
> >  extern bool is_function_default_version (const tree);
> > +extern void handle_ignored_attributes_option (vec<char *> *);
> >  
> >  /* Return a type like TTYPE except that its TYPE_ATTRIBUTES
> >     is ATTRIBUTE.
> > diff --git a/gcc/c-family/c-pragma.c b/gcc/c-family/c-pragma.c
> > index a9be8df0384..6beba5503f0 100644
> > --- a/gcc/c-family/c-pragma.c
> > +++ b/gcc/c-family/c-pragma.c
> > @@ -764,7 +764,7 @@ handle_pragma_diagnostic(cpp_reader *ARG_UNUSED(dummy))
> >    if (token != CPP_NAME)
> >      {
> >        warning_at (loc, OPT_Wpragmas,
> > -		  "missing [error|warning|ignored|push|pop]"
> > +		  "missing [error|warning|ignored|push|pop|ignored_attributes]"
> >  		  " after %<#pragma GCC diagnostic%>");
> >        return;
> >      }
> > @@ -787,10 +787,43 @@ handle_pragma_diagnostic(cpp_reader *ARG_UNUSED(dummy))
> >        diagnostic_pop_diagnostics (global_dc, input_location);
> >        return;
> >      }
> > +  else if (strcmp (kind_string, "ignored_attributes") == 0)
> > +    {
> > +      token = pragma_lex (&x, &loc);
> > +      if (token != CPP_STRING)
> > +	{
> > +	  warning_at (loc, OPT_Wpragmas,
> > +		      "missing attribute name after %<#pragma GCC diagnostic "
> > +		      "ignored_attributes%>");
> > +	  return;
> > +	}
> > +      char *args = xstrdup (TREE_STRING_POINTER (x));
> > +      const size_t l = strlen (args);
> > +      if (l == 0)
> > +	{
> > +	  warning_at (loc, OPT_Wpragmas, "missing argument to %<#pragma GCC "
> > +		      "diagnostic ignored_attributes%>");
> > +	  free (args);
> > +	  return;
> > +	}
> > +      else if (args[l - 1] == ',')
> > +	{
> > +	  warning_at (loc, OPT_Wpragmas, "trailing %<,%> in arguments for "
> > +		      "%<#pragma GCC diagnostic ignored_attributes%>");
> > +	  free (args);
> > +	  return;
> > +	}
> > +      auto_vec<char *> v;
> > +      for (char *p = strtok (args, ","); p; p = strtok (NULL, ","))
> > +	v.safe_push (p);
> > +      handle_ignored_attributes_option (&v);
> > +      /* ??? We can't free (args); here.  */
> > +      return;
> > +    }
> >    else
> >      {
> >        warning_at (loc, OPT_Wpragmas,
> > -		  "expected [error|warning|ignored|push|pop]"
> > +		  "expected [error|warning|ignored|push|pop|ignored_attributes]"
> >  		  " after %<#pragma GCC diagnostic%>");
> >        return;
> >      }
> > diff --git a/gcc/common.opt b/gcc/common.opt
> > index a9644cae4f0..c429e43c6c1 100644
> > --- a/gcc/common.opt
> > +++ b/gcc/common.opt
> > @@ -83,7 +83,7 @@ int flag_gen_aux_info = 0
> >  Variable
> >  int flag_shlib
> >  
> > -; These two are really VEC(char_p,heap) *.
> > +; These three are really VEC(char_p,heap) *.
> >  
> >  Variable
> >  void *flag_instrument_functions_exclude_functions
> > @@ -91,6 +91,9 @@ void *flag_instrument_functions_exclude_functions
> >  Variable
> >  void *flag_instrument_functions_exclude_files
> >  
> > +Variable
> > +void *flag_ignored_attributes
> > +
> >  ; Generic structs (e.g. templates not explicitly specialized)
> >  ; may not have a compilation unit associated with them, and so
> >  ; may need to be treated differently from ordinary structs.
> > @@ -549,6 +552,10 @@ Wattributes
> >  Common Var(warn_attributes) Init(1) Warning
> >  Warn about inappropriate attribute usage.
> >  
> > +Wattributes=
> > +Common Joined
> > +Do not warn about specified attributes.
> > +
> >  Wattribute-alias
> >  Common Alias(Wattribute_alias=, 1, 0) Warning
> >  Warn about type safety and similar errors and mismatches in declarations with alias attributes.
> > diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
> > index b4c5376223a..28fd0cc6dd5 100644
> > --- a/gcc/doc/extend.texi
> > +++ b/gcc/doc/extend.texi
> > @@ -23761,6 +23761,25 @@ restored.
> >    foo(d);                       /* depends on command-line options */
> >  @end smallexample
> >  
> > +@item #pragma GCC diagnostic ignored_attributes
> > +
> > +Similarly to @option{-Wno-attributes=}, this pragma allows users to suppress
> > +warnings about unknown scoped attributes (in C++11 and C2X).  For example,
> > +@code{#pragma GCC diagnostic ignored_attributes "vendor::attr"} disables
> > +warning about the following declaration:
> > +
> > +@smallexample
> > +[[vendor::attr]] void f();
> > +@end smallexample
> > +
> > +whereas @code{#pragma GCC diagnostic ignored_attributes "vendor::"} prevents
> > +warning about both of these declarations:
> > +
> > +@smallexample
> > +[[vendor::safe]] void f();
> > +[[vendor::unsafe]] void f2();
> > +@end smallexample
> > +
> >  @end table
> >  
> >  GCC also offers a simple mechanism for printing messages during
> > diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> > index 5b016166972..fe2cb768c61 100644
> > --- a/gcc/doc/invoke.texi
> > +++ b/gcc/doc/invoke.texi
> > @@ -8623,6 +8623,26 @@ unrecognized attributes, function attributes applied to variables,
> >  etc.  This does not stop errors for incorrect use of supported
> >  attributes.
> >  
> > +Additionally, using @option{-Wno-attributes=}, it is possible to suppress
> > +warnings about unknown scoped attributes (in C++11 and C2X).  For example,
> > +@option{-Wno-attributes=vendor::attr} disables warning about the following
> > +declaration:
> > +
> > +@smallexample
> > +[[vendor::attr]] void f();
> > +@end smallexample
> > +
> > +It is also possible to disable warning about all attributes in a namespace
> > +using @option{-Wno-attributes=vendor::} which prevents warning about both
> > +of these declarations:
> > +
> > +@smallexample
> > +[[vendor::safe]] void f();
> > +[[vendor::unsafe]] void f2();
> > +@end smallexample
> > +
> > +Note that @option{-Wno-attributes=} does not imply @option{-Wno-attributes}.
> > +
> >  @item -Wno-builtin-declaration-mismatch
> >  @opindex Wno-builtin-declaration-mismatch
> >  @opindex Wbuiltin-declaration-mismatch
> > diff --git a/gcc/opts.c b/gcc/opts.c
> > index 6503980cd33..e3b66ad0d43 100644
> > --- a/gcc/opts.c
> > +++ b/gcc/opts.c
> > @@ -2558,6 +2558,26 @@ common_handle_option (struct gcc_options *opts,
> >        /* Currently handled in a prescan.  */
> >        break;
> >  
> > +    case OPT_Wattributes_:
> > +      if (lang_mask == CL_DRIVER)
> > +	break;
> > +
> > +      if (value)
> > +	{
> > +	  error_at (loc, "arguments ignored for %<-Wattributes=%>; use "
> > +		    "%<-Wno-attributes=%> instead");
> > +	  break;
> > +	}
> > +      else if (arg[strlen (arg) - 1] == ',')
> > +	{
> > +	  error_at (loc, "trailing %<,%> in arguments for "
> > +		    "%<-Wno-attributes=%>");
> > +	  break;
> > +	}
> > +
> > +      add_comma_separated_to_vector (&opts->x_flag_ignored_attributes, arg);
> > +      break;
> > +
> >      case OPT_Werror:
> >        dc->warning_as_error_requested = value;
> >        break;
> > diff --git a/gcc/plugin.h b/gcc/plugin.h
> > index 1640e253ca5..5556763d1bf 100644
> > --- a/gcc/plugin.h
> > +++ b/gcc/plugin.h
> > @@ -197,7 +197,9 @@ invoke_plugin_callbacks (int event ATTRIBUTE_UNUSED,
> >  /* In attribs.c.  */
> >  
> >  extern void register_attribute (const struct attribute_spec *attr);
> > +/* The default argument for the third parameter is given in attribs.h.  */
> >  extern struct scoped_attributes* register_scoped_attributes (const struct attribute_spec *,
> > -							     const char *);
> > +							     const char *,
> > +							     bool);
> >  
> >  #endif /* PLUGIN_H */
> > diff --git a/gcc/testsuite/c-c++-common/Wno-attributes-1.c b/gcc/testsuite/c-c++-common/Wno-attributes-1.c
> > new file mode 100644
> > index 00000000000..aac1a69fd85
> > --- /dev/null
> > +++ b/gcc/testsuite/c-c++-common/Wno-attributes-1.c
> > @@ -0,0 +1,55 @@
> > +/* PR c++/101940 */
> > +/* { dg-do compile } */
> > +/* { dg-additional-options "-std=c++11" { target c++ } } */
> > +/* { dg-additional-options "-Wno-attributes=company::,yoyodyne::attr" } */
> > +/* { dg-additional-options "-Wno-attributes=c1::attr,c1::attr,c1::__attr__" } */
> > +/* { dg-additional-options "-Wno-attributes=c2::,c2::attr" } */
> > +/* { dg-additional-options "-Wno-attributes=c3::attr,c3::" } */
> > +/* { dg-additional-options "-Wno-attributes=x::" } */
> > +/* { dg-additional-options "-Wno-attributes=yoyodyne::attr_new" } */
> > +/* { dg-additional-options "-Wno-attributes=c4::__attr__" } */
> > +/* { dg-additional-options "-Wno-attributes=c5::attr" } */
> > +/* { dg-additional-options "-Wno-attributes=__c6__::attr" } */
> > +
> > +[[company::attr]] void f1();
> > +[[company::attr2]] void f2();
> > +
> > +[[yoyodyne::attr]] void f3();
> > +[[yoyodyne::__attr__]] void f3__();
> > +[[yoyodyne::attrx]] void f4(); /* { dg-warning "ignored" } */
> > +[[yoyodyne::__attrx__]] void f4__(); /* { dg-warning "ignored" } */
> > +
> > +[[c1::attr]] void f5();
> > +
> > +[[c2::attr]] void f6();
> > +[[c2::attrx]] void f7();
> > +[[c2::__attr__]] void f6__();
> > +[[c2::__attrx__]] void f7__();
> > +
> > +[[c3::attr]] void f8();
> > +[[c3::attrx]] void f9();
> > +
> > +[[x::x]] void f10();
> > +
> > +[[yoyodyne::attr_new]] void f11();
> > +[[yoyodyne::__attr_new__]] void f11__();
> > +[[yoyodyne::attr_mew]] void f12(); /* { dg-warning "ignored" } */
> > +[[yoyodyne::__attr_mew__]] void f12__(); /* { dg-warning "ignored" } */
> > +
> > +[[c4::attr]] void f13();
> > +[[c4::__attr__]] void f13__();
> > +[[c4::attrx]] void f14(); /* { dg-warning "ignored" } */
> > +
> > +[[c5::attr]] void f15();
> > +[[c5::__attr__]] void f15__();
> > +[[__c5__::attr]] void __f15();
> > +[[__c5__::__attr__]] void __f15__();
> > +[[c5::attrx]] void f15x(); /* { dg-warning "ignored" } */
> > +[[__c5__::attrx]] void f15x(); /* { dg-warning "ignored" } */
> > +
> > +[[c6::attr]] void f16();
> > +[[c6::__attr__]] void f16__();
> > +[[__c6__::attr]] void __f16();
> > +[[__c6__::__attr__]] void __f16__();
> > +[[c6::attrx]] void f16x(); /* { dg-warning "ignored" } */
> > +[[__c6__::attrx]] void f16x(); /* { dg-warning "ignored" } */
> > diff --git a/gcc/testsuite/c-c++-common/Wno-attributes-2.c b/gcc/testsuite/c-c++-common/Wno-attributes-2.c
> > new file mode 100644
> > index 00000000000..4307c74b048
> > --- /dev/null
> > +++ b/gcc/testsuite/c-c++-common/Wno-attributes-2.c
> > @@ -0,0 +1,56 @@
> > +/* PR c++/101940 */
> > +/* { dg-do compile } */
> > +/* { dg-additional-options "-std=c++11" { target c++ } } */
> > +
> > +#pragma GCC diagnostic ignored_attributes "company::,yoyodyne::attr"
> > +#pragma GCC diagnostic ignored_attributes "c1::attr,c1::attr,c1::__attr__"
> > +#pragma GCC diagnostic ignored_attributes "c2::,c2::attr"
> > +#pragma GCC diagnostic ignored_attributes "c3::attr,c3::"
> > +#pragma GCC diagnostic ignored_attributes "x::"
> > +#pragma GCC diagnostic ignored_attributes "yoyodyne::attr_new"
> > +#pragma GCC diagnostic ignored_attributes "c4::__attr__"
> > +#pragma GCC diagnostic ignored_attributes "c5::attr"
> > +#pragma GCC diagnostic ignored_attributes "__c6__::attr"
> > +
> > +[[company::attr]] void f1();
> > +[[company::attr2]] void f2();
> > +
> > +[[yoyodyne::attr]] void f3();
> > +[[yoyodyne::__attr__]] void f3__();
> > +[[yoyodyne::attrx]] void f4(); /* { dg-warning "ignored" } */
> > +[[yoyodyne::__attrx__]] void f4__(); /* { dg-warning "ignored" } */
> > +
> > +[[c1::attr]] void f5();
> > +
> > +[[c2::attr]] void f6();
> > +[[c2::attrx]] void f7();
> > +[[c2::__attr__]] void f6__();
> > +[[c2::__attrx__]] void f7__();
> > +
> > +[[c3::attr]] void f8();
> > +[[c3::attrx]] void f9();
> > +
> > +[[x::x]] void f10();
> > +
> > +[[yoyodyne::attr_new]] void f11();
> > +[[yoyodyne::__attr_new__]] void f11__();
> > +[[yoyodyne::attr_mew]] void f12(); /* { dg-warning "ignored" } */
> > +[[yoyodyne::__attr_mew__]] void f12__(); /* { dg-warning "ignored" } */
> > +
> > +[[c4::attr]] void f13();
> > +[[c4::__attr__]] void f13__();
> > +[[c4::attrx]] void f14(); /* { dg-warning "ignored" } */
> > +
> > +[[c5::attr]] void f15();
> > +[[c5::__attr__]] void f15__();
> > +[[__c5__::attr]] void __f15();
> > +[[__c5__::__attr__]] void __f15__();
> > +[[c5::attrx]] void f15x(); /* { dg-warning "ignored" } */
> > +[[__c5__::attrx]] void f15x(); /* { dg-warning "ignored" } */
> > +
> > +[[c6::attr]] void f16();
> > +[[c6::__attr__]] void f16__();
> > +[[__c6__::attr]] void __f16();
> > +[[__c6__::__attr__]] void __f16__();
> > +[[c6::attrx]] void f16x(); /* { dg-warning "ignored" } */
> > +[[__c6__::attrx]] void f16x(); /* { dg-warning "ignored" } */
> > diff --git a/gcc/toplev.c b/gcc/toplev.c
> > index e1688aae724..f5ccaf8f799 100644
> > --- a/gcc/toplev.c
> > +++ b/gcc/toplev.c
> > @@ -463,6 +463,8 @@ compile_file (void)
> >    if (flag_dump_locations)
> >      dump_location_info (stderr);
> >  
> > +  free_attr_data ();
> > +
> >    /* Compilation is now finished except for writing
> >       what's left of the symbol table output.  */
> >  
> > 
> > base-commit: a11052d98db2f2a61841f0c5ee84de4ca1b3e296
> > -- 
> > 2.31.1
> > 
> 
> Marek

Marek
  
Jason Merrill Nov. 5, 2021, 6:48 p.m. UTC | #3
On 9/28/21 16:20, Marek Polacek wrote:
> On Thu, Sep 23, 2021 at 02:25:16PM -0400, Jason Merrill wrote:
>> On 9/20/21 18:59, Marek Polacek via Gcc-patches wrote:
>>> +      handle_ignored_attributes_option (&v);
>>> +      /* ??? We can't free (args); here.  */
>>
>> Perhaps we want to copy strings in handle_ignored_attributes_option rather
>> than here?
> 
> Well, the other use doesn't need copying, so I left it be.

But the other use is modifying the strings passed on the command line, 
which also seems questionable.  I think it would be better for 
handle_ignored_attributes_option to copy the relevant pieces out.

The patch looks good outside of this issue.

Jason
  

Patch

diff --git a/gcc/attribs.c b/gcc/attribs.c
index 83fafc98b7d..d5fba7f4bbb 100644
--- a/gcc/attribs.c
+++ b/gcc/attribs.c
@@ -87,6 +87,8 @@  struct scoped_attributes
   const char *ns;
   vec<attribute_spec> attributes;
   hash_table<attribute_hasher> *attribute_hash;
+  /* True if we should not warn about unknown attributes in this NS.  */
+  bool ignored_p;
 };
 
 /* The table of scope attributes.  */
@@ -95,6 +97,8 @@  static vec<scoped_attributes> attributes_table;
 static scoped_attributes* find_attribute_namespace (const char*);
 static void register_scoped_attribute (const struct attribute_spec *,
 				       scoped_attributes *);
+static const struct attribute_spec *lookup_scoped_attribute_spec (const_tree,
+								  const_tree);
 
 static bool attributes_initialized = false;
 
@@ -121,12 +125,14 @@  extract_attribute_substring (struct substring *str)
 
 /* Insert an array of attributes ATTRIBUTES into a namespace.  This
    array must be NULL terminated.  NS is the name of attribute
-   namespace.  The function returns the namespace into which the
-   attributes have been registered.  */
+   namespace.  IGNORED_P is true iff all unknown attributes in this
+   namespace should be ignored for the purposes of -Wattributes.  The
+   function returns the namespace into which the attributes have been
+   registered.  */
 
 scoped_attributes *
 register_scoped_attributes (const struct attribute_spec *attributes,
-			    const char *ns)
+			    const char *ns, bool ignored_p /*=false*/)
 {
   scoped_attributes *result = NULL;
 
@@ -144,9 +150,12 @@  register_scoped_attributes (const struct attribute_spec *attributes,
       memset (&sa, 0, sizeof (sa));
       sa.ns = ns;
       sa.attributes.create (64);
+      sa.ignored_p = ignored_p;
       result = attributes_table.safe_push (sa);
       result->attribute_hash = new hash_table<attribute_hasher> (200);
     }
+  else
+    result->ignored_p |= ignored_p;
 
   /* Really add the attributes to their namespace now.  */
   for (unsigned i = 0; attributes[i].name != NULL; ++i)
@@ -224,6 +233,92 @@  check_attribute_tables (void)
 				 attribute_tables[j][l].name));
 }
 
+/* Used to stash pointers to allocated memory so that we can free them at
+   the end of parsing of all TUs. */
+static vec<attribute_spec *> ignored_attributes_table;
+
+/* Parse arguments ARGS of -Wno-attributes=.
+   Currently we accept:
+     vendor::attr
+     vendor::
+   This functions also registers the parsed attributes so that we don't
+   warn that we don't recognize them.  */
+
+void
+handle_ignored_attributes_option (vec<char *> *v)
+{
+  if (v == nullptr)
+    return;
+
+  for (auto opt : v)
+    {
+      char *q = strstr (opt, "::");
+      /* We don't accept '::attr'.  */
+      if (q == nullptr || q == opt)
+	{
+	  error ("wrong argument to ignored attributes");
+	  inform (input_location, "valid format is %<ns::attr%> or %<ns::%>");
+	  continue;
+	}
+      /* Cut off the vendor part.  */
+      *q = '\0';
+      char *vendor = opt;
+      char *attr = q + 2;
+      /* Verify that they look valid.  */
+      auto valid_p = [](const char *s) {
+	for (; *s != '\0'; ++s)
+	  if (!ISALNUM (*s) && *s != '_')
+	    return false;
+	return true;
+      };
+      if (!valid_p (vendor) || !valid_p (attr))
+	{
+	  error ("wrong argument to ignored attributes");
+	  continue;
+	}
+      /* Turn "__attr__" into "attr" so that we have a canonical form of
+	 attribute names.  Likewise for vendor.  */
+      auto strip = [](char *&s) {
+	const size_t l = strlen (s);
+	if (l > 4 && s[0] == '_' && s[1] == '_'
+	    && s[l - 1] == '_' && s[l - 2] == '_')
+	  {
+	    s[l - 2] = '\0';
+	    s += 2;
+	  }
+      };
+      strip (attr);
+      strip (vendor);
+      /* If we've already seen this vendor::attr, ignore it.  Attempting to
+	 register it twice would lead to a crash.  */
+      if (lookup_scoped_attribute_spec (get_identifier (vendor),
+					get_identifier (attr)))
+	continue;
+      /* In the "vendor::" case, we should ignore *any* attribute coming
+	 from this attribute namespace.  */
+      if (attr[0] == '\0')
+	attr = nullptr;
+      /* Create a table with extra attributes which we will register.
+	 We can't free it here, so squirrel away the pointers.  */
+      attribute_spec *table = new attribute_spec[2];
+      ignored_attributes_table.safe_push (table);
+      table[0] = { attr, 0, 0, false, false, false, false, nullptr, nullptr };
+      table[1] = { nullptr, 0, 0, false, false, false, false, nullptr,
+		   nullptr };
+      register_scoped_attributes (table, vendor, !attr);
+    }
+}
+
+/* Free data we might have allocated when adding extra attributes.  */
+
+void
+free_attr_data ()
+{
+  for (auto x : ignored_attributes_table)
+    delete[] x;
+  ignored_attributes_table.release ();
+}
+
 /* Initialize attribute tables, and make some sanity checks if checking is
    enabled.  */
 
@@ -252,6 +347,9 @@  init_attributes (void)
     /* Put all the GNU attributes into the "gnu" namespace.  */
     register_scoped_attributes (attribute_tables[i], "gnu");
 
+  vec<char *> *ignored = (vec<char *> *) flag_ignored_attributes;
+  handle_ignored_attributes_option (ignored);
+
   invoke_plugin_callbacks (PLUGIN_ATTRIBUTES, NULL);
   attributes_initialized = true;
 }
@@ -456,6 +554,19 @@  diag_attr_exclusions (tree last_decl, tree node, tree attrname,
   return found;
 }
 
+/* Return true iff we should not complain about unknown attributes
+   coming from the attribute namespace NS.  This is the case for
+   the -Wno-attributes=ns:: command-line option.  */
+
+static bool
+attr_namespace_ignored_p (tree ns)
+{
+  if (ns == NULL_TREE)
+    return false;
+  scoped_attributes *r = find_attribute_namespace (IDENTIFIER_POINTER (ns));
+  return r && r->ignored_p;
+}
+
 /* Process the attributes listed in ATTRIBUTES and install them in *NODE,
    which is either a DECL (including a TYPE_DECL) or a TYPE.  If a DECL,
    it should be modified in place; if a TYPE, a copy should be created
@@ -556,7 +667,8 @@  decl_attributes (tree *node, tree attributes, int flags,
 
       if (spec == NULL)
 	{
-	  if (!(flags & (int) ATTR_FLAG_BUILT_IN))
+	  if (!(flags & (int) ATTR_FLAG_BUILT_IN)
+	      && !attr_namespace_ignored_p (ns))
 	    {
 	      if (ns == NULL_TREE || !cxx11_attr_p)
 		warning (OPT_Wattributes, "%qE attribute directive ignored",
diff --git a/gcc/attribs.h b/gcc/attribs.h
index 138c509bce1..96a527f67a9 100644
--- a/gcc/attribs.h
+++ b/gcc/attribs.h
@@ -21,6 +21,7 @@  along with GCC; see the file COPYING3.  If not see
 #define GCC_ATTRIBS_H
 
 extern const struct attribute_spec *lookup_attribute_spec (const_tree);
+extern void free_attr_data ();
 extern void init_attributes (void);
 
 /* Process the attributes listed in ATTRIBUTES and install them in *NODE,
@@ -40,12 +41,14 @@  extern void apply_tm_attr (tree, tree);
 extern tree make_attribute (const char *, const char *, tree);
 
 extern struct scoped_attributes* register_scoped_attributes (const struct attribute_spec *,
-							     const char *);
+							     const char *,
+							     bool = false);
 
 extern char *sorted_attr_string (tree);
 extern bool common_function_versions (tree, tree);
 extern tree make_dispatcher_decl (const tree);
 extern bool is_function_default_version (const tree);
+extern void handle_ignored_attributes_option (vec<char *> *);
 
 /* Return a type like TTYPE except that its TYPE_ATTRIBUTES
    is ATTRIBUTE.
diff --git a/gcc/c-family/c-pragma.c b/gcc/c-family/c-pragma.c
index a9be8df0384..6beba5503f0 100644
--- a/gcc/c-family/c-pragma.c
+++ b/gcc/c-family/c-pragma.c
@@ -764,7 +764,7 @@  handle_pragma_diagnostic(cpp_reader *ARG_UNUSED(dummy))
   if (token != CPP_NAME)
     {
       warning_at (loc, OPT_Wpragmas,
-		  "missing [error|warning|ignored|push|pop]"
+		  "missing [error|warning|ignored|push|pop|ignored_attributes]"
 		  " after %<#pragma GCC diagnostic%>");
       return;
     }
@@ -787,10 +787,43 @@  handle_pragma_diagnostic(cpp_reader *ARG_UNUSED(dummy))
       diagnostic_pop_diagnostics (global_dc, input_location);
       return;
     }
+  else if (strcmp (kind_string, "ignored_attributes") == 0)
+    {
+      token = pragma_lex (&x, &loc);
+      if (token != CPP_STRING)
+	{
+	  warning_at (loc, OPT_Wpragmas,
+		      "missing attribute name after %<#pragma GCC diagnostic "
+		      "ignored_attributes%>");
+	  return;
+	}
+      char *args = xstrdup (TREE_STRING_POINTER (x));
+      const size_t l = strlen (args);
+      if (l == 0)
+	{
+	  warning_at (loc, OPT_Wpragmas, "missing argument to %<#pragma GCC "
+		      "diagnostic ignored_attributes%>");
+	  free (args);
+	  return;
+	}
+      else if (args[l - 1] == ',')
+	{
+	  warning_at (loc, OPT_Wpragmas, "trailing %<,%> in arguments for "
+		      "%<#pragma GCC diagnostic ignored_attributes%>");
+	  free (args);
+	  return;
+	}
+      auto_vec<char *> v;
+      for (char *p = strtok (args, ","); p; p = strtok (NULL, ","))
+	v.safe_push (p);
+      handle_ignored_attributes_option (&v);
+      /* ??? We can't free (args); here.  */
+      return;
+    }
   else
     {
       warning_at (loc, OPT_Wpragmas,
-		  "expected [error|warning|ignored|push|pop]"
+		  "expected [error|warning|ignored|push|pop|ignored_attributes]"
 		  " after %<#pragma GCC diagnostic%>");
       return;
     }
diff --git a/gcc/common.opt b/gcc/common.opt
index a9644cae4f0..c429e43c6c1 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -83,7 +83,7 @@  int flag_gen_aux_info = 0
 Variable
 int flag_shlib
 
-; These two are really VEC(char_p,heap) *.
+; These three are really VEC(char_p,heap) *.
 
 Variable
 void *flag_instrument_functions_exclude_functions
@@ -91,6 +91,9 @@  void *flag_instrument_functions_exclude_functions
 Variable
 void *flag_instrument_functions_exclude_files
 
+Variable
+void *flag_ignored_attributes
+
 ; Generic structs (e.g. templates not explicitly specialized)
 ; may not have a compilation unit associated with them, and so
 ; may need to be treated differently from ordinary structs.
@@ -549,6 +552,10 @@  Wattributes
 Common Var(warn_attributes) Init(1) Warning
 Warn about inappropriate attribute usage.
 
+Wattributes=
+Common Joined
+Do not warn about specified attributes.
+
 Wattribute-alias
 Common Alias(Wattribute_alias=, 1, 0) Warning
 Warn about type safety and similar errors and mismatches in declarations with alias attributes.
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index b4c5376223a..28fd0cc6dd5 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -23761,6 +23761,25 @@  restored.
   foo(d);                       /* depends on command-line options */
 @end smallexample
 
+@item #pragma GCC diagnostic ignored_attributes
+
+Similarly to @option{-Wno-attributes=}, this pragma allows users to suppress
+warnings about unknown scoped attributes (in C++11 and C2X).  For example,
+@code{#pragma GCC diagnostic ignored_attributes "vendor::attr"} disables
+warning about the following declaration:
+
+@smallexample
+[[vendor::attr]] void f();
+@end smallexample
+
+whereas @code{#pragma GCC diagnostic ignored_attributes "vendor::"} prevents
+warning about both of these declarations:
+
+@smallexample
+[[vendor::safe]] void f();
+[[vendor::unsafe]] void f2();
+@end smallexample
+
 @end table
 
 GCC also offers a simple mechanism for printing messages during
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 5b016166972..fe2cb768c61 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -8623,6 +8623,26 @@  unrecognized attributes, function attributes applied to variables,
 etc.  This does not stop errors for incorrect use of supported
 attributes.
 
+Additionally, using @option{-Wno-attributes=}, it is possible to suppress
+warnings about unknown scoped attributes (in C++11 and C2X).  For example,
+@option{-Wno-attributes=vendor::attr} disables warning about the following
+declaration:
+
+@smallexample
+[[vendor::attr]] void f();
+@end smallexample
+
+It is also possible to disable warning about all attributes in a namespace
+using @option{-Wno-attributes=vendor::} which prevents warning about both
+of these declarations:
+
+@smallexample
+[[vendor::safe]] void f();
+[[vendor::unsafe]] void f2();
+@end smallexample
+
+Note that @option{-Wno-attributes=} does not imply @option{-Wno-attributes}.
+
 @item -Wno-builtin-declaration-mismatch
 @opindex Wno-builtin-declaration-mismatch
 @opindex Wbuiltin-declaration-mismatch
diff --git a/gcc/opts.c b/gcc/opts.c
index 6503980cd33..e3b66ad0d43 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -2558,6 +2558,26 @@  common_handle_option (struct gcc_options *opts,
       /* Currently handled in a prescan.  */
       break;
 
+    case OPT_Wattributes_:
+      if (lang_mask == CL_DRIVER)
+	break;
+
+      if (value)
+	{
+	  error_at (loc, "arguments ignored for %<-Wattributes=%>; use "
+		    "%<-Wno-attributes=%> instead");
+	  break;
+	}
+      else if (arg[strlen (arg) - 1] == ',')
+	{
+	  error_at (loc, "trailing %<,%> in arguments for "
+		    "%<-Wno-attributes=%>");
+	  break;
+	}
+
+      add_comma_separated_to_vector (&opts->x_flag_ignored_attributes, arg);
+      break;
+
     case OPT_Werror:
       dc->warning_as_error_requested = value;
       break;
diff --git a/gcc/plugin.h b/gcc/plugin.h
index 1640e253ca5..5556763d1bf 100644
--- a/gcc/plugin.h
+++ b/gcc/plugin.h
@@ -197,7 +197,9 @@  invoke_plugin_callbacks (int event ATTRIBUTE_UNUSED,
 /* In attribs.c.  */
 
 extern void register_attribute (const struct attribute_spec *attr);
+/* The default argument for the third parameter is given in attribs.h.  */
 extern struct scoped_attributes* register_scoped_attributes (const struct attribute_spec *,
-							     const char *);
+							     const char *,
+							     bool);
 
 #endif /* PLUGIN_H */
diff --git a/gcc/testsuite/c-c++-common/Wno-attributes-1.c b/gcc/testsuite/c-c++-common/Wno-attributes-1.c
new file mode 100644
index 00000000000..aac1a69fd85
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wno-attributes-1.c
@@ -0,0 +1,55 @@ 
+/* PR c++/101940 */
+/* { dg-do compile } */
+/* { dg-additional-options "-std=c++11" { target c++ } } */
+/* { dg-additional-options "-Wno-attributes=company::,yoyodyne::attr" } */
+/* { dg-additional-options "-Wno-attributes=c1::attr,c1::attr,c1::__attr__" } */
+/* { dg-additional-options "-Wno-attributes=c2::,c2::attr" } */
+/* { dg-additional-options "-Wno-attributes=c3::attr,c3::" } */
+/* { dg-additional-options "-Wno-attributes=x::" } */
+/* { dg-additional-options "-Wno-attributes=yoyodyne::attr_new" } */
+/* { dg-additional-options "-Wno-attributes=c4::__attr__" } */
+/* { dg-additional-options "-Wno-attributes=c5::attr" } */
+/* { dg-additional-options "-Wno-attributes=__c6__::attr" } */
+
+[[company::attr]] void f1();
+[[company::attr2]] void f2();
+
+[[yoyodyne::attr]] void f3();
+[[yoyodyne::__attr__]] void f3__();
+[[yoyodyne::attrx]] void f4(); /* { dg-warning "ignored" } */
+[[yoyodyne::__attrx__]] void f4__(); /* { dg-warning "ignored" } */
+
+[[c1::attr]] void f5();
+
+[[c2::attr]] void f6();
+[[c2::attrx]] void f7();
+[[c2::__attr__]] void f6__();
+[[c2::__attrx__]] void f7__();
+
+[[c3::attr]] void f8();
+[[c3::attrx]] void f9();
+
+[[x::x]] void f10();
+
+[[yoyodyne::attr_new]] void f11();
+[[yoyodyne::__attr_new__]] void f11__();
+[[yoyodyne::attr_mew]] void f12(); /* { dg-warning "ignored" } */
+[[yoyodyne::__attr_mew__]] void f12__(); /* { dg-warning "ignored" } */
+
+[[c4::attr]] void f13();
+[[c4::__attr__]] void f13__();
+[[c4::attrx]] void f14(); /* { dg-warning "ignored" } */
+
+[[c5::attr]] void f15();
+[[c5::__attr__]] void f15__();
+[[__c5__::attr]] void __f15();
+[[__c5__::__attr__]] void __f15__();
+[[c5::attrx]] void f15x(); /* { dg-warning "ignored" } */
+[[__c5__::attrx]] void f15x(); /* { dg-warning "ignored" } */
+
+[[c6::attr]] void f16();
+[[c6::__attr__]] void f16__();
+[[__c6__::attr]] void __f16();
+[[__c6__::__attr__]] void __f16__();
+[[c6::attrx]] void f16x(); /* { dg-warning "ignored" } */
+[[__c6__::attrx]] void f16x(); /* { dg-warning "ignored" } */
diff --git a/gcc/testsuite/c-c++-common/Wno-attributes-2.c b/gcc/testsuite/c-c++-common/Wno-attributes-2.c
new file mode 100644
index 00000000000..4307c74b048
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wno-attributes-2.c
@@ -0,0 +1,56 @@ 
+/* PR c++/101940 */
+/* { dg-do compile } */
+/* { dg-additional-options "-std=c++11" { target c++ } } */
+
+#pragma GCC diagnostic ignored_attributes "company::,yoyodyne::attr"
+#pragma GCC diagnostic ignored_attributes "c1::attr,c1::attr,c1::__attr__"
+#pragma GCC diagnostic ignored_attributes "c2::,c2::attr"
+#pragma GCC diagnostic ignored_attributes "c3::attr,c3::"
+#pragma GCC diagnostic ignored_attributes "x::"
+#pragma GCC diagnostic ignored_attributes "yoyodyne::attr_new"
+#pragma GCC diagnostic ignored_attributes "c4::__attr__"
+#pragma GCC diagnostic ignored_attributes "c5::attr"
+#pragma GCC diagnostic ignored_attributes "__c6__::attr"
+
+[[company::attr]] void f1();
+[[company::attr2]] void f2();
+
+[[yoyodyne::attr]] void f3();
+[[yoyodyne::__attr__]] void f3__();
+[[yoyodyne::attrx]] void f4(); /* { dg-warning "ignored" } */
+[[yoyodyne::__attrx__]] void f4__(); /* { dg-warning "ignored" } */
+
+[[c1::attr]] void f5();
+
+[[c2::attr]] void f6();
+[[c2::attrx]] void f7();
+[[c2::__attr__]] void f6__();
+[[c2::__attrx__]] void f7__();
+
+[[c3::attr]] void f8();
+[[c3::attrx]] void f9();
+
+[[x::x]] void f10();
+
+[[yoyodyne::attr_new]] void f11();
+[[yoyodyne::__attr_new__]] void f11__();
+[[yoyodyne::attr_mew]] void f12(); /* { dg-warning "ignored" } */
+[[yoyodyne::__attr_mew__]] void f12__(); /* { dg-warning "ignored" } */
+
+[[c4::attr]] void f13();
+[[c4::__attr__]] void f13__();
+[[c4::attrx]] void f14(); /* { dg-warning "ignored" } */
+
+[[c5::attr]] void f15();
+[[c5::__attr__]] void f15__();
+[[__c5__::attr]] void __f15();
+[[__c5__::__attr__]] void __f15__();
+[[c5::attrx]] void f15x(); /* { dg-warning "ignored" } */
+[[__c5__::attrx]] void f15x(); /* { dg-warning "ignored" } */
+
+[[c6::attr]] void f16();
+[[c6::__attr__]] void f16__();
+[[__c6__::attr]] void __f16();
+[[__c6__::__attr__]] void __f16__();
+[[c6::attrx]] void f16x(); /* { dg-warning "ignored" } */
+[[__c6__::attrx]] void f16x(); /* { dg-warning "ignored" } */
diff --git a/gcc/toplev.c b/gcc/toplev.c
index e1688aae724..f5ccaf8f799 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -463,6 +463,8 @@  compile_file (void)
   if (flag_dump_locations)
     dump_location_info (stderr);
 
+  free_attr_data ();
+
   /* Compilation is now finished except for writing
      what's left of the symbol table output.  */