[clang] 3632e2f - Diagnose incorrect use of scoped enumerations in format strings

Aaron Ballman via cfe-commits cfe-commits at lists.llvm.org
Fri Jun 16 12:03:48 PDT 2023


Author: Aaron Ballman
Date: 2023-06-16T15:03:38-04:00
New Revision: 3632e2f5179a420ea8ab84e6ca33747ff6130fa2

URL: https://github.com/llvm/llvm-project/commit/3632e2f5179a420ea8ab84e6ca33747ff6130fa2
DIFF: https://github.com/llvm/llvm-project/commit/3632e2f5179a420ea8ab84e6ca33747ff6130fa2.diff

LOG: Diagnose incorrect use of scoped enumerations in format strings

Scoped enumerations in C++ do not undergo conversion to their
underlying type as part of default argument promotion, and so these
uses are UB. GCC correctly diagnoses them, and now Clang matches.

Fixes https://github.com/llvm/llvm-project/issues/38717

Added: 
    

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/lib/AST/FormatString.cpp
    clang/lib/Sema/SemaChecking.cpp
    clang/test/SemaCXX/format-strings.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 82e3b9d5de38b..2f6fcaf0a191f 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -513,6 +513,11 @@ Bug Fixes in This Version
   (`#50244 <https://github.com/llvm/llvm-project/issues/50244>_`).
 - Apply ``-fmacro-prefix-map`` to anonymous tags in template arguments
   (`#63219 <https://github.com/llvm/llvm-project/issues/63219>`_).
+- Clang now properly diagnoses format string mismatches involving scoped
+  enumeration types. A scoped enumeration type is not promoted to an integer
+  type by the default argument promotions, and thus this is UB. Clang's
+  behavior now matches GCC's behavior in C++.
+  (`#38717 <https://github.com/llvm/llvm-project/issues/38717>_`).
 
 Bug Fixes to Compiler Builtins
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

diff  --git a/clang/lib/AST/FormatString.cpp b/clang/lib/AST/FormatString.cpp
index d42e4ea2ea083..ad5af9508983f 100644
--- a/clang/lib/AST/FormatString.cpp
+++ b/clang/lib/AST/FormatString.cpp
@@ -351,10 +351,12 @@ ArgType::matchesType(ASTContext &C, QualType argTy) const {
     case AnyCharTy: {
       if (const auto *ETy = argTy->getAs<EnumType>()) {
         // If the enum is incomplete we know nothing about the underlying type.
-        // Assume that it's 'int'.
+        // Assume that it's 'int'. Do not use the underlying type for a scoped
+        // enumeration.
         if (!ETy->getDecl()->isComplete())
           return NoMatch;
-        argTy = ETy->getDecl()->getIntegerType();
+        if (ETy->isUnscopedEnumerationType())
+          argTy = ETy->getDecl()->getIntegerType();
       }
 
       if (const auto *BT = argTy->getAs<BuiltinType>()) {
@@ -391,10 +393,11 @@ ArgType::matchesType(ASTContext &C, QualType argTy) const {
     case SpecificTy: {
       if (const EnumType *ETy = argTy->getAs<EnumType>()) {
         // If the enum is incomplete we know nothing about the underlying type.
-        // Assume that it's 'int'.
+        // Assume that it's 'int'. Do not use the underlying type for a scoped
+        // enumeration as that needs an exact match.
         if (!ETy->getDecl()->isComplete())
           argTy = C.IntTy;
-        else
+        else if (ETy->isUnscopedEnumerationType())
           argTy = ETy->getDecl()->getIntegerType();
       }
       argTy = C.getCanonicalType(argTy).getUnqualifiedType();

diff  --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 55220138f94f5..34e1476d152e4 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -10564,11 +10564,15 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS,
       ImplicitMatch == ArgType::NoMatchTypeConfusion)
     Match = ImplicitMatch;
   assert(Match != ArgType::MatchPromotion);
-  // Look through enums to their underlying type.
+  // Look through unscoped enums to their underlying type.
   bool IsEnum = false;
   if (auto EnumTy = ExprTy->getAs<EnumType>()) {
-    ExprTy = EnumTy->getDecl()->getIntegerType();
-    IsEnum = true;
+    if (EnumTy->isUnscopedEnumerationType()) {
+      ExprTy = EnumTy->getDecl()->getIntegerType();
+      // This controls whether we're talking about the underlying type or not,
+      // which we only want to do when it's an unscoped enum.
+      IsEnum = true;
+    }
   }
 
   // %C in an Objective-C context prints a unichar, not a wchar_t.

diff  --git a/clang/test/SemaCXX/format-strings.cpp b/clang/test/SemaCXX/format-strings.cpp
index 2def986bdf9f6..f554e905d6455 100644
--- a/clang/test/SemaCXX/format-strings.cpp
+++ b/clang/test/SemaCXX/format-strings.cpp
@@ -213,4 +213,28 @@ void f() {
 
 
 }
+
+namespace ScopedEnumerations {
+enum class Scoped1 { One };
+enum class Scoped2 : unsigned short { Two };
+
+void f(Scoped1 S1, Scoped2 S2) {
+  printf("%hhd", S1); // expected-warning {{format specifies type 'char' but the argument has type 'Scoped1'}}
+  printf("%hd", S1);  // expected-warning {{format specifies type 'short' but the argument has type 'Scoped1'}}
+  printf("%d", S1);   // expected-warning {{format specifies type 'int' but the argument has type 'Scoped1'}}
+
+  printf("%hhd", S2); // expected-warning {{format specifies type 'char' but the argument has type 'Scoped2'}}
+  printf("%hd", S2);  // expected-warning {{format specifies type 'short' but the argument has type 'Scoped2'}}
+  printf("%d", S2);   // expected-warning {{format specifies type 'int' but the argument has type 'Scoped2'}}
+
+  scanf("%hhd", &S1); // expected-warning {{format specifies type 'char *' but the argument has type 'Scoped1 *'}}
+  scanf("%hd", &S1);  // expected-warning {{format specifies type 'short *' but the argument has type 'Scoped1 *'}}
+  scanf("%d", &S1);  // expected-warning {{format specifies type 'int *' but the argument has type 'Scoped1 *'}}
+
+  scanf("%hhd", &S2); // expected-warning {{format specifies type 'char *' but the argument has type 'Scoped2 *'}}
+  scanf("%hd", &S2);  // expected-warning {{format specifies type 'short *' but the argument has type 'Scoped2 *'}}
+  scanf("%d", &S2);  // expected-warning {{format specifies type 'int *' but the argument has type 'Scoped2 *'}}
+}
+}
+
 #endif


        


More information about the cfe-commits mailing list