[clang] [clang] Reject 'auto' storage class with type specifier in C++ (PR #166004)
Osama Abdelkader via cfe-commits
cfe-commits at lists.llvm.org
Sun Nov 2 05:34:16 PST 2025
https://github.com/osamakader updated https://github.com/llvm/llvm-project/pull/166004
>From 649ca4009961f3cc4ceae88fa8477f0d381f788f Mon Sep 17 00:00:00 2001
From: Osama Abdelkader <osama.abdelkader at gmail.com>
Date: Sat, 1 Nov 2025 19:53:55 +0200
Subject: [PATCH] [clang] Reject 'auto' storage class with type specifier in
C++
Previously, clang allowed 'auto int x = 1;' in C++ as an extension
(for C compatibility), emitting only a warning. This was confusing
since 'auto' in C++11+ is a type specifier, not a storage class.
This patch:
- Adds a new error diagnostic 'err_auto_type_specifier'
- Updates the parser to emit an error (instead of warning) when 'auto'
is used as a storage class with a type specifier in C++ mode
- Preserves C23 behavior where 'auto int' is valid
- Adds comprehensive tests
Fixes #164273
Signed-off-by: Osama Abdelkader <osama.abdelkader at gmail.com>
---
.../clang/Basic/DiagnosticParseKinds.td | 2 ++
clang/lib/Parse/ParseDecl.cpp | 21 ++++++++---
.../test/CXX/dcl.dcl/dcl.spec/dcl.stc/p2.cpp | 36 ++++++++++++-------
.../dcl.spec/dcl.type/dcl.spec.auto/p3-1y.cpp | 2 +-
.../dcl.spec/dcl.type/dcl.spec.auto/p3.cpp | 7 +++-
clang/test/CXX/drs/cwg3xx.cpp | 8 +++--
clang/test/Parser/cxx-auto-type-specifier.cpp | 22 ++++++++++++
clang/test/SemaCXX/auto-cxx0x.cpp | 2 +-
clang/test/SemaCXX/class.cpp | 7 ++--
clang/test/SemaCXX/static-data-member.cpp | 7 +++-
10 files changed, 86 insertions(+), 28 deletions(-)
create mode 100644 clang/test/Parser/cxx-auto-type-specifier.cpp
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index e5e071f43fa75..baf91b107b8c4 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -399,6 +399,8 @@ def err_requires_clause_on_declarator_not_declaring_a_function : Error<
"trailing requires clause can only be used when declaring a function">;
def err_requires_clause_inside_parens : Error<
"trailing requires clause should be placed outside parentheses">;
+def err_auto_type_specifier : Error<
+ "'auto' cannot be combined with a type specifier in C++">;
def ext_auto_storage_class : ExtWarn<
"'auto' storage class specifier is not permitted in C++11, and will not "
"be supported in future releases">, InGroup<DiagGroup<"auto-storage-class">>;
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 7e4a164e34eda..1ca182167e1e5 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -4095,11 +4095,22 @@ void Parser::ParseDeclarationSpecifiers(
case tok::kw_auto:
if (getLangOpts().CPlusPlus11 || getLangOpts().C23) {
if (isKnownToBeTypeSpecifier(GetLookAheadToken(1))) {
- isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_auto, Loc,
- PrevSpec, DiagID, Policy);
- if (!isInvalid && !getLangOpts().C23)
- Diag(Tok, diag::ext_auto_storage_class)
- << FixItHint::CreateRemoval(DS.getStorageClassSpecLoc());
+ // In C++ (not C23), 'auto' cannot be combined with a type specifier.
+ // However, OpenCL has its own error handling for this case.
+ if (getLangOpts().CPlusPlus && !getLangOpts().C23 &&
+ !getLangOpts().OpenCLCPlusPlus) {
+ // In C++11+, 'auto' cannot be combined with a type specifier.
+ // Don't set the storage class specifier to avoid Sema emitting a
+ // redundant error (GCC only emits one error for this case: "two or
+ // more data types in declaration").
+ isInvalid = true;
+ PrevSpec = "auto";
+ DiagID = diag::err_auto_type_specifier;
+ } else {
+ // C23 allows 'auto' as storage class with type specifier.
+ isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_auto, Loc,
+ PrevSpec, DiagID, Policy);
+ }
} else
isInvalid = DS.SetTypeSpecType(DeclSpec::TST_auto, Loc, PrevSpec,
DiagID, Policy);
diff --git a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.stc/p2.cpp b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.stc/p2.cpp
index 723a79628116c..da8732dc6e62c 100644
--- a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.stc/p2.cpp
+++ b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.stc/p2.cpp
@@ -5,14 +5,18 @@
// The auto or register specifiers can be applied only to names of objects
// declared in a block (6.3) or to function parameters (8.4).
-auto int ao; // expected-error {{illegal storage class on file-scoped variable}}
+auto int ao;
#if __cplusplus >= 201103L // C++11 or later
-// expected-warning at -2 {{'auto' storage class specifier is not permitted in C++11, and will not be supported in future releases}}
+// expected-error at -2 {{'auto' cannot be combined with a type specifier in C++}}
+#else
+// expected-error at -4 {{illegal storage class on file-scoped variable}}
#endif
-auto void af(); // expected-error {{illegal storage class on function}}
+auto void af();
#if __cplusplus >= 201103L // C++11 or later
-// expected-warning at -2 {{'auto' storage class specifier is not permitted in C++11, and will not be supported in future releases}}
+// expected-error at -2 {{'auto' cannot be combined with a type specifier in C++}}
+#else
+// expected-error at -4 {{illegal storage class on function}}
#endif
register int ro; // expected-error {{illegal storage class on file-scoped variable}}
@@ -25,13 +29,17 @@ register int ro; // expected-error {{illegal storage class on file-scoped variab
register void rf(); // expected-error {{illegal storage class on function}}
struct S {
- auto int ao; // expected-error {{storage class specified for a member declaration}}
+ auto int ao;
#if __cplusplus >= 201103L // C++11 or later
-// expected-warning at -2 {{'auto' storage class specifier is not permitted in C++11, and will not be supported in future releases}}
+// expected-error at -2 {{'auto' cannot be combined with a type specifier in C++}}
+#else
+// expected-error at -4 {{storage class specified for a member declaration}}
#endif
- auto void af(); // expected-error {{storage class specified for a member declaration}}
+ auto void af();
#if __cplusplus >= 201103L // C++11 or later
-// expected-warning at -2 {{'auto' storage class specifier is not permitted in C++11, and will not be supported in future releases}}
+// expected-error at -2 {{'auto' cannot be combined with a type specifier in C++}}
+#else
+// expected-error at -4 {{storage class specified for a member declaration}}
#endif
register int ro; // expected-error {{storage class specified for a member declaration}}
@@ -40,19 +48,21 @@ struct S {
void foo(auto int ap, register int rp) {
#if __cplusplus >= 201703L
-// expected-warning at -2 {{'auto' storage class specifier is not permitted in C++11, and will not be supported in future releases}}
+// expected-error at -2 {{'auto' cannot be combined with a type specifier in C++}}
// expected-error at -3 {{ISO C++17 does not allow 'register' storage class specifier}}
#elif __cplusplus >= 201103L
-// expected-warning at -5 {{'auto' storage class specifier is not permitted in C++11, and will not be supported in future releases}}
+// expected-error at -5 {{'auto' cannot be combined with a type specifier in C++}}
// expected-warning at -6 {{'register' storage class specifier is deprecated}}
#endif
auto int abo;
#if __cplusplus >= 201103L // C++11 or later
-// expected-warning at -2 {{'auto' storage class specifier is not permitted in C++11, and will not be supported in future releases}}
+// expected-error at -2 {{'auto' cannot be combined with a type specifier in C++}}
#endif
- auto void abf(); // expected-error {{illegal storage class on function}}
+ auto void abf();
#if __cplusplus >= 201103L // C++11 or later
-// expected-warning at -2 {{'auto' storage class specifier is not permitted in C++11, and will not be supported in future releases}}
+// expected-error at -2 {{'auto' cannot be combined with a type specifier in C++}}
+#else
+// expected-error at -4 {{illegal storage class on function}}
#endif
register int rbo;
diff --git a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3-1y.cpp b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3-1y.cpp
index e8f12156a4242..983119d6af8d0 100644
--- a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3-1y.cpp
+++ b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3-1y.cpp
@@ -56,7 +56,7 @@ namespace p3_example {
auto x = 5;
const auto *v = &x, u = 6;
static auto y = 0.0;
- auto int r; // expected-warning {{storage class}} expected-error {{file-scope}}
+ auto int r; // expected-error {{'auto' cannot be combined with a type specifier in C++}}
static_assert(is_same<decltype(x), int>(), "");
static_assert(is_same<decltype(v), const int*>(), "");
diff --git a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3.cpp b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3.cpp
index 440c78201293b..0a2688ee288ae 100644
--- a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3.cpp
+++ b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3.cpp
@@ -42,7 +42,12 @@ void p3example() {
static auto y = 0.0;
// In C++98: 'auto' storage class specifier is redundant and incompatible with C++0x
// In C++0x: 'auto' storage class specifier is not permitted in C++0x, and will not be supported in future releases
- auto int r; // expected-warning {{'auto' storage class specifier}}
+ auto int r;
+#if __cplusplus >= 201103L
+ // expected-error at -2 {{'auto' cannot be combined with a type specifier in C++}}
+#else
+ // expected-warning at -4 {{'auto' storage class specifier}}
+#endif
same<__typeof(x), int> xHasTypeInt;
same<__typeof(v), const int*> vHasTypeConstIntPtr;
diff --git a/clang/test/CXX/drs/cwg3xx.cpp b/clang/test/CXX/drs/cwg3xx.cpp
index bbd87c060801a..73b9d513bf3e2 100644
--- a/clang/test/CXX/drs/cwg3xx.cpp
+++ b/clang/test/CXX/drs/cwg3xx.cpp
@@ -1732,11 +1732,13 @@ namespace cwg395 { // cwg395: 3.0
namespace cwg396 { // cwg396: 3.0
void f() {
auto int a();
- // since-cxx11-error at -1 {{'auto' storage class specifier is not permitted in C++11, and will not be supported in future releases}}
- // expected-error at -2 {{illegal storage class on function}}
+ // since-cxx11-error at -1 {{'auto' cannot be combined with a type specifier in C++}}
+ // cxx98-error at -2 {{illegal storage class on function}}
+ // since-cxx11-warning at -3 {{empty parentheses interpreted as a function declaration}}
+ // since-cxx11-note at -4 {{replace parentheses with an initializer to declare a variable}}
int (i); // #cwg396-i
auto int (i);
- // since-cxx11-error at -1 {{'auto' storage class specifier is not permitted in C++11, and will not be supported in future releases}}
+ // since-cxx11-error at -1 {{'auto' cannot be combined with a type specifier in C++}}
// expected-error at -2 {{redefinition of 'i'}}
// expected-note@#cwg396-i {{previous definition is here}}
}
diff --git a/clang/test/Parser/cxx-auto-type-specifier.cpp b/clang/test/Parser/cxx-auto-type-specifier.cpp
new file mode 100644
index 0000000000000..f34195934cc85
--- /dev/null
+++ b/clang/test/Parser/cxx-auto-type-specifier.cpp
@@ -0,0 +1,22 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++14 %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++17 %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++23 %s
+
+// Test that 'auto' cannot be combined with a type specifier in C++.
+void f() {
+ auto int x = 1; // expected-error {{'auto' cannot be combined with a type specifier in C++}}
+ auto char c = 'a'; // expected-error {{'auto' cannot be combined with a type specifier in C++}}
+ auto float f = 1.0f; // expected-error {{'auto' cannot be combined with a type specifier in C++}}
+ auto double d = 1.0; // expected-error {{'auto' cannot be combined with a type specifier in C++}}
+ auto long l = 1L; // expected-error {{'auto' cannot be combined with a type specifier in C++}}
+}
+
+// Test that regular 'auto' (type deduction) still works in C++.
+void h() {
+ auto x = 1;
+ auto y = 2.0;
+ auto z = 'c';
+}
+
diff --git a/clang/test/SemaCXX/auto-cxx0x.cpp b/clang/test/SemaCXX/auto-cxx0x.cpp
index 07687b6066790..2662319b900cd 100644
--- a/clang/test/SemaCXX/auto-cxx0x.cpp
+++ b/clang/test/SemaCXX/auto-cxx0x.cpp
@@ -1,7 +1,7 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++11
// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++1y
void f() {
- auto int a; // expected-warning {{'auto' storage class specifier is not permitted in C++11, and will not be supported in future releases}}
+ auto int a; // expected-error {{'auto' cannot be combined with a type specifier in C++}}
int auto b; // expected-error{{cannot combine with previous 'int' declaration specifier}}
}
diff --git a/clang/test/SemaCXX/class.cpp b/clang/test/SemaCXX/class.cpp
index f1e02d5158aac..e0aa09dca82e4 100644
--- a/clang/test/SemaCXX/class.cpp
+++ b/clang/test/SemaCXX/class.cpp
@@ -2,11 +2,12 @@
// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx98 -Wc++11-compat %s -std=c++98
class C {
public:
- auto int errx; // expected-error {{storage class specified for a member declaration}}
+ auto int errx;
#if __cplusplus <= 199711L
- // expected-warning at -2 {{'auto' storage class specifier is redundant}}
+ // expected-error at -2 {{storage class specified for a member declaration}}
+ // expected-warning at -3 {{'auto' storage class specifier is redundant and incompatible with C++11}}
#else
- // expected-warning at -4 {{'auto' storage class specifier is not permitted in C++11, and will not be supported in future releases}}
+ // expected-error at -5 {{'auto' cannot be combined with a type specifier in C++}}
#endif
register int erry; // expected-error {{storage class specified for a member declaration}}
extern int errz; // expected-error {{storage class specified for a member declaration}}
diff --git a/clang/test/SemaCXX/static-data-member.cpp b/clang/test/SemaCXX/static-data-member.cpp
index fb63da9b40099..49c016dee892b 100644
--- a/clang/test/SemaCXX/static-data-member.cpp
+++ b/clang/test/SemaCXX/static-data-member.cpp
@@ -13,7 +13,12 @@ double ABC::a = 1.0;
extern double ABC::b = 1.0; // expected-error {{static data member definition cannot specify a storage class}}
static double ABC::c = 1.0; // expected-error {{'static' can only be specified inside the class definition}}
__private_extern__ double ABC::d = 1.0; // expected-error {{static data member definition cannot specify a storage class}}
-auto double ABC::e = 1.0; // expected-error {{static data member definition cannot specify a storage class}}
+auto double ABC::e = 1.0;
+#if __cplusplus >= 201103L
+// expected-error at -2 {{'auto' cannot be combined with a type specifier in C++}}
+#else
+// expected-error at -4 {{static data member definition cannot specify a storage class}}
+#endif
#if __cplusplus < 201703L
register double ABC::f = 1.0; // expected-error {{static data member definition cannot specify a storage class}}
#endif
More information about the cfe-commits
mailing list