diff mbox series

[v2] c++: DR 569, DR 1693: fun with semicolons [PR113760]

Message ID ZkOXizZQssE8-see@redhat.com
State New
Headers show
Series [v2] c++: DR 569, DR 1693: fun with semicolons [PR113760] | expand

Commit Message

Marek Polacek May 14, 2024, 4:55 p.m. UTC
On Thu, May 09, 2024 at 12:44:52PM -0400, Jason Merrill wrote:
> On 5/9/24 12:16, Marek Polacek wrote:
> > +static void
> > +maybe_warn_extra_semi (location_t loc, extra_semi_kind kind)
> > +{
> > +  /* -Wno-extra-semi suppresses all.  */
> > +  if (warn_extra_semi == 0)
> > +    return;
> > +
> > +  gcc_rich_location richloc (loc);
> > +  richloc.add_fixit_remove ();
> > +
> > +  switch (kind)
> > +    {
> > +    case extra_semi_kind::decl:
> > +      if (warn_extra_semi > 0)
> > +	warning_at (&richloc, OPT_Wextra_semi,
> > +		    "extra %<;%> outside of a function");
> > +      /* If -Wextra-semi wasn't specified, warn only when -pedantic is in
> > +	 effect in C++98.  DR 569 says that spurious semicolons at namespace
> > +	 scope should be allowed.  */
> > +      else if (pedantic && cxx_dialect < cxx11)
> > +	pedwarn (&richloc, OPT_Wextra_semi,
> > +		 "extra %<;%> outside of a function only allowed in C++11");
> 
> Shouldn't the pedwarn case come first?

Yes, I suppose we want both
-Wpedantic -std=c++98
-Wpedantic -std=c++98 -Wextra-semi
to give pedwarns.  Fixed.
 
> > +      break;
> > +
> > +    case extra_semi_kind::member:
> > +      if (warn_extra_semi > 0)
> > +	warning_at (&richloc, OPT_Wextra_semi, "extra %<;%> inside a struct");
> > +      /* If -Wextra-semi wasn't specified, warn only when -pedantic is in
> > +	 effect in C++98.  DR 1693 added "empty-declaration" to the syntax for
> > +	 "member-declaration".  */
> > +      else if (pedantic && cxx_dialect < cxx11)
> > +	pedwarn (&richloc, OPT_Wextra_semi,
> > +		 "extra %<;%> inside a struct only allowed in C++11");
> 
> And here.

Fixed too.

> > +      break;
> > +
> > +    case extra_semi_kind::in_class_fn_def:
> > +      /* A single semicolon is valid after a member function definition
> > +	 so this is just a warning.  */
> > +      if (warn_extra_semi > 0)
> > +	warning_at (&richloc, OPT_Wextra_semi,
> > +		    "extra %<;%> after in-class function definition");
> > +      break;
> > +
> > +    default:
> > +      gcc_unreachable ();
> > +    }
> > +}
> > +
> >   /* Declarations [gram.dcl.dcl] */
> >   /* Parse an optional declaration-sequence.  TOP_LEVEL is true, if this
> > @@ -15430,11 +15485,11 @@ cp_parser_declaration (cp_parser* parser, tree prefix_attrs)
> >     if (token1->type == CPP_SEMICOLON)
> >       {
> > -      cp_lexer_consume_token (parser->lexer);
> > +      location_t semicolon_loc
> > +	= cp_lexer_consume_token (parser->lexer)->location;
> >         /* A declaration consisting of a single semicolon is invalid
> > -       * before C++11.  Allow it unless we're being pedantic.  */
> > -      if (cxx_dialect < cxx11)
> > -	pedwarn (input_location, OPT_Wpedantic, "extra %<;%>");
> > +	 before C++11.  Allow it unless we're being pedantic.  */
> > +      maybe_warn_extra_semi (semicolon_loc, extra_semi_kind::decl);
> >         return;
> >       }
> >     else if (cp_lexer_nth_token_is (parser->lexer,
> > @@ -28121,19 +28176,27 @@ cp_parser_member_declaration (cp_parser* parser)
> >   	   struct S { ; };
> > -	 [class.mem]
> > +	 [class.mem] used to say
> >   	 Each member-declaration shall declare at least one member
> > -	 name of the class.  */
> > +	 name of the class.
> > +
> > +	 but since DR 1693:
> > +
> > +	 A member-declaration does not declare new members of the class
> > +	 if it is
> > +	 -- [...]
> > +	 -- an empty-declaration.
> > +	 For any other member-declaration, each declared entity that is not
> > +	 an unnamed bit-field is a member of the class, and each such
> > +	 member-declaration shall either declare at least one member name of
> > +	 the class or declare at least one unnamed bit-field.  */
> >         if (!decl_specifiers.any_specifiers_p)
> >   	{
> >   	  cp_token *token = cp_lexer_peek_token (parser->lexer);
> > -	  if (cxx_dialect < cxx11 && !in_system_header_at (token->location))
> > -	    {
> > -	      gcc_rich_location richloc (token->location);
> > -	      richloc.add_fixit_remove ();
> > -	      pedwarn (&richloc, OPT_Wpedantic, "extra %<;%>");
> > -	    }
> > +	  // ??? Should we check !in_system_header_at in maybe_warn_extra_semi?
> 
> The system header check seems to date back to PR 12479.  Apparently it has
> not ever proved necessary for the other cases.
> 
> Furthermore, all pedwarns are now ignored in system headers unless
> -Wsystem-headers is specified, in which case it would seem useful to report
> this.
> 
> So, my inclination is to drop the system header check entirely at this
> point.

Done.

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

-- >8 --
Prompted by c++/113760, I started looking into a bogus "extra ;"
warning in C++11.  It quickly turned out that if I want to fix
this for good, the fix will not be so small.

This patch touches on DR 569, an extra ; at namespace scope should
be allowed since C++11:

  struct S {
  };
  ; // pedwarn in C++98

It also touches on DR 1693, which allows superfluous semicolons in
class definitions since C++11:

  struct S {
    int a;
    ; // pedwarn in C++98
  };

Note that a single semicolon is valid after a member function definition:

  struct S {
    void foo () {}; // only warns with -Wextra-semi
  };

There's a new function maybe_warn_extra_semi to handle all of the above
in a single place.  So now they all get a fix-it hint.

-Wextra-semi turns on all "extra ;" diagnostics.  Currently, options
like -Wc++11-compat or -Wc++11-extensions are not considered.

	DR 1693
	PR c++/113760
	DR 569

gcc/c-family/ChangeLog:

	* c.opt (Wextra-semi): Initialize to -1.

gcc/cp/ChangeLog:

	* parser.cc (extra_semi_kind): New.
	(maybe_warn_extra_semi): New.
	(cp_parser_declaration): Call maybe_warn_extra_semi.
	(cp_parser_member_declaration): Likewise.

gcc/ChangeLog:

	* doc/invoke.texi: Update -Wextra-semi documentation.

gcc/testsuite/ChangeLog:

	* g++.dg/diagnostic/semicolon1.C: New test.
	* g++.dg/diagnostic/semicolon10.C: New test.
	* g++.dg/diagnostic/semicolon11.C: New test.
	* g++.dg/diagnostic/semicolon12.C: New test.
	* g++.dg/diagnostic/semicolon13.C: New test.
	* g++.dg/diagnostic/semicolon14.C: New test.
	* g++.dg/diagnostic/semicolon15.C: New test.
	* g++.dg/diagnostic/semicolon16.C: New test.
	* g++.dg/diagnostic/semicolon17.C: New test.
	* g++.dg/diagnostic/semicolon2.C: New test.
	* g++.dg/diagnostic/semicolon3.C: New test.
	* g++.dg/diagnostic/semicolon4.C: New test.
	* g++.dg/diagnostic/semicolon5.C: New test.
	* g++.dg/diagnostic/semicolon6.C: New test.
	* g++.dg/diagnostic/semicolon7.C: New test.
	* g++.dg/diagnostic/semicolon8.C: New test.
	* g++.dg/diagnostic/semicolon9.C: New test.
---
 gcc/c-family/c.opt                            |  2 +-
 gcc/cp/parser.cc                              | 92 +++++++++++++++----
 gcc/doc/invoke.texi                           | 29 +++++-
 gcc/testsuite/g++.dg/diagnostic/semicolon1.C  | 18 ++++
 gcc/testsuite/g++.dg/diagnostic/semicolon10.C | 11 +++
 gcc/testsuite/g++.dg/diagnostic/semicolon11.C | 29 ++++++
 gcc/testsuite/g++.dg/diagnostic/semicolon12.C | 29 ++++++
 gcc/testsuite/g++.dg/diagnostic/semicolon13.C | 29 ++++++
 gcc/testsuite/g++.dg/diagnostic/semicolon14.C | 29 ++++++
 gcc/testsuite/g++.dg/diagnostic/semicolon15.C | 29 ++++++
 gcc/testsuite/g++.dg/diagnostic/semicolon16.C | 38 ++++++++
 gcc/testsuite/g++.dg/diagnostic/semicolon17.C | 29 ++++++
 gcc/testsuite/g++.dg/diagnostic/semicolon2.C  | 18 ++++
 gcc/testsuite/g++.dg/diagnostic/semicolon3.C  | 18 ++++
 gcc/testsuite/g++.dg/diagnostic/semicolon4.C  | 18 ++++
 gcc/testsuite/g++.dg/diagnostic/semicolon5.C  | 18 ++++
 gcc/testsuite/g++.dg/diagnostic/semicolon6.C  | 23 +++++
 gcc/testsuite/g++.dg/diagnostic/semicolon7.C  | 18 ++++
 gcc/testsuite/g++.dg/diagnostic/semicolon8.C  | 11 +++
 gcc/testsuite/g++.dg/diagnostic/semicolon9.C  | 11 +++
 20 files changed, 480 insertions(+), 19 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/semicolon1.C
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/semicolon10.C
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/semicolon11.C
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/semicolon12.C
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/semicolon13.C
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/semicolon14.C
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/semicolon15.C
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/semicolon16.C
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/semicolon17.C
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/semicolon2.C
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/semicolon3.C
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/semicolon4.C
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/semicolon5.C
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/semicolon6.C
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/semicolon7.C
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/semicolon8.C
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/semicolon9.C


base-commit: 54ba8d44bbd703bca6984700b4d6f978890097e2

Comments

Jason Merrill May 14, 2024, 9:54 p.m. UTC | #1
On 5/14/24 12:55, Marek Polacek wrote:
> On Thu, May 09, 2024 at 12:44:52PM -0400, Jason Merrill wrote:
>> On 5/9/24 12:16, Marek Polacek wrote:
>>> +static void
>>> +maybe_warn_extra_semi (location_t loc, extra_semi_kind kind)
>>> +{
>>> +  /* -Wno-extra-semi suppresses all.  */
>>> +  if (warn_extra_semi == 0)
>>> +    return;
>>> +
>>> +  gcc_rich_location richloc (loc);
>>> +  richloc.add_fixit_remove ();
>>> +
>>> +  switch (kind)
>>> +    {
>>> +    case extra_semi_kind::decl:
>>> +      if (warn_extra_semi > 0)
>>> +	warning_at (&richloc, OPT_Wextra_semi,
>>> +		    "extra %<;%> outside of a function");
>>> +      /* If -Wextra-semi wasn't specified, warn only when -pedantic is in
>>> +	 effect in C++98.  DR 569 says that spurious semicolons at namespace
>>> +	 scope should be allowed.  */
>>> +      else if (pedantic && cxx_dialect < cxx11)
>>> +	pedwarn (&richloc, OPT_Wextra_semi,
>>> +		 "extra %<;%> outside of a function only allowed in C++11");
>>
>> Shouldn't the pedwarn case come first?
> 
> Yes, I suppose we want both
> -Wpedantic -std=c++98
> -Wpedantic -std=c++98 -Wextra-semi
> to give pedwarns.  Fixed.
>   
>>> +      break;
>>> +
>>> +    case extra_semi_kind::member:
>>> +      if (warn_extra_semi > 0)
>>> +	warning_at (&richloc, OPT_Wextra_semi, "extra %<;%> inside a struct");
>>> +      /* If -Wextra-semi wasn't specified, warn only when -pedantic is in
>>> +	 effect in C++98.  DR 1693 added "empty-declaration" to the syntax for
>>> +	 "member-declaration".  */
>>> +      else if (pedantic && cxx_dialect < cxx11)
>>> +	pedwarn (&richloc, OPT_Wextra_semi,
>>> +		 "extra %<;%> inside a struct only allowed in C++11");
>>
>> And here.
> 
> Fixed too.
> 
>>> +      break;
>>> +
>>> +    case extra_semi_kind::in_class_fn_def:
>>> +      /* A single semicolon is valid after a member function definition
>>> +	 so this is just a warning.  */
>>> +      if (warn_extra_semi > 0)
>>> +	warning_at (&richloc, OPT_Wextra_semi,
>>> +		    "extra %<;%> after in-class function definition");
>>> +      break;
>>> +
>>> +    default:
>>> +      gcc_unreachable ();
>>> +    }
>>> +}
>>> +
>>>    /* Declarations [gram.dcl.dcl] */
>>>    /* Parse an optional declaration-sequence.  TOP_LEVEL is true, if this
>>> @@ -15430,11 +15485,11 @@ cp_parser_declaration (cp_parser* parser, tree prefix_attrs)
>>>      if (token1->type == CPP_SEMICOLON)
>>>        {
>>> -      cp_lexer_consume_token (parser->lexer);
>>> +      location_t semicolon_loc
>>> +	= cp_lexer_consume_token (parser->lexer)->location;
>>>          /* A declaration consisting of a single semicolon is invalid
>>> -       * before C++11.  Allow it unless we're being pedantic.  */
>>> -      if (cxx_dialect < cxx11)
>>> -	pedwarn (input_location, OPT_Wpedantic, "extra %<;%>");
>>> +	 before C++11.  Allow it unless we're being pedantic.  */
>>> +      maybe_warn_extra_semi (semicolon_loc, extra_semi_kind::decl);
>>>          return;
>>>        }
>>>      else if (cp_lexer_nth_token_is (parser->lexer,
>>> @@ -28121,19 +28176,27 @@ cp_parser_member_declaration (cp_parser* parser)
>>>    	   struct S { ; };
>>> -	 [class.mem]
>>> +	 [class.mem] used to say
>>>    	 Each member-declaration shall declare at least one member
>>> -	 name of the class.  */
>>> +	 name of the class.
>>> +
>>> +	 but since DR 1693:
>>> +
>>> +	 A member-declaration does not declare new members of the class
>>> +	 if it is
>>> +	 -- [...]
>>> +	 -- an empty-declaration.
>>> +	 For any other member-declaration, each declared entity that is not
>>> +	 an unnamed bit-field is a member of the class, and each such
>>> +	 member-declaration shall either declare at least one member name of
>>> +	 the class or declare at least one unnamed bit-field.  */
>>>          if (!decl_specifiers.any_specifiers_p)
>>>    	{
>>>    	  cp_token *token = cp_lexer_peek_token (parser->lexer);
>>> -	  if (cxx_dialect < cxx11 && !in_system_header_at (token->location))
>>> -	    {
>>> -	      gcc_rich_location richloc (token->location);
>>> -	      richloc.add_fixit_remove ();
>>> -	      pedwarn (&richloc, OPT_Wpedantic, "extra %<;%>");
>>> -	    }
>>> +	  // ??? Should we check !in_system_header_at in maybe_warn_extra_semi?
>>
>> The system header check seems to date back to PR 12479.  Apparently it has
>> not ever proved necessary for the other cases.
>>
>> Furthermore, all pedwarns are now ignored in system headers unless
>> -Wsystem-headers is specified, in which case it would seem useful to report
>> this.
>>
>> So, my inclination is to drop the system header check entirely at this
>> point.
> 
> Done.
> 
> Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?

OK.

> -- >8 --
> Prompted by c++/113760, I started looking into a bogus "extra ;"
> warning in C++11.  It quickly turned out that if I want to fix
> this for good, the fix will not be so small.
> 
> This patch touches on DR 569, an extra ; at namespace scope should
> be allowed since C++11:
> 
>    struct S {
>    };
>    ; // pedwarn in C++98
> 
> It also touches on DR 1693, which allows superfluous semicolons in
> class definitions since C++11:
> 
>    struct S {
>      int a;
>      ; // pedwarn in C++98
>    };
> 
> Note that a single semicolon is valid after a member function definition:
> 
>    struct S {
>      void foo () {}; // only warns with -Wextra-semi
>    };
> 
> There's a new function maybe_warn_extra_semi to handle all of the above
> in a single place.  So now they all get a fix-it hint.
> 
> -Wextra-semi turns on all "extra ;" diagnostics.  Currently, options
> like -Wc++11-compat or -Wc++11-extensions are not considered.
> 
> 	DR 1693
> 	PR c++/113760
> 	DR 569
> 
> gcc/c-family/ChangeLog:
> 
> 	* c.opt (Wextra-semi): Initialize to -1.
> 
> gcc/cp/ChangeLog:
> 
> 	* parser.cc (extra_semi_kind): New.
> 	(maybe_warn_extra_semi): New.
> 	(cp_parser_declaration): Call maybe_warn_extra_semi.
> 	(cp_parser_member_declaration): Likewise.
> 
> gcc/ChangeLog:
> 
> 	* doc/invoke.texi: Update -Wextra-semi documentation.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/diagnostic/semicolon1.C: New test.
> 	* g++.dg/diagnostic/semicolon10.C: New test.
> 	* g++.dg/diagnostic/semicolon11.C: New test.
> 	* g++.dg/diagnostic/semicolon12.C: New test.
> 	* g++.dg/diagnostic/semicolon13.C: New test.
> 	* g++.dg/diagnostic/semicolon14.C: New test.
> 	* g++.dg/diagnostic/semicolon15.C: New test.
> 	* g++.dg/diagnostic/semicolon16.C: New test.
> 	* g++.dg/diagnostic/semicolon17.C: New test.
> 	* g++.dg/diagnostic/semicolon2.C: New test.
> 	* g++.dg/diagnostic/semicolon3.C: New test.
> 	* g++.dg/diagnostic/semicolon4.C: New test.
> 	* g++.dg/diagnostic/semicolon5.C: New test.
> 	* g++.dg/diagnostic/semicolon6.C: New test.
> 	* g++.dg/diagnostic/semicolon7.C: New test.
> 	* g++.dg/diagnostic/semicolon8.C: New test.
> 	* g++.dg/diagnostic/semicolon9.C: New test.
> ---
>   gcc/c-family/c.opt                            |  2 +-
>   gcc/cp/parser.cc                              | 92 +++++++++++++++----
>   gcc/doc/invoke.texi                           | 29 +++++-
>   gcc/testsuite/g++.dg/diagnostic/semicolon1.C  | 18 ++++
>   gcc/testsuite/g++.dg/diagnostic/semicolon10.C | 11 +++
>   gcc/testsuite/g++.dg/diagnostic/semicolon11.C | 29 ++++++
>   gcc/testsuite/g++.dg/diagnostic/semicolon12.C | 29 ++++++
>   gcc/testsuite/g++.dg/diagnostic/semicolon13.C | 29 ++++++
>   gcc/testsuite/g++.dg/diagnostic/semicolon14.C | 29 ++++++
>   gcc/testsuite/g++.dg/diagnostic/semicolon15.C | 29 ++++++
>   gcc/testsuite/g++.dg/diagnostic/semicolon16.C | 38 ++++++++
>   gcc/testsuite/g++.dg/diagnostic/semicolon17.C | 29 ++++++
>   gcc/testsuite/g++.dg/diagnostic/semicolon2.C  | 18 ++++
>   gcc/testsuite/g++.dg/diagnostic/semicolon3.C  | 18 ++++
>   gcc/testsuite/g++.dg/diagnostic/semicolon4.C  | 18 ++++
>   gcc/testsuite/g++.dg/diagnostic/semicolon5.C  | 18 ++++
>   gcc/testsuite/g++.dg/diagnostic/semicolon6.C  | 23 +++++
>   gcc/testsuite/g++.dg/diagnostic/semicolon7.C  | 18 ++++
>   gcc/testsuite/g++.dg/diagnostic/semicolon8.C  | 11 +++
>   gcc/testsuite/g++.dg/diagnostic/semicolon9.C  | 11 +++
>   20 files changed, 480 insertions(+), 19 deletions(-)
>   create mode 100644 gcc/testsuite/g++.dg/diagnostic/semicolon1.C
>   create mode 100644 gcc/testsuite/g++.dg/diagnostic/semicolon10.C
>   create mode 100644 gcc/testsuite/g++.dg/diagnostic/semicolon11.C
>   create mode 100644 gcc/testsuite/g++.dg/diagnostic/semicolon12.C
>   create mode 100644 gcc/testsuite/g++.dg/diagnostic/semicolon13.C
>   create mode 100644 gcc/testsuite/g++.dg/diagnostic/semicolon14.C
>   create mode 100644 gcc/testsuite/g++.dg/diagnostic/semicolon15.C
>   create mode 100644 gcc/testsuite/g++.dg/diagnostic/semicolon16.C
>   create mode 100644 gcc/testsuite/g++.dg/diagnostic/semicolon17.C
>   create mode 100644 gcc/testsuite/g++.dg/diagnostic/semicolon2.C
>   create mode 100644 gcc/testsuite/g++.dg/diagnostic/semicolon3.C
>   create mode 100644 gcc/testsuite/g++.dg/diagnostic/semicolon4.C
>   create mode 100644 gcc/testsuite/g++.dg/diagnostic/semicolon5.C
>   create mode 100644 gcc/testsuite/g++.dg/diagnostic/semicolon6.C
>   create mode 100644 gcc/testsuite/g++.dg/diagnostic/semicolon7.C
>   create mode 100644 gcc/testsuite/g++.dg/diagnostic/semicolon8.C
>   create mode 100644 gcc/testsuite/g++.dg/diagnostic/semicolon9.C
> 
> diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
> index 403abc1f26e..fb34c3b7031 100644
> --- a/gcc/c-family/c.opt
> +++ b/gcc/c-family/c.opt
> @@ -727,7 +727,7 @@ C ObjC C++ ObjC++ Warning
>   ; in common.opt
>   
>   Wextra-semi
> -C++ ObjC++ Var(warn_extra_semi) Warning
> +C++ ObjC++ Var(warn_extra_semi) Init(-1) Warning
>   Warn about semicolon after in-class function definition.
>   
>   Wflex-array-member-not-at-end
> diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
> index 7306ce9a8a8..476ddc0d63a 100644
> --- a/gcc/cp/parser.cc
> +++ b/gcc/cp/parser.cc
> @@ -15331,6 +15331,61 @@ cp_parser_module_export (cp_parser *parser)
>     module_kind = mk;
>   }
>   
> +/* Used for maybe_warn_extra_semi.  */
> +
> +enum class extra_semi_kind { decl, member, in_class_fn_def };
> +
> +/* Warn about an extra semicolon.  KIND says in which context the extra
> +   semicolon occurs.  */
> +
> +static void
> +maybe_warn_extra_semi (location_t loc, extra_semi_kind kind)
> +{
> +  /* -Wno-extra-semi suppresses all.  */
> +  if (warn_extra_semi == 0)
> +    return;
> +
> +  gcc_rich_location richloc (loc);
> +  richloc.add_fixit_remove ();
> +
> +  switch (kind)
> +    {
> +    case extra_semi_kind::decl:
> +      /* If -Wextra-semi wasn't specified, warn only when -pedantic is in
> +	 effect in C++98.  DR 569 says that spurious semicolons at namespace
> +	 scope should be allowed.  */
> +      if (pedantic && cxx_dialect < cxx11)
> +	pedwarn (&richloc, OPT_Wextra_semi,
> +		 "extra %<;%> outside of a function only allowed in C++11");
> +      else if (warn_extra_semi > 0)
> +	warning_at (&richloc, OPT_Wextra_semi,
> +		    "extra %<;%> outside of a function");
> +      break;
> +
> +    case extra_semi_kind::member:
> +      /* If -Wextra-semi wasn't specified, warn only when -pedantic is in
> +	 effect in C++98.  DR 1693 added "empty-declaration" to the syntax for
> +	 "member-declaration".  */
> +      if (pedantic && cxx_dialect < cxx11)
> +	pedwarn (&richloc, OPT_Wextra_semi,
> +		 "extra %<;%> inside a struct only allowed in C++11");
> +      else if (warn_extra_semi > 0)
> +	warning_at (&richloc, OPT_Wextra_semi, "extra %<;%> inside a struct");
> +      break;
> +
> +    case extra_semi_kind::in_class_fn_def:
> +      /* A single semicolon is valid after a member function definition
> +	 so this is just a warning.  */
> +      if (warn_extra_semi > 0)
> +	warning_at (&richloc, OPT_Wextra_semi,
> +		    "extra %<;%> after in-class function definition");
> +      break;
> +
> +    default:
> +      gcc_unreachable ();
> +    }
> +}
> +
>   /* Declarations [gram.dcl.dcl] */
>   
>   /* Parse an optional declaration-sequence.  TOP_LEVEL is true, if this
> @@ -15430,11 +15485,11 @@ cp_parser_declaration (cp_parser* parser, tree prefix_attrs)
>   
>     if (token1->type == CPP_SEMICOLON)
>       {
> -      cp_lexer_consume_token (parser->lexer);
> +      location_t semicolon_loc
> +	= cp_lexer_consume_token (parser->lexer)->location;
>         /* A declaration consisting of a single semicolon is invalid
> -       * before C++11.  Allow it unless we're being pedantic.  */
> -      if (cxx_dialect < cxx11)
> -	pedwarn (input_location, OPT_Wpedantic, "extra %<;%>");
> +	 before C++11.  Allow it unless we're being pedantic.  */
> +      maybe_warn_extra_semi (semicolon_loc, extra_semi_kind::decl);
>         return;
>       }
>     else if (cp_lexer_nth_token_is (parser->lexer,
> @@ -28121,19 +28176,25 @@ cp_parser_member_declaration (cp_parser* parser)
>   
>   	   struct S { ; };
>   
> -	 [class.mem]
> +	 [class.mem] used to say
>   
>   	 Each member-declaration shall declare at least one member
> -	 name of the class.  */
> +	 name of the class.
> +
> +	 but since DR 1693:
> +
> +	 A member-declaration does not declare new members of the class
> +	 if it is
> +	 -- [...]
> +	 -- an empty-declaration.
> +	 For any other member-declaration, each declared entity that is not
> +	 an unnamed bit-field is a member of the class, and each such
> +	 member-declaration shall either declare at least one member name of
> +	 the class or declare at least one unnamed bit-field.  */
>         if (!decl_specifiers.any_specifiers_p)
>   	{
>   	  cp_token *token = cp_lexer_peek_token (parser->lexer);
> -	  if (cxx_dialect < cxx11 && !in_system_header_at (token->location))
> -	    {
> -	      gcc_rich_location richloc (token->location);
> -	      richloc.add_fixit_remove ();
> -	      pedwarn (&richloc, OPT_Wpedantic, "extra %<;%>");
> -	    }
> +	  maybe_warn_extra_semi (token->location, extra_semi_kind::member);
>   	}
>         else
>   	{
> @@ -28564,11 +28625,8 @@ cp_parser_member_declaration (cp_parser* parser)
>   		    {
>   		      location_t semicolon_loc
>   			= cp_lexer_consume_token (parser->lexer)->location;
> -		      gcc_rich_location richloc (semicolon_loc);
> -		      richloc.add_fixit_remove ();
> -		      warning_at (&richloc, OPT_Wextra_semi,
> -				  "extra %<;%> after in-class "
> -				  "function definition");
> +		      maybe_warn_extra_semi (semicolon_loc,
> +					     extra_semi_kind::in_class_fn_def);
>   		    }
>   		  goto out;
>   		}
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index ddcd5213f06..cedc1a28c83 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -4780,7 +4780,34 @@ undefined behavior at runtime.  This warning is enabled by default.
>   @opindex Wextra-semi
>   @opindex Wno-extra-semi
>   @item -Wextra-semi @r{(C++, Objective-C++ only)}
> -Warn about redundant semicolons after in-class function definitions.
> +Warn about redundant semicolons.  There are various contexts in which an extra
> +semicolon can occur.  One is a semicolon after in-class function definitions,
> +which is valid in all C++ dialects (and is never a pedwarn):
> +
> +@smallexample
> +struct S @{
> +  void foo () @{@};
> +@};
> +@end smallexample
> +
> +Another is an extra semicolon at namespace scope, which has been allowed
> +since C++11 (therefore is a pedwarn in C++98):
> +
> +@smallexample
> +struct S @{
> +@};
> +;
> +@end smallexample
> +
> +And yet another is an extra semicolon in class definitions, which has been
> +allowed since C++11 (therefore is a pedwarn in C++98):
> +
> +@smallexample
> +struct S @{
> +  int a;
> +  ;
> +@};
> +@end smallexample
>   
>   @opindex Wno-global-module
>   @opindex Wglobal-module
> diff --git a/gcc/testsuite/g++.dg/diagnostic/semicolon1.C b/gcc/testsuite/g++.dg/diagnostic/semicolon1.C
> new file mode 100644
> index 00000000000..8219d488f4d
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/diagnostic/semicolon1.C
> @@ -0,0 +1,18 @@
> +// DR 1693, Superfluous semicolons in class definitions
> +// PR c++/113760
> +// { dg-do compile }
> +// { dg-options "" }
> +// { dg-prune-output "only available with" }
> +
> +struct X { ; };
> +
> +struct S {
> +  void baz () = delete;
> +  void qux () = delete;
> +  ;
> +  void corge () = delete;
> +  ;
> +  ;
> +  int s;
> +  ;
> +};
> diff --git a/gcc/testsuite/g++.dg/diagnostic/semicolon10.C b/gcc/testsuite/g++.dg/diagnostic/semicolon10.C
> new file mode 100644
> index 00000000000..4753f557453
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/diagnostic/semicolon10.C
> @@ -0,0 +1,11 @@
> +// PR c++/113760
> +// { dg-do compile }
> +// { dg-options "-Wextra-semi" }
> +
> +struct S {
> +  void f1 () {}
> +  // A single semicolon is valid after a member function definition.
> +  void f2 () {}; // { dg-warning "extra .;. after in-class function definition" }
> +  void f3 () const;
> +};
> +void S::f3 () const { }; // { dg-warning "extra .;. outside of a function" }
> diff --git a/gcc/testsuite/g++.dg/diagnostic/semicolon11.C b/gcc/testsuite/g++.dg/diagnostic/semicolon11.C
> new file mode 100644
> index 00000000000..65df5e9abe6
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/diagnostic/semicolon11.C
> @@ -0,0 +1,29 @@
> +// DR 569, Spurious semicolons at namespace scope should be allowed
> +// PR c++/113760
> +// { dg-options "-pedantic-errors" }
> +
> +// C++11 allows extra semicolons at namespace scope.
> +struct S {
> +  void foo();
> +};
> +;			// { dg-error "extra .;. outside of a function" "" { target c++98_only } }
> +
> +void S::foo () {
> +};			// { dg-error "extra .;. outside of a function" "" { target c++98_only } }
> +;			// { dg-error "extra .;. outside of a function" "" { target c++98_only } }
> +
> +namespace N {
> +};			// { dg-error "extra .;. outside of a function" "" { target c++98_only } }
> +;			// { dg-error "extra .;. outside of a function" "" { target c++98_only } }
> +
> +void f();
> +;			// { dg-error "extra .;. outside of a function" "" { target c++98_only } }
> +
> +void
> +f ()
> +{
> +};			// { dg-error "extra .;. outside of a function" "" { target c++98_only } }
> +;			// { dg-error "extra .;. outside of a function" "" { target c++98_only } }
> +
> +int x;
> +;			// { dg-error "extra .;. outside of a function" "" { target c++98_only } }
> diff --git a/gcc/testsuite/g++.dg/diagnostic/semicolon12.C b/gcc/testsuite/g++.dg/diagnostic/semicolon12.C
> new file mode 100644
> index 00000000000..f3e79f80bf0
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/diagnostic/semicolon12.C
> @@ -0,0 +1,29 @@
> +// DR 569, Spurious semicolons at namespace scope should be allowed
> +// PR c++/113760
> +// { dg-options "" }
> +
> +// C++11 allows extra semicolons at namespace scope.
> +struct S {
> +  void foo();
> +};
> +;
> +
> +void S::foo () {
> +};
> +;
> +
> +namespace N {
> +};
> +;
> +
> +void f();
> +;
> +
> +void
> +f ()
> +{
> +};
> +;
> +
> +int x;
> +;
> diff --git a/gcc/testsuite/g++.dg/diagnostic/semicolon13.C b/gcc/testsuite/g++.dg/diagnostic/semicolon13.C
> new file mode 100644
> index 00000000000..c7a41202347
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/diagnostic/semicolon13.C
> @@ -0,0 +1,29 @@
> +// DR 569, Spurious semicolons at namespace scope should be allowed
> +// PR c++/113760
> +// { dg-options "-Wpedantic" }
> +
> +// C++11 allows extra semicolons at namespace scope.
> +struct S {
> +  void foo();
> +};
> +;			// { dg-warning "extra .;. outside of a function" "" { target c++98_only } }
> +
> +void S::foo () {
> +};			// { dg-warning "extra .;. outside of a function" "" { target c++98_only } }
> +;			// { dg-warning "extra .;. outside of a function" "" { target c++98_only } }
> +
> +namespace N {
> +};			// { dg-warning "extra .;. outside of a function" "" { target c++98_only } }
> +;			// { dg-warning "extra .;. outside of a function" "" { target c++98_only } }
> +
> +void f();
> +;			// { dg-warning "extra .;. outside of a function" "" { target c++98_only } }
> +
> +void
> +f ()
> +{
> +};			// { dg-warning "extra .;. outside of a function" "" { target c++98_only } }
> +;			// { dg-warning "extra .;. outside of a function" "" { target c++98_only } }
> +
> +int x;
> +;			// { dg-warning "extra .;. outside of a function" "" { target c++98_only } }
> diff --git a/gcc/testsuite/g++.dg/diagnostic/semicolon14.C b/gcc/testsuite/g++.dg/diagnostic/semicolon14.C
> new file mode 100644
> index 00000000000..ac2b985f3a3
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/diagnostic/semicolon14.C
> @@ -0,0 +1,29 @@
> +// DR 569, Spurious semicolons at namespace scope should be allowed
> +// PR c++/113760
> +// { dg-options "-pedantic-errors -Wno-extra-semi" }
> +
> +// C++11 allows extra semicolons at namespace scope.
> +struct S {
> +  void foo();
> +};
> +;
> +
> +void S::foo () {
> +};
> +;
> +
> +namespace N {
> +};
> +;
> +
> +void f();
> +;
> +
> +void
> +f ()
> +{
> +};
> +;
> +
> +int x;
> +;
> diff --git a/gcc/testsuite/g++.dg/diagnostic/semicolon15.C b/gcc/testsuite/g++.dg/diagnostic/semicolon15.C
> new file mode 100644
> index 00000000000..84b90e4ea35
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/diagnostic/semicolon15.C
> @@ -0,0 +1,29 @@
> +// DR 569, Spurious semicolons at namespace scope should be allowed
> +// PR c++/113760
> +// { dg-options "-Wextra-semi" }
> +
> +// C++11 allows extra semicolons at namespace scope.
> +struct S {
> +  void foo();
> +};
> +;			// { dg-warning "extra .;. outside of a function" }
> +
> +void S::foo () {
> +};			// { dg-warning "extra .;. outside of a function" }
> +;			// { dg-warning "extra .;. outside of a function" }
> +
> +namespace N {
> +};			// { dg-warning "extra .;. outside of a function" }
> +;			// { dg-warning "extra .;. outside of a function" }
> +
> +void f();
> +;			// { dg-warning "extra .;. outside of a function" }
> +
> +void
> +f ()
> +{
> +};			// { dg-warning "extra .;. outside of a function" }
> +;			// { dg-warning "extra .;. outside of a function" }
> +
> +int x;
> +;			// { dg-warning "extra .;. outside of a function" }
> diff --git a/gcc/testsuite/g++.dg/diagnostic/semicolon16.C b/gcc/testsuite/g++.dg/diagnostic/semicolon16.C
> new file mode 100644
> index 00000000000..26259cae552
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/diagnostic/semicolon16.C
> @@ -0,0 +1,38 @@
> +// DR 569, Spurious semicolons at namespace scope should be allowed
> +// PR c++/113760
> +// { dg-options "-Wextra-semi -pedantic-errors" }
> +
> +// C++11 allows extra semicolons at namespace scope.
> +struct S {
> +  void foo();
> +};
> +;			// { dg-warning "extra .;. outside of a function" "" { target c++11 } }
> +			// { dg-error "extra .;. outside of a function" "" { target c++98_only } .-1 }
> +
> +void S::foo () {
> +};			// { dg-warning "extra .;. outside of a function" "" { target c++11 } }
> +			// { dg-error "extra .;. outside of a function" "" { target c++98_only } .-1 }
> +;			// { dg-warning "extra .;. outside of a function" "" { target c++11 } }
> +			// { dg-error "extra .;. outside of a function" "" { target c++98_only } .-1 }
> +
> +namespace N {
> +};			// { dg-warning "extra .;. outside of a function" "" { target c++11 } }
> +			// { dg-error "extra .;. outside of a function" "" { target c++98_only } .-1 }
> +;			// { dg-warning "extra .;. outside of a function" "" { target c++11 } }
> +			// { dg-error "extra .;. outside of a function" "" { target c++98_only } .-1 }
> +
> +void f();
> +;			// { dg-warning "extra .;. outside of a function" "" { target c++11 } }
> +			// { dg-error "extra .;. outside of a function" "" { target c++98_only } .-1 }
> +
> +void
> +f ()
> +{
> +};			// { dg-warning "extra .;. outside of a function" "" { target c++11 } }
> +			// { dg-error "extra .;. outside of a function" "" { target c++98_only } .-1 }
> +;			// { dg-warning "extra .;. outside of a function" "" { target c++11 } }
> +			// { dg-error "extra .;. outside of a function" "" { target c++98_only } .-1 }
> +
> +int x;
> +;			// { dg-warning "extra .;. outside of a function" "" { target c++11 } }
> +			// { dg-error "extra .;. outside of a function" "" { target c++98_only } .-1 }
> diff --git a/gcc/testsuite/g++.dg/diagnostic/semicolon17.C b/gcc/testsuite/g++.dg/diagnostic/semicolon17.C
> new file mode 100644
> index 00000000000..0b8d3f006e5
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/diagnostic/semicolon17.C
> @@ -0,0 +1,29 @@
> +// DR 569, Spurious semicolons at namespace scope should be allowed
> +// PR c++/113760
> +// { dg-options "-pedantic-errors -Wno-error=extra-semi" }
> +
> +// C++11 allows extra semicolons at namespace scope.
> +struct S {
> +  void foo();
> +};
> +;			// { dg-warning "extra .;. outside of a function" "" { target c++98_only } }
> +
> +void S::foo () {
> +};			// { dg-warning "extra .;. outside of a function" "" { target c++98_only } }
> +;			// { dg-warning "extra .;. outside of a function" "" { target c++98_only } }
> +
> +namespace N {
> +};			// { dg-warning "extra .;. outside of a function" "" { target c++98_only } }
> +;			// { dg-warning "extra .;. outside of a function" "" { target c++98_only } }
> +
> +void f();
> +;			// { dg-warning "extra .;. outside of a function" "" { target c++98_only } }
> +
> +void
> +f ()
> +{
> +};			// { dg-warning "extra .;. outside of a function" "" { target c++98_only } }
> +;			// { dg-warning "extra .;. outside of a function" "" { target c++98_only } }
> +
> +int x;
> +;			// { dg-warning "extra .;. outside of a function" "" { target c++98_only } }
> diff --git a/gcc/testsuite/g++.dg/diagnostic/semicolon2.C b/gcc/testsuite/g++.dg/diagnostic/semicolon2.C
> new file mode 100644
> index 00000000000..1cfddf17e9d
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/diagnostic/semicolon2.C
> @@ -0,0 +1,18 @@
> +// DR 1693, Superfluous semicolons in class definitions
> +// PR c++/113760
> +// { dg-do compile }
> +// { dg-options "-Wpedantic" }
> +// { dg-prune-output "only available with" }
> +
> +struct X { ; };		      // { dg-warning "extra .;. inside a struct" "" { target c++98_only } }
> +
> +struct S {
> +  void baz () = delete;
> +  void qux () = delete;
> +  ;			      // { dg-warning "extra .;. inside a struct" "" { target c++98_only } }
> +  void corge () = delete;
> +  ;			      // { dg-warning "extra .;. inside a struct" "" { target c++98_only } }
> +  ;			      // { dg-warning "extra .;. inside a struct" "" { target c++98_only } }
> +  int s;
> +  ;			      // { dg-warning "extra .;. inside a struct" "" { target c++98_only } }
> +};
> diff --git a/gcc/testsuite/g++.dg/diagnostic/semicolon3.C b/gcc/testsuite/g++.dg/diagnostic/semicolon3.C
> new file mode 100644
> index 00000000000..265b655a8fb
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/diagnostic/semicolon3.C
> @@ -0,0 +1,18 @@
> +// DR 1693, Superfluous semicolons in class definitions
> +// PR c++/113760
> +// { dg-do compile }
> +// { dg-options "-pedantic-errors" }
> +// { dg-prune-output "only available with" }
> +
> +struct X { ; };		      // { dg-error "extra .;. inside a struct" "" { target c++98_only } }
> +
> +struct S {
> +  void baz () = delete;
> +  void qux () = delete;
> +  ;			      // { dg-error "extra .;. inside a struct" "" { target c++98_only } }
> +  void corge () = delete;
> +  ;			      // { dg-error "extra .;. inside a struct" "" { target c++98_only } }
> +  ;			      // { dg-error "extra .;. inside a struct" "" { target c++98_only } }
> +  int s;
> +  ;			      // { dg-error "extra .;. inside a struct" "" { target c++98_only } }
> +};
> diff --git a/gcc/testsuite/g++.dg/diagnostic/semicolon4.C b/gcc/testsuite/g++.dg/diagnostic/semicolon4.C
> new file mode 100644
> index 00000000000..22f7a539aed
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/diagnostic/semicolon4.C
> @@ -0,0 +1,18 @@
> +// DR 1693, Superfluous semicolons in class definitions
> +// PR c++/113760
> +// { dg-do compile }
> +// { dg-options "-pedantic-errors -Wno-extra-semi" }
> +// { dg-prune-output "only available with" }
> +
> +struct X { ; };
> +
> +struct S {
> +  void baz () = delete;
> +  void qux () = delete;
> +  ;
> +  void corge () = delete;
> +  ;
> +  ;
> +  int s;
> +  ;
> +};
> diff --git a/gcc/testsuite/g++.dg/diagnostic/semicolon5.C b/gcc/testsuite/g++.dg/diagnostic/semicolon5.C
> new file mode 100644
> index 00000000000..41b7bfa2ed8
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/diagnostic/semicolon5.C
> @@ -0,0 +1,18 @@
> +// DR 1693, Superfluous semicolons in class definitions
> +// PR c++/113760
> +// { dg-do compile }
> +// { dg-options "-Wextra-semi" }
> +// { dg-prune-output "only available with" }
> +
> +struct X { ; };		      // { dg-warning "extra .;. inside a struct" }
> +
> +struct S {
> +  void baz () = delete;
> +  void qux () = delete;
> +  ;			      // { dg-warning "extra .;. inside a struct" }
> +  void corge () = delete;
> +  ;			      // { dg-warning "extra .;. inside a struct" }
> +  ;			      // { dg-warning "extra .;. inside a struct" }
> +  int s;
> +  ;			      // { dg-warning "extra .;. inside a struct" }
> +};
> diff --git a/gcc/testsuite/g++.dg/diagnostic/semicolon6.C b/gcc/testsuite/g++.dg/diagnostic/semicolon6.C
> new file mode 100644
> index 00000000000..0c92a1e9523
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/diagnostic/semicolon6.C
> @@ -0,0 +1,23 @@
> +// DR 1693, Superfluous semicolons in class definitions
> +// PR c++/113760
> +// { dg-do compile }
> +// { dg-options "-Wextra-semi -pedantic-errors" }
> +// { dg-prune-output "only available with" }
> +
> +struct X { ; };		      // { dg-warning "extra .;. inside a struct" "" { target c++11 } }
> +			      // { dg-error "extra .;. inside a struct" "" { target c++98_only } .-1 }
> +
> +struct S {
> +  void baz () = delete;
> +  void qux () = delete;
> +  ;			      // { dg-warning "extra .;. inside a struct" "" { target c++11 } }
> +			      // { dg-error "extra .;. inside a struct" "" { target c++98_only } .-1 }
> +  void corge () = delete;
> +  ;			      // { dg-warning "extra .;. inside a struct" "" { target c++11 } }
> +			      // { dg-error "extra .;. inside a struct" "" { target c++98_only } .-1 }
> +  ;			      // { dg-warning "extra .;. inside a struct" "" { target c++11 } }
> +			      // { dg-error "extra .;. inside a struct" "" { target c++98_only } .-1 }
> +  int s;
> +  ;			      // { dg-warning "extra .;. inside a struct" "" { target c++11 } }
> +			      // { dg-error "extra .;. inside a struct" "" { target c++98_only } .-1 }
> +};
> diff --git a/gcc/testsuite/g++.dg/diagnostic/semicolon7.C b/gcc/testsuite/g++.dg/diagnostic/semicolon7.C
> new file mode 100644
> index 00000000000..aca4f49785d
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/diagnostic/semicolon7.C
> @@ -0,0 +1,18 @@
> +// DR 1693, Superfluous semicolons in class definitions
> +// PR c++/113760
> +// { dg-do compile }
> +// { dg-options "-pedantic-errors -Wno-error=extra-semi" }
> +// { dg-prune-output "only available with" }
> +
> +struct X { ; };		      // { dg-warning "extra .;. inside a struct" "" { target c++98_only } }
> +
> +struct S {
> +  void baz () = delete;
> +  void qux () = delete;
> +  ;			      // { dg-warning "extra .;. inside a struct" "" { target c++98_only } }
> +  void corge () = delete;
> +  ;			      // { dg-warning "extra .;. inside a struct" "" { target c++98_only } }
> +  ;			      // { dg-warning "extra .;. inside a struct" "" { target c++98_only } }
> +  int s;
> +  ;			      // { dg-warning "extra .;. inside a struct" "" { target c++98_only } }
> +};
> diff --git a/gcc/testsuite/g++.dg/diagnostic/semicolon8.C b/gcc/testsuite/g++.dg/diagnostic/semicolon8.C
> new file mode 100644
> index 00000000000..1b0de384dbf
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/diagnostic/semicolon8.C
> @@ -0,0 +1,11 @@
> +// PR c++/113760
> +// { dg-do compile }
> +// { dg-options "-pedantic-errors" }
> +
> +struct S {
> +  void f1 () {}
> +  // A single semicolon is valid after a member function definition.
> +  void f2 () {};
> +  void f3 () const;
> +};
> +void S::f3 () const { }; // { dg-error "extra .;. outside of a function" "" { target c++98_only } }
> diff --git a/gcc/testsuite/g++.dg/diagnostic/semicolon9.C b/gcc/testsuite/g++.dg/diagnostic/semicolon9.C
> new file mode 100644
> index 00000000000..f90539d3bd7
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/diagnostic/semicolon9.C
> @@ -0,0 +1,11 @@
> +// PR c++/113760
> +// { dg-do compile }
> +// { dg-options "-pedantic-errors -Wno-extra-semi" }
> +
> +struct S {
> +  void f1 () {}
> +  // A single semicolon is valid after a member function definition.
> +  void f2 () {};
> +  void f3 () const;
> +};
> +void S::f3 () const { };
> 
> base-commit: 54ba8d44bbd703bca6984700b4d6f978890097e2
diff mbox series

Patch

diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 403abc1f26e..fb34c3b7031 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -727,7 +727,7 @@  C ObjC C++ ObjC++ Warning
 ; in common.opt
 
 Wextra-semi
-C++ ObjC++ Var(warn_extra_semi) Warning
+C++ ObjC++ Var(warn_extra_semi) Init(-1) Warning
 Warn about semicolon after in-class function definition.
 
 Wflex-array-member-not-at-end
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 7306ce9a8a8..476ddc0d63a 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -15331,6 +15331,61 @@  cp_parser_module_export (cp_parser *parser)
   module_kind = mk;
 }
 
+/* Used for maybe_warn_extra_semi.  */
+
+enum class extra_semi_kind { decl, member, in_class_fn_def };
+
+/* Warn about an extra semicolon.  KIND says in which context the extra
+   semicolon occurs.  */
+
+static void
+maybe_warn_extra_semi (location_t loc, extra_semi_kind kind)
+{
+  /* -Wno-extra-semi suppresses all.  */
+  if (warn_extra_semi == 0)
+    return;
+
+  gcc_rich_location richloc (loc);
+  richloc.add_fixit_remove ();
+
+  switch (kind)
+    {
+    case extra_semi_kind::decl:
+      /* If -Wextra-semi wasn't specified, warn only when -pedantic is in
+	 effect in C++98.  DR 569 says that spurious semicolons at namespace
+	 scope should be allowed.  */
+      if (pedantic && cxx_dialect < cxx11)
+	pedwarn (&richloc, OPT_Wextra_semi,
+		 "extra %<;%> outside of a function only allowed in C++11");
+      else if (warn_extra_semi > 0)
+	warning_at (&richloc, OPT_Wextra_semi,
+		    "extra %<;%> outside of a function");
+      break;
+
+    case extra_semi_kind::member:
+      /* If -Wextra-semi wasn't specified, warn only when -pedantic is in
+	 effect in C++98.  DR 1693 added "empty-declaration" to the syntax for
+	 "member-declaration".  */
+      if (pedantic && cxx_dialect < cxx11)
+	pedwarn (&richloc, OPT_Wextra_semi,
+		 "extra %<;%> inside a struct only allowed in C++11");
+      else if (warn_extra_semi > 0)
+	warning_at (&richloc, OPT_Wextra_semi, "extra %<;%> inside a struct");
+      break;
+
+    case extra_semi_kind::in_class_fn_def:
+      /* A single semicolon is valid after a member function definition
+	 so this is just a warning.  */
+      if (warn_extra_semi > 0)
+	warning_at (&richloc, OPT_Wextra_semi,
+		    "extra %<;%> after in-class function definition");
+      break;
+
+    default:
+      gcc_unreachable ();
+    }
+}
+
 /* Declarations [gram.dcl.dcl] */
 
 /* Parse an optional declaration-sequence.  TOP_LEVEL is true, if this
@@ -15430,11 +15485,11 @@  cp_parser_declaration (cp_parser* parser, tree prefix_attrs)
 
   if (token1->type == CPP_SEMICOLON)
     {
-      cp_lexer_consume_token (parser->lexer);
+      location_t semicolon_loc
+	= cp_lexer_consume_token (parser->lexer)->location;
       /* A declaration consisting of a single semicolon is invalid
-       * before C++11.  Allow it unless we're being pedantic.  */
-      if (cxx_dialect < cxx11)
-	pedwarn (input_location, OPT_Wpedantic, "extra %<;%>");
+	 before C++11.  Allow it unless we're being pedantic.  */
+      maybe_warn_extra_semi (semicolon_loc, extra_semi_kind::decl);
       return;
     }
   else if (cp_lexer_nth_token_is (parser->lexer,
@@ -28121,19 +28176,25 @@  cp_parser_member_declaration (cp_parser* parser)
 
 	   struct S { ; };
 
-	 [class.mem]
+	 [class.mem] used to say
 
 	 Each member-declaration shall declare at least one member
-	 name of the class.  */
+	 name of the class.
+
+	 but since DR 1693:
+
+	 A member-declaration does not declare new members of the class
+	 if it is
+	 -- [...]
+	 -- an empty-declaration.
+	 For any other member-declaration, each declared entity that is not
+	 an unnamed bit-field is a member of the class, and each such
+	 member-declaration shall either declare at least one member name of
+	 the class or declare at least one unnamed bit-field.  */
       if (!decl_specifiers.any_specifiers_p)
 	{
 	  cp_token *token = cp_lexer_peek_token (parser->lexer);
-	  if (cxx_dialect < cxx11 && !in_system_header_at (token->location))
-	    {
-	      gcc_rich_location richloc (token->location);
-	      richloc.add_fixit_remove ();
-	      pedwarn (&richloc, OPT_Wpedantic, "extra %<;%>");
-	    }
+	  maybe_warn_extra_semi (token->location, extra_semi_kind::member);
 	}
       else
 	{
@@ -28564,11 +28625,8 @@  cp_parser_member_declaration (cp_parser* parser)
 		    {
 		      location_t semicolon_loc
 			= cp_lexer_consume_token (parser->lexer)->location;
-		      gcc_rich_location richloc (semicolon_loc);
-		      richloc.add_fixit_remove ();
-		      warning_at (&richloc, OPT_Wextra_semi,
-				  "extra %<;%> after in-class "
-				  "function definition");
+		      maybe_warn_extra_semi (semicolon_loc,
+					     extra_semi_kind::in_class_fn_def);
 		    }
 		  goto out;
 		}
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index ddcd5213f06..cedc1a28c83 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -4780,7 +4780,34 @@  undefined behavior at runtime.  This warning is enabled by default.
 @opindex Wextra-semi
 @opindex Wno-extra-semi
 @item -Wextra-semi @r{(C++, Objective-C++ only)}
-Warn about redundant semicolons after in-class function definitions.
+Warn about redundant semicolons.  There are various contexts in which an extra
+semicolon can occur.  One is a semicolon after in-class function definitions,
+which is valid in all C++ dialects (and is never a pedwarn):
+
+@smallexample
+struct S @{
+  void foo () @{@};
+@};
+@end smallexample
+
+Another is an extra semicolon at namespace scope, which has been allowed
+since C++11 (therefore is a pedwarn in C++98):
+
+@smallexample
+struct S @{
+@};
+;
+@end smallexample
+
+And yet another is an extra semicolon in class definitions, which has been
+allowed since C++11 (therefore is a pedwarn in C++98):
+
+@smallexample
+struct S @{
+  int a;
+  ;
+@};
+@end smallexample
 
 @opindex Wno-global-module
 @opindex Wglobal-module
diff --git a/gcc/testsuite/g++.dg/diagnostic/semicolon1.C b/gcc/testsuite/g++.dg/diagnostic/semicolon1.C
new file mode 100644
index 00000000000..8219d488f4d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/semicolon1.C
@@ -0,0 +1,18 @@ 
+// DR 1693, Superfluous semicolons in class definitions
+// PR c++/113760
+// { dg-do compile }
+// { dg-options "" }
+// { dg-prune-output "only available with" }
+
+struct X { ; };
+
+struct S {
+  void baz () = delete;
+  void qux () = delete;
+  ;
+  void corge () = delete;
+  ;
+  ;
+  int s;
+  ;
+};
diff --git a/gcc/testsuite/g++.dg/diagnostic/semicolon10.C b/gcc/testsuite/g++.dg/diagnostic/semicolon10.C
new file mode 100644
index 00000000000..4753f557453
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/semicolon10.C
@@ -0,0 +1,11 @@ 
+// PR c++/113760
+// { dg-do compile }
+// { dg-options "-Wextra-semi" }
+
+struct S {
+  void f1 () {}
+  // A single semicolon is valid after a member function definition.
+  void f2 () {}; // { dg-warning "extra .;. after in-class function definition" }
+  void f3 () const;
+};
+void S::f3 () const { }; // { dg-warning "extra .;. outside of a function" }
diff --git a/gcc/testsuite/g++.dg/diagnostic/semicolon11.C b/gcc/testsuite/g++.dg/diagnostic/semicolon11.C
new file mode 100644
index 00000000000..65df5e9abe6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/semicolon11.C
@@ -0,0 +1,29 @@ 
+// DR 569, Spurious semicolons at namespace scope should be allowed
+// PR c++/113760
+// { dg-options "-pedantic-errors" }
+
+// C++11 allows extra semicolons at namespace scope.
+struct S {
+  void foo();
+};
+;			// { dg-error "extra .;. outside of a function" "" { target c++98_only } }
+
+void S::foo () {
+};			// { dg-error "extra .;. outside of a function" "" { target c++98_only } }
+;			// { dg-error "extra .;. outside of a function" "" { target c++98_only } }
+
+namespace N {
+};			// { dg-error "extra .;. outside of a function" "" { target c++98_only } }
+;			// { dg-error "extra .;. outside of a function" "" { target c++98_only } }
+
+void f();
+;			// { dg-error "extra .;. outside of a function" "" { target c++98_only } }
+
+void
+f ()
+{
+};			// { dg-error "extra .;. outside of a function" "" { target c++98_only } }
+;			// { dg-error "extra .;. outside of a function" "" { target c++98_only } }
+
+int x;
+;			// { dg-error "extra .;. outside of a function" "" { target c++98_only } }
diff --git a/gcc/testsuite/g++.dg/diagnostic/semicolon12.C b/gcc/testsuite/g++.dg/diagnostic/semicolon12.C
new file mode 100644
index 00000000000..f3e79f80bf0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/semicolon12.C
@@ -0,0 +1,29 @@ 
+// DR 569, Spurious semicolons at namespace scope should be allowed
+// PR c++/113760
+// { dg-options "" }
+
+// C++11 allows extra semicolons at namespace scope.
+struct S {
+  void foo();
+};
+;
+
+void S::foo () {
+};
+;
+
+namespace N {
+};
+;
+
+void f();
+;
+
+void
+f ()
+{
+};
+;
+
+int x;
+;
diff --git a/gcc/testsuite/g++.dg/diagnostic/semicolon13.C b/gcc/testsuite/g++.dg/diagnostic/semicolon13.C
new file mode 100644
index 00000000000..c7a41202347
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/semicolon13.C
@@ -0,0 +1,29 @@ 
+// DR 569, Spurious semicolons at namespace scope should be allowed
+// PR c++/113760
+// { dg-options "-Wpedantic" }
+
+// C++11 allows extra semicolons at namespace scope.
+struct S {
+  void foo();
+};
+;			// { dg-warning "extra .;. outside of a function" "" { target c++98_only } }
+
+void S::foo () {
+};			// { dg-warning "extra .;. outside of a function" "" { target c++98_only } }
+;			// { dg-warning "extra .;. outside of a function" "" { target c++98_only } }
+
+namespace N {
+};			// { dg-warning "extra .;. outside of a function" "" { target c++98_only } }
+;			// { dg-warning "extra .;. outside of a function" "" { target c++98_only } }
+
+void f();
+;			// { dg-warning "extra .;. outside of a function" "" { target c++98_only } }
+
+void
+f ()
+{
+};			// { dg-warning "extra .;. outside of a function" "" { target c++98_only } }
+;			// { dg-warning "extra .;. outside of a function" "" { target c++98_only } }
+
+int x;
+;			// { dg-warning "extra .;. outside of a function" "" { target c++98_only } }
diff --git a/gcc/testsuite/g++.dg/diagnostic/semicolon14.C b/gcc/testsuite/g++.dg/diagnostic/semicolon14.C
new file mode 100644
index 00000000000..ac2b985f3a3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/semicolon14.C
@@ -0,0 +1,29 @@ 
+// DR 569, Spurious semicolons at namespace scope should be allowed
+// PR c++/113760
+// { dg-options "-pedantic-errors -Wno-extra-semi" }
+
+// C++11 allows extra semicolons at namespace scope.
+struct S {
+  void foo();
+};
+;
+
+void S::foo () {
+};
+;
+
+namespace N {
+};
+;
+
+void f();
+;
+
+void
+f ()
+{
+};
+;
+
+int x;
+;
diff --git a/gcc/testsuite/g++.dg/diagnostic/semicolon15.C b/gcc/testsuite/g++.dg/diagnostic/semicolon15.C
new file mode 100644
index 00000000000..84b90e4ea35
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/semicolon15.C
@@ -0,0 +1,29 @@ 
+// DR 569, Spurious semicolons at namespace scope should be allowed
+// PR c++/113760
+// { dg-options "-Wextra-semi" }
+
+// C++11 allows extra semicolons at namespace scope.
+struct S {
+  void foo();
+};
+;			// { dg-warning "extra .;. outside of a function" }
+
+void S::foo () {
+};			// { dg-warning "extra .;. outside of a function" }
+;			// { dg-warning "extra .;. outside of a function" }
+
+namespace N {
+};			// { dg-warning "extra .;. outside of a function" }
+;			// { dg-warning "extra .;. outside of a function" }
+
+void f();
+;			// { dg-warning "extra .;. outside of a function" }
+
+void
+f ()
+{
+};			// { dg-warning "extra .;. outside of a function" }
+;			// { dg-warning "extra .;. outside of a function" }
+
+int x;
+;			// { dg-warning "extra .;. outside of a function" }
diff --git a/gcc/testsuite/g++.dg/diagnostic/semicolon16.C b/gcc/testsuite/g++.dg/diagnostic/semicolon16.C
new file mode 100644
index 00000000000..26259cae552
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/semicolon16.C
@@ -0,0 +1,38 @@ 
+// DR 569, Spurious semicolons at namespace scope should be allowed
+// PR c++/113760
+// { dg-options "-Wextra-semi -pedantic-errors" }
+
+// C++11 allows extra semicolons at namespace scope.
+struct S {
+  void foo();
+};
+;			// { dg-warning "extra .;. outside of a function" "" { target c++11 } }
+			// { dg-error "extra .;. outside of a function" "" { target c++98_only } .-1 }
+
+void S::foo () {
+};			// { dg-warning "extra .;. outside of a function" "" { target c++11 } }
+			// { dg-error "extra .;. outside of a function" "" { target c++98_only } .-1 }
+;			// { dg-warning "extra .;. outside of a function" "" { target c++11 } }
+			// { dg-error "extra .;. outside of a function" "" { target c++98_only } .-1 }
+
+namespace N {
+};			// { dg-warning "extra .;. outside of a function" "" { target c++11 } }
+			// { dg-error "extra .;. outside of a function" "" { target c++98_only } .-1 }
+;			// { dg-warning "extra .;. outside of a function" "" { target c++11 } }
+			// { dg-error "extra .;. outside of a function" "" { target c++98_only } .-1 }
+
+void f();
+;			// { dg-warning "extra .;. outside of a function" "" { target c++11 } }
+			// { dg-error "extra .;. outside of a function" "" { target c++98_only } .-1 }
+
+void
+f ()
+{
+};			// { dg-warning "extra .;. outside of a function" "" { target c++11 } }
+			// { dg-error "extra .;. outside of a function" "" { target c++98_only } .-1 }
+;			// { dg-warning "extra .;. outside of a function" "" { target c++11 } }
+			// { dg-error "extra .;. outside of a function" "" { target c++98_only } .-1 }
+
+int x;
+;			// { dg-warning "extra .;. outside of a function" "" { target c++11 } }
+			// { dg-error "extra .;. outside of a function" "" { target c++98_only } .-1 }
diff --git a/gcc/testsuite/g++.dg/diagnostic/semicolon17.C b/gcc/testsuite/g++.dg/diagnostic/semicolon17.C
new file mode 100644
index 00000000000..0b8d3f006e5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/semicolon17.C
@@ -0,0 +1,29 @@ 
+// DR 569, Spurious semicolons at namespace scope should be allowed
+// PR c++/113760
+// { dg-options "-pedantic-errors -Wno-error=extra-semi" }
+
+// C++11 allows extra semicolons at namespace scope.
+struct S {
+  void foo();
+};
+;			// { dg-warning "extra .;. outside of a function" "" { target c++98_only } }
+
+void S::foo () {
+};			// { dg-warning "extra .;. outside of a function" "" { target c++98_only } }
+;			// { dg-warning "extra .;. outside of a function" "" { target c++98_only } }
+
+namespace N {
+};			// { dg-warning "extra .;. outside of a function" "" { target c++98_only } }
+;			// { dg-warning "extra .;. outside of a function" "" { target c++98_only } }
+
+void f();
+;			// { dg-warning "extra .;. outside of a function" "" { target c++98_only } }
+
+void
+f ()
+{
+};			// { dg-warning "extra .;. outside of a function" "" { target c++98_only } }
+;			// { dg-warning "extra .;. outside of a function" "" { target c++98_only } }
+
+int x;
+;			// { dg-warning "extra .;. outside of a function" "" { target c++98_only } }
diff --git a/gcc/testsuite/g++.dg/diagnostic/semicolon2.C b/gcc/testsuite/g++.dg/diagnostic/semicolon2.C
new file mode 100644
index 00000000000..1cfddf17e9d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/semicolon2.C
@@ -0,0 +1,18 @@ 
+// DR 1693, Superfluous semicolons in class definitions
+// PR c++/113760
+// { dg-do compile }
+// { dg-options "-Wpedantic" }
+// { dg-prune-output "only available with" }
+
+struct X { ; };		      // { dg-warning "extra .;. inside a struct" "" { target c++98_only } }
+
+struct S {
+  void baz () = delete;
+  void qux () = delete;
+  ;			      // { dg-warning "extra .;. inside a struct" "" { target c++98_only } }
+  void corge () = delete;
+  ;			      // { dg-warning "extra .;. inside a struct" "" { target c++98_only } }
+  ;			      // { dg-warning "extra .;. inside a struct" "" { target c++98_only } }
+  int s;
+  ;			      // { dg-warning "extra .;. inside a struct" "" { target c++98_only } }
+};
diff --git a/gcc/testsuite/g++.dg/diagnostic/semicolon3.C b/gcc/testsuite/g++.dg/diagnostic/semicolon3.C
new file mode 100644
index 00000000000..265b655a8fb
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/semicolon3.C
@@ -0,0 +1,18 @@ 
+// DR 1693, Superfluous semicolons in class definitions
+// PR c++/113760
+// { dg-do compile }
+// { dg-options "-pedantic-errors" }
+// { dg-prune-output "only available with" }
+
+struct X { ; };		      // { dg-error "extra .;. inside a struct" "" { target c++98_only } }
+
+struct S {
+  void baz () = delete;
+  void qux () = delete;
+  ;			      // { dg-error "extra .;. inside a struct" "" { target c++98_only } }
+  void corge () = delete;
+  ;			      // { dg-error "extra .;. inside a struct" "" { target c++98_only } }
+  ;			      // { dg-error "extra .;. inside a struct" "" { target c++98_only } }
+  int s;
+  ;			      // { dg-error "extra .;. inside a struct" "" { target c++98_only } }
+};
diff --git a/gcc/testsuite/g++.dg/diagnostic/semicolon4.C b/gcc/testsuite/g++.dg/diagnostic/semicolon4.C
new file mode 100644
index 00000000000..22f7a539aed
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/semicolon4.C
@@ -0,0 +1,18 @@ 
+// DR 1693, Superfluous semicolons in class definitions
+// PR c++/113760
+// { dg-do compile }
+// { dg-options "-pedantic-errors -Wno-extra-semi" }
+// { dg-prune-output "only available with" }
+
+struct X { ; };
+
+struct S {
+  void baz () = delete;
+  void qux () = delete;
+  ;
+  void corge () = delete;
+  ;
+  ;
+  int s;
+  ;
+};
diff --git a/gcc/testsuite/g++.dg/diagnostic/semicolon5.C b/gcc/testsuite/g++.dg/diagnostic/semicolon5.C
new file mode 100644
index 00000000000..41b7bfa2ed8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/semicolon5.C
@@ -0,0 +1,18 @@ 
+// DR 1693, Superfluous semicolons in class definitions
+// PR c++/113760
+// { dg-do compile }
+// { dg-options "-Wextra-semi" }
+// { dg-prune-output "only available with" }
+
+struct X { ; };		      // { dg-warning "extra .;. inside a struct" }
+
+struct S {
+  void baz () = delete;
+  void qux () = delete;
+  ;			      // { dg-warning "extra .;. inside a struct" }
+  void corge () = delete;
+  ;			      // { dg-warning "extra .;. inside a struct" }
+  ;			      // { dg-warning "extra .;. inside a struct" }
+  int s;
+  ;			      // { dg-warning "extra .;. inside a struct" }
+};
diff --git a/gcc/testsuite/g++.dg/diagnostic/semicolon6.C b/gcc/testsuite/g++.dg/diagnostic/semicolon6.C
new file mode 100644
index 00000000000..0c92a1e9523
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/semicolon6.C
@@ -0,0 +1,23 @@ 
+// DR 1693, Superfluous semicolons in class definitions
+// PR c++/113760
+// { dg-do compile }
+// { dg-options "-Wextra-semi -pedantic-errors" }
+// { dg-prune-output "only available with" }
+
+struct X { ; };		      // { dg-warning "extra .;. inside a struct" "" { target c++11 } }
+			      // { dg-error "extra .;. inside a struct" "" { target c++98_only } .-1 }
+
+struct S {
+  void baz () = delete;
+  void qux () = delete;
+  ;			      // { dg-warning "extra .;. inside a struct" "" { target c++11 } }
+			      // { dg-error "extra .;. inside a struct" "" { target c++98_only } .-1 }
+  void corge () = delete;
+  ;			      // { dg-warning "extra .;. inside a struct" "" { target c++11 } }
+			      // { dg-error "extra .;. inside a struct" "" { target c++98_only } .-1 }
+  ;			      // { dg-warning "extra .;. inside a struct" "" { target c++11 } }
+			      // { dg-error "extra .;. inside a struct" "" { target c++98_only } .-1 }
+  int s;
+  ;			      // { dg-warning "extra .;. inside a struct" "" { target c++11 } }
+			      // { dg-error "extra .;. inside a struct" "" { target c++98_only } .-1 }
+};
diff --git a/gcc/testsuite/g++.dg/diagnostic/semicolon7.C b/gcc/testsuite/g++.dg/diagnostic/semicolon7.C
new file mode 100644
index 00000000000..aca4f49785d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/semicolon7.C
@@ -0,0 +1,18 @@ 
+// DR 1693, Superfluous semicolons in class definitions
+// PR c++/113760
+// { dg-do compile }
+// { dg-options "-pedantic-errors -Wno-error=extra-semi" }
+// { dg-prune-output "only available with" }
+
+struct X { ; };		      // { dg-warning "extra .;. inside a struct" "" { target c++98_only } }
+
+struct S {
+  void baz () = delete;
+  void qux () = delete;
+  ;			      // { dg-warning "extra .;. inside a struct" "" { target c++98_only } }
+  void corge () = delete;
+  ;			      // { dg-warning "extra .;. inside a struct" "" { target c++98_only } }
+  ;			      // { dg-warning "extra .;. inside a struct" "" { target c++98_only } }
+  int s;
+  ;			      // { dg-warning "extra .;. inside a struct" "" { target c++98_only } }
+};
diff --git a/gcc/testsuite/g++.dg/diagnostic/semicolon8.C b/gcc/testsuite/g++.dg/diagnostic/semicolon8.C
new file mode 100644
index 00000000000..1b0de384dbf
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/semicolon8.C
@@ -0,0 +1,11 @@ 
+// PR c++/113760
+// { dg-do compile }
+// { dg-options "-pedantic-errors" }
+
+struct S {
+  void f1 () {}
+  // A single semicolon is valid after a member function definition.
+  void f2 () {};
+  void f3 () const;
+};
+void S::f3 () const { }; // { dg-error "extra .;. outside of a function" "" { target c++98_only } }
diff --git a/gcc/testsuite/g++.dg/diagnostic/semicolon9.C b/gcc/testsuite/g++.dg/diagnostic/semicolon9.C
new file mode 100644
index 00000000000..f90539d3bd7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/semicolon9.C
@@ -0,0 +1,11 @@ 
+// PR c++/113760
+// { dg-do compile }
+// { dg-options "-pedantic-errors -Wno-extra-semi" }
+
+struct S {
+  void f1 () {}
+  // A single semicolon is valid after a member function definition.
+  void f2 () {};
+  void f3 () const;
+};
+void S::f3 () const { };