PR c++/61339 - add warning for mismatch between struct and class
authorMartin Sebor <msebor@redhat.com>
Tue, 17 Dec 2019 23:53:07 +0000 (23:53 +0000)
committerMartin Sebor <msebor@gcc.gnu.org>
Tue, 17 Dec 2019 23:53:07 +0000 (16:53 -0700)
gcc/c-family/ChangeLog:

PR c++/61339
* c.opt (-Wmismatched-tags, -Wredundant-tags): New options.

gcc/cp/ChangeLog:

PR c++/61339
* parser.c (cp_parser_maybe_warn_enum_key): New function.
(class_decl_loc_t): New class.
(cp_parser_elaborated_type_specifier): Call
cp_parser_maybe_warn_enum_key.
(cp_parser_class_head): Call cp_parser_check_class_key.
(cp_parser_check_class_key): Add arguments.  Call class_decl_loc_t::add.
(c_parse_file): Call class_decl_loc_t::diag_mismatched_tags.

gcc/testsuite/ChangeLog:

PR c++/61339
* g++.dg/warn/Wmismatched-tags.C: New test.
* g++.dg/warn/Wredundant-tags.C: New test.
* g++.dg/pch/Wmismatched-tags.C: New test.
* g++.dg/pch/Wmismatched-tags.Hs: New test header.

gcc/ChangeLog:

PR c++/61339
* doc/invoke.texi (-Wmismatched-tags, -Wredundant-tags): Document
new C++ options.

From-SVN: r279480

gcc/ChangeLog
gcc/c-family/ChangeLog
gcc/c-family/c.opt
gcc/cp/ChangeLog
gcc/cp/parser.c
gcc/doc/invoke.texi
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/pch/Wmismatched-tags.C [new file with mode: 0644]
gcc/testsuite/g++.dg/pch/Wmismatched-tags.Hs [new file with mode: 0644]
gcc/testsuite/g++.dg/warn/Wmismatched-tags.C [new file with mode: 0644]
gcc/testsuite/g++.dg/warn/Wredundant-tags.C [new file with mode: 0644]

index 634d3f8e4b51b8739c2d77e71a4b043c2f8c79b9..a77f573d2c4c6f423b928ad4934ae477ddf9606d 100644 (file)
@@ -1,3 +1,9 @@
+2019-12-17  Martin Sebor  <msebor@redhat.com>
+
+       PR c++/61339
+       * doc/invoke.texi (-Wmismatched-tags, -Wredundant-tags): Document
+       new C++ options.
+
 2019-12-17  Michael Meissner  <meissner@linux.ibm.com>
 
        * config/rs6000/rs6000.c (num_insns_constant_gpr): Return 1 if the
index b264c38177f9a974cd899583bb1944344e316e8d..966607f59c9765ad2cd5c43cc991b31e4f15db73 100644 (file)
@@ -1,3 +1,8 @@
+2019-12-17  Martin Sebor  <msebor@redhat.com>
+
+       PR c++/61339
+       * c.opt (-Wmismatched-tags, -Wredundant-tags): New options.
+
 2019-12-11  David Malcolm  <dmalcolm@redhat.com>
 
        * c-pretty-print.c (c_pretty_printer::clone): New vfunc
index 914a2f0ef4425c340331138081f330bc399d1243..1fcb2e303b0332777cd25bdb4711b1d7a0f13497 100644 (file)
@@ -755,6 +755,10 @@ Wmisleading-indentation
 C C++ Common Var(warn_misleading_indentation) Warning LangEnabledBy(C C++,Wall)
 Warn when the indentation of the code does not reflect the block structure.
 
+Wmismatched-tags
+C++ ObjC++ Var(warn_mismatched_tags) Warning
+Warn when a class is redeclared or referenced using a mismatched class-key.
+
 Wmissing-braces
 C ObjC C++ ObjC++ Var(warn_missing_braces) Warning LangEnabledBy(C ObjC,Wall)
 Warn about possibly missing braces around initializers.
@@ -783,6 +787,10 @@ Wpacked-not-aligned
 C ObjC C++ ObjC++ Var(warn_packed_not_aligned) Warning LangEnabledBy(C ObjC C++ ObjC++,Wall)
 Warn when fields in a struct with the packed attribute are misaligned.
 
+Wredundant-tags
+C++ ObjC++ Var(warn_redundant_tags) Warning
+Warn when a class or enumerated type is referenced using a redundant class-key.
+
 Wsized-deallocation
 C++ ObjC++ Var(warn_sized_deallocation) Warning EnabledBy(Wextra)
 Warn about missing sized deallocation functions.
index 91837d36d6e02ab9047d1df66966ffa64e8b08aa..eefe5bf45389879ae2f23b4f8f015bfdffc03925 100644 (file)
@@ -1,3 +1,14 @@
+2019-12-17  Martin Sebor  <msebor@redhat.com>
+
+       PR c++/61339
+       * parser.c (cp_parser_maybe_warn_enum_key): New function.
+       (class_decl_loc_t): New class.
+       (cp_parser_elaborated_type_specifier): Call
+       cp_parser_maybe_warn_enum_key.
+       (cp_parser_class_head): Call cp_parser_check_class_key.
+       (cp_parser_check_class_key): Add arguments.  Call class_decl_loc_t::add.
+       (c_parse_file): Call class_decl_loc_t::diag_mismatched_tags.
+
 2019-12-17  Jason Merrill  <jason@redhat.com>
 
        PR c++/79592 - missing explanation of invalid constexpr.
index 16d1359c47df92c409c1f7077e30b1f1e024506b..f61089934dfe6bcd8865251dbb210f0c3b320cc7 100644 (file)
@@ -2599,8 +2599,9 @@ static enum tag_types cp_parser_token_is_class_key
   (cp_token *);
 static enum tag_types cp_parser_token_is_type_parameter_key
   (cp_token *);
