[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