[clang] [Clang] Add diagnostic when scoped enumeration requires an explicit conversion for binary operations (PR #152698)
Timothy Choi via cfe-commits
cfe-commits at lists.llvm.org
Sat Aug 9 08:19:20 PDT 2025
https://github.com/tinnamchoi updated https://github.com/llvm/llvm-project/pull/152698
>From 214a9541d4becd3dfae00cc33be5d35228455089 Mon Sep 17 00:00:00 2001
From: tinnamchoi <tinnam.choi at gmail.com>
Date: Fri, 8 Aug 2025 14:39:50 +0800
Subject: [PATCH 1/9] [Clang] Add diagnostic when scoped enumeration requires
an explicit conversion for binary operations
Fixes #24265
---
.../clang/Basic/DiagnosticSemaKinds.td | 5 +
clang/include/clang/Sema/Sema.h | 2 +-
clang/lib/Sema/SemaExpr.cpp | 112 ++++++++++++++----
3 files changed, 97 insertions(+), 22 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 9ce142e7b37cc..e69ab1b9893d9 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -4401,6 +4401,11 @@ def warn_impcast_different_enum_types : Warning<
def warn_impcast_int_to_enum : Warning<
"implicit conversion from %0 to enumeration type %1 is invalid in C++">,
InGroup<ImplicitIntToEnumCast>, DefaultIgnore;
+
+def note_no_implicit_conversion_for_scoped_enum
+ : Note<"no implicit conversion for scoped enum; consider casting to "
+ "underlying type">;
+
def warn_impcast_bool_to_null_pointer : Warning<
"initialization of pointer of type %0 to null from a constant boolean "
"expression">, InGroup<BoolConversion>;
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 5211373367677..3fc8f14eded85 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -8065,7 +8065,7 @@ class Sema final : public SemaBase {
BinaryOperatorKind Opc, QualType *CompLHSTy = nullptr);
QualType CheckSubtractionOperands( // C99 6.5.6
ExprResult &LHS, ExprResult &RHS, SourceLocation Loc,
- QualType *CompLHSTy = nullptr);
+ BinaryOperatorKind Opc, QualType *CompLHSTy = nullptr);
QualType CheckShiftOperands( // C99 6.5.7
ExprResult &LHS, ExprResult &RHS, SourceLocation Loc,
BinaryOperatorKind Opc, bool IsCompAssign = false);
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 6793d6da85cb1..87a6f448ba581 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -10742,6 +10742,45 @@ static void DiagnoseBadDivideOrRemainderValues(Sema& S, ExprResult &LHS,
<< IsDiv << RHS.get()->getSourceRange());
}
+static void diagnoseScopedEnums(Sema &S, const SourceLocation Loc,
+ const ExprResult &LHS, const ExprResult &RHS,
+ BinaryOperatorKind Opc) {
+ const Expr *LHSExpr = LHS.get();
+ const Expr *RHSExpr = RHS.get();
+ if (!LHSExpr || !RHSExpr)
+ return;
+ const QualType LHSType = LHSExpr->getType();
+ const QualType RHSType = RHSExpr->getType();
+ const bool LHSIsScoped = LHSType->isScopedEnumeralType();
+ const bool RHSIsScoped = RHSType->isScopedEnumeralType();
+ if (!LHSIsScoped && !RHSIsScoped)
+ return;
+ if (!LHSIsScoped && !LHSType->isIntegralOrUnscopedEnumerationType())
+ return;
+ if (!RHSIsScoped && !RHSType->isIntegralOrUnscopedEnumerationType())
+ return;
+ if (BinaryOperator::isAssignmentOp(Opc) && LHSIsScoped)
+ return;
+ if (LHSIsScoped) {
+ SourceLocation LHSBegin = LHSExpr->getBeginLoc();
+ QualType LHSIntType =
+ LHSType->castAs<EnumType>()->getDecl()->getIntegerType();
+ S.Diag(LHSBegin, diag::note_no_implicit_conversion_for_scoped_enum)
+ << FixItHint::CreateInsertion(
+ LHSBegin, "static_cast<" + LHSIntType.getAsString() + ">(")
+ << FixItHint::CreateInsertion(LHSExpr->getEndLoc(), ")");
+ }
+ if (RHSIsScoped) {
+ SourceLocation RHSBegin = RHSExpr->getBeginLoc();
+ QualType RHSIntType =
+ RHSType->castAs<EnumType>()->getDecl()->getIntegerType();
+ S.Diag(RHSBegin, diag::note_no_implicit_conversion_for_scoped_enum)
+ << FixItHint::CreateInsertion(
+ RHSBegin, "static_cast<" + RHSIntType.getAsString() + ">(")
+ << FixItHint::CreateInsertion(RHSExpr->getEndLoc(), ")");
+ }
+}
+
QualType Sema::CheckMultiplyDivideOperands(ExprResult &LHS, ExprResult &RHS,
SourceLocation Loc,
bool IsCompAssign, bool IsDiv) {
@@ -10772,9 +10811,14 @@ QualType Sema::CheckMultiplyDivideOperands(ExprResult &LHS, ExprResult &RHS,
if (LHS.isInvalid() || RHS.isInvalid())
return QualType();
-
- if (compType.isNull() || !compType->isArithmeticType())
- return InvalidOperands(Loc, LHS, RHS);
+ if (compType.isNull() || !compType->isArithmeticType()) {
+ InvalidOperands(Loc, LHS, RHS);
+ diagnoseScopedEnums(*this, Loc, LHS, RHS,
+ IsCompAssign ? IsDiv ? BO_DivAssign : BO_MulAssign
+ : IsDiv ? BO_Div
+ : BO_Mul);
+ return QualType();
+ }
if (IsDiv) {
DetectPrecisionLossInComplexDivision(*this, RHS.get()->getType(), Loc);
DiagnoseBadDivideOrRemainderValues(*this, LHS, RHS, Loc, IsDiv);
@@ -10837,8 +10881,12 @@ QualType Sema::CheckRemainderOperands(
if (compType.isNull() ||
(!compType->isIntegerType() &&
- !(getLangOpts().HLSL && compType->isFloatingType())))
- return InvalidOperands(Loc, LHS, RHS);
+ !(getLangOpts().HLSL && compType->isFloatingType()))) {
+ InvalidOperands(Loc, LHS, RHS);
+ diagnoseScopedEnums(*this, Loc, LHS, RHS,
+ IsCompAssign ? BO_RemAssign : BO_Rem);
+ return QualType();
+ }
DiagnoseBadDivideOrRemainderValues(*this, LHS, RHS, Loc, false /* IsDiv */);
return compType;
}
@@ -11194,7 +11242,9 @@ QualType Sema::CheckAdditionOperands(ExprResult &LHS, ExprResult &RHS,
} else if (PExp->getType()->isObjCObjectPointerType()) {
isObjCPointer = true;
} else {
- return InvalidOperands(Loc, LHS, RHS);
+ InvalidOperands(Loc, LHS, RHS);
+ diagnoseScopedEnums(*this, Loc, LHS, RHS, Opc);
+ return QualType();
}
}
assert(PExp->getType()->isAnyPointerType());
@@ -11251,7 +11301,8 @@ QualType Sema::CheckAdditionOperands(ExprResult &LHS, ExprResult &RHS,
// C99 6.5.6
QualType Sema::CheckSubtractionOperands(ExprResult &LHS, ExprResult &RHS,
SourceLocation Loc,
- QualType* CompLHSTy) {
+ BinaryOperatorKind Opc,
+ QualType *CompLHSTy) {
checkArithmeticNull(*this, LHS, RHS, Loc, /*IsCompare=*/false);
if (LHS.get()->getType()->isVectorType() ||
@@ -11396,7 +11447,9 @@ QualType Sema::CheckSubtractionOperands(ExprResult &LHS, ExprResult &RHS,
}
}
- return InvalidOperands(Loc, LHS, RHS);
+ InvalidOperands(Loc, LHS, RHS);
+ diagnoseScopedEnums(*this, Loc, LHS, RHS, Opc);
+ return QualType();
}
static bool isScopedEnumerationType(QualType T) {
@@ -11744,8 +11797,11 @@ QualType Sema::CheckShiftOperands(ExprResult &LHS, ExprResult &RHS,
// Embedded-C 4.1.6.2.2: The LHS may also be fixed-point.
if ((!LHSType->isFixedPointOrIntegerType() &&
!LHSType->hasIntegerRepresentation()) ||
- !RHSType->hasIntegerRepresentation())
- return InvalidOperands(Loc, LHS, RHS);
+ !RHSType->hasIntegerRepresentation()) {
+ InvalidOperands(Loc, LHS, RHS);
+ diagnoseScopedEnums(*this, Loc, LHS, RHS, Opc);
+ return QualType();
+ }
// C++0x: Don't allow scoped enums. FIXME: Use something better than
// hasIntegerRepresentation() above instead of this.
@@ -12311,8 +12367,11 @@ static QualType checkArithmeticOrEnumeralThreeWayCompare(Sema &S,
S.UsualArithmeticConversions(LHS, RHS, Loc, ArithConvKind::Comparison);
if (LHS.isInvalid() || RHS.isInvalid())
return QualType();
- if (Type.isNull())
- return S.InvalidOperands(Loc, LHS, RHS);
+ if (Type.isNull()) {
+ S.InvalidOperands(Loc, LHS, RHS);
+ diagnoseScopedEnums(S, Loc, LHS, RHS, BO_Cmp);
+ return QualType();
+ }
std::optional<ComparisonCategoryType> CCT =
getComparisonCategoryForBuiltinCmp(Type);
@@ -12344,8 +12403,11 @@ static QualType checkArithmeticOrEnumeralCompare(Sema &S, ExprResult &LHS,
S.UsualArithmeticConversions(LHS, RHS, Loc, ArithConvKind::Comparison);
if (LHS.isInvalid() || RHS.isInvalid())
return QualType();
- if (Type.isNull())
- return S.InvalidOperands(Loc, LHS, RHS);
+ if (Type.isNull()) {
+ S.InvalidOperands(Loc, LHS, RHS);
+ diagnoseScopedEnums(S, Loc, LHS, RHS, Opc);
+ return QualType();
+ }
assert(Type->isArithmeticType() || Type->isEnumeralType());
if (Type->isAnyComplexType() && BinaryOperator::isRelationalOp(Opc))
@@ -13355,7 +13417,9 @@ inline QualType Sema::CheckBitwiseOperands(ExprResult &LHS, ExprResult &RHS,
if (!compType.isNull() && compType->isIntegralOrUnscopedEnumerationType())
return compType;
- return InvalidOperands(Loc, LHS, RHS);
+ InvalidOperands(Loc, LHS, RHS);
+ diagnoseScopedEnums(*this, Loc, LHS, RHS, Opc);
+ return QualType();
}
// C99 6.5.[13,14]
@@ -13457,13 +13521,19 @@ inline QualType Sema::CheckLogicalOperands(ExprResult &LHS, ExprResult &RHS,
// C++ [expr.log.or]p1
// The operands are both contextually converted to type bool.
ExprResult LHSRes = PerformContextuallyConvertToBool(LHS.get());
- if (LHSRes.isInvalid())
- return InvalidOperands(Loc, LHS, RHS);
+ if (LHSRes.isInvalid()) {
+ InvalidOperands(Loc, LHS, RHS);
+ diagnoseScopedEnums(*this, Loc, LHS, RHS, Opc);
+ return QualType();
+ }
LHS = LHSRes;
ExprResult RHSRes = PerformContextuallyConvertToBool(RHS.get());
- if (RHSRes.isInvalid())
- return InvalidOperands(Loc, LHS, RHS);
+ if (RHSRes.isInvalid()) {
+ InvalidOperands(Loc, LHS, RHS);
+ diagnoseScopedEnums(*this, Loc, LHS, RHS, Opc);
+ return QualType();
+ }
RHS = RHSRes;
// C++ [expr.log.and]p2
@@ -15069,7 +15139,7 @@ ExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc,
break;
case BO_Sub:
ConvertHalfVec = true;
- ResultTy = CheckSubtractionOperands(LHS, RHS, OpLoc);
+ ResultTy = CheckSubtractionOperands(LHS, RHS, OpLoc, Opc);
break;
case BO_Shl:
case BO_Shr:
@@ -15136,7 +15206,7 @@ ExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc,
break;
case BO_SubAssign:
ConvertHalfVec = true;
- CompResultTy = CheckSubtractionOperands(LHS, RHS, OpLoc, &CompLHSTy);
+ CompResultTy = CheckSubtractionOperands(LHS, RHS, OpLoc, Opc, &CompLHSTy);
if (!CompResultTy.isNull() && !LHS.isInvalid() && !RHS.isInvalid())
ResultTy =
CheckAssignmentOperands(LHS.get(), RHS, OpLoc, CompResultTy, Opc);
>From ab9d87b3d294d6887cd686407dbe1c0df764cca4 Mon Sep 17 00:00:00 2001
From: tinnamchoi <tinnam.choi at gmail.com>
Date: Fri, 8 Aug 2025 15:04:43 +0800
Subject: [PATCH 2/9] [Clang] Update existing tests to reflect new diagnostics
for scoped enums
---
.../over/over.match/over.match.funcs/over.match.oper/p3.cpp | 1 +
clang/test/SemaCXX/enum-scoped.cpp | 3 +++
.../test/SemaCXX/opaque-enum-declaration-in-class-template.cpp | 2 ++
3 files changed, 6 insertions(+)
diff --git a/clang/test/CXX/over/over.match/over.match.funcs/over.match.oper/p3.cpp b/clang/test/CXX/over/over.match/over.match.funcs/over.match.oper/p3.cpp
index d88d5beb4e99e..7a0cacc2e65be 100644
--- a/clang/test/CXX/over/over.match/over.match.funcs/over.match.oper/p3.cpp
+++ b/clang/test/CXX/over/over.match/over.match.funcs/over.match.oper/p3.cpp
@@ -13,6 +13,7 @@ enum class E { e };
template<typename T> int f(T t) { return ~t; } // expected-error {{invalid argument type}}
template<typename T, typename U> int f(T t, U u) { return t % u; } // expected-error {{invalid operands to}}
+ // expected-note at -1 {{no implicit conversion for scoped enum}}
int b1 = ~E::e; // expected-error {{invalid argument type}}
int b2 = f(E::e); // expected-note {{in instantiation of}}
diff --git a/clang/test/SemaCXX/enum-scoped.cpp b/clang/test/SemaCXX/enum-scoped.cpp
index 2d7b3c9557ebd..103b3051c46c9 100644
--- a/clang/test/SemaCXX/enum-scoped.cpp
+++ b/clang/test/SemaCXX/enum-scoped.cpp
@@ -128,7 +128,10 @@ namespace rdar9366066 {
void f(X x) {
x % X::value; // expected-error{{invalid operands to binary expression ('X' and 'rdar9366066::X')}}
+ // expected-note at -1{{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // expected-note at -2{{no implicit conversion for scoped enum; consider casting to underlying type}}
x % 8; // expected-error{{invalid operands to binary expression ('X' and 'int')}}
+ // expected-note at -1{{no implicit conversion for scoped enum; consider casting to underlying type}}
}
}
diff --git a/clang/test/SemaCXX/opaque-enum-declaration-in-class-template.cpp b/clang/test/SemaCXX/opaque-enum-declaration-in-class-template.cpp
index 7101a153c6ebb..4257e53ce9af0 100644
--- a/clang/test/SemaCXX/opaque-enum-declaration-in-class-template.cpp
+++ b/clang/test/SemaCXX/opaque-enum-declaration-in-class-template.cpp
@@ -94,7 +94,9 @@ struct S5 {
};
int X5 = S5<char>::E1{} + '\0'; // expected-error{{invalid operands to binary expression}}
+ // expected-note at -1{{no implicit conversion for scoped enum; consider casting to underlying type}}
int Y5 = S5<char>::E2{} + '\0'; // expected-error{{invalid operands to binary expression}}
+ // expected-note at -1{{no implicit conversion for scoped enum; consider casting to underlying type}}
template <typename T>
>From 469f5594dc0a8262095842da798232fbf6354504 Mon Sep 17 00:00:00 2001
From: tinnamchoi <tinnam.choi at gmail.com>
Date: Fri, 8 Aug 2025 16:06:45 +0800
Subject: [PATCH 3/9] [Clang] Add tests for new diagnostics for scoped enums
---
clang/test/SemaCXX/enum-scoped.cpp | 102 +++++++++++++++++++++++++++++
1 file changed, 102 insertions(+)
diff --git a/clang/test/SemaCXX/enum-scoped.cpp b/clang/test/SemaCXX/enum-scoped.cpp
index 103b3051c46c9..091afff024b4c 100644
--- a/clang/test/SemaCXX/enum-scoped.cpp
+++ b/clang/test/SemaCXX/enum-scoped.cpp
@@ -1,5 +1,6 @@
// RUN: %clang_cc1 -fsyntax-only -pedantic -std=c++11 -verify -triple x86_64-apple-darwin %s
// RUN: %clang_cc1 -fsyntax-only -pedantic -std=c++17 -verify -triple x86_64-apple-darwin %s
+// RUN: %clang_cc1 -fsyntax-only -pedantic -std=c++20 -verify -triple x86_64-apple-darwin %s
enum class E1 {
Val1 = 1L
@@ -328,8 +329,10 @@ namespace PR18044 {
int E::*p; // expected-error {{does not point into a class}}
using E::f; // expected-error {{no member named 'f'}}
+ #if __cplusplus < 202002L
using E::a; // expected-warning {{using declaration naming a scoped enumerator is a C++20 extension}}
E b = a;
+ #endif
}
namespace test11 {
@@ -367,3 +370,102 @@ S<_Atomic(int)> s; // expected-warning {{'_Atomic' is a C11 extension}}
static_assert(__is_same(__underlying_type(S<_Atomic(long long)>::OhBoy), long long), ""); // expected-warning {{'_Atomic' is a C11 extension}}
// expected-note at -1 {{in instantiation of template class 'GH147736::S<_Atomic(long long)>' requested here}}
}
+
+namespace GH24265 {
+ enum class E_int { e };
+ enum class E_long : long { e };
+
+ void f() {
+ E_int::e + E_long::e; // expected-error {{invalid operands to binary expression ('GH24265::E_int' and 'GH24265::E_long')}}
+ // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // expected-note at -2 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ E_int::e + 0; // expected-error {{invalid operands to binary expression ('GH24265::E_int' and 'int')}}
+ // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+
+ 0 * E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
+ // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ 0 / E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
+ // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ 0 % E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
+ // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ 0 + E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
+ // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ 0 - E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
+ // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ 0 << E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
+ // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ 0 >> E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
+ // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+
+ #if __cplusplus >= 202002L
+ 0 <=> E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
+ // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ #endif
+
+ 0 < E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
+ // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ 0 > E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
+ // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ 0 <= E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
+ // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ 0 >= E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
+ // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ 0 == E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
+ // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ 0 != E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
+ // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ 0 & E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
+ // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ 0 ^ E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
+ // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ 0 | E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
+ // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ 0 && E_int::e; // expected-error {{value of type 'GH24265::E_int' is not contextually convertible to 'bool'}}
+ // expected-error at -1 {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
+ // expected-note at -2 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ 0 || E_int::e; // expected-error {{value of type 'GH24265::E_int' is not contextually convertible to 'bool'}}
+ // expected-error at -1 {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
+ // expected-note at -2 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+
+ int a;
+ a *= E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
+ // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ a /= E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
+ // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ a %= E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
+ // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ a += E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
+ // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ a -= E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
+ // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ a <<= E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
+ // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ a >>= E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
+ // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ a &= E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
+ // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ a ^= E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
+ // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ a |= E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
+ // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+
+ // TODO: These do not have the diagnostic yet
+ E_int b;
+ b *= 0; // expected-error {{invalid operands to binary expression ('E_int' and 'int')}}
+ b /= 0; // expected-error {{invalid operands to binary expression ('E_int' and 'int')}}
+ b %= 0; // expected-error {{invalid operands to binary expression ('E_int' and 'int')}}
+ b += 0; // expected-error {{invalid operands to binary expression ('E_int' and 'int')}}
+ b -= 0; // expected-error {{invalid operands to binary expression ('E_int' and 'int')}}
+ b <<= 0; // expected-error {{invalid operands to binary expression ('E_int' and 'int')}}
+ b >>= 0; // expected-error {{invalid operands to binary expression ('E_int' and 'int')}}
+ b &= 0; // expected-error {{invalid operands to binary expression ('E_int' and 'int')}}
+ b ^= 0; // expected-error {{invalid operands to binary expression ('E_int' and 'int')}}
+ b |= 0; // expected-error {{invalid operands to binary expression ('E_int' and 'int')}}
+
+ a = E_int::e; // expected-error {{assigning to 'int' from incompatible type 'GH24265::E_int'}}
+ b = 0; // expected-error {{assigning to 'E_int' from incompatible type 'int'}}
+
+ E_int c = 0; // expected-error {{cannot initialize a variable of type 'E_int' with an rvalue of type 'int'}}
+ int d = E_int::e; // expected-error {{cannot initialize a variable of type 'int' with an rvalue of type 'GH24265::E_int'}}
+ }
+}
>From 79d4d08bfab5c481557b1f010791af9e09dd8b1d Mon Sep 17 00:00:00 2001
From: tinnamchoi <tinnam.choi at gmail.com>
Date: Sat, 9 Aug 2025 16:15:40 +0800
Subject: [PATCH 4/9] [Clang] Remove unnecessary if block in scoped enum test
Block containing `<=>` kept since it didn't exist prior to C++20
---
clang/test/SemaCXX/enum-scoped.cpp | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/clang/test/SemaCXX/enum-scoped.cpp b/clang/test/SemaCXX/enum-scoped.cpp
index 091afff024b4c..584c93eabbb22 100644
--- a/clang/test/SemaCXX/enum-scoped.cpp
+++ b/clang/test/SemaCXX/enum-scoped.cpp
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -fsyntax-only -pedantic -std=c++11 -verify -triple x86_64-apple-darwin %s
-// RUN: %clang_cc1 -fsyntax-only -pedantic -std=c++17 -verify -triple x86_64-apple-darwin %s
+// RUN: %clang_cc1 -fsyntax-only -pedantic -std=c++11 -verify=expected,cxx11-17 -triple x86_64-apple-darwin %s
+// RUN: %clang_cc1 -fsyntax-only -pedantic -std=c++17 -verify=expected,cxx11-17 -triple x86_64-apple-darwin %s
// RUN: %clang_cc1 -fsyntax-only -pedantic -std=c++20 -verify -triple x86_64-apple-darwin %s
enum class E1 {
@@ -329,10 +329,8 @@ namespace PR18044 {
int E::*p; // expected-error {{does not point into a class}}
using E::f; // expected-error {{no member named 'f'}}
- #if __cplusplus < 202002L
- using E::a; // expected-warning {{using declaration naming a scoped enumerator is a C++20 extension}}
+ using E::a; // cxx11-17-warning {{using declaration naming a scoped enumerator is a C++20 extension}}
E b = a;
- #endif
}
namespace test11 {
>From e22ad8b07080ad0f2e51ab9f40511b0cb8389c15 Mon Sep 17 00:00:00 2001
From: tinnamchoi <tinnam.choi at gmail.com>
Date: Sat, 9 Aug 2025 17:26:19 +0800
Subject: [PATCH 5/9] [Clang] Add tests for `fix-it`s for scoped enums
---
clang/test/SemaCXX/enum-scoped.cpp | 67 +++++++++++++++++++++++++++++-
1 file changed, 66 insertions(+), 1 deletion(-)
diff --git a/clang/test/SemaCXX/enum-scoped.cpp b/clang/test/SemaCXX/enum-scoped.cpp
index 584c93eabbb22..49b328f833b9f 100644
--- a/clang/test/SemaCXX/enum-scoped.cpp
+++ b/clang/test/SemaCXX/enum-scoped.cpp
@@ -1,6 +1,7 @@
// RUN: %clang_cc1 -fsyntax-only -pedantic -std=c++11 -verify=expected,cxx11-17 -triple x86_64-apple-darwin %s
// RUN: %clang_cc1 -fsyntax-only -pedantic -std=c++17 -verify=expected,cxx11-17 -triple x86_64-apple-darwin %s
// RUN: %clang_cc1 -fsyntax-only -pedantic -std=c++20 -verify -triple x86_64-apple-darwin %s
+// RUN: not %clang_cc1 -fsyntax-only -fdiagnostics-parseable-fixits -std=c++20 -triple x86_64-apple-darwin %s 2>&1 | FileCheck %s
enum class E1 {
Val1 = 1L
@@ -376,76 +377,140 @@ namespace GH24265 {
void f() {
E_int::e + E_long::e; // expected-error {{invalid operands to binary expression ('GH24265::E_int' and 'GH24265::E_long')}}
// expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
- // expected-note at -2 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:5-[[@LINE-2]]:5}:"static_cast<int>("
+ // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:12-[[@LINE-3]]:12}:")"
+ // expected-note at -4 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CHECK: fix-it:{{.*}}:{[[@LINE-5]]:16-[[@LINE-5]]:16}:"static_cast<long>("
+ // CHECK: fix-it:{{.*}}:{[[@LINE-6]]:24-[[@LINE-6]]:24}:")"
E_int::e + 0; // expected-error {{invalid operands to binary expression ('GH24265::E_int' and 'int')}}
// expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:5-[[@LINE-2]]:5}:"static_cast<int>("
+ // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:12-[[@LINE-3]]:12}:")"
0 * E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
// expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:9-[[@LINE-2]]:9}:"static_cast<int>("
+ // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:16-[[@LINE-3]]:16}:")"
0 / E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
// expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:9-[[@LINE-2]]:9}:"static_cast<int>("
+ // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:16-[[@LINE-3]]:16}:")"
0 % E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
// expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:9-[[@LINE-2]]:9}:"static_cast<int>("
+ // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:16-[[@LINE-3]]:16}:")"
0 + E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
// expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:9-[[@LINE-2]]:9}:"static_cast<int>("
+ // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:16-[[@LINE-3]]:16}:")"
0 - E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
// expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:9-[[@LINE-2]]:9}:"static_cast<int>("
+ // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:16-[[@LINE-3]]:16}:")"
0 << E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
// expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:10-[[@LINE-2]]:10}:"static_cast<int>("
+ // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:17-[[@LINE-3]]:17}:")"
0 >> E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
// expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:10-[[@LINE-2]]:10}:"static_cast<int>("
+ // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:17-[[@LINE-3]]:17}:")"
#if __cplusplus >= 202002L
0 <=> E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
// expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:11-[[@LINE-2]]:11}:"static_cast<int>("
+ // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:18-[[@LINE-3]]:18}:")"
#endif
0 < E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
// expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:9-[[@LINE-2]]:9}:"static_cast<int>("
+ // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:16-[[@LINE-3]]:16}:")"
0 > E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
// expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:9-[[@LINE-2]]:9}:"static_cast<int>("
+ // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:16-[[@LINE-3]]:16}:")"
0 <= E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
// expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:10-[[@LINE-2]]:10}:"static_cast<int>("
+ // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:17-[[@LINE-3]]:17}:")"
0 >= E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
// expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:10-[[@LINE-2]]:10}:"static_cast<int>("
+ // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:17-[[@LINE-3]]:17}:")"
0 == E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
// expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:10-[[@LINE-2]]:10}:"static_cast<int>("
+ // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:17-[[@LINE-3]]:17}:")"
0 != E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
// expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:10-[[@LINE-2]]:10}:"static_cast<int>("
+ // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:17-[[@LINE-3]]:17}:")"
0 & E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
// expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:9-[[@LINE-2]]:9}:"static_cast<int>("
+ // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:16-[[@LINE-3]]:16}:")"
0 ^ E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
// expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:9-[[@LINE-2]]:9}:"static_cast<int>("
+ // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:16-[[@LINE-3]]:16}:")"
0 | E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
// expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:9-[[@LINE-2]]:9}:"static_cast<int>("
+ // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:16-[[@LINE-3]]:16}:")"
0 && E_int::e; // expected-error {{value of type 'GH24265::E_int' is not contextually convertible to 'bool'}}
// expected-error at -1 {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
// expected-note at -2 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:10-[[@LINE-3]]:10}:"static_cast<int>("
+ // CHECK: fix-it:{{.*}}:{[[@LINE-4]]:17-[[@LINE-4]]:17}:")"
0 || E_int::e; // expected-error {{value of type 'GH24265::E_int' is not contextually convertible to 'bool'}}
// expected-error at -1 {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
// expected-note at -2 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:10-[[@LINE-3]]:10}:"static_cast<int>("
+ // CHECK: fix-it:{{.*}}:{[[@LINE-4]]:17-[[@LINE-4]]:17}:")"
int a;
a *= E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
// expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:10-[[@LINE-2]]:10}:"static_cast<int>("
+ // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:17-[[@LINE-3]]:17}:")"
a /= E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
// expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:10-[[@LINE-2]]:10}:"static_cast<int>("
+ // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:17-[[@LINE-3]]:17}:")"
a %= E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
// expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:10-[[@LINE-2]]:10}:"static_cast<int>("
+ // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:17-[[@LINE-3]]:17}:")"
a += E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
// expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:10-[[@LINE-2]]:10}:"static_cast<int>("
+ // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:17-[[@LINE-3]]:17}:")"
a -= E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
// expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:10-[[@LINE-2]]:10}:"static_cast<int>("
+ // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:17-[[@LINE-3]]:17}:")"
a <<= E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
// expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:11-[[@LINE-2]]:11}:"static_cast<int>("
+ // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:18-[[@LINE-3]]:18}:")"
a >>= E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
// expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:11-[[@LINE-2]]:11}:"static_cast<int>("
+ // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:18-[[@LINE-3]]:18}:")"
a &= E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
// expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:10-[[@LINE-2]]:10}:"static_cast<int>("
+ // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:17-[[@LINE-3]]:17}:")"
a ^= E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
// expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:10-[[@LINE-2]]:10}:"static_cast<int>("
+ // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:17-[[@LINE-3]]:17}:")"
a |= E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
// expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:10-[[@LINE-2]]:10}:"static_cast<int>("
+ // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:17-[[@LINE-3]]:17}:")"
// TODO: These do not have the diagnostic yet
E_int b;
>From 76baf51ceaad8793697ae30a55467f9bba87c9e3 Mon Sep 17 00:00:00 2001
From: tinnamchoi <tinnam.choi at gmail.com>
Date: Sat, 9 Aug 2025 17:41:14 +0800
Subject: [PATCH 6/9] [Clang]: Refactor Sema::CheckMultiplyDivideOperands for
clarity
---
clang/include/clang/Sema/Sema.h | 4 ++--
clang/lib/Sema/SemaExpr.cpp | 17 +++++++----------
2 files changed, 9 insertions(+), 12 deletions(-)
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 3fc8f14eded85..dfa078651883b 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -8055,8 +8055,8 @@ class Sema final : public SemaBase {
ExprResult &RHS);
QualType CheckMultiplyDivideOperands( // C99 6.5.5
- ExprResult &LHS, ExprResult &RHS, SourceLocation Loc, bool IsCompAssign,
- bool IsDivide);
+ ExprResult &LHS, ExprResult &RHS, SourceLocation Loc,
+ BinaryOperatorKind Opc);
QualType CheckRemainderOperands( // C99 6.5.5
ExprResult &LHS, ExprResult &RHS, SourceLocation Loc,
bool IsCompAssign = false);
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 87a6f448ba581..3550ec9a27e98 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -10782,8 +10782,10 @@ static void diagnoseScopedEnums(Sema &S, const SourceLocation Loc,
}
QualType Sema::CheckMultiplyDivideOperands(ExprResult &LHS, ExprResult &RHS,
- SourceLocation Loc,
- bool IsCompAssign, bool IsDiv) {
+ SourceLocation Loc, BinaryOperatorKind Opc) {
+ bool IsCompAssign = Opc == BO_MulAssign || Opc == BO_DivAssign;
+ bool IsDiv = Opc == BO_Div || Opc == BO_DivAssign;
+
checkArithmeticNull(*this, LHS, RHS, Loc, /*IsCompare=*/false);
QualType LHSTy = LHS.get()->getType();
@@ -10813,10 +10815,7 @@ QualType Sema::CheckMultiplyDivideOperands(ExprResult &LHS, ExprResult &RHS,
if (compType.isNull() || !compType->isArithmeticType()) {
InvalidOperands(Loc, LHS, RHS);
- diagnoseScopedEnums(*this, Loc, LHS, RHS,
- IsCompAssign ? IsDiv ? BO_DivAssign : BO_MulAssign
- : IsDiv ? BO_Div
- : BO_Mul);
+ diagnoseScopedEnums(*this, Loc, LHS, RHS, Opc);
return QualType();
}
if (IsDiv) {
@@ -15127,8 +15126,7 @@ ExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc,
case BO_Mul:
case BO_Div:
ConvertHalfVec = true;
- ResultTy = CheckMultiplyDivideOperands(LHS, RHS, OpLoc, false,
- Opc == BO_Div);
+ ResultTy = CheckMultiplyDivideOperands(LHS, RHS, OpLoc, Opc);
break;
case BO_Rem:
ResultTy = CheckRemainderOperands(LHS, RHS, OpLoc);
@@ -15183,8 +15181,7 @@ ExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc,
case BO_MulAssign:
case BO_DivAssign:
ConvertHalfVec = true;
- CompResultTy = CheckMultiplyDivideOperands(LHS, RHS, OpLoc, true,
- Opc == BO_DivAssign);
+ CompResultTy = CheckMultiplyDivideOperands(LHS, RHS, OpLoc, Opc);
CompLHSTy = CompResultTy;
if (!CompResultTy.isNull() && !LHS.isInvalid() && !RHS.isInvalid())
ResultTy =
>From 824084e8abb12ac6d48548a41be41f0db3ea3463 Mon Sep 17 00:00:00 2001
From: tinnamchoi <tinnam.choi at gmail.com>
Date: Sat, 9 Aug 2025 18:59:33 +0800
Subject: [PATCH 7/9] [Clang] Suggest using `std::to_underlying` for invalid
scoped enum operations in C++23
---
.../clang/Basic/DiagnosticSemaKinds.td | 3 ++
clang/lib/Sema/SemaExpr.cpp | 31 ++++++++++---------
2 files changed, 20 insertions(+), 14 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index e69ab1b9893d9..d212924147871 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -4405,6 +4405,9 @@ def warn_impcast_int_to_enum : Warning<
def note_no_implicit_conversion_for_scoped_enum
: Note<"no implicit conversion for scoped enum; consider casting to "
"underlying type">;
+def note_no_implicit_conversion_for_scoped_enum_cxx23
+ : Note<"no implicit conversion for scoped enum; consider using "
+ "std::to_underlying">;
def warn_impcast_bool_to_null_pointer : Warning<
"initialization of pointer of type %0 to null from a constant boolean "
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 3550ec9a27e98..f056437d1124f 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -10761,23 +10761,26 @@ static void diagnoseScopedEnums(Sema &S, const SourceLocation Loc,
return;
if (BinaryOperator::isAssignmentOp(Opc) && LHSIsScoped)
return;
+ bool isCxx23 = S.getLangOpts().CPlusPlus23;
+ unsigned diagID =
+ isCxx23 ? diag::note_no_implicit_conversion_for_scoped_enum_cxx23
+ : diag::note_no_implicit_conversion_for_scoped_enum;
+ auto diagnosticHelper = [&S, isCxx23, diagID](const Expr *expr, const QualType type) {
+ SourceLocation beginLoc = expr->getBeginLoc();
+ QualType intType =
+ type->castAs<EnumType>()->getDecl()->getIntegerType();
+ std::string insertionString =
+ isCxx23 ? "std::to_underlying("
+ : "static_cast<" + intType.getAsString() + ">(";
+ S.Diag(beginLoc, diagID)
+ << FixItHint::CreateInsertion(beginLoc, insertionString)
+ << FixItHint::CreateInsertion(expr->getEndLoc(), ")");
+ };
if (LHSIsScoped) {
- SourceLocation LHSBegin = LHSExpr->getBeginLoc();
- QualType LHSIntType =
- LHSType->castAs<EnumType>()->getDecl()->getIntegerType();
- S.Diag(LHSBegin, diag::note_no_implicit_conversion_for_scoped_enum)
- << FixItHint::CreateInsertion(
- LHSBegin, "static_cast<" + LHSIntType.getAsString() + ">(")
- << FixItHint::CreateInsertion(LHSExpr->getEndLoc(), ")");
+ diagnosticHelper(LHSExpr, LHSType);
}
if (RHSIsScoped) {
- SourceLocation RHSBegin = RHSExpr->getBeginLoc();
- QualType RHSIntType =
- RHSType->castAs<EnumType>()->getDecl()->getIntegerType();
- S.Diag(RHSBegin, diag::note_no_implicit_conversion_for_scoped_enum)
- << FixItHint::CreateInsertion(
- RHSBegin, "static_cast<" + RHSIntType.getAsString() + ">(")
- << FixItHint::CreateInsertion(RHSExpr->getEndLoc(), ")");
+ diagnosticHelper(RHSExpr, RHSType);
}
}
>From 6193ea0857a783a5003f9f4936df4f4f21b429f1 Mon Sep 17 00:00:00 2001
From: tinnamchoi <tinnam.choi at gmail.com>
Date: Sat, 9 Aug 2025 21:52:55 +0800
Subject: [PATCH 8/9] [Clang] Add tests for diagnostics for scoped enums in
C++23
---
clang/test/SemaCXX/enum-scoped.cpp | 307 +++++++++++++++++++----------
1 file changed, 204 insertions(+), 103 deletions(-)
diff --git a/clang/test/SemaCXX/enum-scoped.cpp b/clang/test/SemaCXX/enum-scoped.cpp
index 49b328f833b9f..fe327ab28f9af 100644
--- a/clang/test/SemaCXX/enum-scoped.cpp
+++ b/clang/test/SemaCXX/enum-scoped.cpp
@@ -1,7 +1,9 @@
-// RUN: %clang_cc1 -fsyntax-only -pedantic -std=c++11 -verify=expected,cxx11-17 -triple x86_64-apple-darwin %s
-// RUN: %clang_cc1 -fsyntax-only -pedantic -std=c++17 -verify=expected,cxx11-17 -triple x86_64-apple-darwin %s
-// RUN: %clang_cc1 -fsyntax-only -pedantic -std=c++20 -verify -triple x86_64-apple-darwin %s
-// RUN: not %clang_cc1 -fsyntax-only -fdiagnostics-parseable-fixits -std=c++20 -triple x86_64-apple-darwin %s 2>&1 | FileCheck %s
+// RUN: %clang_cc1 -fsyntax-only -pedantic -std=c++11 -verify=expected,cxx11-17,cxx11-20 -triple x86_64-apple-darwin %s
+// RUN: %clang_cc1 -fsyntax-only -pedantic -std=c++17 -verify=expected,cxx11-17,cxx11-20 -triple x86_64-apple-darwin %s
+// RUN: %clang_cc1 -fsyntax-only -pedantic -std=c++20 -verify=expected,cxx11-20 -triple x86_64-apple-darwin %s
+// RUN: %clang_cc1 -fsyntax-only -pedantic -std=c++23 -verify=expected,cxx23 -triple x86_64-apple-darwin %s
+// RUN: not %clang_cc1 -fsyntax-only -fdiagnostics-parseable-fixits -std=c++20 -triple x86_64-apple-darwin %s 2>&1 | FileCheck %s --check-prefix=CXX20
+// RUN: not %clang_cc1 -fsyntax-only -fdiagnostics-parseable-fixits -std=c++23 -triple x86_64-apple-darwin %s 2>&1 | FileCheck %s --check-prefix=CXX23
enum class E1 {
Val1 = 1L
@@ -130,10 +132,13 @@ namespace rdar9366066 {
void f(X x) {
x % X::value; // expected-error{{invalid operands to binary expression ('X' and 'rdar9366066::X')}}
- // expected-note at -1{{no implicit conversion for scoped enum; consider casting to underlying type}}
- // expected-note at -2{{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // cxx11-20-note at -1{{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // cxx23-note at -2{{no implicit conversion for scoped enum; consider using std::to_underlying}}
+ // cxx11-20-note at -3{{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // cxx23-note at -4{{no implicit conversion for scoped enum; consider using std::to_underlying}}
x % 8; // expected-error{{invalid operands to binary expression ('X' and 'int')}}
- // expected-note at -1{{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // cxx11-20-note at -1{{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // cxx23-note at -2{{no implicit conversion for scoped enum; consider using std::to_underlying}}
}
}
@@ -376,141 +381,237 @@ namespace GH24265 {
void f() {
E_int::e + E_long::e; // expected-error {{invalid operands to binary expression ('GH24265::E_int' and 'GH24265::E_long')}}
- // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
- // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:5-[[@LINE-2]]:5}:"static_cast<int>("
- // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:12-[[@LINE-3]]:12}:")"
- // expected-note at -4 {{no implicit conversion for scoped enum; consider casting to underlying type}}
- // CHECK: fix-it:{{.*}}:{[[@LINE-5]]:16-[[@LINE-5]]:16}:"static_cast<long>("
- // CHECK: fix-it:{{.*}}:{[[@LINE-6]]:24-[[@LINE-6]]:24}:")"
+ // cxx11-20-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CXX20: fix-it:{{.*}}:{[[@LINE-2]]:5-[[@LINE-2]]:5}:"static_cast<int>("
+ // CXX20: fix-it:{{.*}}:{[[@LINE-3]]:12-[[@LINE-3]]:12}:")"
+ // cxx23-note at -4 {{no implicit conversion for scoped enum; consider using std::to_underlying}}
+ // CXX23: fix-it:{{.*}}:{[[@LINE-5]]:5-[[@LINE-5]]:5}:"std::to_underlying("
+ // CXX23: fix-it:{{.*}}:{[[@LINE-6]]:12-[[@LINE-6]]:12}:")"
+ // cxx11-20-note at -7 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CXX20: fix-it:{{.*}}:{[[@LINE-8]]:16-[[@LINE-8]]:16}:"static_cast<long>("
+ // CXX20: fix-it:{{.*}}:{[[@LINE-9]]:24-[[@LINE-9]]:24}:")"
+ // cxx23-note at -10 {{no implicit conversion for scoped enum; consider using std::to_underlying}}
+ // CXX23: fix-it:{{.*}}:{[[@LINE-11]]:16-[[@LINE-11]]:16}:"std::to_underlying("
+ // CXX23: fix-it:{{.*}}:{[[@LINE-12]]:24-[[@LINE-12]]:24}:")"
E_int::e + 0; // expected-error {{invalid operands to binary expression ('GH24265::E_int' and 'int')}}
- // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
- // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:5-[[@LINE-2]]:5}:"static_cast<int>("
- // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:12-[[@LINE-3]]:12}:")"
+ // cxx11-20-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CXX20: fix-it:{{.*}}:{[[@LINE-2]]:5-[[@LINE-2]]:5}:"static_cast<int>("
+ // CXX20: fix-it:{{.*}}:{[[@LINE-3]]:12-[[@LINE-3]]:12}:")"
+ // cxx23-note at -4 {{no implicit conversion for scoped enum; consider using std::to_underlying}}
+ // CXX23: fix-it:{{.*}}:{[[@LINE-5]]:5-[[@LINE-5]]:5}:"std::to_underlying("
+ // CXX23: fix-it:{{.*}}:{[[@LINE-6]]:12-[[@LINE-6]]:12}:")"
0 * E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
- // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
- // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:9-[[@LINE-2]]:9}:"static_cast<int>("
- // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:16-[[@LINE-3]]:16}:")"
+ // cxx11-20-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CXX20: fix-it:{{.*}}:{[[@LINE-2]]:9-[[@LINE-2]]:9}:"static_cast<int>("
+ // CXX20: fix-it:{{.*}}:{[[@LINE-3]]:16-[[@LINE-3]]:16}:")"
+ // cxx23-note at -4 {{no implicit conversion for scoped enum; consider using std::to_underlying}}
+ // CXX23: fix-it:{{.*}}:{[[@LINE-5]]:9-[[@LINE-5]]:9}:"std::to_underlying("
+ // CXX23: fix-it:{{.*}}:{[[@LINE-6]]:16-[[@LINE-6]]:16}:")"
0 / E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
- // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
- // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:9-[[@LINE-2]]:9}:"static_cast<int>("
- // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:16-[[@LINE-3]]:16}:")"
+ // cxx11-20-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CXX20: fix-it:{{.*}}:{[[@LINE-2]]:9-[[@LINE-2]]:9}:"static_cast<int>("
+ // CXX20: fix-it:{{.*}}:{[[@LINE-3]]:16-[[@LINE-3]]:16}:")"
+ // cxx23-note at -4 {{no implicit conversion for scoped enum; consider using std::to_underlying}}
+ // CXX23: fix-it:{{.*}}:{[[@LINE-5]]:9-[[@LINE-5]]:9}:"std::to_underlying("
+ // CXX23: fix-it:{{.*}}:{[[@LINE-6]]:16-[[@LINE-6]]:16}:")"
0 % E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
- // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
- // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:9-[[@LINE-2]]:9}:"static_cast<int>("
- // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:16-[[@LINE-3]]:16}:")"
+ // cxx11-20-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CXX20: fix-it:{{.*}}:{[[@LINE-2]]:9-[[@LINE-2]]:9}:"static_cast<int>("
+ // CXX20: fix-it:{{.*}}:{[[@LINE-3]]:16-[[@LINE-3]]:16}:")"
+ // cxx23-note at -4 {{no implicit conversion for scoped enum; consider using std::to_underlying}}
+ // CXX23: fix-it:{{.*}}:{[[@LINE-5]]:9-[[@LINE-5]]:9}:"std::to_underlying("
+ // CXX23: fix-it:{{.*}}:{[[@LINE-6]]:16-[[@LINE-6]]:16}:")"
0 + E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
- // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
- // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:9-[[@LINE-2]]:9}:"static_cast<int>("
- // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:16-[[@LINE-3]]:16}:")"
+ // cxx11-20-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CXX20: fix-it:{{.*}}:{[[@LINE-2]]:9-[[@LINE-2]]:9}:"static_cast<int>("
+ // CXX20: fix-it:{{.*}}:{[[@LINE-3]]:16-[[@LINE-3]]:16}:")"
+ // cxx23-note at -4 {{no implicit conversion for scoped enum; consider using std::to_underlying}}
+ // CXX23: fix-it:{{.*}}:{[[@LINE-5]]:9-[[@LINE-5]]:9}:"std::to_underlying("
+ // CXX23: fix-it:{{.*}}:{[[@LINE-6]]:16-[[@LINE-6]]:16}:")"
0 - E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
- // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
- // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:9-[[@LINE-2]]:9}:"static_cast<int>("
- // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:16-[[@LINE-3]]:16}:")"
+ // cxx11-20-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CXX20: fix-it:{{.*}}:{[[@LINE-2]]:9-[[@LINE-2]]:9}:"static_cast<int>("
+ // CXX20: fix-it:{{.*}}:{[[@LINE-3]]:16-[[@LINE-3]]:16}:")"
+ // cxx23-note at -4 {{no implicit conversion for scoped enum; consider using std::to_underlying}}
+ // CXX23: fix-it:{{.*}}:{[[@LINE-5]]:9-[[@LINE-5]]:9}:"std::to_underlying("
+ // CXX23: fix-it:{{.*}}:{[[@LINE-6]]:16-[[@LINE-6]]:16}:")"
0 << E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
- // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
- // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:10-[[@LINE-2]]:10}:"static_cast<int>("
- // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:17-[[@LINE-3]]:17}:")"
+ // cxx11-20-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CXX20: fix-it:{{.*}}:{[[@LINE-2]]:10-[[@LINE-2]]:10}:"static_cast<int>("
+ // CXX20: fix-it:{{.*}}:{[[@LINE-3]]:17-[[@LINE-3]]:17}:")"
+ // cxx23-note at -4 {{no implicit conversion for scoped enum; consider using std::to_underlying}}
+ // CXX23: fix-it:{{.*}}:{[[@LINE-5]]:10-[[@LINE-5]]:10}:"std::to_underlying("
+ // CXX23: fix-it:{{.*}}:{[[@LINE-6]]:17-[[@LINE-6]]:17}:")"
0 >> E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
- // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
- // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:10-[[@LINE-2]]:10}:"static_cast<int>("
- // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:17-[[@LINE-3]]:17}:")"
+ // cxx11-20-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CXX20: fix-it:{{.*}}:{[[@LINE-2]]:10-[[@LINE-2]]:10}:"static_cast<int>("
+ // CXX20: fix-it:{{.*}}:{[[@LINE-3]]:17-[[@LINE-3]]:17}:")"
+ // cxx23-note at -4 {{no implicit conversion for scoped enum; consider using std::to_underlying}}
+ // CXX23: fix-it:{{.*}}:{[[@LINE-5]]:10-[[@LINE-5]]:10}:"std::to_underlying("
+ // CXX23: fix-it:{{.*}}:{[[@LINE-6]]:17-[[@LINE-6]]:17}:")"
#if __cplusplus >= 202002L
0 <=> E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
- // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
- // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:11-[[@LINE-2]]:11}:"static_cast<int>("
- // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:18-[[@LINE-3]]:18}:")"
+ // cxx11-20-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CXX20: fix-it:{{.*}}:{[[@LINE-2]]:11-[[@LINE-2]]:11}:"static_cast<int>("
+ // CXX20: fix-it:{{.*}}:{[[@LINE-3]]:18-[[@LINE-3]]:18}:")"
+ // cxx23-note at -4 {{no implicit conversion for scoped enum; consider using std::to_underlying}}
+ // CXX23: fix-it:{{.*}}:{[[@LINE-5]]:11-[[@LINE-5]]:11}:"std::to_underlying("
+ // CXX23: fix-it:{{.*}}:{[[@LINE-6]]:18-[[@LINE-6]]:18}:")"
#endif
0 < E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
- // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
- // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:9-[[@LINE-2]]:9}:"static_cast<int>("
- // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:16-[[@LINE-3]]:16}:")"
+ // cxx11-20-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CXX20: fix-it:{{.*}}:{[[@LINE-2]]:9-[[@LINE-2]]:9}:"static_cast<int>("
+ // CXX20: fix-it:{{.*}}:{[[@LINE-3]]:16-[[@LINE-3]]:16}:")"
+ // cxx23-note at -4 {{no implicit conversion for scoped enum; consider using std::to_underlying}}
+ // CXX23: fix-it:{{.*}}:{[[@LINE-5]]:9-[[@LINE-5]]:9}:"std::to_underlying("
+ // CXX23: fix-it:{{.*}}:{[[@LINE-6]]:16-[[@LINE-6]]:16}:")"
0 > E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
- // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
- // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:9-[[@LINE-2]]:9}:"static_cast<int>("
- // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:16-[[@LINE-3]]:16}:")"
+ // cxx11-20-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CXX20: fix-it:{{.*}}:{[[@LINE-2]]:9-[[@LINE-2]]:9}:"static_cast<int>("
+ // CXX20: fix-it:{{.*}}:{[[@LINE-3]]:16-[[@LINE-3]]:16}:")"
+ // cxx23-note at -4 {{no implicit conversion for scoped enum; consider using std::to_underlying}}
+ // CXX23: fix-it:{{.*}}:{[[@LINE-5]]:9-[[@LINE-5]]:9}:"std::to_underlying("
+ // CXX23: fix-it:{{.*}}:{[[@LINE-6]]:16-[[@LINE-6]]:16}:")"
0 <= E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
- // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
- // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:10-[[@LINE-2]]:10}:"static_cast<int>("
- // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:17-[[@LINE-3]]:17}:")"
+ // cxx11-20-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CXX20: fix-it:{{.*}}:{[[@LINE-2]]:10-[[@LINE-2]]:10}:"static_cast<int>("
+ // CXX20: fix-it:{{.*}}:{[[@LINE-3]]:17-[[@LINE-3]]:17}:")"
+ // cxx23-note at -4 {{no implicit conversion for scoped enum; consider using std::to_underlying}}
+ // CXX23: fix-it:{{.*}}:{[[@LINE-5]]:10-[[@LINE-5]]:10}:"std::to_underlying("
+ // CXX23: fix-it:{{.*}}:{[[@LINE-6]]:17-[[@LINE-6]]:17}:")"
0 >= E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
- // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
- // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:10-[[@LINE-2]]:10}:"static_cast<int>("
- // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:17-[[@LINE-3]]:17}:")"
+ // cxx11-20-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CXX20: fix-it:{{.*}}:{[[@LINE-2]]:10-[[@LINE-2]]:10}:"static_cast<int>("
+ // CXX20: fix-it:{{.*}}:{[[@LINE-3]]:17-[[@LINE-3]]:17}:")"
+ // cxx23-note at -4 {{no implicit conversion for scoped enum; consider using std::to_underlying}}
+ // CXX23: fix-it:{{.*}}:{[[@LINE-5]]:10-[[@LINE-5]]:10}:"std::to_underlying("
+ // CXX23: fix-it:{{.*}}:{[[@LINE-6]]:17-[[@LINE-6]]:17}:")"
0 == E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
- // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
- // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:10-[[@LINE-2]]:10}:"static_cast<int>("
- // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:17-[[@LINE-3]]:17}:")"
+ // cxx11-20-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CXX20: fix-it:{{.*}}:{[[@LINE-2]]:10-[[@LINE-2]]:10}:"static_cast<int>("
+ // CXX20: fix-it:{{.*}}:{[[@LINE-3]]:17-[[@LINE-3]]:17}:")"
+ // cxx23-note at -4 {{no implicit conversion for scoped enum; consider using std::to_underlying}}
+ // CXX23: fix-it:{{.*}}:{[[@LINE-5]]:10-[[@LINE-5]]:10}:"std::to_underlying("
+ // CXX23: fix-it:{{.*}}:{[[@LINE-6]]:17-[[@LINE-6]]:17}:")"
0 != E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
- // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
- // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:10-[[@LINE-2]]:10}:"static_cast<int>("
- // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:17-[[@LINE-3]]:17}:")"
+ // cxx11-20-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CXX20: fix-it:{{.*}}:{[[@LINE-2]]:10-[[@LINE-2]]:10}:"static_cast<int>("
+ // CXX20: fix-it:{{.*}}:{[[@LINE-3]]:17-[[@LINE-3]]:17}:")"
+ // cxx23-note at -4 {{no implicit conversion for scoped enum; consider using std::to_underlying}}
+ // CXX23: fix-it:{{.*}}:{[[@LINE-5]]:10-[[@LINE-5]]:10}:"std::to_underlying("
+ // CXX23: fix-it:{{.*}}:{[[@LINE-6]]:17-[[@LINE-6]]:17}:")"
0 & E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
- // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
- // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:9-[[@LINE-2]]:9}:"static_cast<int>("
- // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:16-[[@LINE-3]]:16}:")"
+ // cxx11-20-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CXX20: fix-it:{{.*}}:{[[@LINE-2]]:9-[[@LINE-2]]:9}:"static_cast<int>("
+ // CXX20: fix-it:{{.*}}:{[[@LINE-3]]:16-[[@LINE-3]]:16}:")"
+ // cxx23-note at -4 {{no implicit conversion for scoped enum; consider using std::to_underlying}}
+ // CXX23: fix-it:{{.*}}:{[[@LINE-5]]:9-[[@LINE-5]]:9}:"std::to_underlying("
+ // CXX23: fix-it:{{.*}}:{[[@LINE-6]]:16-[[@LINE-6]]:16}:")"
0 ^ E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
- // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
- // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:9-[[@LINE-2]]:9}:"static_cast<int>("
- // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:16-[[@LINE-3]]:16}:")"
+ // cxx11-20-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CXX20: fix-it:{{.*}}:{[[@LINE-2]]:9-[[@LINE-2]]:9}:"static_cast<int>("
+ // CXX20: fix-it:{{.*}}:{[[@LINE-3]]:16-[[@LINE-3]]:16}:")"
+ // cxx23-note at -4 {{no implicit conversion for scoped enum; consider using std::to_underlying}}
+ // CXX23: fix-it:{{.*}}:{[[@LINE-5]]:9-[[@LINE-5]]:9}:"std::to_underlying("
+ // CXX23: fix-it:{{.*}}:{[[@LINE-6]]:16-[[@LINE-6]]:16}:")"
0 | E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
- // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
- // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:9-[[@LINE-2]]:9}:"static_cast<int>("
- // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:16-[[@LINE-3]]:16}:")"
+ // cxx11-20-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CXX20: fix-it:{{.*}}:{[[@LINE-2]]:9-[[@LINE-2]]:9}:"static_cast<int>("
+ // CXX20: fix-it:{{.*}}:{[[@LINE-3]]:16-[[@LINE-3]]:16}:")"
+ // cxx23-note at -4 {{no implicit conversion for scoped enum; consider using std::to_underlying}}
+ // CXX23: fix-it:{{.*}}:{[[@LINE-5]]:9-[[@LINE-5]]:9}:"std::to_underlying("
+ // CXX23: fix-it:{{.*}}:{[[@LINE-6]]:16-[[@LINE-6]]:16}:")"
0 && E_int::e; // expected-error {{value of type 'GH24265::E_int' is not contextually convertible to 'bool'}}
// expected-error at -1 {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
- // expected-note at -2 {{no implicit conversion for scoped enum; consider casting to underlying type}}
- // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:10-[[@LINE-3]]:10}:"static_cast<int>("
- // CHECK: fix-it:{{.*}}:{[[@LINE-4]]:17-[[@LINE-4]]:17}:")"
+ // cxx11-20-note at -2 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CXX20: fix-it:{{.*}}:{[[@LINE-3]]:10-[[@LINE-3]]:10}:"static_cast<int>("
+ // CXX20: fix-it:{{.*}}:{[[@LINE-4]]:17-[[@LINE-4]]:17}:")"
+ // cxx23-note at -5 {{no implicit conversion for scoped enum; consider using std::to_underlying}}
+ // CXX23: fix-it:{{.*}}:{[[@LINE-6]]:10-[[@LINE-6]]:10}:"std::to_underlying("
+ // CXX23: fix-it:{{.*}}:{[[@LINE-7]]:17-[[@LINE-7]]:17}:")"
0 || E_int::e; // expected-error {{value of type 'GH24265::E_int' is not contextually convertible to 'bool'}}
// expected-error at -1 {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
- // expected-note at -2 {{no implicit conversion for scoped enum; consider casting to underlying type}}
- // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:10-[[@LINE-3]]:10}:"static_cast<int>("
- // CHECK: fix-it:{{.*}}:{[[@LINE-4]]:17-[[@LINE-4]]:17}:")"
+ // cxx11-20-note at -2 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CXX20: fix-it:{{.*}}:{[[@LINE-3]]:10-[[@LINE-3]]:10}:"static_cast<int>("
+ // CXX20: fix-it:{{.*}}:{[[@LINE-4]]:17-[[@LINE-4]]:17}:")"
+ // cxx23-note at -5 {{no implicit conversion for scoped enum; consider using std::to_underlying}}
+ // CXX23: fix-it:{{.*}}:{[[@LINE-6]]:10-[[@LINE-6]]:10}:"std::to_underlying("
+ // CXX23: fix-it:{{.*}}:{[[@LINE-7]]:17-[[@LINE-7]]:17}:")"
int a;
a *= E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
- // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
- // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:10-[[@LINE-2]]:10}:"static_cast<int>("
- // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:17-[[@LINE-3]]:17}:")"
+ // cxx11-20-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CXX20: fix-it:{{.*}}:{[[@LINE-2]]:10-[[@LINE-2]]:10}:"static_cast<int>("
+ // CXX20: fix-it:{{.*}}:{[[@LINE-3]]:17-[[@LINE-3]]:17}:")"
+ // cxx23-note at -4 {{no implicit conversion for scoped enum; consider using std::to_underlying}}
+ // CXX23: fix-it:{{.*}}:{[[@LINE-5]]:10-[[@LINE-5]]:10}:"std::to_underlying("
+ // CXX23: fix-it:{{.*}}:{[[@LINE-6]]:17-[[@LINE-6]]:17}:")"
a /= E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
- // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
- // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:10-[[@LINE-2]]:10}:"static_cast<int>("
- // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:17-[[@LINE-3]]:17}:")"
+ // cxx11-20-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CXX20: fix-it:{{.*}}:{[[@LINE-2]]:10-[[@LINE-2]]:10}:"static_cast<int>("
+ // CXX20: fix-it:{{.*}}:{[[@LINE-3]]:17-[[@LINE-3]]:17}:")"
+ // cxx23-note at -4 {{no implicit conversion for scoped enum; consider using std::to_underlying}}
+ // CXX23: fix-it:{{.*}}:{[[@LINE-5]]:10-[[@LINE-5]]:10}:"std::to_underlying("
+ // CXX23: fix-it:{{.*}}:{[[@LINE-6]]:17-[[@LINE-6]]:17}:")"
a %= E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
- // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
- // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:10-[[@LINE-2]]:10}:"static_cast<int>("
- // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:17-[[@LINE-3]]:17}:")"
+ // cxx11-20-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CXX20: fix-it:{{.*}}:{[[@LINE-2]]:10-[[@LINE-2]]:10}:"static_cast<int>("
+ // CXX20: fix-it:{{.*}}:{[[@LINE-3]]:17-[[@LINE-3]]:17}:")"
+ // cxx23-note at -4 {{no implicit conversion for scoped enum; consider using std::to_underlying}}
+ // CXX23: fix-it:{{.*}}:{[[@LINE-5]]:10-[[@LINE-5]]:10}:"std::to_underlying("
+ // CXX23: fix-it:{{.*}}:{[[@LINE-6]]:17-[[@LINE-6]]:17}:")"
a += E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
- // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
- // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:10-[[@LINE-2]]:10}:"static_cast<int>("
- // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:17-[[@LINE-3]]:17}:")"
+ // cxx11-20-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CXX20: fix-it:{{.*}}:{[[@LINE-2]]:10-[[@LINE-2]]:10}:"static_cast<int>("
+ // CXX20: fix-it:{{.*}}:{[[@LINE-3]]:17-[[@LINE-3]]:17}:")"
+ // cxx23-note at -4 {{no implicit conversion for scoped enum; consider using std::to_underlying}}
+ // CXX23: fix-it:{{.*}}:{[[@LINE-5]]:10-[[@LINE-5]]:10}:"std::to_underlying("
+ // CXX23: fix-it:{{.*}}:{[[@LINE-6]]:17-[[@LINE-6]]:17}:")"
a -= E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
- // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
- // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:10-[[@LINE-2]]:10}:"static_cast<int>("
- // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:17-[[@LINE-3]]:17}:")"
+ // cxx11-20-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CXX20: fix-it:{{.*}}:{[[@LINE-2]]:10-[[@LINE-2]]:10}:"static_cast<int>("
+ // CXX20: fix-it:{{.*}}:{[[@LINE-3]]:17-[[@LINE-3]]:17}:")"
+ // cxx23-note at -4 {{no implicit conversion for scoped enum; consider using std::to_underlying}}
+ // CXX23: fix-it:{{.*}}:{[[@LINE-5]]:10-[[@LINE-5]]:10}:"std::to_underlying("
+ // CXX23: fix-it:{{.*}}:{[[@LINE-6]]:17-[[@LINE-6]]:17}:")"
a <<= E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
- // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
- // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:11-[[@LINE-2]]:11}:"static_cast<int>("
- // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:18-[[@LINE-3]]:18}:")"
+ // cxx11-20-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CXX20: fix-it:{{.*}}:{[[@LINE-2]]:11-[[@LINE-2]]:11}:"static_cast<int>("
+ // CXX20: fix-it:{{.*}}:{[[@LINE-3]]:18-[[@LINE-3]]:18}:")"
+ // cxx23-note at -4 {{no implicit conversion for scoped enum; consider using std::to_underlying}}
+ // CXX23: fix-it:{{.*}}:{[[@LINE-5]]:11-[[@LINE-5]]:11}:"std::to_underlying("
+ // CXX23: fix-it:{{.*}}:{[[@LINE-6]]:18-[[@LINE-6]]:18}:")"
a >>= E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
- // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
- // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:11-[[@LINE-2]]:11}:"static_cast<int>("
- // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:18-[[@LINE-3]]:18}:")"
+ // cxx11-20-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CXX20: fix-it:{{.*}}:{[[@LINE-2]]:11-[[@LINE-2]]:11}:"static_cast<int>("
+ // CXX20: fix-it:{{.*}}:{[[@LINE-3]]:18-[[@LINE-3]]:18}:")"
+ // cxx23-note at -4 {{no implicit conversion for scoped enum; consider using std::to_underlying}}
+ // CXX23: fix-it:{{.*}}:{[[@LINE-5]]:11-[[@LINE-5]]:11}:"std::to_underlying("
+ // CXX23: fix-it:{{.*}}:{[[@LINE-6]]:18-[[@LINE-6]]:18}:")"
a &= E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
- // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
- // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:10-[[@LINE-2]]:10}:"static_cast<int>("
- // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:17-[[@LINE-3]]:17}:")"
+ // cxx11-20-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CXX20: fix-it:{{.*}}:{[[@LINE-2]]:10-[[@LINE-2]]:10}:"static_cast<int>("
+ // CXX20: fix-it:{{.*}}:{[[@LINE-3]]:17-[[@LINE-3]]:17}:")"
+ // cxx23-note at -4 {{no implicit conversion for scoped enum; consider using std::to_underlying}}
+ // CXX23: fix-it:{{.*}}:{[[@LINE-5]]:10-[[@LINE-5]]:10}:"std::to_underlying("
+ // CXX23: fix-it:{{.*}}:{[[@LINE-6]]:17-[[@LINE-6]]:17}:")"
a ^= E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
- // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
- // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:10-[[@LINE-2]]:10}:"static_cast<int>("
- // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:17-[[@LINE-3]]:17}:")"
+ // cxx11-20-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CXX20: fix-it:{{.*}}:{[[@LINE-2]]:10-[[@LINE-2]]:10}:"static_cast<int>("
+ // CXX20: fix-it:{{.*}}:{[[@LINE-3]]:17-[[@LINE-3]]:17}:")"
+ // cxx23-note at -4 {{no implicit conversion for scoped enum; consider using std::to_underlying}}
+ // CXX23: fix-it:{{.*}}:{[[@LINE-5]]:10-[[@LINE-5]]:10}:"std::to_underlying("
+ // CXX23: fix-it:{{.*}}:{[[@LINE-6]]:17-[[@LINE-6]]:17}:")"
a |= E_int::e; // expected-error {{invalid operands to binary expression ('int' and 'GH24265::E_int')}}
- // expected-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
- // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:10-[[@LINE-2]]:10}:"static_cast<int>("
- // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:17-[[@LINE-3]]:17}:")"
+ // cxx11-20-note at -1 {{no implicit conversion for scoped enum; consider casting to underlying type}}
+ // CXX20: fix-it:{{.*}}:{[[@LINE-2]]:10-[[@LINE-2]]:10}:"static_cast<int>("
+ // CXX20: fix-it:{{.*}}:{[[@LINE-3]]:17-[[@LINE-3]]:17}:")"
+ // cxx23-note at -4 {{no implicit conversion for scoped enum; consider using std::to_underlying}}
+ // CXX23: fix-it:{{.*}}:{[[@LINE-5]]:10-[[@LINE-5]]:10}:"std::to_underlying("
+ // CXX23: fix-it:{{.*}}:{[[@LINE-6]]:17-[[@LINE-6]]:17}:")"
// TODO: These do not have the diagnostic yet
E_int b;
>From 45c8c060cc45cd3abfb3d4d4eda676ab6ce30890 Mon Sep 17 00:00:00 2001
From: tinnamchoi <tinnam.choi at gmail.com>
Date: Sat, 9 Aug 2025 23:18:35 +0800
Subject: [PATCH 9/9] [Clang] Update ReleaseNotes.rst
---
clang/docs/ReleaseNotes.rst | 1 +
1 file changed, 1 insertion(+)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 9231f2cae9bfa..0ba1ad5f85c03 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -125,6 +125,7 @@ Improvements to Clang's diagnostics
an override of a virtual method.
- Fixed fix-it hint for fold expressions. Clang now correctly places the suggested right
parenthesis when diagnosing malformed fold expressions. (#GH151787)
+- Added fix-it hint for when scoped enumerations require explicit conversions for binary operations. (#GH24265)
Improvements to Clang's time-trace
----------------------------------
More information about the cfe-commits
mailing list