bolt/deps/llvm-18.1.8/clang-tools-extra/clang-tidy/bugprone/ChainedComparisonCheck.cpp
2025-02-14 19:21:04 +01:00

150 lines
4.4 KiB
C++

//===--- ChainedComparisonCheck.cpp - clang-tidy --------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "ChainedComparisonCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/SmallVector.h"
#include <algorithm>
using namespace clang::ast_matchers;
namespace clang::tidy::bugprone {
static bool isExprAComparisonOperator(const Expr *E) {
if (const auto *Op = dyn_cast_or_null<BinaryOperator>(E->IgnoreImplicit()))
return Op->isComparisonOp();
if (const auto *Op =
dyn_cast_or_null<CXXOperatorCallExpr>(E->IgnoreImplicit()))
return Op->isComparisonOp();
return false;
}
namespace {
AST_MATCHER(BinaryOperator,
hasBinaryOperatorAChildComparisonOperatorWithoutParen) {
return isExprAComparisonOperator(Node.getLHS()) ||
isExprAComparisonOperator(Node.getRHS());
}
AST_MATCHER(CXXOperatorCallExpr,
hasCppOperatorAChildComparisonOperatorWithoutParen) {
return std::any_of(Node.arg_begin(), Node.arg_end(),
isExprAComparisonOperator);
}
struct ChainedComparisonData {
llvm::SmallString<256U> Name;
llvm::SmallVector<const Expr *, 32U> Operands;
explicit ChainedComparisonData(const Expr *Op) { extract(Op); }
private:
void add(const Expr *Operand);
void add(llvm::StringRef Opcode);
void extract(const Expr *Op);
void extract(const BinaryOperator *Op);
void extract(const CXXOperatorCallExpr *Op);
};
void ChainedComparisonData::add(const Expr *Operand) {
if (!Name.empty())
Name += ' ';
Name += 'v';
Name += std::to_string(Operands.size());
Operands.push_back(Operand);
}
void ChainedComparisonData::add(llvm::StringRef Opcode) {
Name += ' ';
Name += Opcode;
}
void ChainedComparisonData::extract(const BinaryOperator *Op) {
const Expr *LHS = Op->getLHS()->IgnoreImplicit();
if (isExprAComparisonOperator(LHS))
extract(LHS);
else
add(LHS);
add(Op->getOpcodeStr());
const Expr *RHS = Op->getRHS()->IgnoreImplicit();
if (isExprAComparisonOperator(RHS))
extract(RHS);
else
add(RHS);
}
void ChainedComparisonData::extract(const CXXOperatorCallExpr *Op) {
const Expr *FirstArg = Op->getArg(0U)->IgnoreImplicit();
if (isExprAComparisonOperator(FirstArg))
extract(FirstArg);
else
add(FirstArg);
add(getOperatorSpelling(Op->getOperator()));
const Expr *SecondArg = Op->getArg(1U)->IgnoreImplicit();
if (isExprAComparisonOperator(SecondArg))
extract(SecondArg);
else
add(SecondArg);
}
void ChainedComparisonData::extract(const Expr *Op) {
if (!Op)
return;
if (const auto *BinaryOp = dyn_cast<BinaryOperator>(Op)) {
extract(BinaryOp);
return;
}
if (const auto *OverloadedOp = dyn_cast<CXXOperatorCallExpr>(Op)) {
if (OverloadedOp->getNumArgs() == 2U)
extract(OverloadedOp);
}
}
} // namespace
void ChainedComparisonCheck::registerMatchers(MatchFinder *Finder) {
const auto OperatorMatcher = expr(anyOf(
binaryOperator(isComparisonOperator(),
hasBinaryOperatorAChildComparisonOperatorWithoutParen()),
cxxOperatorCallExpr(
isComparisonOperator(),
hasCppOperatorAChildComparisonOperatorWithoutParen())));
Finder->addMatcher(
expr(OperatorMatcher, unless(hasParent(OperatorMatcher))).bind("op"),
this);
}
void ChainedComparisonCheck::check(const MatchFinder::MatchResult &Result) {
const auto *MatchedOperator = Result.Nodes.getNodeAs<Expr>("op");
ChainedComparisonData Data(MatchedOperator);
if (Data.Operands.empty())
return;
diag(MatchedOperator->getBeginLoc(),
"chained comparison '%0' may generate unintended results, use "
"parentheses to specify order of evaluation or a logical operator to "
"separate comparison expressions")
<< llvm::StringRef(Data.Name).trim() << MatchedOperator->getSourceRange();
for (std::size_t Index = 0U; Index < Data.Operands.size(); ++Index) {
diag(Data.Operands[Index]->getBeginLoc(), "operand 'v%0' is here",
DiagnosticIDs::Note)
<< Index << Data.Operands[Index]->getSourceRange();
}
}
} // namespace clang::tidy::bugprone