C++ PATCH for c++/79817 - attribute deprecated on namespace
diff mbox series

Message ID 20190821010333.GC14737@redhat.com
State New
Headers show
Series
  • C++ PATCH for c++/79817 - attribute deprecated on namespace
Related show

Commit Message

Marek Polacek Aug. 21, 2019, 1:03 a.m. UTC
This PR complains that we are not handling the attribute deprecated on
namespaces.  This has gotten more important now because of ranges-v3 which
renamed its "view" namespace to "views", following the PascalCase to snake_case
change, and "view" is now deprecated.

Unfortunately we can't just call cp_warn_deprecated_use_scopes in
cp_parser_nested_name_specifier_opt and call it a day because we shouldn't warn
for code like

  namespace [[deprecated]] N {
    void fn();
  }
  void N::fn(); // here
  void N::fn() { } // and here

and in cp_parser_nested_name_specifier_opt we simply don't know if we're
dealing with a function decl.  Calling cp_warn_deprecated_use_scopes from
cp_parser_type_specifier resulted int duplicated diagnostics so that one
is out too.  So I did the following which doesn't seem too bad.

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

2019-08-20  Marek Polacek  <polacek@redhat.com>

	PR c++/79817 - attribute deprecated on namespace.
	* cp-tree.h (cp_warn_deprecated_use_scopes): Declare.
	* decl.c (grokdeclarator): Call cp_warn_deprecated_use_scopes.
	(type_is_deprecated): Likewise.
	* decl2.c (cp_warn_deprecated_use_scopes): New function.
	* name-lookup.c (handle_namespace_attrs): Handle attribute deprecated.
	* parser.c (cp_parser_namespace_alias_definition): Call
	cp_warn_deprecated_use_scopes.
	(cp_parser_using_declaration): Likewise.
	(cp_parser_using_directive): Likewise.
	* semantics.c (finish_id_expression_1): Likewise.

	* g++.dg/cpp0x/attributes-namespace1.C: New test.
	* g++.dg/cpp0x/attributes-namespace2.C: New test.
	* g++.dg/cpp0x/attributes-namespace3.C: New test.
	* g++.dg/cpp0x/attributes-namespace4.C: New test.
	* g++.dg/cpp0x/attributes-namespace5.C: New test.
	* g++.dg/cpp1z/namespace-attribs.C: Adjust.
	* g++.dg/cpp1z/namespace-attribs2.C: Adjust.

Comments

Nathan Sidwell Aug. 22, 2019, 6:01 p.m. UTC | #1
On 8/20/19 9:03 PM, Marek Polacek wrote:

> and in cp_parser_nested_name_specifier_opt we simply don't know if we're
> dealing with a function decl.  Calling cp_warn_deprecated_use_scopes from
> cp_parser_type_specifier resulted int duplicated diagnostics so that one
> is out too.  So I did the following which doesn't seem too bad.

>
> diff --git gcc/cp/decl.c gcc/cp/decl.c
> index 08b7baa40e0..46ad0271f7b 100644
> --- gcc/cp/decl.c
> +++ gcc/cp/decl.c
> @@ -10791,6 +10791,7 @@ grokdeclarator (const cp_declarator *declarator,
>     cp_warn_deprecated_use (type);
>     if (type && TREE_CODE (type) == TYPE_DECL)
>       {
> +      cp_warn_deprecated_use_scopes (DECL_CONTEXT (type));

CP_DECL_CONTEXT would be clearer, here and elsewhere.

>     /* Do warn about using typedefs to a deprecated class.  */
> diff --git gcc/cp/decl2.c gcc/cp/decl2.c
> index a32108f9d16..d6f407d7aef 100644
> --- gcc/cp/decl2.c
> +++ gcc/cp/decl2.c
> @@ -5407,6 +5407,23 @@ cp_warn_deprecated_use (tree decl, tsubst_flags_t complain)
>     return warned;
>   }
>   
> +/* Like above, but takes into account outer scopes.  */
> +
> +void
> +cp_warn_deprecated_use_scopes (tree ns)

Do we need to walk non-namespace scopes here?  can we just bail if NS is 
not a namespace?  if can legitimately not be a namespace, calling it NS 
is confusing :)

> +{
> +  while (ns
> +	 && ns != error_mark_node
> +	 && ns != global_namespace)
> +    {
> +      cp_warn_deprecated_use (ns);
> +      if (TYPE_P (ns))
... and does this ever trigger?

> +	ns = CP_TYPE_CONTEXT (ns);
> +      else
> +	ns = CP_DECL_CONTEXT (ns);
> +    }
> +}

I always worry about such recursive lookups.  NAMESPACE_DECL has so many 
spare flags, could we take one to say 'is, or contained in, deprecated', 
and thus know whether we can bail early.  And stop at the first 
deprecated one -- though not sure why someone would deprecate more than 
one namespace in a nest.  thoughts?

otherwise looks good, with a good set of tests.

nathan
Jason Merrill Aug. 22, 2019, 6:49 p.m. UTC | #2
On Thu, Aug 22, 2019 at 11:01 AM Nathan Sidwell <nathan@acm.org> wrote:
>
> On 8/20/19 9:03 PM, Marek Polacek wrote:
>
> > and in cp_parser_nested_name_specifier_opt we simply don't know if we're
> > dealing with a function decl.  Calling cp_warn_deprecated_use_scopes from
> > cp_parser_type_specifier resulted int duplicated diagnostics so that one
> > is out too.  So I did the following which doesn't seem too bad.
>
> >
> > diff --git gcc/cp/decl.c gcc/cp/decl.c
> > index 08b7baa40e0..46ad0271f7b 100644
> > --- gcc/cp/decl.c
> > +++ gcc/cp/decl.c
> > @@ -10791,6 +10791,7 @@ grokdeclarator (const cp_declarator *declarator,
> >     cp_warn_deprecated_use (type);
> >     if (type && TREE_CODE (type) == TYPE_DECL)
> >       {
> > +      cp_warn_deprecated_use_scopes (DECL_CONTEXT (type));
>
> CP_DECL_CONTEXT would be clearer, here and elsewhere.
>
> >     /* Do warn about using typedefs to a deprecated class.  */
> > diff --git gcc/cp/decl2.c gcc/cp/decl2.c
> > index a32108f9d16..d6f407d7aef 100644
> > --- gcc/cp/decl2.c
> > +++ gcc/cp/decl2.c
> > @@ -5407,6 +5407,23 @@ cp_warn_deprecated_use (tree decl, tsubst_flags_t complain)
> >     return warned;
> >   }
> >
> > +/* Like above, but takes into account outer scopes.  */
> > +
> > +void
> > +cp_warn_deprecated_use_scopes (tree ns)
>
> Do we need to walk non-namespace scopes here?  can we just bail if NS is
> not a namespace?  if can legitimately not be a namespace, calling it NS
> is confusing :)

