clang-tools 23.0.0git
AssignmentInSelectionStatementCheck.cpp
Go to the documentation of this file.
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-exception
6//
7//===----------------------------------------------------------------------===//
8
10#include "clang/AST/IgnoreExpr.h"
11#include "clang/AST/StmtVisitor.h"
12#include "clang/ASTMatchers/ASTMatchFinder.h"
13#include "llvm/ADT/TypeSwitch.h"
14
15using namespace clang;
16using namespace clang::ast_matchers;
17
18namespace {
19
20class ConditionValueCanPropagateFrom
21 : public ConstStmtVisitor<ConditionValueCanPropagateFrom, void> {
22public:
23 llvm::SmallVector<const Expr *, 2> ExprToProcess;
24
25 void VisitBinaryOperator(const BinaryOperator *BO) {
26 if (BO->isCommaOp())
27 ExprToProcess.push_back(BO->getRHS()->IgnoreParenImpCasts());
28 }
29 void VisitAbstractConditionalOperator(const AbstractConditionalOperator *CO) {
30 ExprToProcess.push_back(CO->getFalseExpr()->IgnoreParenImpCasts());
31 ExprToProcess.push_back(CO->getTrueExpr()->IgnoreParenImpCasts());
32 }
33};
34
35AST_MATCHER_P(Expr, conditionValueCanPropagateFrom,
36 ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
37 bool Found = false;
38 ConditionValueCanPropagateFrom Visitor;
39 Visitor.Visit(&Node); // Do not match Node itself.
40 while (!Visitor.ExprToProcess.empty()) {
41 const Expr *E = Visitor.ExprToProcess.pop_back_val();
42 ast_matchers::internal::BoundNodesTreeBuilder Result;
43 if (InnerMatcher.matches(*E, Finder, &Result)) {
44 Found = true;
45 Builder->addMatch(Result);
46 }
47 Visitor.Visit(E);
48 }
49 return Found;
50}
51
52// Ignore implicit casts (including C++ conversion member calls) but not parens.
53AST_MATCHER_P(Expr, ignoringImplicitAsWritten,
54 ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
55 auto IgnoreImplicitMemberCallSingleStep = [](Expr *E) {
56 if (auto *C = dyn_cast<CXXMemberCallExpr>(E)) {
57 Expr *ExprNode = C->getImplicitObjectArgument();
58 if (ExprNode->getSourceRange() == E->getSourceRange())
59 return ExprNode;
60 ExprNode = ExprNode->IgnoreParenImpCasts();
61 if (ExprNode->getSourceRange() == E->getSourceRange())
62 return ExprNode;
63 }
64 return E;
65 };
66
67 const Expr *IgnoreE = IgnoreExprNodes(&Node, IgnoreImplicitSingleStep,
68 IgnoreImplicitCastsExtraSingleStep,
69 IgnoreImplicitMemberCallSingleStep);
70
71 return InnerMatcher.matches(*IgnoreE, Finder, Builder);
72}
73
74} // namespace
75
76namespace clang::tidy::bugprone {
77
79 MatchFinder *Finder) {
80 auto AssignOpNoParens = ignoringImplicitAsWritten(
81 binaryOperation(hasOperatorName("=")).bind("assignment"));
82 auto AssignOpMaybeParens = ignoringParenImpCasts(
83 binaryOperation(hasOperatorName("=")).bind("assignment"));
84 auto AssignOpFromEmbeddedExpr = expr(ignoringParenImpCasts(
85 conditionValueCanPropagateFrom(AssignOpMaybeParens)));
86
87 auto CondExprWithAssign = anyOf(AssignOpNoParens, AssignOpFromEmbeddedExpr);
88 auto OpCondExprWithAssign =
89 anyOf(AssignOpMaybeParens, AssignOpFromEmbeddedExpr);
90
91 // In these cases "single primary expression" is possible.
92 // A single assignment within a 'ParenExpr' is allowed (but not if mixed with
93 // other operators).
94 auto FoundControlStmt = mapAnyOf(ifStmt, whileStmt, doStmt, forStmt)
95 .with(hasCondition(CondExprWithAssign));
96 // In these cases "single primary expression" is not possible because the
97 // assignment is already part of a bigger expression.
98 auto FoundConditionalOperator =
99 mapAnyOf(conditionalOperator, binaryConditionalOperator)
100 .with(hasCondition(OpCondExprWithAssign));
101 auto FoundLogicalOp = binaryOperator(
102 hasAnyOperatorName("&&", "||"),
103 eachOf(hasLHS(OpCondExprWithAssign), hasRHS(OpCondExprWithAssign)));
104
105 auto FoundSelectionStmt =
106 stmt(anyOf(FoundControlStmt, FoundConditionalOperator, FoundLogicalOp))
107 .bind("parent");
108
109 Finder->addMatcher(FoundSelectionStmt, this);
110}
111
113 const MatchFinder::MatchResult &Result) {
114 const auto *FoundAssignment = Result.Nodes.getNodeAs<Stmt>("assignment");
115 assert(FoundAssignment);
116
117 const auto *ParentStmt = Result.Nodes.getNodeAs<Stmt>("parent");
118 const StringRef CondStr =
119 llvm::TypeSwitch<const Stmt *, const char *>(ParentStmt)
120 .Case([](const IfStmt *) { return "condition of 'if' statement"; })
121 .Case<WhileStmt, DoStmt, ForStmt>(
122 [](const Stmt *) { return "condition of a loop"; })
123 .Case([](const ConditionalOperator *) {
124 return "condition of a ternary operator";
125 })
126 .Case([](const BinaryOperator *) {
127 return "operand of a logical operator";
128 })
129 .DefaultUnreachable();
130
131 const SourceLocation OpLoc =
132 llvm::TypeSwitch<const Stmt *, SourceLocation>(FoundAssignment)
133 .Case<BinaryOperator, CXXOperatorCallExpr>(
134 [](const auto *Op) { return Op->getOperatorLoc(); })
135 .Default(FoundAssignment->getBeginLoc());
136 diag(OpLoc, "assignment within %0 may indicate programmer error")
137 << FoundAssignment->getSourceRange() << CondStr;
138 diag(OpLoc, "if it should be an assignment, move it out of the condition",
139 DiagnosticIDs::Note);
140 diag(OpLoc, "if it is meant to be an equality check, change '=' to '=='",
141 DiagnosticIDs::Note);
142}
143
144} // namespace clang::tidy::bugprone
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
AST_MATCHER_P(Stmt, isStatementIdenticalToBoundNode, std::string, ID)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//