150 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "ChainedComparisonCheck.h"10#include "clang/AST/ASTContext.h"11#include "clang/ASTMatchers/ASTMatchFinder.h"12#include "llvm/ADT/SmallString.h"13#include "llvm/ADT/SmallVector.h"14#include <algorithm>15 16using namespace clang::ast_matchers;17 18namespace clang::tidy::bugprone {19static bool isExprAComparisonOperator(const Expr *E) {20 if (const auto *Op = dyn_cast_or_null<BinaryOperator>(E->IgnoreImplicit()))21 return Op->isComparisonOp();22 if (const auto *Op =23 dyn_cast_or_null<CXXOperatorCallExpr>(E->IgnoreImplicit()))24 return Op->isComparisonOp();25 return false;26}27 28namespace {29AST_MATCHER(BinaryOperator,30 hasBinaryOperatorAChildComparisonOperatorWithoutParen) {31 return isExprAComparisonOperator(Node.getLHS()) ||32 isExprAComparisonOperator(Node.getRHS());33}34 35AST_MATCHER(CXXOperatorCallExpr,36 hasCppOperatorAChildComparisonOperatorWithoutParen) {37 return llvm::any_of(Node.arguments(), isExprAComparisonOperator);38}39 40struct ChainedComparisonData {41 llvm::SmallString<256U> Name;42 llvm::SmallVector<const Expr *, 32U> Operands;43 44 explicit ChainedComparisonData(const Expr *Op) { extract(Op); }45 46private: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);52};53 54} // namespace55 56void ChainedComparisonData::add(const Expr *Operand) {57 if (!Name.empty())58 Name += ' ';59 Name += 'v';60 Name += std::to_string(Operands.size());61 Operands.push_back(Operand);62}63 64void ChainedComparisonData::add(llvm::StringRef Opcode) {65 Name += ' ';66 Name += Opcode;67}68 69void ChainedComparisonData::extract(const BinaryOperator *Op) {70 const Expr *LHS = Op->getLHS()->IgnoreImplicit();71 if (isExprAComparisonOperator(LHS))72 extract(LHS);73 else74 add(LHS);75 76 add(Op->getOpcodeStr());77 78 const Expr *RHS = Op->getRHS()->IgnoreImplicit();79 if (isExprAComparisonOperator(RHS))80 extract(RHS);81 else82 add(RHS);83}84 85void ChainedComparisonData::extract(const CXXOperatorCallExpr *Op) {86 const Expr *FirstArg = Op->getArg(0U)->IgnoreImplicit();87 if (isExprAComparisonOperator(FirstArg))88 extract(FirstArg);89 else90 add(FirstArg);91 92 add(getOperatorSpelling(Op->getOperator()));93 94 const Expr *SecondArg = Op->getArg(1U)->IgnoreImplicit();95 if (isExprAComparisonOperator(SecondArg))96 extract(SecondArg);97 else98 add(SecondArg);99}100 101void ChainedComparisonData::extract(const Expr *Op) {102 if (!Op)103 return;104 105 if (const auto *BinaryOp = dyn_cast<BinaryOperator>(Op)) {106 extract(BinaryOp);107 return;108 }109 110 if (const auto *OverloadedOp = dyn_cast<CXXOperatorCallExpr>(Op)) {111 if (OverloadedOp->getNumArgs() == 2U)112 extract(OverloadedOp);113 }114}115 116void ChainedComparisonCheck::registerMatchers(MatchFinder *Finder) {117 const auto OperatorMatcher = expr(anyOf(118 binaryOperator(isComparisonOperator(),119 hasBinaryOperatorAChildComparisonOperatorWithoutParen()),120 cxxOperatorCallExpr(121 isComparisonOperator(),122 hasCppOperatorAChildComparisonOperatorWithoutParen())));123 124 Finder->addMatcher(125 expr(OperatorMatcher, unless(hasParent(OperatorMatcher))).bind("op"),126 this);127}128 129void ChainedComparisonCheck::check(const MatchFinder::MatchResult &Result) {130 const auto *MatchedOperator = Result.Nodes.getNodeAs<Expr>("op");131 132 ChainedComparisonData Data(MatchedOperator);133 if (Data.Operands.empty())134 return;135 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();141 142 for (std::size_t Index = 0U; Index < Data.Operands.size(); ++Index) {143 diag(Data.Operands[Index]->getBeginLoc(), "operand 'v%0' is here",144 DiagnosticIDs::Note)145 << Index << Data.Operands[Index]->getSourceRange();146 }147}148 149} // namespace clang::tidy::bugprone150