1 /*
2 * Cppcheck - A tool for static C/C++ code analysis
3 * Copyright (C) 2007-2019 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 // Remove includes from the benchmark run
22 // Included files aren't found anyway
23 //#include "checkother.h"
24 //#include "mathlib.h"
25 //#include "symboldatabase.h"
26
27 //#include <cctype> // std::isupper
28 //#include <cmath> // fabs()
29 //#include <stack>
30 //---------------------------------------------------------------------------
31
32 // Register this check class (by creating a static instance of it)
33 namespace {
34 CheckOther instance;
35 }
36
37 //---------------------------------------------------------------------------
38
checkIncrementBoolean()39 void CheckOther::checkIncrementBoolean()
40 {
41 if (!_settings->isEnabled("style"))
42 return;
43
44 for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
45 if (Token::Match(tok, "%var% ++")) {
46 if (tok->varId()) {
47 const Token *declTok = Token::findmatch(_tokenizer->tokens(), "bool %varid%", tok->varId());
48 if (declTok)
49 incrementBooleanError(tok);
50 }
51 }
52 }
53 }
54
incrementBooleanError(const Token * tok)55 void CheckOther::incrementBooleanError(const Token *tok)
56 {
57 reportError(
58 tok,
59 Severity::style,
60 "incrementboolean",
61 "The use of a variable of type bool with the ++ postfix operator is always true and deprecated by the C++ Standard.\n"
62 "The operand of a postfix increment operator may be of type bool but it is deprecated by C++ Standard (Annex D-1) and the operand is always set to true\n"
63 );
64 }
65
66 //---------------------------------------------------------------------------
67
68
clarifyCalculation()69 void CheckOther::clarifyCalculation()
70 {
71 if (!_settings->isEnabled("style"))
72 return;
73 for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
74 if (tok->strAt(1) == "?") {
75 // condition
76 const Token *cond = tok;
77 if (cond->isName() || cond->isNumber())
78 cond = cond->previous();
79 else if (cond->str() == ")")
80 cond = cond->link()->previous();
81 else
82 continue;
83
84 // calculation
85 if (!cond->isArithmeticalOp())
86 continue;
87
88 const std::string &op = cond->str();
89 cond = cond->previous();
90
91 // skip previous multiplications..
92 while (cond && cond->strAt(-1) == "*" && (cond->isName() || cond->isNumber()))
93 cond = cond->tokAt(-2);
94
95 if (!cond)
96 continue;
97
98 // first multiplication operand
99 if (cond->str() == ")") {
100 clarifyCalculationError(cond, op);
101 } else if (cond->isName() || cond->isNumber()) {
102 if (Token::Match(cond->previous(),("return|=|+|-|,|(|"+op).c_str()))
103 clarifyCalculationError(cond, op);
104 }
105 }
106 }
107 }
108
clarifyCalculationError(const Token * tok,const std::string & op)109 void CheckOther::clarifyCalculationError(const Token *tok, const std::string &op)
110 {
111 // suspicious calculation
112 const std::string calc("'a" + op + "b?c:d'");
113
114 // recommended calculation #1
115 const std::string s1("'(a" + op + "b)?c:d'");
116
117 // recommended calculation #2
118 const std::string s2("'a" + op + "(b?c:d)'");
119
120 reportError(tok,
121 Severity::style,
122 "clarifyCalculation",
123 "Clarify calculation precedence for " + op + " and ?\n"
124 "Suspicious calculation. Please use parentheses to clarify the code. "
125 "The code " + calc + " should be written as either " + s1 + " or " + s2 + ".");
126 }
127
128
129 // Clarify condition '(x = a < 0)' into '((x = a) < 0)' or '(x = (a < 0))'
clarifyCondition()130 void CheckOther::clarifyCondition()
131 {
132 if (!_settings->isEnabled("style"))
133 return;
134 for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
135 if (Token::Match(tok, "( %var% =")) {
136 for (const Token *tok2 = tok->tokAt(2); tok2; tok2 = tok2->next()) {
137 if (tok2->str() == "(" || tok2->str() == "[")
138 tok2 = tok2->link();
139 else if (Token::Match(tok2, "&&|%oror%|?|)"))
140 break;
141 else if (Token::Match(tok2, "<|<=|==|!=|>|>= %num% )")) {
142 clarifyConditionError(tok);
143 break;
144 }
145 }
146 }
147 }
148 }
149
clarifyConditionError(const Token * tok)150 void CheckOther::clarifyConditionError(const Token *tok)
151 {
152 reportError(tok,
153 Severity::style,
154 "clarifyCondition",
155 "Suspicious condition (assignment+comparison), it can be clarified with parentheses");
156 }
157
158
159
warningOldStylePointerCast()160 void CheckOther::warningOldStylePointerCast()
161 {
162 if (!_settings->isEnabled("style") ||
163 (_tokenizer->tokens() && _tokenizer->fileLine(_tokenizer->tokens()).find(".cpp") == std::string::npos))
164 return;
165
166 for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
167 // Old style pointer casting..
168 if (!Token::Match(tok, "( const| %type% * ) %var%") &&
169 !Token::Match(tok, "( const| %type% * ) (| new"))
170 continue;
171
172 int addToIndex = 0;
173 if (tok->tokAt(1)->str() == "const")
174 addToIndex = 1;
175
176 if (tok->tokAt(4 + addToIndex)->str() == "const")
177 continue;
178
179 // Is "type" a class?
180 const std::string pattern("class " + tok->tokAt(1 + addToIndex)->str());
181 if (!Token::findmatch(_tokenizer->tokens(), pattern.c_str()))
182 continue;
183
184 cstyleCastError(tok);
185 }
186 }
187
188 //---------------------------------------------------------------------------
189 // fflush(stdin) <- fflush only applies to output streams in ANSI C
190 //---------------------------------------------------------------------------
checkFflushOnInputStream()191 void CheckOther::checkFflushOnInputStream()
192 {
193 const Token *tok = _tokenizer->tokens();
194 while (tok && ((tok = Token::findsimplematch(tok, "fflush ( stdin )")) != NULL)) {
195 fflushOnInputStreamError(tok, tok->strAt(2));
196 tok = tok->tokAt(4);
197 }
198 }
199
checkSizeofForNumericParameter()200 void CheckOther::checkSizeofForNumericParameter()
201 {
202 for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
203 if (Token::Match(tok, "sizeof ( %num% )")
204 || Token::Match(tok, "sizeof ( - %num% )")
205 || Token::Match(tok, "sizeof %num%")
206 || Token::Match(tok, "sizeof - %num%")
207 ) {
208 sizeofForNumericParameterError(tok);
209 }
210 }
211 }
212
checkSizeofForArrayParameter()213 void CheckOther::checkSizeofForArrayParameter()
214 {
215 for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
216 if (Token::Match(tok, "sizeof ( %var% )") || Token::Match(tok, "sizeof %var%")) {
217 int tokIdx = 1;
218 if (tok->tokAt(tokIdx)->str() == "(") {
219 ++tokIdx;
220 }
221 if (tok->tokAt(tokIdx)->varId() > 0) {
222 const Token *declTok = Token::findmatch(_tokenizer->tokens(), "%varid%", tok->tokAt(tokIdx)->varId());
223 if (declTok) {
224 if (Token::simpleMatch(declTok->next(), "[")) {
225 declTok = declTok->next()->link();
226 // multidimensional array
227 while (Token::simpleMatch(declTok->next(), "[")) {
228 declTok = declTok->next()->link();
229 }
230 if (!(Token::Match(declTok->next(), "= %str%")) && !(Token::simpleMatch(declTok->next(), "= {")) && !(Token::simpleMatch(declTok->next(), ";"))) {
231 if (Token::simpleMatch(declTok->next(), ",")) {
232 declTok = declTok->next();
233 while (!Token::simpleMatch(declTok, ";")) {
234 if (Token::simpleMatch(declTok, ")")) {
235 sizeofForArrayParameterError(tok);
236 break;
237 }
238 if (Token::Match(declTok, "(|[|{")) {
239 declTok = declTok->link();
240 }
241 declTok = declTok->next();
242 }
243 }
244 }
245 if (Token::simpleMatch(declTok->next(), ")")) {
246 sizeofForArrayParameterError(tok);
247 }
248 }
249 }
250 }
251 }
252 }
253 }
254
255
256
257 //---------------------------------------------------------------------------
258 // switch (x)
259 // {
260 // case 2:
261 // y = a; // <- this assignment is redundant
262 // case 3:
263 // y = b; // <- case 2 falls through and sets y twice
264 // }
265 //---------------------------------------------------------------------------
checkRedundantAssignmentInSwitch()266 void CheckOther::checkRedundantAssignmentInSwitch()
267 {
268 const char switchPattern[] = "switch ( %any% ) { case";
269 const char breakPattern[] = "break|continue|return|exit|goto|throw";
270 const char functionPattern[] = "%var% (";
271
272 // Find the beginning of a switch. E.g.:
273 // switch (var) { ...
274 const Token *tok = Token::findmatch(_tokenizer->tokens(), switchPattern);
275 while (tok) {
276
277 // Check the contents of the switch statement
278 std::map<unsigned int, const Token*> varsAssigned;
279 int indentLevel = 0;
280 for (const Token *tok2 = tok->tokAt(5); tok2; tok2 = tok2->next()) {
281 if (tok2->str() == "{") {
282 // Inside a conditional or loop. Don't mark variable accesses as being redundant. E.g.:
283 // case 3: b = 1;
284 // case 4: if (a) { b = 2; } // Doesn't make the b=1 redundant because it's conditional
285 if (Token::Match(tok2->previous(), ")|else {") && tok2->link()) {
286 const Token* endOfConditional = tok2->link();
287 for (const Token* tok3 = tok2; tok3 != endOfConditional; tok3 = tok3->next()) {
288 if (tok3->varId() != 0)
289 varsAssigned.erase(tok3->varId());
290 else if (Token::Match(tok3, functionPattern) || Token::Match(tok3, breakPattern))
291 varsAssigned.clear();
292 }
293 tok2 = endOfConditional;
294 } else
295 ++indentLevel;
296 } else if (tok2->str() == "}") {
297 --indentLevel;
298
299 // End of the switch block
300 if (indentLevel < 0)
301 break;
302 }
303
304 // Variable assignment. Report an error if it's assigned to twice before a break. E.g.:
305 // case 3: b = 1; // <== redundant
306 // case 4: b = 2;
307 if (Token::Match(tok2->previous(), ";|{|}|: %var% = %any% ;") && tok2->varId() != 0) {
308 std::map<unsigned int, const Token*>::iterator i = varsAssigned.find(tok2->varId());
309 if (i == varsAssigned.end())
310 varsAssigned[tok2->varId()] = tok2;
311 else
312 redundantAssignmentInSwitchError(i->second, i->second->str());
313 }
314 // Not a simple assignment so there may be good reason if this variable is assigned to twice. E.g.:
315 // case 3: b = 1;
316 // case 4: b++;
317 else if (tok2->varId() != 0)
318 varsAssigned.erase(tok2->varId());
319
320 // Reset our record of assignments if there is a break or function call. E.g.:
321 // case 3: b = 1; break;
322 if (Token::Match(tok2, functionPattern) || Token::Match(tok2, breakPattern))
323 varsAssigned.clear();
324
325 }
326
327 tok = Token::findmatch(tok->next(), switchPattern);
328 }
329 }
330
331
checkSwitchCaseFallThrough()332 void CheckOther::checkSwitchCaseFallThrough()
333 {
334 if (!(_settings->isEnabled("style") && _settings->experimental))
335 return;
336
337 const char switchPattern[] = "switch (";
338 const char breakPattern[] = "break|continue|return|exit|goto|throw";
339
340 // Find the beginning of a switch. E.g.:
341 // switch (var) { ...
342 const Token *tok = Token::findmatch(_tokenizer->tokens(), switchPattern);
343 while (tok) {
344
345 // Check the contents of the switch statement
346 std::stack<std::pair<Token *, bool>> ifnest;
347 std::stack<Token *> loopnest;
348 std::stack<Token *> scopenest;
349 bool justbreak = true;
350 bool firstcase = true;
351 for (const Token *tok2 = tok->tokAt(1)->link()->tokAt(2); tok2; tok2 = tok2->next()) {
352 if (Token::simpleMatch(tok2, "if (")) {
353 tok2 = tok2->tokAt(1)->link()->next();
354 if (tok2->link() == NULL) {
355 std::ostringstream errmsg;
356 errmsg << "unmatched if in switch: " << tok2->linenr();
357 reportError(_tokenizer->tokens(), Severity::debug, "debug", errmsg.str());
358 break;
359 }
360 ifnest.push(std::make_pair(tok2->link(), false));
361 justbreak = false;
362 } else if (Token::simpleMatch(tok2, "while (")) {
363 tok2 = tok2->tokAt(1)->link()->next();
364 // skip over "do { } while ( ) ;" case
365 if (tok2->str() == "{") {
366 if (tok2->link() == NULL) {
367 std::ostringstream errmsg;
368 errmsg << "unmatched while in switch: " << tok2->linenr();
369 reportError(_tokenizer->tokens(), Severity::debug, "debug", errmsg.str());
370 break;
371 }
372 loopnest.push(tok2->link());
373 }
374 justbreak = false;
375 } else if (Token::simpleMatch(tok2, "do {")) {
376 tok2 = tok2->tokAt(1);
377 if (tok2->link() == NULL) {
378 std::ostringstream errmsg;
379 errmsg << "unmatched do in switch: " << tok2->linenr();
380 reportError(_tokenizer->tokens(), Severity::debug, "debug", errmsg.str());
381 break;
382 }
383 loopnest.push(tok2->link());
384 justbreak = false;
385 } else if (Token::simpleMatch(tok2, "for (")) {
386 tok2 = tok2->tokAt(1)->link()->next();
387 if (tok2->link() == NULL) {
388 std::ostringstream errmsg;
389 errmsg << "unmatched for in switch: " << tok2->linenr();
390 reportError(_tokenizer->tokens(), Severity::debug, "debug", errmsg.str());
391 break;
392 }
393 loopnest.push(tok2->link());
394 justbreak = false;
395 } else if (Token::Match(tok2, switchPattern)) {
396 // skip over nested switch, we'll come to that soon
397 tok2 = tok2->tokAt(1)->link()->next()->link();
398 } else if (Token::Match(tok2, breakPattern)) {
399 if (loopnest.empty()) {
400 justbreak = true;
401 }
402 tok2 = Token::findsimplematch(tok2, ";");
403 } else if (Token::Match(tok2, "case|default")) {
404 if (!justbreak && !firstcase) {
405 switchCaseFallThrough(tok2);
406 }
407 tok2 = Token::findsimplematch(tok2, ":");
408 justbreak = true;
409 firstcase = false;
410 } else if (tok2->str() == "{") {
411 scopenest.push(tok2->link());
412 } else if (tok2->str() == "}") {
413 if (!ifnest.empty() && tok2 == ifnest.top().first) {
414 if (tok2->next()->str() == "else") {
415 tok2 = tok2->tokAt(2);
416 ifnest.pop();
417 if (tok2->link() == NULL) {
418 std::ostringstream errmsg;
419 errmsg << "unmatched if in switch: " << tok2->linenr();
420 reportError(_tokenizer->tokens(), Severity::debug, "debug", errmsg.str());
421 break;
422 }
423 ifnest.push(std::make_pair(tok2->link(), justbreak));
424 justbreak = false;
425 } else {
426 justbreak &= ifnest.top().second;
427 ifnest.pop();
428 }
429 } else if (!loopnest.empty() && tok2 == loopnest.top()) {
430 loopnest.pop();
431 } else if (!scopenest.empty() && tok2 == scopenest.top()) {
432 scopenest.pop();
433 } else {
434 if (!ifnest.empty() || !loopnest.empty() || !scopenest.empty()) {
435 std::ostringstream errmsg;
436 errmsg << "unexpected end of switch: ";
437 errmsg << "ifnest=" << ifnest.size();
438 if (!ifnest.empty())
439 errmsg << "," << ifnest.top().first->linenr();
440 errmsg << ", loopnest=" << loopnest.size();
441 if (!loopnest.empty())
442 errmsg << "," << loopnest.top()->linenr();
443 errmsg << ", scopenest=" << scopenest.size();
444 if (!scopenest.empty())
445 errmsg << "," << scopenest.top()->linenr();
446 reportError(_tokenizer->tokens(), Severity::debug, "debug", errmsg.str());
447 }
448 // end of switch block
449 break;
450 }
451 } else if (tok2->str() != ";") {
452 justbreak = false;
453 }
454
455 }
456
457 tok = Token::findmatch(tok->next(), switchPattern);
458 }
459 }
460
461
462 //---------------------------------------------------------------------------
463 // int x = 1;
464 // x = x; // <- redundant assignment to self
465 //
466 // int y = y; // <- redundant initialization to self
467 //---------------------------------------------------------------------------
checkSelfAssignment()468 void CheckOther::checkSelfAssignment()
469 {
470 if (!_settings->isEnabled("style"))
471 return;
472
473 // POD variables..
474 std::set<unsigned int> pod;
475 for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
476 if (tok->isStandardType() && Token::Match(tok->tokAt(2), "[,);]") && tok->next()->varId())
477 pod.insert(tok->next()->varId());
478 }
479
480 const char selfAssignmentPattern[] = "%var% = %var% ;|=|)";
481 const Token *tok = Token::findmatch(_tokenizer->tokens(), selfAssignmentPattern);
482 while (tok) {
483 if (Token::Match(tok->previous(), "[;{}]") &&
484 tok->varId() && tok->varId() == tok->tokAt(2)->varId() &&
485 pod.find(tok->varId()) != pod.end()) {
486 selfAssignmentError(tok, tok->str());
487 }
488
489 tok = Token::findmatch(tok->next(), selfAssignmentPattern);
490 }
491 }
492
493 //---------------------------------------------------------------------------
494 // int a = 1;
495 // assert(a = 2); // <- assert should not have a side-effect
496 //---------------------------------------------------------------------------
checkAssignmentInAssert()497 void CheckOther::checkAssignmentInAssert()
498 {
499 if (!_settings->isEnabled("style"))
500 return;
501
502 const char assertPattern[] = "assert ( %any%";
503 const Token *tok = Token::findmatch(_tokenizer->tokens(), assertPattern);
504 const Token *endTok = tok ? tok->next()->link() : NULL;
505
506 while (tok && endTok) {
507 const Token* varTok = Token::findmatch(tok->tokAt(2), "%var% --|++|+=|-=|*=|/=|&=|^=|=", endTok);
508 if (varTok) {
509 assignmentInAssertError(tok, varTok->str());
510 } else if (NULL != (varTok = Token::findmatch(tok->tokAt(2), "--|++ %var%", endTok))) {
511 assignmentInAssertError(tok, varTok->strAt(1));
512 }
513
514 tok = Token::findmatch(endTok->next(), assertPattern);
515 endTok = tok ? tok->next()->link() : NULL;
516 }
517 }
518
519 //---------------------------------------------------------------------------
520 // if ((x != 1) || (x != 3)) // <- always true
521 // if ((x == 1) && (x == 3)) // <- always false
522 // if ((x < 1) && (x > 3)) // <- always false
523 // if ((x > 3) || (x < 10)) // <- always true
524 //---------------------------------------------------------------------------
checkIncorrectLogicOperator()525 void CheckOther::checkIncorrectLogicOperator()
526 {
527 if (!_settings->isEnabled("style"))
528 return;
529
530 const char conditionPattern[] = "if|while (";
531 const Token *tok = Token::findmatch(_tokenizer->tokens(), conditionPattern);
532 const Token *endTok = tok ? tok->next()->link() : NULL;
533
534 while (tok && endTok) {
535 // Find a pair of OR'd terms, with or without parentheses
536 // e.g. if (x != 3 || x != 4)
537 const Token *logicTok = NULL, *term1Tok = NULL, *term2Tok = NULL;
538 const Token *op1Tok = NULL, *op2Tok = NULL, *op3Tok = NULL, *nextTok = NULL;
539 if (NULL != (logicTok = Token::findmatch(tok, "( %any% !=|==|<|>|>=|<= %any% ) &&|%oror% ( %any% !=|==|<|>|>=|<= %any% ) %any%", endTok))) {
540 term1Tok = logicTok->next();
541 term2Tok = logicTok->tokAt(7);
542 op1Tok = logicTok->tokAt(2);
543 op2Tok = logicTok->tokAt(5);
544 op3Tok = logicTok->tokAt(8);
545 nextTok = logicTok->tokAt(11);
546 } else if (NULL != (logicTok = Token::findmatch(tok, "%any% !=|==|<|>|>=|<= %any% &&|%oror% %any% !=|==|<|>|>=|<= %any% %any%", endTok))) {
547 term1Tok = logicTok;
548 term2Tok = logicTok->tokAt(4);
549 op1Tok = logicTok->tokAt(1);
550 op2Tok = logicTok->tokAt(3);
551 op3Tok = logicTok->tokAt(5);
552 nextTok = logicTok->tokAt(7);
553 }
554
555 if (logicTok) {
556 // Find the common variable and the two different-valued constants
557 unsigned int variableTested = 0;
558 std::string firstConstant, secondConstant;
559 bool varFirst1, varFirst2;
560 unsigned int varId;
561 if (Token::Match(term1Tok, "%var% %any% %num%")) {
562 varId = term1Tok->varId();
563 if (!varId) {
564 tok = Token::findmatch(endTok->next(), conditionPattern);
565 endTok = tok ? tok->next()->link() : NULL;
566 continue;
567 }
568 varFirst1 = true;
569 firstConstant = term1Tok->tokAt(2)->str();
570 } else if (Token::Match(term1Tok, "%num% %any% %var%")) {
571 varId = term1Tok->tokAt(2)->varId();
572 if (!varId) {
573 tok = Token::findmatch(endTok->next(), conditionPattern);
574 endTok = tok ? tok->next()->link() : NULL;
575 continue;
576 }
577 varFirst1 = false;
578 firstConstant = term1Tok->str();
579 } else {
580 tok = Token::findmatch(endTok->next(), conditionPattern);
581 endTok = tok ? tok->next()->link() : NULL;
582 continue;
583 }
584
585 if (Token::Match(term2Tok, "%var% %any% %num%")) {
586 const unsigned int varId2 = term2Tok->varId();
587 if (!varId2 || varId != varId2) {
588 tok = Token::findmatch(endTok->next(), conditionPattern);
589 endTok = tok ? tok->next()->link() : NULL;
590 continue;
591 }
592 varFirst2 = true;
593 secondConstant = term2Tok->tokAt(2)->str();
594 variableTested = varId;
595 } else if (Token::Match(term2Tok, "%num% %any% %var%")) {
596 const unsigned int varId2 = term1Tok->tokAt(2)->varId();
597 if (!varId2 || varId != varId2) {
598 tok = Token::findmatch(endTok->next(), conditionPattern);
599 endTok = tok ? tok->next()->link() : NULL;
600 continue;
601 }
602 varFirst2 = false;
603 secondConstant = term2Tok->str();
604 variableTested = varId;
605 } else {
606 tok = Token::findmatch(endTok->next(), conditionPattern);
607 endTok = tok ? tok->next()->link() : NULL;
608 continue;
609 }
610
611 if (variableTested == 0 || firstConstant.empty() || secondConstant.empty()) {
612 tok = Token::findmatch(endTok->next(), conditionPattern);
613 endTok = tok ? tok->next()->link() : NULL;
614 continue;
615 }
616
617 enum Position { First, Second, NA };
618 enum Relation { Equal, NotEqual, Less, LessEqual, More, MoreEqual };
619 struct Condition {
620 const char *before;
621 Position position1;
622 const char *op1TokStr;
623 const char *op2TokStr;
624 Position position2;
625 const char *op3TokStr;
626 const char *after;
627 Relation relation;
628 bool state;
629 } conditions[] = {
630 { "!!&&", NA, "!=", "||", NA, "!=", "!!&&", NotEqual, true }, // (x != 1) || (x != 3) <- always true
631 { "(", NA, "==", "&&", NA, "==", ")", NotEqual, false }, // (x == 1) && (x == 3) <- always false
632 { "(", First, "<", "&&", First, ">", ")", LessEqual, false }, // (x < 1) && (x > 3) <- always false
633 { "(", First, ">", "&&", First, "<", ")", MoreEqual, false }, // (x > 3) && (x < 1) <- always false
634 { "(", Second, ">", "&&", First, ">", ")", LessEqual, false }, // (1 > x) && (x > 3) <- always false
635 { "(", First, ">", "&&", Second, ">", ")", MoreEqual, false }, // (x > 3) && (1 > x) <- always false
636 { "(", First, "<", "&&", Second, "<", ")", LessEqual, false }, // (x < 1) && (3 < x) <- always false
637 { "(", Second, "<", "&&", First, "<", ")", MoreEqual, false }, // (3 < x) && (x < 1) <- always false
638 { "(", Second, ">", "&&", Second, "<", ")", LessEqual, false }, // (1 > x) && (3 < x) <- always false
639 { "(", Second, "<", "&&", Second, ">", ")", MoreEqual, false }, // (3 < x) && (1 > x) <- always false
640 { "(", First, ">|>=", "||", First, "<|<=", ")", Less, true }, // (x > 3) || (x < 10) <- always true
641 { "(", First, "<|<=", "||", First, ">|>=", ")", More, true }, // (x < 10) || (x > 3) <- always true
642 { "(", Second, "<|<=", "||", First, "<|<=", ")", Less, true }, // (3 < x) || (x < 10) <- always true
643 { "(", First, "<|<=", "||", Second, "<|<=", ")", More, true }, // (x < 10) || (3 < x) <- always true
644 { "(", First, ">|>=", "||", Second, ">|>=", ")", Less, true }, // (x > 3) || (10 > x) <- always true
645 { "(", Second, ">|>=", "||", First, ">|>=", ")", More, true }, // (10 > x) || (x > 3) <- always true
646 { "(", Second, "<|<=", "||", Second, ">|<=", ")", Less, true }, // (3 < x) || (10 > x) <- always true
647 { "(", Second, ">|>=", "||", Second, "<|<=", ")", More, true }, // (10 > x) || (3 < x) <- always true
648 };
649
650 for (unsigned int i = 0; i < (sizeof(conditions) / sizeof(conditions[0])); i++) {
651 if (!((conditions[i].position1 == NA) || (((conditions[i].position1 == First) && varFirst1) || ((conditions[i].position1 == Second) && !varFirst1))))
652 continue;
653
654 if (!((conditions[i].position2 == NA) || (((conditions[i].position2 == First) && varFirst2) || ((conditions[i].position2 == Second) && !varFirst2))))
655 continue;
656
657 if (!Token::Match(op1Tok, conditions[i].op1TokStr))
658 continue;
659
660 if (!Token::Match(op2Tok, conditions[i].op2TokStr))
661 continue;
662
663 if (!Token::Match(op3Tok, conditions[i].op3TokStr))
664 continue;
665
666 if (!Token::Match(logicTok->previous(), conditions[i].before))
667 continue;
668
669 if (!Token::Match(nextTok, conditions[i].after))
670 continue;
671
672 if ((conditions[i].relation == Equal && MathLib::isEqual(firstConstant, secondConstant)) ||
673 (conditions[i].relation == NotEqual && MathLib::isNotEqual(firstConstant, secondConstant)) ||
674 (conditions[i].relation == Less && MathLib::isLess(firstConstant, secondConstant)) ||
675 (conditions[i].relation == LessEqual && MathLib::isLessEqual(firstConstant, secondConstant)) ||
676 (conditions[i].relation == More && MathLib::isGreater(firstConstant, secondConstant)) ||
677 (conditions[i].relation == MoreEqual && MathLib::isGreaterEqual(firstConstant, secondConstant)))
678 incorrectLogicOperatorError(term1Tok, conditions[i].state);
679 }
680 }
681
682 tok = Token::findmatch(endTok->next(), conditionPattern);
683 endTok = tok ? tok->next()->link() : NULL;
684 }
685 }
686
687 //---------------------------------------------------------------------------
688 // try {} catch (std::exception err) {} <- Should be "std::exception& err"
689 //---------------------------------------------------------------------------
checkCatchExceptionByValue()690 void CheckOther::checkCatchExceptionByValue()
691 {
692 if (!_settings->isEnabled("style"))
693 return;
694
695 const char catchPattern[] = "} catch (";
696 const Token *tok = Token::findmatch(_tokenizer->tokens(), catchPattern);
697 const Token *endTok = tok ? tok->tokAt(2)->link() : NULL;
698
699 while (tok && endTok) {
700 // Find a pass-by-value declaration in the catch(), excluding basic types
701 // e.g. catch (std::exception err)
702 const Token *tokType = Token::findmatch(tok, "%type% %var% )", endTok);
703 if (tokType && !tokType->isStandardType()) {
704 catchExceptionByValueError(tokType);
705 }
706
707 tok = Token::findmatch(endTok->next(), catchPattern);
708 endTok = tok ? tok->tokAt(2)->link() : NULL;
709 }
710 }
711
712 //---------------------------------------------------------------------------
713 // strtol(str, 0, radix) <- radix must be 0 or 2-36
714 //---------------------------------------------------------------------------
715
invalidFunctionUsage()716 void CheckOther::invalidFunctionUsage()
717 {
718 // strtol and strtoul..
719 for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
720 if (!Token::Match(tok, "strtol|strtoul ("))
721 continue;
722
723 // Locate the third parameter of the function call..
724 int param = 1;
725 for (const Token *tok2 = tok->tokAt(2); tok2; tok2 = tok2->next()) {
726 if (tok2->str() == "(")
727 tok2 = tok2->link();
728 else if (tok2->str() == ")")
729 break;
730 else if (tok2->str() == ",") {
731 ++param;
732 if (param == 3) {
733 if (Token::Match(tok2, ", %num% )")) {
734 const MathLib::bigint radix = MathLib::toLongNumber(tok2->next()->str());
735 if (!(radix == 0 || (radix >= 2 && radix <= 36))) {
736 dangerousUsageStrtolError(tok2);
737 }
738 }
739 break;
740 }
741 }
742 }
743 }
744
745 // sprintf|snprintf overlapping data
746 for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
747 // Get variable id of target buffer..
748 unsigned int varid = 0;
749
750 if (Token::Match(tok, "sprintf|snprintf ( %var% ,"))
751 varid = tok->tokAt(2)->varId();
752
753 else if (Token::Match(tok, "sprintf|snprintf ( %var% . %var% ,"))
754 varid = tok->tokAt(4)->varId();
755
756 if (varid == 0)
757 continue;
758
759 // goto ","
760 const Token *tok2 = tok->tokAt(3);
761 while (tok2 && tok2->str() != ",")
762 tok2 = tok2->next();
763 if (!tok2)
764 continue;
765
766 // is any source buffer overlapping the target buffer?
767 int parlevel = 0;
768 while ((tok2 = tok2->next()) != NULL) {
769 if (tok2->str() == "(")
770 ++parlevel;
771 else if (tok2->str() == ")") {
772 --parlevel;
773 if (parlevel < 0)
774 break;
775 } else if (parlevel == 0 && Token::Match(tok2, ", %varid% [,)]", varid)) {
776 sprintfOverlappingDataError(tok2->next(), tok2->next()->str());
777 break;
778 }
779 }
780 }
781 }
782 //---------------------------------------------------------------------------
783
invalidScanf()784 void CheckOther::invalidScanf()
785 {
786 if (!_settings->isEnabled("style"))
787 return;
788 for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
789 const Token *formatToken = 0;
790 if (Token::Match(tok, "scanf|vscanf ( %str% ,"))
791 formatToken = tok->tokAt(2);
792 else if (Token::Match(tok, "fscanf|vfscanf ( %var% , %str% ,"))
793 formatToken = tok->tokAt(4);
794 else
795 continue;
796
797 bool format = false;
798
799 // scan the string backwards, so we don't need to keep states
800 const std::string &formatstr(formatToken->str());
801 for (unsigned int i = 1; i < formatstr.length(); i++) {
802 if (formatstr[i] == '%')
803 format = !format;
804
805 else if (!format)
806 continue;
807
808 else if (std::isdigit(formatstr[i])) {
809 format = false;
810 }
811
812 else if (std::isalpha(formatstr[i])) {
813 invalidScanfError(tok);
814 format = false;
815 }
816 }
817 }
818 }
819
820 //---------------------------------------------------------------------------
821 // if (!x==3) <- Probably meant to be "x!=3"
822 //---------------------------------------------------------------------------
checkComparisonOfBoolWithInt()823 void CheckOther::checkComparisonOfBoolWithInt()
824 {
825 if (!_settings->isEnabled("style"))
826 return;
827
828 for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
829 if (Token::Match(tok, "( ! %var% ==|!= %num% )")) {
830 const Token *numTok = tok->tokAt(4);
831 if (numTok && numTok->str() != "0") {
832 comparisonOfBoolWithIntError(numTok, tok->strAt(2));
833 }
834 } else if (Token::Match(tok, "( %num% ==|!= ! %var% )")) {
835 const Token *numTok = tok->tokAt(1);
836 if (numTok && numTok->str() != "0") {
837 comparisonOfBoolWithIntError(numTok, tok->strAt(4));
838 }
839 }
840 }
841 }
842
843 //---------------------------------------------------------------------------
844 // switch (x)
845 // {
846 // case 2:
847 // y = a;
848 // break;
849 // break; // <- Redundant break
850 // case 3:
851 // y = b;
852 // }
853 //---------------------------------------------------------------------------
checkDuplicateBreak()854 void CheckOther::checkDuplicateBreak()
855 {
856 if (!_settings->isEnabled("style"))
857 return;
858
859 const char breakPattern[] = "break|continue ; break|continue ;";
860
861 // Find consecutive break or continue statements. e.g.:
862 // break; break;
863 const Token *tok = Token::findmatch(_tokenizer->tokens(), breakPattern);
864 while (tok) {
865 duplicateBreakError(tok);
866 tok = Token::findmatch(tok->next(), breakPattern);
867 }
868 }
869
870
sizeofForNumericParameterError(const Token * tok)871 void CheckOther::sizeofForNumericParameterError(const Token *tok)
872 {
873 reportError(tok, Severity::error,
874 "sizeofwithnumericparameter", "Using sizeof with a numeric constant as function "
875 "argument might not be what you intended.\n"
876 "It is unusual to use constant value with sizeof. For example, this code:\n"
877 " int f() {\n"
878 " return sizeof(10);\n"
879 " }\n"
880 " returns 4 (in 32-bit systems) or 8 (in 64-bit systems) instead of 10."
881 );
882 }
883
sizeofForArrayParameterError(const Token * tok)884 void CheckOther::sizeofForArrayParameterError(const Token *tok)
885 {
886 reportError(tok, Severity::error,
887 "sizeofwithsilentarraypointer", "Using sizeof for array given as function argument "
888 "returns the size of pointer.\n"
889 "Giving array as function parameter and then using sizeof-operator for the array "
890 "argument. In this case the sizeof-operator returns the size of pointer (in the "
891 "system). It does not return the size of the whole array in bytes as might be "
892 "expected. For example, this code:\n"
893 " int f(char a[100]) {\n"
894 " return sizeof(a);\n"
895 " }\n"
896 " returns 4 (in 32-bit systems) or 8 (in 64-bit systems) instead of 100 (the "
897 "size of the array in bytes)."
898 );
899 }
900
invalidScanfError(const Token * tok)901 void CheckOther::invalidScanfError(const Token *tok)
902 {
903 reportError(tok, Severity::warning,
904 "invalidscanf", "scanf without field width limits can crash with huge input data\n"
905 "scanf without field width limits can crash with huge input data. To fix this error "
906 "message add a field width specifier:\n"
907 " %s => %20s\n"
908 " %i => %3i\n"
909 "\n"
910 "Sample program that can crash:\n"
911 "\n"
912 "#include <stdio.h>\n"
913 "int main()\n"
914 "{\n"
915 " int a;\n"
916 " scanf(\"%i\", &a);\n"
917 " return 0;\n"
918 "}\n"
919 "\n"
920 "To make it crash:\n"
921 "perl -e 'print \"5\"x2100000' | ./a.out");
922 }
923
924 //---------------------------------------------------------------------------
925 // Check for unsigned divisions
926 //---------------------------------------------------------------------------
927
checkUnsignedDivision()928 void CheckOther::checkUnsignedDivision()
929 {
930 if (!_settings->isEnabled("style"))
931 return;
932
933 // Check for "ivar / uvar" and "uvar / ivar"
934 std::map<unsigned int, char> varsign;
935 for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
936 if (Token::Match(tok, "[{};(,] %type% %var% [;=,)]")) {
937 if (tok->tokAt(1)->isUnsigned())
938 varsign[tok->tokAt(2)->varId()] = 'u';
939 else
940 varsign[tok->tokAt(2)->varId()] = 's';
941 }
942
943 else if (!Token::Match(tok, "[).]") && Token::Match(tok->next(), "%var% / %num%")) {
944 if (tok->strAt(3)[0] == '-') {
945 char sign1 = varsign[tok->tokAt(1)->varId()];
946 if (sign1 == 'u') {
947 udivError(tok->next());
948 }
949 }
950 }
951
952 else if (Token::Match(tok, "(|[|=|%op% %num% / %var%")) {
953 if (tok->strAt(1)[0] == '-') {
954 char sign2 = varsign[tok->tokAt(3)->varId()];
955 if (sign2 == 'u') {
956 udivError(tok->next());
957 }
958 }
959 }
960 }
961 }
962
963 //---------------------------------------------------------------------------
964 // memset(p, y, 0 /* bytes to fill */) <- 2nd and 3rd arguments inverted
965 //---------------------------------------------------------------------------
checkMemsetZeroBytes()966 void CheckOther::checkMemsetZeroBytes()
967 {
968 const Token *tok = _tokenizer->tokens();
969 while (tok && ((tok = Token::findmatch(tok, "memset ( %var% , %num% , 0 )")) != NULL)) {
970 memsetZeroBytesError(tok, tok->strAt(2));
971 tok = tok->tokAt(8);
972 }
973 }
974 //---------------------------------------------------------------------------
975
976
977
978 //---------------------------------------------------------------------------
979 // Usage of function variables
980 //---------------------------------------------------------------------------
981
982 /**
983 * @brief This class is used to capture the control flow within a function.
984 */
985 class ScopeInfo {
986 public:
ScopeInfo()987 ScopeInfo() : _token(NULL), _parent(NULL) {}
ScopeInfo(const Token * token,ScopeInfo * parent_)988 ScopeInfo(const Token *token, ScopeInfo *parent_) : _token(token), _parent(parent_) {}
989 ~ScopeInfo();
990
parent()991 ScopeInfo *parent() {
992 return _parent;
993 }
994 ScopeInfo *addChild(const Token *token);
995 void remove(ScopeInfo *scope);
996
997 private:
998 const Token *_token;
999 ScopeInfo *_parent;
1000 std::list<ScopeInfo *> _children;
1001 };
1002
~ScopeInfo()1003 ScopeInfo::~ScopeInfo()
1004 {
1005 while (!_children.empty()) {
1006 delete *_children.begin();
1007 _children.pop_front();
1008 }
1009 }
1010
addChild(const Token * token)1011 ScopeInfo *ScopeInfo::addChild(const Token *token)
1012 {
1013 ScopeInfo *temp = new ScopeInfo(token, this);
1014
1015 _children.push_back(temp);
1016
1017 return temp;
1018 }
1019
remove(ScopeInfo * scope)1020 void ScopeInfo::remove(ScopeInfo *scope)
1021 {
1022 std::list<ScopeInfo *>::iterator it;
1023
1024 for (it = _children.begin(); it != _children.end(); ++it) {
1025 if (*it == scope) {
1026 delete *it;
1027 _children.erase(it);
1028 break;
1029 }
1030 }
1031 }
1032
1033 /**
1034 * @brief This class is used create a list of variables within a function.
1035 */
1036 class Variables {
1037 public:
1038 enum VariableType { standard, array, pointer, reference, pointerArray, referenceArray, pointerPointer };
1039
1040 /** Store information about variable usage */
1041 class VariableUsage {
1042 public:
VariableUsage(const Token * name=0,VariableType type=standard,ScopeInfo * scope=NULL,bool read=false,bool write=false,bool modified=false,bool allocateMemory=false)1043 VariableUsage(const Token *name = 0,
1044 VariableType type = standard,
1045 ScopeInfo *scope = NULL,
1046 bool read = false,
1047 bool write = false,
1048 bool modified = false,
1049 bool allocateMemory = false) :
1050 _name(name),
1051 _type(type),
1052 _scope(scope),
1053 _read(read),
1054 _write(write),
1055 _modified(modified),
1056 _allocateMemory(allocateMemory) {}
1057
1058 /** variable is used.. set both read+write */
use()1059 void use() {
1060 _read = true;
1061 _write = true;
1062 }
1063
1064 /** is variable unused? */
unused() const1065 bool unused() const {
1066 return (!_read && !_write);
1067 }
1068
1069 const Token *_name;
1070 VariableType _type;
1071 ScopeInfo *_scope;
1072 bool _read;
1073 bool _write;
1074 bool _modified; // read/modify/write
1075 bool _allocateMemory;
1076 std::set<unsigned int> _aliases;
1077 std::set<ScopeInfo *> _assignments;
1078 };
1079
1080 typedef std::map<unsigned int, VariableUsage> VariableMap;
1081
clear()1082 void clear() {
1083 _varUsage.clear();
1084 }
varUsage()1085 VariableMap &varUsage() {
1086 return _varUsage;
1087 }
1088 void addVar(const Token *name, VariableType type, ScopeInfo *scope, bool write_);
1089 void allocateMemory(unsigned int varid);
1090 void read(unsigned int varid);
1091 void readAliases(unsigned int varid);
1092 void readAll(unsigned int varid);
1093 void write(unsigned int varid);
1094 void writeAliases(unsigned int varid);
1095 void writeAll(unsigned int varid);
1096 void use(unsigned int varid);
1097 void modified(unsigned int varid);
1098 VariableUsage *find(unsigned int varid);
1099 void alias(unsigned int varid1, unsigned int varid2, bool replace);
erase(unsigned int varid)1100 void erase(unsigned int varid) {
1101 _varUsage.erase(varid);
1102 }
1103 void eraseAliases(unsigned int varid);
1104 void eraseAll(unsigned int varid);
1105 void clearAliases(unsigned int varid);
1106
1107 private:
1108 VariableMap _varUsage;
1109 };
1110
1111 /**
1112 * Alias the 2 given variables. Either replace the existing aliases if
1113 * they exist or merge them. You would replace an existing alias when this
1114 * assignment is in the same scope as the previous assignment. You might
1115 * merge the aliases when this assignment is in a different scope from the
1116 * previous assignment depending on the relationship of the 2 scopes.
1117 */
alias(unsigned int varid1,unsigned int varid2,bool replace)1118 void Variables::alias(unsigned int varid1, unsigned int varid2, bool replace)
1119 {
1120 VariableUsage *var1 = find(varid1);
1121 VariableUsage *var2 = find(varid2);
1122
1123 // alias to self
1124 if (varid1 == varid2) {
1125 if (var1)
1126 var1->use();
1127 return;
1128 }
1129
1130 std::set<unsigned int>::iterator i;
1131
1132 if (replace) {
1133 // remove var1 from all aliases
1134 for (i = var1->_aliases.begin(); i != var1->_aliases.end(); ++i) {
1135 VariableUsage *temp = find(*i);
1136
1137 if (temp)
1138 temp->_aliases.erase(var1->_name->varId());
1139 }
1140
1141 // remove all aliases from var1
1142 var1->_aliases.clear();
1143 }
1144
1145 // var1 gets all var2s aliases
1146 for (i = var2->_aliases.begin(); i != var2->_aliases.end(); ++i) {
1147 if (*i != varid1)
1148 var1->_aliases.insert(*i);
1149 }
1150
1151 // var2 is an alias of var1
1152 var2->_aliases.insert(varid1);
1153 var1->_aliases.insert(varid2);
1154
1155 if (var2->_type == Variables::pointer)
1156 var2->_read = true;
1157 }
1158
clearAliases(unsigned int varid)1159 void Variables::clearAliases(unsigned int varid)
1160 {
1161 VariableUsage *usage = find(varid);
1162
1163 if (usage) {
1164 // remove usage from all aliases
1165 std::set<unsigned int>::iterator i;
1166
1167 for (i = usage->_aliases.begin(); i != usage->_aliases.end(); ++i) {
1168 VariableUsage *temp = find(*i);
1169
1170 if (temp)
1171 temp->_aliases.erase(usage->_name->varId());
1172 }
1173
1174 // remove all aliases from usage
1175 usage->_aliases.clear();
1176 }
1177 }
1178
eraseAliases(unsigned int varid)1179 void Variables::eraseAliases(unsigned int varid)
1180 {
1181 VariableUsage *usage = find(varid);
1182
1183 if (usage) {
1184 std::set<unsigned int>::iterator aliases;
1185
1186 for (aliases = usage->_aliases.begin(); aliases != usage->_aliases.end(); ++aliases)
1187 erase(*aliases);
1188 }
1189 }
1190
eraseAll(unsigned int varid)1191 void Variables::eraseAll(unsigned int varid)
1192 {
1193 eraseAliases(varid);
1194 erase(varid);
1195 }
1196
addVar(const Token * name,VariableType type,ScopeInfo * scope,bool write_)1197 void Variables::addVar(const Token *name,
1198 VariableType type,
1199 ScopeInfo *scope,
1200 bool write_)
1201 {
1202 if (name->varId() > 0)
1203 _varUsage.insert(std::make_pair(name->varId(), VariableUsage(name, type, scope, false, write_, false)));
1204 }
1205
allocateMemory(unsigned int varid)1206 void Variables::allocateMemory(unsigned int varid)
1207 {
1208 VariableUsage *usage = find(varid);
1209
1210 if (usage)
1211 usage->_allocateMemory = true;
1212 }
1213
read(unsigned int varid)1214 void Variables::read(unsigned int varid)
1215 {
1216 VariableUsage *usage = find(varid);
1217
1218 if (usage)
1219 usage->_read = true;
1220 }
1221
readAliases(unsigned int varid)1222 void Variables::readAliases(unsigned int varid)
1223 {
1224 VariableUsage *usage = find(varid);
1225
1226 if (usage) {
1227 std::set<unsigned int>::iterator aliases;
1228
1229 for (aliases = usage->_aliases.begin(); aliases != usage->_aliases.end(); ++aliases) {
1230 VariableUsage *aliased = find(*aliases);
1231
1232 if (aliased)
1233 aliased->_read = true;
1234 }
1235 }
1236 }
1237
readAll(unsigned int varid)1238 void Variables::readAll(unsigned int varid)
1239 {
1240 VariableUsage *usage = find(varid);
1241
1242 if (usage) {
1243 usage->_read = true;
1244
1245 std::set<unsigned int>::iterator aliases;
1246
1247 for (aliases = usage->_aliases.begin(); aliases != usage->_aliases.end(); ++aliases) {
1248 VariableUsage *aliased = find(*aliases);
1249
1250 if (aliased)
1251 aliased->_read = true;
1252 }
1253 }
1254 }
1255
write(unsigned int varid)1256 void Variables::write(unsigned int varid)
1257 {
1258 VariableUsage *usage = find(varid);
1259
1260 if (usage)
1261 usage->_write = true;
1262 }
1263
writeAliases(unsigned int varid)1264 void Variables::writeAliases(unsigned int varid)
1265 {
1266 VariableUsage *usage = find(varid);
1267
1268 if (usage) {
1269 std::set<unsigned int>::iterator aliases;
1270
1271 for (aliases = usage->_aliases.begin(); aliases != usage->_aliases.end(); ++aliases) {
1272 VariableUsage *aliased = find(*aliases);
1273
1274 if (aliased)
1275 aliased->_write = true;
1276 }
1277 }
1278 }
1279
writeAll(unsigned int varid)1280 void Variables::writeAll(unsigned int varid)
1281 {
1282 VariableUsage *usage = find(varid);
1283
1284 if (usage) {
1285 usage->_write = true;
1286
1287 std::set<unsigned int>::iterator aliases;
1288
1289 for (aliases = usage->_aliases.begin(); aliases != usage->_aliases.end(); ++aliases) {
1290 VariableUsage *aliased = find(*aliases);
1291
1292 if (aliased)
1293 aliased->_write = true;
1294 }
1295 }
1296 }
1297
use(unsigned int varid)1298 void Variables::use(unsigned int varid)
1299 {
1300 VariableUsage *usage = find(varid);
1301
1302 if (usage) {
1303 usage->use();
1304
1305 std::set<unsigned int>::iterator aliases;
1306
1307 for (aliases = usage->_aliases.begin(); aliases != usage->_aliases.end(); ++aliases) {
1308 VariableUsage *aliased = find(*aliases);
1309
1310 if (aliased)
1311 aliased->use();
1312 }
1313 }
1314 }
1315
modified(unsigned int varid)1316 void Variables::modified(unsigned int varid)
1317 {
1318 VariableUsage *usage = find(varid);
1319
1320 if (usage) {
1321 usage->_modified = true;
1322
1323 std::set<unsigned int>::iterator aliases;
1324
1325 for (aliases = usage->_aliases.begin(); aliases != usage->_aliases.end(); ++aliases) {
1326 VariableUsage *aliased = find(*aliases);
1327
1328 if (aliased)
1329 aliased->_modified = true;
1330 }
1331 }
1332 }
1333
find(unsigned int varid)1334 Variables::VariableUsage *Variables::find(unsigned int varid)
1335 {
1336 if (varid) {
1337 VariableMap::iterator i = _varUsage.find(varid);
1338 if (i != _varUsage.end())
1339 return &i->second;
1340 }
1341 return 0;
1342 }
1343
doAssignment(Variables & variables,const Token * tok,bool dereference,ScopeInfo * scope)1344 static int doAssignment(Variables &variables, const Token *tok, bool dereference, ScopeInfo *scope)
1345 {
1346 int next = 0;
1347
1348 // a = a + b;
1349 if (Token::Match(tok, "%var% = %var% !!;") && tok->str() == tok->strAt(2)) {
1350 return 2;
1351 }
1352
1353 // check for aliased variable
1354 const unsigned int varid1 = tok->varId();
1355 Variables::VariableUsage *var1 = variables.find(varid1);
1356
1357 if (var1) {
1358 int start = 1;
1359
1360 // search for '='
1361 while (tok->tokAt(start)->str() != "=")
1362 start++;
1363
1364 start++;
1365
1366 if (Token::Match(tok->tokAt(start), "&| %var%") ||
1367 Token::Match(tok->tokAt(start), "( const| struct|union| %type% *| ) &| %var%") ||
1368 Token::Match(tok->tokAt(start), "( const| struct|union| %type% *| ) ( &| %var%") ||
1369 Token::Match(tok->tokAt(start), "%any% < const| struct|union| %type% *| > ( &| %var%")) {
1370 unsigned char offset = 0;
1371 unsigned int varid2;
1372 bool addressOf = false;
1373
1374 if (Token::Match(tok->tokAt(start), "%var% ."))
1375 variables.use(tok->tokAt(start)->varId()); // use = read + write
1376
1377 // check for C style cast
1378 if (tok->tokAt(start)->str() == "(") {
1379 if (tok->tokAt(start + 1)->str() == "const")
1380 offset++;
1381
1382 if (Token::Match(tok->tokAt(start + 1 + offset), "struct|union"))
1383 offset++;
1384
1385 if (tok->tokAt(start + 2 + offset)->str() == "*")
1386 offset++;
1387
1388 if (tok->tokAt(start + 3 + offset)->str() == "&") {
1389 addressOf = true;
1390 next = start + 4 + offset;
1391 } else if (tok->tokAt(start + 3 + offset)->str() == "(") {
1392 if (tok->tokAt(start + 4 + offset)->str() == "&") {
1393 addressOf = true;
1394 next = start + 5 + offset;
1395 } else
1396 next = start + 4 + offset;
1397 } else
1398 next = start + 3 + offset;
1399 }
1400
1401 // check for C++ style cast
1402 else if (tok->tokAt(start)->str().find("cast") != std::string::npos &&
1403 tok->tokAt(start + 1)->str() == "<") {
1404 if (tok->tokAt(start + 2)->str() == "const")
1405 offset++;
1406
1407 if (Token::Match(tok->tokAt(start + 2 + offset), "struct|union"))
1408 offset++;
1409
1410 if (tok->tokAt(start + 3 + offset)->str() == "*")
1411 offset++;
1412
1413 if (tok->tokAt(start + 5 + offset)->str() == "&") {
1414 addressOf = true;
1415 next = start + 6 + offset;
1416 } else
1417 next = start + 5 + offset;
1418 }
1419
1420 // check for var ? ...
1421 else if (Token::Match(tok->tokAt(start), "%var% ?")) {
1422 next = start;
1423 }
1424
1425 // no cast
1426 else {
1427 if (tok->tokAt(start)->str() == "&") {
1428 addressOf = true;
1429 next = start + 1;
1430 } else if (tok->tokAt(start)->str() == "new")
1431 return 0;
1432 else
1433 next = start;
1434 }
1435
1436 // check if variable is local
1437 varid2 = tok->tokAt(next)->varId();
1438 Variables::VariableUsage *var2 = variables.find(varid2);
1439
1440 if (var2) { // local variable (alias or read it)
1441 if (var1->_type == Variables::pointer) {
1442 if (dereference)
1443 variables.read(varid2);
1444 else {
1445 if (addressOf ||
1446 var2->_type == Variables::array ||
1447 var2->_type == Variables::pointer) {
1448 bool replace = true;
1449
1450 // check if variable declared in same scope
1451 if (scope == var1->_scope)
1452 replace = true;
1453
1454 // not in same scope as declaration
1455 else {
1456 std::set<ScopeInfo *>::iterator assignment;
1457
1458 // check for an assignment in this scope
1459 assignment = var1->_assignments.find(scope);
1460
1461 // no other assignment in this scope
1462 if (assignment == var1->_assignments.end()) {
1463 // nothing to replace
1464 if (var1->_assignments.empty())
1465 replace = false;
1466
1467 // this variable has previous assignments
1468 else {
1469 /**
1470 * @todo determine if existing aliases should be replaced or merged
1471 */
1472
1473 replace = false;
1474 }
1475 }
1476
1477 // assignment in this scope
1478 else {
1479 // replace when only one other assignment
1480 if (var1->_assignments.size() == 1)
1481 replace = true;
1482
1483 // otherwise, merge them
1484 else
1485 replace = false;
1486 }
1487 }
1488
1489 variables.alias(varid1, varid2, replace);
1490 } else if (tok->tokAt(next + 1)->str() == "?") {
1491 if (var2->_type == Variables::reference)
1492 variables.readAliases(varid2);
1493 else
1494 variables.read(varid2);
1495 }
1496 }
1497 } else if (var1->_type == Variables::reference) {
1498 variables.alias(varid1, varid2, true);
1499 } else {
1500 if (var2->_type == Variables::pointer && tok->tokAt(next + 1)->str() == "[")
1501 variables.readAliases(varid2);
1502
1503 variables.read(varid2);
1504 }
1505 } else { // not a local variable (or an unsupported local variable)
1506 if (var1->_type == Variables::pointer && !dereference) {
1507 // check if variable declaration is in this scope
1508 if (var1->_scope == scope)
1509 variables.clearAliases(varid1);
1510 else {
1511 std::set<ScopeInfo *>::iterator assignment;
1512
1513 // check for an assignment in this scope
1514 assignment = var1->_assignments.find(scope);
1515
1516 // no other assignment in this scope
1517 if (assignment == var1->_assignments.end()) {
1518 /**
1519 * @todo determine if existing aliases should be discarded
1520 */
1521 }
1522
1523 // this assignment replaces the last assignment in this scope
1524 else {
1525 // aliased variables in a larger scope are not supported
1526 // remove all aliases
1527 variables.clearAliases(varid1);
1528 }
1529 }
1530 }
1531 }
1532 }
1533
1534 var1->_assignments.insert(scope);
1535 }
1536
1537 // check for alias to struct member
1538 // char c[10]; a.b = c;
1539 else if (Token::Match(tok->tokAt(-2), "%var% .")) {
1540 if (Token::Match(tok->tokAt(2), "%var%")) {
1541 unsigned int varid2 = tok->tokAt(2)->varId();
1542 Variables::VariableUsage *var2 = variables.find(varid2);
1543
1544 // struct member aliased to local variable
1545 if (var2 && (var2->_type == Variables::array ||
1546 var2->_type == Variables::pointer)) {
1547 // erase aliased variable and all variables that alias it
1548 // to prevent false positives
1549 variables.eraseAll(varid2);
1550 }
1551 }
1552 }
1553
1554 return next;
1555 }
1556
nextIsStandardType(const Token * tok)1557 static bool nextIsStandardType(const Token *tok)
1558 {
1559 tok = tok->next();
1560
1561 if (tok->str() == "static")
1562 tok = tok->next();
1563
1564 return tok->isStandardType();
1565 }
1566
nextIsStandardTypeOrVoid(const Token * tok)1567 static bool nextIsStandardTypeOrVoid(const Token *tok)
1568 {
1569 tok = tok->next();
1570
1571 if (tok->str() == "static")
1572 tok = tok->next();
1573
1574 if (tok->str() == "const")
1575 tok = tok->next();
1576
1577 return tok->isStandardType() || tok->str() == "void";
1578 }
1579
isRecordTypeWithoutSideEffects(const Token * tok)1580 bool CheckOther::isRecordTypeWithoutSideEffects(const Token *tok)
1581 {
1582 const Variable * var = _tokenizer->getSymbolDatabase()->getVariableFromVarId(tok->varId());
1583
1584 // a type that has no side effects (no constructors and no members with constructors)
1585 /** @todo false negative: check base class for side effects */
1586 /** @todo false negative: check constructors for side effects */
1587 if (var && var->type() && var->type()->numConstructors == 0 &&
1588 (var->type()->varlist.empty() || var->type()->needInitialization == Scope::True) &&
1589 var->type()->derivedFrom.empty())
1590 return true;
1591
1592 return false;
1593 }
1594
functionVariableUsage()1595 void CheckOther::functionVariableUsage()
1596 {
1597 if (!_settings->isEnabled("style"))
1598 return;
1599
1600 // Parse all executing scopes..
1601 const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();
1602
1603 std::list<Scope>::const_iterator scope;
1604
1605 for (scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) {
1606 // only check functions
1607 if (scope->type != Scope::eFunction)
1608 continue;
1609
1610 // First token for the current scope..
1611 const Token *const tok1 = scope->classStart;
1612
1613 // varId, usage {read, write, modified}
1614 Variables variables;
1615
1616 // scopes
1617 ScopeInfo scopes;
1618 ScopeInfo *info = &scopes;
1619
1620 unsigned int indentlevel = 0;
1621 for (const Token *tok = tok1; tok; tok = tok->next()) {
1622 if (tok->str() == "{") {
1623 // replace the head node when found
1624 if (indentlevel == 0)
1625 scopes = ScopeInfo(tok, NULL);
1626 // add the new scope
1627 else
1628 info = info->addChild(tok);
1629 ++indentlevel;
1630 } else if (tok->str() == "}") {
1631 --indentlevel;
1632
1633 info = info->parent();
1634
1635 if (indentlevel == 0)
1636 break;
1637 } else if (Token::Match(tok, "struct|union|class {") ||
1638 Token::Match(tok, "struct|union|class %type% {|:")) {
1639 while (tok->str() != "{")
1640 tok = tok->next();
1641 tok = tok->link();
1642 if (!tok)
1643 break;
1644 }
1645
1646 if (Token::Match(tok, "[;{}] asm ( ) ;")) {
1647 variables.clear();
1648 break;
1649 }
1650
1651 // standard type declaration with possible initialization
1652 // int i; int j = 0; static int k;
1653 if (Token::Match(tok, "[;{}] static| %type% %var% ;|=") &&
1654 !Token::Match(tok->next(), "return|throw")) {
1655 tok = tok->next();
1656
1657 const bool isStatic = tok->str() == "static";
1658 if (isStatic)
1659 tok = tok->next();
1660
1661 if (tok->isStandardType() || isRecordTypeWithoutSideEffects(tok->next())) {
1662 variables.addVar(tok->next(), Variables::standard, info,
1663 tok->tokAt(2)->str() == "=" || isStatic);
1664 }
1665 tok = tok->next();
1666 }
1667
1668 // standard const type declaration
1669 // const int i = x;
1670 else if (Token::Match(tok, "[;{}] const %type% %var% =")) {
1671 tok = tok->next()->next();
1672
1673 if (tok->isStandardType() || isRecordTypeWithoutSideEffects(tok->next()))
1674 variables.addVar(tok->next(), Variables::standard, info, true);
1675
1676 tok = tok->next();
1677 }
1678
1679 // std::string declaration with possible initialization
1680 // std::string s; std::string s = "string";
1681 else if (Token::Match(tok, "[;{}] static| std :: string %var% ;|=")) {
1682 tok = tok->next();
1683
1684 const bool isStatic = tok->str() == "static";
1685 if (isStatic)
1686 tok = tok->next();
1687
1688 tok = tok->tokAt(3);
1689 variables.addVar(tok, Variables::standard, info,
1690 tok->next()->str() == "=" || isStatic);
1691 }
1692
1693 // standard struct type declaration with possible initialization
1694 // struct S s; struct S s = { 0 }; static struct S s;
1695 else if (Token::Match(tok, "[;{}] static| struct %type% %var% ;|=") &&
1696 (isRecordTypeWithoutSideEffects(tok->strAt(1) == "static" ? tok->tokAt(4) : tok->tokAt(3)))) {
1697 tok = tok->next();
1698
1699 bool isStatic = tok->str() == "static";
1700 if (isStatic)
1701 tok = tok->next();
1702
1703 tok = tok->next();
1704
1705 variables.addVar(tok->next(), Variables::standard, info,
1706 tok->tokAt(2)->str() == "=" || isStatic);
1707 tok = tok->next();
1708 }
1709
1710 // standard type declaration and initialization using constructor
1711 // int i(0); static int j(0);
1712 else if (Token::Match(tok, "[;{}] static| %type% %var% ( %any% ) ;") &&
1713 nextIsStandardType(tok)) {
1714 tok = tok->next();
1715
1716 if (tok->str() == "static")
1717 tok = tok->next();
1718
1719 variables.addVar(tok->next(), Variables::standard, info, true);
1720
1721 // check if a local variable is used to initialize this variable
1722 if (tok->tokAt(3)->varId() > 0)
1723 variables.readAll(tok->tokAt(3)->varId());
1724 tok = tok->tokAt(4);
1725 }
1726
1727 // standard type declaration of array of with possible initialization
1728 // int i[10]; int j[2] = { 0, 1 }; static int k[2] = { 2, 3 };
1729 else if (Token::Match(tok, "[;{}] static| const| %type% *| %var% [ %any% ] ;|=") &&
1730 nextIsStandardType(tok)) {
1731 tok = tok->next();
1732
1733 const bool isStatic = tok->str() == "static";
1734 if (isStatic)
1735 tok = tok->next();
1736
1737 if (tok->str() == "const")
1738 tok = tok->next();
1739
1740 if (tok->str() != "return" && tok->str() != "throw") {
1741 bool isPointer = bool(tok->strAt(1) == "*");
1742 const Token * const nametok = tok->tokAt(isPointer ? 2 : 1);
1743
1744 variables.addVar(nametok, isPointer ? Variables::pointerArray : Variables::array, info,
1745 nametok->tokAt(4)->str() == "=" || isStatic);
1746
1747 // check for reading array size from local variable
1748 if (nametok->tokAt(2)->varId() != 0)
1749 variables.read(nametok->tokAt(2)->varId());
1750
1751 // look at initializers
1752 if (Token::simpleMatch(nametok->tokAt(4), "= {")) {
1753 tok = nametok->tokAt(6);
1754 while (tok->str() != "}") {
1755 if (Token::Match(tok, "%var%"))
1756 variables.read(tok->varId());
1757 tok = tok->next();
1758 }
1759 } else
1760 tok = nametok->tokAt(3);
1761 }
1762 }
1763
1764 // pointer or reference declaration with possible initialization
1765 // int * i; int * j = 0; static int * k = 0;
1766 else if (Token::Match(tok, "[;{}] static| const| %type% *|& %var% ;|=")) {
1767 tok = tok->next();
1768
1769 const bool isStatic = tok->str() == "static";
1770 if (isStatic)
1771 tok = tok->next();
1772
1773 if (tok->str() == "const")
1774 tok = tok->next();
1775
1776 if (tok->strAt(1) == "::")
1777 tok = tok->tokAt(2);
1778
1779 if (tok->str() != "return" && tok->str() != "throw") {
1780 Variables::VariableType type;
1781
1782 if (tok->next()->str() == "*")
1783 type = Variables::pointer;
1784 else
1785 type = Variables::reference;
1786
1787 bool written = tok->tokAt(3)->str() == "=";
1788
1789 variables.addVar(tok->tokAt(2), type, info, written || isStatic);
1790
1791 int offset = 0;
1792
1793 // check for assignment
1794 if (written)
1795 offset = doAssignment(variables, tok->tokAt(2), false, info);
1796
1797 tok = tok->tokAt(2 + offset);
1798 }
1799 }
1800
1801 // pointer to pointer declaration with possible initialization
1802 // int ** i; int ** j = 0; static int ** k = 0;
1803 else if (Token::Match(tok, "[;{}] static| const| %type% * * %var% ;|=")) {
1804 tok = tok->next();
1805
1806 const bool isStatic = tok->str() == "static";
1807 if (isStatic)
1808 tok = tok->next();
1809
1810 if (tok->str() == "const")
1811 tok = tok->next();
1812
1813 if (tok->str() != "return") {
1814 bool written = tok->tokAt(4)->str() == "=";
1815
1816 variables.addVar(tok->tokAt(3), Variables::pointerPointer, info, written || isStatic);
1817
1818 int offset = 0;
1819
1820 // check for assignment
1821 if (written)
1822 offset = doAssignment(variables, tok->tokAt(3), false, info);
1823
1824 tok = tok->tokAt(3 + offset);
1825 }
1826 }
1827
1828 // pointer or reference of struct or union declaration with possible initialization
1829 // struct s * i; struct s * j = 0; static struct s * k = 0;
1830 else if (Token::Match(tok, "[;{}] static| const| struct|union %type% *|& %var% ;|=")) {
1831 Variables::VariableType type;
1832
1833 tok = tok->next();
1834
1835 const bool isStatic = tok->str() == "static";
1836 if (isStatic)
1837 tok = tok->next();
1838
1839 if (tok->str() == "const")
1840 tok = tok->next();
1841
1842 if (tok->strAt(2) == "*")
1843 type = Variables::pointer;
1844 else
1845 type = Variables::reference;
1846
1847 const bool written = tok->strAt(4) == "=";
1848
1849 variables.addVar(tok->tokAt(3), type, info, written || isStatic);
1850
1851 int offset = 0;
1852
1853 // check for assignment
1854 if (written)
1855 offset = doAssignment(variables, tok->tokAt(3), false, info);
1856
1857 tok = tok->tokAt(3 + offset);
1858 }
1859
1860 // pointer or reference declaration with initialization using constructor
1861 // int * i(j); int * k(i); static int * l(i);
1862 else if (Token::Match(tok, "[;{}] static| const| %type% &|* %var% ( %any% ) ;") &&
1863 nextIsStandardTypeOrVoid(tok)) {
1864 Variables::VariableType type;
1865
1866 tok = tok->next();
1867
1868 if (tok->str() == "static")
1869 tok = tok->next();
1870
1871 if (tok->str() == "const")
1872 tok = tok->next();
1873
1874 if (tok->next()->str() == "*")
1875 type = Variables::pointer;
1876 else
1877 type = Variables::reference;
1878
1879 unsigned int varid = 0;
1880
1881 // check for aliased variable
1882 if (Token::Match(tok->tokAt(4), "%var%"))
1883 varid = tok->tokAt(4)->varId();
1884
1885 variables.addVar(tok->tokAt(2), type, info, true);
1886
1887 // check if a local variable is used to initialize this variable
1888 if (varid > 0) {
1889 Variables::VariableUsage *var = variables.find(varid);
1890
1891 if (type == Variables::pointer) {
1892 variables.use(tok->tokAt(4)->varId());
1893
1894 if (var && (var->_type == Variables::array ||
1895 var->_type == Variables::pointer))
1896 var->_aliases.insert(tok->varId());
1897 } else {
1898 variables.readAll(tok->tokAt(4)->varId());
1899 if (var)
1900 var->_aliases.insert(tok->varId());
1901 }
1902 }
1903 tok = tok->tokAt(5);
1904 }
1905
1906 // array of pointer or reference declaration with possible initialization
1907 // int * p[10]; int * q[10] = { 0 }; static int * * r[10] = { 0 };
1908 else if (Token::Match(tok, "[;{}] static| const| %type% *|& %var% [ %any% ] ;|=")) {
1909 tok = tok->next();
1910
1911 const bool isStatic = tok->str() == "static";
1912 if (isStatic)
1913 tok = tok->next();
1914
1915 if (tok->str() == "const")
1916 tok = tok->next();
1917
1918 if (tok->str() != "return") {
1919 variables.addVar(tok->tokAt(2),
1920 tok->next()->str() == "*" ? Variables::pointerArray : Variables::referenceArray, info,
1921 tok->tokAt(6)->str() == "=" || isStatic);
1922
1923 // check for reading array size from local variable
1924 if (tok->tokAt(4)->varId() != 0)
1925 variables.read(tok->tokAt(4)->varId());
1926
1927 tok = tok->tokAt(5);
1928 }
1929 }
1930
1931 // array of pointer or reference of struct or union declaration with possible initialization
1932 // struct S * p[10]; struct T * q[10] = { 0 }; static struct S * r[10] = { 0 };
1933 else if (Token::Match(tok, "[;{}] static| const| struct|union %type% *|& %var% [ %any% ] ;|=")) {
1934 tok = tok->next();
1935
1936 const bool isStatic = tok->str() == "static";
1937 if (isStatic)
1938 tok = tok->next();
1939
1940 if (tok->str() == "const")
1941 tok = tok->next();
1942
1943 variables.addVar(tok->tokAt(3),
1944 tok->tokAt(2)->str() == "*" ? Variables::pointerArray : Variables::referenceArray, info,
1945 tok->tokAt(7)->str() == "=" || isStatic);
1946
1947 // check for reading array size from local variable
1948 if (tok->tokAt(5)->varId() != 0)
1949 variables.read(tok->tokAt(5)->varId());
1950
1951 tok = tok->tokAt(6);
1952 }
1953
1954 // Freeing memory (not considered "using" the pointer if it was also allocated in this function)
1955 else if (Token::Match(tok, "free|g_free|kfree|vfree ( %var% )") ||
1956 Token::Match(tok, "delete %var% ;") ||
1957 Token::Match(tok, "delete [ ] %var% ;")) {
1958 unsigned int varid = 0;
1959 if (tok->str() != "delete") {
1960 varid = tok->tokAt(2)->varId();
1961 tok = tok->tokAt(3);
1962 } else if (tok->strAt(1) == "[") {
1963 varid = tok->tokAt(3)->varId();
1964 tok = tok->tokAt(4);
1965 } else {
1966 varid = tok->next()->varId();
1967 tok = tok->tokAt(2);
1968 }
1969
1970 Variables::VariableUsage *var = variables.find(varid);
1971 if (var && !var->_allocateMemory) {
1972 variables.readAll(varid);
1973 }
1974 }
1975
1976 else if (Token::Match(tok, "return|throw %var%"))
1977 variables.readAll(tok->next()->varId());
1978
1979 // assignment
1980 else if (Token::Match(tok, "*| (| ++|--| %var% ++|--| )| =") ||
1981 Token::Match(tok, "*| ( const| %type% *| ) %var% =")) {
1982 bool dereference = false;
1983 bool pre = false;
1984 bool post = false;
1985
1986 if (tok->str() == "*") {
1987 dereference = true;
1988 tok = tok->next();
1989 }
1990
1991 if (Token::Match(tok, "( const| %type% *| ) %var% ="))
1992 tok = tok->link()->next();
1993
1994 else if (tok->str() == "(")
1995 tok = tok->next();
1996
1997 if (Token::Match(tok, "++|--")) {
1998 pre = true;
1999 tok = tok->next();
2000 }
2001
2002 if (Token::Match(tok->next(), "++|--"))
2003 post = true;
2004
2005 const unsigned int varid1 = tok->varId();
2006 const Token *start = tok;
2007
2008 tok = tok->tokAt(doAssignment(variables, tok, dereference, info));
2009
2010 if (pre || post)
2011 variables.use(varid1);
2012
2013 if (dereference) {
2014 Variables::VariableUsage *var = variables.find(varid1);
2015 if (var && var->_type == Variables::array)
2016 variables.write(varid1);
2017 variables.writeAliases(varid1);
2018 variables.read(varid1);
2019 } else {
2020 Variables::VariableUsage *var = variables.find(varid1);
2021 if (var && var->_type == Variables::reference) {
2022 variables.writeAliases(varid1);
2023 variables.read(varid1);
2024 }
2025 // Consider allocating memory separately because allocating/freeing alone does not constitute using the variable
2026 else if (var && var->_type == Variables::pointer &&
2027 Token::Match(start, "%var% = new|malloc|calloc|g_malloc|kmalloc|vmalloc")) {
2028 bool allocate = true;
2029
2030 if (start->strAt(2) == "new") {
2031 // is it a user defined type?
2032 if (!start->tokAt(3)->isStandardType()) {
2033 if (!isRecordTypeWithoutSideEffects(start))
2034 allocate = false;
2035 }
2036 }
2037
2038 if (allocate)
2039 variables.allocateMemory(varid1);
2040 else
2041 variables.write(varid1);
2042 } else if (varid1 && Token::Match(tok, "%varid% .", varid1)) {
2043 variables.use(varid1);
2044 } else {
2045 variables.write(varid1);
2046 }
2047
2048 Variables::VariableUsage *var2 = variables.find(tok->varId());
2049 if (var2) {
2050 if (var2->_type == Variables::reference) {
2051 variables.writeAliases(tok->varId());
2052 variables.read(tok->varId());
2053 } else if (tok->varId() != varid1 && Token::Match(tok, "%var% ."))
2054 variables.read(tok->varId());
2055 else if (tok->varId() != varid1 &&
2056 var2->_type == Variables::standard &&
2057 tok->strAt(-1) != "&")
2058 variables.use(tok->varId());
2059 }
2060 }
2061
2062 const Token *equal = tok->next();
2063
2064 if (Token::Match(tok->next(), "[ %any% ]"))
2065 equal = tok->tokAt(4);
2066
2067 // checked for chained assignments
2068 if (tok != start && equal->str() == "=") {
2069 Variables::VariableUsage *var = variables.find(tok->varId());
2070
2071 if (var && var->_type != Variables::reference)
2072 var->_read = true;
2073
2074 tok = tok->previous();
2075 }
2076 }
2077
2078 // assignment
2079 else if (Token::Match(tok, "%var% [") && Token::simpleMatch(tok->next()->link(), "] =")) {
2080 unsigned int varid = tok->varId();
2081 const Variables::VariableUsage *var = variables.find(varid);
2082
2083 if (var) {
2084 // Consider allocating memory separately because allocating/freeing alone does not constitute using the variable
2085 if (var->_type == Variables::pointer &&
2086 Token::Match(tok->next()->link(), "] = new|malloc|calloc|g_malloc|kmalloc|vmalloc")) {
2087 variables.allocateMemory(varid);
2088 } else if (var->_type == Variables::pointer || var->_type == Variables::reference) {
2089 variables.read(varid);
2090 variables.writeAliases(varid);
2091 } else
2092 variables.writeAll(varid);
2093 }
2094 }
2095
2096 else if (Token::Match(tok, ">>|& %var%"))
2097 variables.use(tok->next()->varId()); // use = read + write
2098 else if (Token::Match(tok, "[;{}] %var% >>"))
2099 variables.use(tok->next()->varId()); // use = read + write
2100
2101 // function parameter
2102 else if (Token::Match(tok, "[(,] %var% ["))
2103 variables.use(tok->next()->varId()); // use = read + write
2104 else if (Token::Match(tok, "[(,] %var% [,)]") && tok->previous()->str() != "*")
2105 variables.use(tok->next()->varId()); // use = read + write
2106 else if (Token::Match(tok, "[(,] (") &&
2107 Token::Match(tok->next()->link(), ") %var% [,)]"))
2108 variables.use(tok->next()->link()->next()->varId()); // use = read + write
2109
2110 // function
2111 else if (Token::Match(tok, "%var% (")) {
2112 variables.read(tok->varId());
2113 if (Token::Match(tok->tokAt(2), "%var% ="))
2114 variables.read(tok->tokAt(2)->varId());
2115 }
2116
2117 else if (Token::Match(tok, "[{,] %var% [,}]"))
2118 variables.read(tok->next()->varId());
2119
2120 else if (Token::Match(tok, "%var% ."))
2121 variables.use(tok->varId()); // use = read + write
2122
2123 else if ((Token::Match(tok, "[(=&!]") || tok->isExtendedOp()) &&
2124 (Token::Match(tok->next(), "%var%") && !Token::Match(tok->next(), "true|false|new")))
2125 variables.readAll(tok->next()->varId());
2126
2127 else if (Token::Match(tok, "%var%") && (tok->next()->str() == ")" || tok->next()->isExtendedOp()))
2128 variables.readAll(tok->varId());
2129
2130 else if (Token::Match(tok, "; %var% ;"))
2131 variables.readAll(tok->next()->varId());
2132
2133 if (Token::Match(tok, "++|-- %var%")) {
2134 if (tok->strAt(-1) != ";")
2135 variables.use(tok->next()->varId());
2136 else
2137 variables.modified(tok->next()->varId());
2138 }
2139
2140 else if (Token::Match(tok, "%var% ++|--")) {
2141 if (tok->strAt(-1) != ";")
2142 variables.use(tok->varId());
2143 else
2144 variables.modified(tok->varId());
2145 }
2146
2147 else if (tok->isAssignmentOp()) {
2148 for (const Token *tok2 = tok->next(); tok2 && tok2->str() != ";"; tok2 = tok2->next()) {
2149 if (tok2->varId()) {
2150 variables.read(tok2->varId());
2151 if (tok2->next()->isAssignmentOp())
2152 variables.write(tok2->varId());
2153 }
2154 }
2155 }
2156 }
2157
2158 // Check usage of all variables in the current scope..
2159 Variables::VariableMap::const_iterator it;
2160 for (it = variables.varUsage().begin(); it != variables.varUsage().end(); ++it) {
2161 const Variables::VariableUsage &usage = it->second;
2162 const std::string &varname = usage._name->str();
2163
2164 // variable has been marked as unused so ignore it
2165 if (usage._name->isUnused())
2166 continue;
2167
2168 // skip things that are only partially implemented to prevent false positives
2169 if (usage._type == Variables::pointerPointer ||
2170 usage._type == Variables::pointerArray ||
2171 usage._type == Variables::referenceArray)
2172 continue;
2173
2174 // variable has had memory allocated for it, but hasn't done
2175 // anything with that memory other than, perhaps, freeing it
2176 if (usage.unused() && !usage._modified && usage._allocateMemory)
2177 allocatedButUnusedVariableError(usage._name, varname);
2178
2179 // variable has not been written, read, or modified
2180 else if (usage.unused() && !usage._modified)
2181 unusedVariableError(usage._name, varname);
2182
2183 // variable has not been written but has been modified
2184 else if (usage._modified & !usage._write)
2185 unassignedVariableError(usage._name, varname);
2186
2187 // variable has been written but not read
2188 else if (!usage._read && !usage._modified)
2189 unreadVariableError(usage._name, varname);
2190
2191 // variable has been read but not written
2192 else if (!usage._write && !usage._allocateMemory)
2193 unassignedVariableError(usage._name, varname);
2194 }
2195 }
2196 }
2197
unusedVariableError(const Token * tok,const std::string & varname)2198 void CheckOther::unusedVariableError(const Token *tok, const std::string &varname)
2199 {
2200 reportError(tok, Severity::style, "unusedVariable", "Unused variable: " + varname);
2201 }
2202
allocatedButUnusedVariableError(const Token * tok,const std::string & varname)2203 void CheckOther::allocatedButUnusedVariableError(const Token *tok, const std::string &varname)
2204 {
2205 reportError(tok, Severity::style, "unusedAllocatedMemory", "Variable '" + varname + "' is allocated memory that is never used");
2206 }
2207
unreadVariableError(const Token * tok,const std::string & varname)2208 void CheckOther::unreadVariableError(const Token *tok, const std::string &varname)
2209 {
2210 reportError(tok, Severity::style, "unreadVariable", "Variable '" + varname + "' is assigned a value that is never used");
2211 }
2212
unassignedVariableError(const Token * tok,const std::string & varname)2213 void CheckOther::unassignedVariableError(const Token *tok, const std::string &varname)
2214 {
2215 reportError(tok, Severity::style, "unassignedVariable", "Variable '" + varname + "' is not assigned a value");
2216 }
2217 //---------------------------------------------------------------------------
2218
2219
2220
2221
2222
2223 //---------------------------------------------------------------------------
2224 // Check scope of variables..
2225 //---------------------------------------------------------------------------
2226
checkVariableScope()2227 void CheckOther::checkVariableScope()
2228 {
2229 if (!_settings->isEnabled("information"))
2230 return;
2231
2232 const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();
2233
2234 std::list<Scope>::const_iterator scope;
2235
2236 for (scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) {
2237 // only check functions
2238 if (scope->type != Scope::eFunction)
2239 continue;
2240
2241 // Walk through all tokens..
2242 int indentlevel = 0;
2243 for (const Token *tok = scope->classStart; tok; tok = tok->next()) {
2244 // Skip function local class and struct declarations..
2245 if ((tok->str() == "class") || (tok->str() == "struct") || (tok->str() == "union")) {
2246 for (const Token *tok2 = tok; tok2; tok2 = tok2->next()) {
2247 if (tok2->str() == "{") {
2248 tok = tok2->link();
2249 break;
2250 }
2251 if (Token::Match(tok2, "[,);]")) {
2252 break;
2253 }
2254 }
2255 if (!tok)
2256 break;
2257 }
2258
2259 else if (tok->str() == "{") {
2260 ++indentlevel;
2261 } else if (tok->str() == "}") {
2262 --indentlevel;
2263 if (indentlevel == 0)
2264 break;
2265 }
2266
2267 if (indentlevel > 0 && Token::Match(tok, "[{};]")) {
2268 // First token of statement..
2269 const Token *tok1 = tok->next();
2270 if (!tok1)
2271 continue;
2272
2273 if ((tok1->str() == "return") ||
2274 (tok1->str() == "throw") ||
2275 (tok1->str() == "delete") ||
2276 (tok1->str() == "goto") ||
2277 (tok1->str() == "else"))
2278 continue;
2279
2280 // Variable declaration?
2281 if (Token::Match(tok1, "%type% %var% ; %var% = %num% ;")) {
2282 // Tokenizer modify "int i = 0;" to "int i; i = 0;",
2283 // so to handle this situation we just skip
2284 // initialization (see ticket #272).
2285 const unsigned int firstVarId = tok1->next()->varId();
2286 const unsigned int secondVarId = tok1->tokAt(3)->varId();
2287 if (firstVarId > 0 && firstVarId == secondVarId) {
2288 lookupVar(tok1->tokAt(6), tok1->strAt(1));
2289 }
2290 } else if (tok1->isStandardType() && Token::Match(tok1, "%type% %var% [;=]")) {
2291 lookupVar(tok1, tok1->strAt(1));
2292 }
2293 }
2294 }
2295 }
2296
2297 }
2298 //---------------------------------------------------------------------------
2299
lookupVar(const Token * tok1,const std::string & varname)2300 void CheckOther::lookupVar(const Token *tok1, const std::string &varname)
2301 {
2302 const Token *tok = tok1;
2303
2304 // Skip the variable declaration..
2305 while (tok && tok->str() != ";")
2306 tok = tok->next();
2307
2308 // Check if the variable is used in this indentlevel..
2309 bool used1 = false; // used in one sub-scope -> reducable
2310 bool used2 = false; // used in more sub-scopes -> not reducable
2311 int indentlevel = 0;
2312 int parlevel = 0;
2313 bool for_or_while = false; // is sub-scope a "for/while/etc". anything that is not "if"
2314 while (tok) {
2315 if (tok->str() == "{") {
2316 if (tok->strAt(-1) == "=") {
2317 if (Token::findmatch(tok, varname.c_str(), tok->link())) {
2318 return;
2319 }
2320
2321 tok = tok->link();
2322 } else
2323 ++indentlevel;
2324 }
2325
2326 else if (tok->str() == "}") {
2327 if (indentlevel == 0)
2328 break;
2329 --indentlevel;
2330 if (indentlevel == 0) {
2331 if (for_or_while && used2)
2332 return;
2333 used2 |= used1;
2334 used1 = false;
2335 }
2336 }
2337
2338 else if (tok->str() == "(") {
2339 ++parlevel;
2340 }
2341
2342 else if (tok->str() == ")") {
2343 --parlevel;
2344 }
2345
2346 // Bail out if references are used
2347 else if (Token::simpleMatch(tok, (std::string("& ") + varname).c_str())) {
2348 return;
2349 }
2350
2351 else if (tok->str() == varname) {
2352 if (indentlevel == 0)
2353 return;
2354 used1 = true;
2355 if (for_or_while && !Token::simpleMatch(tok->next(), "="))
2356 used2 = true;
2357 if (used1 && used2)
2358 return;
2359 }
2360
2361 else if (indentlevel == 0) {
2362 // %unknown% ( %any% ) {
2363 // If %unknown% is anything except if, we assume
2364 // that it is a for or while loop or a macro hiding either one
2365 if (Token::simpleMatch(tok->next(), "(") &&
2366 Token::simpleMatch(tok->next()->link(), ") {")) {
2367 if (tok->str() != "if")
2368 for_or_while = true;
2369 }
2370
2371 else if (Token::simpleMatch(tok, "do {"))
2372 for_or_while = true;
2373
2374 // possible unexpanded macro hiding for/while..
2375 else if (tok->str() != "else" && Token::Match(tok->previous(), "[;{}] %type% {")) {
2376 for_or_while = true;
2377 }
2378
2379 if (parlevel == 0 && (tok->str() == ";"))
2380 for_or_while = false;
2381 }
2382
2383 tok = tok->next();
2384 }
2385
2386 // Warning if this variable:
2387 // * not used in this indentlevel
2388 // * used in lower indentlevel
2389 if (used1 || used2)
2390 variableScopeError(tok1, varname);
2391 }
2392 //---------------------------------------------------------------------------
2393
2394
2395 //---------------------------------------------------------------------------
2396 // Check for constant function parameters
2397 //---------------------------------------------------------------------------
2398
checkConstantFunctionParameter()2399 void CheckOther::checkConstantFunctionParameter()
2400 {
2401 if (!_settings->isEnabled("style"))
2402 return;
2403
2404 const SymbolDatabase * const symbolDatabase = _tokenizer->getSymbolDatabase();
2405
2406 for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
2407 // TODO: False negatives. This pattern only checks for string.
2408 // Investigate if there are other classes in the std
2409 // namespace and add them to the pattern. There are
2410 // streams for example (however it seems strange with
2411 // const stream parameter).
2412 if (Token::Match(tok, "[,(] const std :: string %var% [,)]")) {
2413 passedByValueError(tok, tok->strAt(5));
2414 }
2415
2416 else if (Token::Match(tok, "[,(] const std :: %type% < %type% > %var% [,)]")) {
2417 passedByValueError(tok, tok->strAt(8));
2418 }
2419
2420 else if (Token::Match(tok, "[,(] const std :: %type% < std :: %type% > %var% [,)]")) {
2421 passedByValueError(tok, tok->strAt(10));
2422 }
2423
2424 else if (Token::Match(tok, "[,(] const std :: %type% < std :: %type% , std :: %type% > %var% [,)]")) {
2425 passedByValueError(tok, tok->strAt(14));
2426 }
2427
2428 else if (Token::Match(tok, "[,(] const std :: %type% < %type% , std :: %type% > %var% [,)]")) {
2429 passedByValueError(tok, tok->strAt(12));
2430 }
2431
2432 else if (Token::Match(tok, "[,(] const std :: %type% < std :: %type% , %type% > %var% [,)]")) {
2433 passedByValueError(tok, tok->strAt(12));
2434 }
2435
2436 else if (Token::Match(tok, "[,(] const std :: %type% < %type% , %type% > %var% [,)]")) {
2437 passedByValueError(tok, tok->strAt(10));
2438 }
2439
2440 else if (Token::Match(tok, "[,(] const %type% %var% [,)]")) {
2441 // Check if type is a struct or class.
2442 if (symbolDatabase->isClassOrStruct(tok->strAt(2))) {
2443 passedByValueError(tok, tok->strAt(3));
2444 }
2445 }
2446 }
2447 }
2448 //---------------------------------------------------------------------------
2449
2450
2451 //---------------------------------------------------------------------------
2452 // Check that all struct members are used
2453 //---------------------------------------------------------------------------
2454
checkStructMemberUsage()2455 void CheckOther::checkStructMemberUsage()
2456 {
2457 if (!_settings->isEnabled("style"))
2458 return;
2459
2460 std::string structname;
2461 for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
2462 if (tok->fileIndex() != 0)
2463 continue;
2464
2465 if (Token::Match(tok, "struct|union %type% {")) {
2466 structname.clear();
2467 if (Token::simpleMatch(tok->previous(), "extern"))
2468 continue;
2469 if ((!tok->previous() || Token::simpleMatch(tok->previous(), ";")) && Token::Match(tok->tokAt(2)->link(), ("} ; " + tok->strAt(1) + " %var% ;").c_str()))
2470 continue;
2471
2472 structname = tok->strAt(1);
2473
2474 // Bail out if struct/union contain any functions
2475 for (const Token *tok2 = tok->tokAt(2); tok2; tok2 = tok2->next()) {
2476 if (tok2->str() == "(") {
2477 structname.clear();
2478 break;
2479 }
2480
2481 if (tok2->str() == "}")
2482 break;
2483 }
2484
2485 // bail out if struct is inherited
2486 if (!structname.empty() && Token::findmatch(tok, (",|private|protected|public " + structname).c_str()))
2487 structname.clear();
2488
2489 // Bail out if some data is casted to struct..
2490 const std::string s("( struct| " + tok->next()->str() + " * ) & %var% [");
2491 if (Token::findmatch(tok, s.c_str()))
2492 structname.clear();
2493
2494 // Try to prevent false positives when struct members are not used directly.
2495 if (Token::findmatch(tok, (structname + " *").c_str()))
2496 structname.clear();
2497 else if (Token::findmatch(tok, (structname + " %type% *").c_str()))
2498 structname = "";
2499 }
2500
2501 if (tok->str() == "}")
2502 structname.clear();
2503
2504 if (!structname.empty() && Token::Match(tok, "[{;]")) {
2505 // Declaring struct variable..
2506 std::string varname;
2507
2508 // declaring a POD variable?
2509 if (!tok->next()->isStandardType())
2510 continue;
2511
2512 if (Token::Match(tok->next(), "%type% %var% [;[]"))
2513 varname = tok->strAt(2);
2514 else if (Token::Match(tok->next(), "%type% %type% %var% [;[]"))
2515 varname = tok->strAt(3);
2516 else if (Token::Match(tok->next(), "%type% * %var% [;[]"))
2517 varname = tok->strAt(3);
2518 else if (Token::Match(tok->next(), "%type% %type% * %var% [;[]"))
2519 varname = tok->strAt(4);
2520 else
2521 continue;
2522
2523 // Check if the struct variable is used anywhere in the file
2524 const std::string usagePattern(". " + varname);
2525 bool used = false;
2526 for (const Token *tok2 = _tokenizer->tokens(); tok2; tok2 = tok2->next()) {
2527 if (Token::simpleMatch(tok2, usagePattern.c_str())) {
2528 used = true;
2529 break;
2530 }
2531 }
2532
2533 if (!used) {
2534 unusedStructMemberError(tok->next(), structname, varname);
2535 }
2536 }
2537 }
2538 }
2539
2540
2541
2542
2543
2544 //---------------------------------------------------------------------------
2545 // Check usage of char variables..
2546 //---------------------------------------------------------------------------
2547
checkCharVariable()2548 void CheckOther::checkCharVariable()
2549 {
2550 if (!_settings->isEnabled("style"))
2551 return;
2552
2553 for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
2554 // Declaring the variable..
2555 if (Token::Match(tok, "[{};(,] const| char *| %var% [;=,)]") ||
2556 Token::Match(tok, "[{};(,] const| char %var% [")) {
2557 // goto 'char' token
2558 tok = tok->next();
2559 if (tok->str() == "const")
2560 tok = tok->next();
2561
2562 // Check for unsigned char
2563 if (tok->isUnsigned())
2564 continue;
2565
2566 // Set tok to point to the variable name
2567 tok = tok->next();
2568 const bool isPointer(tok->str() == "*" || tok->strAt(1) == "[");
2569 if (tok->str() == "*")
2570 tok = tok->next();
2571
2572 // Check usage of char variable..
2573 int indentlevel = 0;
2574 for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) {
2575 if (tok2->str() == "{")
2576 ++indentlevel;
2577
2578 else if (tok2->str() == "}") {
2579 --indentlevel;
2580 if (indentlevel <= 0)
2581 break;
2582 }
2583
2584 if (!isPointer) {
2585 std::string temp = "%var% [ " + tok->str() + " ]";
2586 if ((tok2->str() != ".") && Token::Match(tok2->next(), temp.c_str())) {
2587 charArrayIndexError(tok2->next());
2588 break;
2589 }
2590 }
2591
2592 if (Token::Match(tok2, "[;{}] %var% = %any% [&|] %any% ;")) {
2593 // is the char variable used in the calculation?
2594 if (tok2->tokAt(3)->varId() != tok->varId() && tok2->tokAt(5)->varId() != tok->varId())
2595 continue;
2596
2597 // it's ok with a bitwise and where the other operand is 0xff or less..
2598 if (tok2->strAt(4) == "&") {
2599 if (tok2->tokAt(3)->isNumber() && MathLib::isGreater("0x100", tok2->strAt(3)))
2600 continue;
2601 if (tok2->tokAt(5)->isNumber() && MathLib::isGreater("0x100", tok2->strAt(5)))
2602 continue;
2603 }
2604
2605 // is the result stored in a short|int|long?
2606 if (!Token::findmatch(_tokenizer->tokens(), "short|int|long %varid%", tok2->next()->varId()))
2607 continue;
2608
2609 // This is an error..
2610 charBitOpError(tok2);
2611 break;
2612 }
2613
2614 if (isPointer && Token::Match(tok2, "[;{}] %var% = %any% [&|] ( * %varid% ) ;", tok->varId())) {
2615 // it's ok with a bitwise and where the other operand is 0xff or less..
2616 if (tok2->strAt(4) == "&" && tok2->tokAt(3)->isNumber() && MathLib::isGreater("0x100", tok2->strAt(3)))
2617 continue;
2618
2619 // is the result stored in a short|int|long?
2620 if (!Token::findmatch(_tokenizer->tokens(), "short|int|long %varid%", tok2->next()->varId()))
2621 continue;
2622
2623 // This is an error..
2624 charBitOpError(tok2);
2625 break;
2626 }
2627 }
2628 }
2629 }
2630 }
2631 //---------------------------------------------------------------------------
2632
2633
2634
2635
2636
2637
2638 //---------------------------------------------------------------------------
2639 // Incomplete statement..
2640 //---------------------------------------------------------------------------
2641
checkIncompleteStatement()2642 void CheckOther::checkIncompleteStatement()
2643 {
2644 if (!_settings->isEnabled("style"))
2645 return;
2646
2647 for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
2648 if (tok->str() == "(") {
2649 tok = tok->link();
2650 if (Token::simpleMatch(tok, ") {") && Token::simpleMatch(tok->next()->link(), "} ;"))
2651 tok = tok->next()->link();
2652 }
2653
2654 else if (Token::simpleMatch(tok, "= {"))
2655 tok = tok->next()->link();
2656
2657 else if (tok->str() == "{" && Token::Match(tok->tokAt(-2), "%type% %var%"))
2658 tok = tok->link();
2659
2660 else if (Token::Match(tok, "[;{}] %str%") || Token::Match(tok, "[;{}] %num%")) {
2661 // bailout if there is a "? :" in this statement
2662 bool bailout = false;
2663 for (const Token *tok2 = tok->tokAt(2); tok2; tok2 = tok2->next()) {
2664 if (tok2->str() == "?")
2665 bailout = true;
2666 else if (tok2->str() == ";")
2667 break;
2668 }
2669 if (bailout)
2670 continue;
2671
2672 constStatementError(tok->next(), tok->next()->isNumber() ? "numeric" : "string");
2673 }
2674 }
2675 }
2676 //---------------------------------------------------------------------------
2677
2678
2679
2680
2681
2682
2683 //---------------------------------------------------------------------------
2684 // str plus char
2685 //---------------------------------------------------------------------------
2686
strPlusChar()2687 void CheckOther::strPlusChar()
2688 {
2689 // Don't use this check for Java and C# programs..
2690 if (_tokenizer->isJavaOrCSharp()) {
2691 return;
2692 }
2693
2694 bool charVars[10000] = {0};
2695
2696 for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
2697 // Declaring char variable..
2698 if (Token::Match(tok, "char|int|short %var% [;=]")) {
2699 unsigned int varid = tok->next()->varId();
2700 if (varid > 0 && varid < 10000)
2701 charVars[varid] = true;
2702 }
2703
2704 //
2705 else if (Token::Match(tok, "[=(] %str% + %any%")) {
2706 // char constant..
2707 const std::string s = tok->strAt(3);
2708 if (s[0] == '\'')
2709 strPlusChar(tok->next());
2710
2711 // char variable..
2712 unsigned int varid = tok->tokAt(3)->varId();
2713 if (varid > 0 && varid < 10000 && charVars[varid])
2714 strPlusChar(tok->next());
2715 }
2716 }
2717 }
2718
checkZeroDivision()2719 void CheckOther::checkZeroDivision()
2720 {
2721 for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
2722
2723 if (Token::Match(tok, "/ %num%") &&
2724 MathLib::isInt(tok->next()->str()) &&
2725 MathLib::toLongNumber(tok->next()->str()) == 0L) {
2726 zerodivError(tok);
2727 } else if (Token::Match(tok, "div|ldiv|lldiv|imaxdiv ( %num% , %num% )") &&
2728 MathLib::isInt(tok->tokAt(4)->str()) &&
2729 MathLib::toLongNumber(tok->tokAt(4)->str()) == 0L) {
2730 zerodivError(tok);
2731 }
2732 }
2733 }
2734
2735
checkMathFunctions()2736 void CheckOther::checkMathFunctions()
2737 {
2738 for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
2739 // case log(-2)
2740 if (tok->varId() == 0 &&
2741 Token::Match(tok, "log|log10 ( %num% )") &&
2742 MathLib::isNegative(tok->tokAt(2)->str()) &&
2743 MathLib::isInt(tok->tokAt(2)->str()) &&
2744 MathLib::toLongNumber(tok->tokAt(2)->str()) <= 0) {
2745 mathfunctionCallError(tok);
2746 }
2747 // case log(-2.0)
2748 else if (tok->varId() == 0 &&
2749 Token::Match(tok, "log|log10 ( %num% )") &&
2750 MathLib::isNegative(tok->tokAt(2)->str()) &&
2751 MathLib::isFloat(tok->tokAt(2)->str()) &&
2752 MathLib::toDoubleNumber(tok->tokAt(2)->str()) <= 0.) {
2753 mathfunctionCallError(tok);
2754 }
2755
2756 // case log(0.0)
2757 else if (tok->varId() == 0 &&
2758 Token::Match(tok, "log|log10 ( %num% )") &&
2759 !MathLib::isNegative(tok->tokAt(2)->str()) &&
2760 MathLib::isFloat(tok->tokAt(2)->str()) &&
2761 MathLib::toDoubleNumber(tok->tokAt(2)->str()) <= 0.) {
2762 mathfunctionCallError(tok);
2763 }
2764
2765 // case log(0)
2766 else if (tok->varId() == 0 &&
2767 Token::Match(tok, "log|log10 ( %num% )") &&
2768 !MathLib::isNegative(tok->tokAt(2)->str()) &&
2769 MathLib::isInt(tok->tokAt(2)->str()) &&
2770 MathLib::toLongNumber(tok->tokAt(2)->str()) <= 0) {
2771 mathfunctionCallError(tok);
2772 }
2773 // acos( x ), asin( x ) where x is defined for interval [-1,+1], but not beyond
2774 else if (tok->varId() == 0 &&
2775 Token::Match(tok, "acos|asin ( %num% )") &&
2776 std::fabs(MathLib::toDoubleNumber(tok->tokAt(2)->str())) > 1.0) {
2777 mathfunctionCallError(tok);
2778 }
2779 // sqrt( x ): if x is negative the result is undefined
2780 else if (tok->varId() == 0 &&
2781 Token::Match(tok, "sqrt|sqrtf|sqrtl ( %num% )") &&
2782 MathLib::isNegative(tok->tokAt(2)->str())) {
2783 mathfunctionCallError(tok);
2784 }
2785 // atan2 ( x , y): x and y can not be zero, because this is mathematically not defined
2786 else if (tok->varId() == 0 &&
2787 Token::Match(tok, "atan2 ( %num% , %num% )") &&
2788 MathLib::isNullValue(tok->tokAt(2)->str()) &&
2789 MathLib::isNullValue(tok->tokAt(4)->str())) {
2790 mathfunctionCallError(tok, 2);
2791 }
2792 // fmod ( x , y) If y is zero, then either a range error will occur or the function will return zero (implementation-defined).
2793 else if (tok->varId() == 0 &&
2794 Token::Match(tok, "fmod ( %num% , %num% )") &&
2795 MathLib::isNullValue(tok->tokAt(4)->str())) {
2796 mathfunctionCallError(tok, 2);
2797 }
2798 // pow ( x , y) If x is zero, and y is negative --> division by zero
2799 else if (tok->varId() == 0 &&
2800 Token::Match(tok, "pow ( %num% , %num% )") &&
2801 MathLib::isNullValue(tok->tokAt(2)->str()) &&
2802 MathLib::isNegative(tok->tokAt(4)->str())) {
2803 mathfunctionCallError(tok, 2);
2804 }
2805
2806 }
2807 }
2808
2809 /** Is there a function with given name? */
isFunction(const std::string & name,const Token * startToken)2810 static bool isFunction(const std::string &name, const Token *startToken)
2811 {
2812 const std::string pattern1(name + " (");
2813 for (const Token *tok = startToken; tok; tok = tok->next()) {
2814 // skip executable scopes etc
2815 if (tok->str() == "(") {
2816 tok = tok->link();
2817 if (Token::simpleMatch(tok, ") {"))
2818 tok = tok->next()->link();
2819 else if (Token::simpleMatch(tok, ") const {"))
2820 tok = tok->tokAt(2)->link();
2821 }
2822
2823 // function declaration/implementation found
2824 if ((tok->str() == "*" || (tok->isName() && tok->str().find(":") ==std::string::npos))
2825 && Token::simpleMatch(tok->next(), pattern1.c_str()))
2826 return true;
2827 }
2828 return false;
2829 }
2830
checkMisusedScopedObject()2831 void CheckOther::checkMisusedScopedObject()
2832 {
2833 // Skip this check for .c files
2834 {
2835 const std::string fname = _tokenizer->getFiles()->at(0);
2836 size_t position = fname.rfind(".");
2837
2838 if (position != std::string::npos) {
2839 const std::string ext = fname.substr(position);
2840 if (ext == ".c" || ext == ".C")
2841 return;
2842 }
2843 }
2844
2845 const SymbolDatabase * const symbolDatabase = _tokenizer->getSymbolDatabase();
2846
2847 std::list<Scope>::const_iterator scope;
2848
2849 for (scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) {
2850 // only check functions
2851 if (scope->type != Scope::eFunction)
2852 continue;
2853
2854 unsigned int depth = 0;
2855
2856 for (const Token *tok = scope->classStart; tok; tok = tok->next()) {
2857 if (tok->str() == "{") {
2858 ++depth;
2859 } else if (tok->str() == "}") {
2860 --depth;
2861 if (depth == 0)
2862 break;
2863 }
2864
2865 if (Token::Match(tok, "[;{}] %var% (")
2866 && Token::simpleMatch(tok->tokAt(2)->link(), ") ;")
2867 && symbolDatabase->isClassOrStruct(tok->next()->str())
2868 && !isFunction(tok->next()->str(), _tokenizer->tokens())) {
2869 tok = tok->next();
2870 misusedScopeObjectError(tok, tok->str());
2871 tok = tok->next();
2872 }
2873 }
2874 }
2875 }
2876
checkIncorrectStringCompare()2877 void CheckOther::checkIncorrectStringCompare()
2878 {
2879 for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
2880 if (Token::Match(tok, ". substr ( %any% , %num% ) ==|!= %str%")) {
2881 size_t clen = MathLib::toLongNumber(tok->tokAt(5)->str());
2882 size_t slen = Token::getStrLength(tok->tokAt(8));
2883 if (clen != slen) {
2884 incorrectStringCompareError(tok->next(), "substr", tok->tokAt(8)->str(), tok->tokAt(5)->str());
2885 }
2886 }
2887 if (Token::Match(tok, "%str% ==|!= %var% . substr ( %any% , %num% )")) {
2888 size_t clen = MathLib::toLongNumber(tok->tokAt(8)->str());
2889 size_t slen = Token::getStrLength(tok);
2890 if (clen != slen) {
2891 incorrectStringCompareError(tok->next(), "substr", tok->str(), tok->tokAt(8)->str());
2892 }
2893 }
2894 }
2895 }
2896
2897 //-----------------------------------------------------------------------------
2898 // check for duplicate expressions in if statements
2899 // if (a) { } else if (a) { }
2900 //-----------------------------------------------------------------------------
2901
stringifyTokens(const Token * start,const Token * end)2902 static const std::string stringifyTokens(const Token *start, const Token *end)
2903 {
2904 const Token *tok = start;
2905 std::string stringified;
2906
2907 if (tok->isUnsigned())
2908 stringified.append("unsigned ");
2909 else if (tok->isSigned())
2910 stringified.append("signed ");
2911
2912 if (tok->isLong())
2913 stringified.append("long ");
2914
2915 stringified.append(tok->str());
2916
2917 while (tok && tok->next() && tok != end) {
2918 if (tok->isUnsigned())
2919 stringified.append("unsigned ");
2920 else if (tok->isSigned())
2921 stringified.append("signed ");
2922
2923 if (tok->isLong())
2924 stringified.append("long ");
2925
2926 tok = tok->next();
2927 stringified.append(" ");
2928 stringified.append(tok->str());
2929 }
2930
2931 return stringified;
2932 }
2933
expressionHasSideEffects(const Token * first,const Token * last)2934 static bool expressionHasSideEffects(const Token *first, const Token *last)
2935 {
2936 for (const Token *tok = first; tok != last->next(); tok = tok->next()) {
2937 // check for assignment
2938 if (tok->isAssignmentOp())
2939 return true;
2940
2941 // check for inc/dec
2942 else if (Token::Match(tok, "++|--"))
2943 return true;
2944
2945 // check for function call
2946 else if (Token::Match(tok, "%var% (") &&
2947 !(Token::Match(tok, "c_str|string") || tok->isStandardType()))
2948 return true;
2949 }
2950
2951 return false;
2952 }
2953
checkDuplicateIf()2954 void CheckOther::checkDuplicateIf()
2955 {
2956 if (!_settings->isEnabled("style"))
2957 return;
2958
2959 const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();
2960
2961 std::list<Scope>::const_iterator scope;
2962
2963 for (scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) {
2964 // only check functions
2965 if (scope->type != Scope::eFunction)
2966 continue;
2967
2968 // check all the code in the function for if (...) and else if (...) statements
2969 for (const Token *tok = scope->classStart; tok && tok != scope->classStart->link(); tok = tok->next()) {
2970 if (Token::simpleMatch(tok, "if (") && tok->strAt(-1) != "else" &&
2971 Token::simpleMatch(tok->next()->link(), ") {")) {
2972 std::map<std::string, const Token*> expressionMap;
2973
2974 // get the expression from the token stream
2975 std::string expression = stringifyTokens(tok->tokAt(2), tok->next()->link()->previous());
2976
2977 // save the expression and its location
2978 expressionMap.insert(std::make_pair(expression, tok));
2979
2980 // find the next else if (...) statement
2981 const Token *tok1 = tok->next()->link()->next()->link();
2982
2983 // check all the else if (...) statements
2984 while (Token::simpleMatch(tok1, "} else if (") &&
2985 Token::simpleMatch(tok1->tokAt(3)->link(), ") {")) {
2986 // get the expression from the token stream
2987 expression = stringifyTokens(tok1->tokAt(4), tok1->tokAt(3)->link()->previous());
2988
2989 // try to look up the expression to check for duplicates
2990 std::map<std::string, const Token *>::iterator it = expressionMap.find(expression);
2991
2992 // found a duplicate
2993 if (it != expressionMap.end()) {
2994 // check for expressions that have side effects and ignore them
2995 if (!expressionHasSideEffects(tok1->tokAt(4), tok1->tokAt(3)->link()->previous()))
2996 duplicateIfError(it->second, tok1->next());
2997 }
2998
2999 // not a duplicate expression so save it and its location
3000 else
3001 expressionMap.insert(std::make_pair(expression, tok1->next()));
3002
3003 // find the next else if (...) statement
3004 tok1 = tok1->tokAt(3)->link()->next()->link();
3005 }
3006
3007 tok = tok->next()->link()->next();
3008 }
3009 }
3010 }
3011 }
3012
duplicateIfError(const Token * tok1,const Token * tok2)3013 void CheckOther::duplicateIfError(const Token *tok1, const Token *tok2)
3014 {
3015 std::list<const Token *> toks;
3016 toks.push_back(tok2);
3017 toks.push_back(tok1);
3018
3019 reportError(toks, Severity::style, "duplicateIf", "Found duplicate if expressions.\n"
3020 "Finding the same expression more than once is suspicious and might indicate "
3021 "a cut and paste or logic error. Please examine this code carefully to determine "
3022 "if it is correct.");
3023 }
3024
3025 //-----------------------------------------------------------------------------
3026 // check for duplicate code in if and else branches
3027 // if (a) { b = true; } else { b = true; }
3028 //-----------------------------------------------------------------------------
3029
checkDuplicateBranch()3030 void CheckOther::checkDuplicateBranch()
3031 {
3032 if (!_settings->isEnabled("style"))
3033 return;
3034
3035 if (!_settings->inconclusive)
3036 return;
3037
3038 const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();
3039
3040 std::list<Scope>::const_iterator scope;
3041
3042 for (scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) {
3043 // only check functions
3044 if (scope->type != Scope::eFunction)
3045 continue;
3046
3047 // check all the code in the function for if (..) else
3048 for (const Token *tok = scope->classStart; tok && tok != scope->classStart->link(); tok = tok->next()) {
3049 if (Token::simpleMatch(tok, "if (") && tok->strAt(-1) != "else" &&
3050 Token::simpleMatch(tok->next()->link(), ") {") &&
3051 Token::simpleMatch(tok->next()->link()->next()->link(), "} else {")) {
3052 // save if branch code
3053 std::string branch1 = stringifyTokens(tok->next()->link()->tokAt(2), tok->next()->link()->next()->link()->previous());
3054
3055 // find else branch
3056 const Token *tok1 = tok->next()->link()->next()->link();
3057
3058 // save else branch code
3059 std::string branch2 = stringifyTokens(tok1->tokAt(3), tok1->tokAt(2)->link()->previous());
3060
3061 // check for duplicates
3062 if (branch1 == branch2)
3063 duplicateBranchError(tok, tok1->tokAt(2));
3064
3065 tok = tok->next()->link()->next();
3066 }
3067 }
3068 }
3069 }
3070
duplicateBranchError(const Token * tok1,const Token * tok2)3071 void CheckOther::duplicateBranchError(const Token *tok1, const Token *tok2)
3072 {
3073 std::list<const Token *> toks;
3074 toks.push_back(tok2);
3075 toks.push_back(tok1);
3076
3077 reportError(toks, Severity::style, "duplicateBranch", "Found duplicate branches for if and else.\n"
3078 "Finding the same code for an if branch and an else branch is suspicious and "
3079 "might indicate a cut and paste or logic error. Please examine this code "
3080 "carefully to determine if it is correct.");
3081 }
3082
3083 //---------------------------------------------------------------------------
3084 // check for the same expression on both sides of an operator
3085 // (x == x), (x && x), (x || x)
3086 // (x.y == x.y), (x.y && x.y), (x.y || x.y)
3087 //---------------------------------------------------------------------------
3088
checkDuplicateExpression()3089 void CheckOther::checkDuplicateExpression()
3090 {
3091 if (!_settings->isEnabled("style"))
3092 return;
3093
3094 // Parse all executing scopes..
3095 const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();
3096
3097 std::list<Scope>::const_iterator scope;
3098
3099 for (scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) {
3100 // only check functions
3101 if (scope->type != Scope::eFunction)
3102 continue;
3103
3104 for (const Token *tok = scope->classStart; tok && tok != scope->classStart->link(); tok = tok->next()) {
3105 if (Token::Match(tok, "(|&&|%oror% %var% &&|%oror%|==|!=|<=|>=|<|>|-|%or% %var% )|&&|%oror%") &&
3106 tok->strAt(1) == tok->strAt(3)) {
3107 // float == float and float != float are valid NaN checks
3108 if (Token::Match(tok->tokAt(2), "==|!=") && tok->next()->varId()) {
3109 const Variable * var = symbolDatabase->getVariableFromVarId(tok->next()->varId());
3110 if (var && var->typeStartToken() == var->typeEndToken()) {
3111 if (Token::Match(var->typeStartToken(), "float|double"))
3112 continue;
3113 }
3114 }
3115
3116 duplicateExpressionError(tok->next(), tok->tokAt(3), tok->strAt(2));
3117 } else if (Token::Match(tok, "(|&&|%oror% %var% . %var% &&|%oror%|==|!=|<=|>=|<|>|-|%or% %var% . %var% )|&&|%oror%") &&
3118 tok->strAt(1) == tok->strAt(5) && tok->strAt(3) == tok->strAt(7)) {
3119 duplicateExpressionError(tok->next(), tok->tokAt(6), tok->strAt(4));
3120 }
3121 }
3122 }
3123 }
3124
duplicateExpressionError(const Token * tok1,const Token * tok2,const std::string & op)3125 void CheckOther::duplicateExpressionError(const Token *tok1, const Token *tok2, const std::string &op)
3126 {
3127 std::list<const Token *> toks;
3128 toks.push_back(tok2);
3129 toks.push_back(tok1);
3130
3131 reportError(toks, Severity::style, "duplicateExpression", "Same expression on both sides of \'" + op + "\'.\n"
3132 "Finding the same expression on both sides of an operator is suspicious and might "
3133 "indicate a cut and paste or logic error. Please examine this code carefully to "
3134 "determine if it is correct.");
3135 }
3136
3137
3138 //---------------------------------------------------------------------------
3139 // Check for string comparison involving two static strings.
3140 // if(strcmp("00FF00","00FF00")==0) // <- statement is always true
3141 //---------------------------------------------------------------------------
3142
checkAlwaysTrueOrFalseStringCompare()3143 void CheckOther::checkAlwaysTrueOrFalseStringCompare()
3144 {
3145 if (!_settings->isEnabled("style"))
3146 return;
3147
3148 const char pattern1[] = "strcmp|stricmp|strcmpi|strcasecmp|wcscmp ( %str% , %str% )";
3149 const char pattern2[] = "QString :: compare ( %str% , %str% )";
3150
3151 const Token *tok = _tokenizer->tokens();
3152 while (tok && (tok = Token::findmatch(tok, pattern1)) != NULL) {
3153 alwaysTrueFalseStringCompare(tok, tok->strAt(2), tok->strAt(4));
3154 tok = tok->tokAt(5);
3155 }
3156
3157 tok = _tokenizer->tokens();
3158 while (tok && (tok = Token::findmatch(tok, pattern2)) != NULL) {
3159 alwaysTrueFalseStringCompare(tok, tok->strAt(4), tok->strAt(6));
3160 tok = tok->tokAt(7);
3161 }
3162 }
3163
alwaysTrueFalseStringCompare(const Token * tok,const std::string & str1,const std::string & str2)3164 void CheckOther::alwaysTrueFalseStringCompare(const Token *tok, const std::string& str1, const std::string& str2)
3165 {
3166 const size_t stringLen = 10;
3167 const std::string string1 = (str1.size() < stringLen) ? str1 : (str1.substr(0, stringLen-2) + "..");
3168 const std::string string2 = (str2.size() < stringLen) ? str2 : (str2.substr(0, stringLen-2) + "..");
3169
3170 if (str1 == str2) {
3171 reportError(tok, Severity::warning, "staticStringCompare",
3172 "Comparison of always identical static strings.\n"
3173 "The compared strings, '" + string1 + "' and '" + string2 + "', are always identical. "
3174 "If the purpose is to compare these two strings, the comparison is unnecessary. "
3175 "If the strings are supposed to be different, then there is a bug somewhere.");
3176 } else {
3177 reportError(tok, Severity::performance, "staticStringCompare",
3178 "Unnecessary comparison of static strings.\n"
3179 "The compared strings, '" + string1 + "' and '" + string2 + "', are static and always different. "
3180 "If the purpose is to compare these two strings, the comparison is unnecessary.");
3181 }
3182 }
3183
3184 //-----------------------------------------------------------------------------
3185
cstyleCastError(const Token * tok)3186 void CheckOther::cstyleCastError(const Token *tok)
3187 {
3188 reportError(tok, Severity::style, "cstyleCast", "C-style pointer casting");
3189 }
3190
dangerousUsageStrtolError(const Token * tok)3191 void CheckOther::dangerousUsageStrtolError(const Token *tok)
3192 {
3193 reportError(tok, Severity::error, "dangerousUsageStrtol", "Invalid radix in call to strtol or strtoul. Must be 0 or 2-36");
3194 }
3195
sprintfOverlappingDataError(const Token * tok,const std::string & varname)3196 void CheckOther::sprintfOverlappingDataError(const Token *tok, const std::string &varname)
3197 {
3198 reportError(tok, Severity::error, "sprintfOverlappingData",
3199 "Undefined behavior: variable is used as parameter and destination in s[n]printf().\n"
3200 "The variable '" + varname + "' is used both as a parameter and as a destination in "
3201 "s[n]printf(). The origin and destination buffers overlap. Quote from glibc (C-library) "
3202 "documentation (http://www.gnu.org/software/libc/manual/html_mono/libc.html#Formatted-Output-Functions): "
3203 "'If copying takes place between objects that overlap as a result of a call "
3204 "to sprintf() or snprintf(), the results are undefined.'");
3205 }
3206
udivError(const Token * tok)3207 void CheckOther::udivError(const Token *tok)
3208 {
3209 reportError(tok, Severity::error, "udivError", "Unsigned division. The result will be wrong.");
3210 }
3211
unusedStructMemberError(const Token * tok,const std::string & structname,const std::string & varname)3212 void CheckOther::unusedStructMemberError(const Token *tok, const std::string &structname, const std::string &varname)
3213 {
3214 reportError(tok, Severity::style, "unusedStructMember", "struct or union member '" + structname + "::" + varname + "' is never used");
3215 }
3216
passedByValueError(const Token * tok,const std::string & parname)3217 void CheckOther::passedByValueError(const Token *tok, const std::string &parname)
3218 {
3219 reportError(tok, Severity::performance, "passedByValue",
3220 "Function parameter '" + parname + "' should be passed by reference.\n"
3221 "Parameter '" + parname + "' is passed as a value. It could be passed "
3222 "as a (const) reference which is usually faster and recommended in C++.");
3223 }
3224
constStatementError(const Token * tok,const std::string & type)3225 void CheckOther::constStatementError(const Token *tok, const std::string &type)
3226 {
3227 reportError(tok, Severity::warning, "constStatement", "Redundant code: Found a statement that begins with " + type + " constant");
3228 }
3229
charArrayIndexError(const Token * tok)3230 void CheckOther::charArrayIndexError(const Token *tok)
3231 {
3232 reportError(tok,
3233 Severity::warning,
3234 "charArrayIndex",
3235 "Using char type as array index\n"
3236 "Using signed char type as array index. If the value "
3237 "can be greater than 127 there will be a buffer overflow "
3238 "(because of sign extension).");
3239 }
3240
charBitOpError(const Token * tok)3241 void CheckOther::charBitOpError(const Token *tok)
3242 {
3243 reportError(tok,
3244 Severity::warning,
3245 "charBitOp",
3246 "When using char variables in bit operations, sign extension can generate unexpected results.\n"
3247 "When using char variables in bit operations, sign extension can generate unexpected results. For example:\n"
3248 " char c = 0x80;\n"
3249 " int i = 0 | c;\n"
3250 " if (i & 0x8000)\n"
3251 " printf(\"not expected\");\n"
3252 "The 'not expected' will be printed on the screen.");
3253 }
3254
variableScopeError(const Token * tok,const std::string & varname)3255 void CheckOther::variableScopeError(const Token *tok, const std::string &varname)
3256 {
3257 reportError(tok,
3258 Severity::information,
3259 "variableScope",
3260 "The scope of the variable '" + varname + "' can be reduced\n"
3261 "The scope of the variable '" + varname + "' can be reduced. Warning: It can be unsafe "
3262 "to fix this message. Be careful. Especially when there are inner loops. Here is an "
3263 "example where cppcheck will write that the scope for 'i' can be reduced:\n"
3264 "void f(int x)\n"
3265 "{\n"
3266 " int i = 0;\n"
3267 " if (x) {\n"
3268 " // it's safe to move 'int i = 0' here\n"
3269 " for (int n = 0; n < 10; ++n) {\n"
3270 " // it is possible but not safe to move 'int i = 0' here\n"
3271 " do_something(&i);\n"
3272 " }\n"
3273 " }\n"
3274 "}\n"
3275 "When you see this message it is always safe to reduce the variable scope 1 level.");
3276 }
3277
conditionAlwaysTrueFalse(const Token * tok,const std::string & truefalse)3278 void CheckOther::conditionAlwaysTrueFalse(const Token *tok, const std::string &truefalse)
3279 {
3280 reportError(tok, Severity::style, "conditionAlwaysTrueFalse", "Condition is always " + truefalse);
3281 }
3282
strPlusChar(const Token * tok)3283 void CheckOther::strPlusChar(const Token *tok)
3284 {
3285 reportError(tok, Severity::error, "strPlusChar", "Unusual pointer arithmetic");
3286 }
3287
zerodivError(const Token * tok)3288 void CheckOther::zerodivError(const Token *tok)
3289 {
3290 reportError(tok, Severity::error, "zerodiv", "Division by zero");
3291 }
3292
mathfunctionCallError(const Token * tok,const unsigned int numParam)3293 void CheckOther::mathfunctionCallError(const Token *tok, const unsigned int numParam)
3294 {
3295 if (tok) {
3296 if (numParam == 1)
3297 reportError(tok, Severity::error, "wrongmathcall", "Passing value " + tok->tokAt(2)->str() + " to " + tok->str() + "() leads to undefined result");
3298 else if (numParam == 2)
3299 reportError(tok, Severity::error, "wrongmathcall", "Passing value " + tok->tokAt(2)->str() + " and " + tok->tokAt(4)->str() + " to " + tok->str() + "() leads to undefined result");
3300 } else
3301 reportError(tok, Severity::error, "wrongmathcall", "Passing value " " to " "() leads to undefined result");
3302 }
3303
fflushOnInputStreamError(const Token * tok,const std::string & varname)3304 void CheckOther::fflushOnInputStreamError(const Token *tok, const std::string &varname)
3305 {
3306 reportError(tok, Severity::error,
3307 "fflushOnInputStream", "fflush() called on input stream \"" + varname + "\" may result in undefined behaviour");
3308 }
3309
sizeofsizeof()3310 void CheckOther::sizeofsizeof()
3311 {
3312 if (!_settings->isEnabled("style"))
3313 return;
3314 for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
3315 if (Token::Match(tok, "sizeof (| sizeof")) {
3316 sizeofsizeofError(tok);
3317 tok = tok->next();
3318 }
3319 }
3320 }
3321
sizeofsizeofError(const Token * tok)3322 void CheckOther::sizeofsizeofError(const Token *tok)
3323 {
3324 reportError(tok, Severity::warning,
3325 "sizeofsizeof", "Calling sizeof for 'sizeof'.\n"
3326 "Calling sizeof for 'sizeof looks like a suspicious code and "
3327 "most likely there should be just one 'sizeof'. The current "
3328 "code is equivalent to 'sizeof(size_t)'");
3329 }
3330
sizeofCalculation()3331 void CheckOther::sizeofCalculation()
3332 {
3333 if (!_settings->isEnabled("style"))
3334 return;
3335 for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
3336 if (Token::simpleMatch(tok, "sizeof (")) {
3337 unsigned int parlevel = 0;
3338 for (const Token *tok2 = tok->tokAt(2); tok2; tok2 = tok2->next()) {
3339 if (tok2->str() == "(")
3340 ++parlevel;
3341 else if (tok2->str() == ")") {
3342 if (parlevel <= 1)
3343 break;
3344 --parlevel;
3345 } else if (Token::Match(tok2, "+|/")) {
3346 sizeofCalculationError(tok2);
3347 break;
3348 }
3349 }
3350 }
3351 }
3352 }
3353
sizeofCalculationError(const Token * tok)3354 void CheckOther::sizeofCalculationError(const Token *tok)
3355 {
3356 reportError(tok, Severity::warning,
3357 "sizeofCalculation", "Found calculation inside sizeof()");
3358 }
3359
redundantAssignmentInSwitchError(const Token * tok,const std::string & varname)3360 void CheckOther::redundantAssignmentInSwitchError(const Token *tok, const std::string &varname)
3361 {
3362 reportError(tok, Severity::warning,
3363 "redundantAssignInSwitch", "Redundant assignment of \"" + varname + "\" in switch");
3364 }
3365
switchCaseFallThrough(const Token * tok)3366 void CheckOther::switchCaseFallThrough(const Token *tok)
3367 {
3368 reportError(tok, Severity::style,
3369 "switchCaseFallThrough", "Switch falls through case without comment");
3370 }
3371
selfAssignmentError(const Token * tok,const std::string & varname)3372 void CheckOther::selfAssignmentError(const Token *tok, const std::string &varname)
3373 {
3374 reportError(tok, Severity::warning,
3375 "selfAssignment", "Redundant assignment of \"" + varname + "\" to itself");
3376 }
3377
assignmentInAssertError(const Token * tok,const std::string & varname)3378 void CheckOther::assignmentInAssertError(const Token *tok, const std::string &varname)
3379 {
3380 reportError(tok, Severity::warning,
3381 "assignmentInAssert", "Assert statement modifies '" + varname + "'.\n"
3382 "Variable '" + varname + "' is modified inside assert statement. "
3383 "Assert statements are removed from release builds so the code inside "
3384 "assert statement is not run. If the code is needed also in release "
3385 "builds this is a bug.");
3386 }
3387
incorrectLogicOperatorError(const Token * tok,bool always)3388 void CheckOther::incorrectLogicOperatorError(const Token *tok, bool always)
3389 {
3390 if (always)
3391 reportError(tok, Severity::warning,
3392 "incorrectLogicOperator", "Mutual exclusion over || always evaluates to true. Did you intend to use && instead?");
3393 else
3394 reportError(tok, Severity::warning,
3395 "incorrectLogicOperator", "Expression always evaluates to false. Did you intend to use || instead?");
3396 }
3397
misusedScopeObjectError(const Token * tok,const std::string & varname)3398 void CheckOther::misusedScopeObjectError(const Token *tok, const std::string& varname)
3399 {
3400 reportError(tok, Severity::error,
3401 "unusedScopedObject", "instance of \"" + varname + "\" object destroyed immediately");
3402 }
3403
catchExceptionByValueError(const Token * tok)3404 void CheckOther::catchExceptionByValueError(const Token *tok)
3405 {
3406 reportError(tok, Severity::style,
3407 "catchExceptionByValue", "Exception should be caught by reference.\n"
3408 "The exception is caught as a value. It could be caught "
3409 "as a (const) reference which is usually recommended in C++.");
3410 }
3411
memsetZeroBytesError(const Token * tok,const std::string & varname)3412 void CheckOther::memsetZeroBytesError(const Token *tok, const std::string &varname)
3413 {
3414 const std::string summary("memset() called to fill 0 bytes of \'" + varname + "\'");
3415 const std::string verbose(summary + ". Second and third arguments might be inverted.");
3416 reportError(tok, Severity::warning, "memsetZeroBytes", summary + "\n" + verbose);
3417 }
3418
incorrectStringCompareError(const Token * tok,const std::string & func,const std::string & string,const std::string & len)3419 void CheckOther::incorrectStringCompareError(const Token *tok, const std::string& func, const std::string &string, const std::string &len)
3420 {
3421 reportError(tok, Severity::warning, "incorrectStringCompare", "String literal " + string + " doesn't match length argument for " + func + "(" + len + ").");
3422 }
3423
comparisonOfBoolWithIntError(const Token * tok,const std::string & varname)3424 void CheckOther::comparisonOfBoolWithIntError(const Token *tok, const std::string &varname)
3425 {
3426 reportError(tok, Severity::warning, "comparisonOfBoolWithInt",
3427 "Comparison of a boolean with a non-zero integer\n"
3428 "The expression \"!" + varname + "\" is of type 'bool' but is compared against a non-zero 'int'.");
3429 }
3430
duplicateBreakError(const Token * tok)3431 void CheckOther::duplicateBreakError(const Token *tok)
3432 {
3433 reportError(tok, Severity::style, "duplicateBreak",
3434 "Consecutive break or continue statements are unnecessary\n"
3435 "The second of the two statements can never be executed, and so should be removed\n");
3436 }
3437
3438
checkAssignBoolToPointer()3439 void CheckOther::checkAssignBoolToPointer()
3440 {
3441 for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
3442 if (Token::Match(tok, "[;{}] %var% = %bool% ;")) {
3443 const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();
3444
3445 const Variable *var1(symbolDatabase->getVariableFromVarId(tok->next()->varId()));
3446
3447 // Is variable a pointer?
3448 if (var1 && var1->nameToken()->strAt(-1) == "*")
3449 assignBoolToPointerError(tok->next());
3450 }
3451 }
3452 }
3453
assignBoolToPointerError(const Token * tok)3454 void CheckOther::assignBoolToPointerError(const Token *tok)
3455 {
3456 reportError(tok, Severity::error, "assignBoolToPointer",
3457 "Assigning bool value to pointer (converting bool value to address)");
3458 }
3459
3460 //---------------------------------------------------------------------------
3461 // Check testing sign of unsigned variables.
3462 //---------------------------------------------------------------------------
3463
checkSignOfUnsignedVariable()3464 void CheckOther::checkSignOfUnsignedVariable()
3465 {
3466 if (!_settings->isEnabled("style"))
3467 return;
3468
3469 const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();
3470
3471 std::list<Scope>::const_iterator scope;
3472
3473 for (scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) {
3474 // only check functions
3475 if (scope->type != Scope::eFunction)
3476 continue;
3477
3478 // check all the code in the function
3479 for (const Token *tok = scope->classStart; tok && tok != scope->classStart->link(); tok = tok->next()) {
3480 if (Token::Match(tok, "( %var% <|<= 0 )") && tok->next()->varId()) {
3481 const Variable * var = symbolDatabase->getVariableFromVarId(tok->next()->varId());
3482 if (var && var->typeEndToken()->isUnsigned())
3483 unsignedLessThanZero(tok->next(), tok->next()->str());
3484 } else if (Token::Match(tok, "( 0 > %var% )") && tok->tokAt(3)->varId()) {
3485 const Variable * var = symbolDatabase->getVariableFromVarId(tok->tokAt(3)->varId());
3486 if (var && var->typeEndToken()->isUnsigned())
3487 unsignedLessThanZero(tok->tokAt(3), tok->strAt(3));
3488 } else if (Token::Match(tok, "( 0 <= %var% )") && tok->tokAt(3)->varId()) {
3489 const Variable * var = symbolDatabase->getVariableFromVarId(tok->tokAt(3)->varId());
3490 if (var && var->typeEndToken()->isUnsigned())
3491 unsignedPositive(tok->tokAt(3), tok->strAt(3));
3492 }
3493 }
3494 }
3495 }
3496
unsignedLessThanZero(const Token * tok,const std::string & varname)3497 void CheckOther::unsignedLessThanZero(const Token *tok, const std::string &varname)
3498 {
3499 reportError(tok, Severity::style, "unsignedLessThanZero",
3500 "Checking if unsigned variable '" + varname + "' is less than zero.\n"
3501 "An unsigned variable will never be negative so it is either pointless or "
3502 "an error to check if it is.");
3503 }
3504
unsignedPositive(const Token * tok,const std::string & varname)3505 void CheckOther::unsignedPositive(const Token *tok, const std::string &varname)
3506 {
3507 reportError(tok, Severity::style, "unsignedPositive",
3508 "Checking if unsigned variable '" + varname + "' is positive is always true.\n"
3509 "An unsigned variable can't be negative so it is either pointless or "
3510 "an error to check if it is.");
3511 }
3512