Cppcheck
checkcondition.h
Go to the documentation of this file.
1 /*
2  * Cppcheck - A tool for static C/C++ code analysis
3  * Copyright (C) 2007-2024 Cppcheck team.
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 
20 //---------------------------------------------------------------------------
21 #ifndef checkconditionH
22 #define checkconditionH
23 //---------------------------------------------------------------------------
24 
25 #include "check.h"
26 #include "config.h"
27 #include "mathlib.h"
28 #include "errortypes.h"
29 #include "tokenize.h"
30 
31 #include <set>
32 #include <string>
33 
34 class Settings;
35 class Token;
36 class ErrorLogger;
37 class ValueType;
38 
39 namespace ValueFlow {
40  class Value;
41 }
42 
43 /// @addtogroup Checks
44 /// @{
45 
46 /**
47  * @brief Check for condition mismatches
48  */
49 
51 public:
52  /** This constructor is used when registering the CheckAssignIf */
53  CheckCondition() : Check(myName()) {}
54 
55 private:
56  /** This constructor is used when running checks. */
57  CheckCondition(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger)
58  : Check(myName(), tokenizer, settings, errorLogger) {}
59 
60  void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override {
61  CheckCondition checkCondition(&tokenizer, &tokenizer.getSettings(), errorLogger);
62  checkCondition.multiCondition();
63  checkCondition.clarifyCondition(); // not simplified because ifAssign
64  checkCondition.multiCondition2();
65  checkCondition.checkIncorrectLogicOperator();
66  checkCondition.checkInvalidTestForOverflow();
67  checkCondition.duplicateCondition();
68  checkCondition.checkPointerAdditionResultNotNull();
69  checkCondition.checkDuplicateConditionalAssign();
70  checkCondition.assignIf();
71  checkCondition.checkBadBitmaskCheck();
72  checkCondition.comparison();
73  checkCondition.checkModuloAlwaysTrueFalse();
74  checkCondition.checkAssignmentInCondition();
75  checkCondition.checkCompareValueOutOfTypeRange();
76  checkCondition.alwaysTrueFalse();
77  }
78 
79  /** mismatching assignment / comparison */
80  void assignIf();
81 
82  /** parse scopes recursively */
83  bool assignIfParseScope(const Token * const assignTok,
84  const Token * const startTok,
85  const nonneg int varid,
86  const bool islocal,
87  const char bitop,
88  const MathLib::bigint num);
89 
90  /** check bitmask using | instead of & */
91  void checkBadBitmaskCheck();
92 
93  /** mismatching lhs and rhs in comparison */
94  void comparison();
95 
96  void duplicateCondition();
97 
98  /** match 'if' and 'else if' conditions */
99  void multiCondition();
100 
101  /**
102  * multiconditions #2
103  * - Opposite inner conditions => always false
104  * - (TODO) Same/Overlapping inner condition => always true
105  * - same condition after early exit => always false
106  **/
107  void multiCondition2();
108 
109  /** @brief %Check for testing for mutual exclusion over ||*/
110  void checkIncorrectLogicOperator();
111 
112  /** @brief %Check for suspicious usage of modulo (e.g. "if(var % 4 == 4)") */
113  void checkModuloAlwaysTrueFalse();
114 
115  /** @brief Suspicious condition (assignment+comparison) */
116  void clarifyCondition();
117 
118  /** @brief Condition is always true/false */
119  void alwaysTrueFalse();
120 
121  /** @brief %Check for invalid test for overflow 'x+100 < x' */
122  void checkInvalidTestForOverflow();
123 
124  /** @brief Check if pointer addition result is NULL '(ptr + 1) == NULL' */
125  void checkPointerAdditionResultNotNull();
126 
127  void checkDuplicateConditionalAssign();
128 
129  /** @brief Assignment in condition */
130  void checkAssignmentInCondition();
131 
132  // The conditions that have been diagnosed
133  std::set<const Token*> mCondDiags;
134  bool diag(const Token* tok, bool insert=true);
135  bool isAliased(const std::set<int> &vars) const;
136  bool isOverlappingCond(const Token * const cond1, const Token * const cond2, bool pure) const;
137  void assignIfError(const Token *tok1, const Token *tok2, const std::string &condition, bool result);
138  void mismatchingBitAndError(const Token *tok1, const MathLib::bigint num1, const Token *tok2, const MathLib::bigint num2);
139  void badBitmaskCheckError(const Token *tok, bool isNoOp = false);
140  void comparisonError(const Token *tok,
141  const std::string &bitop,
142  MathLib::bigint value1,
143  const std::string &op,
144  MathLib::bigint value2,
145  bool result);
146  void duplicateConditionError(const Token *tok1, const Token *tok2, ErrorPath errorPath);
147  void overlappingElseIfConditionError(const Token *tok, nonneg int line1);
148  void oppositeElseIfConditionError(const Token *ifCond, const Token *elseIfCond, ErrorPath errorPath);
149 
150  void oppositeInnerConditionError(const Token *tok1, const Token* tok2, ErrorPath errorPath);
151 
152  void identicalInnerConditionError(const Token *tok1, const Token* tok2, ErrorPath errorPath);
153 
154  void identicalConditionAfterEarlyExitError(const Token *cond1, const Token *cond2, ErrorPath errorPath);
155 
156  void incorrectLogicOperatorError(const Token *tok, const std::string &condition, bool always, bool inconclusive, ErrorPath errors);
157  void redundantConditionError(const Token *tok, const std::string &text, bool inconclusive);
158 
159  void moduloAlwaysTrueFalseError(const Token* tok, const std::string& maxVal);
160 
161  void clarifyConditionError(const Token *tok, bool assign, bool boolop);
162 
163  void alwaysTrueFalseError(const Token* tok, const Token* condition, const ValueFlow::Value* value);
164 
165  void invalidTestForOverflow(const Token* tok, const ValueType *valueType, const std::string &replace);
166  void pointerAdditionResultNotNullError(const Token *tok, const Token *calc);
167 
168  void duplicateConditionalAssignError(const Token *condTok, const Token* assignTok, bool isRedundant = false);
169 
170  void assignmentInCondition(const Token *eq);
171 
172  void checkCompareValueOutOfTypeRange();
173  void compareValueOutOfTypeRangeError(const Token *comparison, const std::string &type, long long value, bool result);
174 
175  void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override {
176  CheckCondition c(nullptr, settings, errorLogger);
177 
178  c.assignIfError(nullptr, nullptr, emptyString, false);
179  c.badBitmaskCheckError(nullptr);
180  c.comparisonError(nullptr, "&", 6, "==", 1, false);
181  c.duplicateConditionError(nullptr, nullptr, ErrorPath{});
182  c.overlappingElseIfConditionError(nullptr, 1);
183  c.mismatchingBitAndError(nullptr, 0xf0, nullptr, 1);
184  c.oppositeInnerConditionError(nullptr, nullptr, ErrorPath{});
185  c.identicalInnerConditionError(nullptr, nullptr, ErrorPath{});
186  c.identicalConditionAfterEarlyExitError(nullptr, nullptr, ErrorPath{});
187  c.incorrectLogicOperatorError(nullptr, "foo > 3 && foo < 4", true, false, ErrorPath{});
188  c.redundantConditionError(nullptr, "If x > 11 the condition x > 10 is always true.", false);
189  c.moduloAlwaysTrueFalseError(nullptr, "1");
190  c.clarifyConditionError(nullptr, true, false);
191  c.alwaysTrueFalseError(nullptr, nullptr, nullptr);
192  c.invalidTestForOverflow(nullptr, nullptr, "false");
193  c.pointerAdditionResultNotNullError(nullptr, nullptr);
194  c.duplicateConditionalAssignError(nullptr, nullptr);
195  c.assignmentInCondition(nullptr);
196  c.compareValueOutOfTypeRangeError(nullptr, "unsigned char", 256, true);
197  }
198 
199  static std::string myName() {
200  return "Condition";
201  }
202 
203  std::string classInfo() const override {
204  return "Match conditions with assignments and other conditions:\n"
205  "- Mismatching assignment and comparison => comparison is always true/false\n"
206  "- Mismatching lhs and rhs in comparison => comparison is always true/false\n"
207  "- Detect usage of | where & should be used\n"
208  "- Duplicate condition and assignment\n"
209  "- Detect matching 'if' and 'else if' conditions\n"
210  "- Mismatching bitand (a &= 0xf0; a &= 1; => a = 0)\n"
211  "- Opposite inner condition is always false\n"
212  "- Identical condition after early exit is always false\n"
213  "- Condition that is always true/false\n"
214  "- Mutual exclusion over || always evaluating to true\n"
215  "- Comparisons of modulo results that are always true/false.\n"
216  "- Known variable values => condition is always true/false\n"
217  "- Invalid test for overflow. Some mainstream compilers remove such overflow tests when optimising code.\n"
218  "- Suspicious assignment of container/iterator in condition => condition is always true.\n";
219  }
220 };
221 /// @}
222 //---------------------------------------------------------------------------
223 #endif // checkconditionH
static bool isAliased(const Token *startTok, const Token *endTok, nonneg int varid)
Definition: astutils.cpp:1073
Check for condition mismatches.
void oppositeInnerConditionError(const Token *tok1, const Token *tok2, ErrorPath errorPath)
void checkInvalidTestForOverflow()
Check for invalid test for overflow 'x+100 < x'
void checkIncorrectLogicOperator()
Check for testing for mutual exclusion over ||
void comparison()
mismatching lhs and rhs in comparison
std::set< const Token * > mCondDiags
void multiCondition()
match 'if' and 'else if' conditions
void assignIf()
mismatching assignment / comparison
void invalidTestForOverflow(const Token *tok, const ValueType *valueType, const std::string &replace)
void checkPointerAdditionResultNotNull()
Check if pointer addition result is NULL '(ptr + 1) == NULL'.
void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override
get error messages
void assignIfError(const Token *tok1, const Token *tok2, const std::string &condition, bool result)
void redundantConditionError(const Token *tok, const std::string &text, bool inconclusive)
void alwaysTrueFalseError(const Token *tok, const Token *condition, const ValueFlow::Value *value)
void identicalConditionAfterEarlyExitError(const Token *cond1, const Token *cond2, ErrorPath errorPath)
void assignmentInCondition(const Token *eq)
void multiCondition2()
multiconditions #2
void badBitmaskCheckError(const Token *tok, bool isNoOp=false)
void identicalInnerConditionError(const Token *tok1, const Token *tok2, ErrorPath errorPath)
void duplicateConditionError(const Token *tok1, const Token *tok2, ErrorPath errorPath)
CheckCondition()
This constructor is used when registering the CheckAssignIf.
void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override
run checks, the token list is not simplified
void moduloAlwaysTrueFalseError(const Token *tok, const std::string &maxVal)
void checkModuloAlwaysTrueFalse()
Check for suspicious usage of modulo (e.g.
void clarifyConditionError(const Token *tok, bool assign, bool boolop)
void mismatchingBitAndError(const Token *tok1, const MathLib::bigint num1, const Token *tok2, const MathLib::bigint num2)
void clarifyCondition()
Suspicious condition (assignment+comparison)
void duplicateConditionalAssignError(const Token *condTok, const Token *assignTok, bool isRedundant=false)
void incorrectLogicOperatorError(const Token *tok, const std::string &condition, bool always, bool inconclusive, ErrorPath errors)
void compareValueOutOfTypeRangeError(const Token *comparison, const std::string &type, long long value, bool result)
void checkDuplicateConditionalAssign()
std::string classInfo() const override
get information about this class, used to generate documentation
void alwaysTrueFalse()
Condition is always true/false.
void overlappingElseIfConditionError(const Token *tok, nonneg int line1)
void comparisonError(const Token *tok, const std::string &bitop, MathLib::bigint value1, const std::string &op, MathLib::bigint value2, bool result)
CheckCondition(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger)
This constructor is used when running checks.
void pointerAdditionResultNotNullError(const Token *tok, const Token *calc)
static std::string myName()
void checkCompareValueOutOfTypeRange()
void checkAssignmentInCondition()
Assignment in condition.
void checkBadBitmaskCheck()
check bitmask using | instead of &
Interface class that cppcheck uses to communicate with the checks.
Definition: check.h:59
This is an interface, which the class responsible of error logging should implement.
Definition: errorlogger.h:214
long long bigint
Definition: mathlib.h:68
This is just a container for general settings so that we don't need to pass individual values to func...
Definition: settings.h:95
The token list that the TokenList generates is a linked-list of this class.
Definition: token.h:150
The main purpose is to tokenize the source code.
Definition: tokenize.h:46
const Settings & getSettings() const
Definition: tokenize.h:615
Value type.
static const std::string emptyString
Definition: config.h:127
#define CPPCHECKLIB
Definition: config.h:35
#define nonneg
Definition: config.h:138
static void replace(std::string &source, const std::unordered_map< std::string, std::string > &substitutionMap)
std::list< ErrorPathItem > ErrorPath
Definition: errortypes.h:130