Skip to content

Commit 214a954

Browse files
committed
[Clang] Add diagnostic when scoped enumeration requires an explicit conversion for binary operations
Fixes #24265
1 parent a708b4b commit 214a954

File tree

3 files changed

+97
-22
lines changed

3 files changed

+97
-22
lines changed

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4401,6 +4401,11 @@ def warn_impcast_different_enum_types : Warning<
44014401
def warn_impcast_int_to_enum : Warning<
44024402
"implicit conversion from %0 to enumeration type %1 is invalid in C++">,
44034403
InGroup<ImplicitIntToEnumCast>, DefaultIgnore;
4404+
4405+
def note_no_implicit_conversion_for_scoped_enum
4406+
: Note<"no implicit conversion for scoped enum; consider casting to "
4407+
"underlying type">;
4408+
44044409
def warn_impcast_bool_to_null_pointer : Warning<
44054410
"initialization of pointer of type %0 to null from a constant boolean "
44064411
"expression">, InGroup<BoolConversion>;

clang/include/clang/Sema/Sema.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8065,7 +8065,7 @@ class Sema final : public SemaBase {
80658065
BinaryOperatorKind Opc, QualType *CompLHSTy = nullptr);
80668066
QualType CheckSubtractionOperands( // C99 6.5.6
80678067
ExprResult &LHS, ExprResult &RHS, SourceLocation Loc,
8068-
QualType *CompLHSTy = nullptr);
8068+
BinaryOperatorKind Opc, QualType *CompLHSTy = nullptr);
80698069
QualType CheckShiftOperands( // C99 6.5.7
80708070
ExprResult &LHS, ExprResult &RHS, SourceLocation Loc,
80718071
BinaryOperatorKind Opc, bool IsCompAssign = false);

clang/lib/Sema/SemaExpr.cpp

Lines changed: 91 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10742,6 +10742,45 @@ static void DiagnoseBadDivideOrRemainderValues(Sema& S, ExprResult &LHS,
1074210742
<< IsDiv << RHS.get()->getSourceRange());
1074310743
}
1074410744

