[clang] [clang] Allow extra semicolons inside classes also in C++ pedantic mode. (PR #172209)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Dec 23 16:55:47 PST 2025
https://github.com/keinflue updated https://github.com/llvm/llvm-project/pull/172209
>From 06f7ef593726a505d9d08dfd6139a341d49cd9c2 Mon Sep 17 00:00:00 2001
From: keinflue <keinflue at posteo.de>
Date: Sun, 14 Dec 2025 13:15:03 +0100
Subject: [PATCH 1/3] [clang] Allow extra semicolons inside classes also in C++
pedantic mode.
With DR 1693 and DR 3079 C++ also allows empty declaration, i.e. extra
semicolons, inside class member declaration lists in all contexts.
This removes warnings for such extra semicolons from -pedantic but keeps them as
a C++98 compatibility warning as well as -Wextra-semi, analogously to extra
semicolons at namespace scope.
fixes #155538
---
clang/docs/ReleaseNotes.rst | 4 ++
.../clang/Basic/DiagnosticParseKinds.td | 14 ++++--
clang/lib/Parse/Parser.cpp | 35 +++++++++-----
clang/test/Parser/cxx-class.cpp | 11 +++--
clang/test/Parser/cxx-extra-semi.cpp | 46 ++++++++++++++++++-
clang/test/Parser/cxx0x-decl.cpp | 9 ----
6 files changed, 91 insertions(+), 28 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index feaf92ad4415f..1578713943665 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -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
------------------
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 442a90ec2472d..9ce9748762b65 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -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|"
+ "inside instance variable list|"
+ "after member function definition}0 is incompatible with C++98">,
InGroup<CXX98CompatExtraSemi>, DefaultIgnore;
def ext_extra_semi : Extension<
"extra ';' %select{"
@@ -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|"
+ "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">,
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index 7b425dd3dda43..6325238d6ef75 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -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())
<< 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));
}
diff --git a/clang/test/Parser/cxx-class.cpp b/clang/test/Parser/cxx-class.cpp
index a6757f39146a1..44665026c4fa0 100644
--- a/clang/test/Parser/cxx-class.cpp
+++ b/clang/test/Parser/cxx-class.cpp
@@ -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 at -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 at -2{{extra ';' inside a class is a C++11 extension}}
+#endif
virtual int vf() const volatile = 0;
diff --git a/clang/test/Parser/cxx-extra-semi.cpp b/clang/test/Parser/cxx-extra-semi.cpp
index de14bc0354d80..9bd69e18e9f02 100644
--- a/clang/test/Parser/cxx-extra-semi.cpp
+++ b/clang/test/Parser/cxx-extra-semi.cpp
@@ -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;;
+};
+
+;
+; ;;
+
+#else
+
class A {
void A1();
void A2() { };
@@ -13,19 +47,25 @@ class A {
// -pedantic is specified, since one semicolon is technically permitted.
// expected-warning at -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 at -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 {
@@ -42,3 +82,5 @@ union B {
// expected-warning at -6{{extra ';' outside of a function is incompatible with C++98}}
// expected-warning at -6{{extra ';' outside of a function is incompatible with C++98}}
#endif
+
+#endif
diff --git a/clang/test/Parser/cxx0x-decl.cpp b/clang/test/Parser/cxx0x-decl.cpp
index 69a8d8a46557d..fd56c487ecdfa 100644
--- a/clang/test/Parser/cxx0x-decl.cpp
+++ b/clang/test/Parser/cxx0x-decl.cpp
@@ -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}}
>From 02bfa1d1ce3911517410f4527f9b92a077619f0f Mon Sep 17 00:00:00 2001
From: keinflue <keinflue at posteo.de>
Date: Sun, 14 Dec 2025 17:47:23 +0100
Subject: [PATCH 2/3] Add DR test cases
---
clang/test/CXX/drs/cwg16xx.cpp | 20 ++++++++++++++++++++
clang/test/CXX/drs/cwg30xx.cpp | 7 ++++++-
2 files changed, 26 insertions(+), 1 deletion(-)
diff --git a/clang/test/CXX/drs/cwg16xx.cpp b/clang/test/CXX/drs/cwg16xx.cpp
index bd2c484344ddf..788496dc66570 100644
--- a/clang/test/CXX/drs/cwg16xx.cpp
+++ b/clang/test/CXX/drs/cwg16xx.cpp
@@ -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
diff --git a/clang/test/CXX/drs/cwg30xx.cpp b/clang/test/CXX/drs/cwg30xx.cpp
index 648ba9e78cd66..91b24fe9bebd5 100644
--- a/clang/test/CXX/drs/cwg30xx.cpp
+++ b/clang/test/CXX/drs/cwg30xx.cpp
@@ -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
@@ -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
>From 9f17a65a5108b93ca474aef3854da7fd9791802e Mon Sep 17 00:00:00 2001
From: keinflue <keinflue at posteo.de>
Date: Wed, 24 Dec 2025 01:52:17 +0100
Subject: [PATCH 3/3] Rework tests and improve =delete/=default diagnostics
---
clang/lib/Parse/ParseDeclCXX.cpp | 2 +-
clang/test/Parser/cxx-extra-semi.cpp | 112 +++++++++++++--------------
2 files changed, 55 insertions(+), 59 deletions(-)
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index d8ed7e3ff96bd..3efde00184dd9 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -3096,7 +3096,7 @@ Parser::DeclGroupPtrTy Parser::ParseCXXClassMemberDeclaration(
LateParsedAttrs.clear();
// Consume the ';' - it's optional unless we have a delete or default
- if (Tok.is(tok::semi))
+ if (Tok.is(tok::semi) && DefinitionKind != FunctionDefinitionKind::Defaulted && DefinitionKind != FunctionDefinitionKind::Deleted)
ConsumeExtraSemi(ExtraSemiKind::AfterMemberFunctionDefinition);
return DeclGroupPtrTy::make(DeclGroupRef(FunDecl));
diff --git a/clang/test/Parser/cxx-extra-semi.cpp b/clang/test/Parser/cxx-extra-semi.cpp
index 9bd69e18e9f02..5c7255da89c87 100644
--- a/clang/test/Parser/cxx-extra-semi.cpp
+++ b/clang/test/Parser/cxx-extra-semi.cpp
@@ -1,86 +1,82 @@
-// RUN: %clang_cc1 -fsyntax-only -pedantic -verify -DPEDANTIC %s
-// RUN: %clang_cc1 -fsyntax-only -Wextra-semi -verify %s
-// RUN: %clang_cc1 -fsyntax-only -Wextra-semi -verify -std=c++11 %s
+// RUN: %clang_cc1 -fsyntax-only -pedantic -verify=compat98 -std=c++98 %s
+// RUN: %clang_cc1 -fsyntax-only -pedantic -verify=cxx11_pedantic -std=c++11 %s
+// RUN: %clang_cc1 -fsyntax-only -Wextra-semi -verify=wextra,compat98 -std=c++98 %s
+// RUN: %clang_cc1 -fsyntax-only -Wextra-semi -verify=wextra,compat11 -std=c++11 %s
// RUN: cp %s %t
// 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
+// cxx11_pedantic-no-diagnostics
+
class A {
void A1();
void A2() { };
- void A2b() { };;
- ;
+ // This warning is only produced if we specify -Wextra-semi, and not if only
+ // -pedantic is specified, since one semicolon is technically permitted.
+ // wextra-warning at -3{{extra ';' after member function definition}}
+ void A2b() { };;
+ // compat98-warning at -1{{multiple extra ';' after member function definition is a C++11 extension}}
+ // compat11-warning at -2{{multiple extra ';' after member function definition is incompatible with C++98}}
+ ;
+ // compat98-warning at -1{{extra ';' inside a class is a C++11 extension}}
+ // compat11-warning at -2{{extra ';' inside a class is incompatible with C++98}}
void A2c() { }
+ ; // wextra-warning{{extra ';' after member function definition}}
+ void A3() { }; ;;
+ // compat98-warning at -1{{multiple extra ';' after member function definition is a C++11 extension}}
+ // compat11-warning at -2{{multiple extra ';' after member function definition is incompatible with C++98}}
+ ;;;;;;;
+ // compat98-warning at -1{{extra ';' inside a class is a C++11 extension}}
+ // compat11-warning at -2{{extra ';' inside a class is incompatible with C++98}}
;
- void A3() { }; ;;
- ;;;;;;;
- ;
- ; ;; ; ;;;
- ; ; ; ; ;;
+ // compat98-warning at -1{{extra ';' inside a class is a C++11 extension}}
+ // compat11-warning at -2{{extra ';' inside a class is incompatible with C++98}}
+ ; ;; ; ;;;
+ // compat98-warning at -1{{extra ';' inside a class is a C++11 extension}}
+ // compat11-warning at -2{{extra ';' inside a class is incompatible with C++98}}
+ ; ; ; ; ;;
+ // compat98-warning at -1{{extra ';' inside a class is a C++11 extension}}
+ // compat11-warning at -2{{extra ';' inside a class is incompatible with C++98}}
void A4();
-
+
union {
;
+ // compat98-warning at -1{{extra ';' inside a union is a C++11 extension}}
+ // compat11-warning at -2{{extra ';' inside a union is incompatible with C++98}}
int a;
;
+ // compat98-warning at -1{{extra ';' inside a union is a C++11 extension}}
+ // compat11-warning at -2{{extra ';' inside a union is incompatible with C++98}}
};
-};
-union B {
- int a1;
- int a2;;
-};
-
-;
-; ;;
+ virtual void f() = 0;
+ virtual void g() = 0;;
+ // compat11-warning at -1{{extra ';' inside a class is incompatible with C++98}}
+ // compat98-warning at -2{{extra ';' inside a class is a C++11 extension}}
-#else
-
-class A {
- void A1();
- void A2() { };
-#ifndef PEDANTIC
- // This warning is only produced if we specify -Wextra-semi, and not if only
- // -pedantic is specified, since one semicolon is technically permitted.
- // expected-warning at -4{{extra ';' after member function definition}}
-#endif
- void A2b() { };; // expected-warning{{multiple extra ';' after member function definition}}
- ; // expected-warning{{extra ';' inside a class}}
- void A2c() { }
- ;
-#ifndef PEDANTIC
- // expected-warning at -2{{extra ';' after member function definition}}
-#endif
- 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();
+#if __cplusplus >= 201103L
+ void h() = delete;
+ void i() = delete;; // wextra-warning{{extra ';' inside a class}}
+ void j() = delete;;; // wextra-warning{{extra ';' inside a class}}
- union {
- ; // expected-warning{{extra ';' inside a union}}
- int a;
- ; // expected-warning{{extra ';' inside a union}}
- };
+ A(const A&) = default;
+ A(A&&) = default;; // wextra-warning{{extra ';' inside a class}}
+ A(A&) = default;;; // wextra-warning{{extra ';' inside a class}}
+#endif
};
union B {
int a1;
- int a2;; // expected-warning{{extra ';' inside a union}}
+ int a2;;
+ // compat11-warning at -1{{extra ';' inside a union is incompatible with C++98}}
+ // compat98-warning at -2{{extra ';' inside a union is a C++11 extension}}
};
;
; ;;
-#if __cplusplus < 201103L
-// expected-warning at -3{{extra ';' outside of a function is a C++11 extension}}
-// expected-warning at -3{{extra ';' outside of a function is a C++11 extension}}
-#elif !defined(PEDANTIC)
-// expected-warning at -6{{extra ';' outside of a function is incompatible with C++98}}
-// expected-warning at -6{{extra ';' outside of a function is incompatible with C++98}}
-#endif
+// compat98-warning at -2{{extra ';' outside of a function is a C++11 extension}}
+// compat98-warning at -2{{extra ';' outside of a function is a C++11 extension}}
+// compat11-warning at -4{{extra ';' outside of a function is incompatible with C++98}}
+// compat11-warning at -4{{extra ';' outside of a function is incompatible with C++98}}
-#endif
More information about the cfe-commits
mailing list