mirror of
https://github.com/intel/llvm.git
synced 2026-01-14 20:10:50 +08:00
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
This commit is contained in:
@@ -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
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user