Author: Timothy Choi Date: 2025-08-23T09:14:16Z New Revision: 8b5503d9b1fca2eb9ceb7439100235ff0e53394b
URL: https://github.com/llvm/llvm-project/commit/8b5503d9b1fca2eb9ceb7439100235ff0e53394b DIFF: https://github.com/llvm/llvm-project/commit/8b5503d9b1fca2eb9ceb7439100235ff0e53394b.diff LOG: [Clang] Add diagnostic when scoped enumeration requires an explicit conversion for binary operations (#152698) ```diff main.cpp:4:8: error: invalid operands to binary expression ('E' and 'int') 4 | E::e + 0; | ~~~~ ^ ~ +main.cpp:4:3: note: no implicit conversion for scoped enum; consider casting to underlying type + 4 | E::e + 0; + | ^ + | static_cast<int>( ) ``` Resolves #24265 Added: clang/test/FixIt/fixit-enum-scoped.cpp Modified: clang/docs/ReleaseNotes.rst clang/include/clang/Basic/DiagnosticSemaKinds.td clang/include/clang/Sema/Sema.h clang/lib/Sema/SemaExpr.cpp clang/test/CXX/over/over.match/over.match.funcs/over.match.oper/p3.cpp clang/test/SemaCXX/enum-scoped.cpp clang/test/SemaCXX/opaque-enum-declaration-in-class-template.cpp Removed: ################################################################################ diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 465c50784f27f..a941c6521d90e 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -209,6 +209,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) - Fixed an issue where emitted format-signedness diagnostics were not associated with an appropriate diagnostic id. Besides being incorrect from an API standpoint, this was user visible, e.g.: diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index f50d245965648..4c98e0f8b04ed 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -4407,6 +4407,11 @@ def warn_impcast_ diff erent_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 12112eb1ad225..a78f5635f44da 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -8065,8 +8065,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); @@ -8075,7 +8075,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 911c80adfd539..caa04e73ab2ab 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -10748,9 +10748,50 @@ 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) { + if (!LHS.isUsable() || !RHS.isUsable()) + return; + const Expr *LHSExpr = LHS.get(); + const Expr *RHSExpr = RHS.get(); + 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 (BinaryOperator::isAssignmentOp(Opc) && LHSIsScoped) + return; + if (!LHSIsScoped && !LHSType->isIntegralOrUnscopedEnumerationType()) + return; + if (!RHSIsScoped && !RHSType->isIntegralOrUnscopedEnumerationType()) + return; + auto DiagnosticHelper = [&S](const Expr *expr, const QualType type) { + SourceLocation BeginLoc = expr->getBeginLoc(); + QualType IntType = type->castAs<EnumType>() + ->getOriginalDecl() + ->getDefinitionOrSelf() + ->getIntegerType(); + std::string InsertionString = "static_cast<" + IntType.getAsString() + ">("; + S.Diag(BeginLoc, diag::note_no_implicit_conversion_for_scoped_enum) + << FixItHint::CreateInsertion(BeginLoc, InsertionString) + << FixItHint::CreateInsertion(expr->getEndLoc(), ")"); + }; + if (LHSIsScoped) { + DiagnosticHelper(LHSExpr, LHSType); + } + if (RHSIsScoped) { + DiagnosticHelper(RHSExpr, RHSType); + } +} + QualType Sema::CheckMultiplyDivideOperands(ExprResult &LHS, ExprResult &RHS, SourceLocation Loc, - bool IsCompAssign, bool IsDiv) { + 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(); @@ -10778,9 +10819,11 @@ 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()) { + QualType ResultTy = InvalidOperands(Loc, LHS, RHS); + diagnoseScopedEnums(*this, Loc, LHS, RHS, Opc); + return ResultTy; + } if (IsDiv) { DetectPrecisionLossInComplexDivision(*this, RHS.get()->getType(), Loc); DiagnoseBadDivideOrRemainderValues(*this, LHS, RHS, Loc, IsDiv); @@ -10843,8 +10886,12 @@ QualType Sema::CheckRemainderOperands( if (compType.isNull() || (!compType->isIntegerType() && - !(getLangOpts().HLSL && compType->isFloatingType()))) - return InvalidOperands(Loc, LHS, RHS); + !(getLangOpts().HLSL && compType->isFloatingType()))) { + QualType ResultTy = InvalidOperands(Loc, LHS, RHS); + diagnoseScopedEnums(*this, Loc, LHS, RHS, + IsCompAssign ? BO_RemAssign : BO_Rem); + return ResultTy; + } DiagnoseBadDivideOrRemainderValues(*this, LHS, RHS, Loc, false /* IsDiv */); return compType; } @@ -11200,7 +11247,9 @@ QualType Sema::CheckAdditionOperands(ExprResult &LHS, ExprResult &RHS, } else if (PExp->getType()->isObjCObjectPointerType()) { isObjCPointer = true; } else { - return InvalidOperands(Loc, LHS, RHS); + QualType ResultTy = InvalidOperands(Loc, LHS, RHS); + diagnoseScopedEnums(*this, Loc, LHS, RHS, Opc); + return ResultTy; } } assert(PExp->getType()->isAnyPointerType()); @@ -11257,7 +11306,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() || @@ -11402,7 +11452,9 @@ QualType Sema::CheckSubtractionOperands(ExprResult &LHS, ExprResult &RHS, } } - return InvalidOperands(Loc, LHS, RHS); + QualType ResultTy = InvalidOperands(Loc, LHS, RHS); + diagnoseScopedEnums(*this, Loc, LHS, RHS, Opc); + return ResultTy; } static bool isScopedEnumerationType(QualType T) { @@ -11750,8 +11802,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()) { + QualType ResultTy = InvalidOperands(Loc, LHS, RHS); + diagnoseScopedEnums(*this, Loc, LHS, RHS, Opc); + return ResultTy; + } // C++0x: Don't allow scoped enums. FIXME: Use something better than // hasIntegerRepresentation() above instead of this. @@ -12319,8 +12374,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()) { + QualType ResultTy = S.InvalidOperands(Loc, LHS, RHS); + diagnoseScopedEnums(S, Loc, LHS, RHS, BO_Cmp); + return ResultTy; + } std::optional<ComparisonCategoryType> CCT = getComparisonCategoryForBuiltinCmp(Type); @@ -12352,8 +12410,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()) { + QualType ResultTy = S.InvalidOperands(Loc, LHS, RHS); + diagnoseScopedEnums(S, Loc, LHS, RHS, Opc); + return ResultTy; + } assert(Type->isArithmeticType() || Type->isEnumeralType()); if (Type->isAnyComplexType() && BinaryOperator::isRelationalOp(Opc)) @@ -13363,7 +13424,9 @@ inline QualType Sema::CheckBitwiseOperands(ExprResult &LHS, ExprResult &RHS, if (!compType.isNull() && compType->isIntegralOrUnscopedEnumerationType()) return compType; - return InvalidOperands(Loc, LHS, RHS); + QualType ResultTy = InvalidOperands(Loc, LHS, RHS); + diagnoseScopedEnums(*this, Loc, LHS, RHS, Opc); + return ResultTy; } // C99 6.5.[13,14] @@ -13465,13 +13528,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()) { + QualType ResultTy = InvalidOperands(Loc, LHS, RHS); + diagnoseScopedEnums(*this, Loc, LHS, RHS, Opc); + return ResultTy; + } LHS = LHSRes; ExprResult RHSRes = PerformContextuallyConvertToBool(RHS.get()); - if (RHSRes.isInvalid()) - return InvalidOperands(Loc, LHS, RHS); + if (RHSRes.isInvalid()) { + QualType ResultTy = InvalidOperands(Loc, LHS, RHS); + diagnoseScopedEnums(*this, Loc, LHS, RHS, Opc); + return ResultTy; + } RHS = RHSRes; // C++ [expr.log.and]p2 @@ -15067,8 +15136,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); @@ -15079,7 +15147,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: @@ -15123,8 +15191,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 = @@ -15146,7 +15213,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); 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/FixIt/fixit-enum-scoped.cpp b/clang/test/FixIt/fixit-enum-scoped.cpp new file mode 100644 index 0000000000000..b7e4138b7d216 --- /dev/null +++ b/clang/test/FixIt/fixit-enum-scoped.cpp @@ -0,0 +1,95 @@ +// RUN: not %clang_cc1 -fsyntax-only -fdiagnostics-parseable-fixits -std=c++20 -triple x86_64-apple-darwin %s 2>&1 | FileCheck %s + +namespace GH24265 { + enum class E_int { e }; + enum class E_long : long { e }; + + void f() { + E_int::e + E_long::e; // CHECK: fix-it:{{.*}}:{[[@LINE]]:5-[[@LINE]]:5}:"static_cast<int>(" + // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:12-[[@LINE-1]]:12}:")" + // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:16-[[@LINE-2]]:16}:"static_cast<long>(" + // CHECK: fix-it:{{.*}}:{[[@LINE-3]]:24-[[@LINE-3]]:24}:")" + E_int::e + 0; // CHECK: fix-it:{{.*}}:{[[@LINE]]:5-[[@LINE]]:5}:"static_cast<int>(" + // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:12-[[@LINE-1]]:12}:")" + + 0 * E_int::e; // CHECK: fix-it:{{.*}}:{[[@LINE]]:9-[[@LINE]]:9}:"static_cast<int>(" + // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:16-[[@LINE-1]]:16}:")" + 0 / E_int::e; // CHECK: fix-it:{{.*}}:{[[@LINE]]:9-[[@LINE]]:9}:"static_cast<int>(" + // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:16-[[@LINE-1]]:16}:")" + 0 % E_int::e; // CHECK: fix-it:{{.*}}:{[[@LINE]]:9-[[@LINE]]:9}:"static_cast<int>(" + // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:16-[[@LINE-1]]:16}:")" + 0 + E_int::e; // CHECK: fix-it:{{.*}}:{[[@LINE]]:9-[[@LINE]]:9}:"static_cast<int>(" + // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:16-[[@LINE-1]]:16}:")" + 0 - E_int::e; // CHECK: fix-it:{{.*}}:{[[@LINE]]:9-[[@LINE]]:9}:"static_cast<int>(" + // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:16-[[@LINE-1]]:16}:")" + 0 << E_int::e; // CHECK: fix-it:{{.*}}:{[[@LINE]]:10-[[@LINE]]:10}:"static_cast<int>(" + // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:17-[[@LINE-1]]:17}:")" + 0 >> E_int::e; // CHECK: fix-it:{{.*}}:{[[@LINE]]:10-[[@LINE]]:10}:"static_cast<int>(" + // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:17-[[@LINE-1]]:17}:")" + 0 <=> E_int::e; // CHECK: fix-it:{{.*}}:{[[@LINE]]:11-[[@LINE]]:11}:"static_cast<int>(" + // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:18-[[@LINE-1]]:18}:")" + 0 < E_int::e; // CHECK: fix-it:{{.*}}:{[[@LINE]]:9-[[@LINE]]:9}:"static_cast<int>(" + // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:16-[[@LINE-1]]:16}:")" + 0 > E_int::e; // CHECK: fix-it:{{.*}}:{[[@LINE]]:9-[[@LINE]]:9}:"static_cast<int>(" + // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:16-[[@LINE-1]]:16}:")" + 0 <= E_int::e; // CHECK: fix-it:{{.*}}:{[[@LINE]]:10-[[@LINE]]:10}:"static_cast<int>(" + // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:17-[[@LINE-1]]:17}:")" + 0 >= E_int::e; // CHECK: fix-it:{{.*}}:{[[@LINE]]:10-[[@LINE]]:10}:"static_cast<int>(" + // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:17-[[@LINE-1]]:17}:")" + 0 == E_int::e; // CHECK: fix-it:{{.*}}:{[[@LINE]]:10-[[@LINE]]:10}:"static_cast<int>(" + // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:17-[[@LINE-1]]:17}:")" + 0 != E_int::e; // CHECK: fix-it:{{.*}}:{[[@LINE]]:10-[[@LINE]]:10}:"static_cast<int>(" + // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:17-[[@LINE-1]]:17}:")" + 0 & E_int::e; // CHECK: fix-it:{{.*}}:{[[@LINE]]:9-[[@LINE]]:9}:"static_cast<int>(" + // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:16-[[@LINE-1]]:16}:")" + 0 ^ E_int::e; // CHECK: fix-it:{{.*}}:{[[@LINE]]:9-[[@LINE]]:9}:"static_cast<int>(" + // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:16-[[@LINE-1]]:16}:")" + 0 | E_int::e; // CHECK: fix-it:{{.*}}:{[[@LINE]]:9-[[@LINE]]:9}:"static_cast<int>(" + // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:16-[[@LINE-1]]:16}:")" + 0 && E_int::e; // CHECK: fix-it:{{.*}}:{[[@LINE]]:10-[[@LINE]]:10}:"static_cast<int>(" + // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:17-[[@LINE-1]]:17}:")" + 0 || E_int::e; // CHECK: fix-it:{{.*}}:{[[@LINE]]:10-[[@LINE]]:10}:"static_cast<int>(" + // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:17-[[@LINE-1]]:17}:")" + + int a; + a *= E_int::e; // CHECK: fix-it:{{.*}}:{[[@LINE]]:10-[[@LINE]]:10}:"static_cast<int>(" + // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:17-[[@LINE-1]]:17}:")" + a /= E_int::e; // CHECK: fix-it:{{.*}}:{[[@LINE]]:10-[[@LINE]]:10}:"static_cast<int>(" + // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:17-[[@LINE-1]]:17}:")" + a %= E_int::e; // CHECK: fix-it:{{.*}}:{[[@LINE]]:10-[[@LINE]]:10}:"static_cast<int>(" + // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:17-[[@LINE-1]]:17}:")" + a += E_int::e; // CHECK: fix-it:{{.*}}:{[[@LINE]]:10-[[@LINE]]:10}:"static_cast<int>(" + // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:17-[[@LINE-1]]:17}:")" + a -= E_int::e; // CHECK: fix-it:{{.*}}:{[[@LINE]]:10-[[@LINE]]:10}:"static_cast<int>(" + // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:17-[[@LINE-1]]:17}:")" + a <<= E_int::e; // CHECK: fix-it:{{.*}}:{[[@LINE]]:11-[[@LINE]]:11}:"static_cast<int>(" + // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:18-[[@LINE-1]]:18}:")" + a >>= E_int::e; // CHECK: fix-it:{{.*}}:{[[@LINE]]:11-[[@LINE]]:11}:"static_cast<int>(" + // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:18-[[@LINE-1]]:18}:")" + a &= E_int::e; // CHECK: fix-it:{{.*}}:{[[@LINE]]:10-[[@LINE]]:10}:"static_cast<int>(" + // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:17-[[@LINE-1]]:17}:")" + a ^= E_int::e; // CHECK: fix-it:{{.*}}:{[[@LINE]]:10-[[@LINE]]:10}:"static_cast<int>(" + // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:17-[[@LINE-1]]:17}:")" + a |= E_int::e; // CHECK: fix-it:{{.*}}:{[[@LINE]]:10-[[@LINE]]:10}:"static_cast<int>(" + // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:17-[[@LINE-1]]:17}:")" + + // TODO: These do not have the diagnostic yet + E_int b; + b *= 0; + b /= 0; + b %= 0; + b += 0; + b -= 0; + b <<= 0; + b >>= 0; + b &= 0; + b ^= 0; + b |= 0; + + a = E_int::e; + b = 0; + + E_int c = 0; + int d = E_int::e; + } +} diff --git a/clang/test/SemaCXX/enum-scoped.cpp b/clang/test/SemaCXX/enum-scoped.cpp index bf43f2928466f..09f3206c84ccb 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++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 { Val1 = 1L @@ -128,7 +129,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}} } } @@ -325,7 +329,7 @@ namespace PR18044 { int E::*p; // expected-error {{does not point into a class}} using E::f; // expected-error {{no member named 'f'}} - 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; } @@ -364,3 +368,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'}} + } +} 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> _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits