Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,10 @@ C++17 Feature Support
Resolutions to C++ Defect Reports
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

- Implement DR1693 and DR3079 by allowing extra semicolons inside class member
declaration lists even in -pedantic mode. The warnings are still available
with -Wextra-semi. (#GH155538)

C Language Changes
------------------

Expand Down
14 changes: 11 additions & 3 deletions clang/include/clang/Basic/DiagnosticParseKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,12 @@ let CategoryName = "Parse Issue" in {
def ext_empty_translation_unit : Extension<
"ISO C requires a translation unit to contain at least one declaration">,
InGroup<DiagGroup<"empty-translation-unit">>;
def warn_cxx98_compat_top_level_semi : Warning<
"extra ';' outside of a function is incompatible with C++98">,
def warn_cxx98_compat_extra_semi : Warning<
"%select{|||multiple }0extra ';' %select{"
"outside of a function|"
"inside a %1|"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see a test for this diagnostic line.

"inside instance variable list|"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see a test for this diagnostic line.

"after member function definition}0 is incompatible with C++98">,
InGroup<CXX98CompatExtraSemi>, DefaultIgnore;
def ext_extra_semi : Extension<
"extra ';' %select{"
Expand All @@ -51,7 +55,11 @@ def ext_extra_semi : Extension<
"after member function definition}0">,
InGroup<ExtraSemi>;
def ext_extra_semi_cxx11 : Extension<
"extra ';' outside of a function is a C++11 extension">,
"%select{|||multiple }0extra ';' %select{"
"outside of a function|"
"inside a %1|"
"inside instance variable list|"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see a test for this diagnostic line.

"after member function definition}0 is a C++11 extension">,
InGroup<CXX11ExtraSemi>;
def warn_extra_semi_after_mem_fn_def : Warning<
"extra ';' after member function definition">,
Expand Down
35 changes: 24 additions & 11 deletions clang/lib/Parse/Parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -205,27 +205,40 @@ void Parser::ConsumeExtraSemi(ExtraSemiKind Kind, DeclSpec::TST TST) {
ConsumeToken();
}

if (Kind == ExtraSemiKind::AfterMemberFunctionDefinition &&
!HadMultipleSemis) {
// A single semicolon is valid after a member function definition.
Diag(StartLoc, diag::warn_extra_semi_after_mem_fn_def)
<< FixItHint::CreateRemoval(SourceRange(StartLoc, EndLoc));
return;
}

// C++11 allows extra semicolons at namespace scope, but not in any of the
// other contexts.
if (Kind == ExtraSemiKind::OutsideFunction && getLangOpts().CPlusPlus) {
// DR 1693 and DR 3079 extend this to class scope as well.
if ((Kind == ExtraSemiKind::OutsideFunction ||
Kind == ExtraSemiKind::InsideStruct ||
Kind == ExtraSemiKind::AfterMemberFunctionDefinition) &&
getLangOpts().CPlusPlus) {
if (getLangOpts().CPlusPlus11)
Diag(StartLoc, diag::warn_cxx98_compat_top_level_semi)
Diag(StartLoc, diag::warn_cxx98_compat_extra_semi)
<< Kind
<< DeclSpec::getSpecifierName(
TST, Actions.getASTContext().getPrintingPolicy())
Comment on lines +224 to +227
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use DiagCompat here @Sirraide ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The warning text is the same for both diagnostics here so that should work; in that case, the diagnostic definition in the TD file needs to be updated to use CXX11Compat (@keinflue grep for that if you’re unsure how to use it)

Copy link
Contributor Author

@keinflue keinflue Dec 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way to preserve the semantics of -Wc++11-extra-semi and -Wc++98-compat-extra-semi with this or what should happen with these flags?

Also, I think with -Wextra-semi given explicitly by the user, there should still be warnings. However, the diagnostic ext_extra_semi is currently an Extension (instead of Warning) for semantics in C. Is there a way to reuse it for this purpose or do I need to duplicate it again either way?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way to preserve the semantics of -Wc++11-extra-semi and -Wc++98-compat-extra-semi with this or what should happen with these flags?

Of course there are separate diagnostic groups for these... no in that case you can’t use CXXCompat for this; that might be the the reason why I didn’t migrate these warnings to use that

<< FixItHint::CreateRemoval(SourceRange(StartLoc, EndLoc));
else
Diag(StartLoc, diag::ext_extra_semi_cxx11)
<< Kind
<< DeclSpec::getSpecifierName(
TST, Actions.getASTContext().getPrintingPolicy())
<< FixItHint::CreateRemoval(SourceRange(StartLoc, EndLoc));
return;
}

if (Kind != ExtraSemiKind::AfterMemberFunctionDefinition || HadMultipleSemis)
Diag(StartLoc, diag::ext_extra_semi)
<< Kind
<< DeclSpec::getSpecifierName(
TST, Actions.getASTContext().getPrintingPolicy())
<< FixItHint::CreateRemoval(SourceRange(StartLoc, EndLoc));
else
// A single semicolon is valid after a member function definition.
Diag(StartLoc, diag::warn_extra_semi_after_mem_fn_def)
Diag(StartLoc, diag::ext_extra_semi)
<< Kind
<< DeclSpec::getSpecifierName(TST,
Actions.getASTContext().getPrintingPolicy())
<< FixItHint::CreateRemoval(SourceRange(StartLoc, EndLoc));
}

Expand Down
20 changes: 20 additions & 0 deletions clang/test/CXX/drs/cwg16xx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,26 @@ namespace cwg1692 { // cwg1692: 9
}
} // namespace cwg1692

namespace cwg1693 { // cwg1693: 22
namespace issue_example {
#if __cplusplus >= 201103L
struct A {
void f1() = delete;
void f2() = delete;;
void f3() = delete;;;
};
#endif
}
struct A {
; // cxx98-error{{extra ';' inside a struct is a C++11 extension}}
struct B {};; // cxx98-error{{extra ';' inside a struct is a C++11 extension}}
int a;; // cxx98-error{{extra ';' inside a struct is a C++11 extension}}
void f();; // cxx98-error{{extra ';' inside a struct is a C++11 extension}}
void h(){
};; // cxx98-error{{multiple extra ';' after member function definition is a C++11 extension}}
};
} // namespace cwg1693

namespace cwg1696 { // cwg1696: 7
namespace std_examples {
#if __cplusplus >= 201402L
Expand Down
7 changes: 6 additions & 1 deletion clang/test/CXX/drs/cwg30xx.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// RUN: %clang_cc1 -std=c++98 -pedantic-errors -verify=expected %s
// RUN: %clang_cc1 -std=c++98 -pedantic-errors -verify=expected,cxx98 %s
// RUN: %clang_cc1 -std=c++11 -pedantic-errors -verify=expected %s
// RUN: %clang_cc1 -std=c++14 -pedantic-errors -verify=expected %s
// RUN: %clang_cc1 -std=c++17 -pedantic-errors -verify=expected %s
Expand All @@ -20,4 +20,9 @@ void f(
// expected-note@#cwg3005-first-param {{previous declaration is here}}
}

namespace cwg3079 { // cwg3079: 22 ready 2025-08-27
struct A { union {int x;;} u; }; // cxx98-error{{extra ';' inside a union is a C++11 extension}}
struct B { union {int x;;}; }; // cxx98-error{{extra ';' inside a union is a C++11 extension}}
}

} // namespace cwg3005
11 changes: 8 additions & 3 deletions clang/test/Parser/cxx-class.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,16 @@ class C {
; // ok, one extra ';' is permitted
void m() {
int l = 2;
};; // expected-warning{{extra ';' after member function definition}}

};;
#if __cplusplus < 201103L
// expected-warning@-2{{extra ';' after member function definition is a C++11 extension}}
#endif
template<typename T> void mt(T) { }
;
; // expected-warning{{extra ';' inside a class}}
;
#if __cplusplus < 201103L
// expected-warning@-2{{extra ';' inside a class is a C++11 extension}}
#endif

virtual int vf() const volatile = 0;

Expand Down
46 changes: 44 additions & 2 deletions clang/test/Parser/cxx-extra-semi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,40 @@
// RUN: %clang_cc1 -x c++ -Wextra-semi -fixit %t
// RUN: %clang_cc1 -x c++ -Wextra-semi -Werror %t

#if __cplusplus >= 201103L && defined(PEDANTIC)
// In C++11 extra semicolons inside classes are allowed via defect reports.
// expected-no-diagnostics
class A {
void A1();
void A2() { };
void A2b() { };;
;
void A2c() { }
;
void A3() { }; ;;
;;;;;;;
;
; ;; ; ;;;
; ; ; ; ;;
void A4();

union {
;
int a;
;
};
};

union B {
int a1;
int a2;;
};

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also add some tests w/ =default/=delete/=0 here

;
; ;;

#else

class A {
void A1();
void A2() { };
Expand All @@ -13,19 +47,25 @@ class A {
// -pedantic is specified, since one semicolon is technically permitted.
// expected-warning@-4{{extra ';' after member function definition}}
#endif
void A2b() { };; // expected-warning{{extra ';' after member function definition}}
void A2b() { };; // expected-warning{{multiple extra ';' after member function definition}}
; // expected-warning{{extra ';' inside a class}}
void A2c() { }
;
#ifndef PEDANTIC
// expected-warning@-2{{extra ';' after member function definition}}
#endif
void A3() { }; ;; // expected-warning{{extra ';' after member function definition}}
void A3() { }; ;; // expected-warning{{multiple extra ';' after member function definition}}
;;;;;;; // expected-warning{{extra ';' inside a class}}
; // expected-warning{{extra ';' inside a class}}
; ;; ; ;;; // expected-warning{{extra ';' inside a class}}
; ; ; ; ;; // expected-warning{{extra ';' inside a class}}
void A4();

union {
; // expected-warning{{extra ';' inside a union}}
int a;
; // expected-warning{{extra ';' inside a union}}
};
};

union B {
Expand All @@ -42,3 +82,5 @@ union B {
// expected-warning@-6{{extra ';' outside of a function is incompatible with C++98}}
// expected-warning@-6{{extra ';' outside of a function is incompatible with C++98}}
#endif

#endif
9 changes: 0 additions & 9 deletions clang/test/Parser/cxx0x-decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,6 @@ namespace OpaqueEnumDecl {

int decltype(f())::*ptr_mem_decltype;

class ExtraSemiAfterMemFn {
// Due to a peculiarity in the C++11 grammar, a deleted or defaulted function
// is permitted to be followed by either one or two semicolons.
void f() = delete // expected-error {{expected ';' after delete}}
void g() = delete; // ok
void h() = delete;; // ok
void i() = delete;;; // expected-error {{extra ';' after member function definition}}
};

int *const const p = 0; // expected-error {{duplicate 'const' declaration specifier}}
const const int *q = 0; // expected-error {{duplicate 'const' declaration specifier}}

Expand Down