+static void cp_parser_maybe_warn_enum_key (cp_parser *, location_t, tree, rid);
 static void cp_parser_check_class_key
-  (enum tag_types, tree type);
+(cp_parser *, location_t, enum tag_types, tree type, bool, bool);
 static void cp_parser_check_access_in_redeclaration
   (tree type, location_t location);
 static bool cp_parser_optional_template_keyword
@@ -18498,6 +18499,11 @@ cp_parser_elaborated_type_specifier (cp_parser* parser,
   tree globalscope;
   cp_token *token = NULL;
 
+  /* For class and enum types the location of the class-key or enum-key.  */
+  location_t key_loc = cp_lexer_peek_token (parser->lexer)->location;
+  /* For a scoped enum, the 'class' or 'struct' keyword id.  */
+  rid scoped_key = RID_MAX;
+
   /* See if we're looking at the `enum' keyword.  */
   if (cp_lexer_next_token_is_keyword (parser->lexer, RID_ENUM))
     {
@@ -18508,10 +18514,11 @@ cp_parser_elaborated_type_specifier (cp_parser* parser,
       /* Issue a warning if the `struct' or `class' key (for C++0x scoped
         enums) is used here.  */
       cp_token *token = cp_lexer_peek_token (parser->lexer);
-      if (cp_parser_is_keyword (token, RID_CLASS)
-         || cp_parser_is_keyword (token, RID_STRUCT))
+      if (cp_parser_is_keyword (token, scoped_key = RID_CLASS)
+         || cp_parser_is_keyword (token, scoped_key = RID_STRUCT))
        {
-         gcc_rich_location richloc (token->location);
+         location_t loc = token->location;
+         gcc_rich_location richloc (loc);
          richloc.add_range (input_location);
          richloc.add_fixit_remove ();
          pedwarn (&richloc, 0, "elaborated-type-specifier for "
@@ -18519,7 +18526,12 @@ cp_parser_elaborated_type_specifier (cp_parser* parser,
                   token->u.value);
          /* Consume the `struct' or `class' and parse it anyway.  */
          cp_lexer_consume_token (parser->lexer);
+         /* Create a combined location for the whole scoped-enum-key.  */
+         key_loc = make_location (key_loc, key_loc, loc);
        }
+      else
+       scoped_key = RID_MAX;
+
       /* Parse the attributes.  */
       attributes = cp_parser_attributes_opt (parser);
     }
@@ -18535,6 +18547,7 @@ cp_parser_elaborated_type_specifier (cp_parser* parser,
   /* Otherwise it must be a class-key.  */
   else
     {
+      key_loc = cp_lexer_peek_token (parser->lexer)->location;
       tag_type = cp_parser_class_key (parser);
       if (tag_type == none_type)
        return error_mark_node;
@@ -18845,13 +18858,18 @@ cp_parser_elaborated_type_specifier (cp_parser* parser,
                 "attributes ignored on elaborated-type-specifier that is not a forward declaration");
     }
 
-  if (tag_type != enum_type)
+  if (tag_type == enum_type)
+    cp_parser_maybe_warn_enum_key (parser, key_loc, type, scoped_key);
+  else
     {
+      /* Diagnose class/struct/union mismatches.  */
+      cp_parser_check_class_key (parser, key_loc, tag_type, type, false,
+                                cp_parser_declares_only_class_p (parser));
+
       /* Indicate whether this class was declared as a `class' or as a
         `struct'.  */
-      if (CLASS_TYPE_P (type))
+      if (CLASS_TYPE_P (type) && !currently_open_class (type))
        CLASSTYPE_DECLARED_CLASS (type) = (tag_type == class_type);
-      cp_parser_check_class_key (tag_type, type);
     }
 
   /* A "<" cannot follow an elaborated type specifier.  If that
@@ -24389,11 +24407,14 @@ cp_parser_class_head (cp_parser* parser,
                       parser->num_template_parameter_lists);
     }
 
+  /* Diagnose class/struct/union mismatches.  */
+  cp_parser_check_class_key (parser, UNKNOWN_LOCATION, class_key, type,
+                            true, true);
+
   /* Indicate whether this class was declared as a `class' or as a
      `struct'.  */
   if (TREE_CODE (type) == RECORD_TYPE)
-    CLASSTYPE_DECLARED_CLASS (type) = (class_key == class_type);
-  cp_parser_check_class_key (class_key, type);
+    CLASSTYPE_DECLARED_CLASS (type) = class_key == class_type;
 
   /* If this type was already complete, and we see another definition,
      that's an error.  Likewise if the type is already being defined:
@@ -30617,14 +30638,169 @@ cp_parser_token_is_type_parameter_key (cp_token* token)
     }
 }
 
-/* Issue an error message if the CLASS_KEY does not match the TYPE.  */
+/* Diagnose redundant enum-keys.  */
 
 static void
-cp_parser_check_class_key (enum tag_types class_key, tree type)
+cp_parser_maybe_warn_enum_key (cp_parser *parser, location_t key_loc,
+                              tree type, rid scoped_key)
+{
+  tree type_decl = TYPE_MAIN_DECL (type);
+  tree name = DECL_NAME (type_decl);
+  /* Look up the NAME to see if it unambiguously refers to the TYPE
+     and set KEY_REDUNDANT if so.  */
+  tree decl = cp_parser_lookup_name_simple (parser, name, input_location);
+
+  /* The enum-key is redundant for uses of the TYPE that are not
+     declarations and for which name lookup returns just the type
+     itself.  */
+  if (decl == type_decl)
+    {
+      gcc_rich_location richloc (key_loc);
+      richloc.add_fixit_remove (key_loc);
+      warning_at (&richloc, OPT_Wredundant_tags,
+                 "redundant enum-key %<enum%s%> in reference to %q#T",
+                 (scoped_key == RID_CLASS ? " class"
+                  : scoped_key == RID_STRUCT ? " struct" : ""), type);
+    }
+}
+
+/* Describes the set of declarations of a struct, class, or class template
+   or its specializations.  Used for -Wmismatched-tags.  */
+
+class class_decl_loc_t
+{
+ public:
+
+  class_decl_loc_t ()
+    : locvec (), idxdef (), def_class_key ()
+  {
+    locvec.create (4);
+  }
+
+  /* Constructs an object for a single declaration of a class with
+     CLASS_KEY at the current location in the current function (or
+     at another scope).  KEY_REDUNDANT is true if the class-key may
+     be omitted in the current context without an ambiguity with
+     another symbol with the same name.
+     DEF_P is true for a class declaration that is a definition.
+     CURLOC is the associated location.  */
+  class_decl_loc_t (tag_types class_key, bool key_redundant, bool def_p,
+                   location_t curloc = input_location)
+    : locvec (), idxdef (def_p ? 0 : UINT_MAX), def_class_key (class_key)
+  {
+    locvec.create (4);
+    class_key_loc_t ckl (current_function_decl, curloc, class_key,
+                        key_redundant);
+    locvec.quick_push (ckl);
+  }
+
+  /* Copy, assign, and destroy the object.  Necessary because LOCVEC
+     isn't safely copyable and assignable and doesn't release storage
+     on its own.  */
+  class_decl_loc_t (const class_decl_loc_t &rhs)
+    : locvec (rhs.locvec.copy ()), idxdef (rhs.idxdef),
+      def_class_key (rhs.def_class_key)
+  { }
+
+  class_decl_loc_t& operator= (const class_decl_loc_t &rhs)
+  {
+    if (this == &rhs)
+      return *this;
+    locvec.release ();
+    locvec = rhs.locvec.copy ();
+    idxdef = rhs.idxdef;
+    def_class_key = rhs.def_class_key;
+    return *this;
+  }
+
+  ~class_decl_loc_t ()
+  {
+    locvec.release ();
+  }
+
+  /* Issues -Wmismatched-tags for a single class.  */
+  void diag_mismatched_tags (tree);
+
+  /* Issues -Wmismatched-tags for all classes.  */
+  static void diag_mismatched_tags ();
+
+  /* Adds TYPE_DECL to the collection of class decls.  */
+  static void add (tree, tag_types, bool, bool);
+
+  /* Either adds this decl to the collection of class decls
+     or diagnoses it, whichever is appropriate.  */
+  void add_or_diag_mismatched_tag (tree, tag_types, bool, bool);
+
+private:
+
+  tree function (unsigned i) const
+  {
+    return locvec[i].func;
+  }
+
+  location_t location (unsigned i) const
+  {
+    return locvec[i].loc;
+  }
+
+  bool key_redundant (unsigned i) const
+  {
+    return locvec[i].key_redundant;
+  }
+
+  tag_types class_key (unsigned i) const
+  {
+    return locvec[i].class_key;
+  }
+
+  /* The location of a single mention of a class type with the given
+     class-key.  */
+  struct class_key_loc_t
+  {
+    class_key_loc_t (tree func, location_t loc, tag_types key, bool redundant)
+      : func (func), loc (loc), class_key (key), key_redundant (redundant) { }
+
+    /* The function the type is mentioned in.  */
+    tree func;
+    /* The exact location.  */
+    location_t loc;
+    /* The class-key used in the mention of the type.  */
+    tag_types class_key;
+    /* True when the class-key could be omitted at this location
+       without an ambiguity with another symbol of the same name.  */
+    bool key_redundant;
+  };
+  /* Avoid using auto_vec here since it's not safe to copy due to pr90904.  */
+  vec <class_key_loc_t> locvec;
+  /* LOCVEC index of the definition or UINT_MAX if none exists.  */
+  unsigned idxdef;
+  /* The class-key the class was last declared with or none_type when
+     it has been declared with a mismatched key.  */
+  tag_types def_class_key;
+
+  /* A mapping between a TYPE_DECL for a class and the class_decl_loc_t
+     description above.  */
+  typedef hash_map<tree_decl_hash, class_decl_loc_t> class_to_loc_map_t;
+  static class_to_loc_map_t class2loc;
+};
+
+class_decl_loc_t::class_to_loc_map_t class_decl_loc_t::class2loc;
+
+/* Issue an error message if the CLASS_KEY does not match the TYPE.
+   DEF_P is expected to be set for a definition of class TYPE.  DECL_P
+   is set for a declaration of class TYPE and clear for a reference to
+   it that is not a declaration of it.  */
+
+static void
+cp_parser_check_class_key (cp_parser *parser, location_t key_loc,
+                          tag_types class_key, tree type, bool def_p,
+                          bool decl_p)
 {
   if (type == error_mark_node)
     return;
-  if ((TREE_CODE (type) == UNION_TYPE) != (class_key == union_type))
+
+  bool seen_as_union = TREE_CODE (type) == UNION_TYPE;
+  if (seen_as_union != (class_key == union_type))
     {
       if (permerror (input_location, "%qs tag used in naming %q#T",
                     class_key == union_type ? "union"
@@ -30632,7 +30808,240 @@ cp_parser_check_class_key (enum tag_types class_key, tree type)
                     type))
        inform (DECL_SOURCE_LOCATION (TYPE_NAME (type)),
                "%q#T was previously declared here", type);
+      return;
     }
+
+  if (!warn_mismatched_tags && !warn_redundant_tags)
+    return;
+
+  tree type_decl = TYPE_MAIN_DECL (type);
+  tree name = DECL_NAME (type_decl);
+  /* Look up the NAME to see if it unambiguously refers to the TYPE
+     and set KEY_REDUNDANT if so.  */
+  tree decl = cp_parser_lookup_name_simple (parser, name, input_location);
+
+  /* The class-key is redundant for uses of the CLASS_TYPE that are
+     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)
+    {
+      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);
+    }
+
+  if (seen_as_union || !warn_mismatched_tags)
+    return;
+
+  class_decl_loc_t::add (type_decl, class_key, key_redundant, def_p);
+}
+
+/* Adds TYPE_DECL to the collection of class decls.  */
+
+void
+class_decl_loc_t::add (tree type_decl, tag_types class_key, bool redundant,
+                      bool def_p)
+{
+  bool exist;
+  class_decl_loc_t *rdl = &class2loc.get_or_insert (type_decl, &exist);
+  if (!exist)
+    {
+      tree type = TREE_TYPE (type_decl);
+      if (def_p || !COMPLETE_TYPE_P (type))
+       {
+         /* TYPE_DECL is the first declaration or definition of the type
+            (outside precompiled headers -- see below).  Just create
+            a new entry for it.  */
+         *rdl = class_decl_loc_t (class_key, false, def_p);
+         return;
+       }
+
+      /* TYPE was previously defined in some unknown precompiled hdeader.
+        Simply add a record of its definition at an unknown location and
+        proceed below to add a reference to it at the current location.
+        (Declarations in precompiled headers that are not definitions
+        are ignored.)  */
+      tag_types def_key
+       = CLASSTYPE_DECLARED_CLASS (type) ? class_type : record_type;
+      location_t def_loc = DECL_SOURCE_LOCATION (type_decl);
+      *rdl = class_decl_loc_t (def_key, false, true, def_loc);
+    }
+
+  /* A prior declaration of TYPE_DECL has been seen.  */
+
+  if (rdl->idxdef != UINT_MAX && rdl->def_class_key == class_key)
+    /* Do nothing if the class-key in this declaration matches
+       the definition.  */
+    return;
+
+  rdl->add_or_diag_mismatched_tag (type_decl, class_key, redundant, def_p);
+}
+
+/* Either adds this DECL corresponding to the TYPE_DECL to the collection
+   of class decls or diagnoses it, whichever is appropriate.  */
+
+void
+class_decl_loc_t::add_or_diag_mismatched_tag (tree type_decl,
+                                             tag_types class_key,
+                                             bool redundant,
+                                             bool def_p)
+{
+  /* Reset the CLASS_KEY associated with this type on mismatch.
+     This is an optimization that lets the diagnostic code skip
+     over classes that use the same class-key in all declarations.  */
+  if (def_class_key != class_key)
+    def_class_key = none_type;
+
+  /* Set IDXDEF to the index of the vector corresponding to
+     the definition.  */
+  if (def_p)
+    idxdef = locvec.length ();
+
+  /* Append a record of this declaration to the vector.  */
+  class_key_loc_t ckl (current_function_decl, input_location, class_key,
+                      redundant);
+  locvec.safe_push (ckl);
+
+  if (idxdef == UINT_MAX)
+    return;
+
+  /* As a space optimization diagnose declarations of a class
+     whose definition has been seen and purge the LOCVEC of
+     all entries except the definition.  */
+  diag_mismatched_tags (type_decl);
+  if (idxdef)
+    {
+      class_decl_loc_t::class_key_loc_t ent = locvec[idxdef];
+      locvec.release ();
+      locvec.reserve (2);
+      locvec.safe_push (ent);
+      idxdef = 0;
+    }
+  else
+    /* Pop the entry pushed above for this declaration.  */
+    locvec.pop ();
+}
+
+/* Issues -Wmismatched-tags for a single class.  */
+
+void
+class_decl_loc_t::diag_mismatched_tags (tree type_decl)
+{
+  unsigned ndecls = locvec.length ();
+
+  /* Skip a declaration that consistently uses the same class-key
+     or one with just a solitary declaration (i.e., TYPE_DECL).  */
+  if (def_class_key != none_type || ndecls < 2)
+    return;
+
+  /* Save the current function before changing it below.  */
+  tree save_func = current_function_decl;
+  /* Set if a class definition for RECLOC has been seen.  */
+  bool def_p = idxdef < ndecls;
+  unsigned idxguide = def_p ? idxdef : 0;
+  unsigned idx = 0;
+  /* Advance IDX to the first declaration that either is not
+     a definition or that doesn't match the first declaration
+     if no definition is provided.  */
+  while (class_key (idx) == class_key (idxguide))
+    if (++idx == ndecls)
+      return;
+
+  /* The class-key the class is expected to be declared with: it's
+     either the key used in its definition or the first declaration
+     if no definition has been provided.  */
+  tag_types xpect_key = class_key (def_p ? idxguide : 0);
+  const char *xmatchkstr = xpect_key == record_type ? "class" : "struct";
+  const char *xpectkstr = xpect_key == record_type ? "struct" : "class";
+  /* Set the function declaration to print in diagnostic context.  */
+  current_function_decl = function (idx);
+
+  location_t loc = location (idx);
+  bool key_redundant_p = key_redundant (idx);
+  auto_diagnostic_group d;
+  /* Issue a warning for the first mismatched declaration.
+     Avoid using "%#qT" since the class-key for the same type will
+     be the same regardless of which one was used in the declaraion.  */
+  warning_at (loc, OPT_Wmismatched_tags,
+             "%qT declared with a mismatched class-key %qs",
+             type_decl, xmatchkstr);
+
+  /* Suggest how to avoid the warning for each instance since
+     the guidance may be different depending on context.  */
+  inform (loc,
+         (key_redundant_p
+          ? G_("remove the class-key or replace it with %qs")
+          : G_("replace the class-key with %qs")),
+         xpectkstr);
+
+  /* Also point to the first declaration or definition that guided
+     the decision to issue the warning above.  */
+  inform (location (idxguide),
+         (def_p
+          ? G_("%qT defined as %qs here")
+          : G_("%qT first declared as %qs here")),
+         type_decl, xpectkstr);
+
+  /* Issue warnings for the remaining inconsistent declarations.  */
+  for (unsigned i = idx + 1; i != ndecls; ++i)
+    {
+      tag_types clskey = class_key (i);
+      /* Skip over the declarations that match either the definition
+        if one was provided or the first declaration.  */
+      if (clskey == xpect_key)
+       continue;
+
+      loc = location (i);
+      key_redundant_p = key_redundant (i);
+      /* Set the function declaration to print in diagnostic context.  */
+      current_function_decl = function (i);
+      warning_at (loc, OPT_Wmismatched_tags,
+                 "%qT declared with a mismatched class-key %qs",
+                 type_decl, xmatchkstr);
+      /* Suggest how to avoid the warning for each instance since
+        the guidance may be different depending on context.  */
+      inform (loc,
+             (key_redundant_p
+              ? G_("remove the class-key or replace it with %qs")
+              : G_("replace the class-key with %qs")),
+             xpectkstr);
+    }
+
+  /* Restore the current function in case it was replaced above.  */
+  current_function_decl = save_func;
+}
+
+/* Issues -Wmismatched-tags for all classes.  Called at the end
+   of processing a translation unit, after declarations of all class
+   types and their uses have been recorded.  */
+
+void
+class_decl_loc_t::diag_mismatched_tags ()
+{
+  /* CLASS2LOC should be empty if -Wmismatched-tags is disabled.  */
+  gcc_assert (warn_mismatched_tags || class2loc.is_empty ());
+
+  /* Save the current function before changing it below.  It should
+     be null at this point.  */
+  tree save_func = current_function_decl;
+
+  /* Iterate over the collected class/struct declarations.  */
+  typedef class_to_loc_map_t::iterator iter_t;
+  for (iter_t it = class2loc.begin (); it != class2loc.end (); ++it)
+    {
+      tree type_decl = (*it).first;
+      class_decl_loc_t &recloc = (*it).second;
+      recloc.diag_mismatched_tags (type_decl);
+    }
+
+  class2loc.empty ();
+  /* Restore the current function.  */
+  current_function_decl = save_func;
 }
 
 /* Issue an error message if DECL is redeclared with different
@@ -43065,6 +43474,8 @@ c_parse_file (void)
   push_deferring_access_checks (flag_access_control
                                ? dk_no_deferred : dk_no_check);
   cp_parser_translation_unit (the_parser);
+  class_decl_loc_t::diag_mismatched_tags ();
+
   the_parser = NULL;
 
   finish_translation_unit ();
index fe5db99d4fc25bb54c1e17f4ae4a61a165a56f8d..1493e8d633c479e398de536e78d8df3ec6ab6829 100644 (file)
@@ -233,10 +233,10 @@ in the following sections.
 -Wabi=@var{n}  -Wabi-tag  -Wcomma-subscript  -Wconversion-null @gol
 -Wctor-dtor-privacy @gol
 -Wdelete-non-virtual-dtor  -Wdeprecated-copy  -Wdeprecated-copy-dtor @gol
--Wliteral-suffix @gol
+-Wliteral-suffix -Wmismatched-tags @gol
 -Wmultiple-inheritance  -Wno-init-list-lifetime @gol
 -Wnamespaces  -Wnarrowing @gol
--Wpessimizing-move  -Wredundant-move @gol
+-Wpessimizing-move  -Wredundant-move -Wredundant-tags @gol
 -Wnoexcept  -Wnoexcept-type  -Wclass-memaccess @gol
 -Wnon-virtual-dtor  -Wreorder  -Wregister @gol
 -Weffc++  -Wstrict-null-sentinel  -Wtemplates @gol
@@ -3323,6 +3323,21 @@ treats the return value as if it were designated by an rvalue.
 
 This warning is enabled by @option{-Wextra}.
 
+@item -Wredundant-tags @r{(C++ and Objective-C++ only)}
+@opindex Wredundant-tags
+@opindex Wno-redundant-tags
+Warn about redundant class-key and enum-key in references to class types
+and enumerated types in contexts where the key can be eliminated without
+causing an ambiguity.  For example
+
+@smallexample
+struct foo;
+struct foo *p;   // -Wredundant-tags, keyword struct can be eliminated
+
+void foo ();   // "hides" struct foo
+void bar (struct foo&);   // no warning, keyword struct cannot be eliminated
+@end smallexample
+
 @item -fext-numeric-literals @r{(C++ and Objective-C++ only)}
 @opindex fext-numeric-literals
 @opindex fno-ext-numeric-literals
@@ -3458,6 +3473,32 @@ The warning is inactive inside a system header file, such as the STL, so
 one can still use the STL.  One may also instantiate or specialize
 templates.
 
+@item -Wmismatched-tags @r{(C++ and Objective-C++ only)}
+@opindex Wmismatched-tags
+@opindex Wno-mismatched-tags
+Warn for declarations of structs, classes, and class templates and their
+specializations with a class-key that does not match either the definition
+or the first declaration if no definition is provided.
+
+For example, the declaration of @code{struct Object} in the argument list
+of @code{draw} triggers the warning.  To avoid it, either remove the redundant
+class-key @code{struct} or replace it with @code{class} to match its definition.
+@smallexample
+class Object @{
+public:
+  virtual ~Object () = 0;
+@};
+void draw (struct Object*);
+@end smallexample
+
+It is not wrong to declare a class with the class-key @code{struct} as
+the example above shows.  The @option{-Wmismatched-tags} option is intended
+to help achieve a consistent style of class declarations.  In code that is
+intended to be portable to Windows-based compilers the warning helps prevent
+unresolved references due to the difference in the mangling of symbols
+declared with different class-keys.  The option can be used either on its
+own or in conjunction with @option{-Wredundant-tags}.
+
 @item -Wmultiple-inheritance @r{(C++ and Objective-C++ only)}
 @opindex Wmultiple-inheritance
 @opindex Wno-multiple-inheritance
index a3125166f97df5e18b2583f5136caab7cabadf54..3020a95efdf52a0aa51c13a00825db79a2043bdc 100644 (file)
@@ -1,3 +1,11 @@
+2019-12-17  Martin Sebor  <msebor@redhat.com>
+
+       PR c++/61339
+       * g++.dg/warn/Wmismatched-tags.C: New test.
+       * g++.dg/warn/Wredundant-tags.C: New test.
+       * g++.dg/pch/Wmismatched-tags.C: New test.
+       * g++.dg/pch/Wmismatched-tags.Hs: New test header.
+
 2019-12-17  Jakub Jelinek  <jakub@redhat.com>
 
        PR c++/59655
diff --git a/gcc/testsuite/g++.dg/pch/Wmismatched-tags.C b/gcc/testsuite/g++.dg/pch/Wmismatched-tags.C
new file mode 100644 (file)
index 0000000..89b6ba5
--- /dev/null
@@ -0,0 +1,15 @@
+/*  PR c++/61339 - add mismatch between struct and class
+    Verify that declarations that don't match definitions in precompiled
+    headers are diagnosed.
+    { dg-options "-Wall -Wmismatched-tags" } */
+
+#include "Wmismatched-tags.H"
+
+class PCHDeclaredClass;
+struct PCHDeclaredStruct;
+
+struct PCHDefinedClass;       // { dg-warning "declared with a mismatched class-key 'struct'" }
+class PCHDefinedStruct;       // { dg-warning "declared with a mismatched class-key 'class'" }
+
+class PCHDeclaredClass { };
+struct PCHDeclaredStruct { };
diff --git a/gcc/testsuite/g++.dg/pch/Wmismatched-tags.Hs b/gcc/testsuite/g++.dg/pch/Wmismatched-tags.Hs
new file mode 100644 (file)
index 0000000..f4c5dc5
--- /dev/null
@@ -0,0 +1,7 @@
+class PCHDeclaredClass;
+
+struct PCHDeclaredStruct;
+
+class PCHDefinedClass { };
+
+struct PCHDefinedStruct { };
\ No newline at end of file
diff --git a/gcc/testsuite/g++.dg/warn/Wmismatched-tags.C b/gcc/testsuite/g++.dg/warn/Wmismatched-tags.C
new file mode 100644 (file)
index 0000000..36a7903
--- /dev/null
@@ -0,0 +1,278 @@
+/* PR c++/61339 - add mismatch between struct and class
+   Test to verify that -Wmismatched-tags is issued for declarations
+   of the same class using different class-ids.
+   { dg-do compile }
+   { dg-options "-Wmismatched-tags" } */
+
+namespace Classes
+{
+class A;
+class A;
+
+struct B;
+struct B;
+
+union C;
+union C;
+
+struct D;                   // { dg-warning "Classes::D' declared with a mismatched class-key 'struct'" }
+class D { };                // { dg-message "Classes::D' defined as 'class' here" }
+
+class E;                    // { dg-warning "Classes::E' declared with a mismatched class-key 'class'" }
+struct E { };               // { dg-message "Classes::E' defined as 'struct' here" }
+
+class D;
+struct E;
+
+class D;
+struct E;
+
+struct D;                   // { dg-warning "Classes::D' declared with a mismatched class-key" }
+
+class E;                    // { dg-warning "Classes::E' declared with a mismatched class-key" }
+
+class F;                    // { dg-message "Classes::F' first declared as 'class' here" }
+class F;
+
+struct G { };               // { dg-message "Classes::G' defined as 'struct' here" }
+}   // namespace Classes
+
+
+namespace Classes
+{
+class A;
+struct B;
+union C;
+class D;
+struct E;
+
+struct F;                   // { dg-warning "Classes::F' declared with a mismatched class-key" }
+
+struct G;
+}
+
+// Verify that the correct hint is provided, one to remove the class-key
+// when it's redundant, and one to (only) replace it with the correct one
+// when it's needed to disambiguate the reference to the class type.
+namespace RemoveOrReplace
+{
+struct Func;
+class Func;                 // { dg-warning "RemoveOrReplace::Func' declared with a mismatched class-key 'class'" }
+                            // { dg-message "replace the class-key with 'struct'" "hint to remove" { target *-*-* } .-1 }
+
+void Func ();
+
+class Func;                 // { dg-warning "RemoveOrReplace::Func' declared with a mismatched class-key 'class'" }
+                            // { dg-message "replace the class-key with 'struct'" "hint to replace" { target *-*-* } .-1 }
+
+class Var;
+struct Var;                  // { dg-warning "RemoveOrReplace::Var' declared with a mismatched class-key 'struct'" }
+                            // { dg-message "replace the class-key with 'class'" "hint to remove" { target *-*-* } .-1 }
+void f (struct Var*);       // { dg-warning "RemoveOrReplace::Var' declared with a mismatched class-key 'struct'" }
+                            // { dg-message "remove the class-key or replace it with 'class'" "hint to remove" { target *-*-* } .-1 }
+
+int Var;
+
+struct Var;                  // { dg-warning "RemoveOrReplace::Var' declared with a mismatched class-key 'struct'" }
+                            // { dg-message "replace the class-key with 'class'" "hint to replace" { target *-*-* } .-1 }
+}
+
+namespace GlobalObjects
+{
+class A;                    // { dg-message "'GlobalObjects::A' first declared as 'class' here" }
+struct B;                   // { dg-message "'GlobalObjects::B' first declared as 'struct' here" }
+class C { };                // { dg-message "'GlobalObjects::C' defined as 'class' here" }
+
+extern A a0;
+extern class A a1;
+extern class A a2;
+
+extern B b0;
+extern struct B b1;
+extern struct B b2;
+
+extern struct A a3;         // { dg-warning "GlobalObjects::A' declared with a mismatched class-key" }
+extern class A a4;
+
+extern class B b3;          // { dg-warning "GlobalObjects::B' declared with a mismatched class-key" }
+extern struct B b4;
+
+extern struct C c[];        // { dg-warning "GlobalObjects::C' declared with a mismatched class-key" }
+                            // { dg-message "remove the class-key or replace it with 'class'" "hint to remove" { target *-*-* } .-1 }
+
+extern char
+arr[sizeof (struct C)];     // { dg-warning "GlobalObjects::C' declared with a mismatched class-key" }
+                            // { dg-message "remove the class-key or replace it with 'class'" "hint to remove" { target *-*-* } .-1 }
+}   // namespace GlobalObjects
+
+
+namespace LocalObjects
+{
+class A;                    // { dg-message "LocalObjects::A' first declared as 'class' here" }
+struct B;                   // { dg-message "LocalObjects::B' first declared as 'struct' here" }
+
+void f (A*, B&)
+{
+  class A *a1;
+  class A *a2;
+
+  struct B *b1;
+  struct B *b2;
+
+  struct A *a3;             // { dg-warning "LocalObjects::A' declared with a mismatched class-key" }
+  class A *a4;
+
+  class B *b3;              // { dg-warning "LocalObjects::B' declared with a mismatched class-key" }
+  struct B *b4;
+}
+
+void g (struct A*);         // { dg-warning "LocalObjects::A' declared with a mismatched class-key" }
+
+}   // namespace LocalObjects
+
+
+namespace MemberClasses
+{
+struct A { struct B; };
+struct C { struct D; struct D; struct D { }; };
+struct E { class F; class F { }; class F; };
+
+struct G {
+  struct H;                 // { dg-message "MemberClasses::G::H' first declared as 'struct' here" }
+  class H;                  // { dg-warning "MemberClasses::G::H' declared with a mismatched class-key" }
+  class I { };              // { dg-message "MemberClasses::G::I' defined as 'class' here" }
+  struct I;                 // { dg-warning "MemberClasses::G::I' declared with a mismatched class-key" }
+};
+}   // namespace MemberClasses
+
+
+namespace DataMembers
+{
+struct A { struct B *p; };
+struct C { struct D *p; struct D *q; struct D { } d; };
+struct E { class F &r; class F { } f; class F *p; };
+
+class G;                    // { dg-message "DataMembers::G' first declared as 'class' here" }
+struct H;                   // { dg-message "DataMembers::H' first declared as 'struct' here" }
+
+struct I {
+  struct G *p0;             // { dg-warning "DataMembers::G' declared with a mismatched class-key" }
+  class G *p1;
+
+  struct H &r0;
+  class H &r1;              // { dg-warning "DataMembers::H' declared with a mismatched class-key" }
+
+  class J { };              // { dg-message "DataMembers::I::J' defined as 'class' here" }
+  struct K { };             // { dg-message "DataMembers::I::K' defined as 'struct' here" }
+
+  class J j0;
+  class K k0;               // { dg-warning "DataMembers::I::K' declared with a mismatched class-key" }
+
+  struct J j1;              // { dg-warning "DataMembers::I::J' declared with a mismatched class-key" }
+  struct K k1;
+};
+}   // namespace DataMembers
+
+
+namespace Templates
+{
+template <int> class A;
+template <int> class A;
+
+template <int> struct B;
+template <int> struct B;
+
+template <int> union C;
+template <int> union C;
+
+template <int> struct D;    // { dg-warning "Templates::D\[^\n\r]*' declared with a mismatched class-key" }
+template <int>
+class D                     // { dg-message "Templates::D\[^\n\r]*' defined as 'class' here" }
+{ public: D (); };
+
+template <int> class E;     // { dg-warning "Templates::E\[^\n\r]*' declared with a mismatched class-key" }
+template <int>
+struct E                    // { dg-message "Templates::E\[^\n\r]*' defined as 'struct' here" }
+{ int i; };
+
+template <int> class D;
+template <int> struct E;
+
+template <int>
+struct D;                   // { dg-warning "Templates::D\[^\n\r]*' declared with a mismatched class-key" }
+                            // { dg-message "replace the class-key with 'class'" "hint" { target *-*-* } .-1 }
+}   // namespace Templates
+
+
+namespace ExplicitSpecializations
+{
+template <int> class A;
+template <> class A<0>;
+template <> struct A<1>;
+template <> struct A<1> { };
+
+template <int> struct B;
+template <> struct B<0>;
+template <> class B<1>;
+template <> class B<2> { public: B (); };
+
+template <int> union C;
+template <> union C<0>;
+
+template <int> class D;
+template <> class D<0>;     // { dg-warning "ExplicitSpecializations::D\[^\n\r]*' declared with a mismatched class-key " }
+template <>
+struct D<0> { };            // { dg-message "ExplicitSpecializations::D\[^\n\r]*' defined as 'struct' here" }
+
+template <int> struct E;
+template <> struct E<0>;    // { dg-warning "ExplicitSpecializations::E\[^\n\r]*' declared with a mismatched class-key" }
+template <>
+class E<0> { };             // { dg-message "ExplicitSpecializations::E\[^\n\r]*' defined as 'class' here" }
+
+template <int> struct F;
+template <> class F<0> { }; // { dg-message "ExplicitSpecializations::F\[^\n\r]*' defined as 'class' here" }
+
+template <>
+struct F<0>;                // { dg-warning "ExplicitSpecializations::F\[^\n\r]*' declared with a mismatched class-key" }
+}   // namespace ExplicitSpecializations
+
+
+namespace PartialSpecializations
+{
+template <class> class A;
+template <class T> struct A<const T>;
+template <class T> struct A<volatile T>;
+
+template <class> struct B;
+template <class T> class B<const T>;
+template <class T> class B<volatile T>;
+
+template <class> class C { };
+template <class T> struct C<const T> { };
+template <class T> struct C<volatile T> { };
+
+template <class> struct D { };
+template <class T> class D<const T> { };
+template <class T> class D<volatile T> { };
+
+template <class> class E;
+template <class T>
+struct E<const T>;          // { dg-message "PartialSpecializations::E<const T>' first declared as 'struct' here" }
+
+template <class T>
+class E<const T>;           // { dg-warning "PartialSpecializations::E<const T>' declared with a mismatched class-key" }
+
+template <class> class F;
+template <class T>
+class F<const T>;           // { dg-message "PartialSpecializations::F<const T>' first declared as 'class' here" }
+template <class T>
+struct F<const T>;          // { dg-warning "PartialSpecializations::F<const T>' declared with a mismatched class-key" }
+}   // namespace PartialSpecializations
+
+
+namespace Classes
+{
+struct G;
+
+class G;                    // { dg-warning "Classes::G' declared with a mismatched class-key 'class'" }
+}
diff --git a/gcc/testsuite/g++.dg/warn/Wredundant-tags.C b/gcc/testsuite/g++.dg/warn/Wredundant-tags.C
new file mode 100644 (file)
index 0000000..ac5afa9
--- /dev/null
@@ -0,0 +1,128 @@
+/* PR c++/61339 - add mismatch between struct and class
+   Test to verify that -Wredundant-tags is issued for references to class
+   types that use the class-key even though they don't need to.
+   { dg-do compile }
+   { dg-options "-Wredundant-tags" } */
+
+struct A;
+
+extern A *pa;
+extern struct A *pa;        // { dg-warning "redundant class-key 'struct' in reference to 'struct A'" }
+
+extern A aa[];
+extern struct A aa[];       // { dg-warning "redundant class-key 'struct' in reference to 'struct A'" }
+
+void func (A*);
+void func (struct A*);      // { dg-warning "redundant class-key 'struct' in reference to 'struct A'" }
+
+int A;
+
+extern struct A *pa;
+extern struct A aa[];
+void func (struct A*);
+
+
+class B;
+
+extern B *pb;
+extern class B *pb;         // { dg-warning "redundant class-key 'class' in reference to 'class B'" }
+
+extern B ab[];
+extern class B ab[];        // { dg-warning "redundant class-key 'class' in reference to 'class B'" }
+
+void func (B*);
+void func (class B*);       // { dg-warning "redundant class-key 'class' in reference to 'class B'" }
+
+int B;
+
+extern class B *pb;
+extern class B ab[];
+void func (class B*);
+
+
+enum C { c0 };
+
+extern C *pc;
+extern enum C *pc;          // { dg-warning "redundant enum-key 'enum' in reference to 'enum C'" }
+
+extern C ac[];
+extern enum C ac[];         // { dg-warning "redundant enum-key 'enum' in reference to 'enum C'" }
+
+void func (C*);
+void func (enum C*);        // { dg-warning "redundant enum-key 'enum' in reference to 'enum C'" }
+
+int C;
+
+extern enum C *pc;
+extern enum C ac[];
+void func (enum C*);
+
+
+#if __cplusplus > 199711L
+
+enum class D1 { d1 };
+enum struct D2 { d2 };
+
+#else
+
+enum D1 { d1 };
+enum D2 { d2 };
+
+#endif
+
+extern D1 *pd1;
+extern D2 *pd2;
+extern enum D1 *pd1;        // { dg-warning "redundant enum-key 'enum' in reference to 'enum class D1'" "C++ 11 and above" { target c++11 } }
+                            // { dg-warning "redundant enum-key 'enum' in reference to 'enum D1'" "C++ 98" { target c++98_only } .-1 }
+
+extern enum D2 *pd2;        // { dg-warning "redundant enum-key 'enum' in reference to 'enum class D2'" "C++ 11 and above" { target c++11 } }
+                            // { dg-warning "redundant enum-key 'enum' in reference to 'enum D2'" "C++ 98" { target c++98_only } .-1 }
+
+extern D1 ad1[];
+extern D2 ad2[];
+
+#if __cplusplus > 199711L
+extern enum class D1 ad1[]; // { dg-warning "redundant enum-key 'enum class' in reference to 'enum class D1'" "C++ 11 and above" { target c++11 } }
+                            // { dg-warning "elaborated-type-specifier for a scoped enum must not use the 'class' keyword" "C++ 11 and above" { target c++11 } .-1 }
+/* The pretty printer cannot differentiate between enum class and enum struct
+   because the C++ front-end doesn't encode it so allow for both in the text
+   of the warning below.  */
+extern enum struct D2 ad2[]; // { dg-warning "redundant enum-key 'enum struct' in reference to 'enum \(class|struct\) D2'" "C++ 11 and above" { target c++11 } }
+                            // { dg-warning "elaborated-type-specifier for a scoped enum must not use the 'struct' keyword" "C++ 11 and above" { target c++11 } .-1 }
+#else
+extern enum D1 ad1[];       // { dg-warning "redundant enum-key 'enum' in reference to 'enum D1'" "C++ 98" { target c++98_only } }
+#endif
+
+void func (D1*);
+void func (enum D1*);       // { dg-warning "redundant enum-key 'enum' in reference to 'enum " }
+
+void func (D2*);
+void func (enum D2*);       // { dg-warning "redundant enum-key 'enum' in reference to 'enum " }
+
+int D1, D2;
+
+extern enum D1 *pd1;
+extern enum D1 ad1[];
+void func (enum D1*);
+
+extern enum D2 *pd2;
+extern enum D2 ad2[];
+void func (enum D2*);
+
+
+union U;
+
+extern U *pu;
+extern union U *pu;         // { dg-warning "redundant class-key 'union' in reference to 'union U'" }
+
+extern U au[];
+extern union U au[];        // { dg-warning "redundant class-key 'union' in reference to 'union U'" }
+
+void func (U*);
+void func (union U*);       // { dg-warning "redundant class-key 'union' in reference to 'union U'" }
+
+int U;
+
+extern union U *pu;
+extern union U au[];
+void func (union U*);