10745+
static void diagnoseScopedEnums(Sema &S, const SourceLocation Loc,
10746+
const ExprResult &LHS, const ExprResult &RHS,
10747+
BinaryOperatorKind Opc) {
10748+
const Expr *LHSExpr = LHS.get();
10749+
const Expr *RHSExpr = RHS.get();
10750+
if (!LHSExpr || !RHSExpr)
10751+
return;
10752+
const QualType LHSType = LHSExpr->getType();
10753+
const QualType RHSType = RHSExpr->getType();
10754+
const bool LHSIsScoped = LHSType->isScopedEnumeralType();
10755+
const bool RHSIsScoped = RHSType->isScopedEnumeralType();
10756+
if (!LHSIsScoped && !RHSIsScoped)
10757+
return;
10758+
if (!LHSIsScoped && !LHSType->isIntegralOrUnscopedEnumerationType())
10759+
return;
10760+
if (!RHSIsScoped && !RHSType->isIntegralOrUnscopedEnumerationType())
10761+
return;
10762+
if (BinaryOperator::isAssignmentOp(Opc) && LHSIsScoped)
10763+
return;
10764+
if (LHSIsScoped) {
10765+
SourceLocation LHSBegin = LHSExpr->getBeginLoc();
10766+
QualType LHSIntType =
10767+
LHSType->castAs<EnumType>()->getDecl()->getIntegerType();
10768+
S.Diag(LHSBegin, diag::note_no_implicit_conversion_for_scoped_enum)
10769+
<< FixItHint::CreateInsertion(
10770+
LHSBegin, "static_cast<" + LHSIntType.getAsString() + ">(")
10771+
<< FixItHint::CreateInsertion(LHSExpr->getEndLoc(), ")");
10772+
}
10773+
if (RHSIsScoped) {
10774+
SourceLocation RHSBegin = RHSExpr->getBeginLoc();
10775+
QualType RHSIntType =
10776+
RHSType->castAs<EnumType>()->getDecl()->getIntegerType();
10777+
S.Diag(RHSBegin, diag::note_no_implicit_conversion_for_scoped_enum)
10778+
<< FixItHint::CreateInsertion(
10779+
RHSBegin, "static_cast<" + RHSIntType.getAsString() + ">(")
10780+
<< FixItHint::CreateInsertion(RHSExpr->getEndLoc(), ")");
10781+
}
10782+
}
10783+
1074510784
QualType Sema::CheckMultiplyDivideOperands(ExprResult &LHS, ExprResult &RHS,
1074610785
SourceLocation Loc,
1074710786
bool IsCompAssign, bool IsDiv) {
@@ -10772,9 +10811,14 @@ QualType Sema::CheckMultiplyDivideOperands(ExprResult &LHS, ExprResult &RHS,
1077210811
if (LHS.isInvalid() || RHS.isInvalid())
1077310812
return QualType();
1077410813

10775-
10776-
if (compType.isNull() || !compType->isArithmeticType())
10777-
return InvalidOperands(Loc, LHS, RHS);
10814+
if (compType.isNull() || !compType->isArithmeticType()) {
10815+
InvalidOperands(Loc, LHS, RHS);
10816+
diagnoseScopedEnums(*this, Loc, LHS, RHS,
10817+
IsCompAssign ? IsDiv ? BO_DivAssign : BO_MulAssign
10818+
: IsDiv ? BO_Div
10819+
: BO_Mul);
10820+
return QualType();
10821+
}
1077810822
if (IsDiv) {
1077910823
DetectPrecisionLossInComplexDivision(*this, RHS.get()->getType(), Loc);
1078010824
DiagnoseBadDivideOrRemainderValues(*this, LHS, RHS, Loc, IsDiv);
@@ -10837,8 +10881,12 @@ QualType Sema::CheckRemainderOperands(
1083710881

1083810882
if (compType.isNull() ||
1083910883
(!compType->isIntegerType() &&
10840-
!(getLangOpts().HLSL && compType->isFloatingType())))
10841-
return InvalidOperands(Loc, LHS, RHS);
10884+
!(getLangOpts().HLSL && compType->isFloatingType()))) {
10885+
InvalidOperands(Loc, LHS, RHS);
10886+
diagnoseScopedEnums(*this, Loc, LHS, RHS,
10887+
IsCompAssign ? BO_RemAssign : BO_Rem);
10888+
return QualType();
10889+
}
1084210890
DiagnoseBadDivideOrRemainderValues(*this, LHS, RHS, Loc, false /* IsDiv */);
1084310891
return compType;
1084410892
}
@@ -11194,7 +11242,9 @@ QualType Sema::CheckAdditionOperands(ExprResult &LHS, ExprResult &RHS,
1119411242
} else if (PExp->getType()->isObjCObjectPointerType()) {
1119511243
isObjCPointer = true;
1119611244
} else {
11197-
return InvalidOperands(Loc, LHS, RHS);
11245+
InvalidOperands(Loc, LHS, RHS);
11246+
diagnoseScopedEnums(*this, Loc, LHS, RHS, Opc);
11247+
return QualType();
1119811248
}
1119911249
}
1120011250
assert(PExp->getType()->isAnyPointerType());
@@ -11251,7 +11301,8 @@ QualType Sema::CheckAdditionOperands(ExprResult &LHS, ExprResult &RHS,
1125111301
// C99 6.5.6
1125211302
QualType Sema::CheckSubtractionOperands(ExprResult &LHS, ExprResult &RHS,
1125311303
SourceLocation Loc,
11254-
QualType* CompLHSTy) {
11304+
BinaryOperatorKind Opc,
11305+
QualType *CompLHSTy) {
1125511306
checkArithmeticNull(*this, LHS, RHS, Loc, /*IsCompare=*/false);
1125611307

1125711308
if (LHS.get()->getType()->isVectorType() ||
@@ -11396,7 +11447,9 @@ QualType Sema::CheckSubtractionOperands(ExprResult &LHS, ExprResult &RHS,
1139611447
}
1139711448
}
1139811449

11399-
return InvalidOperands(Loc, LHS, RHS);
11450+
InvalidOperands(Loc, LHS, RHS);
11451+
diagnoseScopedEnums(*this, Loc, LHS, RHS, Opc);
11452+
return QualType();
1140011453
}
1140111454

1140211455
static bool isScopedEnumerationType(QualType T) {
@@ -11744,8 +11797,11 @@ QualType Sema::CheckShiftOperands(ExprResult &LHS, ExprResult &RHS,
1174411797
// Embedded-C 4.1.6.2.2: The LHS may also be fixed-point.
1174511798
if ((!LHSType->isFixedPointOrIntegerType() &&
1174611799
!LHSType->hasIntegerRepresentation()) ||
11747-
!RHSType->hasIntegerRepresentation())
11748-
return InvalidOperands(Loc, LHS, RHS);
11800+
!RHSType->hasIntegerRepresentation()) {
11801+
InvalidOperands(Loc, LHS, RHS);
11802+
diagnoseScopedEnums(*this, Loc, LHS, RHS, Opc);
11803+
return QualType();
11804+
}
1174911805

1175011806
// C++0x: Don't allow scoped enums. FIXME: Use something better than
1175111807
// hasIntegerRepresentation() above instead of this.
@@ -12311,8 +12367,11 @@ static QualType checkArithmeticOrEnumeralThreeWayCompare(Sema &S,
1231112367
S.UsualArithmeticConversions(LHS, RHS, Loc, ArithConvKind::Comparison);
1231212368
if (LHS.isInvalid() || RHS.isInvalid())
1231312369
return QualType();
12314-
if (Type.isNull())
12315-
return S.InvalidOperands(Loc, LHS, RHS);
12370+
if (Type.isNull()) {
12371+
S.InvalidOperands(Loc, LHS, RHS);
12372+
diagnoseScopedEnums(S, Loc, LHS, RHS, BO_Cmp);
12373+
return QualType();
12374+
}
1231612375

1231712376
std::optional<ComparisonCategoryType> CCT =
1231812377
getComparisonCategoryForBuiltinCmp(Type);
@@ -12344,8 +12403,11 @@ static QualType checkArithmeticOrEnumeralCompare(Sema &S, ExprResult &LHS,
1234412403
S.UsualArithmeticConversions(LHS, RHS, Loc, ArithConvKind::Comparison);
1234512404
if (LHS.isInvalid() || RHS.isInvalid())
1234612405
return QualType();
12347-
if (Type.isNull())
12348-
return S.InvalidOperands(Loc, LHS, RHS);
12406+
if (Type.isNull()) {
12407+
S.InvalidOperands(Loc, LHS, RHS);
12408+
diagnoseScopedEnums(S, Loc, LHS, RHS, Opc);
12409+
return QualType();
12410+
}
1234912411
assert(Type->isArithmeticType() || Type->isEnumeralType());
1235012412

1235112413
if (Type->isAnyComplexType() && BinaryOperator::isRelationalOp(Opc))
@@ -13355,7 +13417,9 @@ inline QualType Sema::CheckBitwiseOperands(ExprResult &LHS, ExprResult &RHS,
1335513417

1335613418
if (!compType.isNull() && compType->isIntegralOrUnscopedEnumerationType())
1335713419
return compType;
13358-
return InvalidOperands(Loc, LHS, RHS);
13420+
InvalidOperands(Loc, LHS, RHS);
13421+
diagnoseScopedEnums(*this, Loc, LHS, RHS, Opc);
13422+
return QualType();
1335913423
}
1336013424

1336113425
// C99 6.5.[13,14]
@@ -13457,13 +13521,19 @@ inline QualType Sema::CheckLogicalOperands(ExprResult &LHS, ExprResult &RHS,
1345713521
// C++ [expr.log.or]p1
1345813522
// The operands are both contextually converted to type bool.
1345913523
ExprResult LHSRes = PerformContextuallyConvertToBool(LHS.get());
13460-
if (LHSRes.isInvalid())
13461-
return InvalidOperands(Loc, LHS, RHS);
13524+
if (LHSRes.isInvalid()) {
13525+
InvalidOperands(Loc, LHS, RHS);
13526+
diagnoseScopedEnums(*this, Loc, LHS, RHS, Opc);
13527+
return QualType();
13528+
}
1346213529
LHS = LHSRes;
1346313530

1346413531
ExprResult RHSRes = PerformContextuallyConvertToBool(RHS.get());
13465-
if (RHSRes.isInvalid())
13466-
return InvalidOperands(Loc, LHS, RHS);
13532+
if (RHSRes.isInvalid()) {
13533+
InvalidOperands(Loc, LHS, RHS);
13534+
diagnoseScopedEnums(*this, Loc, LHS, RHS, Opc);
13535+
return QualType();
13536+
}
1346713537
RHS = RHSRes;
1346813538

1346913539
// C++ [expr.log.and]p2
@@ -15069,7 +15139,7 @@ ExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc,
1506915139
break;
1507015140
case BO_Sub:
1507115141
ConvertHalfVec = true;
15072-
ResultTy = CheckSubtractionOperands(LHS, RHS, OpLoc);
15142+
ResultTy = CheckSubtractionOperands(LHS, RHS, OpLoc, Opc);
1507315143
break;
1507415144
case BO_Shl:
1507515145
case BO_Shr:
@@ -15136,7 +15206,7 @@ ExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc,
1513615206
break;
1513715207
case BO_SubAssign:
1513815208
ConvertHalfVec = true;
15139-
CompResultTy = CheckSubtractionOperands(LHS, RHS, OpLoc, &CompLHSTy);
15209+
CompResultTy = CheckSubtractionOperands(LHS, RHS, OpLoc, Opc, &CompLHSTy);
1514015210
if (!CompResultTy.isNull() && !LHS.isInvalid() && !RHS.isInvalid())
1514115211
ResultTy =
1514215212
CheckAssignmentOperands(LHS.get(), RHS, OpLoc, CompResultTy, Opc);

0 commit comments

Comments
 (0)