PR 93804 points out that issuing -Wredundant-tags for declarations
in C headers included in C++ isn't helpful because the tags cannot
be removed without breaking C programs that depend on the headers.

Attached is a patch that avoids the warning in these cases tested
on x86_64-linux.  While strictly not a regression (and even though
I initially considered it a GCC 11 enhancement), since most C++
code includes some C headers, without the patch the warning would
likely cause too much noise to be widely useful.

Martin
PR c++/93804 - exempt extern C headers from -Wredundant-tags

gcc/cp/ChangeLog:

	PR c++/93804
	* parser.c (cp_parser_check_class_key): Avoid issuing -Wredundant-tags
	in shared C/C++ code in headers.

gcc/testsuite/ChangeLog:

	PR c++/93804
	* g++.dg/warn/Wredundant-tags-4.C: New test.
	* g++.dg/warn/Wredundant-tags-5.C: New test.
	* g++.dg/warn/Wredundant-tags-5.h: New test.

diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index e8a536ae22f..2c6f5522bf3 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -30999,15 +30999,32 @@ cp_parser_check_class_key (cp_parser *parser, location_t key_loc,
      neither definitions of it nor declarations, and for which name
      lookup returns just the type itself.  */
   bool key_redundant = !def_p && !decl_p && decl == type_decl;
+
+  if (key_redundant
+      && class_key != class_type
+      && current_lang_name != lang_name_cplusplus
+      && current_namespace == global_namespace)
+    {
+      /* Avoid issuing the diagnostic for apparently redundant struct
+	 and union class-keys in shared C/C++ code in files (such as
+	 headers) included in the main source file.  */
+      const line_map_ordinary *map = NULL;
+      linemap_resolve_location (line_table, key_loc,
+				LRK_MACRO_DEFINITION_LOCATION,
+				&map);
+      if (!MAIN_FILE_P (map))
+	key_redundant = false;
+    }
+
   if (key_redundant)
     {
       gcc_rich_location richloc (key_loc);
       richloc.add_fixit_remove (key_loc);
       warning_at (&richloc, OPT_Wredundant_tags,
-		"redundant class-key %qs in reference to %q#T",
-		class_key == union_type ? "union"
-		: class_key == record_type ? "struct" : "class",
-		type);
+		  "redundant class-key %qs in reference to %q#T",
+		  class_key == union_type ? "union"
+		  : class_key == record_type ? "struct" : "class",
+		  type);
     }
 
   if (seen_as_union || !warn_mismatched_tags)