It seems like it can be a class in some cases, and I would want to
warn about deprecated classes named in a nested-name-specifier.  Did
we not already warn about that?

I agree that the parameter name is confusing.

> > +{
> > +  while (ns
> > +      && ns != error_mark_node
> > +      && ns != global_namespace)
> > +    {
> > +      cp_warn_deprecated_use (ns);
> > +      if (TYPE_P (ns))
> ... and does this ever trigger?
>
> > +     ns = CP_TYPE_CONTEXT (ns);
> > +      else
> > +     ns = CP_DECL_CONTEXT (ns);
> > +    }
> > +}
>
> I always worry about such recursive lookups.  NAMESPACE_DECL has so many
> spare flags, could we take one to say 'is, or contained in, deprecated',
> and thus know whether we can bail early.  And stop at the first
> deprecated one -- though not sure why someone would deprecate more than
> one namespace in a nest.  thoughts?

I can imagine deprecating an inner namespace and later deprecating an
outer namespace, but I don't think it's important to warn about more
than one in that case.

Jason
Marek Polacek Aug. 23, 2019, 6:55 p.m. UTC | #3
On Thu, Aug 22, 2019 at 11:49:49AM -0700, Jason Merrill wrote:
> On Thu, Aug 22, 2019 at 11:01 AM Nathan Sidwell <nathan@acm.org> wrote:
> >
> > On 8/20/19 9:03 PM, Marek Polacek wrote:
> >
> > > and in cp_parser_nested_name_specifier_opt we simply don't know if we're
> > > dealing with a function decl.  Calling cp_warn_deprecated_use_scopes from
> > > cp_parser_type_specifier resulted int duplicated diagnostics so that one
> > > is out too.  So I did the following which doesn't seem too bad.
> >
> > >
> > > diff --git gcc/cp/decl.c gcc/cp/decl.c
> > > index 08b7baa40e0..46ad0271f7b 100644
> > > --- gcc/cp/decl.c
> > > +++ gcc/cp/decl.c
> > > @@ -10791,6 +10791,7 @@ grokdeclarator (const cp_declarator *declarator,
> > >     cp_warn_deprecated_use (type);
> > >     if (type && TREE_CODE (type) == TYPE_DECL)
> > >       {
> > > +      cp_warn_deprecated_use_scopes (DECL_CONTEXT (type));
> >
> > CP_DECL_CONTEXT would be clearer, here and elsewhere.

Oh right, I failed to adjust that.  Fixed now.

> > >     /* Do warn about using typedefs to a deprecated class.  */
> > > diff --git gcc/cp/decl2.c gcc/cp/decl2.c
> > > index a32108f9d16..d6f407d7aef 100644
> > > --- gcc/cp/decl2.c
> > > +++ gcc/cp/decl2.c
> > > @@ -5407,6 +5407,23 @@ cp_warn_deprecated_use (tree decl, tsubst_flags_t complain)
> > >     return warned;
> > >   }
> > >
> > > +/* Like above, but takes into account outer scopes.  */
> > > +
> > > +void
> > > +cp_warn_deprecated_use_scopes (tree ns)
> >
> > Do we need to walk non-namespace scopes here?  can we just bail if NS is
> > not a namespace?  if can legitimately not be a namespace, calling it NS
> > is confusing :)
> 
> It seems like it can be a class in some cases, and I would want to
> warn about deprecated classes named in a nested-name-specifier.  Did
> we not already warn about that?

I know of at least this case:

namespace  N {
  enum [[deprecated]] E { X };
}

int i = N::E::X;

we didn't warn about the deprecated 'E' but with this patch we do.

> I agree that the parameter name is confusing.

True, I didn't realize.  Fixed now.

> > > +{
> > > +  while (ns
> > > +      && ns != error_mark_node
> > > +      && ns != global_namespace)
> > > +    {
> > > +      cp_warn_deprecated_use (ns);
> > > +      if (TYPE_P (ns))
> > ... and does this ever trigger?

Yeah, it can, see the testcase above.

> > > +     ns = CP_TYPE_CONTEXT (ns);
> > > +      else
> > > +     ns = CP_DECL_CONTEXT (ns);
> > > +    }
> > > +}
> >
> > I always worry about such recursive lookups.  NAMESPACE_DECL has so many
> > spare flags, could we take one to say 'is, or contained in, deprecated',
> > and thus know whether we can bail early.  And stop at the first
> > deprecated one -- though not sure why someone would deprecate more than
> > one namespace in a nest.  thoughts?

