1//===--- ChainedComparisonCheck.cpp - clang-tidy --------------------------===//
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-exception
6//
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>(Val: E->IgnoreImplicit()))
21 return Op->isComparisonOp();
22 if (const auto *Op =
23 dyn_cast_or_null<CXXOperatorCallExpr>(Val: E->IgnoreImplicit()))
24 return Op->isComparisonOp();
25 return false;
26}
27
28namespace {
29AST_MATCHER(BinaryOperator,
30 hasBinaryOperatorAChildComparisonOperatorWithoutParen) {
31 return isExprAComparisonOperator(E: Node.getLHS()) ||
32 isExprAComparisonOperator(E: 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
54void ChainedComparisonData::add(const Expr *Operand) {
55 if (!Name.empty())
56 Name += ' ';
57 Name += 'v';
58 Name += std::to_string(val: Operands.size());
59 Operands.push_back(Elt: Operand);
60}
61
62void ChainedComparisonData::add(llvm::StringRef Opcode) {
63 Name += ' ';
64 Name += Opcode;
65}
66
67void ChainedComparisonData::extract(const BinaryOperator *Op) {
68 const Expr *LHS = Op->getLHS()->IgnoreImplicit();
69 if (isExprAComparisonOperator(E: LHS))
70 extract(Op: LHS);
71 else
72 add(Operand: LHS);
73
74 add(Opcode: Op->getOpcodeStr());
75
76 const Expr *RHS = Op->getRHS()->IgnoreImplicit();
77 if (isExprAComparisonOperator(E: RHS))
78 extract(Op: RHS);
79 else
80 add(Operand: RHS);
81}
82
83void ChainedComparisonData::extract(const CXXOperatorCallExpr *Op) {
84 const Expr *FirstArg = Op->getArg(0U)->IgnoreImplicit();
85 if (isExprAComparisonOperator(E: FirstArg))
86 extract(Op: FirstArg);
87 else
88 add(Operand: FirstArg);
89
90 add(Opcode: getOperatorSpelling(Operator: Op->getOperator()));
91
92 const Expr *SecondArg = Op->getArg(1U)->IgnoreImplicit();
93 if (isExprAComparisonOperator(E: SecondArg))
94 extract(Op: SecondArg);
95 else
96 add(Operand: SecondArg);
97}
98
99void ChainedComparisonData::extract(const Expr *Op) {
100 if (!Op)
101 return;
102
103 if (const auto *BinaryOp = dyn_cast<BinaryOperator>(Val: Op)) {
104 extract(Op: BinaryOp);
105 return;
106 }
107
108 if (const auto *OverloadedOp = dyn_cast<CXXOperatorCallExpr>(Val: Op)) {
109 if (OverloadedOp->getNumArgs() == 2U)
110 extract(Op: OverloadedOp);
111 }
112}
113
114} // namespace
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 NodeMatch: expr(OperatorMatcher, unless(hasParent(OperatorMatcher))).bind(ID: "op"),
126 Action: this);
127}
128
129void ChainedComparisonCheck::check(const MatchFinder::MatchResult &Result) {
130 const auto *MatchedOperator = Result.Nodes.getNodeAs<Expr>(ID: "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::bugprone
150

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

source code of clang-tools-extra/clang-tidy/bugprone/ChainedComparisonCheck.cpp