xref: /llvm-project/clang-tools-extra/clang-tidy/bugprone/IncDecInConditionsCheck.cpp (revision 11a411a49b62c129bba551df4587dd446fcdc660)
1 //===--- IncDecInConditionsCheck.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 "IncDecInConditionsCheck.h"
10 #include "../utils/Matchers.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 
14 using namespace clang::ast_matchers;
15 
16 namespace clang::tidy::bugprone {
17 
AST_MATCHER(BinaryOperator,isLogicalOperator)18 AST_MATCHER(BinaryOperator, isLogicalOperator) { return Node.isLogicalOp(); }
19 
AST_MATCHER(UnaryOperator,isUnaryPrePostOperator)20 AST_MATCHER(UnaryOperator, isUnaryPrePostOperator) {
21   return Node.isPrefix() || Node.isPostfix();
22 }
23 
AST_MATCHER(CXXOperatorCallExpr,isPrePostOperator)24 AST_MATCHER(CXXOperatorCallExpr, isPrePostOperator) {
25   return Node.getOperator() == OO_PlusPlus ||
26          Node.getOperator() == OO_MinusMinus;
27 }
28 
registerMatchers(MatchFinder * Finder)29 void IncDecInConditionsCheck::registerMatchers(MatchFinder *Finder) {
30   auto OperatorMatcher = expr(
31       anyOf(binaryOperator(anyOf(isComparisonOperator(), isLogicalOperator())),
32             cxxOperatorCallExpr(isComparisonOperator())));
33 
34   auto IsInUnevaluatedContext =
35       expr(anyOf(hasAncestor(expr(matchers::hasUnevaluatedContext())),
36                  hasAncestor(typeLoc())));
37 
38   Finder->addMatcher(
39       expr(
40           OperatorMatcher, unless(isExpansionInSystemHeader()),
41           unless(hasAncestor(OperatorMatcher)), expr().bind("parent"),
42 
43           forEachDescendant(
44               expr(anyOf(unaryOperator(isUnaryPrePostOperator(),
45                                        hasUnaryOperand(expr().bind("operand"))),
46                          cxxOperatorCallExpr(
47                              isPrePostOperator(),
48                              hasUnaryOperand(expr().bind("operand")))),
49                    unless(IsInUnevaluatedContext),
50                    hasAncestor(
51                        expr(equalsBoundNode("parent"),
52                             hasDescendant(
53                                 expr(unless(equalsBoundNode("operand")),
54                                      matchers::isStatementIdenticalToBoundNode(
55                                          "operand"),
56                                      unless(IsInUnevaluatedContext))
57                                     .bind("second")))))
58                   .bind("operator"))),
59       this);
60 }
61 
check(const MatchFinder::MatchResult & Result)62 void IncDecInConditionsCheck::check(const MatchFinder::MatchResult &Result) {
63 
64   SourceLocation ExprLoc;
65   bool IsIncrementOp = false;
66 
67   if (const auto *MatchedDecl =
68           Result.Nodes.getNodeAs<CXXOperatorCallExpr>("operator")) {
69     ExprLoc = MatchedDecl->getExprLoc();
70     IsIncrementOp = (MatchedDecl->getOperator() == OO_PlusPlus);
71   } else if (const auto *MatchedDecl =
72                  Result.Nodes.getNodeAs<UnaryOperator>("operator")) {
73     ExprLoc = MatchedDecl->getExprLoc();
74     IsIncrementOp = MatchedDecl->isIncrementOp();
75   } else
76     return;
77 
78   diag(ExprLoc,
79        "%select{decrementing|incrementing}0 and referencing a variable in a "
80        "complex condition can cause unintended side-effects due to C++'s order "
81        "of evaluation, consider moving the modification outside of the "
82        "condition to avoid misunderstandings")
83       << IsIncrementOp;
84   diag(Result.Nodes.getNodeAs<Expr>("second")->getExprLoc(),
85        "variable is referenced here", DiagnosticIDs::Note);
86 }
87 
88 } // namespace clang::tidy::bugprone
89