Well, this patch uses the TREE_DEPRECATED bit for a NAMESPACE_DECL.  I'm not
exactly sure how another bit would help us, where I would set it (I guess the
innermost scope, but that doesn't have to be a NAMESPACE_DECL), and how that
would play with diagnostics -- i.e. if we'd print the correct name.

> I can imagine deprecating an inner namespace and later deprecating an
> outer namespace, but I don't think it's important to warn about more
> than one in that case.

Agreed, and to that effect I tweaked cp_warn_deprecated_use_scopes to return
if cp_warn_deprecated_use issued a warning.

Thank you both!

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

2019-08-23  Marek Polacek  <polacek@redhat.com>

	PR c++/79817 - attribute deprecated on namespace.
	* cp-tree.h (cp_warn_deprecated_use_scopes): Declare.
	* decl.c (grokdeclarator): Call cp_warn_deprecated_use_scopes.
	(type_is_deprecated): Likewise.
	* decl2.c (cp_warn_deprecated_use_scopes): New function.
	* name-lookup.c (handle_namespace_attrs): Handle attribute deprecated.
	* parser.c (cp_parser_namespace_alias_definition): Call
	cp_warn_deprecated_use_scopes.
	(cp_parser_using_declaration): Likewise.
	(cp_parser_using_directive): Likewise.
	* semantics.c (finish_id_expression_1): Likewise.

	* g++.dg/cpp0x/attributes-namespace1.C: New test.
	* g++.dg/cpp0x/attributes-namespace2.C: New test.
	* g++.dg/cpp0x/attributes-namespace3.C: New test.
	* g++.dg/cpp0x/attributes-namespace4.C: New test.
	* g++.dg/cpp0x/attributes-namespace5.C: New test.
	* g++.dg/cpp1z/namespace-attribs.C: Adjust.
	* g++.dg/cpp1z/namespace-attribs2.C: Adjust.

diff --git gcc/cp/cp-tree.h gcc/cp/cp-tree.h
index 05f91861b42..42f180d1dd3 100644
--- gcc/cp/cp-tree.h
+++ gcc/cp/cp-tree.h
@@ -6264,6 +6264,7 @@ extern bool is_list_ctor			(tree);
 extern void validate_conversion_obstack		(void);
 extern void mark_versions_used			(tree);
 extern bool cp_warn_deprecated_use		(tree, tsubst_flags_t = tf_warning_or_error);
+extern void cp_warn_deprecated_use_scopes	(tree);
 extern tree get_function_version_dispatcher	(tree);
 
 /* in class.c */
diff --git gcc/cp/decl.c gcc/cp/decl.c
index 88aa69ce5de..cb5571e4f24 100644
--- gcc/cp/decl.c
+++ gcc/cp/decl.c
@@ -10791,6 +10791,7 @@ grokdeclarator (const cp_declarator *declarator,
   cp_warn_deprecated_use (type);
   if (type && TREE_CODE (type) == TYPE_DECL)
     {
+      cp_warn_deprecated_use_scopes (CP_DECL_CONTEXT (type));
       typedef_decl = type;
       type = TREE_TYPE (typedef_decl);
       if (DECL_ARTIFICIAL (typedef_decl))
@@ -13230,7 +13231,10 @@ type_is_deprecated (tree type)
       if (TREE_DEPRECATED (TYPE_NAME (type)))
 	return type;
       else
-	return NULL_TREE;
+	{
+	  cp_warn_deprecated_use_scopes (CP_DECL_CONTEXT (TYPE_NAME (type)));
+	  return NULL_TREE;
+	}
     }
 
   /* Do warn about using typedefs to a deprecated class.  */
diff --git gcc/cp/decl2.c gcc/cp/decl2.c
index a32108f9d16..aca37a28f49 100644
--- gcc/cp/decl2.c
+++ gcc/cp/decl2.c
@@ -5407,6 +5407,24 @@ cp_warn_deprecated_use (tree decl, tsubst_flags_t complain)
   return warned;
 }
 
+/* Like above, but takes into account outer scopes.  */
+
+void
+cp_warn_deprecated_use_scopes (tree scope)
+{
+  while (scope
+	 && scope != error_mark_node
+	 && scope != global_namespace)
+    {
+      if (cp_warn_deprecated_use (scope))
+	return;
+      if (TYPE_P (scope))
+	scope = CP_TYPE_CONTEXT (scope);
+      else
+	scope = CP_DECL_CONTEXT (scope);
+    }
+}
+
 /* Mark DECL (either a _DECL or a BASELINK) as "used" in the program.
    If DECL is a specialization or implicitly declared class member,
    generate the actual definition.  Return false if something goes
diff --git gcc/cp/name-lookup.c gcc/cp/name-lookup.c
index 5f5ff81f405..a8ab4db4d0d 100644
--- gcc/cp/name-lookup.c
+++ gcc/cp/name-lookup.c
@@ -4905,6 +4905,24 @@ handle_namespace_attrs (tree ns, tree attributes)
 	    DECL_ATTRIBUTES (ns) = tree_cons (name, args,
 					      DECL_ATTRIBUTES (ns));
 	}
+      else if (is_attribute_p ("deprecated", name))
+	{
+	  if (!DECL_NAME (ns))
+	    {
+	      warning (OPT_Wattributes, "ignoring %qD attribute on anonymous "
+		       "namespace", name);
+	      continue;
+	    }
+	  if (args && TREE_CODE (TREE_VALUE (args)) != STRING_CST)
+	    {
+	      error ("deprecated message is not a string");
+	      continue;
+	    }
+	  TREE_DEPRECATED (ns) = 1;
+	  if (args)
+	    DECL_ATTRIBUTES (ns) = tree_cons (name, args,
+					      DECL_ATTRIBUTES (ns));
+	}
       else
 	{
 	  warning (OPT_Wattributes, "%qD attribute directive ignored",
diff --git gcc/cp/parser.c gcc/cp/parser.c
index 504f77a4908..53514787554 100644
--- gcc/cp/parser.c
+++ gcc/cp/parser.c
@@ -19378,6 +19378,7 @@ cp_parser_namespace_alias_definition (cp_parser* parser)
   /* Look for the qualified-namespace-specifier.  */
   namespace_specifier
     = cp_parser_qualified_namespace_specifier (parser);
+  cp_warn_deprecated_use_scopes (namespace_specifier);
   /* Look for the `;' token.  */
   cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON);
 
