10#include "clang/AST/ASTContext.h"
11#include "clang/ASTMatchers/ASTMatchFinder.h"
12#include "llvm/ADT/SmallString.h"
13#include "llvm/ADT/SmallVector.h"
20 if (
const auto *Op = dyn_cast_or_null<BinaryOperator>(E->IgnoreImplicit()))
21 return Op->isComparisonOp();
23 dyn_cast_or_null<CXXOperatorCallExpr>(E->IgnoreImplicit()))
24 return Op->isComparisonOp();
29AST_MATCHER(BinaryOperator,
30 hasBinaryOperatorAChildComparisonOperatorWithoutParen) {
35AST_MATCHER(CXXOperatorCallExpr,
36 hasCppOperatorAChildComparisonOperatorWithoutParen) {
40struct ChainedComparisonData {
41 llvm::SmallString<256U> Name;
42 llvm::SmallVector<const Expr *, 32U> Operands;
44 explicit ChainedComparisonData(
const Expr *Op) { extract(Op); }
47 void add(
const Expr *Operand);
48 void add(llvm::StringRef Opcode);
49 void extract(
const Expr *Op);
50 void extract(
const BinaryOperator *Op);
51 void extract(
const CXXOperatorCallExpr *Op);
54void ChainedComparisonData::add(
const Expr *Operand) {
58 Name += std::to_string(Operands.size());
59 Operands.push_back(Operand);
62void ChainedComparisonData::add(llvm::StringRef Opcode) {
67void ChainedComparisonData::extract(
const BinaryOperator *Op) {
68 const Expr *LHS = Op->getLHS()->IgnoreImplicit();
74 add(Op->getOpcodeStr());
76 const Expr *RHS = Op->getRHS()->IgnoreImplicit();
83void ChainedComparisonData::extract(
const CXXOperatorCallExpr *Op) {
84 const Expr *FirstArg = Op->getArg(0U)->IgnoreImplicit();
90 add(getOperatorSpelling(Op->getOperator()));
92 const Expr *SecondArg = Op->getArg(1U)->IgnoreImplicit();
99void ChainedComparisonData::extract(
const Expr *Op) {
103 if (
const auto *BinaryOp = dyn_cast<BinaryOperator>(Op)) {
108 if (
const auto *OverloadedOp = dyn_cast<CXXOperatorCallExpr>(Op)) {
109 if (OverloadedOp->getNumArgs() == 2U)
110 extract(OverloadedOp);
117 const auto OperatorMatcher = expr(anyOf(
118 binaryOperator(isComparisonOperator(),
119 hasBinaryOperatorAChildComparisonOperatorWithoutParen()),
121 isComparisonOperator(),
122 hasCppOperatorAChildComparisonOperatorWithoutParen())));
125 expr(OperatorMatcher, unless(hasParent(OperatorMatcher))).bind(
"op"),
130 const auto *MatchedOperator = Result.Nodes.getNodeAs<Expr>(
"op");
132 ChainedComparisonData Data(MatchedOperator);
133 if (Data.Operands.empty())
136 diag(MatchedOperator->getBeginLoc(),
137 "chained comparison '%0' may generate unintended results, use "
138 "parentheses to specify order of evaluation or a logical operator to "
139 "separate comparison expressions")
140 << llvm::StringRef(Data.Name).trim() << MatchedOperator->getSourceRange();
142 for (std::size_t Index = 0U; Index < Data.Operands.size(); ++Index) {
143 diag(Data.Operands[Index]->getBeginLoc(),
"operand 'v%0' is here",
145 << Index << Data.Operands[Index]->getSourceRange();
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
void registerMatchers(ast_matchers::MatchFinder *Finder) override
static bool isExprAComparisonOperator(const Expr *E)