xref: /llvm-project/clang-tools-extra/clang-tidy/bugprone/AssignmentInIfConditionCheck.cpp (revision b9852ff5fc4986c6cf8c4ecd1eb5726d55a08ea3)
1 //===--- AssignmentInIfConditionCheck.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 "AssignmentInIfConditionCheck.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/AST/RecursiveASTVisitor.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 
14 using namespace clang::ast_matchers;
15 
16 namespace clang::tidy::bugprone {
17 
18 void AssignmentInIfConditionCheck::registerMatchers(MatchFinder *Finder) {
19   Finder->addMatcher(translationUnitDecl(), this);
20 }
21 
22 void AssignmentInIfConditionCheck::check(
23     const ast_matchers::MatchFinder::MatchResult &Result) {
24   class Visitor : public RecursiveASTVisitor<Visitor> {
25     AssignmentInIfConditionCheck &Check;
26 
27   public:
28     explicit Visitor(AssignmentInIfConditionCheck &Check) : Check(Check) {}
29     bool VisitIfStmt(IfStmt *If) {
30       class ConditionVisitor : public RecursiveASTVisitor<ConditionVisitor> {
31         AssignmentInIfConditionCheck &Check;
32 
33       public:
34         explicit ConditionVisitor(AssignmentInIfConditionCheck &Check)
35             : Check(Check) {}
36 
37         // Dont traverse into any lambda expressions.
38         bool TraverseLambdaExpr(LambdaExpr *, DataRecursionQueue * = nullptr) {
39           return true;
40         }
41 
42         // Dont traverse into any requires expressions.
43         bool TraverseRequiresExpr(RequiresExpr *,
44                                   DataRecursionQueue * = nullptr) {
45           return true;
46         }
47 
48         bool VisitBinaryOperator(BinaryOperator *BO) {
49           if (BO->isAssignmentOp())
50             Check.report(BO);
51           return true;
52         }
53 
54         bool VisitCXXOperatorCallExpr(CXXOperatorCallExpr *OCE) {
55           if (OCE->isAssignmentOp())
56             Check.report(OCE);
57           return true;
58         }
59       };
60 
61       ConditionVisitor(Check).TraverseStmt(If->getCond());
62       return true;
63     }
64   };
65   Visitor(*this).TraverseAST(*Result.Context);
66 }
67 
68 void AssignmentInIfConditionCheck::report(const Expr *AssignmentExpr) {
69   SourceLocation OpLoc =
70       isa<BinaryOperator>(AssignmentExpr)
71           ? cast<BinaryOperator>(AssignmentExpr)->getOperatorLoc()
72           : cast<CXXOperatorCallExpr>(AssignmentExpr)->getOperatorLoc();
73 
74   diag(OpLoc, "an assignment within an 'if' condition is bug-prone")
75       << AssignmentExpr->getSourceRange();
76   diag(OpLoc,
77        "if it should be an assignment, move it out of the 'if' condition",
78        DiagnosticIDs::Note);
79   diag(OpLoc, "if it is meant to be an equality check, change '=' to '=='",
80        DiagnosticIDs::Note);
81 }
82 
83 } // namespace clang::tidy::bugprone
84