[clang] [clang] Emit diagnostic for typedef+auto missed case in C++98/pre-C23 C (PR #210141)
via cfe-commits
cfe-commits at lists.llvm.org
Sun Jul 19 22:46:01 PDT 2026
https://github.com/rahulana-quic updated https://github.com/llvm/llvm-project/pull/210141
>From 62c7c85e99e7551ade93e3b6173e00b47d5019a6 Mon Sep 17 00:00:00 2001
From: Rahul Anand Radhakrishnan <quic_rahulana at quicinc.com>
Date: Thu, 16 Jul 2026 11:43:59 -0700
Subject: [PATCH 1/2] [clang] Add test for typedef+auto diagnostic in
C++98/pre-C23 C
---
clang/test/SemaCXX/auto-cxx98.cpp | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/clang/test/SemaCXX/auto-cxx98.cpp b/clang/test/SemaCXX/auto-cxx98.cpp
index 1e28d0635a48d..db2036d462532 100644
--- a/clang/test/SemaCXX/auto-cxx98.cpp
+++ b/clang/test/SemaCXX/auto-cxx98.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++98 -Wc++11-compat
+// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++98 -Wc++11-compat
void f() {
auto int a; // expected-warning {{'auto' storage class specifier is redundant and incompatible with C++11}}
int auto b; // expected-warning {{'auto' storage class specifier is redundant and incompatible with C++11}}
@@ -6,3 +6,11 @@ void f() {
static auto d = 0; // expected-warning {{C++11 extension}}
auto static e = 0; // expected-warning {{C++11 extension}}
}
+
+// typedef and auto storage-class-specifier cannot appear in the same
+// decl-specifier-seq ([dcl.stc] p1). This must be diagnosed in C++98 even
+// though 'auto int' (without typedef) is valid there.
+void g() {
+ typedef auto int t1; // expected-error {{cannot combine with previous 'typedef' declaration specifier}}
+ auto typedef int t2; // expected-error {{cannot combine with previous 'typedef' declaration specifier}}
+}
>From f27d290caa8bd53e6fdf77000faf113fb02b4199 Mon Sep 17 00:00:00 2001
From: Rahul Anand Radhakrishnan <quic_rahulana at quicinc.com>
Date: Thu, 16 Jul 2026 11:43:59 -0700
Subject: [PATCH 2/2] [clang] Emit diagnostic for typedef+auto missed case in
C++98/C23
CheckTypeSpec() converted 'auto' to a storage-class specifier without
checking whether 'typedef' was already set. [dcl.stc]p1 unconditionally
forbids typedef alongside any storage-class specifier regardless of C++
version.
This change adds the check for tydef in the code handling auto in the
C++98/C23 paths.
---
clang/lib/Sema/DeclSpec.cpp | 36 ++++++++++++++++++++++++++----------
1 file changed, 26 insertions(+), 10 deletions(-)
diff --git a/clang/lib/Sema/DeclSpec.cpp b/clang/lib/Sema/DeclSpec.cpp
index 4d20657d5e517..8751aab1c9b8d 100644
--- a/clang/lib/Sema/DeclSpec.cpp
+++ b/clang/lib/Sema/DeclSpec.cpp
@@ -1244,11 +1244,19 @@ void DeclSpec::CheckTypeSpec(Sema &S, const PrintingPolicy &Policy) {
(S.getLangOpts().CPlusPlus && !S.getLangOpts().CPlusPlus11)) {
// In C23 or C++98, convert 'auto' to storage class specifier
if (TypeSpecType == TST_auto) {
- // "auto int" case: Convert 'auto' to storage class specifier
- StorageClassSpec = SCS_auto;
- StorageClassSpecLoc = TSTLoc;
- TypeSpecType = ConflictingTypeSpecifier;
- TSTLoc = ConflictingTypeSpecifierLoc;
+ // "auto int" case: Convert 'auto' to storage class specifier.
+ // But typedef + any storage-class-specifier is unconditionally invalid
+ // per [dcl.stc]p1, regardless of C++ version.
+ if (StorageClassSpec == SCS_typedef) {
+ S.Diag(TSTLoc, diag::err_invalid_decl_spec_combination)
+ << "typedef" << FixItHint::CreateRemoval(TSTLoc);
+ TypeSpecType = TST_error;
+ } else {
+ StorageClassSpec = SCS_auto;
+ StorageClassSpecLoc = TSTLoc;
+ TypeSpecType = ConflictingTypeSpecifier;
+ TSTLoc = ConflictingTypeSpecifierLoc;
+ }
// Clear the conflict tracking
ConflictingTypeSpecifier = TST_unspecified;
ConflictingTypeSpecifierLoc = SourceLocation();
@@ -1273,11 +1281,19 @@ void DeclSpec::CheckTypeSpec(Sema &S, const PrintingPolicy &Policy) {
return;
}
// int auto (without constexpr): Convert 'auto' to storage class
- // specifier. No type conflict error - auto is treated as storage class,
- // not type specifier.
- StorageClassSpec = SCS_auto;
- StorageClassSpecLoc = ConflictingTypeSpecifierLoc;
- // TypeSpecType already has the correct type (e.g., TST_int)
+ // specifier. But typedef + any storage-class-specifier is
+ // unconditionally invalid per [dcl.stc]p1.
+ if (StorageClassSpec == SCS_typedef) {
+ S.Diag(ConflictingTypeSpecifierLoc,
+ diag::err_invalid_decl_spec_combination)
+ << "typedef"
+ << FixItHint::CreateRemoval(ConflictingTypeSpecifierLoc);
+ TypeSpecType = TST_error;
+ } else {
+ StorageClassSpec = SCS_auto;
+ StorageClassSpecLoc = ConflictingTypeSpecifierLoc;
+ // TypeSpecType already has the correct type (e.g., TST_int)
+ }
// Clear the conflict tracking
ConflictingTypeSpecifier = TST_unspecified;
ConflictingTypeSpecifierLoc = SourceLocation();
More information about the cfe-commits
mailing list