[llvm-branch-commits] [clang] release/23.x: [clang] Emit diagnostic for typedef+auto missed case in C++98/C23 (#210141) (PR #211391)

Tobias Hieta via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Thu Jul 23 07:20:25 PDT 2026


https://github.com/tru updated https://github.com/llvm/llvm-project/pull/211391

>From 01e4c77761c87d6ec873e445dff1b571e369031f Mon Sep 17 00:00:00 2001
From: Rahul <quic_rahulana at quicinc.com>
Date: Wed, 22 Jul 2026 20:10:58 +0530
Subject: [PATCH] [clang] Emit diagnostic for typedef+auto missed case in
 C++98/C23 (#210141)

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 add the check for tydef in the code handling auto.

(cherry picked from commit 34436db53d3e4ad36e86019109fb5ceee9bb4d8c)
---
 clang/lib/Sema/DeclSpec.cpp       | 36 ++++++++++++++++++++++---------
 clang/test/SemaCXX/auto-cxx98.cpp | 10 ++++++++-
 2 files changed, 35 insertions(+), 11 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();
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}}
+}



More information about the llvm-branch-commits mailing list