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