1 | //===--- RedundantExpressionCheck.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 "RedundantExpressionCheck.h" |
10 | #include "../utils/Matchers.h" |
11 | #include "clang/AST/ASTContext.h" |
12 | #include "clang/ASTMatchers/ASTMatchFinder.h" |
13 | #include "clang/Basic/LLVM.h" |
14 | #include "clang/Basic/SourceLocation.h" |
15 | #include "clang/Basic/SourceManager.h" |
16 | #include "clang/Lex/Lexer.h" |
17 | #include "llvm/ADT/APInt.h" |
18 | #include "llvm/ADT/APSInt.h" |
19 | #include "llvm/ADT/FoldingSet.h" |
20 | #include "llvm/ADT/SmallBitVector.h" |
21 | #include "llvm/Support/FormatVariadic.h" |
22 | #include <algorithm> |
23 | #include <cassert> |
24 | #include <cstdint> |
25 | #include <optional> |
26 | #include <string> |
27 | |
28 | using namespace clang::ast_matchers; |
29 | using namespace clang::tidy::matchers; |
30 | |
31 | namespace clang::tidy::misc { |
32 | namespace { |
33 | using llvm::APSInt; |
34 | |
35 | static constexpr llvm::StringLiteral KnownBannedMacroNames[] = { |
36 | "EAGAIN" , |
37 | "EWOULDBLOCK" , |
38 | "SIGCLD" , |
39 | "SIGCHLD" , |
40 | }; |
41 | |
42 | static bool incrementWithoutOverflow(const APSInt &Value, APSInt &Result) { |
43 | Result = Value; |
44 | ++Result; |
45 | return Value < Result; |
46 | } |
47 | |
48 | static bool areEquivalentNameSpecifier(const NestedNameSpecifier *Left, |
49 | const NestedNameSpecifier *Right) { |
50 | llvm::FoldingSetNodeID LeftID, RightID; |
51 | Left->Profile(ID&: LeftID); |
52 | Right->Profile(ID&: RightID); |
53 | return LeftID == RightID; |
54 | } |
55 | |
56 | static bool areEquivalentExpr(const Expr *Left, const Expr *Right) { |
57 | if (!Left || !Right) |
58 | return !Left && !Right; |
59 | |
60 | Left = Left->IgnoreParens(); |
61 | Right = Right->IgnoreParens(); |
62 | |
63 | // Compare classes. |
64 | if (Left->getStmtClass() != Right->getStmtClass()) |
65 | return false; |
66 | |
67 | // Compare children. |
68 | Expr::const_child_iterator LeftIter = Left->child_begin(); |
69 | Expr::const_child_iterator RightIter = Right->child_begin(); |
70 | while (LeftIter != Left->child_end() && RightIter != Right->child_end()) { |
71 | if (!areEquivalentExpr(Left: dyn_cast_or_null<Expr>(Val: *LeftIter), |
72 | Right: dyn_cast_or_null<Expr>(Val: *RightIter))) |
73 | return false; |
74 | ++LeftIter; |
75 | ++RightIter; |
76 | } |
77 | if (LeftIter != Left->child_end() || RightIter != Right->child_end()) |
78 | return false; |
79 | |
80 | // Perform extra checks. |
81 | switch (Left->getStmtClass()) { |
82 | default: |
83 | return false; |
84 | |
85 | case Stmt::CharacterLiteralClass: |
86 | return cast<CharacterLiteral>(Val: Left)->getValue() == |
87 | cast<CharacterLiteral>(Val: Right)->getValue(); |
88 | case Stmt::IntegerLiteralClass: { |
89 | llvm::APInt LeftLit = cast<IntegerLiteral>(Val: Left)->getValue(); |
90 | llvm::APInt RightLit = cast<IntegerLiteral>(Val: Right)->getValue(); |
91 | return LeftLit.getBitWidth() == RightLit.getBitWidth() && |
92 | LeftLit == RightLit; |
93 | } |
94 | case Stmt::FloatingLiteralClass: |
95 | return cast<FloatingLiteral>(Val: Left)->getValue().bitwiseIsEqual( |
96 | RHS: cast<FloatingLiteral>(Val: Right)->getValue()); |
97 | case Stmt::StringLiteralClass: |
98 | return cast<StringLiteral>(Val: Left)->getBytes() == |
99 | cast<StringLiteral>(Val: Right)->getBytes(); |
100 | case Stmt::CXXOperatorCallExprClass: |
101 | return cast<CXXOperatorCallExpr>(Val: Left)->getOperator() == |
102 | cast<CXXOperatorCallExpr>(Val: Right)->getOperator(); |
103 | case Stmt::DependentScopeDeclRefExprClass: |
104 | if (cast<DependentScopeDeclRefExpr>(Val: Left)->getDeclName() != |
105 | cast<DependentScopeDeclRefExpr>(Val: Right)->getDeclName()) |
106 | return false; |
107 | return areEquivalentNameSpecifier( |
108 | Left: cast<DependentScopeDeclRefExpr>(Val: Left)->getQualifier(), |
109 | Right: cast<DependentScopeDeclRefExpr>(Val: Right)->getQualifier()); |
110 | case Stmt::DeclRefExprClass: |
111 | return cast<DeclRefExpr>(Val: Left)->getDecl() == |
112 | cast<DeclRefExpr>(Val: Right)->getDecl(); |
113 | case Stmt::MemberExprClass: |
114 | return cast<MemberExpr>(Val: Left)->getMemberDecl() == |
115 | cast<MemberExpr>(Val: Right)->getMemberDecl(); |
116 | case Stmt::CXXFoldExprClass: |
117 | return cast<CXXFoldExpr>(Val: Left)->getOperator() == |
118 | cast<CXXFoldExpr>(Val: Right)->getOperator(); |
119 | case Stmt::CXXFunctionalCastExprClass: |
120 | case Stmt::CStyleCastExprClass: |
121 | return cast<ExplicitCastExpr>(Val: Left)->getTypeAsWritten() == |
122 | cast<ExplicitCastExpr>(Val: Right)->getTypeAsWritten(); |
123 | case Stmt::CallExprClass: |
124 | case Stmt::ImplicitCastExprClass: |
125 | case Stmt::ArraySubscriptExprClass: |
126 | return true; |
127 | case Stmt::UnaryOperatorClass: |
128 | if (cast<UnaryOperator>(Val: Left)->isIncrementDecrementOp()) |
129 | return false; |
130 | return cast<UnaryOperator>(Val: Left)->getOpcode() == |
131 | cast<UnaryOperator>(Val: Right)->getOpcode(); |
132 | case Stmt::BinaryOperatorClass: |
133 | if (cast<BinaryOperator>(Val: Left)->isAssignmentOp()) |
134 | return false; |
135 | return cast<BinaryOperator>(Val: Left)->getOpcode() == |
136 | cast<BinaryOperator>(Val: Right)->getOpcode(); |
137 | case Stmt::UnaryExprOrTypeTraitExprClass: |
138 | const auto *LeftUnaryExpr = cast<UnaryExprOrTypeTraitExpr>(Val: Left); |
139 | const auto *RightUnaryExpr = cast<UnaryExprOrTypeTraitExpr>(Val: Right); |
140 | if (LeftUnaryExpr->isArgumentType() && RightUnaryExpr->isArgumentType()) |
141 | return LeftUnaryExpr->getKind() == RightUnaryExpr->getKind() && |
142 | LeftUnaryExpr->getArgumentType() == |
143 | RightUnaryExpr->getArgumentType(); |
144 | if (!LeftUnaryExpr->isArgumentType() && !RightUnaryExpr->isArgumentType()) |
145 | return areEquivalentExpr(Left: LeftUnaryExpr->getArgumentExpr(), |
146 | Right: RightUnaryExpr->getArgumentExpr()); |
147 | |
148 | return false; |
149 | } |
150 | } |
151 | |
152 | // For a given expression 'x', returns whether the ranges covered by the |
153 | // relational operators are equivalent (i.e. x <= 4 is equivalent to x < 5). |
154 | static bool areEquivalentRanges(BinaryOperatorKind OpcodeLHS, |
155 | const APSInt &ValueLHS, |
156 | BinaryOperatorKind OpcodeRHS, |
157 | const APSInt &ValueRHS) { |
158 | assert(APSInt::compareValues(ValueLHS, ValueRHS) <= 0 && |
159 | "Values must be ordered" ); |
160 | // Handle the case where constants are the same: x <= 4 <==> x <= 4. |
161 | if (APSInt::compareValues(I1: ValueLHS, I2: ValueRHS) == 0) |
162 | return OpcodeLHS == OpcodeRHS; |
163 | |
164 | // Handle the case where constants are off by one: x <= 4 <==> x < 5. |
165 | APSInt ValueLhsPlus1; |
166 | return ((OpcodeLHS == BO_LE && OpcodeRHS == BO_LT) || |
167 | (OpcodeLHS == BO_GT && OpcodeRHS == BO_GE)) && |
168 | incrementWithoutOverflow(Value: ValueLHS, Result&: ValueLhsPlus1) && |
169 | APSInt::compareValues(I1: ValueLhsPlus1, I2: ValueRHS) == 0; |
170 | } |
171 | |
172 | // For a given expression 'x', returns whether the ranges covered by the |
173 | // relational operators are fully disjoint (i.e. x < 4 and x > 7). |
174 | static bool areExclusiveRanges(BinaryOperatorKind OpcodeLHS, |
175 | const APSInt &ValueLHS, |
176 | BinaryOperatorKind OpcodeRHS, |
177 | const APSInt &ValueRHS) { |
178 | assert(APSInt::compareValues(ValueLHS, ValueRHS) <= 0 && |
179 | "Values must be ordered" ); |
180 | |
181 | // Handle cases where the constants are the same. |
182 | if (APSInt::compareValues(I1: ValueLHS, I2: ValueRHS) == 0) { |
183 | switch (OpcodeLHS) { |
184 | case BO_EQ: |
185 | return OpcodeRHS == BO_NE || OpcodeRHS == BO_GT || OpcodeRHS == BO_LT; |
186 | case BO_NE: |
187 | return OpcodeRHS == BO_EQ; |
188 | case BO_LE: |
189 | return OpcodeRHS == BO_GT; |
190 | case BO_GE: |
191 | return OpcodeRHS == BO_LT; |
192 | case BO_LT: |
193 | return OpcodeRHS == BO_EQ || OpcodeRHS == BO_GT || OpcodeRHS == BO_GE; |
194 | case BO_GT: |
195 | return OpcodeRHS == BO_EQ || OpcodeRHS == BO_LT || OpcodeRHS == BO_LE; |
196 | default: |
197 | return false; |
198 | } |
199 | } |
200 | |
201 | // Handle cases where the constants are different. |
202 | if ((OpcodeLHS == BO_EQ || OpcodeLHS == BO_LT || OpcodeLHS == BO_LE) && |
203 | (OpcodeRHS == BO_EQ || OpcodeRHS == BO_GT || OpcodeRHS == BO_GE)) |
204 | return true; |
205 | |
206 | // Handle the case where constants are off by one: x > 5 && x < 6. |
207 | APSInt ValueLhsPlus1; |
208 | if (OpcodeLHS == BO_GT && OpcodeRHS == BO_LT && |
209 | incrementWithoutOverflow(Value: ValueLHS, Result&: ValueLhsPlus1) && |
210 | APSInt::compareValues(I1: ValueLhsPlus1, I2: ValueRHS) == 0) |
211 | return true; |
212 | |
213 | return false; |
214 | } |
215 | |
216 | // Returns whether the ranges covered by the union of both relational |
217 | // expressions cover the whole domain (i.e. x < 10 and x > 0). |
218 | static bool rangesFullyCoverDomain(BinaryOperatorKind OpcodeLHS, |
219 | const APSInt &ValueLHS, |
220 | BinaryOperatorKind OpcodeRHS, |
221 | const APSInt &ValueRHS) { |
222 | assert(APSInt::compareValues(ValueLHS, ValueRHS) <= 0 && |
223 | "Values must be ordered" ); |
224 | |
225 | // Handle cases where the constants are the same: x < 5 || x >= 5. |
226 | if (APSInt::compareValues(I1: ValueLHS, I2: ValueRHS) == 0) { |
227 | switch (OpcodeLHS) { |
228 | case BO_EQ: |
229 | return OpcodeRHS == BO_NE; |
230 | case BO_NE: |
231 | return OpcodeRHS == BO_EQ; |
232 | case BO_LE: |
233 | return OpcodeRHS == BO_GT || OpcodeRHS == BO_GE; |
234 | case BO_LT: |
235 | return OpcodeRHS == BO_GE; |
236 | case BO_GE: |
237 | return OpcodeRHS == BO_LT || OpcodeRHS == BO_LE; |
238 | case BO_GT: |
239 | return OpcodeRHS == BO_LE; |
240 | default: |
241 | return false; |
242 | } |
243 | } |
244 | |
245 | // Handle the case where constants are off by one: x <= 4 || x >= 5. |
246 | APSInt ValueLhsPlus1; |
247 | if (OpcodeLHS == BO_LE && OpcodeRHS == BO_GE && |
248 | incrementWithoutOverflow(Value: ValueLHS, Result&: ValueLhsPlus1) && |
249 | APSInt::compareValues(I1: ValueLhsPlus1, I2: ValueRHS) == 0) |
250 | return true; |
251 | |
252 | // Handle cases where the constants are different: x > 4 || x <= 7. |
253 | if ((OpcodeLHS == BO_GT || OpcodeLHS == BO_GE) && |
254 | (OpcodeRHS == BO_LT || OpcodeRHS == BO_LE)) |
255 | return true; |
256 | |
257 | // Handle cases where constants are different but both ops are !=, like: |
258 | // x != 5 || x != 10 |
259 | if (OpcodeLHS == BO_NE && OpcodeRHS == BO_NE) |
260 | return true; |
261 | |
262 | return false; |
263 | } |
264 | |
265 | static bool rangeSubsumesRange(BinaryOperatorKind OpcodeLHS, |
266 | const APSInt &ValueLHS, |
267 | BinaryOperatorKind OpcodeRHS, |
268 | const APSInt &ValueRHS) { |
269 | int Comparison = APSInt::compareValues(I1: ValueLHS, I2: ValueRHS); |
270 | switch (OpcodeLHS) { |
271 | case BO_EQ: |
272 | return OpcodeRHS == BO_EQ && Comparison == 0; |
273 | case BO_NE: |
274 | return (OpcodeRHS == BO_NE && Comparison == 0) || |
275 | (OpcodeRHS == BO_EQ && Comparison != 0) || |
276 | (OpcodeRHS == BO_LT && Comparison >= 0) || |
277 | (OpcodeRHS == BO_LE && Comparison > 0) || |
278 | (OpcodeRHS == BO_GT && Comparison <= 0) || |
279 | (OpcodeRHS == BO_GE && Comparison < 0); |
280 | |
281 | case BO_LT: |
282 | return ((OpcodeRHS == BO_LT && Comparison >= 0) || |
283 | (OpcodeRHS == BO_LE && Comparison > 0) || |
284 | (OpcodeRHS == BO_EQ && Comparison > 0)); |
285 | case BO_GT: |
286 | return ((OpcodeRHS == BO_GT && Comparison <= 0) || |
287 | (OpcodeRHS == BO_GE && Comparison < 0) || |
288 | (OpcodeRHS == BO_EQ && Comparison < 0)); |
289 | case BO_LE: |
290 | return (OpcodeRHS == BO_LT || OpcodeRHS == BO_LE || OpcodeRHS == BO_EQ) && |
291 | Comparison >= 0; |
292 | case BO_GE: |
293 | return (OpcodeRHS == BO_GT || OpcodeRHS == BO_GE || OpcodeRHS == BO_EQ) && |
294 | Comparison <= 0; |
295 | default: |
296 | return false; |
297 | } |
298 | } |
299 | |
300 | static void transformSubToCanonicalAddExpr(BinaryOperatorKind &Opcode, |
301 | APSInt &Value) { |
302 | if (Opcode == BO_Sub) { |
303 | Opcode = BO_Add; |
304 | Value = -Value; |
305 | } |
306 | } |
307 | |
308 | // to use in the template below |
309 | static OverloadedOperatorKind getOp(const BinaryOperator *Op) { |
310 | return BinaryOperator::getOverloadedOperator(Opc: Op->getOpcode()); |
311 | } |
312 | |
313 | static OverloadedOperatorKind getOp(const CXXOperatorCallExpr *Op) { |
314 | if (Op->getNumArgs() != 2) |
315 | return OO_None; |
316 | return Op->getOperator(); |
317 | } |
318 | |
319 | static std::pair<const Expr *, const Expr *> |
320 | getOperands(const BinaryOperator *Op) { |
321 | return {Op->getLHS()->IgnoreParenImpCasts(), |
322 | Op->getRHS()->IgnoreParenImpCasts()}; |
323 | } |
324 | |
325 | static std::pair<const Expr *, const Expr *> |
326 | getOperands(const CXXOperatorCallExpr *Op) { |
327 | return {Op->getArg(0)->IgnoreParenImpCasts(), |
328 | Op->getArg(1)->IgnoreParenImpCasts()}; |
329 | } |
330 | |
331 | template <typename TExpr> |
332 | static const TExpr *checkOpKind(const Expr *TheExpr, |
333 | OverloadedOperatorKind OpKind) { |
334 | const auto *AsTExpr = dyn_cast_or_null<TExpr>(TheExpr); |
335 | if (AsTExpr && getOp(AsTExpr) == OpKind) |
336 | return AsTExpr; |
337 | |
338 | return nullptr; |
339 | } |
340 | |
341 | // returns true if a subexpression has two directly equivalent operands and |
342 | // is already handled by operands/parametersAreEquivalent |
343 | template <typename TExpr, unsigned N> |
344 | static bool collectOperands(const Expr *Part, |
345 | SmallVector<const Expr *, N> &AllOperands, |
346 | OverloadedOperatorKind OpKind) { |
347 | if (const auto *BinOp = checkOpKind<TExpr>(Part, OpKind)) { |
348 | const std::pair<const Expr *, const Expr *> Operands = getOperands(BinOp); |
349 | if (areEquivalentExpr(Left: Operands.first, Right: Operands.second)) |
350 | return true; |
351 | return collectOperands<TExpr>(Operands.first, AllOperands, OpKind) || |
352 | collectOperands<TExpr>(Operands.second, AllOperands, OpKind); |
353 | } |
354 | |
355 | AllOperands.push_back(Part); |
356 | return false; |
357 | } |
358 | |
359 | template <typename TExpr> |
360 | static bool hasSameOperatorParent(const Expr *TheExpr, |
361 | OverloadedOperatorKind OpKind, |
362 | ASTContext &Context) { |
363 | // IgnoreParenImpCasts logic in reverse: skip surrounding uninteresting nodes |
364 | const DynTypedNodeList Parents = Context.getParents(Node: *TheExpr); |
365 | for (DynTypedNode DynParent : Parents) { |
366 | if (const auto *Parent = DynParent.get<Expr>()) { |
367 | bool Skip = isa<ParenExpr>(Val: Parent) || isa<ImplicitCastExpr>(Val: Parent) || |
368 | isa<FullExpr>(Val: Parent) || |
369 | isa<MaterializeTemporaryExpr>(Val: Parent); |
370 | if (Skip && hasSameOperatorParent<TExpr>(Parent, OpKind, Context)) |
371 | return true; |
372 | if (checkOpKind<TExpr>(Parent, OpKind)) |
373 | return true; |
374 | } |
375 | } |
376 | |
377 | return false; |
378 | } |
379 | |
380 | template <typename TExpr> |
381 | static bool |
382 | markDuplicateOperands(const TExpr *TheExpr, |
383 | ast_matchers::internal::BoundNodesTreeBuilder *Builder, |
384 | ASTContext &Context) { |
385 | const OverloadedOperatorKind OpKind = getOp(TheExpr); |
386 | if (OpKind == OO_None) |
387 | return false; |
388 | // if there are no nested operators of the same kind, it's handled by |
389 | // operands/parametersAreEquivalent |
390 | const std::pair<const Expr *, const Expr *> Operands = getOperands(TheExpr); |
391 | if (!(checkOpKind<TExpr>(Operands.first, OpKind) || |
392 | checkOpKind<TExpr>(Operands.second, OpKind))) |
393 | return false; |
394 | |
395 | // if parent is the same kind of operator, it's handled by a previous call to |
396 | // markDuplicateOperands |
397 | if (hasSameOperatorParent<TExpr>(TheExpr, OpKind, Context)) |
398 | return false; |
399 | |
400 | SmallVector<const Expr *, 4> AllOperands; |
401 | if (collectOperands<TExpr>(Operands.first, AllOperands, OpKind)) |
402 | return false; |
403 | if (collectOperands<TExpr>(Operands.second, AllOperands, OpKind)) |
404 | return false; |
405 | size_t NumOperands = AllOperands.size(); |
406 | llvm::SmallBitVector Duplicates(NumOperands); |
407 | for (size_t I = 0; I < NumOperands; I++) { |
408 | if (Duplicates[I]) |
409 | continue; |
410 | bool FoundDuplicates = false; |
411 | |
412 | for (size_t J = I + 1; J < NumOperands; J++) { |
413 | if (AllOperands[J]->HasSideEffects(Ctx: Context)) |
414 | break; |
415 | |
416 | if (areEquivalentExpr(Left: AllOperands[I], Right: AllOperands[J])) { |
417 | FoundDuplicates = true; |
418 | Duplicates.set(J); |
419 | Builder->setBinding(Id: SmallString<11>(llvm::formatv(Fmt: "duplicate{0}" , Vals&: J)), |
420 | DynNode: DynTypedNode::create(Node: *AllOperands[J])); |
421 | } |
422 | } |
423 | |
424 | if (FoundDuplicates) |
425 | Builder->setBinding(Id: SmallString<11>(llvm::formatv(Fmt: "duplicate{0}" , Vals&: I)), |
426 | DynNode: DynTypedNode::create(Node: *AllOperands[I])); |
427 | } |
428 | |
429 | return Duplicates.any(); |
430 | } |
431 | |
432 | AST_MATCHER(Expr, isIntegerConstantExpr) { |
433 | if (Node.isInstantiationDependent()) |
434 | return false; |
435 | return Node.isIntegerConstantExpr(Ctx: Finder->getASTContext()); |
436 | } |
437 | |
438 | AST_MATCHER(BinaryOperator, operandsAreEquivalent) { |
439 | return areEquivalentExpr(Left: Node.getLHS(), Right: Node.getRHS()); |
440 | } |
441 | |
442 | AST_MATCHER(BinaryOperator, nestedOperandsAreEquivalent) { |
443 | return markDuplicateOperands(TheExpr: &Node, Builder, Context&: Finder->getASTContext()); |
444 | } |
445 | |
446 | AST_MATCHER(ConditionalOperator, expressionsAreEquivalent) { |
447 | return areEquivalentExpr(Left: Node.getTrueExpr(), Right: Node.getFalseExpr()); |
448 | } |
449 | |
450 | AST_MATCHER(CallExpr, parametersAreEquivalent) { |
451 | return Node.getNumArgs() == 2 && |
452 | areEquivalentExpr(Left: Node.getArg(Arg: 0), Right: Node.getArg(Arg: 1)); |
453 | } |
454 | |
455 | AST_MATCHER(CXXOperatorCallExpr, nestedParametersAreEquivalent) { |
456 | return markDuplicateOperands(TheExpr: &Node, Builder, Context&: Finder->getASTContext()); |
457 | } |
458 | |
459 | AST_MATCHER(BinaryOperator, binaryOperatorIsInMacro) { |
460 | return Node.getOperatorLoc().isMacroID(); |
461 | } |
462 | |
463 | AST_MATCHER(ConditionalOperator, conditionalOperatorIsInMacro) { |
464 | return Node.getQuestionLoc().isMacroID() || Node.getColonLoc().isMacroID(); |
465 | } |
466 | |
467 | AST_MATCHER(Expr, isMacro) { return Node.getExprLoc().isMacroID(); } |
468 | |
469 | AST_MATCHER_P(Expr, expandedByMacro, ArrayRef<llvm::StringLiteral>, Names) { |
470 | const SourceManager &SM = Finder->getASTContext().getSourceManager(); |
471 | const LangOptions &LO = Finder->getASTContext().getLangOpts(); |
472 | SourceLocation Loc = Node.getExprLoc(); |
473 | while (Loc.isMacroID()) { |
474 | StringRef MacroName = Lexer::getImmediateMacroName(Loc, SM, LangOpts: LO); |
475 | if (llvm::is_contained(Range: Names, Element: MacroName)) |
476 | return true; |
477 | Loc = SM.getImmediateMacroCallerLoc(Loc); |
478 | } |
479 | return false; |
480 | } |
481 | |
482 | // Returns a matcher for integer constant expressions. |
483 | static ast_matchers::internal::Matcher<Expr> |
484 | matchIntegerConstantExpr(StringRef Id) { |
485 | std::string CstId = (Id + "-const" ).str(); |
486 | return expr(isIntegerConstantExpr()).bind(ID: CstId); |
487 | } |
488 | |
489 | // Retrieves the integer expression matched by 'matchIntegerConstantExpr' with |
490 | // name 'Id' and stores it into 'ConstExpr', the value of the expression is |
491 | // stored into `Value`. |
492 | static bool retrieveIntegerConstantExpr(const MatchFinder::MatchResult &Result, |
493 | StringRef Id, APSInt &Value, |
494 | const Expr *&ConstExpr) { |
495 | std::string CstId = (Id + "-const" ).str(); |
496 | ConstExpr = Result.Nodes.getNodeAs<Expr>(ID: CstId); |
497 | if (!ConstExpr) |
498 | return false; |
499 | std::optional<llvm::APSInt> R = |
500 | ConstExpr->getIntegerConstantExpr(Ctx: *Result.Context); |
501 | if (!R) |
502 | return false; |
503 | Value = *R; |
504 | return true; |
505 | } |
506 | |
507 | // Overloaded `retrieveIntegerConstantExpr` for compatibility. |
508 | static bool retrieveIntegerConstantExpr(const MatchFinder::MatchResult &Result, |
509 | StringRef Id, APSInt &Value) { |
510 | const Expr *ConstExpr = nullptr; |
511 | return retrieveIntegerConstantExpr(Result, Id, Value, ConstExpr); |
512 | } |
513 | |
514 | // Returns a matcher for symbolic expressions (matches every expression except |
515 | // ingeter constant expressions). |
516 | static ast_matchers::internal::Matcher<Expr> matchSymbolicExpr(StringRef Id) { |
517 | std::string SymId = (Id + "-sym" ).str(); |
518 | return ignoringParenImpCasts( |
519 | InnerMatcher: expr(unless(isIntegerConstantExpr())).bind(ID: SymId)); |
520 | } |
521 | |
522 | // Retrieves the expression matched by 'matchSymbolicExpr' with name 'Id' and |
523 | // stores it into 'SymExpr'. |
524 | static bool retrieveSymbolicExpr(const MatchFinder::MatchResult &Result, |
525 | StringRef Id, const Expr *&SymExpr) { |
526 | std::string SymId = (Id + "-sym" ).str(); |
527 | if (const auto *Node = Result.Nodes.getNodeAs<Expr>(ID: SymId)) { |
528 | SymExpr = Node; |
529 | return true; |
530 | } |
531 | return false; |
532 | } |
533 | |
534 | // Match a binary operator between a symbolic expression and an integer constant |
535 | // expression. |
536 | static ast_matchers::internal::Matcher<Expr> |
537 | matchBinOpIntegerConstantExpr(StringRef Id) { |
538 | const auto BinOpCstExpr = |
539 | expr(anyOf(binaryOperator(hasAnyOperatorName("+" , "|" , "&" ), |
540 | hasOperands(Matcher1: matchSymbolicExpr(Id), |
541 | Matcher2: matchIntegerConstantExpr(Id))), |
542 | binaryOperator(hasOperatorName(Name: "-" ), |
543 | hasLHS(InnerMatcher: matchSymbolicExpr(Id)), |
544 | hasRHS(InnerMatcher: matchIntegerConstantExpr(Id))))) |
545 | .bind(ID: Id); |
546 | return ignoringParenImpCasts(InnerMatcher: BinOpCstExpr); |
547 | } |
548 | |
549 | // Retrieves sub-expressions matched by 'matchBinOpIntegerConstantExpr' with |
550 | // name 'Id'. |
551 | static bool |
552 | retrieveBinOpIntegerConstantExpr(const MatchFinder::MatchResult &Result, |
553 | StringRef Id, BinaryOperatorKind &Opcode, |
554 | const Expr *&Symbol, APSInt &Value) { |
555 | if (const auto *BinExpr = Result.Nodes.getNodeAs<BinaryOperator>(ID: Id)) { |
556 | Opcode = BinExpr->getOpcode(); |
557 | return retrieveSymbolicExpr(Result, Id, SymExpr&: Symbol) && |
558 | retrieveIntegerConstantExpr(Result, Id, Value); |
559 | } |
560 | return false; |
561 | } |
562 | |
563 | // Matches relational expressions: 'Expr <op> k' (i.e. x < 2, x != 3, 12 <= x). |
564 | static ast_matchers::internal::Matcher<Expr> |
565 | matchRelationalIntegerConstantExpr(StringRef Id) { |
566 | std::string CastId = (Id + "-cast" ).str(); |
567 | std::string SwapId = (Id + "-swap" ).str(); |
568 | std::string NegateId = (Id + "-negate" ).str(); |
569 | std::string OverloadId = (Id + "-overload" ).str(); |
570 | std::string ConstId = (Id + "-const" ).str(); |
571 | |
572 | const auto RelationalExpr = ignoringParenImpCasts(InnerMatcher: binaryOperator( |
573 | isComparisonOperator(), expr().bind(ID: Id), |
574 | anyOf(allOf(hasLHS(InnerMatcher: matchSymbolicExpr(Id)), |
575 | hasRHS(InnerMatcher: matchIntegerConstantExpr(Id))), |
576 | allOf(hasLHS(InnerMatcher: matchIntegerConstantExpr(Id)), |
577 | hasRHS(InnerMatcher: matchSymbolicExpr(Id)), expr().bind(ID: SwapId))))); |
578 | |
579 | // A cast can be matched as a comparator to zero. (i.e. if (x) is equivalent |
580 | // to if (x != 0)). |
581 | const auto CastExpr = |
582 | implicitCastExpr(hasCastKind(Kind: CK_IntegralToBoolean), |
583 | hasSourceExpression(InnerMatcher: matchSymbolicExpr(Id))) |
584 | .bind(ID: CastId); |
585 | |
586 | const auto NegateRelationalExpr = |
587 | unaryOperator(hasOperatorName(Name: "!" ), |
588 | hasUnaryOperand(InnerMatcher: anyOf(CastExpr, RelationalExpr))) |
589 | .bind(ID: NegateId); |
590 | |
591 | // Do not bind to double negation. |
592 | const auto NegateNegateRelationalExpr = |
593 | unaryOperator(hasOperatorName(Name: "!" ), |
594 | hasUnaryOperand(InnerMatcher: unaryOperator( |
595 | hasOperatorName(Name: "!" ), |
596 | hasUnaryOperand(InnerMatcher: anyOf(CastExpr, RelationalExpr))))); |
597 | |
598 | const auto OverloadedOperatorExpr = |
599 | cxxOperatorCallExpr( |
600 | hasAnyOverloadedOperatorName("==" , "!=" , "<" , "<=" , ">" , ">=" ), |
601 | // Filter noisy false positives. |
602 | unless(isMacro()), unless(isInTemplateInstantiation()), |
603 | anyOf(hasLHS(InnerMatcher: ignoringParenImpCasts(InnerMatcher: integerLiteral().bind(ID: ConstId))), |
604 | hasRHS(InnerMatcher: ignoringParenImpCasts(InnerMatcher: integerLiteral().bind(ID: ConstId))))) |
605 | .bind(ID: OverloadId); |
606 | |
607 | return anyOf(RelationalExpr, CastExpr, NegateRelationalExpr, |
608 | NegateNegateRelationalExpr, OverloadedOperatorExpr); |
609 | } |
610 | |
611 | // Checks whether a function param is non constant reference type, and may |
612 | // be modified in the function. |
613 | static bool isNonConstReferenceType(QualType ParamType) { |
614 | return ParamType->isReferenceType() && |
615 | !ParamType.getNonReferenceType().isConstQualified(); |
616 | } |
617 | |
618 | // Checks whether the arguments of an overloaded operator can be modified in the |
619 | // function. |
620 | // For operators that take an instance and a constant as arguments, only the |
621 | // first argument (the instance) needs to be checked, since the constant itself |
622 | // is a temporary expression. Whether the second parameter is checked is |
623 | // controlled by the parameter `ParamsToCheckCount`. |
624 | static bool |
625 | canOverloadedOperatorArgsBeModified(const CXXOperatorCallExpr *OperatorCall, |
626 | bool CheckSecondParam) { |
627 | const auto *OperatorDecl = |
628 | dyn_cast_or_null<FunctionDecl>(OperatorCall->getCalleeDecl()); |
629 | // if we can't find the declaration, conservatively assume it can modify |
630 | // arguments |
631 | if (!OperatorDecl) |
632 | return true; |
633 | |
634 | unsigned ParamCount = OperatorDecl->getNumParams(); |
635 | |
636 | // Overloaded operators declared inside a class have only one param. |
637 | // These functions must be declared const in order to not be able to modify |
638 | // the instance of the class they are called through. |
639 | if (ParamCount == 1 && |
640 | !OperatorDecl->getType()->castAs<FunctionType>()->isConst()) |
641 | return true; |
642 | |
643 | if (isNonConstReferenceType(OperatorDecl->getParamDecl(0)->getType())) |
644 | return true; |
645 | |
646 | return CheckSecondParam && ParamCount == 2 && |
647 | isNonConstReferenceType(OperatorDecl->getParamDecl(1)->getType()); |
648 | } |
649 | |
650 | // Retrieves sub-expressions matched by 'matchRelationalIntegerConstantExpr' |
651 | // with name 'Id'. |
652 | static bool retrieveRelationalIntegerConstantExpr( |
653 | const MatchFinder::MatchResult &Result, StringRef Id, |
654 | const Expr *&OperandExpr, BinaryOperatorKind &Opcode, const Expr *&Symbol, |
655 | APSInt &Value, const Expr *&ConstExpr) { |
656 | std::string CastId = (Id + "-cast" ).str(); |
657 | std::string SwapId = (Id + "-swap" ).str(); |
658 | std::string NegateId = (Id + "-negate" ).str(); |
659 | std::string OverloadId = (Id + "-overload" ).str(); |
660 | |
661 | if (const auto *Bin = Result.Nodes.getNodeAs<BinaryOperator>(ID: Id)) { |
662 | // Operand received with explicit comparator. |
663 | Opcode = Bin->getOpcode(); |
664 | OperandExpr = Bin; |
665 | |
666 | if (!retrieveIntegerConstantExpr(Result, Id, Value, ConstExpr)) |
667 | return false; |
668 | } else if (const auto *Cast = Result.Nodes.getNodeAs<CastExpr>(ID: CastId)) { |
669 | // Operand received with implicit comparator (cast). |
670 | Opcode = BO_NE; |
671 | OperandExpr = Cast; |
672 | Value = APSInt(32, false); |
673 | } else if (const auto *OverloadedOperatorExpr = |
674 | Result.Nodes.getNodeAs<CXXOperatorCallExpr>(ID: OverloadId)) { |
675 | if (canOverloadedOperatorArgsBeModified(OperatorCall: OverloadedOperatorExpr, CheckSecondParam: false)) |
676 | return false; |
677 | |
678 | bool IntegerConstantIsFirstArg = false; |
679 | |
680 | if (const auto *Arg = OverloadedOperatorExpr->getArg(1)) { |
681 | if (!Arg->isValueDependent() && |
682 | !Arg->isIntegerConstantExpr(*Result.Context)) { |
683 | IntegerConstantIsFirstArg = true; |
684 | if (const auto *Arg = OverloadedOperatorExpr->getArg(0)) { |
685 | if (!Arg->isValueDependent() && |
686 | !Arg->isIntegerConstantExpr(*Result.Context)) |
687 | return false; |
688 | } else |
689 | return false; |
690 | } |
691 | } else |
692 | return false; |
693 | |
694 | Symbol = OverloadedOperatorExpr->getArg(IntegerConstantIsFirstArg ? 1 : 0); |
695 | OperandExpr = OverloadedOperatorExpr; |
696 | Opcode = BinaryOperator::getOverloadedOpcode( |
697 | OO: OverloadedOperatorExpr->getOperator()); |
698 | |
699 | if (!retrieveIntegerConstantExpr(Result, Id, Value, ConstExpr)) |
700 | return false; |
701 | |
702 | if (!BinaryOperator::isComparisonOp(Opc: Opcode)) |
703 | return false; |
704 | |
705 | // The call site of this function expects the constant on the RHS, |
706 | // so change the opcode accordingly. |
707 | if (IntegerConstantIsFirstArg) |
708 | Opcode = BinaryOperator::reverseComparisonOp(Opc: Opcode); |
709 | |
710 | return true; |
711 | } else { |
712 | return false; |
713 | } |
714 | |
715 | if (!retrieveSymbolicExpr(Result, Id, SymExpr&: Symbol)) |
716 | return false; |
717 | |
718 | if (Result.Nodes.getNodeAs<Expr>(ID: SwapId)) |
719 | Opcode = BinaryOperator::reverseComparisonOp(Opc: Opcode); |
720 | if (Result.Nodes.getNodeAs<Expr>(ID: NegateId)) |
721 | Opcode = BinaryOperator::negateComparisonOp(Opc: Opcode); |
722 | return true; |
723 | } |
724 | |
725 | // Checks for expressions like (X == 4) && (Y != 9) |
726 | static bool areSidesBinaryConstExpressions(const BinaryOperator *&BinOp, |
727 | const ASTContext *AstCtx) { |
728 | const auto *LhsBinOp = dyn_cast<BinaryOperator>(Val: BinOp->getLHS()); |
729 | const auto *RhsBinOp = dyn_cast<BinaryOperator>(Val: BinOp->getRHS()); |
730 | |
731 | if (!LhsBinOp || !RhsBinOp) |
732 | return false; |
733 | |
734 | auto IsIntegerConstantExpr = [AstCtx](const Expr *E) { |
735 | return !E->isValueDependent() && E->isIntegerConstantExpr(Ctx: *AstCtx); |
736 | }; |
737 | |
738 | if ((IsIntegerConstantExpr(LhsBinOp->getLHS()) || |
739 | IsIntegerConstantExpr(LhsBinOp->getRHS())) && |
740 | (IsIntegerConstantExpr(RhsBinOp->getLHS()) || |
741 | IsIntegerConstantExpr(RhsBinOp->getRHS()))) |
742 | return true; |
743 | return false; |
744 | } |
745 | |
746 | static bool areSidesBinaryConstExpressionsOrDefinesOrIntegerConstant( |
747 | const BinaryOperator *&BinOp, const ASTContext *AstCtx) { |
748 | if (areSidesBinaryConstExpressions(BinOp, AstCtx)) |
749 | return true; |
750 | |
751 | const Expr *Lhs = BinOp->getLHS(); |
752 | const Expr *Rhs = BinOp->getRHS(); |
753 | |
754 | if (!Lhs || !Rhs) |
755 | return false; |
756 | |
757 | auto IsDefineExpr = [AstCtx](const Expr *E) { |
758 | const SourceRange Lsr = E->getSourceRange(); |
759 | if (!Lsr.getBegin().isMacroID() || E->isValueDependent() || |
760 | !E->isIntegerConstantExpr(Ctx: *AstCtx)) |
761 | return false; |
762 | return true; |
763 | }; |
764 | |
765 | return IsDefineExpr(Lhs) || IsDefineExpr(Rhs); |
766 | } |
767 | |
768 | // Retrieves integer constant subexpressions from binary operator expressions |
769 | // that have two equivalent sides. |
770 | // E.g.: from (X == 5) && (X == 5) retrieves 5 and 5. |
771 | static bool retrieveConstExprFromBothSides(const BinaryOperator *&BinOp, |
772 | BinaryOperatorKind &MainOpcode, |
773 | BinaryOperatorKind &SideOpcode, |
774 | const Expr *&LhsConst, |
775 | const Expr *&RhsConst, |
776 | const ASTContext *AstCtx) { |
777 | assert(areSidesBinaryConstExpressions(BinOp, AstCtx) && |
778 | "Both sides of binary operator must be constant expressions!" ); |
779 | |
780 | MainOpcode = BinOp->getOpcode(); |
781 | |
782 | const auto *BinOpLhs = cast<BinaryOperator>(Val: BinOp->getLHS()); |
783 | const auto *BinOpRhs = cast<BinaryOperator>(Val: BinOp->getRHS()); |
784 | |
785 | auto IsIntegerConstantExpr = [AstCtx](const Expr *E) { |
786 | return !E->isValueDependent() && E->isIntegerConstantExpr(Ctx: *AstCtx); |
787 | }; |
788 | |
789 | LhsConst = IsIntegerConstantExpr(BinOpLhs->getLHS()) ? BinOpLhs->getLHS() |
790 | : BinOpLhs->getRHS(); |
791 | RhsConst = IsIntegerConstantExpr(BinOpRhs->getLHS()) ? BinOpRhs->getLHS() |
792 | : BinOpRhs->getRHS(); |
793 | |
794 | if (!LhsConst || !RhsConst) |
795 | return false; |
796 | |
797 | assert(BinOpLhs->getOpcode() == BinOpRhs->getOpcode() && |
798 | "Sides of the binary operator must be equivalent expressions!" ); |
799 | |
800 | SideOpcode = BinOpLhs->getOpcode(); |
801 | |
802 | return true; |
803 | } |
804 | |
805 | static bool isSameRawIdentifierToken(const Token &T1, const Token &T2, |
806 | const SourceManager &SM) { |
807 | if (T1.getKind() != T2.getKind()) |
808 | return false; |
809 | if (T1.isNot(K: tok::raw_identifier)) |
810 | return true; |
811 | if (T1.getLength() != T2.getLength()) |
812 | return false; |
813 | return StringRef(SM.getCharacterData(SL: T1.getLocation()), T1.getLength()) == |
814 | StringRef(SM.getCharacterData(SL: T2.getLocation()), T2.getLength()); |
815 | } |
816 | |
817 | bool isTokAtEndOfExpr(SourceRange ExprSR, Token T, const SourceManager &SM) { |
818 | return SM.getExpansionLoc(Loc: ExprSR.getEnd()) == T.getLocation(); |
819 | } |
820 | |
821 | /// Returns true if both LhsExpr and RhsExpr are |
822 | /// macro expressions and they are expanded |
823 | /// from different macros. |
824 | static bool areExprsFromDifferentMacros(const Expr *LhsExpr, |
825 | const Expr *RhsExpr, |
826 | const ASTContext *AstCtx) { |
827 | if (!LhsExpr || !RhsExpr) |
828 | return false; |
829 | const SourceRange Lsr = LhsExpr->getSourceRange(); |
830 | const SourceRange Rsr = RhsExpr->getSourceRange(); |
831 | if (!Lsr.getBegin().isMacroID() || !Rsr.getBegin().isMacroID()) |
832 | return false; |
833 | |
834 | const SourceManager &SM = AstCtx->getSourceManager(); |
835 | const LangOptions &LO = AstCtx->getLangOpts(); |
836 | |
837 | std::pair<FileID, unsigned> LsrLocInfo = |
838 | SM.getDecomposedLoc(Loc: SM.getExpansionLoc(Loc: Lsr.getBegin())); |
839 | std::pair<FileID, unsigned> RsrLocInfo = |
840 | SM.getDecomposedLoc(Loc: SM.getExpansionLoc(Loc: Rsr.getBegin())); |
841 | llvm::MemoryBufferRef MB = SM.getBufferOrFake(FID: LsrLocInfo.first); |
842 | |
843 | const char *LTokenPos = MB.getBufferStart() + LsrLocInfo.second; |
844 | const char *RTokenPos = MB.getBufferStart() + RsrLocInfo.second; |
845 | Lexer LRawLex(SM.getLocForStartOfFile(FID: LsrLocInfo.first), LO, |
846 | MB.getBufferStart(), LTokenPos, MB.getBufferEnd()); |
847 | Lexer RRawLex(SM.getLocForStartOfFile(FID: RsrLocInfo.first), LO, |
848 | MB.getBufferStart(), RTokenPos, MB.getBufferEnd()); |
849 | |
850 | Token LTok, RTok; |
851 | do { // Compare the expressions token-by-token. |
852 | LRawLex.LexFromRawLexer(Result&: LTok); |
853 | RRawLex.LexFromRawLexer(Result&: RTok); |
854 | } while (!LTok.is(K: tok::eof) && !RTok.is(K: tok::eof) && |
855 | isSameRawIdentifierToken(T1: LTok, T2: RTok, SM) && |
856 | !isTokAtEndOfExpr(ExprSR: Lsr, T: LTok, SM) && |
857 | !isTokAtEndOfExpr(ExprSR: Rsr, T: RTok, SM)); |
858 | return (!isTokAtEndOfExpr(ExprSR: Lsr, T: LTok, SM) || |
859 | !isTokAtEndOfExpr(ExprSR: Rsr, T: RTok, SM)) || |
860 | !isSameRawIdentifierToken(T1: LTok, T2: RTok, SM); |
861 | } |
862 | |
863 | static bool areExprsMacroAndNonMacro(const Expr *&LhsExpr, |
864 | const Expr *&RhsExpr) { |
865 | if (!LhsExpr || !RhsExpr) |
866 | return false; |
867 | |
868 | const SourceLocation LhsLoc = LhsExpr->getExprLoc(); |
869 | const SourceLocation RhsLoc = RhsExpr->getExprLoc(); |
870 | |
871 | return LhsLoc.isMacroID() != RhsLoc.isMacroID(); |
872 | } |
873 | |
874 | static bool areStringsSameIgnoreSpaces(const llvm::StringRef Left, |
875 | const llvm::StringRef Right) { |
876 | if (Left == Right) |
877 | return true; |
878 | |
879 | // Do running comparison ignoring spaces |
880 | llvm::StringRef L = Left.trim(); |
881 | llvm::StringRef R = Right.trim(); |
882 | while (!L.empty() && !R.empty()) { |
883 | L = L.ltrim(); |
884 | R = R.ltrim(); |
885 | if (L.empty() && R.empty()) |
886 | return true; |
887 | // If symbol compared are different ==> strings are not the same |
888 | if (L.front() != R.front()) |
889 | return false; |
890 | L = L.drop_front(); |
891 | R = R.drop_front(); |
892 | } |
893 | return L.empty() && R.empty(); |
894 | } |
895 | |
896 | static bool (const BinaryOperator *BinOp, |
897 | const ASTContext *Context) { |
898 | |
899 | if (!BinOp) |
900 | return false; |
901 | |
902 | const Expr *Lhs = BinOp->getLHS(); |
903 | const Expr *Rhs = BinOp->getRHS(); |
904 | const SourceManager &SM = Context->getSourceManager(); |
905 | |
906 | const SourceRange Lsr = Lhs->getSourceRange(); |
907 | const SourceRange Rsr = Rhs->getSourceRange(); |
908 | if (Lsr.getBegin().isMacroID()) { |
909 | // Left is macro so right macro too |
910 | if (Rsr.getBegin().isMacroID()) { |
911 | // Both sides are macros so they are same macro or literal |
912 | const llvm::StringRef L = Lexer::getSourceText( |
913 | Range: CharSourceRange::getTokenRange(R: Lsr), SM, LangOpts: Context->getLangOpts(), Invalid: 0); |
914 | const llvm::StringRef R = Lexer::getSourceText( |
915 | Range: CharSourceRange::getTokenRange(R: Rsr), SM, LangOpts: Context->getLangOpts(), Invalid: 0); |
916 | return areStringsSameIgnoreSpaces(Left: L, Right: R); |
917 | } |
918 | // Left is macro but right is not so they are not same macro or literal |
919 | return false; |
920 | } |
921 | const auto *Lil = dyn_cast<IntegerLiteral>(Val: Lhs); |
922 | const auto *Ril = dyn_cast<IntegerLiteral>(Val: Rhs); |
923 | if (Lil && Ril) |
924 | return Lil->getValue() == Ril->getValue(); |
925 | |
926 | const auto *Lbl = dyn_cast<CXXBoolLiteralExpr>(Val: Lhs); |
927 | const auto *Rbl = dyn_cast<CXXBoolLiteralExpr>(Val: Rhs); |
928 | if (Lbl && Rbl) |
929 | return Lbl->getValue() == Rbl->getValue(); |
930 | |
931 | return false; |
932 | } |
933 | } // namespace |
934 | |
935 | void RedundantExpressionCheck::registerMatchers(MatchFinder *Finder) { |
936 | const auto BannedIntegerLiteral = |
937 | integerLiteral(expandedByMacro(Names: KnownBannedMacroNames)); |
938 | const auto IsInUnevaluatedContext = expr(anyOf( |
939 | hasAncestor(expr(hasUnevaluatedContext())), hasAncestor(typeLoc()))); |
940 | |
941 | // Binary with equivalent operands, like (X != 2 && X != 2). |
942 | Finder->addMatcher( |
943 | NodeMatch: traverse(TK: TK_AsIs, |
944 | InnerMatcher: binaryOperator(anyOf(isComparisonOperator(), |
945 | hasAnyOperatorName("-" , "/" , "%" , "|" , "&" , |
946 | "^" , "&&" , "||" , "=" )), |
947 | operandsAreEquivalent(), |
948 | // Filter noisy false positives. |
949 | unless(isInTemplateInstantiation()), |
950 | unless(binaryOperatorIsInMacro()), |
951 | unless(hasAncestor(arraySubscriptExpr())), |
952 | unless(hasDescendant(BannedIntegerLiteral)), |
953 | unless(IsInUnevaluatedContext)) |
954 | .bind(ID: "binary" )), |
955 | Action: this); |
956 | |
957 | // Logical or bitwise operator with equivalent nested operands, like (X && Y |
958 | // && X) or (X && (Y && X)) |
959 | Finder->addMatcher( |
960 | NodeMatch: binaryOperator(hasAnyOperatorName("|" , "&" , "||" , "&&" , "^" ), |
961 | nestedOperandsAreEquivalent(), |
962 | // Filter noisy false positives. |
963 | unless(isInTemplateInstantiation()), |
964 | unless(binaryOperatorIsInMacro()), |
965 | // TODO: if the banned macros are themselves duplicated |
966 | unless(hasDescendant(BannedIntegerLiteral)), |
967 | unless(IsInUnevaluatedContext)) |
968 | .bind(ID: "nested-duplicates" ), |
969 | Action: this); |
970 | |
971 | // Conditional (ternary) operator with equivalent operands, like (Y ? X : X). |
972 | Finder->addMatcher( |
973 | NodeMatch: traverse(TK: TK_AsIs, |
974 | InnerMatcher: conditionalOperator(expressionsAreEquivalent(), |
975 | // Filter noisy false positives. |
976 | unless(conditionalOperatorIsInMacro()), |
977 | unless(isInTemplateInstantiation()), |
978 | unless(IsInUnevaluatedContext)) |
979 | .bind(ID: "cond" )), |
980 | Action: this); |
981 | |
982 | // Overloaded operators with equivalent operands. |
983 | Finder->addMatcher( |
984 | NodeMatch: traverse(TK: TK_AsIs, |
985 | InnerMatcher: cxxOperatorCallExpr( |
986 | hasAnyOverloadedOperatorName("-" , "/" , "%" , "|" , "&" , "^" , |
987 | "==" , "!=" , "<" , "<=" , ">" , |
988 | ">=" , "&&" , "||" , "=" ), |
989 | parametersAreEquivalent(), |
990 | // Filter noisy false positives. |
991 | unless(isMacro()), unless(isInTemplateInstantiation()), |
992 | unless(IsInUnevaluatedContext)) |
993 | .bind(ID: "call" )), |
994 | Action: this); |
995 | |
996 | // Overloaded operators with equivalent operands. |
997 | Finder->addMatcher( |
998 | NodeMatch: cxxOperatorCallExpr( |
999 | hasAnyOverloadedOperatorName("|" , "&" , "||" , "&&" , "^" ), |
1000 | nestedParametersAreEquivalent(), argumentCountIs(N: 2), |
1001 | // Filter noisy false positives. |
1002 | unless(isMacro()), unless(isInTemplateInstantiation()), |
1003 | unless(IsInUnevaluatedContext)) |
1004 | .bind(ID: "nested-duplicates" ), |
1005 | Action: this); |
1006 | |
1007 | // Match expressions like: !(1 | 2 | 3) |
1008 | Finder->addMatcher( |
1009 | NodeMatch: traverse(TK: TK_AsIs, |
1010 | InnerMatcher: implicitCastExpr( |
1011 | hasImplicitDestinationType(InnerMatcher: isInteger()), |
1012 | has(unaryOperator( |
1013 | hasOperatorName(Name: "!" ), |
1014 | hasUnaryOperand(InnerMatcher: ignoringParenImpCasts(InnerMatcher: binaryOperator( |
1015 | hasAnyOperatorName("|" , "&" ), |
1016 | hasLHS(InnerMatcher: anyOf( |
1017 | binaryOperator(hasAnyOperatorName("|" , "&" )), |
1018 | integerLiteral())), |
1019 | hasRHS(InnerMatcher: integerLiteral()))))) |
1020 | .bind(ID: "logical-bitwise-confusion" )), |
1021 | unless(IsInUnevaluatedContext))), |
1022 | Action: this); |
1023 | |
1024 | // Match expressions like: (X << 8) & 0xFF |
1025 | Finder->addMatcher( |
1026 | NodeMatch: traverse(TK: TK_AsIs, |
1027 | InnerMatcher: binaryOperator( |
1028 | hasOperatorName(Name: "&" ), |
1029 | hasOperands(Matcher1: ignoringParenImpCasts(InnerMatcher: binaryOperator( |
1030 | hasOperatorName(Name: "<<" ), |
1031 | hasRHS(InnerMatcher: ignoringParenImpCasts( |
1032 | InnerMatcher: integerLiteral().bind(ID: "shift-const" ))))), |
1033 | Matcher2: ignoringParenImpCasts( |
1034 | InnerMatcher: integerLiteral().bind(ID: "and-const" ))), |
1035 | unless(IsInUnevaluatedContext)) |
1036 | .bind(ID: "left-right-shift-confusion" )), |
1037 | Action: this); |
1038 | |
1039 | // Match common expressions and apply more checks to find redundant |
1040 | // sub-expressions. |
1041 | // a) Expr <op> K1 == K2 |
1042 | // b) Expr <op> K1 == Expr |
1043 | // c) Expr <op> K1 == Expr <op> K2 |
1044 | // see: 'checkArithmeticExpr' and 'checkBitwiseExpr' |
1045 | const auto BinOpCstLeft = matchBinOpIntegerConstantExpr(Id: "lhs" ); |
1046 | const auto BinOpCstRight = matchBinOpIntegerConstantExpr(Id: "rhs" ); |
1047 | const auto CstRight = matchIntegerConstantExpr(Id: "rhs" ); |
1048 | const auto SymRight = matchSymbolicExpr(Id: "rhs" ); |
1049 | |
1050 | // Match expressions like: x <op> 0xFF == 0xF00. |
1051 | Finder->addMatcher( |
1052 | NodeMatch: traverse(TK: TK_AsIs, InnerMatcher: binaryOperator(isComparisonOperator(), |
1053 | hasOperands(Matcher1: BinOpCstLeft, Matcher2: CstRight), |
1054 | unless(IsInUnevaluatedContext)) |
1055 | .bind(ID: "binop-const-compare-to-const" )), |
1056 | Action: this); |
1057 | |
1058 | // Match expressions like: x <op> 0xFF == x. |
1059 | Finder->addMatcher( |
1060 | NodeMatch: traverse( |
1061 | TK: TK_AsIs, |
1062 | InnerMatcher: binaryOperator(isComparisonOperator(), |
1063 | anyOf(allOf(hasLHS(InnerMatcher: BinOpCstLeft), hasRHS(InnerMatcher: SymRight)), |
1064 | allOf(hasLHS(InnerMatcher: SymRight), hasRHS(InnerMatcher: BinOpCstLeft))), |
1065 | unless(IsInUnevaluatedContext)) |
1066 | .bind(ID: "binop-const-compare-to-sym" )), |
1067 | Action: this); |
1068 | |
1069 | // Match expressions like: x <op> 10 == x <op> 12. |
1070 | Finder->addMatcher( |
1071 | NodeMatch: traverse(TK: TK_AsIs, |
1072 | InnerMatcher: binaryOperator(isComparisonOperator(), hasLHS(InnerMatcher: BinOpCstLeft), |
1073 | hasRHS(InnerMatcher: BinOpCstRight), |
1074 | // Already reported as redundant. |
1075 | unless(operandsAreEquivalent()), |
1076 | unless(IsInUnevaluatedContext)) |
1077 | .bind(ID: "binop-const-compare-to-binop-const" )), |
1078 | Action: this); |
1079 | |
1080 | // Match relational expressions combined with logical operators and find |
1081 | // redundant sub-expressions. |
1082 | // see: 'checkRelationalExpr' |
1083 | |
1084 | // Match expressions like: x < 2 && x > 2. |
1085 | const auto ComparisonLeft = matchRelationalIntegerConstantExpr(Id: "lhs" ); |
1086 | const auto ComparisonRight = matchRelationalIntegerConstantExpr(Id: "rhs" ); |
1087 | Finder->addMatcher( |
1088 | NodeMatch: traverse(TK: TK_AsIs, |
1089 | InnerMatcher: binaryOperator(hasAnyOperatorName("||" , "&&" ), |
1090 | hasLHS(InnerMatcher: ComparisonLeft), hasRHS(InnerMatcher: ComparisonRight), |
1091 | // Already reported as redundant. |
1092 | unless(operandsAreEquivalent()), |
1093 | unless(IsInUnevaluatedContext)) |
1094 | .bind(ID: "comparisons-of-symbol-and-const" )), |
1095 | Action: this); |
1096 | } |
1097 | |
1098 | void RedundantExpressionCheck::checkArithmeticExpr( |
1099 | const MatchFinder::MatchResult &Result) { |
1100 | APSInt LhsValue, RhsValue; |
1101 | const Expr *LhsSymbol = nullptr, *RhsSymbol = nullptr; |
1102 | BinaryOperatorKind LhsOpcode{}, RhsOpcode{}; |
1103 | |
1104 | if (const auto *ComparisonOperator = Result.Nodes.getNodeAs<BinaryOperator>( |
1105 | ID: "binop-const-compare-to-sym" )) { |
1106 | BinaryOperatorKind Opcode = ComparisonOperator->getOpcode(); |
1107 | if (!retrieveBinOpIntegerConstantExpr(Result, Id: "lhs" , Opcode&: LhsOpcode, Symbol&: LhsSymbol, |
1108 | Value&: LhsValue) || |
1109 | !retrieveSymbolicExpr(Result, Id: "rhs" , SymExpr&: RhsSymbol) || |
1110 | !areEquivalentExpr(Left: LhsSymbol, Right: RhsSymbol)) |
1111 | return; |
1112 | |
1113 | // Check expressions: x + k == x or x - k == x. |
1114 | if (LhsOpcode == BO_Add || LhsOpcode == BO_Sub) { |
1115 | if ((LhsValue != 0 && Opcode == BO_EQ) || |
1116 | (LhsValue == 0 && Opcode == BO_NE)) |
1117 | diag(Loc: ComparisonOperator->getOperatorLoc(), |
1118 | Description: "logical expression is always false" ); |
1119 | else if ((LhsValue == 0 && Opcode == BO_EQ) || |
1120 | (LhsValue != 0 && Opcode == BO_NE)) |
1121 | diag(Loc: ComparisonOperator->getOperatorLoc(), |
1122 | Description: "logical expression is always true" ); |
1123 | } |
1124 | } else if (const auto *ComparisonOperator = |
1125 | Result.Nodes.getNodeAs<BinaryOperator>( |
1126 | ID: "binop-const-compare-to-binop-const" )) { |
1127 | BinaryOperatorKind Opcode = ComparisonOperator->getOpcode(); |
1128 | |
1129 | if (!retrieveBinOpIntegerConstantExpr(Result, Id: "lhs" , Opcode&: LhsOpcode, Symbol&: LhsSymbol, |
1130 | Value&: LhsValue) || |
1131 | !retrieveBinOpIntegerConstantExpr(Result, Id: "rhs" , Opcode&: RhsOpcode, Symbol&: RhsSymbol, |
1132 | Value&: RhsValue) || |
1133 | !areEquivalentExpr(Left: LhsSymbol, Right: RhsSymbol)) |
1134 | return; |
1135 | |
1136 | transformSubToCanonicalAddExpr(Opcode&: LhsOpcode, Value&: LhsValue); |
1137 | transformSubToCanonicalAddExpr(Opcode&: RhsOpcode, Value&: RhsValue); |
1138 | |
1139 | // Check expressions: x + 1 == x + 2 or x + 1 != x + 2. |
1140 | if (LhsOpcode == BO_Add && RhsOpcode == BO_Add) { |
1141 | if ((Opcode == BO_EQ && APSInt::compareValues(I1: LhsValue, I2: RhsValue) == 0) || |
1142 | (Opcode == BO_NE && APSInt::compareValues(I1: LhsValue, I2: RhsValue) != 0)) { |
1143 | diag(Loc: ComparisonOperator->getOperatorLoc(), |
1144 | Description: "logical expression is always true" ); |
1145 | } else if ((Opcode == BO_EQ && |
1146 | APSInt::compareValues(I1: LhsValue, I2: RhsValue) != 0) || |
1147 | (Opcode == BO_NE && |
1148 | APSInt::compareValues(I1: LhsValue, I2: RhsValue) == 0)) { |
1149 | diag(Loc: ComparisonOperator->getOperatorLoc(), |
1150 | Description: "logical expression is always false" ); |
1151 | } |
1152 | } |
1153 | } |
1154 | } |
1155 | |
1156 | static bool exprEvaluatesToZero(BinaryOperatorKind Opcode, APSInt Value) { |
1157 | return (Opcode == BO_And || Opcode == BO_AndAssign) && Value == 0; |
1158 | } |
1159 | |
1160 | static bool exprEvaluatesToBitwiseNegatedZero(BinaryOperatorKind Opcode, |
1161 | APSInt Value) { |
1162 | return (Opcode == BO_Or || Opcode == BO_OrAssign) && ~Value == 0; |
1163 | } |
1164 | |
1165 | static bool exprEvaluatesToSymbolic(BinaryOperatorKind Opcode, APSInt Value) { |
1166 | return ((Opcode == BO_Or || Opcode == BO_OrAssign) && Value == 0) || |
1167 | ((Opcode == BO_And || Opcode == BO_AndAssign) && ~Value == 0); |
1168 | } |
1169 | |
1170 | void RedundantExpressionCheck::checkBitwiseExpr( |
1171 | const MatchFinder::MatchResult &Result) { |
1172 | if (const auto *ComparisonOperator = Result.Nodes.getNodeAs<BinaryOperator>( |
1173 | ID: "binop-const-compare-to-const" )) { |
1174 | BinaryOperatorKind Opcode = ComparisonOperator->getOpcode(); |
1175 | |
1176 | APSInt LhsValue, RhsValue; |
1177 | const Expr *LhsSymbol = nullptr; |
1178 | BinaryOperatorKind LhsOpcode{}; |
1179 | if (!retrieveBinOpIntegerConstantExpr(Result, Id: "lhs" , Opcode&: LhsOpcode, Symbol&: LhsSymbol, |
1180 | Value&: LhsValue) || |
1181 | !retrieveIntegerConstantExpr(Result, Id: "rhs" , Value&: RhsValue)) |
1182 | return; |
1183 | |
1184 | uint64_t LhsConstant = LhsValue.getZExtValue(); |
1185 | uint64_t RhsConstant = RhsValue.getZExtValue(); |
1186 | SourceLocation Loc = ComparisonOperator->getOperatorLoc(); |
1187 | |
1188 | // Check expression: x & k1 == k2 (i.e. x & 0xFF == 0xF00) |
1189 | if (LhsOpcode == BO_And && (LhsConstant & RhsConstant) != RhsConstant) { |
1190 | if (Opcode == BO_EQ) |
1191 | diag(Loc, Description: "logical expression is always false" ); |
1192 | else if (Opcode == BO_NE) |
1193 | diag(Loc, Description: "logical expression is always true" ); |
1194 | } |
1195 | |
1196 | // Check expression: x | k1 == k2 (i.e. x | 0xFF == 0xF00) |
1197 | if (LhsOpcode == BO_Or && (LhsConstant | RhsConstant) != RhsConstant) { |
1198 | if (Opcode == BO_EQ) |
1199 | diag(Loc, Description: "logical expression is always false" ); |
1200 | else if (Opcode == BO_NE) |
1201 | diag(Loc, Description: "logical expression is always true" ); |
1202 | } |
1203 | } else if (const auto *IneffectiveOperator = |
1204 | Result.Nodes.getNodeAs<BinaryOperator>( |
1205 | ID: "ineffective-bitwise" )) { |
1206 | APSInt Value; |
1207 | const Expr *Sym = nullptr, *ConstExpr = nullptr; |
1208 | |
1209 | if (!retrieveSymbolicExpr(Result, Id: "ineffective-bitwise" , SymExpr&: Sym) || |
1210 | !retrieveIntegerConstantExpr(Result, Id: "ineffective-bitwise" , Value, |
1211 | ConstExpr)) |
1212 | return; |
1213 | |
1214 | if ((Value != 0 && ~Value != 0) || Sym->getExprLoc().isMacroID()) |
1215 | return; |
1216 | |
1217 | SourceLocation Loc = IneffectiveOperator->getOperatorLoc(); |
1218 | |
1219 | BinaryOperatorKind Opcode = IneffectiveOperator->getOpcode(); |
1220 | if (exprEvaluatesToZero(Opcode, Value)) { |
1221 | diag(Loc, Description: "expression always evaluates to 0" ); |
1222 | } else if (exprEvaluatesToBitwiseNegatedZero(Opcode, Value)) { |
1223 | SourceRange ConstExprRange(ConstExpr->getBeginLoc(), |
1224 | ConstExpr->getEndLoc()); |
1225 | StringRef ConstExprText = Lexer::getSourceText( |
1226 | Range: CharSourceRange::getTokenRange(R: ConstExprRange), SM: *Result.SourceManager, |
1227 | LangOpts: Result.Context->getLangOpts()); |
1228 | |
1229 | diag(Loc, Description: "expression always evaluates to '%0'" ) << ConstExprText; |
1230 | |
1231 | } else if (exprEvaluatesToSymbolic(Opcode, Value)) { |
1232 | SourceRange SymExprRange(Sym->getBeginLoc(), Sym->getEndLoc()); |
1233 | |
1234 | StringRef ExprText = Lexer::getSourceText( |
1235 | Range: CharSourceRange::getTokenRange(R: SymExprRange), SM: *Result.SourceManager, |
1236 | LangOpts: Result.Context->getLangOpts()); |
1237 | |
1238 | diag(Loc, Description: "expression always evaluates to '%0'" ) << ExprText; |
1239 | } |
1240 | } |
1241 | } |
1242 | |
1243 | void RedundantExpressionCheck::checkRelationalExpr( |
1244 | const MatchFinder::MatchResult &Result) { |
1245 | if (const auto *ComparisonOperator = Result.Nodes.getNodeAs<BinaryOperator>( |
1246 | ID: "comparisons-of-symbol-and-const" )) { |
1247 | // Matched expressions are: (x <op> k1) <REL> (x <op> k2). |
1248 | // E.g.: (X < 2) && (X > 4) |
1249 | BinaryOperatorKind Opcode = ComparisonOperator->getOpcode(); |
1250 | |
1251 | const Expr *LhsExpr = nullptr, *RhsExpr = nullptr; |
1252 | const Expr *LhsSymbol = nullptr, *RhsSymbol = nullptr; |
1253 | const Expr *LhsConst = nullptr, *RhsConst = nullptr; |
1254 | BinaryOperatorKind LhsOpcode{}, RhsOpcode{}; |
1255 | APSInt LhsValue, RhsValue; |
1256 | |
1257 | if (!retrieveRelationalIntegerConstantExpr( |
1258 | Result, Id: "lhs" , OperandExpr&: LhsExpr, Opcode&: LhsOpcode, Symbol&: LhsSymbol, Value&: LhsValue, ConstExpr&: LhsConst) || |
1259 | !retrieveRelationalIntegerConstantExpr( |
1260 | Result, Id: "rhs" , OperandExpr&: RhsExpr, Opcode&: RhsOpcode, Symbol&: RhsSymbol, Value&: RhsValue, ConstExpr&: RhsConst) || |
1261 | !areEquivalentExpr(Left: LhsSymbol, Right: RhsSymbol)) |
1262 | return; |
1263 | |
1264 | // Bring expr to a canonical form: smallest constant must be on the left. |
1265 | if (APSInt::compareValues(I1: LhsValue, I2: RhsValue) > 0) { |
1266 | std::swap(a&: LhsExpr, b&: RhsExpr); |
1267 | std::swap(a&: LhsValue, b&: RhsValue); |
1268 | std::swap(a&: LhsSymbol, b&: RhsSymbol); |
1269 | std::swap(a&: LhsOpcode, b&: RhsOpcode); |
1270 | } |
1271 | |
1272 | // Constants come from two different macros, or one of them is a macro. |
1273 | if (areExprsFromDifferentMacros(LhsExpr: LhsConst, RhsExpr: RhsConst, AstCtx: Result.Context) || |
1274 | areExprsMacroAndNonMacro(LhsExpr&: LhsConst, RhsExpr&: RhsConst)) |
1275 | return; |
1276 | |
1277 | if ((Opcode == BO_LAnd || Opcode == BO_LOr) && |
1278 | areEquivalentRanges(OpcodeLHS: LhsOpcode, ValueLHS: LhsValue, OpcodeRHS: RhsOpcode, ValueRHS: RhsValue)) { |
1279 | diag(Loc: ComparisonOperator->getOperatorLoc(), |
1280 | Description: "equivalent expression on both sides of logical operator" ); |
1281 | return; |
1282 | } |
1283 | |
1284 | if (Opcode == BO_LAnd) { |
1285 | if (areExclusiveRanges(OpcodeLHS: LhsOpcode, ValueLHS: LhsValue, OpcodeRHS: RhsOpcode, ValueRHS: RhsValue)) { |
1286 | diag(Loc: ComparisonOperator->getOperatorLoc(), |
1287 | Description: "logical expression is always false" ); |
1288 | } else if (rangeSubsumesRange(OpcodeLHS: LhsOpcode, ValueLHS: LhsValue, OpcodeRHS: RhsOpcode, ValueRHS: RhsValue)) { |
1289 | diag(Loc: LhsExpr->getExprLoc(), Description: "expression is redundant" ); |
1290 | } else if (rangeSubsumesRange(OpcodeLHS: RhsOpcode, ValueLHS: RhsValue, OpcodeRHS: LhsOpcode, ValueRHS: LhsValue)) { |
1291 | diag(Loc: RhsExpr->getExprLoc(), Description: "expression is redundant" ); |
1292 | } |
1293 | } |
1294 | |
1295 | if (Opcode == BO_LOr) { |
1296 | if (rangesFullyCoverDomain(OpcodeLHS: LhsOpcode, ValueLHS: LhsValue, OpcodeRHS: RhsOpcode, ValueRHS: RhsValue)) { |
1297 | diag(Loc: ComparisonOperator->getOperatorLoc(), |
1298 | Description: "logical expression is always true" ); |
1299 | } else if (rangeSubsumesRange(OpcodeLHS: LhsOpcode, ValueLHS: LhsValue, OpcodeRHS: RhsOpcode, ValueRHS: RhsValue)) { |
1300 | diag(Loc: RhsExpr->getExprLoc(), Description: "expression is redundant" ); |
1301 | } else if (rangeSubsumesRange(OpcodeLHS: RhsOpcode, ValueLHS: RhsValue, OpcodeRHS: LhsOpcode, ValueRHS: LhsValue)) { |
1302 | diag(Loc: LhsExpr->getExprLoc(), Description: "expression is redundant" ); |
1303 | } |
1304 | } |
1305 | } |
1306 | } |
1307 | |
1308 | void RedundantExpressionCheck::check(const MatchFinder::MatchResult &Result) { |
1309 | if (const auto *BinOp = Result.Nodes.getNodeAs<BinaryOperator>(ID: "binary" )) { |
1310 | // If the expression's constants are macros, check whether they are |
1311 | // intentional. |
1312 | |
1313 | // |
1314 | // Special case for floating-point representation. |
1315 | // |
1316 | // If expressions on both sides of comparison operator are of type float, |
1317 | // then for some comparison operators no warning shall be |
1318 | // reported even if the expressions are identical from a symbolic point of |
1319 | // view. Comparison between expressions, declared variables and literals |
1320 | // are treated differently. |
1321 | // |
1322 | // != and == between float literals that have the same value should NOT |
1323 | // warn. < > between float literals that have the same value SHOULD warn. |
1324 | // |
1325 | // != and == between the same float declaration should NOT warn. |
1326 | // < > between the same float declaration SHOULD warn. |
1327 | // |
1328 | // != and == between eq. expressions that evaluates into float |
1329 | // should NOT warn. |
1330 | // < > between eq. expressions that evaluates into float |
1331 | // should NOT warn. |
1332 | // |
1333 | const Expr *LHS = BinOp->getLHS()->IgnoreParenImpCasts(); |
1334 | const Expr *RHS = BinOp->getRHS()->IgnoreParenImpCasts(); |
1335 | const BinaryOperator::Opcode Op = BinOp->getOpcode(); |
1336 | const bool OpEqualEQorNE = ((Op == BO_EQ) || (Op == BO_NE)); |
1337 | |
1338 | const auto *DeclRef1 = dyn_cast<DeclRefExpr>(Val: LHS); |
1339 | const auto *DeclRef2 = dyn_cast<DeclRefExpr>(Val: RHS); |
1340 | const auto *FloatLit1 = dyn_cast<FloatingLiteral>(Val: LHS); |
1341 | const auto *FloatLit2 = dyn_cast<FloatingLiteral>(Val: RHS); |
1342 | |
1343 | if (DeclRef1 && DeclRef2 && |
1344 | DeclRef1->getType()->hasFloatingRepresentation() && |
1345 | DeclRef2->getType()->hasFloatingRepresentation() && |
1346 | (DeclRef1->getDecl() == DeclRef2->getDecl()) && OpEqualEQorNE) { |
1347 | return; |
1348 | } |
1349 | |
1350 | if (FloatLit1 && FloatLit2 && |
1351 | FloatLit1->getValue().bitwiseIsEqual(RHS: FloatLit2->getValue()) && |
1352 | OpEqualEQorNE) { |
1353 | return; |
1354 | } |
1355 | |
1356 | if (areSidesBinaryConstExpressionsOrDefinesOrIntegerConstant( |
1357 | BinOp, AstCtx: Result.Context)) { |
1358 | const Expr *LhsConst = nullptr, *RhsConst = nullptr; |
1359 | BinaryOperatorKind MainOpcode{}, SideOpcode{}; |
1360 | if (areSidesBinaryConstExpressions(BinOp, AstCtx: Result.Context)) { |
1361 | if (!retrieveConstExprFromBothSides(BinOp, MainOpcode, SideOpcode, |
1362 | LhsConst, RhsConst, AstCtx: Result.Context)) |
1363 | return; |
1364 | |
1365 | if (areExprsFromDifferentMacros(LhsExpr: LhsConst, RhsExpr: RhsConst, AstCtx: Result.Context) || |
1366 | areExprsMacroAndNonMacro(LhsExpr&: LhsConst, RhsExpr&: RhsConst)) |
1367 | return; |
1368 | } else { |
1369 | if (!areExprsSameMacroOrLiteral(BinOp, Context: Result.Context)) |
1370 | return; |
1371 | } |
1372 | } |
1373 | diag(Loc: BinOp->getOperatorLoc(), Description: "both sides of operator are equivalent" ); |
1374 | } |
1375 | |
1376 | if (const auto *CondOp = |
1377 | Result.Nodes.getNodeAs<ConditionalOperator>(ID: "cond" )) { |
1378 | const Expr *TrueExpr = CondOp->getTrueExpr(); |
1379 | const Expr *FalseExpr = CondOp->getFalseExpr(); |
1380 | |
1381 | if (areExprsFromDifferentMacros(LhsExpr: TrueExpr, RhsExpr: FalseExpr, AstCtx: Result.Context) || |
1382 | areExprsMacroAndNonMacro(LhsExpr&: TrueExpr, RhsExpr&: FalseExpr)) |
1383 | return; |
1384 | diag(CondOp->getColonLoc(), |
1385 | "'true' and 'false' expressions are equivalent" ); |
1386 | } |
1387 | |
1388 | if (const auto *Call = Result.Nodes.getNodeAs<CXXOperatorCallExpr>(ID: "call" )) { |
1389 | if (canOverloadedOperatorArgsBeModified(OperatorCall: Call, CheckSecondParam: true)) |
1390 | return; |
1391 | |
1392 | diag(Loc: Call->getOperatorLoc(), |
1393 | Description: "both sides of overloaded operator are equivalent" ); |
1394 | } |
1395 | |
1396 | if (const auto *Op = Result.Nodes.getNodeAs<Expr>(ID: "nested-duplicates" )) { |
1397 | const auto *Call = dyn_cast<CXXOperatorCallExpr>(Val: Op); |
1398 | if (Call && canOverloadedOperatorArgsBeModified(OperatorCall: Call, CheckSecondParam: true)) |
1399 | return; |
1400 | |
1401 | StringRef Message = |
1402 | Call ? "overloaded operator has equivalent nested operands" |
1403 | : "operator has equivalent nested operands" ; |
1404 | |
1405 | const auto Diag = diag(Loc: Op->getExprLoc(), Description: Message); |
1406 | for (const auto &KeyValue : Result.Nodes.getMap()) { |
1407 | if (StringRef(KeyValue.first).starts_with(Prefix: "duplicate" )) |
1408 | Diag << KeyValue.second.getSourceRange(); |
1409 | } |
1410 | } |
1411 | |
1412 | if (const auto *NegateOperator = |
1413 | Result.Nodes.getNodeAs<UnaryOperator>(ID: "logical-bitwise-confusion" )) { |
1414 | SourceLocation OperatorLoc = NegateOperator->getOperatorLoc(); |
1415 | |
1416 | auto Diag = |
1417 | diag(Loc: OperatorLoc, |
1418 | Description: "ineffective logical negation operator used; did you mean '~'?" ); |
1419 | SourceLocation LogicalNotLocation = OperatorLoc.getLocWithOffset(Offset: 1); |
1420 | |
1421 | if (!LogicalNotLocation.isMacroID()) |
1422 | Diag << FixItHint::CreateReplacement( |
1423 | RemoveRange: CharSourceRange::getCharRange(B: OperatorLoc, E: LogicalNotLocation), Code: "~" ); |
1424 | } |
1425 | |
1426 | if (const auto *BinaryAndExpr = Result.Nodes.getNodeAs<BinaryOperator>( |
1427 | ID: "left-right-shift-confusion" )) { |
1428 | const auto *ShiftingConst = Result.Nodes.getNodeAs<Expr>(ID: "shift-const" ); |
1429 | assert(ShiftingConst && "Expr* 'ShiftingConst' is nullptr!" ); |
1430 | std::optional<llvm::APSInt> ShiftingValue = |
1431 | ShiftingConst->getIntegerConstantExpr(Ctx: *Result.Context); |
1432 | |
1433 | if (!ShiftingValue) |
1434 | return; |
1435 | |
1436 | const auto *AndConst = Result.Nodes.getNodeAs<Expr>(ID: "and-const" ); |
1437 | assert(AndConst && "Expr* 'AndCont' is nullptr!" ); |
1438 | std::optional<llvm::APSInt> AndValue = |
1439 | AndConst->getIntegerConstantExpr(Ctx: *Result.Context); |
1440 | if (!AndValue) |
1441 | return; |
1442 | |
1443 | // If ShiftingConst is shifted left with more bits than the position of the |
1444 | // leftmost 1 in the bit representation of AndValue, AndConstant is |
1445 | // ineffective. |
1446 | if (AndValue->getActiveBits() > *ShiftingValue) |
1447 | return; |
1448 | |
1449 | auto Diag = diag(Loc: BinaryAndExpr->getOperatorLoc(), |
1450 | Description: "ineffective bitwise and operation" ); |
1451 | } |
1452 | |
1453 | // Check for the following bound expressions: |
1454 | // - "binop-const-compare-to-sym", |
1455 | // - "binop-const-compare-to-binop-const", |
1456 | // Produced message: |
1457 | // -> "logical expression is always false/true" |
1458 | checkArithmeticExpr(Result); |
1459 | |
1460 | // Check for the following bound expression: |
1461 | // - "binop-const-compare-to-const", |
1462 | // - "ineffective-bitwise" |
1463 | // Produced message: |
1464 | // -> "logical expression is always false/true" |
1465 | // -> "expression always evaluates to ..." |
1466 | checkBitwiseExpr(Result); |
1467 | |
1468 | // Check for te following bound expression: |
1469 | // - "comparisons-of-symbol-and-const", |
1470 | // Produced messages: |
1471 | // -> "equivalent expression on both sides of logical operator", |
1472 | // -> "logical expression is always false/true" |
1473 | // -> "expression is redundant" |
1474 | checkRelationalExpr(Result); |
1475 | } |
1476 | |
1477 | } // namespace clang::tidy::misc |
1478 | |