@@ -19492,6 +19493,8 @@ cp_parser_using_declaration (cp_parser* parser,
 	   && !TYPE_FUNCTION_SCOPE_P (qscope))
     qscope = CP_TYPE_CONTEXT (qscope);
 
+  cp_warn_deprecated_use_scopes (qscope);
+
   if (access_declaration_p && cp_parser_error_occurred (parser))
     /* Something has already gone wrong; there's no need to parse
        further.  Since an error has occurred, the return value of
@@ -19752,6 +19755,7 @@ cp_parser_using_directive (cp_parser* parser)
 				       /*is_declaration=*/true);
   /* Get the namespace being used.  */
   namespace_decl = cp_parser_namespace_name (parser);
+  cp_warn_deprecated_use_scopes (namespace_decl);
   /* And any specified attributes.  */
   attribs = cp_parser_attributes_opt (parser);
 
diff --git gcc/cp/semantics.c gcc/cp/semantics.c
index 7ac1ba058e5..8aec4eff9b3 100644
--- gcc/cp/semantics.c
+++ gcc/cp/semantics.c
@@ -3811,6 +3811,8 @@ finish_id_expression_1 (tree id_expression,
 	  if (TREE_CODE (decl) == FUNCTION_DECL)
 	    mark_used (decl);
 
+	  cp_warn_deprecated_use_scopes (scope);
+
 	  if (TYPE_P (scope))
 	    decl = finish_qualified_id_expr (scope,
 					     decl,
diff --git gcc/testsuite/g++.dg/cpp0x/attributes-namespace1.C gcc/testsuite/g++.dg/cpp0x/attributes-namespace1.C
new file mode 100644
index 00000000000..d49ebb0b721
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/attributes-namespace1.C
@@ -0,0 +1,50 @@
+// PR c++/79817 - attribute deprecated on namespace.
+// { dg-do compile { target c++11 } }
+
+namespace [[deprecated]] ns1 { int i; }
+namespace [[deprecated("foo")]] ns2 { int i; }
+namespace __attribute__((deprecated)) ns3 { int i; }
+namespace __attribute__((deprecated("foo"))) ns4 { int i; }
+
+namespace [[deprecated]] ns6
+{
+  enum E { X };
+  void fn();
+}
+
+namespace [[deprecated]] ns7
+{
+  namespace ns8 {
+    int x;
+    struct Z { };
+  }
+  struct S { };
+}
+
+namespace N1
+{
+  namespace N2
+  {
+    namespace [[deprecated]] N3
+    {
+      namespace N4 { int x; }
+    }
+  }
+}
+
+void
+f ()
+{
+  ns1::i = 0; // { dg-warning ".ns1. is deprecated" }
+  ns2::i = 0; // { dg-warning ".ns2. is deprecated: foo" }
+  ns3::i = 0; // { dg-warning ".ns3. is deprecated" }
+  ns4::i = 0; // { dg-warning ".ns4. is deprecated" }
+  int i = ns1::i; // { dg-warning ".ns1. is deprecated" }
+  int k = ns6::E::X;  // { dg-warning ".ns6. is deprecated" }
+  ns7::ns8::x = 42; // { dg-warning ".ns7. is deprecated" }
+  N1::N2::N3::N4::x = 42; // { dg-warning ".N1::N2::N3. is deprecated" }
+  ns6::fn(); // { dg-warning ".ns6. is deprecated" }
+  ns7::S s; // { dg-warning ".ns7. is deprecated" }
+  ns7::S sfn(int); // { dg-warning ".ns7. is deprecated" }
+  ns7::ns8::Z sfn2(int); // { dg-warning ".ns7. is deprecated" }
+}
diff --git gcc/testsuite/g++.dg/cpp0x/attributes-namespace2.C gcc/testsuite/g++.dg/cpp0x/attributes-namespace2.C
new file mode 100644
index 00000000000..08a043a24e3
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/attributes-namespace2.C
@@ -0,0 +1,27 @@
+// PR c++/79817 - attribute deprecated on namespace.
+// { dg-do compile { target c++11 } }
+
+namespace [[deprecated]] { // { dg-warning "ignoring .deprecated. attribute on anonymous namespace" }
+  int nn;
+}
+
+inline namespace [[deprecated]] I { 
+  int x;
+}
+
+namespace M {
+  int y;
+  inline namespace [[deprecated]] N {
+    int x;
+  }
+}
+
+void
+g ()
+{
+  nn = 42;
+  I::x = 42; // { dg-warning ".I. is deprecated" }
+  M::x = 42;
+  M::y = 42;
+  M::N::x = 42; // { dg-warning ".M::N. is deprecated" }
+}
diff --git gcc/testsuite/g++.dg/cpp0x/attributes-namespace3.C gcc/testsuite/g++.dg/cpp0x/attributes-namespace3.C
new file mode 100644
index 00000000000..81355ab5677
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/attributes-namespace3.C
@@ -0,0 +1,33 @@
+// PR c++/79817 - attribute deprecated on namespace.
+// { dg-do compile { target c++11 } }
+
+namespace [[deprecated]] N
+{
+  typedef decltype(sizeof(int)) T;
+  int x;
+
+  namespace N2 {
+    typedef decltype(sizeof(int)) T;
+    int y;
+  }
+}
+
+namespace M {
+  namespace [[deprecated]] M2 {
+    typedef decltype(sizeof(int)) T;
+    int z;
+  }
+}
+
+void
+fn2 ()
+{
+  using N::x; // { dg-warning ".N. is deprecated" }
+  N::T j; // { dg-warning ".N. is deprecated" }
+
+  using M::M2::z; // { dg-warning ".M::M2. is deprecated" }
+  M::M2::T l; // { dg-warning ".M::M2. is deprecated" }
+
+  using N::N2::y; // { dg-warning ".N. is deprecated" }
+  N::N2::T k; // { dg-warning ".N. is deprecated" }
+}
diff --git gcc/testsuite/g++.dg/cpp0x/attributes-namespace4.C gcc/testsuite/g++.dg/cpp0x/attributes-namespace4.C
new file mode 100644
index 00000000000..de0c6df8d9d
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/attributes-namespace4.C
@@ -0,0 +1,45 @@
+// PR c++/79817 - attribute deprecated on namespace.
+// { dg-do compile { target c++11 } }
+
+namespace [[deprecated]] N {
+  struct S { };
+  using T = int;
+  const int value = 42;
+  int arr[10];
+}
+
+namespace [[deprecated]] Y {
+  int x;
+  int i = x;
+}
+
+namespace [[deprecated]] M {
+  namespace M2 {
+  }
+}
+
+enum E { F =  N::value }; // { dg-warning ".N. is deprecated" }
+
+template<N::T> // { dg-warning ".N. is deprecated" }
+struct X { };
+
+N::T foo(); // { dg-warning ".N. is deprecated" }
+
+void
+g(N::T p) // { dg-warning ".N. is deprecated" }
+{
+  N::S s; // { dg-warning ".N. is deprecated" }
+  N::arr[0] = 42; // { dg-warning ".N. is deprecated" }
+}
+
+namespace Z = Y; // { dg-warning ".Y. is deprecated" }
+namespace Z2 = M::M2; // { dg-warning ".M. is deprecated" }
+
+void
+g2 ()
+{
+  using namespace Y; // { dg-warning ".Y. is deprecated" }
+  using namespace M::M2; // { dg-warning ".M. is deprecated" }
+  using TT = N::T; // { dg-warning ".N. is deprecated" }
+  using N::T; // { dg-warning ".N. is deprecated" }
+}
diff --git gcc/testsuite/g++.dg/cpp0x/attributes-namespace5.C gcc/testsuite/g++.dg/cpp0x/attributes-namespace5.C
new file mode 100644
index 00000000000..6dbcf326061
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/attributes-namespace5.C
@@ -0,0 +1,20 @@
+// PR c++/79817 - attribute deprecated on namespace.
+// { dg-do compile { target c++11 } }
+
+namespace [[deprecated]] Y {
+  void f();
+  void f2(int);
+
+  template<typename>
+  struct S {
+    void f3 ();
+  };
+}
+
+void Y::f();
+void Y::f() { }
+void Y::f2(int);
+void Y::f2([[maybe_unused]] int);
+void Y::f2(int) { }
+template <> void Y::S<int>::f3();
+template <> void Y::S<int>::f3() { }
diff --git gcc/testsuite/g++.dg/cpp1z/namespace-attribs.C gcc/testsuite/g++.dg/cpp1z/namespace-attribs.C
index dd1855137de..29f8ce472be 100644
--- gcc/testsuite/g++.dg/cpp1z/namespace-attribs.C
+++ gcc/testsuite/g++.dg/cpp1z/namespace-attribs.C
@@ -3,9 +3,8 @@
 
 namespace A __attribute ((visibility ("default"))) {}
 
-namespace B [[deprecated]] {} // { dg-warning "ignored" }
+namespace B [[deprecated]] {}
 
 namespace __attribute ((visibility ("default"))) C {}
 
-namespace [[deprecated]] D {} // { dg-warning "ignored" }
-
+namespace [[deprecated]] D {}
diff --git gcc/testsuite/g++.dg/cpp1z/namespace-attribs2.C gcc/testsuite/g++.dg/cpp1z/namespace-attribs2.C
index 193dbf6e017..7996b4b680c 100644
--- gcc/testsuite/g++.dg/cpp1z/namespace-attribs2.C
+++ gcc/testsuite/g++.dg/cpp1z/namespace-attribs2.C
@@ -1,7 +1,6 @@
 // { dg-do compile { target c++17 } }
 // { dg-additional-options "-pedantic" }
 
-namespace B [[deprecated]] {} // { dg-warning "ignored|must precede" }
-
-namespace [[deprecated]] D {} // { dg-warning "ignored" }
+namespace B [[deprecated]] {} // { dg-error "must precede" }
 
+namespace [[deprecated]] D {}
Jason Merrill Aug. 23, 2019, 9:27 p.m. UTC | #4
On 8/23/19 11:55 AM, Marek Polacek wrote:
> On Thu, Aug 22, 2019 at 11:49:49AM -0700, Jason Merrill wrote:
>> On Thu, Aug 22, 2019 at 11:01 AM Nathan Sidwell <nathan@acm.org> wrote:
>>>
>>> On 8/20/19 9:03 PM, Marek Polacek wrote:
>>>
>>>> and in cp_parser_nested_name_specifier_opt we simply don't know if we're
>>>> dealing with a function decl.  Calling cp_warn_deprecated_use_scopes from
>>>> cp_parser_type_specifier resulted int duplicated diagnostics so that one
>>>> is out too.  So I did the following which doesn't seem too bad.
>>>
>>>>
>>>> diff --git gcc/cp/decl.c gcc/cp/decl.c
>>>> index 08b7baa40e0..46ad0271f7b 100644
>>>> --- gcc/cp/decl.c
>>>> +++ gcc/cp/decl.c
>>>> @@ -10791,6 +10791,7 @@ grokdeclarator (const cp_declarator *declarator,
>>>>      cp_warn_deprecated_use (type);
>>>>      if (type && TREE_CODE (type) == TYPE_DECL)
>>>>        {
>>>> +      cp_warn_deprecated_use_scopes (DECL_CONTEXT (type));
>>>
>>> CP_DECL_CONTEXT would be clearer, here and elsewhere.
> 
> Oh right, I failed to adjust that.  Fixed now.
> 
>>>>      /* Do warn about using typedefs to a deprecated class.  */
>>>> diff --git gcc/cp/decl2.c gcc/cp/decl2.c
>>>> index a32108f9d16..d6f407d7aef 100644
>>>> --- gcc/cp/decl2.c
>>>> +++ gcc/cp/decl2.c
>>>> @@ -5407,6 +5407,23 @@ cp_warn_deprecated_use (tree decl, tsubst_flags_t complain)
>>>>      return warned;
>>>>    }
>>>>
>>>> +/* Like above, but takes into account outer scopes.  */
>>>> +
>>>> +void
>>>> +cp_warn_deprecated_use_scopes (tree ns)
>>>
>>> Do we need to walk non-namespace scopes here?  can we just bail if NS is
>>> not a namespace?  if can legitimately not be a namespace, calling it NS
>>> is confusing :)
>>
>> It seems like it can be a class in some cases, and I would want to
>> warn about deprecated classes named in a nested-name-specifier.  Did
>> we not already warn about that?
> 
> I know of at least this case:
> 
> namespace  N {
>    enum [[deprecated]] E { X };
> }
> 
> int i = N::E::X;
> 
> we didn't warn about the deprecated 'E' but with this patch we do.
> 
>> I agree that the parameter name is confusing.
> 
> True, I didn't realize.  Fixed now.
> 
>>>> +{
>>>> +  while (ns
>>>> +      && ns != error_mark_node
>>>> +      && ns != global_namespace)
>>>> +    {
>>>> +      cp_warn_deprecated_use (ns);
>>>> +      if (TYPE_P (ns))
>>> ... and does this ever trigger?
> 
> Yeah, it can, see the testcase above.
> 
>>>> +     ns = CP_TYPE_CONTEXT (ns);
>>>> +      else
>>>> +     ns = CP_DECL_CONTEXT (ns);
>>>> +    }
>>>> +}
>>>
>>> I always worry about such recursive lookups.  NAMESPACE_DECL has so many
>>> spare flags, could we take one to say 'is, or contained in, deprecated',
>>> and thus know whether we can bail early.  And stop at the first
>>> deprecated one -- though not sure why someone would deprecate more than
>>> one namespace in a nest.  thoughts?
> 
> Well, this patch uses the TREE_DEPRECATED bit for a NAMESPACE_DECL.  I'm not
> exactly sure how another bit would help us, where I would set it (I guess the
> innermost scope, but that doesn't have to be a NAMESPACE_DECL), and how that
> would play with diagnostics -- i.e. if we'd print the correct name.
> 
>> I can imagine deprecating an inner namespace and later deprecating an
>> outer namespace, but I don't think it's important to warn about more
>> than one in that case.
> 
> Agreed, and to that effect I tweaked cp_warn_deprecated_use_scopes to return
> if cp_warn_deprecated_use issued a warning.
> 
> Thank you both!
> 
> Bootstrapped/regtested on x86_64-linux, ok for trunk?

OK on Monday if Nathan doesn't have more comments before then.

Jason
Nathan Sidwell Aug. 23, 2019, 9:29 p.m. UTC | #5
On 8/23/19 5:27 PM, Jason Merrill wrote:
> On 8/23/19 11:55 AM, Marek Polacek wrote:

>> Bootstrapped/regtested on x86_64-linux, ok for trunk?
> 
> OK on Monday if Nathan doesn't have more comments before then.

ok  by me, thanks

nathan

Patch
diff mbox series

diff --git gcc/cp/cp-tree.h gcc/cp/cp-tree.h
index 05f91861b42..42f180d1dd3 100644
--- gcc/cp/cp-tree.h
+++ gcc/cp/cp-tree.h
@@ -6264,6 +6264,7 @@  extern bool is_list_ctor			(tree);
 extern void validate_conversion_obstack		(void);
 extern void mark_versions_used			(tree);
 extern bool cp_warn_deprecated_use		(tree, tsubst_flags_t = tf_warning_or_error);
+extern void cp_warn_deprecated_use_scopes	(tree);
 extern tree get_function_version_dispatcher	(tree);
 
 /* in class.c */
diff --git gcc/cp/decl.c gcc/cp/decl.c
index 08b7baa40e0..46ad0271f7b 100644
--- gcc/cp/decl.c
+++ gcc/cp/decl.c
@@ -10791,6 +10791,7 @@  grokdeclarator (const cp_declarator *declarator,
   cp_warn_deprecated_use (type);
   if (type && TREE_CODE (type) == TYPE_DECL)
     {
+      cp_warn_deprecated_use_scopes (DECL_CONTEXT (type));
       typedef_decl = type;
       type = TREE_TYPE (typedef_decl);
       if (DECL_ARTIFICIAL (typedef_decl))
@@ -13230,7 +13231,10 @@  type_is_deprecated (tree type)
       if (TREE_DEPRECATED (TYPE_NAME (type)))
 	return type;
       else
-	return NULL_TREE;
+	{
+	  cp_warn_deprecated_use_scopes (DECL_CONTEXT (TYPE_NAME (type)));
+	  return NULL_TREE;
+	}
     }
 
   /* Do warn about using typedefs to a deprecated class.  */
diff --git gcc/cp/decl2.c gcc/cp/decl2.c
index a32108f9d16..d6f407d7aef 100644
--- gcc/cp/decl2.c
+++ gcc/cp/decl2.c
@@ -5407,6 +5407,23 @@  cp_warn_deprecated_use (tree decl, tsubst_flags_t complain)
   return warned;
 }
 
+/* Like above, but takes into account outer scopes.  */
+
+void
+cp_warn_deprecated_use_scopes (tree ns)
+{
+  while (ns
+	 && ns != error_mark_node
+	 && ns != global_namespace)
+    {
+      cp_warn_deprecated_use (ns);
+      if (TYPE_P (ns))
+	ns = CP_TYPE_CONTEXT (ns);
+      else
+	ns = CP_DECL_CONTEXT (ns);
+    }
+}
+
 /* Mark DECL (either a _DECL or a BASELINK) as "used" in the program.
    If DECL is a specialization or implicitly declared class member,
    generate the actual definition.  Return false if something goes
diff --git gcc/cp/name-lookup.c gcc/cp/name-lookup.c
index 5f5ff81f405..a8ab4db4d0d 100644
--- gcc/cp/name-lookup.c
+++ gcc/cp/name-lookup.c
@@ -4905,6 +4905,24 @@  handle_namespace_attrs (tree ns, tree attributes)
 	    DECL_ATTRIBUTES (ns) = tree_cons (name, args,
 					      DECL_ATTRIBUTES (ns));
 	}
+      else if (is_attribute_p ("deprecated", name))
+	{
+	  if (!DECL_NAME (ns))
+	    {
+	      warning (OPT_Wattributes, "ignoring %qD attribute on anonymous "
+		       "namespace", name);
+	      continue;
+	    }
+	  if (args && TREE_CODE (TREE_VALUE (args)) != STRING_CST)
+	    {
+	      error ("deprecated message is not a string");
+	      continue;
+	    }
+	  TREE_DEPRECATED (ns) = 1;
+	  if (args)
+	    DECL_ATTRIBUTES (ns) = tree_cons (name, args,
+					      DECL_ATTRIBUTES (ns));
+	}
       else
 	{
 	  warning (OPT_Wattributes, "%qD attribute directive ignored",
diff --git gcc/cp/parser.c gcc/cp/parser.c
index dbbfe1dbc2f..82d4949b30b 100644
--- gcc/cp/parser.c
+++ gcc/cp/parser.c
@@ -19374,6 +19374,7 @@  cp_parser_namespace_alias_definition (cp_parser* parser)
   /* Look for the qualified-namespace-specifier.  */
   namespace_specifier
     = cp_parser_qualified_namespace_specifier (parser);
+  cp_warn_deprecated_use_scopes (namespace_specifier);
   /* Look for the `;' token.  */
   cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON);
 
@@ -19488,6 +19489,8 @@  cp_parser_using_declaration (cp_parser* parser,
 	   && !TYPE_FUNCTION_SCOPE_P (qscope))
     qscope = CP_TYPE_CONTEXT (qscope);
 
+  cp_warn_deprecated_use_scopes (qscope);
+
   if (access_declaration_p && cp_parser_error_occurred (parser))
     /* Something has already gone wrong; there's no need to parse
        further.  Since an error has occurred, the return value of
@@ -19748,6 +19751,7 @@  cp_parser_using_directive (cp_parser* parser)
 				       /*is_declaration=*/true);
   /* Get the namespace being used.  */
   namespace_decl = cp_parser_namespace_name (parser);
+  cp_warn_deprecated_use_scopes (namespace_decl);
   /* And any specified attributes.  */
   attribs = cp_parser_attributes_opt (parser);
 
diff --git gcc/cp/semantics.c gcc/cp/semantics.c
index 7ac1ba058e5..8aec4eff9b3 100644
--- gcc/cp/semantics.c
+++ gcc/cp/semantics.c
@@ -3811,6 +3811,8 @@  finish_id_expression_1 (tree id_expression,
 	  if (TREE_CODE (decl) == FUNCTION_DECL)
 	    mark_used (decl);
 
+	  cp_warn_deprecated_use_scopes (scope);
+
 	  if (TYPE_P (scope))
 	    decl = finish_qualified_id_expr (scope,
 					     decl,
diff --git gcc/testsuite/g++.dg/cpp0x/attributes-namespace1.C gcc/testsuite/g++.dg/cpp0x/attributes-namespace1.C
new file mode 100644
index 00000000000..d49ebb0b721
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/attributes-namespace1.C
@@ -0,0 +1,50 @@ 
+// PR c++/79817 - attribute deprecated on namespace.
+// { dg-do compile { target c++11 } }
+
+namespace [[deprecated]] ns1 { int i; }
+namespace [[deprecated("foo")]] ns2 { int i; }
+namespace __attribute__((deprecated)) ns3 { int i; }
+namespace __attribute__((deprecated("foo"))) ns4 { int i; }
+
+namespace [[deprecated]] ns6
+{
+  enum E { X };
+  void fn();
+}
+
+namespace [[deprecated]] ns7
+{
+  namespace ns8 {
+    int x;
+    struct Z { };
+  }
+  struct S { };
+}
+
+namespace N1
+{
+  namespace N2
+  {
+    namespace [[deprecated]] N3
+    {
+      namespace N4 { int x; }
+    }
+  }
+}
+
+void
+f ()
+{
+  ns1::i = 0; // { dg-warning ".ns1. is deprecated" }
+  ns2::i = 0; // { dg-warning ".ns2. is deprecated: foo" }
+  ns3::i = 0; // { dg-warning ".ns3. is deprecated" }
+  ns4::i = 0; // { dg-warning ".ns4. is deprecated" }
+  int i = ns1::i; // { dg-warning ".ns1. is deprecated" }
+  int k = ns6::E::X;  // { dg-warning ".ns6. is deprecated" }
+  ns7::ns8::x = 42; // { dg-warning ".ns7. is deprecated" }
+  N1::N2::N3::N4::x = 42; // { dg-warning ".N1::N2::N3. is deprecated" }
+  ns6::fn(); // { dg-warning ".ns6. is deprecated" }
+  ns7::S s; // { dg-warning ".ns7. is deprecated" }
+  ns7::S sfn(int); // { dg-warning ".ns7. is deprecated" }
+  ns7::ns8::Z sfn2(int); // { dg-warning ".ns7. is deprecated" }
+}
diff --git gcc/testsuite/g++.dg/cpp0x/attributes-namespace2.C gcc/testsuite/g++.dg/cpp0x/attributes-namespace2.C
new file mode 100644
index 00000000000..08a043a24e3
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/attributes-namespace2.C
@@ -0,0 +1,27 @@ 
+// PR c++/79817 - attribute deprecated on namespace.
+// { dg-do compile { target c++11 } }
+
+namespace [[deprecated]] { // { dg-warning "ignoring .deprecated. attribute on anonymous namespace" }
+  int nn;
+}
+
+inline namespace [[deprecated]] I { 
+  int x;
+}
+
+namespace M {
+  int y;
+  inline namespace [[deprecated]] N {
+    int x;
+  }
+}
+
+void
+g ()
+{
+  nn = 42;
+  I::x = 42; // { dg-warning ".I. is deprecated" }
+  M::x = 42;
+  M::y = 42;
+  M::N::x = 42; // { dg-warning ".M::N. is deprecated" }
+}
diff --git gcc/testsuite/g++.dg/cpp0x/attributes-namespace3.C gcc/testsuite/g++.dg/cpp0x/attributes-namespace3.C
new file mode 100644
index 00000000000..81355ab5677
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/attributes-namespace3.C
@@ -0,0 +1,33 @@ 
+// PR c++/79817 - attribute deprecated on namespace.
+// { dg-do compile { target c++11 } }
+
+namespace [[deprecated]] N
+{
+  typedef decltype(sizeof(int)) T;
+  int x;
+
+  namespace N2 {
+    typedef decltype(sizeof(int)) T;
+    int y;
+  }
+}
+
+namespace M {
+  namespace [[deprecated]] M2 {
+    typedef decltype(sizeof(int)) T;
+    int z;
+  }
+}
+
+void
+fn2 ()
+{
+  using N::x; // { dg-warning ".N. is deprecated" }
+  N::T j; // { dg-warning ".N. is deprecated" }
+
+  using M::M2::z; // { dg-warning ".M::M2. is deprecated" }
+  M::M2::T l; // { dg-warning ".M::M2. is deprecated" }
+
+  using N::N2::y; // { dg-warning ".N. is deprecated" }
+  N::N2::T k; // { dg-warning ".N. is deprecated" }
+}
diff --git gcc/testsuite/g++.dg/cpp0x/attributes-namespace4.C gcc/testsuite/g++.dg/cpp0x/attributes-namespace4.C
new file mode 100644
index 00000000000..de0c6df8d9d
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/attributes-namespace4.C
@@ -0,0 +1,45 @@ 
+// PR c++/79817 - attribute deprecated on namespace.
+// { dg-do compile { target c++11 } }
+
+namespace [[deprecated]] N {
+  struct S { };
+  using T = int;
+  const int value = 42;
+  int arr[10];
+}
+
+namespace [[deprecated]] Y {
+  int x;
+  int i = x;
+}
+
+namespace [[deprecated]] M {
+  namespace M2 {
+  }
+}
+
+enum E { F =  N::value }; // { dg-warning ".N. is deprecated" }
+
+template<N::T> // { dg-warning ".N. is deprecated" }
+struct X { };
+
+N::T foo(); // { dg-warning ".N. is deprecated" }
+
+void
+g(N::T p) // { dg-warning ".N. is deprecated" }
+{
+  N::S s; // { dg-warning ".N. is deprecated" }
+  N::arr[0] = 42; // { dg-warning ".N. is deprecated" }
+}
+
+namespace Z = Y; // { dg-warning ".Y. is deprecated" }
+namespace Z2 = M::M2; // { dg-warning ".M. is deprecated" }
+
+void
+g2 ()
+{
+  using namespace Y; // { dg-warning ".Y. is deprecated" }
+  using namespace M::M2; // { dg-warning ".M. is deprecated" }
+  using TT = N::T; // { dg-warning ".N. is deprecated" }
+  using N::T; // { dg-warning ".N. is deprecated" }
+}
diff --git gcc/testsuite/g++.dg/cpp0x/attributes-namespace5.C gcc/testsuite/g++.dg/cpp0x/attributes-namespace5.C
new file mode 100644
index 00000000000..6dbcf326061
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/attributes-namespace5.C
@@ -0,0 +1,20 @@ 
+// PR c++/79817 - attribute deprecated on namespace.
+// { dg-do compile { target c++11 } }
+
+namespace [[deprecated]] Y {
+  void f();
+  void f2(int);
+
+  template<typename>
+  struct S {
+    void f3 ();
+  };
+}
+
+void Y::f();
+void Y::f() { }
+void Y::f2(int);
+void Y::f2([[maybe_unused]] int);
+void Y::f2(int) { }
+template <> void Y::S<int>::f3();
+template <> void Y::S<int>::f3() { }
diff --git gcc/testsuite/g++.dg/cpp1z/namespace-attribs.C gcc/testsuite/g++.dg/cpp1z/namespace-attribs.C
index dd1855137de..29f8ce472be 100644
--- gcc/testsuite/g++.dg/cpp1z/namespace-attribs.C
+++ gcc/testsuite/g++.dg/cpp1z/namespace-attribs.C
@@ -3,9 +3,8 @@ 
 
 namespace A __attribute ((visibility ("default"))) {}
 
-namespace B [[deprecated]] {} // { dg-warning "ignored" }
+namespace B [[deprecated]] {}
 
 namespace __attribute ((visibility ("default"))) C {}
 
-namespace [[deprecated]] D {} // { dg-warning "ignored" }
-
+namespace [[deprecated]] D {}
diff --git gcc/testsuite/g++.dg/cpp1z/namespace-attribs2.C gcc/testsuite/g++.dg/cpp1z/namespace-attribs2.C
index 193dbf6e017..7996b4b680c 100644
--- gcc/testsuite/g++.dg/cpp1z/namespace-attribs2.C
+++ gcc/testsuite/g++.dg/cpp1z/namespace-attribs2.C
@@ -1,7 +1,6 @@ 
 // { dg-do compile { target c++17 } }
 // { dg-additional-options "-pedantic" }
 
-namespace B [[deprecated]] {} // { dg-warning "ignored|must precede" }
-
-namespace [[deprecated]] D {} // { dg-warning "ignored" }
+namespace B [[deprecated]] {} // { dg-error "must precede" }
 
+namespace [[deprecated]] D {}