From 92c22e604b8d0b9a37c22b1dc7831bf02199f1f1 Mon Sep 17 00:00:00 2001 From: GrumpyPigSkin Date: Mon, 17 Nov 2025 21:28:33 +0000 Subject: [PATCH 1/6] [clang][diagnostics] added warning for possible enum compare typo --- .../clang/Basic/DiagnosticSemaKinds.td | 10 +++++ clang/lib/Sema/SemaDecl.cpp | 24 ++++++++++++ clang/test/Sema/warn-enum-compare-typo.c | 38 +++++++++++++++++++ 3 files changed, 72 insertions(+) create mode 100644 clang/test/Sema/warn-enum-compare-typo.c diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 3e864475f22a1..b4da42584c78f 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -13707,4 +13707,14 @@ def err_amdgcn_load_lds_size_invalid_value : Error<"invalid size value">; def note_amdgcn_load_lds_size_valid_value : Note<"size must be %select{1, 2, or 4|1, 2, 4, 12 or 16}0">; def err_amdgcn_coop_atomic_invalid_as : Error<"cooperative atomic requires a global or generic pointer">; + +def warn_comparison_in_enum_initializer + : Warning<"comparison operator '%0' in enumerator constant is likely a " + "typo for " + "the shift operator '%1'">, + InGroup>; + +def note_enum_compare_typo_suggest + : Note<"use '%0' to perform a bitwise shift">; + } // end of sema component. diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 086dd8ba1c670..ae3e6f2f49917 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -20129,6 +20129,30 @@ EnumConstantDecl *Sema::CheckEnumConstant(EnumDecl *Enum, if (Val) Val = DefaultLvalueConversion(Val).get(); + if (Val) { + if (const BinaryOperator *BinOp = + dyn_cast(Val->IgnoreParenImpCasts())) { + if (BinOp->getOpcode() == BO_LT || BinOp->getOpcode() == BO_GT) { + const Expr *LHS = BinOp->getLHS()->IgnoreParenImpCasts(); + if (const auto *IntLiteral = dyn_cast(LHS)) { + if (IntLiteral->getValue() == 1) { + auto suggestedOp = (BinOp->getOpcode() == BO_LT) + ? BinaryOperator::getOpcodeStr(BO_Shl) + : BinaryOperator::getOpcodeStr(BO_Shr); + SourceLocation OperatorLoc = BinOp->getOperatorLoc(); + + Diag(OperatorLoc, diag::warn_comparison_in_enum_initializer) + << BinOp->getOpcodeStr() << suggestedOp; + + Diag(OperatorLoc, diag::note_enum_compare_typo_suggest) + << suggestedOp + << FixItHint::CreateReplacement(OperatorLoc, suggestedOp); + } + } + } + } + } + if (Val) { if (Enum->isDependentType() || Val->isTypeDependent() || Val->containsErrors()) diff --git a/clang/test/Sema/warn-enum-compare-typo.c b/clang/test/Sema/warn-enum-compare-typo.c new file mode 100644 index 0000000000000..ee6ef0676b4b8 --- /dev/null +++ b/clang/test/Sema/warn-enum-compare-typo.c @@ -0,0 +1,38 @@ +// RUN: %clang_cc1 -fsyntax-only -Wenum-compare-typo -verify %s +// RUN: %clang_cc1 -fsyntax-only -Wenum-compare-typo -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s + +enum E { + kOptionGood1 = 1ull << 0, + kOptionGood2 = 1ull >> 0, + // expected-warning@+3 {{comparison operator '<' in enumerator constant is likely a typo for the shift operator '<<'}} + // expected-note@+2 {{use '<<' to perform a bitwise shift}} + // CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:22-[[@LINE+1]]:23}:"<<" + kOptionBad1 = 1ull < 1, + // expected-warning@+3 {{comparison operator '>' in enumerator constant is likely a typo for the shift operator '>>'}} + // expected-note@+2 {{use '>>' to perform a bitwise shift}} + // CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:19-[[@LINE+1]]:20}:">>" + kOptionBad2 = 1 > 3, + // expected-warning@+3 {{comparison operator '>' in enumerator constant is likely a typo for the shift operator '>>'}} + // expected-note@+2 {{use '>>' to perform a bitwise shift}} + // CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:20-[[@LINE+1]]:21}:">>" + kOptionBad3 = (1 > 2) +}; + +// Ensure the warning does not fire on valid code +enum F { + kSomeValue = 10, + kComparison = kSomeValue > 5, // No warning + kMaxEnum = 255, + kIsValid = kMaxEnum > 0, // No warning + kSum = 10 + 20, // No warning + kShift = 2 << 5 // No warning +}; + +// Ensure the diagnostic group works + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wenum-compare-typo" +enum G { + kIgnored = 1 < 10 // No warning +}; +#pragma clang diagnostic pop From 2db6fa40ab5fd57e7e4dc579d97b7ba8048bf291 Mon Sep 17 00:00:00 2001 From: GrumpyPigSkin Date: Tue, 18 Nov 2025 22:19:41 +0000 Subject: [PATCH 2/6] [clang][diagnostics] Improved warning message for enum-compare-typo --- clang/include/clang/Basic/DiagnosticSemaKinds.td | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index b4da42584c78f..69a1c755066ee 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -13709,9 +13709,7 @@ def note_amdgcn_load_lds_size_valid_value : Note<"size must be %select{1, 2, or def err_amdgcn_coop_atomic_invalid_as : Error<"cooperative atomic requires a global or generic pointer">; def warn_comparison_in_enum_initializer - : Warning<"comparison operator '%0' in enumerator constant is likely a " - "typo for " - "the shift operator '%1'">, + : Warning<"comparison operator '%0' is potentially a typo for a shift operator '%1'">, InGroup>; def note_enum_compare_typo_suggest From 7fb63d37260b2e55924273b4678dda4816ec6c0c Mon Sep 17 00:00:00 2001 From: GrumpyPigSkin Date: Tue, 18 Nov 2025 22:20:43 +0000 Subject: [PATCH 3/6] [clang][diagnostics] Moved check for enum-compare-typo and improved detection of typos --- clang/lib/Sema/SemaDecl.cpp | 80 ++++++++++++++++++++++++++----------- 1 file changed, 56 insertions(+), 24 deletions(-) diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index ae3e6f2f49917..73d1b8772b5e6 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -20129,30 +20129,6 @@ EnumConstantDecl *Sema::CheckEnumConstant(EnumDecl *Enum, if (Val) Val = DefaultLvalueConversion(Val).get(); - if (Val) { - if (const BinaryOperator *BinOp = - dyn_cast(Val->IgnoreParenImpCasts())) { - if (BinOp->getOpcode() == BO_LT || BinOp->getOpcode() == BO_GT) { - const Expr *LHS = BinOp->getLHS()->IgnoreParenImpCasts(); - if (const auto *IntLiteral = dyn_cast(LHS)) { - if (IntLiteral->getValue() == 1) { - auto suggestedOp = (BinOp->getOpcode() == BO_LT) - ? BinaryOperator::getOpcodeStr(BO_Shl) - : BinaryOperator::getOpcodeStr(BO_Shr); - SourceLocation OperatorLoc = BinOp->getOperatorLoc(); - - Diag(OperatorLoc, diag::warn_comparison_in_enum_initializer) - << BinOp->getOpcodeStr() << suggestedOp; - - Diag(OperatorLoc, diag::note_enum_compare_typo_suggest) - << suggestedOp - << FixItHint::CreateReplacement(OperatorLoc, suggestedOp); - } - } - } - } - } - if (Val) { if (Enum->isDependentType() || Val->isTypeDependent() || Val->containsErrors()) @@ -20608,6 +20584,61 @@ bool Sema::IsValueInFlagEnum(const EnumDecl *ED, const llvm::APInt &Val, return !(FlagMask & Val) || (AllowMask && !(FlagMask & ~Val)); } +// Emits a warning when a suspicious comparison operator is used along side +// binary operators in enum initializers. +static void CheckForComparisonInEnumInitializer(SemaBase &Sema, + const EnumDecl *Enum) { + bool HasBitwiseOp = false; + SmallVector SuspiciousCompares; + + // Iterate over all the enum values, gather suspisious comparison ops and + // whether any enum initialisers contain a binary operator. + for (const auto *ECD : Enum->enumerators()) { + const Expr *InitExpr = ECD->getInitExpr(); + if (!InitExpr) + continue; + + const Expr *E = InitExpr->IgnoreParenImpCasts(); + + if (const auto *BinOp = dyn_cast(E)) { + BinaryOperatorKind Op = BinOp->getOpcode(); + + // Check for bitwise ops (<<, >>, &, |) + if (Op == BO_Shl || Op == BO_Shr || Op == BO_And || Op == BO_Or) { + HasBitwiseOp = true; + } + // Check for the typo pattern (Comparison < or >) + else if (Op == BO_LT || Op == BO_GT) { + const Expr *LHS = BinOp->getLHS()->IgnoreParenImpCasts(); + if (const auto *IntLiteral = dyn_cast(LHS)) { + // Specifically looking for accidental bitshifts "1 < X" or "1 > X" + if (IntLiteral->getValue() == 1) { + SuspiciousCompares.push_back(BinOp); + } + } + } + } + } + + // If we found a bitwise op and some sus compares, iterate over the compares + // and warn. + if (HasBitwiseOp && !SuspiciousCompares.empty()) { + for (const auto *BinOp : SuspiciousCompares) { + auto suggestedOp = (BinOp->getOpcode() == BO_LT) + ? BinaryOperator::getOpcodeStr(BO_Shl) + : BinaryOperator::getOpcodeStr(BO_Shr); + SourceLocation OperatorLoc = BinOp->getOperatorLoc(); + + Sema.Diag(OperatorLoc, diag::warn_comparison_in_enum_initializer) + << BinOp->getOpcodeStr() << suggestedOp; + + Sema.Diag(OperatorLoc, diag::note_enum_compare_typo_suggest) + << suggestedOp + << FixItHint::CreateReplacement(OperatorLoc, suggestedOp); + } + } +} + void Sema::ActOnEnumBody(SourceLocation EnumLoc, SourceRange BraceRange, Decl *EnumDeclX, ArrayRef Elements, Scope *S, const ParsedAttributesView &Attrs) { @@ -20745,6 +20776,7 @@ void Sema::ActOnEnumBody(SourceLocation EnumLoc, SourceRange BraceRange, NumPositiveBits, NumNegativeBits); CheckForDuplicateEnumValues(*this, Elements, Enum, EnumType); + CheckForComparisonInEnumInitializer(*this, Enum); if (Enum->isClosedFlag()) { for (Decl *D : Elements) { From c026f8b3f9b4eaaa9e87e505d238a8f1298a6615 Mon Sep 17 00:00:00 2001 From: GrumpyPigSkin Date: Tue, 18 Nov 2025 22:21:18 +0000 Subject: [PATCH 4/6] [clang][diagnostics] Updated test code for enum-compare-typo --- clang/test/Sema/warn-enum-compare-typo.c | 106 ++++++++++++++++++----- 1 file changed, 83 insertions(+), 23 deletions(-) diff --git a/clang/test/Sema/warn-enum-compare-typo.c b/clang/test/Sema/warn-enum-compare-typo.c index ee6ef0676b4b8..f937df1e6cd87 100644 --- a/clang/test/Sema/warn-enum-compare-typo.c +++ b/clang/test/Sema/warn-enum-compare-typo.c @@ -1,38 +1,98 @@ // RUN: %clang_cc1 -fsyntax-only -Wenum-compare-typo -verify %s // RUN: %clang_cc1 -fsyntax-only -Wenum-compare-typo -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s -enum E { - kOptionGood1 = 1ull << 0, - kOptionGood2 = 1ull >> 0, - // expected-warning@+3 {{comparison operator '<' in enumerator constant is likely a typo for the shift operator '<<'}} + +enum PossibleTypoLeft { + Val1 = 1 << 0, + // expected-warning@+3 {{comparison operator '<' is potentially a typo for a shift operator '<<'}} // expected-note@+2 {{use '<<' to perform a bitwise shift}} - // CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:22-[[@LINE+1]]:23}:"<<" - kOptionBad1 = 1ull < 1, - // expected-warning@+3 {{comparison operator '>' in enumerator constant is likely a typo for the shift operator '>>'}} + // CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:12-[[@LINE+1]]:13}:"<<" + Bad1 = 1 < 2, + // expected-warning@+3 {{comparison operator '>' is potentially a typo for a shift operator '>>'}} // expected-note@+2 {{use '>>' to perform a bitwise shift}} - // CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:19-[[@LINE+1]]:20}:">>" - kOptionBad2 = 1 > 3, - // expected-warning@+3 {{comparison operator '>' in enumerator constant is likely a typo for the shift operator '>>'}} + // CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:12-[[@LINE+1]]:13}:">>" + Bad2 = 1 > 3, + // expected-warning@+3 {{comparison operator '>' is potentially a typo for a shift operator '>>'}} // expected-note@+2 {{use '>>' to perform a bitwise shift}} - // CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:20-[[@LINE+1]]:21}:">>" - kOptionBad3 = (1 > 2) + // CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:13-[[@LINE+1]]:14}:">>" + Bad3 = (1 > 3) }; -// Ensure the warning does not fire on valid code -enum F { - kSomeValue = 10, - kComparison = kSomeValue > 5, // No warning - kMaxEnum = 255, - kIsValid = kMaxEnum > 0, // No warning - kSum = 10 + 20, // No warning - kShift = 2 << 5 // No warning +enum PossibleTypoRight { + Val2 = 1 >> 0, + // expected-warning@+3 {{comparison operator '<' is potentially a typo for a shift operator '<<'}} + // expected-note@+2 {{use '<<' to perform a bitwise shift}} + // CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:12-[[@LINE+1]]:13}:"<<" + Bad4 = 1 < 2, + // expected-warning@+3 {{comparison operator '>' is potentially a typo for a shift operator '>>'}} + // expected-note@+2 {{use '>>' to perform a bitwise shift}} + // CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:12-[[@LINE+1]]:13}:">>" + Bad5 = 1 > 3, + // expected-warning@+3 {{comparison operator '<' is potentially a typo for a shift operator '<<'}} + // expected-note@+2 {{use '<<' to perform a bitwise shift}} + // CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:13-[[@LINE+1]]:14}:"<<" + Bad6 = (1 < 3) }; -// Ensure the diagnostic group works +// Case 3: Context provided by other bitwise operators (&, |) +// Even though there are no shifts, the presence of '|' implies flags. +enum PossibleTypoBitwiseOr { + FlagA = 0x1, + FlagB = 0x2, + FlagCombo = FlagA | FlagB, + // expected-warning@+3 {{comparison operator '<' is potentially a typo for a shift operator '<<'}} + // expected-note@+2 {{use '<<' to perform a bitwise shift}} + // CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:17-[[@LINE+1]]:18}:"<<" + FlagTypo1 = 1 < FlagCombo, + // expected-warning@+3 {{comparison operator '>' is potentially a typo for a shift operator '>>'}} + // expected-note@+2 {{use '>>' to perform a bitwise shift}} + // CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:17-[[@LINE+1]]:18}:">>" + FlagTypo2 = 1 > FlagCombo +}; +enum PossibleTypoBitwiseAnd { + FlagAnd = FlagA & FlagB, + // expected-warning@+3 {{comparison operator '<' is potentially a typo for a shift operator '<<'}} + // expected-note@+2 {{use '<<' to perform a bitwise shift}} + // CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:17-[[@LINE+1]]:18}:"<<" + FlagTypo3 = 1 < FlagAnd, + // expected-warning@+3 {{comparison operator '>' is potentially a typo for a shift operator '>>'}} + // expected-note@+2 {{use '>>' to perform a bitwise shift}} + // CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:17-[[@LINE+1]]:18}:">>" + FlagTypo4 = 1 > FlagAnd +}; + +enum NoWarningOnDirectInit { + A = 0, + B = 1, + Ok1 = 1 < 2, // No warning expected + Ok2 = 1 > 2 // No warning expected +}; + +enum NoWarningOnArith { + D = 0 + 1, + E = D * 10, + F = E - D, + G = F / D, + Ok3 = 1 < E, // No warning expected + Ok4 = 1 > E // No warning expected +}; + +enum NoWarningOnExplicitCast { + Bit1 = 1 << 0, + Ok5 = (int)(1 < 2) // No warning expected +}; + +enum NoWarningOnNoneBitShift { + Bit2 = 1 << 0, + Ok6 = (3 < 2) // No warning expected +}; + +// Ensure the diagnostic group works #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wenum-compare-typo" -enum G { - kIgnored = 1 < 10 // No warning +enum IGNORED { + Ok7 = 1 << 1, + Ignored3 = 1 < 10 // No warning }; #pragma clang diagnostic pop From 6803a95dcf0f3a2bff275ca9ebe6ac55c9cd628f Mon Sep 17 00:00:00 2001 From: GrumpyPigSkin Date: Tue, 18 Nov 2025 22:30:25 +0000 Subject: [PATCH 5/6] [clang][diagnostics] Code formatting --- clang/lib/Sema/SemaDecl.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 73d1b8772b5e6..2c1d2de4134fc 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -20589,10 +20589,10 @@ bool Sema::IsValueInFlagEnum(const EnumDecl *ED, const llvm::APInt &Val, static void CheckForComparisonInEnumInitializer(SemaBase &Sema, const EnumDecl *Enum) { bool HasBitwiseOp = false; - SmallVector SuspiciousCompares; + SmallVector SuspiciousCompares; // Iterate over all the enum values, gather suspisious comparison ops and - // whether any enum initialisers contain a binary operator. + // whether any enum initialisers contain a binary operator. for (const auto *ECD : Enum->enumerators()) { const Expr *InitExpr = ECD->getInitExpr(); if (!InitExpr) From bda83e33cbf7ccba86d0a0a39b7c00afc11a7de1 Mon Sep 17 00:00:00 2001 From: GrumpyPigSkin Date: Wed, 19 Nov 2025 21:15:56 +0000 Subject: [PATCH 6/6] [clang][diagnostics] Implemented code review comments and added release note. --- clang/docs/ReleaseNotes.rst | 4 +++ .../clang/Basic/DiagnosticSemaKinds.td | 10 ++++---- clang/lib/Sema/SemaDecl.cpp | 25 +++++++++---------- 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index e9c0723f6787f..8f7366008f967 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -415,6 +415,10 @@ Improvements to Clang's diagnostics - Clang now emits a diagnostic in case `vector_size` or `ext_vector_type` attributes are used with a negative size (#GH165463). +- A new warning ``-Wenum-compare-typo`` has been added to detect potential erroneous + comparison operators when mixed with bitwise operators in enum value initializers. + This can be locally disabled by explicitly casting the initializer value. + Improvements to Clang's time-trace ---------------------------------- diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 69a1c755066ee..ea6853dd9232b 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -13708,11 +13708,11 @@ def note_amdgcn_load_lds_size_valid_value : Note<"size must be %select{1, 2, or def err_amdgcn_coop_atomic_invalid_as : Error<"cooperative atomic requires a global or generic pointer">; -def warn_comparison_in_enum_initializer - : Warning<"comparison operator '%0' is potentially a typo for a shift operator '%1'">, - InGroup>; +def warn_comparison_in_enum_initializer : Warning< + "comparison operator '%0' is potentially a typo for a shift operator '%1'">, + InGroup>; -def note_enum_compare_typo_suggest - : Note<"use '%0' to perform a bitwise shift">; +def note_enum_compare_typo_suggest : Note< + "use '%0' to perform a bitwise shift">; } // end of sema component. diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 2c1d2de4134fc..c45b10f196434 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -62,6 +62,7 @@ #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" #include "llvm/Support/SaveAndRestore.h" #include "llvm/TargetParser/Triple.h" #include @@ -20604,17 +20605,15 @@ static void CheckForComparisonInEnumInitializer(SemaBase &Sema, BinaryOperatorKind Op = BinOp->getOpcode(); // Check for bitwise ops (<<, >>, &, |) - if (Op == BO_Shl || Op == BO_Shr || Op == BO_And || Op == BO_Or) { + if (BinOp->isBitwiseOp() || BinOp->isShiftOp()) { HasBitwiseOp = true; - } - // Check for the typo pattern (Comparison < or >) - else if (Op == BO_LT || Op == BO_GT) { + } else if (Op == BO_LT || Op == BO_GT) { + // Check for the typo pattern (Comparison < or >) const Expr *LHS = BinOp->getLHS()->IgnoreParenImpCasts(); if (const auto *IntLiteral = dyn_cast(LHS)) { // Specifically looking for accidental bitshifts "1 < X" or "1 > X" - if (IntLiteral->getValue() == 1) { + if (IntLiteral->getValue() == 1) SuspiciousCompares.push_back(BinOp); - } } } } @@ -20622,19 +20621,19 @@ static void CheckForComparisonInEnumInitializer(SemaBase &Sema, // If we found a bitwise op and some sus compares, iterate over the compares // and warn. - if (HasBitwiseOp && !SuspiciousCompares.empty()) { + if (HasBitwiseOp) { for (const auto *BinOp : SuspiciousCompares) { - auto suggestedOp = (BinOp->getOpcode() == BO_LT) - ? BinaryOperator::getOpcodeStr(BO_Shl) - : BinaryOperator::getOpcodeStr(BO_Shr); + StringRef SuggestedOp = (BinOp->getOpcode() == BO_LT) + ? BinaryOperator::getOpcodeStr(BO_Shl) + : BinaryOperator::getOpcodeStr(BO_Shr); SourceLocation OperatorLoc = BinOp->getOperatorLoc(); Sema.Diag(OperatorLoc, diag::warn_comparison_in_enum_initializer) - << BinOp->getOpcodeStr() << suggestedOp; + << BinOp->getOpcodeStr() << SuggestedOp; Sema.Diag(OperatorLoc, diag::note_enum_compare_typo_suggest) - << suggestedOp - << FixItHint::CreateReplacement(OperatorLoc, suggestedOp); + << SuggestedOp + << FixItHint::CreateReplacement(OperatorLoc, SuggestedOp); } } }