diff --git a/gcc/testsuite/g++.dg/warn/Wredundant-tags-4.C b/gcc/testsuite/g++.dg/warn/Wredundant-tags-4.C
new file mode 100644
index 00000000000..1a5833e6994
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wredundant-tags-4.C
@@ -0,0 +1,92 @@
+/* PR c++/93804 - exempt extern "C" headers from -Wredundant-tags
+   Verify that -Wredundant-tags is not issued for redundant class-key
+   in extern "C" references in a header file.
+   { dg-do compile }
+   { dg-options "-Wredundant-tags" }  */
+
+# 1 "Wredundant-tags-4.C"
+# 1 "Wredundant-tags-4.h" 1
+extern "C" {
+
+class C1 { };
+struct S1 { };
+union U1 { };
+
+# line 16
+  /* The warning should be issued for the class-key class even in
+     an extern "C" block.  */
+  void f0 (class C1);     // { dg-warning "\\\[-Wredundant-tags" }
+  void f1 (struct S1);    // { dg-bogus "\\\[-Wredundant-tags" }
+  void f2 (union U1);     // { dg-bogus "\\\[-Wredundant-tags" }
+
+  inline int
+  finline1 (class C1)     // { dg-warning "\\\[-Wredundant-tags" }
+  { return sizeof (class C1); }   // { dg-warning "\\\[-Wredundant-tags" }
+
+  inline int
+  finline2 (struct S1)    // { dg-bogus "\\\[-Wredundant-tags" }
+  { return sizeof (struct S1); }
+
+  inline int
+  finline3 (union U1)     // { dg-bogus "\\\[-Wredundant-tags" }
+  { return sizeof (union U1); }
+
+  extern class C1 c1;     // { dg-warning "\\\[-Wredundant-tags" }
+  extern struct S1 s1;    // { dg-bogus "\\\[-Wredundant-tags" }
+  extern union U1 u1;     // { dg-bogus "\\\[-Wredundant-tags" }
+
+  namespace N1 {
+  /* Verify that -Wredundant-tags is issued in a namespace enclosed
+     in an extern "C" block.  (Such code cannot be shared with C.)  */
+  extern class C1 n1c1;   // { dg-warning "\\\[-Wredundant-tags" }
+  extern struct S1 n1s1;  // { dg-warning "\\\[-Wredundant-tags" }
+  extern union U1 n1u1;   // { dg-warning "\\\[-Wredundant-tags" }
+  }
+}
+
+
+extern "C++" {
+
+class C2 { };
+struct S2 { };
+union U2 { };
+
+  void f3 (class C2);     // { dg-warning "\\\[-Wredundant-tags" }
+  void f4 (struct S2);    // { dg-warning "\\\[-Wredundant-tags" }
+  void f5 (union U2);     // { dg-warning "\\\[-Wredundant-tags" }
+
+  extern class C2 c2;     // { dg-warning "\\\[-Wredundant-tags" }
+  extern struct S2 s2;    // { dg-warning "\\\[-Wredundant-tags" }
+  extern union U2 u2;     // { dg-warning "\\\[-Wredundant-tags" }
+}
+
+
+namespace N {
+
+class C3 { };
+struct S3 { };
+union U3 { };
+
+void f6 (class C3);       // { dg-warning "\\\[-Wredundant-tags" }
+void f7 (struct S3);      // { dg-warning "\\\[-Wredundant-tags" }
+void f8 (union U3);       // { dg-warning "\\\[-Wredundant-tags" }
+
+extern class C3 c3;       // { dg-warning "\\\[-Wredundant-tags" }
+extern struct S3 s3;      // { dg-warning "\\\[-Wredundant-tags" }
+extern union U3 u3;       // { dg-warning "\\\[-Wredundant-tags" }
+
+extern "C" {
+
+  /* Verify that -Wredundant-tags is issued in an extern "C" block
+     enclosed within a namespace.  (Such code cannot be shared with
+     C.)  */
+  class C4 { };
+  struct S4 { };
+  union U4 { };
+
+  extern class C4 c4;     // { dg-warning "\\\[-Wredundant-tags" }
+  extern struct S4 s4;    // { dg-warning "\\\[-Wredundant-tags" }
+  extern union U4 u4;     // { dg-warning "\\\[-Wredundant-tags" }
+}
+
+}   // namespace N
diff --git a/gcc/testsuite/g++.dg/warn/Wredundant-tags-5.C b/gcc/testsuite/g++.dg/warn/Wredundant-tags-5.C
new file mode 100644
index 00000000000..9bb5cc4e1b5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wredundant-tags-5.C
@@ -0,0 +1,49 @@
+// PR c++/93804 - exempt extern "C" headers from -Wredundant-tags
+// Verify that -Wredundant-tags is issued even for redundant class-key
+// in references in the main source file to extern "C" classes defined
+// in headers.
+// { dg-do compile }
+// { dg-options "-Wredundant-tags" }
+
+#include "Wredundant-tags-5.h"
+
+extern "C" {
+
+  void f0 (class C1)      // { dg-warning "\\\[-Wredundant-tags" }
+  {
+  }
+
+  void f1 (struct S1)     // { dg-warning "\\\[-Wredundant-tags" }
+  {
+  }
+
+  void f2 (union U1)      // { dg-warning "\\\[-Wredundant-tags" }
+  {
+  }
+
+}
+
+void f3 (class C2)        // { dg-warning "\\\[-Wredundant-tags" }
+{
+}
+
+void f4 (struct S2)       // { dg-warning "\\\[-Wredundant-tags" }
+{
+}
+
+void f5 (union U2)        // { dg-warning "\\\[-Wredundant-tags" }
+{
+}
+
+
+void f6 (class C3)        // { dg-warning "\\\[-Wredundant-tags" }
+{
+}
+
+void f7 (struct S3)       // { dg-warning "\\\[-Wredundant-tags" }
+{
+}
+
+void f8 (union U3)        // { dg-warning "\\\[-Wredundant-tags" }
+{
+}
diff --git a/gcc/testsuite/g++.dg/warn/Wredundant-tags-5.h b/gcc/testsuite/g++.dg/warn/Wredundant-tags-5.h
new file mode 100644
index 00000000000..c9c2ec278c8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wredundant-tags-5.h
@@ -0,0 +1,58 @@
+#ifndef WREDUNDANT_TAGS_H
+#define WREDUNDANT_TAGS_H
+
+extern "C" {
+
+class C1 { };
+struct S1 { };
+union U1 { };
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wredundant-tags"
+  void f0 (class C1);     // -Wredundant-tags
+#pragma GCC diagnostic pop
+
+  void f1 (struct S1);
+  void f2 (union U1);
+
+  void f0 (C1);
+  void f1 (S1);
+  void f2 (U1);
+}
+
+
+extern "C++" {
+
+class C2 { };
+struct S2 { };
+union U2 { };
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wredundant-tags"
+  void f3 (class C2);     // -Wredundant-tags
+  void f4 (struct S2);    // -Wredundant-tags
+  void f5 (union U2);     // -Wredundant-tags
+#pragma GCC diagnostic pop
+
+  void f3 (C2);
+  void f4 (S2);
+  void f5 (U2);
+}
+
+
+class C3 { };
+struct S3 { };
+union U3 { };
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wredundant-tags"
+  void f6 (class C3);     // -Wredundant-tags
+  void f7 (struct S3);    // -Wredundant-tags
+  void f8 (union U3);     // -Wredundant-tags
+#pragma GCC diagnostic pop
+
+  void f6 (C3);
+  void f7 (S3);
+  void f8 (U3);
+
+#endif   // WREDUNDANT_TAGS_H

Reply via email to