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 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits