/src/cppcheck/lib/checkfunctions.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Cppcheck - A tool for static C/C++ code analysis |
3 | | * Copyright (C) 2007-2023 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 | | // Check functions |
21 | | //--------------------------------------------------------------------------- |
22 | | |
23 | | #include "checkfunctions.h" |
24 | | |
25 | | #include "astutils.h" |
26 | | #include "mathlib.h" |
27 | | #include "platform.h" |
28 | | #include "standards.h" |
29 | | #include "symboldatabase.h" |
30 | | #include "token.h" |
31 | | #include "tokenize.h" |
32 | | #include "valueflow.h" |
33 | | #include "vfvalue.h" |
34 | | |
35 | | #include <iomanip> |
36 | | #include <list> |
37 | | #include <sstream> |
38 | | #include <unordered_map> |
39 | | #include <vector> |
40 | | |
41 | | //--------------------------------------------------------------------------- |
42 | | |
43 | | |
44 | | // Register this check class (by creating a static instance of it) |
45 | | namespace { |
46 | | CheckFunctions instance; |
47 | | } |
48 | | |
49 | | static const CWE CWE252(252U); // Unchecked Return Value |
50 | | static const CWE CWE477(477U); // Use of Obsolete Functions |
51 | | static const CWE CWE758(758U); // Reliance on Undefined, Unspecified, or Implementation-Defined Behavior |
52 | | static const CWE CWE628(628U); // Function Call with Incorrectly Specified Arguments |
53 | | static const CWE CWE686(686U); // Function Call With Incorrect Argument Type |
54 | | static const CWE CWE687(687U); // Function Call With Incorrectly Specified Argument Value |
55 | | static const CWE CWE688(688U); // Function Call With Incorrect Variable or Reference as Argument |
56 | | |
57 | | void CheckFunctions::checkProhibitedFunctions() |
58 | 1.36k | { |
59 | 1.36k | const bool checkAlloca = mSettings->severity.isEnabled(Severity::warning) && ((mSettings->standards.c >= Standards::C99 && mTokenizer->isC()) || mSettings->standards.cpp >= Standards::CPP11); |
60 | | |
61 | 1.36k | logChecker("CheckFunctions::checkProhibitedFunctions"); |
62 | | |
63 | 1.36k | const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); |
64 | 1.73k | for (const Scope *scope : symbolDatabase->functionScopes) { |
65 | 37.6k | for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { |
66 | 35.9k | if (!Token::Match(tok, "%name% (") && tok->varId() == 0) |
67 | 28.2k | continue; |
68 | | // alloca() is special as it depends on the code being C or C++, so it is not in Library |
69 | 7.68k | if (checkAlloca && Token::simpleMatch(tok, "alloca (") && (!tok->function() || tok->function()->nestedIn->type == Scope::eGlobal)) { |
70 | 0 | if (mTokenizer->isC()) { |
71 | 0 | if (mSettings->standards.c > Standards::C89) |
72 | 0 | reportError(tok, Severity::warning, "allocaCalled", |
73 | 0 | "$symbol:alloca\n" |
74 | 0 | "Obsolete function 'alloca' called. In C99 and later it is recommended to use a variable length array instead.\n" |
75 | 0 | "The obsolete function 'alloca' is called. In C99 and later it is recommended to use a variable length array or " |
76 | 0 | "a dynamically allocated array instead. The function 'alloca' is dangerous for many reasons " |
77 | 0 | "(http://stackoverflow.com/questions/1018853/why-is-alloca-not-considered-good-practice and http://linux.die.net/man/3/alloca)."); |
78 | 0 | } else |
79 | 0 | reportError(tok, Severity::warning, "allocaCalled", |
80 | 0 | "$symbol:alloca\n" |
81 | 0 | "Obsolete function 'alloca' called.\n" |
82 | 0 | "The obsolete function 'alloca' is called. In C++11 and later it is recommended to use std::array<> or " |
83 | 0 | "a dynamically allocated array instead. The function 'alloca' is dangerous for many reasons " |
84 | 0 | "(http://stackoverflow.com/questions/1018853/why-is-alloca-not-considered-good-practice and http://linux.die.net/man/3/alloca)."); |
85 | 7.68k | } else { |
86 | 7.68k | if (tok->function() && tok->function()->hasBody()) |
87 | 0 | continue; |
88 | | |
89 | 7.68k | const Library::WarnInfo* wi = mSettings->library.getWarnInfo(tok); |
90 | 7.68k | if (wi) { |
91 | 0 | if (mSettings->severity.isEnabled(wi->severity) && mSettings->standards.c >= wi->standards.c && mSettings->standards.cpp >= wi->standards.cpp) { |
92 | 0 | const std::string daca = mSettings->daca ? "prohibited" : ""; |
93 | 0 | reportError(tok, wi->severity, daca + tok->str() + "Called", wi->message, CWE477, Certainty::normal); |
94 | 0 | } |
95 | 0 | } |
96 | 7.68k | } |
97 | 7.68k | } |
98 | 1.73k | } |
99 | 1.36k | } |
100 | | |
101 | | //--------------------------------------------------------------------------- |
102 | | // Check <valid>, <strz> and <not-bool> |
103 | | //--------------------------------------------------------------------------- |
104 | | void CheckFunctions::invalidFunctionUsage() |
105 | 1.36k | { |
106 | 1.36k | logChecker("CheckFunctions::invalidFunctionUsage"); |
107 | 1.36k | const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); |
108 | 1.73k | for (const Scope *scope : symbolDatabase->functionScopes) { |
109 | 35.9k | for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { |
110 | 34.1k | if (!Token::Match(tok, "%name% ( !!)")) |
111 | 32.7k | continue; |
112 | 1.46k | const Token * const functionToken = tok; |
113 | 1.46k | const std::vector<const Token *> arguments = getArguments(tok); |
114 | 2.84k | for (int argnr = 1; argnr <= arguments.size(); ++argnr) { |
115 | 1.38k | const Token * const argtok = arguments[argnr-1]; |
116 | | |
117 | | // check <valid>...</valid> |
118 | 1.38k | const ValueFlow::Value *invalidValue = argtok->getInvalidValue(functionToken,argnr,mSettings); |
119 | 1.38k | if (invalidValue) { |
120 | 0 | invalidFunctionArgError(argtok, functionToken->next()->astOperand1()->expressionString(), argnr, invalidValue, mSettings->library.validarg(functionToken, argnr)); |
121 | 0 | } |
122 | | |
123 | 1.38k | if (astIsBool(argtok)) { |
124 | | // check <not-bool> |
125 | 1.03k | if (mSettings->library.isboolargbad(functionToken, argnr)) |
126 | 0 | invalidFunctionArgBoolError(argtok, functionToken->str(), argnr); |
127 | | |
128 | | // Are the values 0 and 1 valid? |
129 | 1.03k | else if (!mSettings->library.isIntArgValid(functionToken, argnr, 0)) |
130 | 0 | invalidFunctionArgError(argtok, functionToken->str(), argnr, nullptr, mSettings->library.validarg(functionToken, argnr)); |
131 | 1.03k | else if (!mSettings->library.isIntArgValid(functionToken, argnr, 1)) |
132 | 0 | invalidFunctionArgError(argtok, functionToken->str(), argnr, nullptr, mSettings->library.validarg(functionToken, argnr)); |
133 | 1.03k | } |
134 | | // check <strz> |
135 | 1.38k | if (mSettings->library.isargstrz(functionToken, argnr)) { |
136 | 0 | if (Token::Match(argtok, "& %var% !![") && argtok->next() && argtok->next()->valueType()) { |
137 | 0 | const ValueType * valueType = argtok->next()->valueType(); |
138 | 0 | const Variable * variable = argtok->next()->variable(); |
139 | 0 | if ((valueType->type == ValueType::Type::CHAR || valueType->type == ValueType::Type::WCHAR_T || (valueType->type == ValueType::Type::RECORD && Token::Match(argtok, "& %var% . %var% ,|)"))) && |
140 | 0 | !variable->isArray() && |
141 | 0 | (variable->isConst() || !variable->isGlobal()) && |
142 | 0 | (!argtok->next()->hasKnownValue() || argtok->next()->getValue(0) == nullptr)) { |
143 | 0 | invalidFunctionArgStrError(argtok, functionToken->str(), argnr); |
144 | 0 | } |
145 | 0 | } |
146 | 0 | const ValueType* const valueType = argtok->valueType(); |
147 | 0 | const Variable* const variable = argtok->variable(); |
148 | | // Is non-null terminated local variable of type char (e.g. char buf[] = {'x'};) ? |
149 | 0 | if (variable && variable->isLocal() |
150 | 0 | && valueType && (valueType->type == ValueType::Type::CHAR || valueType->type == ValueType::Type::WCHAR_T) |
151 | 0 | && !isVariablesChanged(variable->declEndToken(), functionToken, 0 /*indirect*/, { variable }, mSettings, mTokenizer->isCPP())) { |
152 | 0 | const Token* varTok = variable->declEndToken(); |
153 | 0 | auto count = -1; // Find out explicitly set count, e.g.: char buf[3] = {...}. Variable 'count' is set to 3 then. |
154 | 0 | if (varTok && Token::simpleMatch(varTok->astOperand1(), "[")) |
155 | 0 | { |
156 | 0 | const Token* const countTok = varTok->astOperand1()->astOperand2(); |
157 | 0 | if (countTok && countTok->hasKnownIntValue()) |
158 | 0 | count = countTok->getKnownIntValue(); |
159 | 0 | } |
160 | 0 | if (Token::simpleMatch(varTok, "= {")) { |
161 | 0 | varTok = varTok->tokAt(1); |
162 | 0 | auto charsUntilFirstZero = 0; |
163 | 0 | bool search = true; |
164 | 0 | while (search && varTok && !Token::simpleMatch(varTok->next(), "}")) { |
165 | 0 | varTok = varTok->next(); |
166 | 0 | if (!Token::simpleMatch(varTok, ",")) { |
167 | 0 | if (Token::Match(varTok, "%op%")) { |
168 | 0 | varTok = varTok->next(); |
169 | 0 | continue; |
170 | 0 | } |
171 | 0 | ++charsUntilFirstZero; |
172 | 0 | if (varTok && varTok->hasKnownIntValue() && varTok->getKnownIntValue() == 0) |
173 | 0 | search=false; // stop counting for cases like char buf[3] = {'x', '\0', 'y'}; |
174 | 0 | } |
175 | 0 | } |
176 | 0 | if (varTok && varTok->hasKnownIntValue() && varTok->getKnownIntValue() != 0 |
177 | 0 | && (count == -1 || (count > 0 && count <= charsUntilFirstZero))) { |
178 | 0 | invalidFunctionArgStrError(argtok, functionToken->str(), argnr); |
179 | 0 | } |
180 | 0 | } else if (count > -1 && Token::Match(varTok, "= %str%")) { |
181 | 0 | const Token* strTok = varTok->getValueTokenMinStrSize(mSettings); |
182 | 0 | if (strTok) { |
183 | 0 | const int strSize = Token::getStrArraySize(strTok); |
184 | 0 | if (strSize > count && strTok->str().find('\0') == std::string::npos) |
185 | 0 | invalidFunctionArgStrError(argtok, functionToken->str(), argnr); |
186 | 0 | } |
187 | 0 | } |
188 | 0 | } |
189 | 0 | } |
190 | 1.38k | } |
191 | 1.46k | } |
192 | 1.73k | } |
193 | 1.36k | } |
194 | | |
195 | | void CheckFunctions::invalidFunctionArgError(const Token *tok, const std::string &functionName, int argnr, const ValueFlow::Value *invalidValue, const std::string &validstr) |
196 | 0 | { |
197 | 0 | std::ostringstream errmsg; |
198 | 0 | errmsg << "$symbol:" << functionName << '\n'; |
199 | 0 | if (invalidValue && invalidValue->condition) |
200 | 0 | errmsg << ValueFlow::eitherTheConditionIsRedundant(invalidValue->condition) |
201 | 0 | << " or $symbol() argument nr " << argnr << " can have invalid value."; |
202 | 0 | else |
203 | 0 | errmsg << "Invalid $symbol() argument nr " << argnr << '.'; |
204 | 0 | if (invalidValue) |
205 | 0 | errmsg << " The value is " << std::setprecision(10) << (invalidValue->isIntValue() ? invalidValue->intvalue : invalidValue->floatValue) << " but the valid values are '" << validstr << "'."; |
206 | 0 | else |
207 | 0 | errmsg << " The value is 0 or 1 (boolean) but the valid values are '" << validstr << "'."; |
208 | 0 | if (invalidValue) |
209 | 0 | reportError(getErrorPath(tok, invalidValue, "Invalid argument"), |
210 | 0 | invalidValue->errorSeverity() && invalidValue->isKnown() ? Severity::error : Severity::warning, |
211 | 0 | "invalidFunctionArg", |
212 | 0 | errmsg.str(), |
213 | 0 | CWE628, |
214 | 0 | invalidValue->isInconclusive() ? Certainty::inconclusive : Certainty::normal); |
215 | 0 | else |
216 | 0 | reportError(tok, |
217 | 0 | Severity::error, |
218 | 0 | "invalidFunctionArg", |
219 | 0 | errmsg.str(), |
220 | 0 | CWE628, |
221 | 0 | Certainty::normal); |
222 | 0 | } |
223 | | |
224 | | void CheckFunctions::invalidFunctionArgBoolError(const Token *tok, const std::string &functionName, int argnr) |
225 | 0 | { |
226 | 0 | std::ostringstream errmsg; |
227 | 0 | errmsg << "$symbol:" << functionName << '\n'; |
228 | 0 | errmsg << "Invalid $symbol() argument nr " << argnr << ". A non-boolean value is required."; |
229 | 0 | reportError(tok, Severity::error, "invalidFunctionArgBool", errmsg.str(), CWE628, Certainty::normal); |
230 | 0 | } |
231 | | |
232 | | void CheckFunctions::invalidFunctionArgStrError(const Token *tok, const std::string &functionName, nonneg int argnr) |
233 | 0 | { |
234 | 0 | std::ostringstream errmsg; |
235 | 0 | errmsg << "$symbol:" << functionName << '\n'; |
236 | 0 | errmsg << "Invalid $symbol() argument nr " << argnr << ". A nul-terminated string is required."; |
237 | 0 | reportError(tok, Severity::error, "invalidFunctionArgStr", errmsg.str(), CWE628, Certainty::normal); |
238 | 0 | } |
239 | | |
240 | | //--------------------------------------------------------------------------- |
241 | | // Check for ignored return values. |
242 | | //--------------------------------------------------------------------------- |
243 | | void CheckFunctions::checkIgnoredReturnValue() |
244 | 1.36k | { |
245 | 1.36k | if (!mSettings->severity.isEnabled(Severity::warning) && !mSettings->severity.isEnabled(Severity::style)) |
246 | 0 | return; |
247 | | |
248 | 1.36k | logChecker("CheckFunctions::checkIgnoredReturnValue"); // style,warning |
249 | | |
250 | 1.36k | const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); |
251 | 1.73k | for (const Scope *scope : symbolDatabase->functionScopes) { |
252 | 24.0k | for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { |
253 | | // skip c++11 initialization, ({...}) |
254 | 22.3k | if (Token::Match(tok, "%var%|(|,|return {")) |
255 | 0 | tok = tok->linkAt(1); |
256 | 22.3k | else if (Token::Match(tok, "[(<]") && tok->link()) |
257 | 1.66k | tok = tok->link(); |
258 | | |
259 | 22.3k | if (tok->varId() || tok->isKeyword() || tok->isStandardType() || !Token::Match(tok, "%name% (")) |
260 | 22.3k | continue; |
261 | | |
262 | 0 | const Token *parent = tok->next()->astParent(); |
263 | 0 | while (Token::Match(parent, "%cop%")) { |
264 | 0 | if (Token::Match(parent, "<<|>>|*") && !parent->astParent()) |
265 | 0 | break; |
266 | 0 | parent = parent->astParent(); |
267 | 0 | } |
268 | 0 | if (parent) |
269 | 0 | continue; |
270 | | |
271 | 0 | if (!tok->scope()->isExecutable()) { |
272 | 0 | tok = tok->scope()->bodyEnd; |
273 | 0 | continue; |
274 | 0 | } |
275 | | |
276 | 0 | if ((!tok->function() || !Token::Match(tok->function()->retDef, "void %name%")) && |
277 | 0 | tok->next()->astOperand1()) { |
278 | 0 | const Library::UseRetValType retvalTy = mSettings->library.getUseRetValType(tok); |
279 | 0 | const bool warn = (tok->function() && tok->function()->isAttributeNodiscard()) || // avoid duplicate warnings for resource-allocating functions |
280 | 0 | (retvalTy == Library::UseRetValType::DEFAULT && mSettings->library.getAllocFuncInfo(tok) == nullptr); |
281 | 0 | if (mSettings->severity.isEnabled(Severity::warning) && warn) |
282 | 0 | ignoredReturnValueError(tok, tok->next()->astOperand1()->expressionString()); |
283 | 0 | else if (mSettings->severity.isEnabled(Severity::style) && |
284 | 0 | retvalTy == Library::UseRetValType::ERROR_CODE) |
285 | 0 | ignoredReturnErrorCode(tok, tok->next()->astOperand1()->expressionString()); |
286 | 0 | } |
287 | 0 | } |
288 | 1.73k | } |
289 | 1.36k | } |
290 | | |
291 | | void CheckFunctions::ignoredReturnValueError(const Token* tok, const std::string& function) |
292 | 0 | { |
293 | 0 | reportError(tok, Severity::warning, "ignoredReturnValue", |
294 | 0 | "$symbol:" + function + "\nReturn value of function $symbol() is not used.", CWE252, Certainty::normal); |
295 | 0 | } |
296 | | |
297 | | void CheckFunctions::ignoredReturnErrorCode(const Token* tok, const std::string& function) |
298 | 0 | { |
299 | 0 | reportError(tok, Severity::style, "ignoredReturnErrorCode", |
300 | 0 | "$symbol:" + function + "\nError code from the return value of function $symbol() is not used.", CWE252, Certainty::normal); |
301 | 0 | } |
302 | | |
303 | | //--------------------------------------------------------------------------- |
304 | | // Check for ignored return values. |
305 | | //--------------------------------------------------------------------------- |
306 | | static const Token *checkMissingReturnScope(const Token *tok, const Library &library); |
307 | | |
308 | | void CheckFunctions::checkMissingReturn() |
309 | 1.36k | { |
310 | 1.36k | logChecker("CheckFunctions::checkMissingReturn"); |
311 | 1.36k | const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); |
312 | 1.73k | for (const Scope *scope : symbolDatabase->functionScopes) { |
313 | 1.73k | const Function *function = scope->function; |
314 | 1.73k | if (!function || !function->hasBody()) |
315 | 0 | continue; |
316 | 1.73k | if (function->name() == "main" && !(mSettings->standards.c < Standards::C99 && mTokenizer->isC())) |
317 | 0 | continue; |
318 | 1.73k | if (function->type != Function::Type::eFunction && function->type != Function::Type::eOperatorEqual) |
319 | 0 | continue; |
320 | 1.73k | if (Token::Match(function->retDef, "%name% (") && function->retDef->isUpperCaseName()) |
321 | 0 | continue; |
322 | 1.73k | if (Function::returnsVoid(function, true)) |
323 | 0 | continue; |
324 | 1.73k | const Token *errorToken = checkMissingReturnScope(scope->bodyEnd, mSettings->library); |
325 | 1.73k | if (errorToken) |
326 | 0 | missingReturnError(errorToken); |
327 | 1.73k | } |
328 | 1.36k | } |
329 | | |
330 | | static bool isForwardJump(const Token *gotoToken) |
331 | 0 | { |
332 | 0 | if (!Token::Match(gotoToken, "goto %name% ;")) |
333 | 0 | return false; |
334 | 0 | for (const Token *prev = gotoToken; gotoToken; gotoToken = gotoToken->previous()) { |
335 | 0 | if (Token::Match(prev, "%name% :") && prev->str() == gotoToken->next()->str()) |
336 | 0 | return true; |
337 | 0 | if (prev->str() == "{" && prev->scope()->type == Scope::eFunction) |
338 | 0 | return false; |
339 | 0 | } |
340 | 0 | return false; |
341 | 0 | } |
342 | | |
343 | | static const Token *checkMissingReturnScope(const Token *tok, const Library &library) |
344 | 1.73k | { |
345 | 1.73k | const Token *lastStatement = nullptr; |
346 | 5.87k | while ((tok = tok->previous()) != nullptr) { |
347 | 5.87k | if (tok->str() == ")") |
348 | 75 | tok = tok->link(); |
349 | 5.87k | if (tok->str() == "{") |
350 | 0 | return lastStatement ? lastStatement : tok->next(); |
351 | 5.87k | if (tok->str() == "}") { |
352 | 0 | for (const Token *prev = tok->link()->previous(); prev && prev->scope() == tok->scope() && !Token::Match(prev, "[;{}]"); prev = prev->previous()) { |
353 | 0 | if (prev->isKeyword() && Token::Match(prev, "return|throw")) |
354 | 0 | return nullptr; |
355 | 0 | if (prev->str() == "goto" && !isForwardJump(prev)) |
356 | 0 | return nullptr; |
357 | 0 | } |
358 | 0 | if (tok->scope()->type == Scope::ScopeType::eSwitch) { |
359 | | // find reachable break / !default |
360 | 0 | bool hasDefault = false; |
361 | 0 | bool reachable = false; |
362 | 0 | for (const Token *switchToken = tok->link()->next(); switchToken != tok; switchToken = switchToken->next()) { |
363 | 0 | if (reachable && Token::simpleMatch(switchToken, "break ;")) { |
364 | 0 | if (Token::simpleMatch(switchToken->previous(), "}") && !checkMissingReturnScope(switchToken->previous(), library)) |
365 | 0 | reachable = false; |
366 | 0 | else |
367 | 0 | return switchToken; |
368 | 0 | } |
369 | 0 | if (switchToken->isKeyword() && Token::Match(switchToken, "return|throw")) |
370 | 0 | reachable = false; |
371 | 0 | if (Token::Match(switchToken, "%name% (") && library.isnoreturn(switchToken)) |
372 | 0 | reachable = false; |
373 | 0 | if (Token::Match(switchToken, "case|default")) |
374 | 0 | reachable = true; |
375 | 0 | if (Token::simpleMatch(switchToken, "default :")) |
376 | 0 | hasDefault = true; |
377 | 0 | else if (switchToken->str() == "{" && (switchToken->scope()->isLoopScope() || switchToken->scope()->type == Scope::ScopeType::eSwitch)) |
378 | 0 | switchToken = switchToken->link(); |
379 | 0 | } |
380 | 0 | if (!hasDefault) |
381 | 0 | return tok->link(); |
382 | 0 | } else if (tok->scope()->type == Scope::ScopeType::eIf) { |
383 | 0 | const Token *condition = tok->scope()->classDef->next()->astOperand2(); |
384 | 0 | if (condition && condition->hasKnownIntValue() && condition->getKnownIntValue() == 1) |
385 | 0 | return checkMissingReturnScope(tok, library); |
386 | 0 | return tok; |
387 | 0 | } else if (tok->scope()->type == Scope::ScopeType::eElse) { |
388 | 0 | const Token *errorToken = checkMissingReturnScope(tok, library); |
389 | 0 | if (errorToken) |
390 | 0 | return errorToken; |
391 | 0 | tok = tok->link(); |
392 | 0 | if (Token::simpleMatch(tok->tokAt(-2), "} else {")) |
393 | 0 | return checkMissingReturnScope(tok->tokAt(-2), library); |
394 | 0 | return tok; |
395 | 0 | } |
396 | | // FIXME |
397 | 0 | return nullptr; |
398 | 0 | } |
399 | 5.87k | if (tok->isKeyword() && Token::Match(tok, "return|throw")) |
400 | 1.73k | return nullptr; |
401 | 4.13k | if (tok->str() == "goto" && !isForwardJump(tok)) |
402 | 0 | return nullptr; |
403 | 4.13k | if (Token::Match(tok, "%name% (") && !library.isnotnoreturn(tok)) { |
404 | 0 | return nullptr; |
405 | 0 | } |
406 | 4.13k | if (Token::Match(tok, "[;{}] %name% :")) |
407 | 0 | return tok; |
408 | 4.13k | if (Token::Match(tok, "; !!}") && !lastStatement) |
409 | 0 | lastStatement = tok->next(); |
410 | 4.13k | } |
411 | 0 | return nullptr; |
412 | 1.73k | } |
413 | | |
414 | | void CheckFunctions::missingReturnError(const Token* tok) |
415 | 0 | { |
416 | 0 | reportError(tok, Severity::error, "missingReturn", |
417 | 0 | "Found an exit path from function with non-void return type that has missing return statement", CWE758, Certainty::normal); |
418 | 0 | } |
419 | | //--------------------------------------------------------------------------- |
420 | | // Detect passing wrong values to <cmath> functions like atan(0, x); |
421 | | //--------------------------------------------------------------------------- |
422 | | void CheckFunctions::checkMathFunctions() |
423 | 1.36k | { |
424 | 1.36k | const bool styleC99 = mSettings->severity.isEnabled(Severity::style) && mSettings->standards.c != Standards::C89 && mSettings->standards.cpp != Standards::CPP03; |
425 | 1.36k | const bool printWarnings = mSettings->severity.isEnabled(Severity::warning); |
426 | | |
427 | 1.36k | if (!styleC99 && !printWarnings) |
428 | 0 | return; |
429 | | |
430 | 1.36k | logChecker("CheckFunctions::checkMathFunctions"); // style,warning,c99,c++11 |
431 | | |
432 | 1.36k | const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); |
433 | 1.73k | for (const Scope *scope : symbolDatabase->functionScopes) { |
434 | 35.9k | for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { |
435 | 34.1k | if (tok->varId()) |
436 | 6.22k | continue; |
437 | 27.9k | if (printWarnings && Token::Match(tok, "%name% ( !!)")) { |
438 | 1.46k | if (tok->strAt(-1) != "." |
439 | 1.46k | && Token::Match(tok, "log|logf|logl|log10|log10f|log10l|log2|log2f|log2l ( %num% )")) { |
440 | 0 | const std::string& number = tok->strAt(2); |
441 | 0 | if ((MathLib::isInt(number) && MathLib::toLongNumber(number) <= 0) || |
442 | 0 | (MathLib::isFloat(number) && MathLib::toDoubleNumber(number) <= 0.)) |
443 | 0 | mathfunctionCallWarning(tok); |
444 | 1.46k | } else if (Token::Match(tok, "log1p|log1pf|log1pl ( %num% )")) { |
445 | 0 | const std::string& number = tok->strAt(2); |
446 | 0 | if ((MathLib::isInt(number) && MathLib::toLongNumber(number) <= -1) || |
447 | 0 | (MathLib::isFloat(number) && MathLib::toDoubleNumber(number) <= -1.)) |
448 | 0 | mathfunctionCallWarning(tok); |
449 | 0 | } |
450 | | // atan2 ( x , y): x and y can not be zero, because this is mathematically not defined |
451 | 1.46k | else if (Token::Match(tok, "atan2|atan2f|atan2l ( %num% , %num% )")) { |
452 | 0 | if (MathLib::isNullValue(tok->strAt(2)) && MathLib::isNullValue(tok->strAt(4))) |
453 | 0 | mathfunctionCallWarning(tok, 2); |
454 | 0 | } |
455 | | // fmod ( x , y) If y is zero, then either a range error will occur or the function will return zero (implementation-defined). |
456 | 1.46k | else if (Token::Match(tok, "fmod|fmodf|fmodl (")) { |
457 | 0 | const Token* nextArg = tok->tokAt(2)->nextArgument(); |
458 | 0 | if (nextArg && MathLib::isNullValue(nextArg->str())) |
459 | 0 | mathfunctionCallWarning(tok, 2); |
460 | 0 | } |
461 | | // pow ( x , y) If x is zero, and y is negative --> division by zero |
462 | 1.46k | else if (Token::Match(tok, "pow|powf|powl ( %num% , %num% )")) { |
463 | 0 | if (MathLib::isNullValue(tok->strAt(2)) && MathLib::isNegative(tok->strAt(4))) |
464 | 0 | mathfunctionCallWarning(tok, 2); |
465 | 0 | } |
466 | 1.46k | } |
467 | | |
468 | 27.9k | if (styleC99) { |
469 | 27.9k | if (Token::Match(tok, "%num% - erf (") && Tokenizer::isOneNumber(tok->str()) && tok->next()->astOperand2() == tok->tokAt(3)) { |
470 | 0 | mathfunctionCallWarning(tok, "1 - erf(x)", "erfc(x)"); |
471 | 27.9k | } else if (Token::simpleMatch(tok, "exp (") && Token::Match(tok->linkAt(1), ") - %num%") && Tokenizer::isOneNumber(tok->linkAt(1)->strAt(2)) && tok->linkAt(1)->next()->astOperand1() == tok->next()) { |
472 | 0 | mathfunctionCallWarning(tok, "exp(x) - 1", "expm1(x)"); |
473 | 27.9k | } else if (Token::simpleMatch(tok, "log (") && tok->next()->astOperand2()) { |
474 | 0 | const Token* plus = tok->next()->astOperand2(); |
475 | 0 | if (plus->str() == "+" && ((plus->astOperand1() && Tokenizer::isOneNumber(plus->astOperand1()->str())) || (plus->astOperand2() && Tokenizer::isOneNumber(plus->astOperand2()->str())))) |
476 | 0 | mathfunctionCallWarning(tok, "log(1 + x)", "log1p(x)"); |
477 | 0 | } |
478 | 27.9k | } |
479 | 27.9k | } |
480 | 1.73k | } |
481 | 1.36k | } |
482 | | |
483 | | void CheckFunctions::mathfunctionCallWarning(const Token *tok, const nonneg int numParam) |
484 | 0 | { |
485 | 0 | if (tok) { |
486 | 0 | if (numParam == 1) |
487 | 0 | reportError(tok, Severity::warning, "wrongmathcall", "$symbol:" + tok->str() + "\nPassing value " + tok->strAt(2) + " to $symbol() leads to implementation-defined result.", CWE758, Certainty::normal); |
488 | 0 | else if (numParam == 2) |
489 | 0 | reportError(tok, Severity::warning, "wrongmathcall", "$symbol:" + tok->str() + "\nPassing values " + tok->strAt(2) + " and " + tok->strAt(4) + " to $symbol() leads to implementation-defined result.", CWE758, Certainty::normal); |
490 | 0 | } else |
491 | 0 | reportError(tok, Severity::warning, "wrongmathcall", "Passing value '#' to #() leads to implementation-defined result.", CWE758, Certainty::normal); |
492 | 0 | } |
493 | | |
494 | | void CheckFunctions::mathfunctionCallWarning(const Token *tok, const std::string& oldexp, const std::string& newexp) |
495 | 0 | { |
496 | 0 | reportError(tok, Severity::style, "unpreciseMathCall", "Expression '" + oldexp + "' can be replaced by '" + newexp + "' to avoid loss of precision.", CWE758, Certainty::normal); |
497 | 0 | } |
498 | | |
499 | | //--------------------------------------------------------------------------- |
500 | | // memset(p, y, 0 /* bytes to fill */) <- 2nd and 3rd arguments inverted |
501 | | //--------------------------------------------------------------------------- |
502 | | void CheckFunctions::memsetZeroBytes() |
503 | 1.36k | { |
504 | | // FIXME: |
505 | | // Replace this with library configuration. |
506 | | // For instance: |
507 | | // <arg nr="3"> |
508 | | // <warn knownIntValue="0" severity="warning" msg="..."/> |
509 | | // </arg> |
510 | | |
511 | 1.36k | if (!mSettings->severity.isEnabled(Severity::warning)) |
512 | 0 | return; |
513 | | |
514 | 1.36k | logChecker("CheckFunctions::memsetZeroBytes"); // warning |
515 | | |
516 | 1.36k | const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); |
517 | 1.73k | for (const Scope *scope : symbolDatabase->functionScopes) { |
518 | 35.9k | for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { |
519 | 34.1k | if (Token::Match(tok, "memset|wmemset (") && (numberOfArguments(tok)==3)) { |
520 | 0 | const std::vector<const Token *> &arguments = getArguments(tok); |
521 | 0 | if (WRONG_DATA(arguments.size() != 3U, tok)) |
522 | 0 | continue; |
523 | 0 | const Token* lastParamTok = arguments[2]; |
524 | 0 | if (MathLib::isNullValue(lastParamTok->str())) |
525 | 0 | memsetZeroBytesError(tok); |
526 | 0 | } |
527 | 34.1k | } |
528 | 1.73k | } |
529 | 1.36k | } |
530 | | |
531 | | void CheckFunctions::memsetZeroBytesError(const Token *tok) |
532 | 0 | { |
533 | 0 | const std::string summary("memset() called to fill 0 bytes."); |
534 | 0 | const std::string verbose(summary + " The second and third arguments might be inverted." |
535 | 0 | " The function memset ( void * ptr, int value, size_t num ) sets the" |
536 | 0 | " first num bytes of the block of memory pointed by ptr to the specified value."); |
537 | 0 | reportError(tok, Severity::warning, "memsetZeroBytes", summary + "\n" + verbose, CWE687, Certainty::normal); |
538 | 0 | } |
539 | | |
540 | | void CheckFunctions::memsetInvalid2ndParam() |
541 | 1.36k | { |
542 | | // FIXME: |
543 | | // Replace this with library configuration. |
544 | | // For instance: |
545 | | // <arg nr="2"> |
546 | | // <not-float/> |
547 | | // <warn possibleIntValue=":-129,256:" severity="warning" msg="..."/> |
548 | | // </arg> |
549 | | |
550 | 1.36k | const bool printPortability = mSettings->severity.isEnabled(Severity::portability); |
551 | 1.36k | const bool printWarning = mSettings->severity.isEnabled(Severity::warning); |
552 | 1.36k | if (!printWarning && !printPortability) |
553 | 0 | return; |
554 | | |
555 | 1.36k | logChecker("CheckFunctions::memsetInvalid2ndParam"); // warning,portability |
556 | | |
557 | 1.36k | const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); |
558 | 1.73k | for (const Scope *scope : symbolDatabase->functionScopes) { |
559 | 35.9k | for (const Token* tok = scope->bodyStart->next(); tok && (tok != scope->bodyEnd); tok = tok->next()) { |
560 | 34.1k | if (!Token::simpleMatch(tok, "memset (")) |
561 | 34.1k | continue; |
562 | | |
563 | 0 | const std::vector<const Token *> args = getArguments(tok); |
564 | 0 | if (args.size() != 3) |
565 | 0 | continue; |
566 | | |
567 | | // Second parameter is zero literal, i.e. 0.0f |
568 | 0 | const Token * const secondParamTok = args[1]; |
569 | 0 | if (Token::Match(secondParamTok, "%num% ,") && MathLib::isNullValue(secondParamTok->str())) |
570 | 0 | continue; |
571 | | |
572 | | // Check if second parameter is a float variable or a float literal != 0.0f |
573 | 0 | if (printPortability && astIsFloat(secondParamTok,false)) { |
574 | 0 | memsetFloatError(secondParamTok, secondParamTok->expressionString()); |
575 | 0 | } |
576 | |
|
577 | 0 | if (printWarning && secondParamTok->isNumber()) { // Check if the second parameter is a literal and is out of range |
578 | 0 | const long long int value = MathLib::toLongNumber(secondParamTok->str()); |
579 | 0 | const long long sCharMin = mSettings->platform.signedCharMin(); |
580 | 0 | const long long uCharMax = mSettings->platform.unsignedCharMax(); |
581 | 0 | if (value < sCharMin || value > uCharMax) |
582 | 0 | memsetValueOutOfRangeError(secondParamTok, secondParamTok->str()); |
583 | 0 | } |
584 | 0 | } |
585 | 1.73k | } |
586 | 1.36k | } |
587 | | |
588 | | void CheckFunctions::memsetFloatError(const Token *tok, const std::string &var_value) |
589 | 0 | { |
590 | 0 | const std::string message("The 2nd memset() argument '" + var_value + |
591 | 0 | "' is a float, its representation is implementation defined."); |
592 | 0 | const std::string verbose(message + " memset() is used to set each byte of a block of memory to a specific value and" |
593 | 0 | " the actual representation of a floating-point value is implementation defined."); |
594 | 0 | reportError(tok, Severity::portability, "memsetFloat", message + "\n" + verbose, CWE688, Certainty::normal); |
595 | 0 | } |
596 | | |
597 | | void CheckFunctions::memsetValueOutOfRangeError(const Token *tok, const std::string &value) |
598 | 0 | { |
599 | 0 | const std::string message("The 2nd memset() argument '" + value + "' doesn't fit into an 'unsigned char'."); |
600 | 0 | const std::string verbose(message + " The 2nd parameter is passed as an 'int', but the function fills the block of memory using the 'unsigned char' conversion of this value."); |
601 | 0 | reportError(tok, Severity::warning, "memsetValueOutOfRange", message + "\n" + verbose, CWE686, Certainty::normal); |
602 | 0 | } |
603 | | |
604 | | //--------------------------------------------------------------------------- |
605 | | // --check-library => warn for unconfigured functions |
606 | | //--------------------------------------------------------------------------- |
607 | | |
608 | | void CheckFunctions::checkLibraryMatchFunctions() |
609 | 1.36k | { |
610 | 1.36k | if (!mSettings->checkLibrary) |
611 | 1.36k | return; |
612 | | |
613 | 0 | bool insideNew = false; |
614 | 0 | for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { |
615 | 0 | if (!tok->scope() || !tok->scope()->isExecutable()) |
616 | 0 | continue; |
617 | | |
618 | 0 | if (tok->str() == "new") |
619 | 0 | insideNew = true; |
620 | 0 | else if (tok->str() == ";") |
621 | 0 | insideNew = false; |
622 | 0 | else if (insideNew) |
623 | 0 | continue; |
624 | | |
625 | 0 | if (tok->isKeyword() || !Token::Match(tok, "%name% (")) |
626 | 0 | continue; |
627 | | |
628 | 0 | if (tok->varId() != 0 || tok->type() || tok->isStandardType()) |
629 | 0 | continue; |
630 | | |
631 | 0 | if (tok->linkAt(1)->strAt(1) == "(") |
632 | 0 | continue; |
633 | | |
634 | 0 | if (tok->function()) |
635 | 0 | continue; |
636 | | |
637 | 0 | if (Token::simpleMatch(tok->astTop(), "throw")) |
638 | 0 | continue; |
639 | | |
640 | 0 | if (Token::simpleMatch(tok->astParent(), ".")) { |
641 | 0 | const Token* contTok = tok->astParent()->astOperand1(); |
642 | 0 | if (astContainerAction(contTok) != Library::Container::Action::NO_ACTION) |
643 | 0 | continue; |
644 | 0 | if (astContainerYield(contTok) != Library::Container::Yield::NO_YIELD) |
645 | 0 | continue; |
646 | 0 | } |
647 | | |
648 | 0 | if (!mSettings->library.isNotLibraryFunction(tok)) |
649 | 0 | continue; |
650 | | |
651 | 0 | const std::string &functionName = mSettings->library.getFunctionName(tok); |
652 | 0 | if (functionName.empty()) |
653 | 0 | continue; |
654 | | |
655 | 0 | if (mSettings->library.functions.find(functionName) != mSettings->library.functions.end()) |
656 | 0 | continue; |
657 | | |
658 | 0 | if (mSettings->library.podtype(tok->expressionString())) |
659 | 0 | continue; |
660 | | |
661 | 0 | if (mSettings->library.getTypeCheck("unusedvar", functionName) != Library::TypeCheck::def) |
662 | 0 | continue; |
663 | | |
664 | 0 | const Token* start = tok; |
665 | 0 | while (Token::Match(start->tokAt(-2), "%name% ::")) |
666 | 0 | start = start->tokAt(-2); |
667 | 0 | if (mSettings->library.detectContainerOrIterator(start)) |
668 | 0 | continue; |
669 | | |
670 | 0 | reportError(tok, |
671 | 0 | Severity::information, |
672 | 0 | "checkLibraryFunction", |
673 | 0 | "--check-library: There is no matching configuration for function " + functionName + "()"); |
674 | 0 | } |
675 | 0 | } |
676 | | |
677 | | // Check for problems to compiler apply (Named) Return Value Optimization for local variable |
678 | | // Technically we have different guarantees between standard versions |
679 | | // details: https://en.cppreference.com/w/cpp/language/copy_elision |
680 | | void CheckFunctions::returnLocalStdMove() |
681 | 1.36k | { |
682 | 1.36k | if (!mTokenizer->isCPP() || mSettings->standards.cpp < Standards::CPP11) |
683 | 0 | return; |
684 | | |
685 | 1.36k | if (!mSettings->severity.isEnabled(Severity::performance)) |
686 | 0 | return; |
687 | | |
688 | 1.36k | logChecker("CheckFunctions::returnLocalStdMove"); // performance,c++11 |
689 | | |
690 | 1.36k | const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); |
691 | 1.73k | for (const Scope *scope : symbolDatabase->functionScopes) { |
692 | | // Expect return by-value |
693 | 1.73k | if (Function::returnsReference(scope->function, /*unknown*/ true, /*includeRValueRef*/ true)) |
694 | 0 | continue; |
695 | 1.73k | const auto rets = Function::findReturns(scope->function); |
696 | 1.91k | for (const Token* ret : rets) { |
697 | 1.91k | if (!Token::simpleMatch(ret->tokAt(-3), "std :: move (")) |
698 | 1.91k | continue; |
699 | 0 | const Token* retval = ret->astOperand2(); |
700 | | // NRVO |
701 | 0 | if (retval->variable() && retval->variable()->isLocal() && !retval->variable()->isVolatile()) |
702 | 0 | copyElisionError(retval); |
703 | | // RVO |
704 | 0 | if (Token::Match(retval, "(|{") && !retval->isCast() && !(retval->valueType() && retval->valueType()->reference != Reference::None)) |
705 | 0 | copyElisionError(retval); |
706 | 0 | } |
707 | 1.73k | } |
708 | 1.36k | } |
709 | | |
710 | | void CheckFunctions::copyElisionError(const Token *tok) |
711 | 0 | { |
712 | 0 | reportError(tok, |
713 | 0 | Severity::performance, |
714 | 0 | "returnStdMoveLocal", |
715 | 0 | "Using std::move for returning object by-value from function will affect copy elision optimization." |
716 | 0 | " More: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rf-return-move-local"); |
717 | 0 | } |
718 | | |
719 | | void CheckFunctions::useStandardLibrary() |
720 | 1.36k | { |
721 | 1.36k | if (!mSettings->severity.isEnabled(Severity::style)) |
722 | 0 | return; |
723 | | |
724 | 1.36k | logChecker("CheckFunctions::useStandardLibrary"); // style |
725 | | |
726 | 4.94k | for (const Scope& scope: mTokenizer->getSymbolDatabase()->scopeList) { |
727 | 4.94k | if (scope.type != Scope::ScopeType::eFor) |
728 | 4.94k | continue; |
729 | | |
730 | 0 | const Token *forToken = scope.classDef; |
731 | | // for ( initToken ; condToken ; stepToken ) |
732 | 0 | const Token* initToken = getInitTok(forToken); |
733 | 0 | if (!initToken) |
734 | 0 | continue; |
735 | 0 | const Token* condToken = getCondTok(forToken); |
736 | 0 | if (!condToken) |
737 | 0 | continue; |
738 | 0 | const Token* stepToken = getStepTok(forToken); |
739 | 0 | if (!stepToken) |
740 | 0 | continue; |
741 | | |
742 | | // 1. we expect that idx variable will be initialized with 0 |
743 | 0 | const Token* idxToken = initToken->astOperand1(); |
744 | 0 | const Token* initVal = initToken->astOperand2(); |
745 | 0 | if (!idxToken || !initVal || !initVal->hasKnownIntValue() || initVal->getKnownIntValue() != 0) |
746 | 0 | continue; |
747 | 0 | const auto idxVarId = idxToken->varId(); |
748 | 0 | if (0 == idxVarId) |
749 | 0 | continue; |
750 | | |
751 | | // 2. we expect that idx will be less of some variable |
752 | 0 | if (!condToken->isComparisonOp()) |
753 | 0 | continue; |
754 | | |
755 | 0 | const auto& secondOp = condToken->str(); |
756 | 0 | const bool isLess = "<" == secondOp && |
757 | 0 | isConstExpression(condToken->astOperand2(), mSettings->library, mTokenizer->isCPP()) && |
758 | 0 | condToken->astOperand1()->varId() == idxVarId; |
759 | 0 | const bool isMore = ">" == secondOp && |
760 | 0 | isConstExpression(condToken->astOperand1(), mSettings->library, mTokenizer->isCPP()) && |
761 | 0 | condToken->astOperand2()->varId() == idxVarId; |
762 | |
|
763 | 0 | if (!(isLess || isMore)) |
764 | 0 | continue; |
765 | | |
766 | | // 3. we expect idx incrementing by 1 |
767 | 0 | const bool inc = stepToken->str() == "++" && stepToken->astOperand1()->varId() == idxVarId; |
768 | 0 | const bool plusOne = stepToken->isBinaryOp() && stepToken->str() == "+=" && |
769 | 0 | stepToken->astOperand1()->varId() == idxVarId && |
770 | 0 | stepToken->astOperand2()->str() == "1"; |
771 | 0 | if (!inc && !plusOne) |
772 | 0 | continue; |
773 | | |
774 | | // technically using void* here is not correct but some compilers could allow it |
775 | | |
776 | 0 | const Token *tok = scope.bodyStart; |
777 | 0 | const std::string memcpyName = mTokenizer->isCPP() ? "std::memcpy" : "memcpy"; |
778 | | // (reinterpret_cast<uint8_t*>(dest))[i] = (reinterpret_cast<const uint8_t*>(src))[i]; |
779 | 0 | if (Token::Match(tok, "{ (| reinterpret_cast < uint8_t|int8_t|char|void * > ( %var% ) )| [ %varid% ] = " |
780 | 0 | "(| reinterpret_cast < const| uint8_t|int8_t|char|void * > ( %var% ) )| [ %varid% ] ; }", idxVarId)) { |
781 | 0 | useStandardLibraryError(tok->next(), memcpyName); |
782 | 0 | continue; |
783 | 0 | } |
784 | | |
785 | | // ((char*)dst)[i] = ((const char*)src)[i]; |
786 | 0 | if (Token::Match(tok, "{ ( ( uint8_t|int8_t|char|void * ) (| %var% ) )| [ %varid% ] = " |
787 | 0 | "( ( const| uint8_t|int8_t|char|void * ) (| %var% ) )| [ %varid% ] ; }", idxVarId)) { |
788 | 0 | useStandardLibraryError(tok->next(), memcpyName); |
789 | 0 | continue; |
790 | 0 | } |
791 | | |
792 | | |
793 | 0 | const static std::string memsetName = mTokenizer->isCPP() ? "std::memset" : "memset"; |
794 | | // ((char*)dst)[i] = 0; |
795 | 0 | if (Token::Match(tok, "{ ( ( uint8_t|int8_t|char|void * ) (| %var% ) )| [ %varid% ] = %char%|%num% ; }", idxVarId)) { |
796 | 0 | useStandardLibraryError(tok->next(), memsetName); |
797 | 0 | continue; |
798 | 0 | } |
799 | | |
800 | | // ((char*)dst)[i] = (const char*)0; |
801 | 0 | if (Token::Match(tok, "{ ( ( uint8_t|int8_t|char|void * ) (| %var% ) )| [ %varid% ] = " |
802 | 0 | "( const| uint8_t|int8_t|char ) (| %char%|%num% )| ; }", idxVarId)) { |
803 | 0 | useStandardLibraryError(tok->next(), memsetName); |
804 | 0 | continue; |
805 | 0 | } |
806 | | |
807 | | // (reinterpret_cast<uint8_t*>(dest))[i] = static_cast<const uint8_t>(0); |
808 | 0 | if (Token::Match(tok, "{ (| reinterpret_cast < uint8_t|int8_t|char|void * > ( %var% ) )| [ %varid% ] = " |
809 | 0 | "(| static_cast < const| uint8_t|int8_t|char > ( %char%|%num% ) )| ; }", idxVarId)) { |
810 | 0 | useStandardLibraryError(tok->next(), memsetName); |
811 | 0 | continue; |
812 | 0 | } |
813 | | |
814 | | // (reinterpret_cast<int8_t*>(dest))[i] = 0; |
815 | 0 | if (Token::Match(tok, "{ (| reinterpret_cast < uint8_t|int8_t|char|void * > ( %var% ) )| [ %varid% ] = " |
816 | 0 | "%char%|%num% ; }", idxVarId)) { |
817 | 0 | useStandardLibraryError(tok->next(), memsetName); |
818 | 0 | continue; |
819 | 0 | } |
820 | 0 | } |
821 | 1.36k | } |
822 | | |
823 | | void CheckFunctions::useStandardLibraryError(const Token *tok, const std::string& expected) |
824 | 0 | { |
825 | 0 | reportError(tok, Severity::style, |
826 | 0 | "useStandardLibrary", |
827 | 0 | "Consider using " + expected + " instead of loop."); |
828 | 0 | } |