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.c...@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.c...@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@-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@-1{{no implicit conversion for scoped enum; consider casting to underlying type}} + // expected-note@-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@-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@-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@-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.c...@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@-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@-1 {{no implicit conversion for scoped enum; consider casting to underlying type}} + // expected-note@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-1 {{invalid operands to binary expression ('int' and 'GH24265::E_int')}} + // expected-note@-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@-1 {{invalid operands to binary expression ('int' and 'GH24265::E_int')}} + // expected-note@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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.c...@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.c...@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@-1 {{no implicit conversion for scoped enum; consider casting to underlying type}} - // expected-note@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-1 {{invalid operands to binary expression ('int' and 'GH24265::E_int')}} // expected-note@-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@-1 {{invalid operands to binary expression ('int' and 'GH24265::E_int')}} // expected-note@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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.c...@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.c...@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.c...@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@-1{{no implicit conversion for scoped enum; consider casting to underlying type}} - // expected-note@-2{{no implicit conversion for scoped enum; consider casting to underlying type}} + // cxx11-20-note@-1{{no implicit conversion for scoped enum; consider casting to underlying type}} + // cxx23-note@-2{{no implicit conversion for scoped enum; consider using std::to_underlying}} + // cxx11-20-note@-3{{no implicit conversion for scoped enum; consider casting to underlying type}} + // cxx23-note@-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@-1{{no implicit conversion for scoped enum; consider casting to underlying type}} + // cxx11-20-note@-1{{no implicit conversion for scoped enum; consider casting to underlying type}} + // cxx23-note@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-1 {{invalid operands to binary expression ('int' and 'GH24265::E_int')}} - // expected-note@-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@-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@-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@-1 {{invalid operands to binary expression ('int' and 'GH24265::E_int')}} - // expected-note@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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.c...@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 ---------------------------------- _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits